@veams/status-quo 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +3 -3
- package/.turbo/turbo-test.log +115 -15
- package/CHANGELOG.md +2 -0
- package/README.md +51 -7
- package/dist/config/status-quo-config.d.ts +22 -1
- package/dist/config/status-quo-config.js +46 -2
- package/dist/config/status-quo-config.js.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.js +22 -2
- package/dist/index.js.map +1 -1
- package/dist/react/hooks/__tests__/state-provider.spec.js +2 -2
- package/dist/react/hooks/__tests__/state-provider.spec.js.map +1 -1
- package/dist/react/hooks/index.d.ts +6 -3
- package/dist/react/hooks/index.js +12 -3
- package/dist/react/hooks/index.js.map +1 -1
- package/dist/react/hooks/state-actions.d.ts +9 -1
- package/dist/react/hooks/state-actions.js +21 -2
- package/dist/react/hooks/state-actions.js.map +1 -1
- package/dist/react/hooks/state-factory.d.ts +7 -0
- package/dist/react/hooks/state-factory.js +23 -1
- package/dist/react/hooks/state-factory.js.map +1 -1
- package/dist/react/hooks/state-handler.d.ts +4 -0
- package/dist/react/hooks/state-handler.js +18 -1
- package/dist/react/hooks/state-handler.js.map +1 -1
- package/dist/react/hooks/state-provider.d.ts +18 -9
- package/dist/react/hooks/state-provider.js +25 -17
- package/dist/react/hooks/state-provider.js.map +1 -1
- package/dist/react/hooks/state-singleton.d.ts +8 -2
- package/dist/react/hooks/state-singleton.js +21 -3
- package/dist/react/hooks/state-singleton.js.map +1 -1
- package/dist/react/hooks/state-subscription-selector.d.ts +4 -0
- package/dist/react/hooks/state-subscription-selector.js +64 -8
- package/dist/react/hooks/state-subscription-selector.js.map +1 -1
- package/dist/react/hooks/state-subscription.d.ts +12 -0
- package/dist/react/hooks/state-subscription.js +49 -1
- package/dist/react/hooks/state-subscription.js.map +1 -1
- package/dist/react/index.d.ts +4 -1
- package/dist/react/index.js +5 -1
- package/dist/react/index.js.map +1 -1
- package/dist/store/__tests__/native-state-handler.spec.d.ts +1 -0
- package/dist/store/__tests__/native-state-handler.spec.js +210 -0
- package/dist/store/__tests__/native-state-handler.spec.js.map +1 -0
- package/dist/store/__tests__/observable-state-handler.spec.d.ts +7 -0
- package/dist/store/__tests__/observable-state-handler.spec.js.map +1 -1
- package/dist/store/base-state-handler.d.ts +42 -0
- package/dist/store/base-state-handler.js +73 -10
- package/dist/store/base-state-handler.js.map +1 -1
- package/dist/store/dev-tools.d.ts +42 -17
- package/dist/store/dev-tools.js +24 -8
- package/dist/store/dev-tools.js.map +1 -1
- package/dist/store/index.d.ts +7 -0
- package/dist/store/index.js +9 -0
- package/dist/store/index.js.map +1 -1
- package/dist/store/native-state-handler.d.ts +44 -0
- package/dist/store/native-state-handler.js +62 -0
- package/dist/store/native-state-handler.js.map +1 -0
- package/dist/store/observable-state-handler.d.ts +34 -0
- package/dist/store/observable-state-handler.js +45 -1
- package/dist/store/observable-state-handler.js.map +1 -1
- package/dist/store/signal-state-handler.d.ts +26 -0
- package/dist/store/signal-state-handler.js +35 -0
- package/dist/store/signal-state-handler.js.map +1 -1
- package/dist/store/state-singleton.d.ts +14 -0
- package/dist/store/state-singleton.js +20 -1
- package/dist/store/state-singleton.js.map +1 -1
- package/dist/types/types.d.ts +9 -0
- package/dist/types/types.js +3 -0
- package/dist/types/types.js.map +1 -1
- package/dist/utils/selector-cache.d.ts +17 -0
- package/dist/utils/selector-cache.js +28 -1
- package/dist/utils/selector-cache.js.map +1 -1
- package/package.json +26 -1
- package/src/config/status-quo-config.ts +64 -1
- package/src/index.ts +31 -2
- package/src/react/hooks/__tests__/state-provider.spec.tsx +2 -2
- package/src/react/hooks/index.ts +13 -8
- package/src/react/hooks/state-actions.tsx +23 -2
- package/src/react/hooks/state-factory.tsx +34 -0
- package/src/react/hooks/state-handler.tsx +15 -0
- package/src/react/hooks/state-provider.tsx +36 -40
- package/src/react/hooks/state-singleton.tsx +37 -7
- package/src/react/hooks/state-subscription-selector.tsx +85 -7
- package/src/react/hooks/state-subscription.tsx +75 -0
- package/src/react/index.ts +16 -1
- package/src/store/__tests__/native-state-handler.spec.ts +291 -0
- package/src/store/__tests__/observable-state-handler.spec.ts +8 -0
- package/src/store/base-state-handler.ts +89 -12
- package/src/store/dev-tools.ts +72 -27
- package/src/store/index.ts +16 -0
- package/src/store/native-state-handler.ts +98 -0
- package/src/store/observable-state-handler.ts +57 -0
- package/src/store/signal-state-handler.ts +47 -1
- package/src/store/state-singleton.ts +30 -0
- package/src/types/types.ts +16 -0
- package/src/utils/selector-cache.ts +37 -0
|
@@ -1,13 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility hook for creating and subscribing to a state handler within a component.
|
|
3
|
+
* Combines creation and subscription logic in a single call.
|
|
4
|
+
*/
|
|
1
5
|
import { useStateHandler } from './state-handler.js';
|
|
2
6
|
import { useStateSubscription } from './state-subscription.js';
|
|
7
|
+
/**
|
|
8
|
+
* Default identity selector returns the whole state.
|
|
9
|
+
*/
|
|
3
10
|
const identitySelector = (state) => state;
|
|
4
|
-
export function useStateFactory(
|
|
11
|
+
export function useStateFactory(
|
|
12
|
+
// Implementation of the overloaded useStateFactory hook.
|
|
13
|
+
stateFactoryFunction,
|
|
14
|
+
// Mixed argument: can be a selector or params array.
|
|
15
|
+
selectorOrParams = [],
|
|
16
|
+
// Mixed argument: can be an equality function or params array.
|
|
17
|
+
isEqualOrParams = Object.is,
|
|
18
|
+
// Optional params array.
|
|
19
|
+
params = []) {
|
|
20
|
+
// Determine whether a selector was provided.
|
|
5
21
|
const hasSelector = typeof selectorOrParams === 'function';
|
|
22
|
+
// Fallback to identity selector if none is provided.
|
|
6
23
|
const selector = (hasSelector ? selectorOrParams : identitySelector);
|
|
24
|
+
// Determine whether a custom equality function was provided.
|
|
7
25
|
const hasCustomEquality = hasSelector && typeof isEqualOrParams === 'function';
|
|
26
|
+
// Fallback to default equality check (Object.is).
|
|
8
27
|
const isEqual = (hasCustomEquality ? isEqualOrParams : Object.is);
|
|
28
|
+
// Resolve the parameters for the state handler factory.
|
|
9
29
|
const stateFactoryParams = (hasSelector ? (hasCustomEquality ? params : isEqualOrParams) : selectorOrParams);
|
|
30
|
+
// Create and persist the state handler instance using its factory and parameters.
|
|
10
31
|
const stateHandler = useStateHandler(stateFactoryFunction, stateFactoryParams);
|
|
32
|
+
// Subscribe to the state handler and return the selected state and actions.
|
|
11
33
|
return useStateSubscription(stateHandler, selector, isEqual);
|
|
12
34
|
}
|
|
13
35
|
//# sourceMappingURL=state-factory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-factory.js","sourceRoot":"","sources":["../../../src/react/hooks/state-factory.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"state-factory.js","sourceRoot":"","sources":["../../../src/react/hooks/state-factory.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAU/D;;GAEG;AACH,MAAM,gBAAgB,GAAG,CAAS,KAAY,EAAE,EAAE,CAAC,KAAK,CAAC;AA8BzD,MAAM,UAAU,eAAe;AAC7B,yDAAyD;AACzD,oBAAoE;AACpE,qDAAqD;AACrD,mBAA8C,EAAkB;AAChE,+DAA+D;AAC/D,kBAAuC,MAAM,CAAC,EAAqB;AACnE,yBAAyB;AACzB,SAAY,EAAkB;IAE9B,6CAA6C;IAC7C,MAAM,WAAW,GAAG,OAAO,gBAAgB,KAAK,UAAU,CAAC;IAC3D,qDAAqD;IACrD,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAA0B,CAAC;IAC9F,6DAA6D;IAC7D,MAAM,iBAAiB,GAAG,WAAW,IAAI,OAAO,eAAe,KAAK,UAAU,CAAC;IAC/E,kDAAkD;IAClD,MAAM,OAAO,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClE,wDAAwD;IACxD,MAAM,kBAAkB,GAAG,CACzB,WAAW,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAC3E,CAAC;IACP,kFAAkF;IAClF,MAAM,YAAY,GAAG,eAAe,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAC;IAE/E,4EAA4E;IAC5E,OAAO,oBAAoB,CAAC,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { StateSubscriptionHandler } from '../../types/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a stable state handler instance based on a factory function.
|
|
4
|
+
* Uses a ref to ensure the factory function is only executed during initial render.
|
|
5
|
+
*/
|
|
2
6
|
export declare function useStateHandler<V, A, P extends unknown[]>(stateFactoryFunction: (...args: P) => StateSubscriptionHandler<V, A>, params?: P): StateSubscriptionHandler<V, A>;
|
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility hook for managing the lifecycle of a state handler instance.
|
|
3
|
+
* Ensures the instance is created once and persisted across component re-renders.
|
|
4
|
+
*/
|
|
1
5
|
import { useRef } from 'react';
|
|
2
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Returns a stable state handler instance based on a factory function.
|
|
8
|
+
* Uses a ref to ensure the factory function is only executed during initial render.
|
|
9
|
+
*/
|
|
10
|
+
export function useStateHandler(
|
|
11
|
+
// Function to create a new state handler instance.
|
|
12
|
+
stateFactoryFunction,
|
|
13
|
+
// Parameters to pass to the factory function.
|
|
14
|
+
params = []) {
|
|
15
|
+
// Use a ref to store the state handler instance.
|
|
16
|
+
// This prevents the state handler from being recreated on every re-render.
|
|
3
17
|
const stateHandlerRef = useRef(null);
|
|
18
|
+
// If the ref is currently null, we create the instance for the first time.
|
|
4
19
|
if (!stateHandlerRef.current) {
|
|
20
|
+
// Invoke the factory function with the provided parameters.
|
|
5
21
|
stateHandlerRef.current = stateFactoryFunction(...params);
|
|
6
22
|
}
|
|
23
|
+
// Return the stable state handler instance.
|
|
7
24
|
return stateHandlerRef.current;
|
|
8
25
|
}
|
|
9
26
|
//# sourceMappingURL=state-handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-handler.js","sourceRoot":"","sources":["../../../src/react/hooks/state-handler.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAI/B,MAAM,UAAU,eAAe,
|
|
1
|
+
{"version":3,"file":"state-handler.js","sourceRoot":"","sources":["../../../src/react/hooks/state-handler.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAI/B;;;GAGG;AACH,MAAM,UAAU,eAAe;AAC7B,mDAAmD;AACnD,oBAAoE;AACpE,8CAA8C;AAC9C,SAAY,EAAkB;IAE9B,iDAAiD;IACjD,2EAA2E;IAC3E,MAAM,eAAe,GAAG,MAAM,CAAwC,IAAI,CAAC,CAAC;IAE5E,2EAA2E;IAC3E,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;QAC7B,4DAA4D;QAC5D,eAAe,CAAC,OAAO,GAAG,oBAAoB,CAAC,GAAG,MAAM,CAAC,CAAC;IAC5D,CAAC;IAED,4CAA4C;IAC5C,OAAO,eAAe,CAAC,OAAO,CAAC;AACjC,CAAC"}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility hook for providing a state handler through React Context.
|
|
3
|
+
* Allows components deep in the component tree to access a common state handler instance.
|
|
4
|
+
*/
|
|
1
5
|
import React from 'react';
|
|
2
|
-
import type { PropsWithChildren } from 'react';
|
|
3
6
|
import type { StateSubscriptionHandler } from '../../types/types.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Interface for the state provider component props.
|
|
9
|
+
*/
|
|
10
|
+
interface StateProviderProps<V, A> {
|
|
11
|
+
children: React.ReactNode;
|
|
7
12
|
instance: StateSubscriptionHandler<V, A>;
|
|
8
|
-
}
|
|
9
|
-
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Provides a state handler instance to its descendant components using React Context.
|
|
16
|
+
*/
|
|
10
17
|
export declare function StateProvider<V, A>({ children, instance }: StateProviderProps<V, A>): React.JSX.Element;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Custom hook to access the state handler provided by the StateProvider.
|
|
20
|
+
* Throws an error if the hook is used outside of a StateProvider.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useProvidedStateHandler<V, A>(): StateSubscriptionHandler<V, A>;
|
|
14
23
|
export {};
|
|
@@ -1,24 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility hook for providing a state handler through React Context.
|
|
3
|
+
* Allows components deep in the component tree to access a common state handler instance.
|
|
4
|
+
*/
|
|
1
5
|
import React, { createContext, useContext } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Creates a React Context for storing and providing the state handler instance.
|
|
8
|
+
* Initialized with null as there is no default state handler.
|
|
9
|
+
*/
|
|
10
|
+
const StateContext = createContext(null);
|
|
11
|
+
/**
|
|
12
|
+
* Provides a state handler instance to its descendant components using React Context.
|
|
13
|
+
*/
|
|
14
|
+
export function StateProvider({ children, instance }) {
|
|
15
|
+
// Use a context provider to share the state handler instance.
|
|
16
|
+
return (React.createElement(StateContext.Provider, { value: instance }, children));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Custom hook to access the state handler provided by the StateProvider.
|
|
20
|
+
* Throws an error if the hook is used outside of a StateProvider.
|
|
21
|
+
*/
|
|
6
22
|
export function useProvidedStateHandler() {
|
|
7
|
-
|
|
23
|
+
// Retrieve the state handler from the nearest context provider.
|
|
24
|
+
const stateHandler = useContext(StateContext);
|
|
25
|
+
// If no state handler is found, it means the hook is being used incorrectly.
|
|
8
26
|
if (!stateHandler) {
|
|
9
|
-
throw new Error('
|
|
27
|
+
throw new Error('useProvidedStateHandler must be used within a StateProvider');
|
|
10
28
|
}
|
|
29
|
+
// Cast and return the state handler instance.
|
|
11
30
|
return stateHandler;
|
|
12
31
|
}
|
|
13
|
-
export function StateProvider({ children, instance }) {
|
|
14
|
-
return (React.createElement(StateProviderContext.Provider, { value: instance }, children));
|
|
15
|
-
}
|
|
16
|
-
export function useProvidedStateActions() {
|
|
17
|
-
const stateHandler = useProvidedStateHandler();
|
|
18
|
-
return useStateActions(stateHandler);
|
|
19
|
-
}
|
|
20
|
-
export function useProvidedStateSubscription(selector = identitySelector, isEqual = Object.is) {
|
|
21
|
-
const stateHandler = useProvidedStateHandler();
|
|
22
|
-
return useStateSubscription(stateHandler, selector, isEqual);
|
|
23
|
-
}
|
|
24
32
|
//# sourceMappingURL=state-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-provider.js","sourceRoot":"","sources":["../../../src/react/hooks/state-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"state-provider.js","sourceRoot":"","sources":["../../../src/react/hooks/state-provider.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAczD;;;GAGG;AACH,MAAM,YAAY,GAAG,aAAa,CAAoD,IAAI,CAAC,CAAC;AAE5F;;GAEG;AACH,MAAM,UAAU,aAAa,CAAO,EAAE,QAAQ,EAAE,QAAQ,EAA4B;IAClF,8DAA8D;IAC9D,OAAO,CACL,oBAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,QAAsD,IACjF,QAAQ,CACa,CACzB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,gEAAgE;IAChE,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IAE9C,6EAA6E;IAC7E,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IAED,8CAA8C;IAC9C,OAAO,YAA8C,CAAC;AACxD,CAAC"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { StateSingleton } from '../../store/state-singleton.js';
|
|
2
|
+
/**
|
|
3
|
+
* Type signatures for selector and equality functions.
|
|
4
|
+
*/
|
|
2
5
|
type StateSelector<State, SelectedState> = (state: State) => SelectedState;
|
|
3
6
|
type EqualityFn<SelectedState> = (current: SelectedState, next: SelectedState) => boolean;
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Singleton hook to subscribe to a state singleton.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useStateSingleton<V, A>(singleton: StateSingleton<V, A>): [V, A];
|
|
11
|
+
export declare function useStateSingleton<V, A, Sel>(singleton: StateSingleton<V, A>, selector: StateSelector<V, Sel>, isEqual?: EqualityFn<Sel>): [Sel, A];
|
|
6
12
|
export {};
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility hook for subscribing to a state singleton within a component.
|
|
3
|
+
*/
|
|
1
4
|
import { useStateSubscription } from './state-subscription.js';
|
|
5
|
+
/**
|
|
6
|
+
* Default identity selector returns the whole state.
|
|
7
|
+
*/
|
|
2
8
|
const identitySelector = (state) => state;
|
|
3
|
-
export function useStateSingleton(
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
export function useStateSingleton(
|
|
10
|
+
// Implementation of the overloaded useStateSingleton hook.
|
|
11
|
+
singleton,
|
|
12
|
+
// Mixed argument: can be a selector or equality function.
|
|
13
|
+
selectorOrIsEqual = identitySelector,
|
|
14
|
+
// Mixed argument: can be an equality function or undefined.
|
|
15
|
+
isEqual = Object.is) {
|
|
16
|
+
// Determine whether a selector was provided as the first optional argument.
|
|
17
|
+
const hasSelector = typeof selectorOrIsEqual === 'function' && selectorOrIsEqual.length === 1;
|
|
18
|
+
// Fallback to identity selector if none is provided.
|
|
19
|
+
const selector = (hasSelector ? selectorOrIsEqual : identitySelector);
|
|
20
|
+
// Resolve the equality function to use.
|
|
21
|
+
const equalityFn = (hasSelector ? isEqual : selectorOrIsEqual);
|
|
22
|
+
// Subscribe to the singleton state handler and return the selected state and actions.
|
|
23
|
+
return useStateSubscription(singleton, selector, equalityFn);
|
|
6
24
|
}
|
|
7
25
|
//# sourceMappingURL=state-singleton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-singleton.js","sourceRoot":"","sources":["../../../src/react/hooks/state-singleton.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"state-singleton.js","sourceRoot":"","sources":["../../../src/react/hooks/state-singleton.tsx"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAU/D;;GAEG;AACH,MAAM,gBAAgB,GAAG,CAAS,KAAY,EAAE,EAAE,CAAC,KAAK,CAAC;AAiBzD,MAAM,UAAU,iBAAiB;AAC/B,2DAA2D;AAC3D,SAA+B;AAC/B,0DAA0D;AAC1D,oBAA6D,gBAG5D;AACD,4DAA4D;AAC5D,UAA2B,MAAM,CAAC,EAAqB;IAEvD,4EAA4E;IAC5E,MAAM,WAAW,GAAG,OAAO,iBAAiB,KAAK,UAAU,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,CAAC;IAC9F,qDAAqD;IACrD,MAAM,QAAQ,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,gBAAgB,CAA0B,CAAC;IAC/F,wCAAwC;IACxC,MAAM,UAAU,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAoB,CAAC;IAElF,sFAAsF;IACtF,OAAO,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { StateSubscriptionHandler } from '../../types/types.js';
|
|
2
2
|
import type { EqualityFn, Selector } from '../../utils/selector-cache.js';
|
|
3
|
+
/**
|
|
4
|
+
* Custom hook to select and subscribe to a specific piece of state from a handler.
|
|
5
|
+
* Efficiently manages subscriptions and selector results.
|
|
6
|
+
*/
|
|
3
7
|
export declare function useStateSubscriptionSelector<V, A, Sel>(stateSubscriptionHandler: StateSubscriptionHandler<V, A>, selector: Selector<V, Sel>, isEqual?: EqualityFn<Sel>, destroyOnCleanup?: boolean): Sel;
|
|
@@ -1,11 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility hook for subscribing to a state handler and selecting a specific value from the state.
|
|
3
|
+
* Uses useSyncExternalStore to ensure consistent state reads and avoid unnecessary re-renders.
|
|
4
|
+
*/
|
|
1
5
|
import { useCallback, useRef, useSyncExternalStore } from 'react';
|
|
2
6
|
import { createSelectorCache, selectWithCache } from '../../utils/selector-cache.js';
|
|
7
|
+
// Global map to track deferred destruction status for each state handler instance.
|
|
3
8
|
const deferredDestroyMap = new WeakMap();
|
|
9
|
+
/**
|
|
10
|
+
* Returns the deferred destruction status for a given state handler instance.
|
|
11
|
+
* Initializes the status if it does not already exist.
|
|
12
|
+
*/
|
|
4
13
|
function getDeferredDestroyState(stateSubscriptionHandler) {
|
|
14
|
+
// Retrieve the existing status from the map.
|
|
5
15
|
const existingState = deferredDestroyMap.get(stateSubscriptionHandler);
|
|
16
|
+
// If status already exists, return it.
|
|
6
17
|
if (existingState) {
|
|
7
18
|
return existingState;
|
|
8
19
|
}
|
|
20
|
+
// Create and store a new status for the handler.
|
|
9
21
|
const nextState = {
|
|
10
22
|
refCount: 0,
|
|
11
23
|
timeoutId: null,
|
|
@@ -13,102 +25,146 @@ function getDeferredDestroyState(stateSubscriptionHandler) {
|
|
|
13
25
|
deferredDestroyMap.set(stateSubscriptionHandler, nextState);
|
|
14
26
|
return nextState;
|
|
15
27
|
}
|
|
16
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Custom hook to select and subscribe to a specific piece of state from a handler.
|
|
30
|
+
* Efficiently manages subscriptions and selector results.
|
|
31
|
+
*/
|
|
32
|
+
export function useStateSubscriptionSelector(
|
|
33
|
+
// The state handler instance to subscribe to.
|
|
34
|
+
stateSubscriptionHandler,
|
|
35
|
+
// Selector function to derive a value from the state.
|
|
36
|
+
selector,
|
|
37
|
+
// Equality function to compare selected values for changes.
|
|
38
|
+
isEqual = Object.is,
|
|
39
|
+
// Whether to automatically destroy the handler instance on component unmount.
|
|
40
|
+
destroyOnCleanup = true) {
|
|
41
|
+
// Cache for the selector results to ensure referential stability.
|
|
17
42
|
const selectorCacheRef = useRef(null);
|
|
18
|
-
// Tracks store notifications so getSnapshot can reuse the same selected value
|
|
19
|
-
// within one store version. This keeps useSyncExternalStore reads referentially stable.
|
|
43
|
+
// Tracks store notifications so getSnapshot can reuse the same selected value within one store version.
|
|
20
44
|
const snapshotVersionRef = useRef(0);
|
|
21
45
|
// Client-side cache for selected snapshots per source snapshot/version pair.
|
|
22
46
|
const snapshotCacheRef = useRef(null);
|
|
23
47
|
// Separate cache for the server snapshot function used by SSR/hydration paths.
|
|
24
48
|
const serverSnapshotCacheRef = useRef(null);
|
|
49
|
+
// Initialize the selector cache if it doesn't already exist.
|
|
25
50
|
if (!selectorCacheRef.current) {
|
|
26
51
|
selectorCacheRef.current = createSelectorCache();
|
|
27
52
|
}
|
|
28
53
|
const selectorCache = selectorCacheRef.current;
|
|
54
|
+
// Subscription function to be used by useSyncExternalStore.
|
|
29
55
|
const subscribe = useCallback((listener) => {
|
|
56
|
+
// Access the deferred destruction status for this handler.
|
|
30
57
|
const sharedStateSubscriptionHandler = stateSubscriptionHandler;
|
|
31
58
|
const deferredDestroyState = getDeferredDestroyState(sharedStateSubscriptionHandler);
|
|
59
|
+
// Increment the consumer reference count.
|
|
32
60
|
deferredDestroyState.refCount += 1;
|
|
61
|
+
// If a pending destruction timeout is scheduled, cancel it.
|
|
33
62
|
if (deferredDestroyState.timeoutId) {
|
|
34
63
|
clearTimeout(deferredDestroyState.timeoutId);
|
|
35
64
|
deferredDestroyState.timeoutId = null;
|
|
36
65
|
}
|
|
66
|
+
// Subscribe to the state handler.
|
|
37
67
|
const unsubscribe = stateSubscriptionHandler.subscribe(() => {
|
|
38
|
-
// Invalidate the selected snapshot cache before notifying React.
|
|
39
|
-
// Any next getSnapshot call should recompute from the new store state.
|
|
68
|
+
// Invalidate the selected snapshot cache before notifying React of a change.
|
|
40
69
|
snapshotVersionRef.current += 1;
|
|
41
70
|
snapshotCacheRef.current = null;
|
|
71
|
+
// Notify React to re-trigger a getSnapshot call.
|
|
42
72
|
listener();
|
|
43
73
|
});
|
|
74
|
+
// Return an unsubscribe function to be called by React.
|
|
44
75
|
return () => {
|
|
76
|
+
// Execute the handler's unsubscribe method.
|
|
45
77
|
unsubscribe();
|
|
78
|
+
// If automatic cleanup is disabled, stop here.
|
|
46
79
|
if (!destroyOnCleanup) {
|
|
47
80
|
return;
|
|
48
81
|
}
|
|
82
|
+
// Retrieve the current destruction status.
|
|
49
83
|
const activeDeferredDestroyState = deferredDestroyMap.get(sharedStateSubscriptionHandler);
|
|
84
|
+
// If no status is found, stop here.
|
|
50
85
|
if (!activeDeferredDestroyState) {
|
|
51
86
|
return;
|
|
52
87
|
}
|
|
88
|
+
// Decrement the consumer reference count.
|
|
53
89
|
activeDeferredDestroyState.refCount -= 1;
|
|
90
|
+
// If there are still active consumers, do not destroy the handler.
|
|
54
91
|
if (activeDeferredDestroyState.refCount > 0) {
|
|
55
92
|
return;
|
|
56
93
|
}
|
|
94
|
+
// Reset the reference count to zero.
|
|
57
95
|
activeDeferredDestroyState.refCount = 0;
|
|
96
|
+
// Schedule deferred destruction to allow for potential immediate re-subscriptions.
|
|
58
97
|
activeDeferredDestroyState.timeoutId = setTimeout(() => {
|
|
98
|
+
// Check if the handler still has no consumers after the timeout.
|
|
59
99
|
const pendingDeferredDestroyState = deferredDestroyMap.get(sharedStateSubscriptionHandler);
|
|
100
|
+
// If consumers have reappeared, do not destroy the handler.
|
|
60
101
|
if (!pendingDeferredDestroyState || pendingDeferredDestroyState.refCount > 0) {
|
|
61
102
|
return;
|
|
62
103
|
}
|
|
104
|
+
// Clear the pending timeout and destroy the state handler.
|
|
63
105
|
pendingDeferredDestroyState.timeoutId = null;
|
|
64
106
|
stateSubscriptionHandler.destroy();
|
|
107
|
+
// Remove the status from the global map.
|
|
65
108
|
deferredDestroyMap.delete(sharedStateSubscriptionHandler);
|
|
66
109
|
}, 0);
|
|
67
110
|
};
|
|
68
111
|
}, [destroyOnCleanup, stateSubscriptionHandler]);
|
|
112
|
+
// Helper to execute selection using the cache.
|
|
69
113
|
const selectSnapshot = useCallback((snapshot) => {
|
|
70
114
|
return selectWithCache(selectorCache, snapshot, selector, isEqual).value;
|
|
71
115
|
}, [isEqual, selector, selectorCache]);
|
|
116
|
+
// Reference to track changes in selection strategy.
|
|
72
117
|
const selectorCacheControlRef = useRef(selectSnapshot);
|
|
118
|
+
// If the selector or equality function changes, clear all caches.
|
|
73
119
|
if (selectorCacheControlRef.current !== selectSnapshot) {
|
|
74
|
-
// Selector/equality changes define a new selection strategy, so clear all caches.
|
|
75
120
|
selectorCacheControlRef.current = selectSnapshot;
|
|
76
121
|
snapshotVersionRef.current = 0;
|
|
77
122
|
snapshotCacheRef.current = null;
|
|
78
123
|
serverSnapshotCacheRef.current = null;
|
|
79
124
|
}
|
|
125
|
+
// Snapshot retrieval function to be used by useSyncExternalStore.
|
|
80
126
|
const getSnapshot = useCallback(() => {
|
|
127
|
+
// Retrieve the current source state from the handler.
|
|
81
128
|
const sourceSnapshot = stateSubscriptionHandler.getSnapshot();
|
|
129
|
+
// Access the current version and cached value.
|
|
82
130
|
const version = snapshotVersionRef.current;
|
|
83
131
|
const cachedSnapshot = snapshotCacheRef.current;
|
|
132
|
+
// If the cached selection is still valid for this version and source state, return it.
|
|
84
133
|
if (cachedSnapshot &&
|
|
85
134
|
cachedSnapshot.version === version &&
|
|
86
135
|
Object.is(cachedSnapshot.sourceSnapshot, sourceSnapshot)) {
|
|
87
|
-
// Same source snapshot in the same store version: return the exact same selected reference.
|
|
88
136
|
return cachedSnapshot.selectedSnapshot;
|
|
89
137
|
}
|
|
138
|
+
// Compute a new selection and update the cache.
|
|
90
139
|
const selectedSnapshot = selectSnapshot(sourceSnapshot);
|
|
91
140
|
snapshotCacheRef.current = {
|
|
92
141
|
selectedSnapshot,
|
|
93
142
|
sourceSnapshot,
|
|
94
143
|
version,
|
|
95
144
|
};
|
|
145
|
+
// Return the new selection.
|
|
96
146
|
return selectedSnapshot;
|
|
97
147
|
}, [selectSnapshot, stateSubscriptionHandler]);
|
|
148
|
+
// Server snapshot retrieval function to be used for SSR/hydration.
|
|
98
149
|
const getServerSnapshot = useCallback(() => {
|
|
150
|
+
// Retrieve the initial source state from the handler.
|
|
99
151
|
const sourceSnapshot = stateSubscriptionHandler.getInitialState();
|
|
152
|
+
// Access the current cached server snapshot.
|
|
100
153
|
const cachedSnapshot = serverSnapshotCacheRef.current;
|
|
154
|
+
// If the cached server selection is still valid for the initial state, return it.
|
|
101
155
|
if (cachedSnapshot && Object.is(cachedSnapshot.sourceSnapshot, sourceSnapshot)) {
|
|
102
|
-
// Keep server snapshot reads stable for hydration by reusing cached selection.
|
|
103
156
|
return cachedSnapshot.selectedSnapshot;
|
|
104
157
|
}
|
|
158
|
+
// Compute a new server selection and update its cache.
|
|
105
159
|
const selectedSnapshot = selectSnapshot(sourceSnapshot);
|
|
106
160
|
serverSnapshotCacheRef.current = {
|
|
107
161
|
selectedSnapshot,
|
|
108
162
|
sourceSnapshot,
|
|
109
163
|
};
|
|
164
|
+
// Return the initial selection result.
|
|
110
165
|
return selectedSnapshot;
|
|
111
166
|
}, [selectSnapshot, stateSubscriptionHandler]);
|
|
167
|
+
// Use the useSyncExternalStore hook to integrate the state with React's rendering lifecycle.
|
|
112
168
|
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
113
169
|
}
|
|
114
170
|
//# sourceMappingURL=state-subscription-selector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-subscription-selector.js","sourceRoot":"","sources":["../../../src/react/hooks/state-subscription-selector.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"state-subscription-selector.js","sourceRoot":"","sources":["../../../src/react/hooks/state-subscription-selector.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import type { StateSingleton } from '../../store/state-singleton.js';
|
|
2
2
|
import type { StateSubscriptionHandler } from '../../types/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Type signatures for selector and equality functions.
|
|
5
|
+
*/
|
|
3
6
|
type StateSelector<State, SelectedState> = (state: State) => SelectedState;
|
|
4
7
|
type EqualityFn<SelectedState> = (current: SelectedState, next: SelectedState) => boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Custom hook to subscribe to a state handler or singleton and receive its state and actions.
|
|
10
|
+
* Correctly manages shared instances and reference counting.
|
|
11
|
+
*/
|
|
5
12
|
export declare function useStateSubscription<V, A>(source: StateSubscriptionHandler<V, A>): [V, A];
|
|
6
13
|
export declare function useStateSubscription<V, A, Sel>(source: StateSubscriptionHandler<V, A>, selector: StateSelector<V, Sel>, isEqual?: EqualityFn<Sel>): [Sel, A];
|
|
7
14
|
export declare function useStateSubscription<V, A>(source: StateSingleton<V, A>): [V, A];
|
|
8
15
|
export declare function useStateSubscription<V, A, Sel>(source: StateSingleton<V, A>, selector: StateSelector<V, Sel>, isEqual?: EqualityFn<Sel>): [Sel, A];
|
|
16
|
+
/**
|
|
17
|
+
* Custom hook to subscribe to the state handler provided by the nearest StateProvider.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useProvidedStateSubscription<V, A>(): [V, A];
|
|
20
|
+
export declare function useProvidedStateSubscription<V, A, Sel>(selector: StateSelector<V, Sel>, isEqual?: EqualityFn<Sel>): [Sel, A];
|
|
9
21
|
export {};
|
|
@@ -1,53 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility hook for subscribing to a state handler or a state singleton.
|
|
3
|
+
* Manages the lifecycle and reference counting for shared state handler instances.
|
|
4
|
+
*/
|
|
1
5
|
import { useEffect, useMemo } from 'react';
|
|
2
6
|
import { useStateActions } from './state-actions.js';
|
|
3
7
|
import { useStateSubscriptionSelector } from './state-subscription-selector.js';
|
|
8
|
+
import { useProvidedStateHandler } from './state-provider.js';
|
|
9
|
+
/**
|
|
10
|
+
* Global map to track reference counts for singleton state handler instances.
|
|
11
|
+
* Used to determine when it's safe to destroy a shared singleton.
|
|
12
|
+
*/
|
|
4
13
|
const singletonReferences = new WeakMap();
|
|
14
|
+
/**
|
|
15
|
+
* Default identity selector returns the whole state.
|
|
16
|
+
*/
|
|
5
17
|
const identitySelector = (state) => state;
|
|
18
|
+
/**
|
|
19
|
+
* Type guard function to check if a source is a StateSingleton.
|
|
20
|
+
*/
|
|
6
21
|
function isStateSingleton(source) {
|
|
22
|
+
// Check for the presence of the getInstance method.
|
|
7
23
|
return 'getInstance' in source;
|
|
8
24
|
}
|
|
9
|
-
export function useStateSubscription(
|
|
25
|
+
export function useStateSubscription(
|
|
26
|
+
// Implementation of the overloaded useStateSubscription hook.
|
|
27
|
+
source,
|
|
28
|
+
// Selector function to derive a specific value from the state.
|
|
29
|
+
selector = identitySelector,
|
|
30
|
+
// Optional equality function to compare selected values for changes.
|
|
31
|
+
isEqual = Object.is) {
|
|
32
|
+
// Determine if the source is a singleton instance or a direct state handler.
|
|
10
33
|
const singletonSource = isStateSingleton(source) ? source : null;
|
|
34
|
+
// Resolve the final state subscription handler instance to use.
|
|
11
35
|
const stateSubscriptionHandler = useMemo(() => {
|
|
36
|
+
// If it's a singleton, access its managed instance.
|
|
12
37
|
if (singletonSource) {
|
|
13
38
|
return singletonSource.getInstance();
|
|
14
39
|
}
|
|
40
|
+
// Otherwise, return the source handler instance directly.
|
|
15
41
|
return source;
|
|
16
42
|
}, [singletonSource, source]);
|
|
43
|
+
// Use an effect to manage the lifecycle and reference count of singleton instances.
|
|
17
44
|
useEffect(() => {
|
|
45
|
+
// If the source is not a singleton, no lifecycle management is needed here.
|
|
18
46
|
if (!singletonSource) {
|
|
19
47
|
return undefined;
|
|
20
48
|
}
|
|
49
|
+
// Cast the source to access management properties and retrieve the current reference.
|
|
21
50
|
const singleton = singletonSource;
|
|
22
51
|
const sharedStateHandler = stateSubscriptionHandler;
|
|
23
52
|
const singletonReference = singletonReferences.get(singleton);
|
|
53
|
+
// Update the reference count for the singleton instance.
|
|
24
54
|
if (!singletonReference || singletonReference.stateHandler !== sharedStateHandler) {
|
|
55
|
+
// Initialize the count if it doesn't already exist or has changed.
|
|
25
56
|
singletonReferences.set(singleton, { count: 1, stateHandler: sharedStateHandler });
|
|
26
57
|
}
|
|
27
58
|
else {
|
|
59
|
+
// Increment the consumer count.
|
|
28
60
|
singletonReference.count += 1;
|
|
29
61
|
}
|
|
62
|
+
// Return an effect cleanup function to decrement the count when the component unmounts.
|
|
30
63
|
return () => {
|
|
64
|
+
// Access the active reference for the singleton.
|
|
31
65
|
const activeReference = singletonReferences.get(singleton);
|
|
66
|
+
// If no active reference is found, do nothing.
|
|
32
67
|
if (!activeReference || activeReference.stateHandler !== sharedStateHandler) {
|
|
33
68
|
return;
|
|
34
69
|
}
|
|
70
|
+
// Decrement the consumer count.
|
|
35
71
|
activeReference.count -= 1;
|
|
72
|
+
// If there are still active consumers, do not destroy the instance.
|
|
36
73
|
if (activeReference.count <= 0) {
|
|
74
|
+
// Remove the reference from the map when the count reaches zero.
|
|
37
75
|
singletonReferences.delete(singleton);
|
|
76
|
+
// Only proceed with destruction if destroyOnNoConsumers is explicitly enabled.
|
|
38
77
|
if (singleton.destroyOnNoConsumers !== true) {
|
|
39
78
|
return;
|
|
40
79
|
}
|
|
80
|
+
// Use the singleton's internal destroy method if available.
|
|
41
81
|
if (singleton.destroyInstance) {
|
|
42
82
|
singleton.destroyInstance();
|
|
43
83
|
return;
|
|
44
84
|
}
|
|
85
|
+
// Otherwise, invoke the handler's destroy method directly.
|
|
45
86
|
stateSubscriptionHandler.destroy();
|
|
46
87
|
}
|
|
47
88
|
};
|
|
48
89
|
}, [singletonSource, stateSubscriptionHandler]);
|
|
90
|
+
// Select and subscribe to the state value using the useStateSubscriptionSelector hook.
|
|
49
91
|
const state = useStateSubscriptionSelector(stateSubscriptionHandler, selector, isEqual, !singletonSource);
|
|
92
|
+
// Retrieve the set of actions from the state handler.
|
|
50
93
|
const actions = useStateActions(stateSubscriptionHandler);
|
|
94
|
+
// Return the selected state and its actions as a tuple.
|
|
51
95
|
return [state, actions];
|
|
52
96
|
}
|
|
97
|
+
export function useProvidedStateSubscription(selector = identitySelector, isEqual = Object.is) {
|
|
98
|
+
const stateHandler = useProvidedStateHandler();
|
|
99
|
+
return useStateSubscription(stateHandler, selector, isEqual);
|
|
100
|
+
}
|
|
53
101
|
//# sourceMappingURL=state-subscription.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-subscription.js","sourceRoot":"","sources":["../../../src/react/hooks/state-subscription.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"state-subscription.js","sourceRoot":"","sources":["../../../src/react/hooks/state-subscription.tsx"],"names":[],"mappings":"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"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Export core React hooks and components for Status Quo.
|
|
3
|
+
*/
|
|
4
|
+
export { StateProvider, useProvidedStateActions, useProvidedStateHandler, useProvidedStateSubscription, useStateActions, useStateFactory, useStateHandler, useStateSingleton, useStateSubscription, } from './hooks/index.js';
|
package/dist/react/index.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Export core React hooks and components for Status Quo.
|
|
3
|
+
*/
|
|
4
|
+
// Export hooks and components to manage state and actions.
|
|
5
|
+
export { StateProvider, useProvidedStateActions, useProvidedStateHandler, useProvidedStateSubscription, useStateActions, useStateFactory, useStateHandler, useStateSingleton, useStateSubscription, } from './hooks/index.js';
|
|
2
6
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,2DAA2D;AAC3D,OAAO,EACL,aAAa,EACb,uBAAuB,EACvB,uBAAuB,EACvB,4BAA4B,EAC5B,eAAe,EACf,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|