@pyreon/state-tree 0.6.0 → 0.8.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/lib/types/devtools.d.ts +37 -101
- package/lib/types/devtools.d.ts.map +1 -1
- package/lib/types/index.d.ts +178 -296
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/lib/types/devtools2.d.ts +0 -40
- package/lib/types/devtools2.d.ts.map +0 -1
- package/lib/types/index2.d.ts +0 -198
- package/lib/types/index2.d.ts.map +0 -1
package/lib/types/devtools.d.ts
CHANGED
|
@@ -1,104 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
function _notify() {
|
|
37
|
-
for (const listener of _listeners) listener();
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Register a model instance for devtools inspection.
|
|
41
|
-
* Call this when creating instances you want visible in devtools.
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* const counter = Counter.create()
|
|
45
|
-
* registerInstance("app-counter", counter)
|
|
46
|
-
*/
|
|
47
|
-
function registerInstance(name, instance) {
|
|
48
|
-
_activeModels.set(name, new WeakRef(instance));
|
|
49
|
-
_notify();
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Unregister a model instance.
|
|
53
|
-
*/
|
|
54
|
-
function unregisterInstance(name) {
|
|
55
|
-
_activeModels.delete(name);
|
|
56
|
-
_notify();
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Get all registered model instance names.
|
|
60
|
-
* Automatically cleans up garbage-collected instances.
|
|
61
|
-
*/
|
|
62
|
-
function getActiveModels() {
|
|
63
|
-
for (const [name, ref] of _activeModels) if (ref.deref() === void 0) _activeModels.delete(name);
|
|
64
|
-
return [..._activeModels.keys()];
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Get a model instance by name (or undefined if GC'd or not registered).
|
|
68
|
-
*/
|
|
69
|
-
function getModelInstance(name) {
|
|
70
|
-
const ref = _activeModels.get(name);
|
|
71
|
-
if (!ref) return void 0;
|
|
72
|
-
const instance = ref.deref();
|
|
73
|
-
if (!instance) {
|
|
74
|
-
_activeModels.delete(name);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
return instance;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Get a snapshot of a registered model instance.
|
|
81
|
-
*/
|
|
82
|
-
function getModelSnapshot(name) {
|
|
83
|
-
const instance = getModelInstance(name);
|
|
84
|
-
if (!instance) return void 0;
|
|
85
|
-
return getSnapshot(instance);
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Subscribe to model registry changes. Returns unsubscribe function.
|
|
89
|
-
*/
|
|
90
|
-
function onModelChange(listener) {
|
|
91
|
-
_listeners.add(listener);
|
|
92
|
-
return () => {
|
|
93
|
-
_listeners.delete(listener);
|
|
94
|
-
};
|
|
95
|
-
}
|
|
1
|
+
//#region src/devtools.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* @pyreon/state-tree devtools introspection API.
|
|
4
|
+
* Import: `import { ... } from "@pyreon/state-tree/devtools"`
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Register a model instance for devtools inspection.
|
|
8
|
+
* Call this when creating instances you want visible in devtools.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const counter = Counter.create()
|
|
12
|
+
* registerInstance("app-counter", counter)
|
|
13
|
+
*/
|
|
14
|
+
declare function registerInstance(name: string, instance: object): void;
|
|
15
|
+
/**
|
|
16
|
+
* Unregister a model instance.
|
|
17
|
+
*/
|
|
18
|
+
declare function unregisterInstance(name: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Get all registered model instance names.
|
|
21
|
+
* Automatically cleans up garbage-collected instances.
|
|
22
|
+
*/
|
|
23
|
+
declare function getActiveModels(): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Get a model instance by name (or undefined if GC'd or not registered).
|
|
26
|
+
*/
|
|
27
|
+
declare function getModelInstance(name: string): object | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Get a snapshot of a registered model instance.
|
|
30
|
+
*/
|
|
31
|
+
declare function getModelSnapshot(name: string): Record<string, unknown> | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Subscribe to model registry changes. Returns unsubscribe function.
|
|
34
|
+
*/
|
|
35
|
+
declare function onModelChange(listener: () => void): () => void;
|
|
96
36
|
/** @internal — reset devtools registry (for tests). */
|
|
97
|
-
function _resetDevtools()
|
|
98
|
-
_activeModels.clear();
|
|
99
|
-
_listeners.clear();
|
|
100
|
-
}
|
|
101
|
-
|
|
37
|
+
declare function _resetDevtools(): void;
|
|
102
38
|
//#endregion
|
|
103
39
|
export { _resetDevtools, getActiveModels, getModelInstance, getModelSnapshot, onModelChange, registerInstance, unregisterInstance };
|
|
104
|
-
//# sourceMappingURL=
|
|
40
|
+
//# sourceMappingURL=devtools2.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"devtools2.d.ts","names":[],"sources":["../../../src/devtools.ts"],"mappings":";;AAuBA;;;;;AAQA;;;;;AASA;iBAjBgB,gBAAA,CAAiB,IAAA,UAAc,QAAA;;;;iBAQ/B,kBAAA,CAAmB,IAAA;;;;;iBASnB,eAAA,CAAA;;;;iBAUA,gBAAA,CAAiB,IAAA;AAyBjC;;;AAAA,iBAXgB,gBAAA,CACd,IAAA,WACC,MAAA;;AAiBH;;iBARgB,aAAA,CAAc,QAAA;;iBAQd,cAAA,CAAA"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,316 +1,198 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Computed, Signal } from "@pyreon/reactivity";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/** Returns true when a value is a model instance (has metadata registered). */
|
|
10
|
-
function isModelInstance(value) {
|
|
11
|
-
return value != null && typeof value === "object" && instanceMeta.has(value);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/middleware.ts
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
/** Property key stamped on every ModelDefinition to distinguish it from plain objects. */
|
|
5
|
+
declare const MODEL_BRAND: "__pyreonMod";
|
|
6
|
+
type StateShape = Record<string, unknown>;
|
|
16
7
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const dispatch = (idx, c) => {
|
|
28
|
-
if (idx >= meta.middlewares.length) return fn(...c.args);
|
|
29
|
-
const mw = meta.middlewares[idx];
|
|
30
|
-
if (!mw) return fn(...c.args);
|
|
31
|
-
return mw(c, nextCall => dispatch(idx + 1, nextCall));
|
|
32
|
-
};
|
|
33
|
-
return dispatch(0, call);
|
|
34
|
-
}
|
|
8
|
+
* Resolve a state field type:
|
|
9
|
+
* - ModelDefinition → the instance type it produces
|
|
10
|
+
* - Anything else → as-is
|
|
11
|
+
*/
|
|
12
|
+
type ResolveField<T> = T extends {
|
|
13
|
+
readonly __pyreonMod: true;
|
|
14
|
+
create(initial?: any): infer I;
|
|
15
|
+
} ? I : T;
|
|
16
|
+
/** Map state shape to per-field signals. */
|
|
17
|
+
type StateSignals<TState extends StateShape> = { readonly [K in keyof TState]: Signal<ResolveField<TState[K]>> };
|
|
35
18
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
* console.log(`> ${call.name}(${call.args})`)
|
|
44
|
-
* const result = next(call)
|
|
45
|
-
* console.log(`< ${call.name}`)
|
|
46
|
-
* return result
|
|
47
|
-
* })
|
|
48
|
-
*/
|
|
49
|
-
function addMiddleware(instance, middleware) {
|
|
50
|
-
const meta = instanceMeta.get(instance);
|
|
51
|
-
if (!meta) throw new Error("[@pyreon/state-tree] addMiddleware: not a model instance");
|
|
52
|
-
meta.middlewares.push(middleware);
|
|
53
|
-
return () => {
|
|
54
|
-
const idx = meta.middlewares.indexOf(middleware);
|
|
55
|
-
if (idx !== -1) meta.middlewares.splice(idx, 1);
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
//#endregion
|
|
60
|
-
//#region src/patch.ts
|
|
61
|
-
/** Property names that must never be used as patch path segments. */
|
|
62
|
-
|
|
19
|
+
* `self` type inside actions / views:
|
|
20
|
+
* strongly typed for state signals, `any` for actions and views so that
|
|
21
|
+
* actions can call each other without circular type issues.
|
|
22
|
+
*/
|
|
23
|
+
type ModelSelf<TState extends StateShape> = StateSignals<TState> & Record<string, any>;
|
|
24
|
+
/** The public instance type returned by `.create()` and hooks. */
|
|
25
|
+
type ModelInstance<TState extends StateShape, TActions extends Record<string, (...args: any[]) => any>, TViews extends Record<string, Signal<any> | Computed<any>>> = StateSignals<TState> & TActions & TViews;
|
|
63
26
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const read = () => inner();
|
|
72
|
-
read.peek = () => inner.peek();
|
|
73
|
-
read.subscribe = listener => inner.subscribe(listener);
|
|
74
|
-
read.set = newValue => {
|
|
75
|
-
const prev = inner.peek();
|
|
76
|
-
inner.set(newValue);
|
|
77
|
-
if (!Object.is(prev, newValue) && (!hasListeners || hasListeners())) emitPatch({
|
|
78
|
-
op: "replace",
|
|
79
|
-
path,
|
|
80
|
-
value: isModelInstance(newValue) ? snapshotValue(newValue) : newValue
|
|
81
|
-
});
|
|
82
|
-
};
|
|
83
|
-
read.update = fn => {
|
|
84
|
-
read.set(fn(inner.peek()));
|
|
27
|
+
* Extract the state type from a ModelDefinition.
|
|
28
|
+
* Used by Snapshot to recursively resolve nested model types.
|
|
29
|
+
*/
|
|
30
|
+
type ExtractModelState<T> = T extends {
|
|
31
|
+
readonly __pyreonMod: true;
|
|
32
|
+
readonly _config: {
|
|
33
|
+
state: infer S extends StateShape;
|
|
85
34
|
};
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
/** Shallow snapshot helper (avoids importing snapshot.ts to prevent circular deps). */
|
|
89
|
-
function snapshotValue(instance) {
|
|
90
|
-
const meta = instanceMeta.get(instance);
|
|
91
|
-
if (!meta) return instance;
|
|
92
|
-
const out = {};
|
|
93
|
-
for (const key of meta.stateKeys) {
|
|
94
|
-
const sig = instance[key];
|
|
95
|
-
if (!sig) continue;
|
|
96
|
-
const val = sig.peek();
|
|
97
|
-
out[key] = isModelInstance(val) ? snapshotValue(val) : val;
|
|
98
|
-
}
|
|
99
|
-
return out;
|
|
100
|
-
}
|
|
35
|
+
} ? S : never;
|
|
101
36
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
function onPatch(instance, listener) {
|
|
113
|
-
const meta = instanceMeta.get(instance);
|
|
114
|
-
if (!meta) throw new Error("[@pyreon/state-tree] onPatch: not a model instance");
|
|
115
|
-
meta.patchListeners.add(listener);
|
|
116
|
-
return () => meta.patchListeners.delete(listener);
|
|
37
|
+
* Snapshot type: plain JS values (no signals, no model instances).
|
|
38
|
+
* Nested model fields recursively produce their own typed snapshot.
|
|
39
|
+
*/
|
|
40
|
+
type Snapshot<TState extends StateShape> = { [K in keyof TState]: TState[K] extends {
|
|
41
|
+
readonly __pyreonMod: true;
|
|
42
|
+
} ? Snapshot<ExtractModelState<TState[K]>> : TState[K] };
|
|
43
|
+
interface Patch {
|
|
44
|
+
op: 'replace';
|
|
45
|
+
path: string;
|
|
46
|
+
value: unknown;
|
|
117
47
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
* applyPatch(counter, { op: "replace", path: "/count", value: 10 })
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* // Replay patches recorded from onPatch (undo/redo, time-travel)
|
|
130
|
-
* applyPatch(counter, [
|
|
131
|
-
* { op: "replace", path: "/count", value: 1 },
|
|
132
|
-
* { op: "replace", path: "/count", value: 2 },
|
|
133
|
-
* ])
|
|
134
|
-
*/
|
|
135
|
-
function applyPatch(instance, patch) {
|
|
136
|
-
const patches = Array.isArray(patch) ? patch : [patch];
|
|
137
|
-
batch(() => {
|
|
138
|
-
for (const p of patches) {
|
|
139
|
-
if (p.op !== "replace") throw new Error(`[@pyreon/state-tree] applyPatch: unsupported op "${p.op}"`);
|
|
140
|
-
const segments = p.path.split("/").filter(Boolean);
|
|
141
|
-
if (segments.length === 0) throw new Error("[@pyreon/state-tree] applyPatch: empty path");
|
|
142
|
-
let target = instance;
|
|
143
|
-
for (let i = 0; i < segments.length - 1; i++) {
|
|
144
|
-
const segment = segments[i];
|
|
145
|
-
if (RESERVED_KEYS.has(segment)) throw new Error(`[@pyreon/state-tree] applyPatch: reserved property name "${segment}"`);
|
|
146
|
-
if (!instanceMeta.get(target)) throw new Error(`[@pyreon/state-tree] applyPatch: not a model instance at "${segment}"`);
|
|
147
|
-
const sig = target[segment];
|
|
148
|
-
if (!sig || typeof sig.peek !== "function") throw new Error(`[@pyreon/state-tree] applyPatch: unknown state key "${segment}"`);
|
|
149
|
-
const nested = sig.peek();
|
|
150
|
-
if (!nested || typeof nested !== "object" || !isModelInstance(nested)) throw new Error(`[@pyreon/state-tree] applyPatch: "${segment}" is not a nested model instance`);
|
|
151
|
-
target = nested;
|
|
152
|
-
}
|
|
153
|
-
const lastKey = segments[segments.length - 1];
|
|
154
|
-
if (RESERVED_KEYS.has(lastKey)) throw new Error(`[@pyreon/state-tree] applyPatch: reserved property name "${lastKey}"`);
|
|
155
|
-
const meta = instanceMeta.get(target);
|
|
156
|
-
if (!meta) throw new Error("[@pyreon/state-tree] applyPatch: not a model instance");
|
|
157
|
-
if (!meta.stateKeys.includes(lastKey)) throw new Error(`[@pyreon/state-tree] applyPatch: unknown state key "${lastKey}"`);
|
|
158
|
-
const sig = target[lastKey];
|
|
159
|
-
if (sig && typeof sig.set === "function") sig.set(p.value);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
48
|
+
type PatchListener = (patch: Patch) => void;
|
|
49
|
+
interface ActionCall {
|
|
50
|
+
/** Action name. */
|
|
51
|
+
name: string;
|
|
52
|
+
/** Arguments passed to the action. */
|
|
53
|
+
args: unknown[];
|
|
54
|
+
/** JSON-pointer-style path, e.g. `"/inc"`. */
|
|
55
|
+
path: string;
|
|
162
56
|
}
|
|
163
|
-
|
|
57
|
+
type MiddlewareFn = (call: ActionCall, next: (nextCall: ActionCall) => unknown) => unknown;
|
|
164
58
|
//#endregion
|
|
165
|
-
//#region src/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
function isModelDef(v) {
|
|
171
|
-
if (v == null || typeof v !== "object") return false;
|
|
172
|
-
return v[MODEL_BRAND] === true;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Create a live model instance from a config + optional initial snapshot.
|
|
176
|
-
* Called by `ModelDefinition.create()`.
|
|
177
|
-
*/
|
|
178
|
-
function createInstance(config, initial) {
|
|
179
|
-
const instance = {};
|
|
180
|
-
const meta = {
|
|
181
|
-
stateKeys: [],
|
|
182
|
-
patchListeners: /* @__PURE__ */new Set(),
|
|
183
|
-
middlewares: [],
|
|
184
|
-
emitPatch(patch) {
|
|
185
|
-
if (this.patchListeners.size === 0) return;
|
|
186
|
-
for (const listener of this.patchListeners) listener(patch);
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
instanceMeta.set(instance, meta);
|
|
190
|
-
const self = new Proxy(instance, {
|
|
191
|
-
get(_, k) {
|
|
192
|
-
return instance[k];
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
for (const [key, defaultValue] of Object.entries(config.state)) {
|
|
196
|
-
meta.stateKeys.push(key);
|
|
197
|
-
const path = `/${key}`;
|
|
198
|
-
const initValue = key in initial ? initial[key] : void 0;
|
|
199
|
-
let rawSig;
|
|
200
|
-
if (isModelDef(defaultValue)) {
|
|
201
|
-
const nestedInstance = createInstance(defaultValue._config, initValue ?? {});
|
|
202
|
-
rawSig = signal(nestedInstance);
|
|
203
|
-
onPatch(nestedInstance, patch => {
|
|
204
|
-
meta.emitPatch({
|
|
205
|
-
...patch,
|
|
206
|
-
path: path + patch.path
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
} else rawSig = signal(initValue !== void 0 ? initValue : defaultValue);
|
|
210
|
-
instance[key] = trackedSignal(rawSig, path, p => meta.emitPatch(p), () => meta.patchListeners.size > 0);
|
|
211
|
-
}
|
|
212
|
-
if (config.views) {
|
|
213
|
-
const views = config.views(self);
|
|
214
|
-
for (const [key, view] of Object.entries(views)) instance[key] = view;
|
|
215
|
-
}
|
|
216
|
-
if (config.actions) {
|
|
217
|
-
const rawActions = config.actions(self);
|
|
218
|
-
for (const [key, actionFn] of Object.entries(rawActions)) instance[key] = (...args) => runAction(meta, key, actionFn, args);
|
|
219
|
-
}
|
|
220
|
-
return instance;
|
|
59
|
+
//#region src/instance.d.ts
|
|
60
|
+
interface ModelConfig<TState extends StateShape, TActions, TViews> {
|
|
61
|
+
state: TState;
|
|
62
|
+
views?: (self: any) => TViews;
|
|
63
|
+
actions?: (self: any) => TActions;
|
|
221
64
|
}
|
|
222
|
-
|
|
223
65
|
//#endregion
|
|
224
|
-
//#region src/model.ts
|
|
225
|
-
|
|
66
|
+
//#region src/model.d.ts
|
|
226
67
|
/** Destroy a hook singleton by id so next call re-creates the instance. */
|
|
227
|
-
function resetHook(id)
|
|
228
|
-
_hookRegistry.delete(id);
|
|
229
|
-
}
|
|
68
|
+
declare function resetHook(id: string): void;
|
|
230
69
|
/** Destroy all hook singletons. */
|
|
231
|
-
function resetAllHooks()
|
|
232
|
-
|
|
70
|
+
declare function resetAllHooks(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Returned by `model()`. Call `.create()` for instances or `.asHook(id)` for
|
|
73
|
+
* a Zustand-style singleton hook.
|
|
74
|
+
*/
|
|
75
|
+
declare class ModelDefinition<TState extends StateShape, TActions extends Record<string, (...args: any[]) => any>, TViews extends Record<string, Signal<any> | Computed<any>>> {
|
|
76
|
+
/** Brand used to identify ModelDefinition objects at runtime (without instanceof). */
|
|
77
|
+
readonly [MODEL_BRAND]: true;
|
|
78
|
+
/** @internal — exposed so nested instance creation can read it. */
|
|
79
|
+
readonly _config: ModelConfig<TState, TActions, TViews>;
|
|
80
|
+
constructor(config: ModelConfig<TState, TActions, TViews>);
|
|
81
|
+
/**
|
|
82
|
+
* Create a new independent model instance.
|
|
83
|
+
* Pass a partial snapshot to override defaults.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const counter = Counter.create({ count: 5 })
|
|
87
|
+
*/
|
|
88
|
+
create(initial?: Partial<Snapshot<TState>>): ModelInstance<TState, TActions, TViews>;
|
|
89
|
+
/**
|
|
90
|
+
* Returns a hook function that always returns the same singleton instance
|
|
91
|
+
* for the given `id` — Zustand / Pinia style.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const useCounter = Counter.asHook("app-counter")
|
|
95
|
+
* // Any call to useCounter() returns the same instance.
|
|
96
|
+
* const store = useCounter()
|
|
97
|
+
*/
|
|
98
|
+
asHook(id: string): () => ModelInstance<TState, TActions, TViews>;
|
|
233
99
|
}
|
|
234
100
|
/**
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
|
|
238
|
-
|
|
101
|
+
* Define a reactive model with state, views, and actions.
|
|
102
|
+
*
|
|
103
|
+
* - **state** — plain JS object; each key becomes a `Signal<T>` on the instance.
|
|
104
|
+
* - **views** — factory receiving `self`; return computed signals for derived state.
|
|
105
|
+
* - **actions** — factory receiving `self`; return functions that mutate state.
|
|
106
|
+
*
|
|
107
|
+
* Use nested `ModelDefinition` values in `state` to compose models.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* const Counter = model({
|
|
111
|
+
* state: { count: 0 },
|
|
112
|
+
* views: (self) => ({
|
|
113
|
+
* doubled: computed(() => self.count() * 2),
|
|
114
|
+
* }),
|
|
115
|
+
* actions: (self) => ({
|
|
116
|
+
* inc: () => self.count.update(c => c + 1),
|
|
117
|
+
* reset: () => self.count.set(0),
|
|
118
|
+
* }),
|
|
119
|
+
* })
|
|
120
|
+
*
|
|
121
|
+
* const c = Counter.create({ count: 5 })
|
|
122
|
+
* c.count() // 5
|
|
123
|
+
* c.inc()
|
|
124
|
+
* c.doubled() // 12
|
|
125
|
+
*/
|
|
126
|
+
declare function model<TState extends StateShape, TActions extends Record<string, (...args: any[]) => any> = Record<never, never>, TViews extends Record<string, Signal<any> | Computed<any>> = Record<never, never>>(config: ModelConfig<TState, TActions, TViews>): ModelDefinition<TState, TActions, TViews>;
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/snapshot.d.ts
|
|
239
129
|
/**
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
* }),
|
|
258
|
-
* })
|
|
259
|
-
*
|
|
260
|
-
* const c = Counter.create({ count: 5 })
|
|
261
|
-
* c.count() // 5
|
|
262
|
-
* c.inc()
|
|
263
|
-
* c.doubled() // 12
|
|
264
|
-
*/
|
|
265
|
-
function model(config) {
|
|
266
|
-
return new ModelDefinition(config);
|
|
267
|
-
}
|
|
268
|
-
|
|
130
|
+
* Serialize a model instance to a plain JS object (no signals, no functions).
|
|
131
|
+
* Nested model instances are recursively serialized.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* getSnapshot(counter) // { count: 6 }
|
|
135
|
+
* getSnapshot(app) // { profile: { name: "Alice" }, title: "My App" }
|
|
136
|
+
*/
|
|
137
|
+
declare function getSnapshot<TState extends StateShape>(instance: object): Snapshot<TState>;
|
|
138
|
+
/**
|
|
139
|
+
* Restore a model instance from a plain-object snapshot.
|
|
140
|
+
* All signal writes are coalesced via `batch()` for a single reactive flush.
|
|
141
|
+
* Keys absent from the snapshot are left unchanged.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* applySnapshot(counter, { count: 0 })
|
|
145
|
+
*/
|
|
146
|
+
declare function applySnapshot<TState extends StateShape>(instance: object, snapshot: Partial<Snapshot<TState>>): void;
|
|
269
147
|
//#endregion
|
|
270
|
-
//#region src/
|
|
148
|
+
//#region src/patch.d.ts
|
|
271
149
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
for (const key of meta.stateKeys) {
|
|
284
|
-
const sig = instance[key];
|
|
285
|
-
if (!sig) continue;
|
|
286
|
-
const val = sig.peek();
|
|
287
|
-
out[key] = isModelInstance(val) ? getSnapshot(val) : val;
|
|
288
|
-
}
|
|
289
|
-
return out;
|
|
290
|
-
}
|
|
150
|
+
* Subscribe to every state mutation in `instance` as a JSON patch.
|
|
151
|
+
* Also captures mutations in nested model instances (path is prefixed).
|
|
152
|
+
*
|
|
153
|
+
* Returns an unsubscribe function.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* const unsub = onPatch(counter, patch => {
|
|
157
|
+
* // { op: "replace", path: "/count", value: 6 }
|
|
158
|
+
* })
|
|
159
|
+
*/
|
|
160
|
+
declare function onPatch(instance: object, listener: PatchListener): () => void;
|
|
291
161
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
162
|
+
* Apply a JSON patch (or array of patches) to a model instance.
|
|
163
|
+
* Only "replace" operations are supported (matching the patches emitted by `onPatch`).
|
|
164
|
+
*
|
|
165
|
+
* Paths use JSON pointer format: `"/count"` for top-level, `"/profile/name"` for nested.
|
|
166
|
+
* Nested model instances are resolved automatically.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* applyPatch(counter, { op: "replace", path: "/count", value: 10 })
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* // Replay patches recorded from onPatch (undo/redo, time-travel)
|
|
173
|
+
* applyPatch(counter, [
|
|
174
|
+
* { op: "replace", path: "/count", value: 1 },
|
|
175
|
+
* { op: "replace", path: "/count", value: 2 },
|
|
176
|
+
* ])
|
|
177
|
+
*/
|
|
178
|
+
declare function applyPatch(instance: object, patch: Patch | Patch[]): void;
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/middleware.d.ts
|
|
181
|
+
/**
|
|
182
|
+
* Intercept every action call on `instance`.
|
|
183
|
+
* Middlewares run in registration order — call `next(call)` to continue.
|
|
184
|
+
*
|
|
185
|
+
* Returns an unsubscribe function.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* const unsub = addMiddleware(counter, (call, next) => {
|
|
189
|
+
* console.log(`> ${call.name}(${call.args})`)
|
|
190
|
+
* const result = next(call)
|
|
191
|
+
* console.log(`< ${call.name}`)
|
|
192
|
+
* return result
|
|
193
|
+
* })
|
|
194
|
+
*/
|
|
195
|
+
declare function addMiddleware(instance: object, middleware: MiddlewareFn): () => void;
|
|
314
196
|
//#endregion
|
|
315
|
-
export { addMiddleware, applyPatch, applySnapshot, getSnapshot, model, onPatch, resetAllHooks, resetHook };
|
|
316
|
-
//# sourceMappingURL=
|
|
197
|
+
export { type ActionCall, type MiddlewareFn, type ModelDefinition, type ModelInstance, type ModelSelf, type Patch, type PatchListener, type Snapshot, type StateShape, addMiddleware, applyPatch, applySnapshot, getSnapshot, model, onPatch, resetAllHooks, resetHook };
|
|
198
|
+
//# sourceMappingURL=index2.d.ts.map
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"
|
|
1
|
+
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/types.ts","../../../src/instance.ts","../../../src/model.ts","../../../src/snapshot.ts","../../../src/patch.ts","../../../src/middleware.ts"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/state-tree",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Structured reactive state tree — composable models with snapshots, patches, and middleware",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@pyreon/reactivity": ">=0.
|
|
48
|
+
"@pyreon/reactivity": ">=0.7.0 <0.8.0"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/lib/types/devtools2.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
//#region src/devtools.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* @pyreon/state-tree devtools introspection API.
|
|
4
|
-
* Import: `import { ... } from "@pyreon/state-tree/devtools"`
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Register a model instance for devtools inspection.
|
|
8
|
-
* Call this when creating instances you want visible in devtools.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* const counter = Counter.create()
|
|
12
|
-
* registerInstance("app-counter", counter)
|
|
13
|
-
*/
|
|
14
|
-
declare function registerInstance(name: string, instance: object): void;
|
|
15
|
-
/**
|
|
16
|
-
* Unregister a model instance.
|
|
17
|
-
*/
|
|
18
|
-
declare function unregisterInstance(name: string): void;
|
|
19
|
-
/**
|
|
20
|
-
* Get all registered model instance names.
|
|
21
|
-
* Automatically cleans up garbage-collected instances.
|
|
22
|
-
*/
|
|
23
|
-
declare function getActiveModels(): string[];
|
|
24
|
-
/**
|
|
25
|
-
* Get a model instance by name (or undefined if GC'd or not registered).
|
|
26
|
-
*/
|
|
27
|
-
declare function getModelInstance(name: string): object | undefined;
|
|
28
|
-
/**
|
|
29
|
-
* Get a snapshot of a registered model instance.
|
|
30
|
-
*/
|
|
31
|
-
declare function getModelSnapshot(name: string): Record<string, unknown> | undefined;
|
|
32
|
-
/**
|
|
33
|
-
* Subscribe to model registry changes. Returns unsubscribe function.
|
|
34
|
-
*/
|
|
35
|
-
declare function onModelChange(listener: () => void): () => void;
|
|
36
|
-
/** @internal — reset devtools registry (for tests). */
|
|
37
|
-
declare function _resetDevtools(): void;
|
|
38
|
-
//#endregion
|
|
39
|
-
export { _resetDevtools, getActiveModels, getModelInstance, getModelSnapshot, onModelChange, registerInstance, unregisterInstance };
|
|
40
|
-
//# sourceMappingURL=devtools2.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"devtools2.d.ts","names":[],"sources":["../../src/devtools.ts"],"mappings":";;AAuBA;;;;;AAQA;;;;;AASA;iBAjBgB,gBAAA,CAAiB,IAAA,UAAc,QAAA;;;;iBAQ/B,kBAAA,CAAmB,IAAA;;;;;iBASnB,eAAA,CAAA;;;;iBAUA,gBAAA,CAAiB,IAAA;AAyBjC;;;AAAA,iBAXgB,gBAAA,CACd,IAAA,WACC,MAAA;;AAiBH;;iBARgB,aAAA,CAAc,QAAA;;iBAQd,cAAA,CAAA"}
|
package/lib/types/index2.d.ts
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { Computed, Signal } from "@pyreon/reactivity";
|
|
2
|
-
|
|
3
|
-
//#region src/types.d.ts
|
|
4
|
-
/** Property key stamped on every ModelDefinition to distinguish it from plain objects. */
|
|
5
|
-
declare const MODEL_BRAND: "__pyreonMod";
|
|
6
|
-
type StateShape = Record<string, unknown>;
|
|
7
|
-
/**
|
|
8
|
-
* Resolve a state field type:
|
|
9
|
-
* - ModelDefinition → the instance type it produces
|
|
10
|
-
* - Anything else → as-is
|
|
11
|
-
*/
|
|
12
|
-
type ResolveField<T> = T extends {
|
|
13
|
-
readonly __pyreonMod: true;
|
|
14
|
-
create(initial?: any): infer I;
|
|
15
|
-
} ? I : T;
|
|
16
|
-
/** Map state shape to per-field signals. */
|
|
17
|
-
type StateSignals<TState extends StateShape> = { readonly [K in keyof TState]: Signal<ResolveField<TState[K]>> };
|
|
18
|
-
/**
|
|
19
|
-
* `self` type inside actions / views:
|
|
20
|
-
* strongly typed for state signals, `any` for actions and views so that
|
|
21
|
-
* actions can call each other without circular type issues.
|
|
22
|
-
*/
|
|
23
|
-
type ModelSelf<TState extends StateShape> = StateSignals<TState> & Record<string, any>;
|
|
24
|
-
/** The public instance type returned by `.create()` and hooks. */
|
|
25
|
-
type ModelInstance<TState extends StateShape, TActions extends Record<string, (...args: any[]) => any>, TViews extends Record<string, Signal<any> | Computed<any>>> = StateSignals<TState> & TActions & TViews;
|
|
26
|
-
/**
|
|
27
|
-
* Extract the state type from a ModelDefinition.
|
|
28
|
-
* Used by Snapshot to recursively resolve nested model types.
|
|
29
|
-
*/
|
|
30
|
-
type ExtractModelState<T> = T extends {
|
|
31
|
-
readonly __pyreonMod: true;
|
|
32
|
-
readonly _config: {
|
|
33
|
-
state: infer S extends StateShape;
|
|
34
|
-
};
|
|
35
|
-
} ? S : never;
|
|
36
|
-
/**
|
|
37
|
-
* Snapshot type: plain JS values (no signals, no model instances).
|
|
38
|
-
* Nested model fields recursively produce their own typed snapshot.
|
|
39
|
-
*/
|
|
40
|
-
type Snapshot<TState extends StateShape> = { [K in keyof TState]: TState[K] extends {
|
|
41
|
-
readonly __pyreonMod: true;
|
|
42
|
-
} ? Snapshot<ExtractModelState<TState[K]>> : TState[K] };
|
|
43
|
-
interface Patch {
|
|
44
|
-
op: 'replace';
|
|
45
|
-
path: string;
|
|
46
|
-
value: unknown;
|
|
47
|
-
}
|
|
48
|
-
type PatchListener = (patch: Patch) => void;
|
|
49
|
-
interface ActionCall {
|
|
50
|
-
/** Action name. */
|
|
51
|
-
name: string;
|
|
52
|
-
/** Arguments passed to the action. */
|
|
53
|
-
args: unknown[];
|
|
54
|
-
/** JSON-pointer-style path, e.g. `"/inc"`. */
|
|
55
|
-
path: string;
|
|
56
|
-
}
|
|
57
|
-
type MiddlewareFn = (call: ActionCall, next: (nextCall: ActionCall) => unknown) => unknown;
|
|
58
|
-
//#endregion
|
|
59
|
-
//#region src/instance.d.ts
|
|
60
|
-
interface ModelConfig<TState extends StateShape, TActions, TViews> {
|
|
61
|
-
state: TState;
|
|
62
|
-
views?: (self: any) => TViews;
|
|
63
|
-
actions?: (self: any) => TActions;
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/model.d.ts
|
|
67
|
-
/** Destroy a hook singleton by id so next call re-creates the instance. */
|
|
68
|
-
declare function resetHook(id: string): void;
|
|
69
|
-
/** Destroy all hook singletons. */
|
|
70
|
-
declare function resetAllHooks(): void;
|
|
71
|
-
/**
|
|
72
|
-
* Returned by `model()`. Call `.create()` for instances or `.asHook(id)` for
|
|
73
|
-
* a Zustand-style singleton hook.
|
|
74
|
-
*/
|
|
75
|
-
declare class ModelDefinition<TState extends StateShape, TActions extends Record<string, (...args: any[]) => any>, TViews extends Record<string, Signal<any> | Computed<any>>> {
|
|
76
|
-
/** Brand used to identify ModelDefinition objects at runtime (without instanceof). */
|
|
77
|
-
readonly [MODEL_BRAND]: true;
|
|
78
|
-
/** @internal — exposed so nested instance creation can read it. */
|
|
79
|
-
readonly _config: ModelConfig<TState, TActions, TViews>;
|
|
80
|
-
constructor(config: ModelConfig<TState, TActions, TViews>);
|
|
81
|
-
/**
|
|
82
|
-
* Create a new independent model instance.
|
|
83
|
-
* Pass a partial snapshot to override defaults.
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* const counter = Counter.create({ count: 5 })
|
|
87
|
-
*/
|
|
88
|
-
create(initial?: Partial<Snapshot<TState>>): ModelInstance<TState, TActions, TViews>;
|
|
89
|
-
/**
|
|
90
|
-
* Returns a hook function that always returns the same singleton instance
|
|
91
|
-
* for the given `id` — Zustand / Pinia style.
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* const useCounter = Counter.asHook("app-counter")
|
|
95
|
-
* // Any call to useCounter() returns the same instance.
|
|
96
|
-
* const store = useCounter()
|
|
97
|
-
*/
|
|
98
|
-
asHook(id: string): () => ModelInstance<TState, TActions, TViews>;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Define a reactive model with state, views, and actions.
|
|
102
|
-
*
|
|
103
|
-
* - **state** — plain JS object; each key becomes a `Signal<T>` on the instance.
|
|
104
|
-
* - **views** — factory receiving `self`; return computed signals for derived state.
|
|
105
|
-
* - **actions** — factory receiving `self`; return functions that mutate state.
|
|
106
|
-
*
|
|
107
|
-
* Use nested `ModelDefinition` values in `state` to compose models.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* const Counter = model({
|
|
111
|
-
* state: { count: 0 },
|
|
112
|
-
* views: (self) => ({
|
|
113
|
-
* doubled: computed(() => self.count() * 2),
|
|
114
|
-
* }),
|
|
115
|
-
* actions: (self) => ({
|
|
116
|
-
* inc: () => self.count.update(c => c + 1),
|
|
117
|
-
* reset: () => self.count.set(0),
|
|
118
|
-
* }),
|
|
119
|
-
* })
|
|
120
|
-
*
|
|
121
|
-
* const c = Counter.create({ count: 5 })
|
|
122
|
-
* c.count() // 5
|
|
123
|
-
* c.inc()
|
|
124
|
-
* c.doubled() // 12
|
|
125
|
-
*/
|
|
126
|
-
declare function model<TState extends StateShape, TActions extends Record<string, (...args: any[]) => any> = Record<never, never>, TViews extends Record<string, Signal<any> | Computed<any>> = Record<never, never>>(config: ModelConfig<TState, TActions, TViews>): ModelDefinition<TState, TActions, TViews>;
|
|
127
|
-
//#endregion
|
|
128
|
-
//#region src/snapshot.d.ts
|
|
129
|
-
/**
|
|
130
|
-
* Serialize a model instance to a plain JS object (no signals, no functions).
|
|
131
|
-
* Nested model instances are recursively serialized.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* getSnapshot(counter) // { count: 6 }
|
|
135
|
-
* getSnapshot(app) // { profile: { name: "Alice" }, title: "My App" }
|
|
136
|
-
*/
|
|
137
|
-
declare function getSnapshot<TState extends StateShape>(instance: object): Snapshot<TState>;
|
|
138
|
-
/**
|
|
139
|
-
* Restore a model instance from a plain-object snapshot.
|
|
140
|
-
* All signal writes are coalesced via `batch()` for a single reactive flush.
|
|
141
|
-
* Keys absent from the snapshot are left unchanged.
|
|
142
|
-
*
|
|
143
|
-
* @example
|
|
144
|
-
* applySnapshot(counter, { count: 0 })
|
|
145
|
-
*/
|
|
146
|
-
declare function applySnapshot<TState extends StateShape>(instance: object, snapshot: Partial<Snapshot<TState>>): void;
|
|
147
|
-
//#endregion
|
|
148
|
-
//#region src/patch.d.ts
|
|
149
|
-
/**
|
|
150
|
-
* Subscribe to every state mutation in `instance` as a JSON patch.
|
|
151
|
-
* Also captures mutations in nested model instances (path is prefixed).
|
|
152
|
-
*
|
|
153
|
-
* Returns an unsubscribe function.
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* const unsub = onPatch(counter, patch => {
|
|
157
|
-
* // { op: "replace", path: "/count", value: 6 }
|
|
158
|
-
* })
|
|
159
|
-
*/
|
|
160
|
-
declare function onPatch(instance: object, listener: PatchListener): () => void;
|
|
161
|
-
/**
|
|
162
|
-
* Apply a JSON patch (or array of patches) to a model instance.
|
|
163
|
-
* Only "replace" operations are supported (matching the patches emitted by `onPatch`).
|
|
164
|
-
*
|
|
165
|
-
* Paths use JSON pointer format: `"/count"` for top-level, `"/profile/name"` for nested.
|
|
166
|
-
* Nested model instances are resolved automatically.
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* applyPatch(counter, { op: "replace", path: "/count", value: 10 })
|
|
170
|
-
*
|
|
171
|
-
* @example
|
|
172
|
-
* // Replay patches recorded from onPatch (undo/redo, time-travel)
|
|
173
|
-
* applyPatch(counter, [
|
|
174
|
-
* { op: "replace", path: "/count", value: 1 },
|
|
175
|
-
* { op: "replace", path: "/count", value: 2 },
|
|
176
|
-
* ])
|
|
177
|
-
*/
|
|
178
|
-
declare function applyPatch(instance: object, patch: Patch | Patch[]): void;
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region src/middleware.d.ts
|
|
181
|
-
/**
|
|
182
|
-
* Intercept every action call on `instance`.
|
|
183
|
-
* Middlewares run in registration order — call `next(call)` to continue.
|
|
184
|
-
*
|
|
185
|
-
* Returns an unsubscribe function.
|
|
186
|
-
*
|
|
187
|
-
* @example
|
|
188
|
-
* const unsub = addMiddleware(counter, (call, next) => {
|
|
189
|
-
* console.log(`> ${call.name}(${call.args})`)
|
|
190
|
-
* const result = next(call)
|
|
191
|
-
* console.log(`< ${call.name}`)
|
|
192
|
-
* return result
|
|
193
|
-
* })
|
|
194
|
-
*/
|
|
195
|
-
declare function addMiddleware(instance: object, middleware: MiddlewareFn): () => void;
|
|
196
|
-
//#endregion
|
|
197
|
-
export { type ActionCall, type MiddlewareFn, type ModelDefinition, type ModelInstance, type ModelSelf, type Patch, type PatchListener, type Snapshot, type StateShape, addMiddleware, applyPatch, applySnapshot, getSnapshot, model, onPatch, resetAllHooks, resetHook };
|
|
198
|
-
//# sourceMappingURL=index2.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/types.ts","../../src/instance.ts","../../src/model.ts","../../src/snapshot.ts","../../src/patch.ts","../../src/middleware.ts"],"mappings":"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"}
|