diff --git a/README.md b/README.md index 5ac8ac22..1ed10d40 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ We've rewritten most of the logic in the original library to address the followi - [x] Fixed incorrect zoom center (happens during pinching and double tapping) - [x] Fixed incorrect pan boundaries - [x] Added the ability to zoom and pan at the same time (before you can only perform 1 of these 2 at a time) -- [x] Added “pan momentum”, “zoom to”, and “boundaries-crossed” animations +- [x] Added “zoom to” animations - [x] Added onSingleTap (besides the existing onDoubleTap) - [x] Added animated touch feedback when the zoom subject is tapped on - [x] Added "react-native-builder-bob" as a framework for library management/maintenance @@ -80,7 +80,6 @@ Use the component: minZoom={0.5} zoomStep={0.5} initialZoom={1} - bindToBorders={true} onZoomAfter={this.logOutZoomState} style={{ padding: 10, @@ -147,70 +146,70 @@ const styles = StyleSheet.create({ These options can be used to limit and change the zoom behavior. -| name | type | description | default | -| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | -| zoomEnabled | boolean | Can be used to enable or disable the zooming dynamically | true | -| panEnabled | boolean | Can be used to enable or disable the panning dynamically | true | -| initialZoom | number | Initial zoom level on startup | 1.0 | -| maxZoom | number | Maximum possible zoom level (zoom in). Can be set to `null` to allow unlimited zooming | 1.5 | -| minZoom | number | Minimum possible zoom level (zoom out) | 0.5 | -| disablePanOnInitialZoom | boolean | If true, panning is disabled when zoom level is equal to the initial zoom level | false | -| doubleTapDelay | number | How much delay will still be recognized as double press (ms) | 300 | -| doubleTapZoomToCenter | boolean | If true, double tapping will always zoom to center of View instead of the direction it was double tapped in | -| bindToBorders | boolean | If true, it makes sure the object stays within box borders | true | -| zoomStep | number | How much zoom should be applied on double tap | 0.5 | -| pinchToZoomInSensitivity | number | the level of resistance (sensitivity) to zoom in (0 - 10) - higher is less sensitive | 3 | -| pinchToZoomOutSensitivity | number | the level of resistance (sensitivity) to zoom out (0 - 10) - higher is less sensitive | 1 | -| movementSensibility | number | how resistant should shifting the view around be? (0.5 - 5) - higher is less sensitive | 1.9 | -| initialOffsetX | number | The horizontal offset the image should start at | 0 | -| initialOffsetY | number | The vertical offset the image should start at | 0 | -| contentHeight | number | Specify if you want to treat the height of the **centered** content inside the zoom subject as the zoom subject's height | undefined | -| contentWidth | number | Specify if you want to treat the width of the **centered** content inside the zoom subject as the zoom subject's width | undefined | -| panBoundaryPadding | number | At certain scales, the edge of the content is bounded too close to the edge of the container, making it difficult to pan to and interact with the edge of the content. To fix this, we'd wanna allow the content to pan just a little further away from the container's edge. Hence, the "pan boundary padding", measured in pixels. | 0 | -| longPressDuration | number | Duration in ms until a press is considered a long press | 700 | -| visualTouchFeedbackEnabled | boolean | Whether to show a touch feedback circle on touch | true | +| name | type | description | default | +| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ | --------- | +| zoomEnabled | boolean | Can be used to enable or disable the zooming dynamically | true | +| panEnabled | boolean | Can be used to enable or disable the panning dynamically | true | +| initialZoom | number | Initial zoom level on startup | 1.0 | +| maxZoom | number | Maximum possible zoom level (zoom in). Can be set to `null` to allow unlimited zooming | 1.5 | +| minZoom | number | Minimum possible zoom level (zoom out) | 0.5 | +| disablePanOnInitialZoom | boolean | If true, panning is disabled when zoom level is equal to the initial zoom level | false | +| doubleTapDelay | number | How much delay will still be recognized as double press (ms) | 300 | +| doubleTapZoomToCenter | boolean | If true, double tapping will always zoom to center of View instead of the direction it was double tapped in | +| zoomStep | number | How much zoom should be applied on double tap | 0.5 | +| pinchToZoomInSensitivity | number | the level of resistance (sensitivity) to zoom in (0 - 10) - higher is less sensitive | 1 | +| pinchToZoomOutSensitivity | number | the level of resistance (sensitivity) to zoom out (0 - 10) - higher is less sensitive | 1 | +| movementSensibility | number | how resistant should shifting the view around be? (0.5 - 5) - higher is less sensitive | 1 | +| initialOffsetX | number | The horizontal offset the image should start at | 0 | +| initialOffsetY | number | The vertical offset the image should start at | 0 | +| contentHeight | number | Specify if you want to treat the height of the **centered** content inside the zoom subject as the zoom subject's height | undefined | +| contentWidth | number | Specify if you want to treat the width of the **centered** content inside the zoom subject as the zoom subject's width | undefined | +| longPressDuration | number | Duration in ms until a press is considered a long press | 700 | +| visualTouchFeedbackEnabled | boolean | Whether to show a touch feedback circle on touch | true | #### Static Pin Position These optional props can be used to keep a "static" pin in the centre of the screen and move the map underneath it. This is very useful for maps. -| name | type | description | -| ------------------------- | ------------------------- | --------------------------------------------------------- | -| staticPinPosition | Vec2D | Where in the viewport to put the pin | -| staticPinIcon | Element | The pin icon itself | -| onStaticPinPositionChange | (position: Vec2D) => void | Callback every time the pin is at rest | -| onStaticPinPositionMove | (position: Vec2D) => void | Callback live while the pin is moving | -| animatePin | boolean | Whether to make the pin bounce up and down while dragging | +| name | type | description | +| ------------------------- | ------------------------- | -------------------------------------- | +| staticPinPosition | Vec2D | Where in the viewport to put the pin | +| staticPinIcon | Element | The pin icon itself | +| onStaticPinPress | (event) => void | Callback when the pin is pressed | +| onStaticPinLongPress | (event) => void | Callback when the pin is long pressed | +| onStaticPinPositionChange | (position: Vec2D) => void | Callback every time the pin is at rest | +| onStaticPinPositionMove | (position: Vec2D) => void | Callback live while the pin is moving | +| pinProps | ViewProps | Props forwarded to the pin wrapper | #### Callbacks These events can be used to work with data after specific events. -| name | description | params | expected return | -| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| onTransform | Will be called when the transformation configuration (zoom level and offset) changes | zoomableViewEventObject | void | -| onDoubleTapBefore | Will be called, at the start of a double tap | event, gestureState, zoomableViewEventObject | void | -| onDoubleTapAfter | Will be called at the end of a double tap | event, gestureState, zoomableViewEventObject | void | -| onShiftingBefore | Will be called, when user taps and moves the view, but before our view movement work kicks in (so this is the place to interrupt movement, if you need to) | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the shift, otherwise it will | -| onShiftingAfter | Will be called, when user taps and moves the view, but after the values have changed already | event, gestureState, zoomableViewEventObject | void | -| onShiftingEnd | Will be called, when user stops a tap and move gesture | event, gestureState, zoomableViewEventObject | void | -| onZoomBefore | Will be called, while the user pinches the screen, but before our zoom work kicks in (so this is the place to interrupt zooming, if you need to) | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will | -| onZoomAfter | Will be called, while the user pinches the screen, but after the values have changed already | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will | -| onZoomEnd | Will be called after pinchzooming has ended | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will | -| onLongPress | Will be called after the user pressed on the image for a while | event, gestureState | void | -| onLayout | Like `View`'s `onLayout`, but different in that it syncs with this component's internal state and returns a fake sythentic event | Like `View`'s `onLayout` but the synthetic event is fake | void | +| name | description | params | expected return | +| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| onTransform | Will be called when the transformation configuration (zoom level and offset) changes | zoomableViewEventObject | void | +| onDoubleTapBefore | Will be called at the start of a double tap | event, gestureState, zoomableViewEventObject | void | +| onDoubleTapAfter | Will be called at the end of a double tap | event, gestureState, zoomableViewEventObject | void | +| onShiftingBefore | Will be called when user taps and moves the view, but before our view movement work kicks in (so this is the place to interrupt movement, if you need to) | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the shift, otherwise it will | +| onShiftingAfter | Will be called when user taps and moves the view, but after the values have changed already | event, gestureState, zoomableViewEventObject | void | +| onShiftingEnd | Will be called when user stops a tap and move gesture | event, gestureState, zoomableViewEventObject | void | +| onZoomBefore | Will be called while the user pinches the screen, but before our zoom work kicks in (so this is the place to interrupt zooming, if you need to) | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will | +| onZoomAfter | Will be called while the user pinches the screen, but after the values have changed already | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will | +| onZoomEnd | Will be called after pinchzooming has ended | event, gestureState, zoomableViewEventObject | {boolean} if this returns true, ZoomableView will not process the pinch, otherwise it will | +| onLongPress | Will be called after the user pressed on the image for a while | event, gestureState | void | +| onLayout | Like `View`'s `onLayout`, but different in that it syncs with this component's internal state and returns a fake sythentic event | Like `View`'s `onLayout` but the synthetic event is fake | void | #### Methods The following methods allow you to control the ZoomableView zoom level & position from your component. (think of control buttons, ...) -| name | description | params | expected return | -| ------ | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | --------------- | -| zoomTo | Changes the zoom level to a specific number | newZoomLevel: number, bindToBorders = true | Promise | -| zoomBy | Changes the zoom level relative to the current level (use positive numbers to zoom in, negative numbers to zoom out) | zoomLevelChange: number, bindToBorders = true | Promise | -| moveTo | Shifts the zoomed part to a specific point (in px relative to x: 0, y: 0) | newOffsetX: number, newOffsetY: number, bindToBorders = true | Promise | -| moveBy | Shifts the zoomed part by a specific pixel number | newOffsetX: number, newOffsetY: number, bindToBorders = true | Promise | +| name | description | params | expected return | +| ------ | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | --------------- | +| zoomTo | Changes the zoom level to a specific number | newZoomLevel: number | boolean | +| zoomBy | Changes the zoom level relative to the current level (use positive numbers to zoom in, negative numbers to zoom out) | zoomLevelChange: number | boolean | +| moveTo | Shifts the zoomed part to a specific point (in px relative to x: 0, y: 0) | newOffsetX: number, newOffsetY: number | void | +| moveBy | Shifts the zoomed part by a specific pixel number | newOffsetX: number, newOffsetY: number | void | #### Properties @@ -231,7 +230,6 @@ export default function App() { HelloWorld diff --git a/example/App.tsx b/example/App.tsx index 79478625..8d249fd2 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -46,7 +46,6 @@ export default function App() { }} > diff --git a/src/ReactNativeZoomableView.tsx b/src/ReactNativeZoomableView.tsx index 49b5bc94..4d135a86 100644 --- a/src/ReactNativeZoomableView.tsx +++ b/src/ReactNativeZoomableView.tsx @@ -2,7 +2,6 @@ import { debounce } from 'lodash'; import React, { Component, createRef, RefObject } from 'react'; import { Animated, - Easing, GestureResponderEvent, PanResponder, PanResponderCallbacks, @@ -12,11 +11,7 @@ import { View, } from 'react-native'; -import { - getBoundaryCrossedAnim, - getPanMomentumDecayAnim, - getZoomToAnimation, -} from './animations'; +import { getZoomToAnimation } from './animations'; import { AnimatedTouchFeedback } from './components'; import { StaticPin } from './components/StaticPin'; import { DebugTouchPoint } from './debugHelper'; @@ -25,7 +20,6 @@ import { calcGestureTouchDistance, calcNewScaledOffsetForZoomCentering, } from './helper'; -import { applyPanBoundariesToOffset } from './helper/applyPanBoundariesToOffset'; import { viewportPositionToImagePosition } from './helper/coordinateConversion'; import { ReactNativeZoomableViewProps, @@ -66,43 +60,38 @@ class ReactNativeZoomableView extends Component< pinchToZoomOutSensitivity: 1, movementSensibility: 1, doubleTapDelay: 300, - bindToBorders: true, zoomStep: 0.5, onLongPress: null, longPressDuration: 700, contentWidth: undefined, contentHeight: undefined, - panBoundaryPadding: 0, visualTouchFeedbackEnabled: true, staticPinPosition: undefined, staticPinIcon: undefined, onStaticPinPositionChange: undefined, onStaticPinPositionMove: undefined, - animatePin: true, disablePanOnInitialZoom: false, }; private panAnim = new Animated.ValueXY({ x: 0, y: 0 }); - private zoomAnim = new Animated.Value(1); - private pinAnim = new Animated.ValueXY({ x: 0, y: 0 }); - - private __offsets = { - x: { - value: 0, - boundaryCrossedAnimInEffect: false, - }, - y: { - value: 0, - boundaryCrossedAnimInEffect: false, - }, - }; + private readonly zoomAnim = new Animated.Value(1); + + private offsetX = 0; + private offsetY = 0; private zoomLevel = 1; private lastGestureCenterPosition: { x: number; y: number } | null = null; private lastGestureTouchDistance: number | null = null; private gestureType: 'pinch' | 'shift' | null; + private panListenerId: string | undefined; + private zoomListenerId: string | undefined; + private panTransformListenerId: string | undefined; + private zoomTransformListenerId: string | undefined; + private zoomToListenerId: string | undefined; + private _gestureStarted = false; + private mounted = false; private set gestureStarted(v: boolean) { this._gestureStarted = v; } @@ -182,11 +171,11 @@ class ReactNativeZoomableView extends Component< this.panAnim.setValue({ x: this.offsetX, y: this.offsetY }); this.zoomAnim.setValue(this.zoomLevel); - this.panAnim.addListener(({ x, y }) => { + this.panListenerId = this.panAnim.addListener(({ x, y }) => { this.offsetX = x; this.offsetY = y; }); - this.zoomAnim.addListener(({ value }) => { + this.zoomListenerId = this.zoomAnim.addListener(({ value }) => { this.zoomLevel = value; }); @@ -195,85 +184,6 @@ class ReactNativeZoomableView extends Component< this.gestureType = null; } - private raisePin() { - if (!this.props.animatePin) return; - Animated.timing(this.pinAnim, { - toValue: { x: 0, y: -10 }, - useNativeDriver: true, - easing: Easing.out(Easing.ease), - duration: 100, - }).start(); - } - - private dropPin() { - if (!this.props.animatePin) return; - Animated.timing(this.pinAnim, { - toValue: { x: 0, y: 0 }, - useNativeDriver: true, - easing: Easing.out(Easing.ease), - duration: 100, - }).start(); - } - - private set offsetX(x: number) { - this.__setOffset('x', x); - } - - private set offsetY(y: number) { - this.__setOffset('y', y); - } - - private get offsetX() { - return this.__getOffset('x'); - } - - private get offsetY() { - return this.__getOffset('y'); - } - - private __setOffset(axis: 'x' | 'y', offset: number) { - const offsetState = this.__offsets[axis]; - - if (this.props.bindToBorders) { - const containerSize = - axis === 'x' ? this.state.originalWidth : this.state.originalHeight; - const contentSize = - axis === 'x' - ? this.props.contentWidth || this.state.originalWidth - : this.props.contentHeight || this.state.originalHeight; - - const boundOffset = - contentSize && containerSize && this.props.panBoundaryPadding != null - ? applyPanBoundariesToOffset( - offset, - containerSize, - contentSize, - this.zoomLevel, - this.props.panBoundaryPadding - ) - : offset; - - if (!this.gestureType && !offsetState.boundaryCrossedAnimInEffect) { - const boundariesApplied = - boundOffset !== offset && - boundOffset.toFixed(3) !== offset.toFixed(3); - if (boundariesApplied) { - offsetState.boundaryCrossedAnimInEffect = true; - getBoundaryCrossedAnim(this.panAnim[axis], boundOffset).start(() => { - offsetState.boundaryCrossedAnimInEffect = false; - }); - return; - } - } - } - - offsetState.value = offset; - } - - private __getOffset(axis: 'x' | 'y') { - return this.__offsets[axis].value; - } - componentDidUpdate( prevProps: ReactNativeZoomableViewProps, prevState: ReactNativeZoomableViewState @@ -287,8 +197,12 @@ class ReactNativeZoomableView extends Component< !this.onTransformInvocationInitialized && this._invokeOnTransform().successful ) { - this.panAnim.addListener(() => this._invokeOnTransform()); - this.zoomAnim.addListener(() => this._invokeOnTransform()); + this.panTransformListenerId = this.panAnim.addListener(() => + this._invokeOnTransform() + ); + this.zoomTransformListenerId = this.zoomAnim.addListener(() => + this._invokeOnTransform() + ); this.onTransformInvocationInitialized = true; } @@ -327,6 +241,7 @@ class ReactNativeZoomableView extends Component< } componentDidMount() { + this.mounted = true; this.measureZoomSubject(); // We've already run `grabZoomSubjectOriginalMeasurements` at various events // to make sure the measurements are promptly updated. @@ -339,8 +254,32 @@ class ReactNativeZoomableView extends Component< } componentWillUnmount() { + this.mounted = false; this.measureZoomSubjectInterval && clearInterval(this.measureZoomSubjectInterval); + + // Stop in-flight animations to prevent post-unmount callbacks + // (e.g. _resolveAndHandleTap's 200ms pan animation calling onStaticPinPositionChange) + if (!this.props.panAnimatedValueXY) this.panAnim.stopAnimation(); + if (!this.props.zoomAnimatedValue) this.zoomAnim.stopAnimation(); + + // Remove animation listeners to prevent memory leaks and post-unmount callbacks + if (this.panListenerId) this.panAnim.removeListener(this.panListenerId); + if (this.zoomListenerId) this.zoomAnim.removeListener(this.zoomListenerId); + if (this.panTransformListenerId) + this.panAnim.removeListener(this.panTransformListenerId); + if (this.zoomTransformListenerId) + this.zoomAnim.removeListener(this.zoomTransformListenerId); + // Clean up zoomTo() listener if mid-animation at unmount + if (this.zoomToListenerId) + this.zoomAnim.removeListener(this.zoomToListenerId); + + // Clear pending timeouts that could fire post-unmount + if (this.singleTapTimeoutId) clearTimeout(this.singleTapTimeoutId); + if (this.longPressTimeout) clearTimeout(this.longPressTimeout); + + // Cancel debounced calls that could invoke props post-unmount + this.debouncedOnStaticPinPositionChange.cancel(); } debouncedOnStaticPinPositionChange = debounce( @@ -397,10 +336,12 @@ class ReactNativeZoomableView extends Component< private measureZoomSubject = () => { // make sure we measure after animations are complete requestAnimationFrame(() => { + if (!this.mounted) return; // this setTimeout is here to fix a weird issue on iOS where the measurements are all `0` // when navigating back (react-navigation stack) from another view // while closing the keyboard at the same time setTimeout(() => { + if (!this.mounted) return; // In normal conditions, we're supposed to measure zoomSubject instead of its wrapper. // However, our zoomSubject may have been transformed by an initial zoomLevel or offset, // in which case these measurements will not represent the true "original" measurements. @@ -408,6 +349,7 @@ class ReactNativeZoomableView extends Component< // (no border, space, or anything between them) this.zoomSubjectWrapperRef.current?.measure( (x, y, width, height, pageX, pageY) => { + if (!this.mounted) return; // When the component is off-screen, these become all 0s, so we don't set them // to avoid messing up calculations, especially ones that are done right after // the component transitions from hidden to visible. @@ -479,6 +421,11 @@ class ReactNativeZoomableView extends Component< _handlePanResponderGrant: NonNullable< PanResponderCallbacks['onPanResponderGrant'] > = (e, gestureState) => { + if (this.singleTapTimeoutId) { + clearTimeout(this.singleTapTimeoutId); + this.singleTapTimeoutId = undefined; + } + if (this.props.onLongPress) { e.persist(); this.longPressTimeout = setTimeout(() => { @@ -497,11 +444,16 @@ class ReactNativeZoomableView extends Component< this._getZoomableViewEventObject() ); - this.panAnim.stopAnimation(); - this.zoomAnim.stopAnimation(); + this.panAnim.x.stopAnimation((x) => { + this.offsetX = x; + }); + this.panAnim.y.stopAnimation((y) => { + this.offsetY = y; + }); + this.zoomAnim.stopAnimation((zoom) => { + this.zoomLevel = zoom; + }); this.gestureStarted = true; - - this.raisePin(); }; /** @@ -523,22 +475,6 @@ class ReactNativeZoomableView extends Component< this.lastGestureCenterPosition = null; - const disableMomentum = - this.props.disableMomentum || - (this.props.panEnabled && - this.gestureType === 'shift' && - this.props.disablePanOnInitialZoom && - this.zoomLevel === this.props.initialZoom); - - // Trigger final shift animation unless disablePanOnInitialZoom is set and we're on the initial zoom level - // or disableMomentum - if (!disableMomentum) { - getPanMomentumDecayAnim(this.panAnim, { - x: gestureState.vx / this.zoomLevel, - y: gestureState.vy / this.zoomLevel, - }).start(); - } - if (this.longPressTimeout) { clearTimeout(this.longPressTimeout); this.longPressTimeout = null; @@ -568,8 +504,6 @@ class ReactNativeZoomableView extends Component< this._updateStaticPin(); } - this.dropPin(); - this.gestureType = null; this.gestureStarted = false; }; @@ -882,8 +816,6 @@ class ReactNativeZoomableView extends Component< } this._setNewOffsetPosition(offsetX, offsetY); - - this.raisePin(); } /** @@ -964,8 +896,8 @@ class ReactNativeZoomableView extends Component< }, useNativeDriver: true, duration: 200, - }).start(() => { - this._updateStaticPin(); + }).start(({ finished }) => { + if (finished && this.mounted) this._updateStaticPin(); }); } @@ -1034,6 +966,7 @@ class ReactNativeZoomableView extends Component< } private _removeTouch(touch: TouchPoint) { + if (!this.mounted) return; this.touches.splice(this.touches.indexOf(touch), 1); this.setState({ touches: [...this.touches] }); } @@ -1119,7 +1052,13 @@ class ReactNativeZoomableView extends Component< this.props.onZoomBefore?.(null, null, this._getZoomableViewEventObject()); // == Perform Pan Animation to preserve the zoom center while zooming == - let listenerId = ''; + // Remove any in-flight zoomTo listener before adding a new one, + // otherwise rapid successive zoomTo() calls (e.g. fast double-taps) + // leak the previous listener on zoomAnim permanently. + if (this.zoomToListenerId) { + this.zoomAnim.removeListener(this.zoomToListenerId); + this.zoomToListenerId = undefined; + } if (zoomCenter) { // Calculates panAnim values based on changes in zoomAnim. let prevScale = this.zoomLevel; @@ -1127,7 +1066,7 @@ class ReactNativeZoomableView extends Component< // it will jitter panAnim once in a while, // because here panAnim is being calculated in js. // However the jittering should mostly occur in simulator. - listenerId = this.zoomAnim.addListener(({ value: newScale }) => { + const listenerId = this.zoomAnim.addListener(({ value: newScale }) => { this.panAnim.setValue({ x: calcNewScaledOffsetForZoomCentering( this.offsetX, @@ -1146,11 +1085,18 @@ class ReactNativeZoomableView extends Component< }); prevScale = newScale; }); + this.zoomToListenerId = listenerId; } // == Perform Zoom Animation == + const listenerId = this.zoomToListenerId; getZoomToAnimation(this.zoomAnim, newZoomLevel).start(() => { - this.zoomAnim.removeListener(listenerId); + if (listenerId) { + this.zoomAnim.removeListener(listenerId); + if (this.zoomToListenerId === listenerId) { + this.zoomToListenerId = undefined; + } + } }); // == Zoom Animation Ends == @@ -1219,6 +1165,7 @@ class ReactNativeZoomableView extends Component< children, visualTouchFeedbackEnabled, doubleTapDelay, + longPressDuration, staticPinPosition, onStaticPinLongPress, onStaticPinPress, @@ -1281,7 +1228,16 @@ class ReactNativeZoomableView extends Component< onPress={onStaticPinPress} onLongPress={onStaticPinLongPress} onParentMove={this._handlePanResponderMove} - pinAnim={this.pinAnim} + onParentRelease={this._handlePanResponderEnd} + onParentTerminate={(evt, gestureState) => { + this._handlePanResponderEnd(evt, gestureState); + this.props.onPanResponderTerminate?.( + evt, + gestureState, + this._getZoomableViewEventObject() + ); + }} + longPressDuration={longPressDuration} setPinSize={(size: Size2D) => { this.setState({ pinSize: size }); }} diff --git a/src/animations/index.ts b/src/animations/index.ts index cfe67f83..bd72f4ba 100644 --- a/src/animations/index.ts +++ b/src/animations/index.ts @@ -1,29 +1,5 @@ import { Animated, Easing } from 'react-native'; -import { Vec2D } from '../typings'; - -export function getBoundaryCrossedAnim( - animValue: Animated.Value, - toValue: number -) { - return Animated.spring(animValue, { - overshootClamping: true, - toValue, - useNativeDriver: true, - }); -} - -export function getPanMomentumDecayAnim( - animValue: Animated.Value | Animated.ValueXY, - velocity: number | Vec2D -) { - return Animated.decay(animValue, { - velocity, - deceleration: 0.994, - useNativeDriver: true, - }); -} - export function getZoomToAnimation(animValue: Animated.Value, toValue: number) { return Animated.timing(animValue, { easing: Easing.out(Easing.ease), diff --git a/src/components/StaticPin.tsx b/src/components/StaticPin.tsx index acb13de5..601c5a96 100644 --- a/src/components/StaticPin.tsx +++ b/src/components/StaticPin.tsx @@ -13,17 +13,18 @@ import { Size2D } from 'src/typings'; export const StaticPin = ({ staticPinPosition, - pinAnim, staticPinIcon, pinSize, onParentMove, + onParentRelease, + onParentTerminate, + longPressDuration, onPress, onLongPress, setPinSize, pinProps = {}, }: { staticPinPosition: { x: number; y: number }; - pinAnim: Animated.ValueXY; staticPinIcon: React.ReactNode; pinSize: Size2D; /** Internal handler for passing move event to parent */ @@ -31,16 +32,30 @@ export const StaticPin = ({ evt: GestureResponderEvent, gestureState: PanResponderGestureState ) => boolean | undefined; + onParentRelease: ( + evt: GestureResponderEvent, + gestureState: PanResponderGestureState + ) => void; + onParentTerminate: ( + evt: GestureResponderEvent, + gestureState: PanResponderGestureState + ) => void; + longPressDuration?: number; onPress?: (evt: GestureResponderEvent) => void; onLongPress?: (evt: GestureResponderEvent) => void; setPinSize: (size: Size2D) => void; pinProps?: ViewProps; }) => { const tapTime = React.useRef(0); + const hasDragged = React.useRef(false); + const parentNotified = React.useRef(false); + const { style: pinStyle, ...restPinProps } = pinProps; + const pressDuration = longPressDuration ?? 500; + const pressDurationRef = React.useRef(pressDuration); + pressDurationRef.current = pressDuration; const transform = [ { translateY: -pinSize.height }, { translateX: -pinSize.width / 2 }, - ...pinAnim.getTranslateTransform(), ]; const opacity = pinSize.width && pinSize.height ? 1 : 0; @@ -49,6 +64,8 @@ export const StaticPin = ({ PanResponder.create({ onStartShouldSetPanResponder: () => { tapTime.current = Date.now(); + hasDragged.current = false; + parentNotified.current = false; // We want to handle tap on this so set true return true; @@ -56,21 +73,45 @@ export const StaticPin = ({ onPanResponderMove: (evt, gestureState) => { // However if the user moves finger we want to pass this evt to parent // to handle panning (tap not recognized) - if (Math.abs(gestureState.dx) > 5 && Math.abs(gestureState.dy) > 5) - onParentMove(evt, gestureState); + if (Math.abs(gestureState.dx) > 5 || Math.abs(gestureState.dy) > 5) { + hasDragged.current = true; + const accepted = onParentMove(evt, gestureState); + if (accepted === undefined) { + parentNotified.current = true; + } else if (accepted) { + // Parent handled it internally (e.g. 3-touch branch) — + // clear parentNotified so release doesn't fire a spurious + // onParentRelease → _resolveAndHandleTap. + parentNotified.current = false; + } + } }, onPanResponderRelease: (evt, gestureState) => { - if (Math.abs(gestureState.dx) > 5 || Math.abs(gestureState.dy) > 5) + if (parentNotified.current) { + hasDragged.current = false; + parentNotified.current = false; + onParentRelease(evt, gestureState); return; + } + if (hasDragged.current) { + hasDragged.current = false; + return; + } const dt = Date.now() - tapTime.current; - if (onPress && dt < 500) { + if (onPress && dt < pressDurationRef.current) { onPress(evt); } - if (onLongPress && dt > 500) { - // RN long press is 500ms + if (onLongPress && dt >= pressDurationRef.current) { onLongPress(evt); } }, + onPanResponderTerminate: (evt, gestureState) => { + if (parentNotified.current) { + hasDragged.current = false; + parentNotified.current = false; + onParentTerminate(evt, gestureState); + } + }, }) ).current; @@ -83,8 +124,9 @@ export const StaticPin = ({ }, styles.pinWrapper, { opacity, transform }, + pinStyle, ]} - {...pinProps} + {...restPinProps} > { diff --git a/src/helper/applyPanBoundariesToOffset.ts b/src/helper/applyPanBoundariesToOffset.ts deleted file mode 100644 index 1bcfc02a..00000000 --- a/src/helper/applyPanBoundariesToOffset.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Takes a single offset value and calculates the correct offset value - * to make sure it's within the pan boundaries - * - * - * @param offsetScaled - * @param containerSize - * @param contentSize - * @param scale - * @param boundaryPadding - see README - * - * @returns {number} - */ -export function applyPanBoundariesToOffset( - offsetScaled: number, - containerSize: number, - contentSize: number, - scale: number, - boundaryPadding: number -) { - const contentSizeUnscaled = contentSize * scale; - const offsetUnscaled = offsetScaled * scale; - - const contentStartBorderUnscaled = - containerSize / 2 + offsetUnscaled - contentSizeUnscaled / 2; - const contentEndBorderUnscaled = - contentStartBorderUnscaled + contentSizeUnscaled; - - const containerStartBorder = 0; - const containerEndBorder = containerStartBorder + containerSize; - - // do not let boundary padding be greater than the container size or less than 0 - if (!boundaryPadding || boundaryPadding < 0) boundaryPadding = 0; - if (boundaryPadding > containerSize) boundaryPadding = containerSize; - - // Calculate container's measurements with boundary padding applied. - // this should shrink the container's size by the amount of the boundary padding, - // so that the content inside can be panned a bit further away from the original container's boundaries. - const paddedContainerSize = containerSize - boundaryPadding * 2; - const paddedContainerStartBorder = containerStartBorder + boundaryPadding; - const paddedContainerEndBorder = containerEndBorder - boundaryPadding; - - // if content is smaller than the padded container, - // don't let the content move - if (contentSizeUnscaled < paddedContainerSize) { - return 0; - } - - // if content is larger than the padded container, - // don't let the padded container go outside of content - - // maximum distance the content's center can move from its original position. - // assuming the content original center is the container's center. - const contentMaxOffsetScaled = - (paddedContainerSize / 2 - contentSizeUnscaled / 2) / scale; - - if ( - // content reaching the end boundary - contentEndBorderUnscaled < paddedContainerEndBorder - ) { - return contentMaxOffsetScaled; - } - if ( - // content reaching the start boundary - contentStartBorderUnscaled > paddedContainerStartBorder - ) { - return -contentMaxOffsetScaled; - } - - return offsetScaled; -} diff --git a/src/typings/index.ts b/src/typings/index.ts index 9e16abb9..3ea99ba3 100644 --- a/src/typings/index.ts +++ b/src/typings/index.ts @@ -35,12 +35,10 @@ export interface ReactNativeZoomableViewProps { initialOffsetY?: number; contentWidth?: number; contentHeight?: number; - panBoundaryPadding?: number; maxZoom?: number; minZoom?: number; doubleTapDelay?: number; doubleTapZoomToCenter?: boolean; - bindToBorders?: boolean; zoomStep?: number; pinchToZoomInSensitivity?: number; pinchToZoomOutSensitivity?: number; @@ -156,9 +154,7 @@ export interface ReactNativeZoomableViewProps { staticPinIcon?: React.ReactElement; onStaticPinPositionChange?: (position: Vec2D) => void; onStaticPinPositionMove?: (position: Vec2D) => void; - animatePin: boolean; pinProps?: ViewProps; - disableMomentum?: boolean; } export interface Vec2D { diff --git a/yarn.lock b/yarn.lock index b01883f3..b239cc84 100644 --- a/yarn.lock +++ b/yarn.lock @@ -49,7 +49,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== -"@babel/core@^7.11.6", "@babel/core@^7.23.9", "@babel/core@^7.25.2": +"@babel/core@^7.11.6", "@babel/core@^7.25.2": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== @@ -130,7 +130,7 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.20.0", "@babel/generator@^7.28.5", "@babel/generator@^7.7.2": +"@babel/generator@^7.20.0", "@babel/generator@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== @@ -502,7 +502,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016" integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA== -"@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.3", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": +"@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.25.3", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== @@ -616,7 +616,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.27.1", "@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== @@ -672,7 +672,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.27.1", "@babel/plugin-syntax-typescript@^7.7.2": +"@babel/plugin-syntax-typescript@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== @@ -1378,11 +1378,6 @@ "@babel/helper-validator-identifier" "^7.24.7" to-fast-properties "^2.0.0" -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - "@commitlint/cli@^11.0.0": version "11.0.0" resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-11.0.0.tgz#698199bc52afed50aa28169237758fa14a67b5d3" @@ -1596,57 +1591,11 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": +"@istanbuljs/schema@^0.1.2": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" - integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - -"@jest/core@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" - integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== - dependencies: - "@jest/console" "^29.7.0" - "@jest/reporters" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.7.0" - jest-config "^29.7.0" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-resolve-dependencies "^29.7.0" - jest-runner "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - jest-watcher "^29.7.0" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/create-cache-key-function@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0" @@ -1664,21 +1613,6 @@ "@types/node" "*" jest-mock "^29.7.0" -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== - dependencies: - jest-get-type "^29.6.3" - -"@jest/expect@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" - integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== - dependencies: - expect "^29.7.0" - jest-snapshot "^29.7.0" - "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -1691,46 +1625,6 @@ jest-mock "^29.7.0" jest-util "^29.7.0" -"@jest/globals@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" - integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/types" "^29.6.3" - jest-mock "^29.7.0" - -"@jest/reporters@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" - integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^6.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - jest-worker "^29.7.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -1738,35 +1632,6 @@ dependencies: "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" - integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.18" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" - integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== - dependencies: - "@jest/console" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" - integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== - dependencies: - "@jest/test-result" "^29.7.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - slash "^3.0.0" - "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -1858,7 +1723,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.28": +"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.28": version "0.3.31" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== @@ -2217,11 +2082,6 @@ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== -"@types/istanbul-lib-coverage@^2.0.1": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" - integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== - "@types/istanbul-lib-report@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" @@ -2236,14 +2096,6 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@29": - version "29.5.14" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" - integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -3123,11 +2975,6 @@ chalk@^2.0.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -3170,11 +3017,6 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.1.tgz#58331f6f472a25fe3a50a351ae3052936c2c7f32" integrity sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg== -cjs-module-lexer@^1.0.0: - version "1.4.3" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" - integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -3241,16 +3083,6 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" - integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -3567,19 +3399,6 @@ cosmiconfig@^9.0.0: js-yaml "^4.1.0" parse-json "^5.2.0" -create-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" - integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-config "^29.7.0" - jest-util "^29.7.0" - prompts "^2.0.1" - cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -3650,13 +3469,6 @@ debug@4, debug@4.3.2, debug@^4.1.0: dependencies: ms "2.1.2" -debug@^4.1.1, debug@^4.4.0, debug@^4.4.1: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" @@ -3664,6 +3476,13 @@ debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "^2.1.3" +debug@^4.4.0, debug@^4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" @@ -3701,11 +3520,6 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -dedent@^1.0.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.1.tgz#364661eea3d73f3faba7089214420ec2f8f13e15" - integrity sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg== - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -3716,11 +3530,6 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -3810,16 +3619,6 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3873,11 +3672,6 @@ electron-to-chromium@^1.5.4: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.24.tgz#b3cd2f71b7a84bac340d862e3b7b0aadf48478de" integrity sha512-0x0wLCmpdKFCi9ulhvYZebgcPmHTkFVUfU2wzDykadkslKwT4oAmDTHEKLnlrDsMGZe4B+ksn8quZfZjYsBetA== -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -4317,7 +4111,7 @@ event-target-shim@^5.0.0, event-target-shim@^5.0.1: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -execa@5.1.1, execa@^5.0.0: +execa@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -4347,22 +4141,6 @@ execa@^4.0.2, execa@^4.0.3: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - -expect@^29.0.0, expect@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - exponential-backoff@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.3.tgz#51cf92c1c0493c766053f9d3abee4434c244d2f6" @@ -4493,7 +4271,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -5050,11 +4828,6 @@ hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: dependencies: lru-cache "^6.0.0" -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -5185,14 +4958,6 @@ import-lazy@^2.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= -import-local@^3.0.2: - version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" - integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -5405,11 +5170,6 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - is-generator-function@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" @@ -5663,11 +5423,6 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.2.tgz#36786d4d82aad2ea5911007e255e2da6b5f80d86" integrity sha512-o5+eTUYzCJ11/+JhW5/FUCdfsdoYVdQ/8I/OveE2XsjehYn5DdeSnNQAbjYaO8gQ6hvGTN6GM6ddQqpTVG5j8g== -istanbul-lib-coverage@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" - integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== - istanbul-lib-instrument@^5.0.4: version "5.0.4" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.0.4.tgz#e976f2aa66ebc6737f236d3ab05b76e36f885c80" @@ -5679,43 +5434,6 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" -istanbul-lib-instrument@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" - integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== - dependencies: - "@babel/core" "^7.23.9" - "@babel/parser" "^7.23.9" - "@istanbuljs/schema" "^0.1.3" - istanbul-lib-coverage "^3.2.0" - semver "^7.5.4" - -istanbul-lib-report@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" - integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^4.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" - integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - iterator.prototype@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" @@ -5727,114 +5445,6 @@ iterator.prototype@^1.1.2: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" -jest-changed-files@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" - integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== - dependencies: - execa "^5.0.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - -jest-circus@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" - integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^1.0.0" - is-generator-fn "^2.0.0" - jest-each "^29.7.0" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - p-limit "^3.1.0" - pretty-format "^29.7.0" - pure-rand "^6.0.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" - integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== - dependencies: - "@jest/core" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - chalk "^4.0.0" - create-jest "^29.7.0" - exit "^0.1.2" - import-local "^3.0.2" - jest-config "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - yargs "^17.3.1" - -jest-config@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" - integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.7.0" - "@jest/types" "^29.6.3" - babel-jest "^29.7.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.7.0" - jest-environment-node "^29.7.0" - jest-get-type "^29.6.3" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-runner "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.7.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-docblock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" - integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" - integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - jest-get-type "^29.6.3" - jest-util "^29.7.0" - pretty-format "^29.7.0" - jest-environment-node@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" @@ -5871,24 +5481,6 @@ jest-haste-map@^29.7.0: optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" - integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== - dependencies: - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -5913,120 +5505,11 @@ jest-mock@^29.7.0: "@types/node" "*" jest-util "^29.7.0" -jest-pnp-resolver@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" - integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== - jest-regex-util@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" - integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== - dependencies: - jest-regex-util "^29.6.3" - jest-snapshot "^29.7.0" - -jest-resolve@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" - integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.7.0" - jest-validate "^29.7.0" - resolve "^1.20.0" - resolve.exports "^2.0.0" - slash "^3.0.0" - -jest-runner@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" - integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== - dependencies: - "@jest/console" "^29.7.0" - "@jest/environment" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.7.0" - jest-environment-node "^29.7.0" - jest-haste-map "^29.7.0" - jest-leak-detector "^29.7.0" - jest-message-util "^29.7.0" - jest-resolve "^29.7.0" - jest-runtime "^29.7.0" - jest-util "^29.7.0" - jest-watcher "^29.7.0" - jest-worker "^29.7.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" - integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/globals" "^29.7.0" - "@jest/source-map" "^29.6.3" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" - integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.7.0" - graceful-fs "^4.2.9" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - natural-compare "^1.4.0" - pretty-format "^29.7.0" - semver "^7.5.3" - jest-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" @@ -6051,20 +5534,6 @@ jest-validate@^29.6.3, jest-validate@^29.7.0: leven "^3.1.0" pretty-format "^29.7.0" -jest-watcher@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" - integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== - dependencies: - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.7.0" - string-length "^4.0.1" - jest-worker@^29.6.3, jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" @@ -6075,16 +5544,6 @@ jest-worker@^29.6.3, jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@29: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" - integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== - dependencies: - "@jest/core" "^29.7.0" - "@jest/types" "^29.6.3" - import-local "^3.0.2" - jest-cli "^29.7.0" - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6372,13 +5831,6 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-dir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" - integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== - dependencies: - semver "^7.5.3" - makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -7270,7 +6722,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2, p-limit@^3.1.0: +p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -7462,13 +6914,6 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - pkg-dir@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" @@ -7530,7 +6975,7 @@ prettier@^2.0.5: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== -pretty-format@^29.0.0, pretty-format@^29.7.0: +pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== @@ -7551,7 +6996,7 @@ promise@^8.3.0: dependencies: asap "~2.0.6" -prompts@^2.0.1, prompts@^2.4.2: +prompts@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== @@ -7602,11 +7047,6 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -pure-rand@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" - integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== - q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -7973,13 +7413,6 @@ resolve-alpn@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -8002,11 +7435,6 @@ resolve-global@1.0.0, resolve-global@^1.0.0: dependencies: global-dirs "^0.1.1" -resolve.exports@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" - integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== - resolve@^1.1.6, resolve@^1.10.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" @@ -8015,7 +7443,7 @@ resolve@^1.1.6, resolve@^1.10.0: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@^1.20.0, resolve@^1.22.10, resolve@^1.22.8: +resolve@^1.22.10, resolve@^1.22.8: version "1.22.11" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== @@ -8174,7 +7602,7 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.1.3, semver@^7.5.3, semver@^7.5.4: +semver@^7.1.3: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -8315,14 +7743,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -8425,14 +7845,6 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - string-natural-compare@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" @@ -8543,11 +7955,6 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -8990,15 +8397,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-to-istanbul@^9.0.1: - version "9.3.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" - integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -9283,7 +8681,7 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1, yargs@^17.5.1, yargs@^17.6.2: +yargs@^17.5.1, yargs@^17.6.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==