muya 1.0.3 → 2.0.0-beta.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/README.md +194 -216
- package/cjs/index.js +1 -1
- package/esm/__tests__/create-async.test.js +1 -0
- package/esm/create.js +1 -1
- package/esm/index.js +1 -1
- package/esm/subscriber.js +1 -0
- package/esm/types.js +1 -1
- package/esm/use.js +1 -0
- package/esm/utils/__tests__/context.test.js +1 -0
- package/esm/utils/__tests__/is.test.js +1 -0
- package/esm/utils/__tests__/sub-memo.test.js +1 -0
- package/esm/utils/common.js +1 -0
- package/esm/utils/create-context.js +1 -0
- package/esm/utils/create-emitter.js +1 -0
- package/esm/utils/is.js +1 -0
- package/esm/utils/scheduler.js +1 -0
- package/esm/utils/shallow.js +1 -0
- package/esm/utils/sub-memo.js +1 -0
- package/package.json +1 -4
- package/packages/core/__tests__/bench.test.tsx +261 -0
- package/packages/core/__tests__/create-async.test.ts +88 -0
- package/packages/core/__tests__/create.test.tsx +107 -0
- package/packages/core/__tests__/use-async.test.tsx +44 -0
- package/packages/core/__tests__/use.test.tsx +76 -0
- package/packages/core/create.ts +67 -0
- package/packages/core/index.ts +4 -0
- package/packages/core/subscriber.ts +121 -0
- package/packages/core/types.ts +15 -0
- package/packages/core/use.ts +59 -0
- package/packages/core/utils/__tests__/context.test.ts +198 -0
- package/{src → packages/core/utils}/__tests__/is.test.ts +1 -30
- package/packages/core/utils/__tests__/sub-memo.test.ts +13 -0
- package/packages/core/utils/common.ts +48 -0
- package/packages/core/utils/create-context.ts +60 -0
- package/packages/core/utils/create-emitter.ts +53 -0
- package/{src → packages/core/utils}/is.ts +11 -13
- package/packages/core/utils/scheduler.ts +59 -0
- package/{src → packages/core/utils}/shallow.ts +3 -3
- package/packages/core/utils/sub-memo.ts +37 -0
- package/types/create.d.ts +14 -21
- package/types/index.d.ts +2 -4
- package/types/subscriber.d.ts +25 -0
- package/types/types.d.ts +9 -65
- package/types/use.d.ts +2 -0
- package/types/utils/common.d.ts +15 -0
- package/types/utils/create-context.d.ts +5 -0
- package/types/utils/create-emitter.d.ts +20 -0
- package/types/{is.d.ts → utils/is.d.ts} +5 -6
- package/types/utils/scheduler.d.ts +6 -0
- package/types/utils/sub-memo.d.ts +6 -0
- package/esm/__tests__/common.test.js +0 -1
- package/esm/__tests__/is.test.js +0 -1
- package/esm/__tests__/merge.test.js +0 -1
- package/esm/__tests__/types.test.js +0 -1
- package/esm/common.js +0 -1
- package/esm/create-base-state.js +0 -1
- package/esm/create-emitter.js +0 -1
- package/esm/create-getter-state.js +0 -1
- package/esm/is.js +0 -1
- package/esm/merge.js +0 -1
- package/esm/select.js +0 -1
- package/esm/shallow.js +0 -1
- package/esm/use-state-value.js +0 -1
- package/src/__tests__/common.test.ts +0 -63
- package/src/__tests__/create.test.tsx +0 -84
- package/src/__tests__/merge.test.ts +0 -78
- package/src/__tests__/state.test.tsx +0 -619
- package/src/__tests__/types.test.ts +0 -17
- package/src/common.ts +0 -60
- package/src/create-base-state.ts +0 -31
- package/src/create-emitter.ts +0 -24
- package/src/create-getter-state.ts +0 -18
- package/src/create.ts +0 -127
- package/src/index.ts +0 -6
- package/src/merge.ts +0 -38
- package/src/select.ts +0 -33
- package/src/types.ts +0 -94
- package/src/use-state-value.ts +0 -32
- package/types/common.d.ts +0 -17
- package/types/create-base-state.d.ts +0 -10
- package/types/create-emitter.d.ts +0 -7
- package/types/create-getter-state.d.ts +0 -6
- package/types/merge.d.ts +0 -4
- package/types/select.d.ts +0 -2
- package/types/use-state-value.d.ts +0 -10
- /package/esm/{__tests__ → utils/__tests__}/shallow.test.js +0 -0
- /package/{src → packages/core}/__tests__/test-utils.ts +0 -0
- /package/{src → packages/core/utils}/__tests__/shallow.test.ts +0 -0
- /package/types/{shallow.d.ts → utils/shallow.d.ts} +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const THRESHOLD = 0.2
|
|
2
|
+
const THRESHOLD_ITEMS = 10
|
|
3
|
+
const RESCHEDULE_COUNT = 0
|
|
4
|
+
|
|
5
|
+
interface Options<T> {
|
|
6
|
+
readonly onResolveItem?: (item: T) => void
|
|
7
|
+
readonly onFinish: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createScheduler<T>(options: Options<T>) {
|
|
11
|
+
const batches = new Set<T>()
|
|
12
|
+
const { onResolveItem, onFinish } = options
|
|
13
|
+
let frame = performance.now()
|
|
14
|
+
let scheduled = false
|
|
15
|
+
|
|
16
|
+
function schedule() {
|
|
17
|
+
const startFrame = performance.now()
|
|
18
|
+
const frameSizeDiffIn = startFrame - frame
|
|
19
|
+
const { size } = batches
|
|
20
|
+
if (frameSizeDiffIn < THRESHOLD && size > 0 && size < THRESHOLD_ITEMS) {
|
|
21
|
+
frame = startFrame
|
|
22
|
+
flush()
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!scheduled) {
|
|
27
|
+
scheduled = true
|
|
28
|
+
Promise.resolve().then(() => {
|
|
29
|
+
scheduled = false
|
|
30
|
+
frame = performance.now()
|
|
31
|
+
flush()
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function flush() {
|
|
37
|
+
if (batches.size === 0) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
for (const value of batches) {
|
|
41
|
+
if (onResolveItem) {
|
|
42
|
+
onResolveItem(value)
|
|
43
|
+
}
|
|
44
|
+
batches.delete(value)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (batches.size > RESCHEDULE_COUNT) {
|
|
48
|
+
schedule()
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
onFinish()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function addValue(value: T) {
|
|
55
|
+
batches.add(value)
|
|
56
|
+
schedule()
|
|
57
|
+
}
|
|
58
|
+
return addValue
|
|
59
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
/* eslint-disable sonarjs/cognitive-complexity */
|
|
1
2
|
import { isArray, isMap, isSet } from './is'
|
|
2
3
|
|
|
3
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
4
4
|
export function shallow<T>(valueA: T, valueB: T): boolean {
|
|
5
5
|
if (valueA == valueB) {
|
|
6
6
|
return true
|
|
@@ -40,8 +40,8 @@ export function shallow<T>(valueA: T, valueB: T): boolean {
|
|
|
40
40
|
return true
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const keysA = Object.keys(valueA
|
|
44
|
-
const keysB = Object.keys(valueB
|
|
43
|
+
const keysA = Object.keys(valueA)
|
|
44
|
+
const keysB = Object.keys(valueB)
|
|
45
45
|
if (keysA.length !== keysB.length) return false
|
|
46
46
|
for (const key of keysA) {
|
|
47
47
|
if (
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Subscribe } from '../subscriber'
|
|
2
|
+
import { subscriber } from '../subscriber'
|
|
3
|
+
import type { AnyFunction } from '../types'
|
|
4
|
+
|
|
5
|
+
interface CacheItem<T extends AnyFunction> {
|
|
6
|
+
count: number
|
|
7
|
+
returnType: Subscribe<T>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const cache = new WeakMap<AnyFunction, CacheItem<AnyFunction>>()
|
|
11
|
+
|
|
12
|
+
export function subMemo<F extends AnyFunction>(anyFunction: F) {
|
|
13
|
+
return {
|
|
14
|
+
call(): Subscribe<F> {
|
|
15
|
+
const item = cache.get(anyFunction)
|
|
16
|
+
if (item) {
|
|
17
|
+
item.count++
|
|
18
|
+
return item.returnType
|
|
19
|
+
}
|
|
20
|
+
const returnType = subscriber(anyFunction)
|
|
21
|
+
const newItem = { count: 1, returnType }
|
|
22
|
+
cache.set(anyFunction, newItem)
|
|
23
|
+
return newItem.returnType
|
|
24
|
+
},
|
|
25
|
+
destroy() {
|
|
26
|
+
const item = cache.get(anyFunction)
|
|
27
|
+
|
|
28
|
+
if (item) {
|
|
29
|
+
item.count--
|
|
30
|
+
if (item.count === 0) {
|
|
31
|
+
item.returnType.destroy()
|
|
32
|
+
cache.delete(anyFunction)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
}
|
package/types/create.d.ts
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* const user = userState();
|
|
16
|
-
*
|
|
17
|
-
* // Access partial data from the state using slice
|
|
18
|
-
* const userAge = userState.slice((state) => state.age)();
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export declare function create<T>(defaultValue: DefaultValue<T>, isEqual?: IsEqual<T>): SetterState<Awaited<T>>;
|
|
1
|
+
import type { Emitter } from './utils/create-emitter';
|
|
2
|
+
import type { Callable, DefaultValue, IsEqual, Listener, SetValue } from './types';
|
|
3
|
+
interface RawState<T> {
|
|
4
|
+
(): T;
|
|
5
|
+
id: number;
|
|
6
|
+
set: (value: SetValue<T>) => void;
|
|
7
|
+
emitter: Emitter<T>;
|
|
8
|
+
listen: Listener<T>;
|
|
9
|
+
}
|
|
10
|
+
export type State<T> = {
|
|
11
|
+
readonly [K in keyof RawState<T>]: RawState<T>[K];
|
|
12
|
+
} & Callable<T>;
|
|
13
|
+
export declare function create<T>(initialValue: DefaultValue<T>, isEqual?: IsEqual<T>): State<T>;
|
|
14
|
+
export {};
|
package/types/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { create } from './create';
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export { useStateValue } from './use-state-value';
|
|
6
|
-
export { shallow } from './shallow';
|
|
3
|
+
export { use } from './use';
|
|
4
|
+
export { shallow } from './utils/shallow';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AnyFunction, Callable, Listener } from './types';
|
|
2
|
+
import type { Emitter } from './utils/create-emitter';
|
|
3
|
+
interface SubscribeContext<T = unknown> {
|
|
4
|
+
addEmitter: (emitter: Emitter<T>) => void;
|
|
5
|
+
id: number;
|
|
6
|
+
sub: () => void;
|
|
7
|
+
}
|
|
8
|
+
interface SubscribeRaw<F extends AnyFunction, T extends ReturnType<F> = ReturnType<F>> {
|
|
9
|
+
(): T;
|
|
10
|
+
emitter: Emitter<T | undefined>;
|
|
11
|
+
destroy: () => void;
|
|
12
|
+
id: number;
|
|
13
|
+
listen: Listener<T>;
|
|
14
|
+
abort: () => void;
|
|
15
|
+
}
|
|
16
|
+
export type Subscribe<F extends AnyFunction, T extends ReturnType<F> = ReturnType<F>> = {
|
|
17
|
+
readonly [K in keyof SubscribeRaw<F, T>]: SubscribeRaw<F, T>[K];
|
|
18
|
+
} & Callable<T>;
|
|
19
|
+
export declare const context: {
|
|
20
|
+
run: <R>(ctxValue: SubscribeContext<unknown> | undefined, cb: () => R | Promise<R>) => R;
|
|
21
|
+
use: () => SubscribeContext<unknown> | undefined;
|
|
22
|
+
wrap: <X>(cb: () => X | Promise<X>) => () => X | Promise<X>;
|
|
23
|
+
};
|
|
24
|
+
export declare function subscriber<F extends AnyFunction, T extends ReturnType<F> = ReturnType<F>>(anyFunction: () => T): Subscribe<F, T>;
|
|
25
|
+
export {};
|
package/types/types.d.ts
CHANGED
|
@@ -1,68 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Equality check function.
|
|
4
|
-
*/
|
|
1
|
+
export type AnyFunction = (...args: any[]) => any;
|
|
5
2
|
export type IsEqual<T = unknown> = (a: T, b: T) => boolean;
|
|
6
|
-
export type
|
|
7
|
-
|
|
8
|
-
* Set new state value function.
|
|
9
|
-
*/
|
|
10
|
-
export type SetValue<T> = T | Setter<T>;
|
|
11
|
-
export type UpdateValue<T> = T extends object ? Partial<T> : T;
|
|
12
|
-
/**
|
|
13
|
-
* Set new state function
|
|
14
|
-
*/
|
|
15
|
-
export type StateValue<T, S> = undefined extends S ? T : S;
|
|
16
|
-
export type Set<T> = (value: SetValue<T>) => void;
|
|
17
|
-
export type Update<T> = (value: UpdateValue<T>) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Getting state value function.
|
|
20
|
-
*/
|
|
21
|
-
export type GetState<T> = () => T;
|
|
22
|
-
export interface StateDataInternal<T = unknown> {
|
|
23
|
-
value?: T;
|
|
24
|
-
updateVersion: number;
|
|
25
|
-
abortController?: AbortController;
|
|
26
|
-
}
|
|
27
|
-
export declare enum StateKeys {
|
|
28
|
-
IS_STATE = "isState",
|
|
29
|
-
IS_SLICE = "isSlice"
|
|
30
|
-
}
|
|
31
|
-
export interface BaseState<T> {
|
|
32
|
-
/**
|
|
33
|
-
* Reset state to default value if it's basic atom - if it's family - it will clear all family members
|
|
34
|
-
*/
|
|
35
|
-
reset: () => void;
|
|
36
|
-
/**
|
|
37
|
-
* Get current state value
|
|
38
|
-
*/
|
|
39
|
-
getState: GetState<T>;
|
|
40
|
-
select: <S>(selector: (value: T) => S, isEqual?: IsEqual<S>) => GetterState<S>;
|
|
41
|
-
/**
|
|
42
|
-
* Internal state data
|
|
43
|
-
*/
|
|
44
|
-
__internal: {
|
|
45
|
-
emitter: Emitter<T>;
|
|
46
|
-
};
|
|
47
|
-
subscribe: (listener: (value: T) => void) => () => void;
|
|
48
|
-
}
|
|
49
|
-
export interface GetterState<T> extends BaseState<T> {
|
|
50
|
-
<S>(selector?: (state: T) => S, isEqual?: IsEqual<S>): StateValue<T, S>;
|
|
51
|
-
}
|
|
52
|
-
export interface SetterState<T> extends GetterState<T> {
|
|
53
|
-
/**
|
|
54
|
-
* Set new state value
|
|
55
|
-
*/
|
|
56
|
-
setState: Set<T>;
|
|
57
|
-
/**
|
|
58
|
-
* Set new state value
|
|
59
|
-
*/
|
|
60
|
-
updateState: Update<T>;
|
|
61
|
-
}
|
|
62
|
-
export type State<T> = SetterState<T> | GetterState<T>;
|
|
3
|
+
export type SetStateCb<T> = (value: T) => Awaited<T>;
|
|
4
|
+
export type SetValue<T> = SetStateCb<T> | Awaited<T>;
|
|
63
5
|
export type DefaultValue<T> = T | (() => T);
|
|
64
|
-
export
|
|
65
|
-
export interface
|
|
66
|
-
current
|
|
67
|
-
|
|
6
|
+
export type Listener<T> = (listener: (value: T) => void) => () => void;
|
|
7
|
+
export interface Cache<T> {
|
|
8
|
+
current?: T;
|
|
9
|
+
previous?: T;
|
|
68
10
|
}
|
|
11
|
+
export type Callable<T> = () => T;
|
|
12
|
+
export declare const EMPTY_SELECTOR: <T>(stateValue: T) => T;
|
package/types/use.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Cache, IsEqual } from '../types';
|
|
2
|
+
export declare enum Abort {
|
|
3
|
+
Error = "StateAbortError"
|
|
4
|
+
}
|
|
5
|
+
export interface CancelablePromise<T> {
|
|
6
|
+
promise?: Promise<T>;
|
|
7
|
+
controller?: AbortController;
|
|
8
|
+
resolveInitialPromise?: (value: T) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Cancelable promise function, return promise and controller
|
|
12
|
+
*/
|
|
13
|
+
export declare function cancelablePromise<T>(promise: Promise<T>, previousController?: AbortController): CancelablePromise<T>;
|
|
14
|
+
export declare function generateId(): number;
|
|
15
|
+
export declare function canUpdate<T>(cache: Cache<T>, isEqual?: IsEqual<T>): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type EmitterSubscribe<P = undefined> = (listener: (...params: P[]) => void) => () => void;
|
|
2
|
+
export interface Emitter<T, P = undefined> {
|
|
3
|
+
subscribe: EmitterSubscribe<P>;
|
|
4
|
+
subscribeToOtherEmitter: (emitter: Emitter<unknown>) => void;
|
|
5
|
+
getSnapshot: () => T;
|
|
6
|
+
getInitialSnapshot?: () => T;
|
|
7
|
+
emit: (...params: P[]) => void;
|
|
8
|
+
getSize: () => number;
|
|
9
|
+
clear: () => void;
|
|
10
|
+
contains: (listener: (...params: P[]) => void) => boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Generics parameters are:
|
|
14
|
+
* T: Type of the state
|
|
15
|
+
* R: Type of the snapshot
|
|
16
|
+
* P: Type of the parameters
|
|
17
|
+
* @param getSnapshot
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
export declare function createEmitter<T, P = undefined>(getSnapshot: () => T, getInitialSnapshot?: () => T): Emitter<T, P>;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function isPromise(value: unknown): value is Promise<
|
|
3
|
-
export declare function isFunction
|
|
4
|
-
export declare function isSetValueFunction<T>(value: SetValue<T>): value is Setter<T>;
|
|
5
|
-
export declare function isObject(value: unknown): value is Record<string, unknown>;
|
|
6
|
-
export declare function isRef<T>(value: unknown): value is Ref<T>;
|
|
1
|
+
import type { SetStateCb, SetValue } from '../types';
|
|
2
|
+
export declare function isPromise<T>(value: unknown): value is Promise<T>;
|
|
3
|
+
export declare function isFunction<T extends (...args: unknown[]) => unknown>(value: unknown): value is T;
|
|
7
4
|
export declare function isMap(value: unknown): value is Map<unknown, unknown>;
|
|
8
5
|
export declare function isSet(value: unknown): value is Set<unknown>;
|
|
9
6
|
export declare function isArray(value: unknown): value is Array<unknown>;
|
|
10
7
|
export declare function isEqualBase<T>(valueA: T, valueB: T): boolean;
|
|
8
|
+
export declare function isSetValueFunction<T>(value: SetValue<T>): value is SetStateCb<T>;
|
|
11
9
|
export declare function isAbortError(value: unknown): value is DOMException;
|
|
12
10
|
export declare function isAnyOtherError(value: unknown): value is Error;
|
|
11
|
+
export declare function isUndefined(value: unknown): value is undefined;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{cancelablePromise as l,toType as m,useSyncExternalStore as r}from"../common";import{renderHook as s}from"@testing-library/react-hooks";import{createEmitter as n}from"../create-emitter";import{longPromise as i}from"./test-utils";describe("toType",()=>{it("should cast object to specified type",()=>{const t=m({a:1});expect(t.a).toBe(1)})}),describe("useSyncExternalStore",()=>{it("should return the initial state value",()=>{const e=n(()=>0),{result:t}=s(()=>r(e,o=>o));expect(t.current).toBe(0)}),it("should update when the state value changes",()=>{let e=0;const t=n(()=>e),{result:o}=s(()=>r(t,c=>c));e=1,t.emit(),expect(o.current).toBe(1)}),it("should use the selector function",()=>{let e=0;const t=n(()=>({count:e})),{result:o}=s(()=>r(t,c=>c.count));e=1,t.emit(),expect(o.current).toBe(1)}),it("should use the isEqual function",()=>{let e=0;const t=n(()=>({count:e})),o=jest.fn((u,a)=>u===a),{result:c}=s(()=>r(t,u=>u.count,o));e=1,t.emit(),expect(c.current).toBe(1),expect(o).toHaveBeenCalled()}),it("should test cancelable promise to abort",async()=>{const{promise:e,controller:t}=l(i(1e6));t.abort(),expect(e).rejects.toThrow("aborted")}),it("should test cancelable promise to resolve",async()=>{const{promise:e}=l(i(0));expect(await e).toBe(0)})});
|
package/esm/__tests__/is.test.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Abort as l}from"../common";import{isPromise as e,isFunction as r,isSetValueFunction as t,isObject as o,isRef as s,isMap as u,isSet as i,isArray as n,isEqualBase as a,isAbortError as f}from"../is";describe("isPromise",()=>{it("should return true for a Promise",()=>{expect(e(Promise.resolve())).toBe(!0)}),it("should return false for a non-Promise",()=>{expect(e(123)).toBe(!1)})}),describe("isFunction",()=>{it("should return true for a function",()=>{expect(r(()=>{})).toBe(!0)}),it("should return false for a non-function",()=>{expect(r(123)).toBe(!1)})}),describe("isSetValueFunction",()=>{it("should return true for a function",()=>{expect(t(()=>{})).toBe(!0)}),it("should return false for a non-function",()=>{expect(t(123)).toBe(!1)})}),describe("isObject",()=>{it("should return true for an object",()=>{expect(o({})).toBe(!0)}),it("should return false for a non-object",()=>{expect(o(123)).toBe(!1)})}),describe("isRef",()=>{it("should return true for a ref object",()=>{expect(s({isRef:!0})).toBe(!0)}),it("should return false for a non-ref object",()=>{expect(s({})).toBe(!1)})}),describe("isMap",()=>{it("should return true for a Map",()=>{expect(u(new Map)).toBe(!0)}),it("should return false for a non-Map",()=>{expect(u(123)).toBe(!1)})}),describe("isSet",()=>{it("should return true for a Set",()=>{expect(i(new Set)).toBe(!0)}),it("should return false for a non-Set",()=>{expect(i(123)).toBe(!1)})}),describe("isArray",()=>{it("should return true for an array",()=>{expect(n([])).toBe(!0)}),it("should return false for a non-array",()=>{expect(n(123)).toBe(!1)})}),describe("isEqualBase",()=>{it("should return true for equal values",()=>{expect(a(1,1)).toBe(!0)}),it("should return false for non-equal values",()=>{expect(a(1,2)).toBe(!1)})}),describe("isAbortError",()=>{it("should return true for an AbortError",()=>{expect(f(new DOMException("",l.Error))).toBe(!0)}),it("should return false for a non-AbortError",()=>{expect(f(new DOMException("","Error"))).toBe(!1)})});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{create as t}from"../create";import{renderHook as i,act as x}from"@testing-library/react";import{merge as a}from"../merge";describe("merge",()=>{it("should test merge multiple-state",()=>{const o=t(1),c=t(1),n=t(3),u=a([o,c,n],(s,e,r)=>(expect(s).toBe(1),expect(e).toBe(1),expect(r).toBe(3),`${s} ${e} ${r}`)),p=i(()=>u());expect(p.result.current).toBe("1 1 3")}),it("should test merge multiple-state with isEqual",()=>{const o=t(1),c=t(1),n=t(3),u=a([o,c,n],(s,e,r)=>(expect(s).toBe(1),expect(e).toBe(1),expect(r).toBe(3),`${s} ${e} ${r}`),(s,e)=>s===e),p=i(()=>u());expect(p.result.current).toBe("1 1 3")}),it("should test merge multiple-state with different type",()=>{const o=t(1),c=t("1"),n=t({value:3}),u=t([1,2,3]),p=a([o,c,n,u],(e,r,l,m)=>(expect(e).toBe(1),expect(r).toBe("1"),expect(l).toStrictEqual({value:3}),expect(m).toStrictEqual([1,2,3]),`${e} ${r} ${l.value} ${m.join(" ")}`)),s=i(()=>p());expect(s.result.current).toBe("1 1 3 1 2 3")}),it("should test merge with reset",()=>{const o=t(1),c=t(1),n=t(3),u=a([o,c,n],(s,e,r)=>`${s} ${e} ${r}`),p=i(()=>u());x(()=>{o.setState(2),c.setState(2),n.setState(4)}),expect(p.result.current).toBe("2 2 4"),x(()=>{u.reset()}),expect(p.result.current).toBe("1 1 3")})});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{getDefaultValue as t}from"../types";describe("getDefaultValue",()=>{it("should return the value if it is not a function or promise",()=>{expect(t(123)).toBe(123)}),it("should return the resolved value if it is a function",()=>{expect(t(()=>123)).toBe(123)}),it("should return the promise if it is a promise",()=>{const e=Promise.resolve(123);expect(t(e)).toBe(e),expect(t(()=>e)).toBe(e)})});
|
package/esm/common.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{useSyncExternalStoreWithSelector as c}from"use-sync-external-store/shim/with-selector";import{useDebugValue as i}from"react";function l(e){return e}function p(e,r,t){const o=c(e.subscribe,e.getSnapshot,e.getSnapshot,r?n=>r(n):l,t);return i(o),o}var u=(r=>(r.Error="StateAbortError",r))(u||{});function T(e,r){r&&r.abort();const t=new AbortController,{signal:o}=t;return{promise:new Promise((a,s)=>{o.addEventListener("abort",()=>{s(new DOMException("Promise was aborted","StateAbortError"))}),e.then(a).catch(s)}),controller:t}}export{u as Abort,T as cancelablePromise,l as toType,p as useSyncExternalStore};
|
package/esm/create-base-state.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{select as m}from"./select";function p(a){const{emitter:r,getGetterState:o,reset:s,getState:e}=a;return{getState:e,reset:s,select(t,i){const n=o();return m(n,t,i)},__internal:{emitter:r},subscribe(t){return t(e()),r.subscribe(()=>{t(e())})}}}export{p as createBaseState};
|
package/esm/create-emitter.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function n(r){const t=new Set;return{subscribe:e=>(t.add(e),()=>{t.delete(e)}),emit:(...e)=>{for(const i of t)i(...e)},getSnapshot:r}}export{n as createEmitter};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{useStateValue as n}from"./use-state-value";function i(a){const{baseState:e}=a,t=(r,s)=>n(t,r,s);return t.__internal=e.__internal,t.getState=e.getState,t.reset=e.reset,t.select=e.select,t.subscribe=e.subscribe,t}export{i as createGetterState};
|
package/esm/is.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Abort as o}from"./common";function u(n){return n instanceof Promise}function i(n){return typeof n=="function"}function s(n){return typeof n=="function"}function t(n){return typeof n=="object"&&n!==null}function f(n){return t(n)&&n.isRef===!0}function c(n){return n instanceof Map}function a(n){return n instanceof Set}function p(n){return Array.isArray(n)}function k(n,r){return n===r?!0:!!Object.is(n,r)}function w(n){return n instanceof DOMException&&n.name===o.Error}function x(n){return n instanceof Error&&n.name!==o.Error}export{w as isAbortError,x as isAnyOtherError,p as isArray,k as isEqualBase,i as isFunction,c as isMap,t as isObject,u as isPromise,f as isRef,a as isSet,s as isSetValueFunction};
|
package/esm/merge.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createBaseState as s}from"./create-base-state";import{createEmitter as f}from"./create-emitter";import{createGetterState as u}from"./create-getter-state";import{isEqualBase as p}from"./is";function T(e,a,S=p){let r;const o=f(()=>{const t=a(...e.map(m=>m.getState()));return r!==void 0&&S(r,t)?r:(r=t,t)});for(const t of e)t.__internal.emitter.subscribe(()=>{o.emit()});const i=s({emitter:o,getGetterState:()=>n,getState:()=>a(...e.map(t=>t.getState())),reset(){for(const t of e)t.reset()}}),n=u({baseState:i});return n}export{T as merge};
|
package/esm/select.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createBaseState as m}from"./create-base-state";import{createEmitter as s}from"./create-emitter";import{createGetterState as u}from"./create-getter-state";import{isEqualBase as c}from"./is";function d(t,a,o=c){let e;const S=s(()=>{const r=a(t.getState());return e!==void 0&&o(e,r)?e:(e=r,r)});t.__internal.emitter.subscribe(()=>{S.emit()});const n=m({emitter:S,getGetterState:()=>i,getState:()=>a(t.getState()),reset:t.reset}),i=u({baseState:n});return i}export{d as select};
|
package/esm/shallow.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isArray as o,isMap as f,isSet as i}from"./is";function k(t,r){if(t==r)return!0;if(typeof t!="object"||t==null||typeof r!="object"||r==null)return!1;if(f(t)&&f(r)){if(t.size!==r.size)return!1;for(const[n,e]of t)if(!Object.is(e,r.get(n)))return!1;return!0}if(i(t)&&i(r)){if(t.size!==r.size)return!1;for(const n of t)if(!r.has(n))return!1;return!0}if(o(t)&&o(r)){if(t.length!==r.length)return!1;for(const[n,e]of t.entries())if(!Object.is(e,r[n]))return!1;return!0}const s=Object.keys(t),c=Object.keys(r);if(s.length!==c.length)return!1;for(const n of s)if(!Object.prototype.hasOwnProperty.call(r,n)||!Object.is(t[n],r[n]))return!1;return!0}export{k as shallow};
|
package/esm/use-state-value.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{useSyncExternalStore as a,toType as i}from"./common";import{isAnyOtherError as s,isPromise as S}from"./is";function f(e,r=t=>i(t),o){const t=a(e.__internal.emitter,n=>r(n),o);if(S(t)||s(t))throw t;return t}export{f as useStateValue};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { cancelablePromise, toType, useSyncExternalStore } from '../common'
|
|
2
|
-
import { renderHook } from '@testing-library/react-hooks'
|
|
3
|
-
import { createEmitter } from '../create-emitter'
|
|
4
|
-
import { longPromise } from './test-utils'
|
|
5
|
-
|
|
6
|
-
describe('toType', () => {
|
|
7
|
-
it('should cast object to specified type', () => {
|
|
8
|
-
const object = { a: 1 }
|
|
9
|
-
const result = toType<{ a: number }>(object)
|
|
10
|
-
expect(result.a).toBe(1)
|
|
11
|
-
})
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
describe('useSyncExternalStore', () => {
|
|
15
|
-
it('should return the initial state value', () => {
|
|
16
|
-
const emitter = createEmitter(() => 0)
|
|
17
|
-
const { result } = renderHook(() => useSyncExternalStore(emitter, (state) => state))
|
|
18
|
-
expect(result.current).toBe(0)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('should update when the state value changes', () => {
|
|
22
|
-
let value = 0
|
|
23
|
-
const emitter = createEmitter(() => value)
|
|
24
|
-
const { result } = renderHook(() => useSyncExternalStore(emitter, (state) => state))
|
|
25
|
-
|
|
26
|
-
value = 1
|
|
27
|
-
emitter.emit()
|
|
28
|
-
expect(result.current).toBe(1)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should use the selector function', () => {
|
|
32
|
-
let value = 0
|
|
33
|
-
const emitter = createEmitter(() => ({ count: value }))
|
|
34
|
-
const { result } = renderHook(() => useSyncExternalStore(emitter, (state: { count: number }) => state.count))
|
|
35
|
-
|
|
36
|
-
value = 1
|
|
37
|
-
emitter.emit()
|
|
38
|
-
expect(result.current).toBe(1)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should use the isEqual function', () => {
|
|
42
|
-
let value = 0
|
|
43
|
-
const emitter = createEmitter(() => ({ count: value }))
|
|
44
|
-
const isEqual = jest.fn((a, b) => a === b)
|
|
45
|
-
const { result } = renderHook(() => useSyncExternalStore(emitter, (state: { count: number }) => state.count, isEqual))
|
|
46
|
-
|
|
47
|
-
value = 1
|
|
48
|
-
emitter.emit()
|
|
49
|
-
expect(result.current).toBe(1)
|
|
50
|
-
expect(isEqual).toHaveBeenCalled()
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should test cancelable promise to abort', async () => {
|
|
54
|
-
const { promise, controller } = cancelablePromise(longPromise(1000 * 1000))
|
|
55
|
-
controller.abort()
|
|
56
|
-
expect(promise).rejects.toThrow('aborted')
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should test cancelable promise to resolve', async () => {
|
|
60
|
-
const { promise } = cancelablePromise(longPromise(0))
|
|
61
|
-
expect(await promise).toBe(0)
|
|
62
|
-
})
|
|
63
|
-
})
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Suspense } from 'react'
|
|
2
|
-
import { create } from '../create'
|
|
3
|
-
import { renderHook, waitFor, act, render } from '@testing-library/react'
|
|
4
|
-
import { ErrorBoundary, longPromise } from './test-utils'
|
|
5
|
-
|
|
6
|
-
describe('create', () => {
|
|
7
|
-
it('should create test with base value', () => {
|
|
8
|
-
const state = create(0)
|
|
9
|
-
const result = renderHook(() => state())
|
|
10
|
-
expect(result.result.current).toBe(0)
|
|
11
|
-
})
|
|
12
|
-
it('should create test with function', () => {
|
|
13
|
-
const state = create(() => 0)
|
|
14
|
-
const result = renderHook(() => state())
|
|
15
|
-
expect(result.result.current).toBe(0)
|
|
16
|
-
})
|
|
17
|
-
it('should create test with promise', async () => {
|
|
18
|
-
const state = create(Promise.resolve(0))
|
|
19
|
-
const result = renderHook(() => state())
|
|
20
|
-
await waitFor(() => {
|
|
21
|
-
expect(result.result.current).toBe(0)
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
it('should create test with promise and wait to be resolved', async () => {
|
|
25
|
-
const state = create(longPromise)
|
|
26
|
-
const result = renderHook(() => state(), { wrapper: ({ children }) => <Suspense fallback={null}>{children}</Suspense> })
|
|
27
|
-
|
|
28
|
-
await waitFor(() => {
|
|
29
|
-
expect(result.result.current).toBe(0)
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
it('should create test with lazy promise and wait to be resolved', async () => {
|
|
33
|
-
const state = create(async () => await longPromise())
|
|
34
|
-
const result = renderHook(() => state(), { wrapper: ({ children }) => <Suspense fallback={null}>{children}</Suspense> })
|
|
35
|
-
|
|
36
|
-
await waitFor(() => {
|
|
37
|
-
expect(result.result.current).toBe(0)
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
it('should create test with promise and set value during the promise is pending', async () => {
|
|
41
|
-
const state = create(longPromise)
|
|
42
|
-
const result = renderHook(() => state(), { wrapper: ({ children }) => <Suspense fallback={null}>{children}</Suspense> })
|
|
43
|
-
|
|
44
|
-
act(() => {
|
|
45
|
-
state.setState(10)
|
|
46
|
-
})
|
|
47
|
-
await waitFor(() => {
|
|
48
|
-
expect(result.result.current).toBe(10)
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('should create test with lazy promise and set value during the promise is pending', async () => {
|
|
53
|
-
const state = create(async () => await longPromise())
|
|
54
|
-
const result = renderHook(() => state(), { wrapper: ({ children }) => <Suspense fallback={null}>{children}</Suspense> })
|
|
55
|
-
|
|
56
|
-
act(() => {
|
|
57
|
-
state.setState(10)
|
|
58
|
-
})
|
|
59
|
-
await waitFor(() => {
|
|
60
|
-
expect(result.result.current).toBe(10)
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('should fail inside the hook when the promise is rejected with not abort isWithError', async () => {
|
|
65
|
-
const state = create(Promise.reject('error-message'))
|
|
66
|
-
|
|
67
|
-
function Component() {
|
|
68
|
-
state()
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const result = render(
|
|
73
|
-
<ErrorBoundary fallback={<div>An error occurred.</div>}>
|
|
74
|
-
<Suspense fallback={'suspense-error'}>
|
|
75
|
-
<Component />
|
|
76
|
-
</Suspense>
|
|
77
|
-
</ErrorBoundary>,
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
await waitFor(() => {
|
|
81
|
-
expect(result.container.textContent).toBe('An error occurred.')
|
|
82
|
-
})
|
|
83
|
-
})
|
|
84
|
-
})
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { create } from '../create'
|
|
2
|
-
import { renderHook, act } from '@testing-library/react'
|
|
3
|
-
import { merge } from '../merge'
|
|
4
|
-
|
|
5
|
-
describe('merge', () => {
|
|
6
|
-
it('should test merge multiple-state', () => {
|
|
7
|
-
const state1 = create(1)
|
|
8
|
-
const state2 = create(1)
|
|
9
|
-
const state3 = create(3)
|
|
10
|
-
|
|
11
|
-
const useMerged = merge([state1, state2, state3], (value1, value2, value3) => {
|
|
12
|
-
expect(value1).toBe(1)
|
|
13
|
-
expect(value2).toBe(1)
|
|
14
|
-
expect(value3).toBe(3)
|
|
15
|
-
return `${value1} ${value2} ${value3}`
|
|
16
|
-
})
|
|
17
|
-
const result = renderHook(() => useMerged())
|
|
18
|
-
expect(result.result.current).toBe('1 1 3')
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('should test merge multiple-state with isEqual', () => {
|
|
22
|
-
const state1 = create(1)
|
|
23
|
-
const state2 = create(1)
|
|
24
|
-
const state3 = create(3)
|
|
25
|
-
|
|
26
|
-
const useMerged = merge(
|
|
27
|
-
[state1, state2, state3],
|
|
28
|
-
(value1, value2, value3) => {
|
|
29
|
-
expect(value1).toBe(1)
|
|
30
|
-
expect(value2).toBe(1)
|
|
31
|
-
expect(value3).toBe(3)
|
|
32
|
-
return `${value1} ${value2} ${value3}`
|
|
33
|
-
},
|
|
34
|
-
(a, b) => a === b,
|
|
35
|
-
)
|
|
36
|
-
const result = renderHook(() => useMerged())
|
|
37
|
-
expect(result.result.current).toBe('1 1 3')
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should test merge multiple-state with different type', () => {
|
|
41
|
-
const state1 = create(1)
|
|
42
|
-
const state2 = create('1')
|
|
43
|
-
const state3 = create({ value: 3 })
|
|
44
|
-
const state4 = create([1, 2, 3])
|
|
45
|
-
|
|
46
|
-
const useMerged = merge([state1, state2, state3, state4], (value1, value2, value3, value4) => {
|
|
47
|
-
expect(value1).toBe(1)
|
|
48
|
-
expect(value2).toBe('1')
|
|
49
|
-
expect(value3).toStrictEqual({ value: 3 })
|
|
50
|
-
expect(value4).toStrictEqual([1, 2, 3])
|
|
51
|
-
return `${value1} ${value2} ${value3.value} ${value4.join(' ')}`
|
|
52
|
-
})
|
|
53
|
-
const result = renderHook(() => useMerged())
|
|
54
|
-
expect(result.result.current).toBe('1 1 3 1 2 3')
|
|
55
|
-
})
|
|
56
|
-
it('should test merge with reset', () => {
|
|
57
|
-
const state1 = create(1)
|
|
58
|
-
const state2 = create(1)
|
|
59
|
-
const state3 = create(3)
|
|
60
|
-
|
|
61
|
-
const useMerged = merge([state1, state2, state3], (value1, value2, value3) => {
|
|
62
|
-
return `${value1} ${value2} ${value3}`
|
|
63
|
-
})
|
|
64
|
-
const result = renderHook(() => useMerged())
|
|
65
|
-
|
|
66
|
-
act(() => {
|
|
67
|
-
state1.setState(2)
|
|
68
|
-
state2.setState(2)
|
|
69
|
-
state3.setState(4)
|
|
70
|
-
})
|
|
71
|
-
expect(result.result.current).toBe('2 2 4')
|
|
72
|
-
|
|
73
|
-
act(() => {
|
|
74
|
-
useMerged.reset()
|
|
75
|
-
})
|
|
76
|
-
expect(result.result.current).toBe('1 1 3')
|
|
77
|
-
})
|
|
78
|
-
})
|