@nice-code/state 0.7.0 → 0.9.0
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/build/Store-B65MojT2.d.ts +201 -0
- package/build/Store-CI9N0P6I.js +366 -0
- package/build/Store-CI9N0P6I.js.map +1 -0
- package/build/Store-PjfFkZ2I.js +349 -0
- package/build/Store-PjfFkZ2I.js.map +1 -0
- package/build/devtools/browser/index.d.ts +120 -0
- package/build/devtools/browser/index.js +2750 -2357
- package/build/devtools/browser/index.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +2 -244
- package/build/react/index.d.ts +58 -0
- package/build/react/index.js +59 -308
- package/build/react/index.js.map +1 -0
- package/package.json +29 -26
- package/build/types/core/Store.d.ts +0 -136
- package/build/types/core/index.d.ts +0 -1
- package/build/types/devtools/browser/NiceStateDevtools.d.ts +0 -10
- package/build/types/devtools/browser/components/ChangeDetailPanel.d.ts +0 -12
- package/build/types/devtools/browser/components/ChangeList.d.ts +0 -9
- package/build/types/devtools/browser/components/DiffView.d.ts +0 -13
- package/build/types/devtools/browser/components/JsonDiffView.d.ts +0 -24
- package/build/types/devtools/browser/components/JsonView.d.ts +0 -7
- package/build/types/devtools/browser/components/PanelChrome.d.ts +0 -54
- package/build/types/devtools/browser/components/SectionLabel.d.ts +0 -4
- package/build/types/devtools/browser/components/StateInspector.d.ts +0 -16
- package/build/types/devtools/browser/components/StoreTabs.d.ts +0 -12
- package/build/types/devtools/browser/components/utils.d.ts +0 -98
- package/build/types/devtools/browser/devtools_dock.d.ts +0 -54
- package/build/types/devtools/browser/index.d.ts +0 -3
- package/build/types/devtools/core/StateDevtools.types.d.ts +0 -43
- package/build/types/devtools/core/StateDevtoolsCore.d.ts +0 -56
- package/build/types/devtools/core/devtools_colors.d.ts +0 -26
- package/build/types/index.d.ts +0 -1
- package/build/types/react/InjectStoreState.d.ts +0 -18
- package/build/types/react/index.d.ts +0 -3
- package/build/types/react/useLocalStore.d.ts +0 -8
- package/build/types/react/useStoreState.d.ts +0 -23
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/react/useStoreState.ts","../../src/react/InjectStoreState.ts","../../src/react/useLocalStore.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from \"react\";\r\nimport type { Store } from \"../core/Store\";\r\n\r\n/**\r\n * Compares the previously selected value against the next one. Returning `true`\r\n * tells the hook the value is unchanged, so React is handed the *same*\r\n * reference and no re-render is scheduled.\r\n */\r\nexport type TEqualityFn<SS> = (a: SS, b: SS) => boolean;\r\n\r\n// Default equality is a strict reference check — the fastest possible path, and\r\n// exactly right when selectors return state branches (Immer's structural\r\n// sharing keeps unchanged branches referentially stable). Pass `deepEqual` from\r\n// `fast-equals` when a selector builds a fresh object/array each call.\r\nfunction strictEqual(a: unknown, b: unknown): boolean {\r\n return a === b;\r\n}\r\n\r\ninterface ISelectionCache<R> {\r\n value: R;\r\n}\r\n\r\n/**\r\n * Subscribe a React component to a store, optionally narrowing to a derived\r\n * slice.\r\n *\r\n * Built on `useSyncExternalStore`, so it is tear-free under React 18+\r\n * concurrent rendering with no manual `useState`/`useEffect` subscription loop.\r\n *\r\n * A `useRef` cache holds the last selected value. On every store emission the\r\n * selector is re-evaluated and the result is run through a strict-reference\r\n * fast-path first; only if the reference differs is `equalityFn` consulted. A\r\n * new reference is handed back to React exclusively when a genuine change is\r\n * detected, so equal-but-new selector results never trigger a render.\r\n */\r\nfunction useStoreState<S extends object>(store: Store<S>): S;\r\nfunction useStoreState<S extends object, SS>(\r\n store: Store<S>,\r\n getSubState: (state: S) => SS,\r\n equalityFn?: TEqualityFn<SS>,\r\n): SS;\r\nfunction useStoreState<S extends object, SS>(\r\n store: Store<S>,\r\n getSubState?: (state: S) => SS,\r\n equalityFn: (a: S | SS, b: S | SS) => boolean = strictEqual,\r\n): S | SS {\r\n // Keep the latest selector/equality without forcing a resubscribe when a\r\n // caller passes inline functions. The store-emission cache below guarantees\r\n // referential stability regardless of identity churn here.\r\n const selectorRef = useRef(getSubState);\r\n const equalityRef = useRef(equalityFn);\r\n selectorRef.current = getSubState;\r\n equalityRef.current = equalityFn;\r\n\r\n const cacheRef = useRef<ISelectionCache<S | SS> | null>(null);\r\n\r\n const getSnapshot = useCallback((): S | SS => {\r\n const rawState = store.getRawState();\r\n const selector = selectorRef.current;\r\n const nextValue: S | SS = selector ? selector(rawState) : rawState;\r\n\r\n const cache = cacheRef.current;\r\n\r\n if (cache === null) {\r\n cacheRef.current = { value: nextValue };\r\n return nextValue;\r\n }\r\n\r\n const lastValue = cache.value;\r\n\r\n // Fast-path: reference is identical → return the cached value untouched.\r\n if (nextValue === lastValue) {\r\n return lastValue;\r\n }\r\n\r\n // Reference changed → consult the configurable equality function. When it\r\n // reports equality, keep the old reference so React skips the render.\r\n if (equalityRef.current(lastValue, nextValue)) {\r\n return lastValue;\r\n }\r\n\r\n cache.value = nextValue;\r\n return nextValue;\r\n }, [store]);\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void): (() => void) => store.subscribe(onStoreChange),\r\n [store],\r\n );\r\n\r\n // Same snapshot getter serves the server: it reads the store's current value,\r\n // which on the server is the SSR-seeded state.\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n\r\nexport { useStoreState };\r\n","import type { ReactElement } from \"react\";\r\nimport type { Store } from \"../core/Store\";\r\nimport { type TEqualityFn, useStoreState } from \"./useStoreState\";\r\n\r\nexport interface IPropsInjectStoreState<S extends object, SS> {\r\n store: Store<S>;\r\n on?: (state: S) => SS;\r\n /**\r\n * Optional equality function for the selected slice. Defaults to a strict\r\n * reference check; pass `deepEqual` from `fast-equals` for inline objects.\r\n */\r\n equalityFn?: TEqualityFn<SS>;\r\n children: (output: SS) => ReactElement;\r\n}\r\n\r\n/**\r\n * Render-prop binding: subscribes to `store` (optionally narrowed by `on`) and\r\n * renders `children` with the selected slice.\r\n */\r\nexport function InjectStoreState<S extends object, SS = S>({\r\n store,\r\n on,\r\n equalityFn,\r\n children,\r\n}: IPropsInjectStoreState<S, SS>): ReactElement {\r\n const state = useStoreState(store, on ?? ((s: S) => s as unknown as SS), equalityFn);\r\n return children(state);\r\n}\r\n","import { useRef } from \"react\";\r\nimport { Store } from \"../core/Store\";\r\n\r\n/**\r\n * Standard shallow comparison of dependency arrays — the same semantics React\r\n * uses for its own hook dependency lists. No deep traversal.\r\n */\r\nfunction shallowEqualDeps(\r\n a: ReadonlyArray<unknown> | undefined,\r\n b: ReadonlyArray<unknown> | undefined,\r\n): boolean {\r\n if (a === b) {\r\n return true;\r\n }\r\n if (a == null || b == null || a.length !== b.length) {\r\n return false;\r\n }\r\n for (let i = 0; i < a.length; i++) {\r\n if (a[i] !== b[i]) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * Create a component-local {@link Store} that persists across renders. Passing a\r\n * `deps` array re-creates the store (with a fresh initial state) whenever the\r\n * dependencies change by shallow comparison.\r\n */\r\nfunction useLocalStore<S extends object>(\r\n initialState: (() => S) | S,\r\n deps?: ReadonlyArray<unknown>,\r\n): Store<S> {\r\n const storeRef = useRef<Store<S> | null>(null);\r\n const depsRef = useRef<ReadonlyArray<unknown> | undefined>(deps);\r\n\r\n if (storeRef.current === null) {\r\n storeRef.current = new Store(initialState);\r\n } else if (deps !== undefined && !shallowEqualDeps(depsRef.current, deps)) {\r\n storeRef.current = new Store(initialState);\r\n depsRef.current = deps;\r\n }\r\n\r\n return storeRef.current;\r\n}\r\n\r\nexport { useLocalStore };\r\n"],"mappings":";;;AAcA,SAAS,YAAY,GAAY,GAAqB;CACpD,OAAO,MAAM;AACf;AAyBA,SAAS,cACP,OACA,aACA,aAAgD,aACxC;CAIR,MAAM,cAAc,OAAO,WAAW;CACtC,MAAM,cAAc,OAAO,UAAU;CACrC,YAAY,UAAU;CACtB,YAAY,UAAU;CAEtB,MAAM,WAAW,OAAuC,IAAI;CAE5D,MAAM,cAAc,kBAA0B;EAC5C,MAAM,WAAW,MAAM,YAAY;EACnC,MAAM,WAAW,YAAY;EAC7B,MAAM,YAAoB,WAAW,SAAS,QAAQ,IAAI;EAE1D,MAAM,QAAQ,SAAS;EAEvB,IAAI,UAAU,MAAM;GAClB,SAAS,UAAU,EAAE,OAAO,UAAU;GACtC,OAAO;EACT;EAEA,MAAM,YAAY,MAAM;EAGxB,IAAI,cAAc,WAChB,OAAO;EAKT,IAAI,YAAY,QAAQ,WAAW,SAAS,GAC1C,OAAO;EAGT,MAAM,QAAQ;EACd,OAAO;CACT,GAAG,CAAC,KAAK,CAAC;CASV,OAAO,qBAPW,aACf,kBAA4C,MAAM,UAAU,aAAa,GAC1E,CAAC,KAAK,CAK4B,GAAG,aAAa,WAAW;AACjE;;;;;;;AC1EA,SAAgB,iBAA2C,EACzD,OACA,IACA,YACA,YAC8C;CAE9C,OAAO,SADO,cAAc,OAAO,QAAQ,MAAS,IAAqB,UACrD,CAAC;AACvB;;;;;;;ACpBA,SAAS,iBACP,GACA,GACS;CACT,IAAI,MAAM,GACR,OAAO;CAET,IAAI,KAAK,QAAQ,KAAK,QAAQ,EAAE,WAAW,EAAE,QAC3C,OAAO;CAET,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,IAAI,EAAE,OAAO,EAAE,IACb,OAAO;CAGX,OAAO;AACT;;;;;;AAOA,SAAS,cACP,cACA,MACU;CACV,MAAM,WAAW,OAAwB,IAAI;CAC7C,MAAM,UAAU,OAA2C,IAAI;CAE/D,IAAI,SAAS,YAAY,MACvB,SAAS,UAAU,IAAI,MAAM,YAAY;MACpC,IAAI,SAAS,KAAA,KAAa,CAAC,iBAAiB,QAAQ,SAAS,IAAI,GAAG;EACzE,SAAS,UAAU,IAAI,MAAM,YAAY;EACzC,QAAQ,UAAU;CACpB;CAEA,OAAO,SAAS;AAClB"}
|
package/package.json
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nice-code/state",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"private": false,
|
|
5
|
-
"type": "module",
|
|
3
|
+
"version": "0.9.0",
|
|
6
4
|
"exports": {
|
|
7
5
|
".": {
|
|
8
6
|
"source": "./src/index.ts",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
},
|
|
12
|
-
"./react": {
|
|
13
|
-
"source": "./src/react/index.ts",
|
|
14
|
-
"types": "./build/types/react/index.d.ts",
|
|
15
|
-
"import": "./build/react/index.js"
|
|
7
|
+
"import": "./build/index.js",
|
|
8
|
+
"types": "./build/index.d.ts"
|
|
16
9
|
},
|
|
17
10
|
"./devtools/browser": {
|
|
18
11
|
"source": "./src/devtools/browser/index.ts",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
12
|
+
"import": "./build/devtools/browser/index.js",
|
|
13
|
+
"types": "./build/devtools/browser/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./react": {
|
|
16
|
+
"source": "./src/react/index.ts",
|
|
17
|
+
"import": "./build/react/index.js",
|
|
18
|
+
"types": "./build/react/index.d.ts"
|
|
21
19
|
}
|
|
22
20
|
},
|
|
23
21
|
"files": [
|
|
@@ -25,32 +23,37 @@
|
|
|
25
23
|
"package.json",
|
|
26
24
|
"README.md"
|
|
27
25
|
],
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": ">=19"
|
|
28
|
+
},
|
|
29
|
+
"peerDependenciesMeta": {
|
|
30
|
+
"react": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"private": false,
|
|
28
35
|
"publishConfig": {
|
|
29
36
|
"access": "public"
|
|
30
37
|
},
|
|
31
38
|
"scripts": {
|
|
39
|
+
"build-bun": "bun run clean-build && bun run build.ts && bun run build-types",
|
|
40
|
+
"build": "bun run clean-build && tsdown",
|
|
41
|
+
"build-types": "tsc --project tsconfig.build.json",
|
|
42
|
+
"build-watch": "bun run clean-build && bun run build.ts --watch && bun run build-types --watch",
|
|
43
|
+
"clean-build": "bunx rimraf build",
|
|
32
44
|
"type-check": "bunx tsc --noEmit",
|
|
33
45
|
"type-check-watch": "bunx tsc --noEmit --watch",
|
|
34
|
-
"clean-build": "bunx rimraf build",
|
|
35
46
|
"vitest": "vitest --typecheck",
|
|
36
|
-
"vitest-agent": "vitest --typecheck --reporter=agent"
|
|
37
|
-
"build": "bun run clean-build && bun run build.ts && bun run build-types",
|
|
38
|
-
"build-watch": "bun run clean-build && bun run build.ts --watch && bun run build-types --watch",
|
|
39
|
-
"build-types": "tsc --project tsconfig.build.json"
|
|
47
|
+
"vitest-agent": "vitest --typecheck --reporter=agent"
|
|
40
48
|
},
|
|
49
|
+
"type": "module",
|
|
41
50
|
"dependencies": {
|
|
51
|
+
"@tanstack/react-virtual": "^3.13.26",
|
|
52
|
+
"fast-equals": "6.0.0",
|
|
42
53
|
"immer": "11.1.8",
|
|
43
|
-
"
|
|
54
|
+
"nice-devtools-shared": "0.9.0"
|
|
44
55
|
},
|
|
45
56
|
"devDependencies": {
|
|
46
57
|
"react": "19.2.7"
|
|
47
|
-
},
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"react": ">=19"
|
|
50
|
-
},
|
|
51
|
-
"peerDependenciesMeta": {
|
|
52
|
-
"react": {
|
|
53
|
-
"optional": true
|
|
54
|
-
}
|
|
55
58
|
}
|
|
56
59
|
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { type Draft, type Patch, type PatchListener } from "immer";
|
|
2
|
-
/**
|
|
3
|
-
* A plain pub/sub listener. Fired (in the order added) every time the store's
|
|
4
|
-
* root state reference changes. This is the vanilla integration point that any
|
|
5
|
-
* ecosystem — including the React adapter's `useSyncExternalStore` — builds on.
|
|
6
|
-
*/
|
|
7
|
-
export type TUpdateListener = () => void;
|
|
8
|
-
/**
|
|
9
|
-
* @typeParam S The store's state
|
|
10
|
-
* @param draft The mutable draft to change during this update (Immer proxy).
|
|
11
|
-
* @param original A readonly snapshot of the state as it was before this update.
|
|
12
|
-
*/
|
|
13
|
-
export type TUpdateFunction<S> = (draft: Draft<S>, original: S) => void;
|
|
14
|
-
/**
|
|
15
|
-
* A selector that derives a watched slice `T` from the full store state `S`.
|
|
16
|
-
*/
|
|
17
|
-
export type TStoreWatch<S extends object, T> = (state: S) => T;
|
|
18
|
-
/**
|
|
19
|
-
* Fired by {@link Store.watch} whenever the watched slice changes structurally.
|
|
20
|
-
*/
|
|
21
|
-
export type TStoreSubscriptionListener<S extends object, T> = (watched: T, allState: S, previousWatched: T) => void;
|
|
22
|
-
/**
|
|
23
|
-
* Runs inside an Immer `produce` whenever a watched slice changes, letting the
|
|
24
|
-
* store derive further state from its own mutations.
|
|
25
|
-
*/
|
|
26
|
-
export type TReactionFunction<S extends object, T> = (watched: T, draft: Draft<S>, original: S, previousWatched: T) => void;
|
|
27
|
-
export type TStoreActionUpdate<S extends object> = (updater: TUpdateFunction<S> | TUpdateFunction<S>[], patchesCallback?: TPatchesCallback) => void;
|
|
28
|
-
export type TStoreAction<S extends object> = (update: TStoreActionUpdate<S>) => void;
|
|
29
|
-
export type TPatchesCallback = (patches: Patch[], inversePatches: Patch[]) => void;
|
|
30
|
-
export interface IStoreInternalOptions<S extends object> {
|
|
31
|
-
ssr: boolean;
|
|
32
|
-
reactionCreators?: TReactionCreator<S>[];
|
|
33
|
-
}
|
|
34
|
-
export interface ICreateReactionOptions {
|
|
35
|
-
runNow?: boolean;
|
|
36
|
-
runNowWithSideEffects?: boolean;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* A framework-agnostic, Immer-backed state container.
|
|
40
|
-
*
|
|
41
|
-
* The store owns a single immutable state value and a `Set` of plain
|
|
42
|
-
* listeners. Every mutation runs through Immer's `produce`, and listeners are
|
|
43
|
-
* only notified when the resulting root reference actually changes — making
|
|
44
|
-
* "no-op" updates genuinely free for subscribers.
|
|
45
|
-
*
|
|
46
|
-
* @typeParam S Your store's state interface.
|
|
47
|
-
*/
|
|
48
|
-
export declare class Store<S extends object = object> {
|
|
49
|
-
private currentState;
|
|
50
|
-
private readonly createInitialState;
|
|
51
|
-
private ssr;
|
|
52
|
-
/** Plain pub/sub listeners — a Set both dedupes and dispatches fast. */
|
|
53
|
-
private readonly listeners;
|
|
54
|
-
private reactionCreators;
|
|
55
|
-
private reactions;
|
|
56
|
-
private clientSubscriptions;
|
|
57
|
-
constructor(initialState: S | (() => S));
|
|
58
|
-
get state(): S;
|
|
59
|
-
/**
|
|
60
|
-
* Returns the raw state object contained within this store at this moment.
|
|
61
|
-
*
|
|
62
|
-
* ---
|
|
63
|
-
* ** WARNING **
|
|
64
|
-
*
|
|
65
|
-
* Most of the time, if you're using this in your app, there's probably a
|
|
66
|
-
* better way to do it (a selector subscription or the React adapter).
|
|
67
|
-
* ---
|
|
68
|
-
*/
|
|
69
|
-
getRawState(): S;
|
|
70
|
-
/**
|
|
71
|
-
* Subscribe a plain listener to store mutations. The listener fires once per
|
|
72
|
-
* committed update (root reference change) and receives no arguments — read
|
|
73
|
-
* the latest value with {@link getRawState}.
|
|
74
|
-
*
|
|
75
|
-
* This is the low-level primitive that vanilla code and framework adapters
|
|
76
|
-
* (e.g. React's `useSyncExternalStore`) build upon.
|
|
77
|
-
*
|
|
78
|
-
* @returns An unsubscribe function.
|
|
79
|
-
*/
|
|
80
|
-
subscribe(listener: TUpdateListener): () => void;
|
|
81
|
-
/**
|
|
82
|
-
* Subscribe to a derived slice of state. The `listener` only fires when the
|
|
83
|
-
* watched value changes structurally (per the Fast-Path rule).
|
|
84
|
-
*
|
|
85
|
-
* @returns An unsubscribe function.
|
|
86
|
-
*/
|
|
87
|
-
watch<T>(watch: TStoreWatch<S, T>, listener: TStoreSubscriptionListener<S, T>): () => void;
|
|
88
|
-
/**
|
|
89
|
-
* Register a reaction: when the watched slice changes, the `reaction` recipe
|
|
90
|
-
* runs inside Immer to derive further state on the same store.
|
|
91
|
-
*
|
|
92
|
-
* @returns A function that removes the reaction.
|
|
93
|
-
*/
|
|
94
|
-
createReaction<T>(watch: TStoreWatch<S, T>, reaction: TReactionFunction<S, T>, { runNow, runNowWithSideEffects }?: ICreateReactionOptions): () => void;
|
|
95
|
-
/**
|
|
96
|
-
* Subscribe to the Immer patches produced by each update.
|
|
97
|
-
*
|
|
98
|
-
* @returns An unsubscribe function.
|
|
99
|
-
*/
|
|
100
|
-
listenToPatches(patchListener: PatchListener): () => void;
|
|
101
|
-
/**
|
|
102
|
-
* Mutate the store via one or more Immer update functions.
|
|
103
|
-
*
|
|
104
|
-
* @param updater A single update function, or an array applied in order.
|
|
105
|
-
* @param patchesCallback Optional callback receiving the patches for this update.
|
|
106
|
-
*/
|
|
107
|
-
update(updater: TUpdateFunction<S> | TUpdateFunction<S>[], patchesCallback?: TPatchesCallback): void;
|
|
108
|
-
/**
|
|
109
|
-
* Replace the store's state entirely with a new state value.
|
|
110
|
-
*/
|
|
111
|
-
replace(newState: S): void;
|
|
112
|
-
/**
|
|
113
|
-
* Replace the store's state by mapping from the current state.
|
|
114
|
-
*/
|
|
115
|
-
replaceFromCurrent(replacer: (state: S) => S): void;
|
|
116
|
-
/**
|
|
117
|
-
* Apply a set of Immer patches to the store.
|
|
118
|
-
*/
|
|
119
|
-
applyPatches(patches: Patch[]): void;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Apply Immer patches to a store, committing only if the root reference changes.
|
|
123
|
-
*/
|
|
124
|
-
export declare function applyPatchesToStore<S extends object = object>(store: Store<S>, patches: Patch[]): void;
|
|
125
|
-
/**
|
|
126
|
-
* Mutate a store via one or more Immer update functions.
|
|
127
|
-
*
|
|
128
|
-
* Patches are only computed when there's a consumer for them (a registered
|
|
129
|
-
* patch listener or a `patchesCallback`), keeping the common path allocation-free.
|
|
130
|
-
* Listeners are notified only when the root reference actually changes.
|
|
131
|
-
*
|
|
132
|
-
* @param store The store to update.
|
|
133
|
-
* @param updater A single update function, or an array applied in order.
|
|
134
|
-
* @param patchesCallback Optional callback receiving the patches for this update.
|
|
135
|
-
*/
|
|
136
|
-
export declare function update<S extends object = object>(store: Store<S>, updater: TUpdateFunction<S> | TUpdateFunction<S>[], patchesCallback?: TPatchesCallback): void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { applyPatchesToStore, type ICreateReactionOptions, type IStoreInternalOptions, Store, type TPatchesCallback, type TReactionFunction, type TStoreAction, type TStoreActionUpdate, type TStoreSubscriptionListener, type TStoreWatch, type TUpdateFunction, type TUpdateListener, update, } from "./Store";
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { TDevtoolsPosition } from "../core/StateDevtools.types";
|
|
2
|
-
import type { StateDevtoolsCore } from "../core/StateDevtoolsCore";
|
|
3
|
-
export interface INiceStateDevtoolsProps {
|
|
4
|
-
core: StateDevtoolsCore;
|
|
5
|
-
position?: TDevtoolsPosition;
|
|
6
|
-
initialOpen?: boolean;
|
|
7
|
-
/** Show the panel even when NODE_ENV is not "development". */
|
|
8
|
-
forceEnable?: boolean;
|
|
9
|
-
}
|
|
10
|
-
export declare function NiceStateDevtools({ forceEnable, ...props }: INiceStateDevtoolsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { IDevtoolsStateChange } from "../../core/StateDevtools.types";
|
|
2
|
-
export type TChangeView = "diff" | "props" | "before" | "after";
|
|
3
|
-
export declare function ChangeDetailPanel({ change, onRevert, view, onViewChange, compress, onCompressChange, latestFirst, onLatestFirstChange, }: {
|
|
4
|
-
change: IDevtoolsStateChange;
|
|
5
|
-
onRevert: (change: IDevtoolsStateChange) => void;
|
|
6
|
-
view: TChangeView;
|
|
7
|
-
onViewChange: (view: TChangeView) => void;
|
|
8
|
-
compress: boolean;
|
|
9
|
-
onCompressChange: (compress: boolean) => void;
|
|
10
|
-
latestFirst: boolean;
|
|
11
|
-
onLatestFirstChange: (latestFirst: boolean) => void;
|
|
12
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties } from "react";
|
|
2
|
-
import { type IChangeGroup } from "./utils";
|
|
3
|
-
export declare function ChangeList({ groups, selectedCuid, onSelect, showStore, style, }: {
|
|
4
|
-
groups: IChangeGroup[];
|
|
5
|
-
selectedCuid: string | null;
|
|
6
|
-
onSelect: (cuid: string) => void;
|
|
7
|
-
showStore: boolean;
|
|
8
|
-
style?: CSSProperties;
|
|
9
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test-framework-style diff: only the paths that changed. Changed paths are
|
|
3
|
-
* grouped into a hierarchy by their shared parents — `countHistory.0` and
|
|
4
|
-
* `countHistory.1` nest under a single `countHistory` node, each shown by its
|
|
5
|
-
* direct key only. Single-child chains collapse to a dotted path (`a.b.c`) so
|
|
6
|
-
* lone deep changes stay on one line. Short values render inline with the key
|
|
7
|
-
* (`count: 0 → 1`); long ones keep the removed/added line block.
|
|
8
|
-
*/
|
|
9
|
-
export declare function DiffView({ before, after, latestFirst, }: {
|
|
10
|
-
before: unknown;
|
|
11
|
-
after: unknown;
|
|
12
|
-
latestFirst?: boolean;
|
|
13
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified, git-style diff of the entire state snapshot. Both sides are rendered
|
|
3
|
-
* as pretty JSON and line-diffed, so unchanged structure stays visible for
|
|
4
|
-
* context while removed lines (red, `−`) and added lines (green, `+`) call out
|
|
5
|
-
* exactly which sections of the whole state moved.
|
|
6
|
-
*/
|
|
7
|
-
export declare function JsonDiffView({ before, after, compress, latestFirst, }: {
|
|
8
|
-
before: unknown;
|
|
9
|
-
after: unknown;
|
|
10
|
-
compress?: boolean;
|
|
11
|
-
latestFirst?: boolean;
|
|
12
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
-
/**
|
|
14
|
-
* The full JSON of one snapshot with the lines that differ from the other side
|
|
15
|
-
* highlighted in place — red behind removed lines on the "before" side, green
|
|
16
|
-
* behind added lines on the "after" side. Lines common to both render plainly so
|
|
17
|
-
* the highlight reads as "here is what changed within the whole state".
|
|
18
|
-
*/
|
|
19
|
-
export declare function HighlightedJsonView({ before, after, side, compress, }: {
|
|
20
|
-
before: unknown;
|
|
21
|
-
after: unknown;
|
|
22
|
-
side: "before" | "after";
|
|
23
|
-
compress?: boolean;
|
|
24
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { CSSProperties } from "react";
|
|
2
|
-
export declare function renderColoredJson(text: string): React.ReactNode[];
|
|
3
|
-
export declare function JsonView({ value, indent, style, }: {
|
|
4
|
-
value: unknown;
|
|
5
|
-
indent?: number;
|
|
6
|
-
style?: CSSProperties;
|
|
7
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { type ReactNode } from "react";
|
|
2
|
-
import type { TDevtoolsPosition } from "../../core/StateDevtools.types";
|
|
3
|
-
export type TDockSide = "top" | "bottom" | "left" | "right";
|
|
4
|
-
export declare function getDockSide(pos: TDevtoolsPosition): TDockSide;
|
|
5
|
-
export interface IDevtoolsLauncherItem {
|
|
6
|
-
id: string;
|
|
7
|
-
label: string;
|
|
8
|
-
icon: string;
|
|
9
|
-
badge?: string;
|
|
10
|
-
onOpen: () => void;
|
|
11
|
-
}
|
|
12
|
-
export declare function PanelHeader({ position, onPositionChange, onClose, onClear, paused, onTogglePause, openOthers, children, }: {
|
|
13
|
-
position: TDevtoolsPosition;
|
|
14
|
-
onPositionChange: (p: TDevtoolsPosition) => void;
|
|
15
|
-
onClose: () => void;
|
|
16
|
-
onClear?: () => void;
|
|
17
|
-
paused: boolean;
|
|
18
|
-
onTogglePause: () => void;
|
|
19
|
-
openOthers?: IDevtoolsLauncherItem[];
|
|
20
|
-
children?: ReactNode;
|
|
21
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
22
|
-
export declare function ResizeHandle({ dockSide, dockedSize, onChange, }: {
|
|
23
|
-
dockSide: TDockSide;
|
|
24
|
-
dockedSize: number;
|
|
25
|
-
onChange: (size: number) => void;
|
|
26
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
27
|
-
/**
|
|
28
|
-
* Draggable divider between the list and the detail pane. `horizontal` refers to
|
|
29
|
-
* the split axis: a row layout (dock top/bottom) splits horizontally and drags
|
|
30
|
-
* left/right; a column layout (dock left/right) splits vertically. The reported
|
|
31
|
-
* ratio is the fraction of the container the *detail* pane should occupy.
|
|
32
|
-
*/
|
|
33
|
-
export declare function SplitHandle({ horizontal, onRatioChange, }: {
|
|
34
|
-
horizontal: boolean;
|
|
35
|
-
onRatioChange: (ratio: number) => void;
|
|
36
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
37
|
-
/** A compact segmented toggle used for the Timeline / State mode switch. */
|
|
38
|
-
export declare function SegmentedControl<T extends string>({ options, value, onChange, }: {
|
|
39
|
-
options: {
|
|
40
|
-
value: T;
|
|
41
|
-
label: string;
|
|
42
|
-
}[];
|
|
43
|
-
value: T;
|
|
44
|
-
onChange: (value: T) => void;
|
|
45
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
46
|
-
/**
|
|
47
|
-
* The combined, page-wide launcher shown while every devtool is collapsed — one
|
|
48
|
-
* grouped pill with a segment per registered devtool, so the buttons never
|
|
49
|
-
* overlap or hide behind each other. Rendered by the coordinator's "primary"
|
|
50
|
-
* devtool only.
|
|
51
|
-
*/
|
|
52
|
-
export declare function DevtoolsLauncher({ items }: {
|
|
53
|
-
items: IDevtoolsLauncherItem[];
|
|
54
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { IDevtoolsStoreInfo } from "../../core/StateDevtools.types";
|
|
2
|
-
/**
|
|
3
|
-
* Live current-state view (top half) plus a direct JSON editor (bottom half) for
|
|
4
|
-
* one store — a fixed 50/50 split so the editor is usable without manual
|
|
5
|
-
* resizing. Editing is the "trigger edits directly for testing" capability: the
|
|
6
|
-
* draft is parsed and handed to {@link StateDevtoolsCore.applyEdit}, which
|
|
7
|
-
* replaces the store state.
|
|
8
|
-
*
|
|
9
|
-
* The draft is seeded from the live state only while it is clean — once the user
|
|
10
|
-
* types, incoming external updates no longer clobber their edit (a "reload"
|
|
11
|
-
* button re-syncs on demand).
|
|
12
|
-
*/
|
|
13
|
-
export declare function StateInspector({ store, onApply, }: {
|
|
14
|
-
store: IDevtoolsStoreInfo;
|
|
15
|
-
onApply: (storeId: string, newState: unknown) => void;
|
|
16
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { IDevtoolsStoreInfo } from "../../core/StateDevtools.types";
|
|
2
|
-
/**
|
|
3
|
-
* Horizontal store filter. `null` selection means "all stores". When
|
|
4
|
-
* `includeAll` is false (the State inspector needs a concrete store) the All
|
|
5
|
-
* pill is omitted.
|
|
6
|
-
*/
|
|
7
|
-
export declare function StoreTabs({ stores, selectedStoreId, onSelect, includeAll, }: {
|
|
8
|
-
stores: IDevtoolsStoreInfo[];
|
|
9
|
-
selectedStoreId: string | null;
|
|
10
|
-
onSelect: (id: string | null) => void;
|
|
11
|
-
includeAll?: boolean;
|
|
12
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type { Patch } from "immer";
|
|
2
|
-
import type { IDevtoolsStateChange, TStateChangeSource } from "../../core/StateDevtools.types";
|
|
3
|
-
export declare function safeStringify(value: unknown, indent?: number): string;
|
|
4
|
-
export declare function formatTimestamp(ms: number): string;
|
|
5
|
-
export declare function formatRelativeAge(ms: number): string;
|
|
6
|
-
export declare const SOURCE_LABEL: Record<TStateChangeSource, string>;
|
|
7
|
-
export declare const SOURCE_COLOR: Record<TStateChangeSource, string>;
|
|
8
|
-
/** Render an Immer patch path (`["todos", 0, "done"]`) as `todos.0.done`. */
|
|
9
|
-
export declare function patchPathToString(path: Patch["path"]): string;
|
|
10
|
-
/**
|
|
11
|
-
* A terse, single-line summary of what a change touched, for the timeline row.
|
|
12
|
-
* Prefers a `path: prev → next` form for single scalar replaces, otherwise lists
|
|
13
|
-
* the distinct top-level paths affected.
|
|
14
|
-
*/
|
|
15
|
-
export declare function summarizeChange(change: IDevtoolsStateChange): string;
|
|
16
|
-
/** A run of consecutive, structurally-equal changes collapsed into one row. */
|
|
17
|
-
export interface IChangeGroup {
|
|
18
|
-
/** Newest change in the run — the one the row renders and selects. */
|
|
19
|
-
representative: IDevtoolsStateChange;
|
|
20
|
-
/**
|
|
21
|
-
* Oldest change in the run — the one that actually moved the state (every later
|
|
22
|
-
* member re-applied the same mutation as a no-op). Its `prevSnapshot` is the
|
|
23
|
-
* real "before" of the whole run, so the detail panel uses it to show what the
|
|
24
|
-
* initial update changed instead of the representative's empty self-diff.
|
|
25
|
-
*/
|
|
26
|
-
oldest: IDevtoolsStateChange;
|
|
27
|
-
/** How many changes the run collapses (1 = ungrouped). */
|
|
28
|
-
count: number;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Structural identity of a change by its *resulting* state: same store, same
|
|
32
|
-
* snapshot. The feature collapses updates that "didn't change the state from the
|
|
33
|
-
* previous update", so we compare the committed state itself rather than the
|
|
34
|
-
* mutation that produced it. Keying on patches would miss the no-op cases this is
|
|
35
|
-
* meant to catch — a fresh object reference with identical content, or a
|
|
36
|
-
* same-value write from a different source (e.g. a devtools edit followed by an
|
|
37
|
-
* app update). Those carry patches yet leave the state untouched, so they belong
|
|
38
|
-
* in the run that already reached this state regardless of source.
|
|
39
|
-
*/
|
|
40
|
-
export declare function changeGroupKey(change: IDevtoolsStateChange): string;
|
|
41
|
-
/** Collapse consecutive structurally-equal changes (list is newest-first). */
|
|
42
|
-
export declare function groupChanges(changes: IDevtoolsStateChange[]): IChangeGroup[];
|
|
43
|
-
export type TDiffKind = "added" | "removed" | "changed";
|
|
44
|
-
export interface IDiffEntry {
|
|
45
|
-
path: string;
|
|
46
|
-
segments: (string | number)[];
|
|
47
|
-
kind: TDiffKind;
|
|
48
|
-
before?: unknown;
|
|
49
|
-
after?: unknown;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Structural diff between two snapshots, flattened to the individual leaf/branch
|
|
53
|
-
* paths that actually changed — the data behind the test-framework-style "Diff"
|
|
54
|
-
* view. Unchanged branches are skipped entirely (Immer's structural sharing
|
|
55
|
-
* means equal branches keep their reference, so this stays cheap).
|
|
56
|
-
*/
|
|
57
|
-
export declare function computeDiff(before: unknown, after: unknown): IDiffEntry[];
|
|
58
|
-
export type TLineDiffKind = "common" | "removed" | "added";
|
|
59
|
-
export interface ILineDiffOp {
|
|
60
|
-
kind: TLineDiffKind;
|
|
61
|
-
text: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Classic LCS line diff between two blocks of text — the data behind the unified
|
|
65
|
-
* git-style "Diff View" and the per-line highlighting in the Before / After
|
|
66
|
-
* panels. Snapshots rendered as pretty JSON stay small, so the O(n·m) table is
|
|
67
|
-
* comfortably cheap.
|
|
68
|
-
*/
|
|
69
|
-
export declare function computeLineDiff(beforeText: string, afterText: string): ILineDiffOp[];
|
|
70
|
-
/**
|
|
71
|
-
* Reorder each contiguous changed hunk so additions sit above removals when
|
|
72
|
-
* `latestFirst` is set (the new state on top). Common lines anchor the hunks in
|
|
73
|
-
* place; only the −/+ lines within a run are regrouped, so the surrounding
|
|
74
|
-
* context never moves. With `latestFirst` off the ops are returned untouched.
|
|
75
|
-
*/
|
|
76
|
-
export declare function orderLineDiffOps(ops: ILineDiffOp[], latestFirst: boolean): ILineDiffOp[];
|
|
77
|
-
export type TCompressedTone = "common" | "added" | "removed" | "placeholder";
|
|
78
|
-
/** Which document a compressed diff is being rendered for. */
|
|
79
|
-
export type TDiffSide = "unified" | "before" | "after";
|
|
80
|
-
export interface ICompressedLine {
|
|
81
|
-
/** Nesting depth — the renderer turns this into leading indentation. */
|
|
82
|
-
depth: number;
|
|
83
|
-
/** Gutter glyph: `+` added, `−` removed, or a space for structure/placeholder. */
|
|
84
|
-
sign: string;
|
|
85
|
-
tone: TCompressedTone;
|
|
86
|
-
/** The line's JSON (or placeholder) text, without indentation. */
|
|
87
|
-
text: string;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* A structure-aware "address" diff: the JSON tree pruned down to just the
|
|
91
|
-
* branches that actually changed. Parents of a change keep their brackets so the
|
|
92
|
-
* path stays legible, and each run of untouched siblings collapses into a single
|
|
93
|
-
* `… N unchanged …` placeholder (which, for arrays, naturally reports the counts
|
|
94
|
-
* before and after a change). `side` picks which document we're rebuilding —
|
|
95
|
-
* `"unified"` shows removed (−) and added (+) together, while `"before"` /
|
|
96
|
-
* `"after"` show only the lines that belong to that one snapshot.
|
|
97
|
-
*/
|
|
98
|
-
export declare function computeCompressedDiff(before: unknown, after: unknown, side: TDiffSide, latestFirst?: boolean): ICompressedLine[];
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
export type TDockSide = "top" | "bottom" | "left" | "right";
|
|
2
|
-
/** A handle to one registered devtool, used to render launch controls. */
|
|
3
|
-
export interface IDockDevtoolRef {
|
|
4
|
-
id: string;
|
|
5
|
-
label: string;
|
|
6
|
-
icon: string;
|
|
7
|
-
badge?: string;
|
|
8
|
-
onOpen: () => void;
|
|
9
|
-
}
|
|
10
|
-
/** The live, syncable part of a devtool's registration. */
|
|
11
|
-
export interface IDockDevtoolSync {
|
|
12
|
-
side: TDockSide;
|
|
13
|
-
size: number;
|
|
14
|
-
open: boolean;
|
|
15
|
-
badge?: string;
|
|
16
|
-
}
|
|
17
|
-
export interface IDockDevtoolInput extends IDockDevtoolSync {
|
|
18
|
-
id: string;
|
|
19
|
-
label: string;
|
|
20
|
-
icon: string;
|
|
21
|
-
onOpen: () => void;
|
|
22
|
-
}
|
|
23
|
-
export interface IDockView {
|
|
24
|
-
/** Offset (px) from the docked edge — stacks open panels on the same side. */
|
|
25
|
-
dockOffset: number;
|
|
26
|
-
/**
|
|
27
|
-
* True when this panel shares its dock side with another open panel (stacked
|
|
28
|
-
* either nearer or further from the edge). Stacked panels square off all their
|
|
29
|
-
* corners so they sit flush as one continuous block — only a panel alone on
|
|
30
|
-
* its side keeps its rounded, page-facing corners.
|
|
31
|
-
*/
|
|
32
|
-
stacked: boolean;
|
|
33
|
-
/** Is any devtool on the page currently open? */
|
|
34
|
-
anyOpen: boolean;
|
|
35
|
-
/** First-registered devtool — the one that renders the combined launcher. */
|
|
36
|
-
isPrimary: boolean;
|
|
37
|
-
/** Every registered devtool, for the combined launcher. */
|
|
38
|
-
devtools: IDockDevtoolRef[];
|
|
39
|
-
/** Closed devtools other than this one, for an open panel's header. */
|
|
40
|
-
otherClosed: IDockDevtoolRef[];
|
|
41
|
-
}
|
|
42
|
-
export interface IDevtoolsDockCoordinator {
|
|
43
|
-
version: number;
|
|
44
|
-
register(panel: IDockDevtoolInput): () => void;
|
|
45
|
-
update(id: string, next: IDockDevtoolSync): void;
|
|
46
|
-
getView(id: string): IDockView;
|
|
47
|
-
subscribe(listener: () => void): () => void;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Returns the page-wide dock coordinator, installing it on `window` the first
|
|
51
|
-
* time it is requested. On the server (no `window`) a throwaway instance is
|
|
52
|
-
* returned so callers can use it unconditionally.
|
|
53
|
-
*/
|
|
54
|
-
export declare function getDevtoolsDockCoordinator(): IDevtoolsDockCoordinator;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export type { IDevtoolsStateChange, IDevtoolsStoreInfo, IStateDevtoolsSnapshot, TDevtoolsPosition, TStateChangeSource, TStateDevtoolsListener, } from "../core/StateDevtools.types";
|
|
2
|
-
export { type IStateDevtoolsCoreOptions, StateDevtoolsCore, } from "../core/StateDevtoolsCore";
|
|
3
|
-
export { type INiceStateDevtoolsProps, NiceStateDevtools } from "./NiceStateDevtools";
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { Patch } from "immer";
|
|
2
|
-
export type TDevtoolsPosition = "dock-bottom" | "dock-top" | "dock-left" | "dock-right";
|
|
3
|
-
/**
|
|
4
|
-
* How a recorded change came to be. `update` is the normal Immer-patch path,
|
|
5
|
-
* `replace` is a whole-state swap with no patches, and the two `devtools-*`
|
|
6
|
-
* sources are writes the devtools itself made (manual edits / reverts) so they
|
|
7
|
-
* can be visually distinguished from real app traffic.
|
|
8
|
-
*/
|
|
9
|
-
export type TStateChangeSource = "update" | "replace" | "devtools-edit" | "devtools-revert";
|
|
10
|
-
/**
|
|
11
|
-
* A single committed mutation of a registered store. Captures both the Immer
|
|
12
|
-
* patches (when available) and full before/after snapshots so the panel can
|
|
13
|
-
* render a precise diff and offer a one-click revert.
|
|
14
|
-
*/
|
|
15
|
-
export interface IDevtoolsStateChange {
|
|
16
|
-
cuid: string;
|
|
17
|
-
storeId: string;
|
|
18
|
-
storeLabel: string;
|
|
19
|
-
timestamp: number;
|
|
20
|
-
patches: Patch[];
|
|
21
|
-
inversePatches: Patch[];
|
|
22
|
-
prevSnapshot: unknown;
|
|
23
|
-
snapshot: unknown;
|
|
24
|
-
source: TStateChangeSource;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Live summary of a registered store, refreshed on every commit so the panel's
|
|
28
|
-
* tabs and state inspector always reflect the current value.
|
|
29
|
-
*/
|
|
30
|
-
export interface IDevtoolsStoreInfo {
|
|
31
|
-
id: string;
|
|
32
|
-
label: string;
|
|
33
|
-
currentState: unknown;
|
|
34
|
-
changeCount: number;
|
|
35
|
-
lastChangeTime?: number;
|
|
36
|
-
}
|
|
37
|
-
export interface IStateDevtoolsSnapshot {
|
|
38
|
-
stores: IDevtoolsStoreInfo[];
|
|
39
|
-
/** Most-recent-first across all registered stores. */
|
|
40
|
-
changes: IDevtoolsStateChange[];
|
|
41
|
-
paused: boolean;
|
|
42
|
-
}
|
|
43
|
-
export type TStateDevtoolsListener = (snapshot: IStateDevtoolsSnapshot) => void;
|