@pyreon/state-tree 0.21.0 → 0.23.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/README.md +20 -2
- package/lib/_chunks/snapshot-dNsO8P8p.js +62 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/devtools.js +1 -34
- package/lib/index.js +1 -58
- package/package.json +3 -3
- package/lib/analysis/devtools.js.html +0 -5406
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# @pyreon/state-tree
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MobX-State-Tree-inspired structured models on signals — `model({ state, views, actions })`.
|
|
4
|
+
|
|
5
|
+
Structured reactive state trees built on `@pyreon/reactivity`: `model({ state, views, actions })` composes signal-backed state, computed views, and the only legal mutation path (actions). Supports nested model composition, typed snapshots, JSON-patch record/replay (replace only), and action interception middleware. Fits when `@pyreon/store` is too flat — use it for tree-shaped domain models with undo/redo, time-travel, or audit-log requirements.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
bun add @pyreon/state-tree
|
|
10
|
+
bun add @pyreon/state-tree @pyreon/reactivity
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Quick Start
|
|
@@ -247,3 +249,19 @@ applyPatch(counter, history) // replays to count: 2
|
|
|
247
249
|
- `applySnapshot` leaves missing keys unchanged; it does not delete extra state.
|
|
248
250
|
- `self` inside actions/views is loosely typed for non-state keys to prevent circular type resolution. Use explicit types if needed.
|
|
249
251
|
- Always call `resetAllHooks()` in test `afterEach` to prevent singleton leakage between tests.
|
|
252
|
+
|
|
253
|
+
## Devtools
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
import { stateTreeRegistry } from '@pyreon/state-tree/devtools'
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
WeakRef-based registry of live model instances — tree-shakeable. Used by the Pyreon devtools panel.
|
|
260
|
+
|
|
261
|
+
## Documentation
|
|
262
|
+
|
|
263
|
+
Full docs: [docs.pyreon.dev/docs/state-tree](https://docs.pyreon.dev/docs/state-tree) (or `docs/docs/state-tree.md` in this repo).
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
MIT
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { batch } from "@pyreon/reactivity";
|
|
2
|
+
|
|
3
|
+
//#region src/registry.ts
|
|
4
|
+
/**
|
|
5
|
+
* WeakMap from every model instance object → its internal metadata.
|
|
6
|
+
* Shared across patch, middleware, and snapshot modules.
|
|
7
|
+
*/
|
|
8
|
+
const instanceMeta = /* @__PURE__ */ new WeakMap();
|
|
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/snapshot.ts
|
|
16
|
+
/**
|
|
17
|
+
* Serialize a model instance to a plain JS object (no signals, no functions).
|
|
18
|
+
* Nested model instances are recursively serialized.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* getSnapshot(counter) // { count: 6 }
|
|
22
|
+
* getSnapshot(app) // { profile: { name: "Alice" }, title: "My App" }
|
|
23
|
+
*/
|
|
24
|
+
function getSnapshot(instance) {
|
|
25
|
+
const meta = instanceMeta.get(instance);
|
|
26
|
+
if (!meta) throw new Error("[@pyreon/state-tree] getSnapshot: not a model instance");
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const key of meta.stateKeys) {
|
|
29
|
+
const sig = instance[key];
|
|
30
|
+
if (!sig) continue;
|
|
31
|
+
const val = sig.peek();
|
|
32
|
+
out[key] = isModelInstance(val) ? getSnapshot(val) : val;
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Restore a model instance from a plain-object snapshot.
|
|
38
|
+
* All signal writes are coalesced via `batch()` for a single reactive flush.
|
|
39
|
+
* Keys absent from the snapshot are left unchanged.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* applySnapshot(counter, { count: 0 })
|
|
43
|
+
*/
|
|
44
|
+
function applySnapshot(instance, snapshot) {
|
|
45
|
+
const meta = instanceMeta.get(instance);
|
|
46
|
+
if (!meta) throw new Error("[@pyreon/state-tree] applySnapshot: not a model instance");
|
|
47
|
+
batch(() => {
|
|
48
|
+
for (const key of meta.stateKeys) {
|
|
49
|
+
if (!(key in snapshot)) continue;
|
|
50
|
+
const sig = instance[key];
|
|
51
|
+
if (!sig) continue;
|
|
52
|
+
const val = snapshot[key];
|
|
53
|
+
const current = sig.peek();
|
|
54
|
+
if (isModelInstance(current)) applySnapshot(current, val);
|
|
55
|
+
else sig.set(val);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { isModelInstance as i, getSnapshot as n, instanceMeta as r, applySnapshot as t };
|
|
62
|
+
//# sourceMappingURL=snapshot-dNsO8P8p.js.map
|
|
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
|
|
|
5386
5386
|
</script>
|
|
5387
5387
|
<script>
|
|
5388
5388
|
/*<!--*/
|
|
5389
|
-
const data = {"version":2,"tree":{"name":"root","children":[{"name":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"devtools.js","children":[{"name":"src/devtools.ts","uid":"33598a70-1"}]},{"name":"index.js","children":[{"name":"src","children":[{"uid":"33598a70-3","name":"middleware.ts"},{"uid":"33598a70-5","name":"patch.ts"},{"uid":"33598a70-7","name":"types.ts"},{"uid":"33598a70-9","name":"instance.ts"},{"uid":"33598a70-11","name":"model.ts"},{"uid":"33598a70-13","name":"index.ts"}]}]},{"name":"_chunks/snapshot-dNsO8P8p.js","children":[{"name":"src","children":[{"uid":"33598a70-15","name":"registry.ts"},{"uid":"33598a70-17","name":"snapshot.ts"}]}]}],"isRoot":true},"nodeParts":{"33598a70-1":{"renderedLength":1843,"gzipLength":729,"brotliLength":0,"metaUid":"33598a70-0"},"33598a70-3":{"renderedLength":1291,"gzipLength":627,"brotliLength":0,"metaUid":"33598a70-2"},"33598a70-5":{"renderedLength":4252,"gzipLength":1554,"brotliLength":0,"metaUid":"33598a70-4"},"33598a70-7":{"renderedLength":162,"gzipLength":155,"brotliLength":0,"metaUid":"33598a70-6"},"33598a70-9":{"renderedLength":1691,"gzipLength":758,"brotliLength":0,"metaUid":"33598a70-8"},"33598a70-11":{"renderedLength":2187,"gzipLength":1001,"brotliLength":0,"metaUid":"33598a70-10"},"33598a70-13":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"33598a70-12"},"33598a70-15":{"renderedLength":420,"gzipLength":292,"brotliLength":0,"metaUid":"33598a70-14"},"33598a70-17":{"renderedLength":1396,"gzipLength":602,"brotliLength":0,"metaUid":"33598a70-16"}},"nodeMetas":{"33598a70-0":{"id":"/src/devtools.ts","moduleParts":{"devtools.js":"33598a70-1"},"imported":[{"uid":"33598a70-16"}],"importedBy":[],"isEntry":true},"33598a70-2":{"id":"/src/middleware.ts","moduleParts":{"index.js":"33598a70-3"},"imported":[{"uid":"33598a70-14"}],"importedBy":[{"uid":"33598a70-12"},{"uid":"33598a70-8"}]},"33598a70-4":{"id":"/src/patch.ts","moduleParts":{"index.js":"33598a70-5"},"imported":[{"uid":"33598a70-18"},{"uid":"33598a70-14"}],"importedBy":[{"uid":"33598a70-12"},{"uid":"33598a70-8"}]},"33598a70-6":{"id":"/src/types.ts","moduleParts":{"index.js":"33598a70-7"},"imported":[],"importedBy":[{"uid":"33598a70-10"},{"uid":"33598a70-8"}]},"33598a70-8":{"id":"/src/instance.ts","moduleParts":{"index.js":"33598a70-9"},"imported":[{"uid":"33598a70-18"},{"uid":"33598a70-2"},{"uid":"33598a70-4"},{"uid":"33598a70-14"},{"uid":"33598a70-6"}],"importedBy":[{"uid":"33598a70-10"}]},"33598a70-10":{"id":"/src/model.ts","moduleParts":{"index.js":"33598a70-11"},"imported":[{"uid":"33598a70-8"},{"uid":"33598a70-6"}],"importedBy":[{"uid":"33598a70-12"}]},"33598a70-12":{"id":"/src/index.ts","moduleParts":{"index.js":"33598a70-13"},"imported":[{"uid":"33598a70-10"},{"uid":"33598a70-16"},{"uid":"33598a70-4"},{"uid":"33598a70-2"}],"importedBy":[],"isEntry":true},"33598a70-14":{"id":"/src/registry.ts","moduleParts":{"_chunks/snapshot-dNsO8P8p.js":"33598a70-15"},"imported":[],"importedBy":[{"uid":"33598a70-16"},{"uid":"33598a70-4"},{"uid":"33598a70-2"},{"uid":"33598a70-8"}]},"33598a70-16":{"id":"/src/snapshot.ts","moduleParts":{"_chunks/snapshot-dNsO8P8p.js":"33598a70-17"},"imported":[{"uid":"33598a70-18"},{"uid":"33598a70-14"}],"importedBy":[{"uid":"33598a70-0"},{"uid":"33598a70-12"}]},"33598a70-18":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"33598a70-16"},{"uid":"33598a70-4"},{"uid":"33598a70-8"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
package/lib/devtools.js
CHANGED
|
@@ -1,38 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* WeakMap from every model instance object → its internal metadata.
|
|
4
|
-
* Shared across patch, middleware, and snapshot modules.
|
|
5
|
-
*/
|
|
6
|
-
const instanceMeta = /* @__PURE__ */ new WeakMap();
|
|
7
|
-
/** Returns true when a value is a model instance (has metadata registered). */
|
|
8
|
-
function isModelInstance(value) {
|
|
9
|
-
return value != null && typeof value === "object" && instanceMeta.has(value);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/snapshot.ts
|
|
14
|
-
/**
|
|
15
|
-
* Serialize a model instance to a plain JS object (no signals, no functions).
|
|
16
|
-
* Nested model instances are recursively serialized.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* getSnapshot(counter) // { count: 6 }
|
|
20
|
-
* getSnapshot(app) // { profile: { name: "Alice" }, title: "My App" }
|
|
21
|
-
*/
|
|
22
|
-
function getSnapshot(instance) {
|
|
23
|
-
const meta = instanceMeta.get(instance);
|
|
24
|
-
if (!meta) throw new Error("[@pyreon/state-tree] getSnapshot: not a model instance");
|
|
25
|
-
const out = {};
|
|
26
|
-
for (const key of meta.stateKeys) {
|
|
27
|
-
const sig = instance[key];
|
|
28
|
-
if (!sig) continue;
|
|
29
|
-
const val = sig.peek();
|
|
30
|
-
out[key] = isModelInstance(val) ? getSnapshot(val) : val;
|
|
31
|
-
}
|
|
32
|
-
return out;
|
|
33
|
-
}
|
|
1
|
+
import { n as getSnapshot } from "./_chunks/snapshot-dNsO8P8p.js";
|
|
34
2
|
|
|
35
|
-
//#endregion
|
|
36
3
|
//#region src/devtools.ts
|
|
37
4
|
/**
|
|
38
5
|
* @pyreon/state-tree devtools introspection API.
|
package/lib/index.js
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
|
+
import { i as isModelInstance, n as getSnapshot, r as instanceMeta, t as applySnapshot } from "./_chunks/snapshot-dNsO8P8p.js";
|
|
1
2
|
import { batch, signal } from "@pyreon/reactivity";
|
|
2
3
|
|
|
3
|
-
//#region src/registry.ts
|
|
4
|
-
/**
|
|
5
|
-
* WeakMap from every model instance object → its internal metadata.
|
|
6
|
-
* Shared across patch, middleware, and snapshot modules.
|
|
7
|
-
*/
|
|
8
|
-
const instanceMeta = /* @__PURE__ */ new WeakMap();
|
|
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
4
|
//#region src/middleware.ts
|
|
16
5
|
/**
|
|
17
6
|
* Run an action through the middleware chain registered on `meta`.
|
|
@@ -302,52 +291,6 @@ function model(config) {
|
|
|
302
291
|
return new ModelDefinition(config);
|
|
303
292
|
}
|
|
304
293
|
|
|
305
|
-
//#endregion
|
|
306
|
-
//#region src/snapshot.ts
|
|
307
|
-
/**
|
|
308
|
-
* Serialize a model instance to a plain JS object (no signals, no functions).
|
|
309
|
-
* Nested model instances are recursively serialized.
|
|
310
|
-
*
|
|
311
|
-
* @example
|
|
312
|
-
* getSnapshot(counter) // { count: 6 }
|
|
313
|
-
* getSnapshot(app) // { profile: { name: "Alice" }, title: "My App" }
|
|
314
|
-
*/
|
|
315
|
-
function getSnapshot(instance) {
|
|
316
|
-
const meta = instanceMeta.get(instance);
|
|
317
|
-
if (!meta) throw new Error("[@pyreon/state-tree] getSnapshot: not a model instance");
|
|
318
|
-
const out = {};
|
|
319
|
-
for (const key of meta.stateKeys) {
|
|
320
|
-
const sig = instance[key];
|
|
321
|
-
if (!sig) continue;
|
|
322
|
-
const val = sig.peek();
|
|
323
|
-
out[key] = isModelInstance(val) ? getSnapshot(val) : val;
|
|
324
|
-
}
|
|
325
|
-
return out;
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* Restore a model instance from a plain-object snapshot.
|
|
329
|
-
* All signal writes are coalesced via `batch()` for a single reactive flush.
|
|
330
|
-
* Keys absent from the snapshot are left unchanged.
|
|
331
|
-
*
|
|
332
|
-
* @example
|
|
333
|
-
* applySnapshot(counter, { count: 0 })
|
|
334
|
-
*/
|
|
335
|
-
function applySnapshot(instance, snapshot) {
|
|
336
|
-
const meta = instanceMeta.get(instance);
|
|
337
|
-
if (!meta) throw new Error("[@pyreon/state-tree] applySnapshot: not a model instance");
|
|
338
|
-
batch(() => {
|
|
339
|
-
for (const key of meta.stateKeys) {
|
|
340
|
-
if (!(key in snapshot)) continue;
|
|
341
|
-
const sig = instance[key];
|
|
342
|
-
if (!sig) continue;
|
|
343
|
-
const val = snapshot[key];
|
|
344
|
-
const current = sig.peek();
|
|
345
|
-
if (isModelInstance(current)) applySnapshot(current, val);
|
|
346
|
-
else sig.set(val);
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
294
|
//#endregion
|
|
352
295
|
export { addMiddleware, applyPatch, applySnapshot, getSnapshot, model, onPatch, resetAllHooks, resetHook };
|
|
353
296
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/state-tree",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Structured reactive state tree — composable models with snapshots, patches, and middleware",
|
|
5
5
|
"homepage": "https://github.com/pyreon/pyreon/tree/main/packages/state-tree#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@happy-dom/global-registrator": "^20.8.9",
|
|
51
51
|
"@pyreon/manifest": "0.13.1",
|
|
52
|
-
"@pyreon/reactivity": "^0.
|
|
52
|
+
"@pyreon/reactivity": "^0.23.0",
|
|
53
53
|
"bun-types": "^1.3.12"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@pyreon/reactivity": "^0.
|
|
56
|
+
"@pyreon/reactivity": "^0.23.0"
|
|
57
57
|
}
|
|
58
58
|
}
|