@e280/strata 0.0.0-0 → 0.0.0-10
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 +207 -31
- package/package.json +14 -8
- package/s/index.ts +3 -3
- package/s/signals/index.ts +6 -0
- package/s/signals/parts/derive.ts +29 -0
- package/s/signals/parts/effect.ts +23 -0
- package/s/signals/parts/lazy.ts +27 -0
- package/s/signals/parts/signal.ts +44 -0
- package/s/signals/parts/types.ts +11 -0
- package/s/signals/parts/units.ts +152 -0
- package/s/signals/signals.test.ts +285 -0
- package/s/tests.test.ts +46 -99
- package/s/tracker/index.ts +3 -0
- package/s/tracker/tracker.test.ts +40 -0
- package/s/tracker/tracker.ts +73 -0
- package/s/tree/index.ts +7 -0
- package/s/tree/parts/branch.ts +41 -0
- package/s/tree/parts/chronobranch.ts +84 -0
- package/s/tree/parts/persistence.ts +31 -0
- package/s/tree/parts/trunk.ts +72 -0
- package/s/tree/parts/types.ts +65 -0
- package/s/{parts → tree/parts}/utils/process-options.ts +1 -1
- package/s/tree/parts/utils/setup.ts +40 -0
- package/s/tree/tree.test.ts +316 -0
- package/x/index.d.ts +3 -3
- package/x/index.js +3 -2
- package/x/index.js.map +1 -1
- package/x/signals/index.d.ts +4 -0
- package/x/signals/index.js +5 -0
- package/x/signals/index.js.map +1 -0
- package/x/signals/parts/derive.d.ts +12 -0
- package/x/signals/parts/derive.js +12 -0
- package/x/signals/parts/derive.js.map +1 -0
- package/x/signals/parts/effect.d.ts +5 -0
- package/x/signals/parts/effect.js +17 -0
- package/x/signals/parts/effect.js.map +1 -0
- package/x/signals/parts/lazy.d.ts +10 -0
- package/x/signals/parts/lazy.js +12 -0
- package/x/signals/parts/lazy.js.map +1 -0
- package/x/signals/parts/signal.d.ts +21 -0
- package/x/signals/parts/signal.js +18 -0
- package/x/signals/parts/signal.js.map +1 -0
- package/x/signals/parts/types.d.ts +7 -0
- package/x/signals/parts/types.js.map +1 -0
- package/x/signals/parts/units.d.ts +43 -0
- package/x/signals/parts/units.js +133 -0
- package/x/signals/parts/units.js.map +1 -0
- package/x/signals/signals.test.d.ts +24 -0
- package/x/signals/signals.test.js +230 -0
- package/x/signals/signals.test.js.map +1 -0
- package/x/tests.test.js +46 -100
- package/x/tests.test.js.map +1 -1
- package/x/tracker/index.d.ts +1 -0
- package/x/tracker/index.js +2 -0
- package/x/tracker/index.js.map +1 -0
- package/x/tracker/tracker.d.ts +29 -0
- package/x/tracker/tracker.js +62 -0
- package/x/tracker/tracker.js.map +1 -0
- package/x/tracker/tracker.test.d.ts +6 -0
- package/x/tracker/tracker.test.js +32 -0
- package/x/tracker/tracker.test.js.map +1 -0
- package/x/tree/index.d.ts +5 -0
- package/x/tree/index.js +6 -0
- package/x/tree/index.js.map +1 -0
- package/x/tree/parts/branch.d.ts +12 -0
- package/x/tree/parts/branch.js +31 -0
- package/x/tree/parts/branch.js.map +1 -0
- package/x/tree/parts/chronobranch.d.ts +23 -0
- package/x/tree/parts/chronobranch.js +74 -0
- package/x/tree/parts/chronobranch.js.map +1 -0
- package/x/tree/parts/persistence.d.ts +2 -0
- package/x/tree/parts/persistence.js +23 -0
- package/x/tree/parts/persistence.js.map +1 -0
- package/x/tree/parts/trunk.d.ts +17 -0
- package/x/tree/parts/trunk.js +56 -0
- package/x/tree/parts/trunk.js.map +1 -0
- package/x/tree/parts/types.d.ts +43 -0
- package/x/tree/parts/types.js +2 -0
- package/x/{parts → tree/parts}/types.js.map +1 -1
- package/x/tree/parts/utils/process-options.js +4 -0
- package/x/tree/parts/utils/process-options.js.map +1 -0
- package/x/tree/parts/utils/setup.d.ts +8 -0
- package/x/tree/parts/utils/setup.js +24 -0
- package/x/tree/parts/utils/setup.js.map +1 -0
- package/x/tree/tree.test.d.ts +37 -0
- package/x/tree/tree.test.js +279 -0
- package/x/tree/tree.test.js.map +1 -0
- package/s/parts/strata.ts +0 -48
- package/s/parts/substrata.ts +0 -55
- package/s/parts/types.ts +0 -11
- package/x/parts/strata.d.ts +0 -10
- package/x/parts/strata.js +0 -38
- package/x/parts/strata.js.map +0 -1
- package/x/parts/substrata.d.ts +0 -13
- package/x/parts/substrata.js +0 -42
- package/x/parts/substrata.js.map +0 -1
- package/x/parts/types.d.ts +0 -7
- package/x/parts/utils/process-options.js +0 -4
- package/x/parts/utils/process-options.js.map +0 -1
- /package/x/{parts → signals/parts}/types.js +0 -0
- /package/x/{parts → tree/parts}/utils/process-options.d.ts +0 -0
package/x/tests.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tests.test.js","sourceRoot":"","sources":["../s/tests.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAE,OAAO,EAAC,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"tests.test.js","sourceRoot":"","sources":["../s/tests.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./tracker.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../s/tracker/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type TrackableItem = object | symbol;
|
|
2
|
+
/**
|
|
3
|
+
* tracking system for state management
|
|
4
|
+
* - it tracks when items are seen or changed
|
|
5
|
+
*
|
|
6
|
+
* for state item integration (like you're integrating a new kind of state object)
|
|
7
|
+
* - items can call `tracker.see(this)` when they are accessed
|
|
8
|
+
* - items can call `tracker.change(this)` when they are reassigned
|
|
9
|
+
*
|
|
10
|
+
* for reactivity integration (like you're integrating a new view library that reacts to state changes)
|
|
11
|
+
* - run `tracker.seen(renderFn)`, collecting a set of seen items
|
|
12
|
+
* - loop over each seen item, attach a changed handler `tracker.changed(item, handlerFn)`
|
|
13
|
+
*/
|
|
14
|
+
export declare class Tracker<Item extends TrackableItem = any> {
|
|
15
|
+
#private;
|
|
16
|
+
/** indicate item was accessed */
|
|
17
|
+
see(item: Item): void;
|
|
18
|
+
/** collect which items were seen during fn */
|
|
19
|
+
seen<R>(fn: () => R): {
|
|
20
|
+
seen: Set<Item>;
|
|
21
|
+
result: R;
|
|
22
|
+
};
|
|
23
|
+
/** indicate item was changed */
|
|
24
|
+
change(item: Item): Promise<void>;
|
|
25
|
+
/** respond to changes by calling fn */
|
|
26
|
+
changed(item: Item, fn: () => Promise<void>): () => void;
|
|
27
|
+
}
|
|
28
|
+
/** standard global tracker for integrations */
|
|
29
|
+
export declare const tracker: Tracker;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { sub } from "@e280/stz";
|
|
2
|
+
/**
|
|
3
|
+
* tracking system for state management
|
|
4
|
+
* - it tracks when items are seen or changed
|
|
5
|
+
*
|
|
6
|
+
* for state item integration (like you're integrating a new kind of state object)
|
|
7
|
+
* - items can call `tracker.see(this)` when they are accessed
|
|
8
|
+
* - items can call `tracker.change(this)` when they are reassigned
|
|
9
|
+
*
|
|
10
|
+
* for reactivity integration (like you're integrating a new view library that reacts to state changes)
|
|
11
|
+
* - run `tracker.seen(renderFn)`, collecting a set of seen items
|
|
12
|
+
* - loop over each seen item, attach a changed handler `tracker.changed(item, handlerFn)`
|
|
13
|
+
*/
|
|
14
|
+
export class Tracker {
|
|
15
|
+
#seeables = [];
|
|
16
|
+
#changeables = new WeakMap();
|
|
17
|
+
#changeStack = [];
|
|
18
|
+
#busy = new Set();
|
|
19
|
+
/** indicate item was accessed */
|
|
20
|
+
see(item) {
|
|
21
|
+
this.#seeables.at(-1)?.add(item);
|
|
22
|
+
}
|
|
23
|
+
/** collect which items were seen during fn */
|
|
24
|
+
seen(fn) {
|
|
25
|
+
this.#seeables.push(new Set());
|
|
26
|
+
const result = fn();
|
|
27
|
+
const seen = this.#seeables.pop();
|
|
28
|
+
return { seen, result };
|
|
29
|
+
}
|
|
30
|
+
/** indicate item was changed */
|
|
31
|
+
async change(item) {
|
|
32
|
+
if (this.#busy.has(item))
|
|
33
|
+
throw new Error("circularity forbidden");
|
|
34
|
+
const prom = this.#guaranteeChangeable(item).pub();
|
|
35
|
+
this.#changeStack.at(-1)?.add(prom);
|
|
36
|
+
return prom;
|
|
37
|
+
}
|
|
38
|
+
/** respond to changes by calling fn */
|
|
39
|
+
changed(item, fn) {
|
|
40
|
+
return this.#guaranteeChangeable(item)(async () => {
|
|
41
|
+
const collected = new Set();
|
|
42
|
+
this.#changeStack.push(collected);
|
|
43
|
+
this.#busy.add(item);
|
|
44
|
+
collected.add(fn());
|
|
45
|
+
this.#busy.delete(item);
|
|
46
|
+
await Promise.all(collected);
|
|
47
|
+
this.#changeStack.pop();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
#guaranteeChangeable(item) {
|
|
51
|
+
let on = this.#changeables.get(item);
|
|
52
|
+
if (!on) {
|
|
53
|
+
on = sub();
|
|
54
|
+
this.#changeables.set(item, on);
|
|
55
|
+
}
|
|
56
|
+
return on;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const key = Symbol.for("e280.tracker.v2");
|
|
60
|
+
/** standard global tracker for integrations */
|
|
61
|
+
export const tracker = globalThis[key] ??= new Tracker();
|
|
62
|
+
//# sourceMappingURL=tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../s/tracker/tracker.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAM,MAAM,WAAW,CAAA;AAIlC;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,OAAO;IACnB,SAAS,GAAgB,EAAE,CAAA;IAC3B,YAAY,GAAG,IAAI,OAAO,EAAa,CAAA;IACvC,YAAY,GAAyB,EAAE,CAAA;IACvC,KAAK,GAAG,IAAI,GAAG,EAAQ,CAAA;IAEvB,iCAAiC;IACjC,GAAG,CAAC,IAAU;QACb,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;IACjC,CAAC;IAED,8CAA8C;IAC9C,IAAI,CAAI,EAAW;QAClB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,CAAA;QAC9B,MAAM,MAAM,GAAG,EAAE,EAAE,CAAA;QACnB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAG,CAAA;QAClC,OAAO,EAAC,IAAI,EAAE,MAAM,EAAC,CAAA;IACtB,CAAC;IAED,gCAAgC;IAChC,KAAK,CAAC,MAAM,CAAC,IAAU;QACtB,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAA;QAClD,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;QACnC,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,uCAAuC;IACvC,OAAO,CAAC,IAAU,EAAE,EAAuB;QAC1C,OAAO,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,KAAK,IAAG,EAAE;YAChD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAiB,CAAA;YAC1C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACjC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACpB,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAA;YACnB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YACvB,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YAC5B,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,CAAA;QACxB,CAAC,CAAC,CAAA;IACH,CAAC;IAED,oBAAoB,CAAC,IAAU;QAC9B,IAAI,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACpC,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,EAAE,GAAG,GAAG,EAAE,CAAA;YACV,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAChC,CAAC;QACD,OAAO,EAAE,CAAA;IACV,CAAC;CACD;AAED,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AAEzC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,OAAO,GAAa,UAAkB,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Science, test, expect } from "@e280/science";
|
|
2
|
+
import { Tracker } from "./tracker.js";
|
|
3
|
+
export default Science.suite({
|
|
4
|
+
"change waits for downstream effects to settle": test(async () => {
|
|
5
|
+
const tracker = new Tracker();
|
|
6
|
+
let order = [];
|
|
7
|
+
const item = {};
|
|
8
|
+
tracker.changed(item, async () => {
|
|
9
|
+
await Promise.resolve();
|
|
10
|
+
order.push("effect");
|
|
11
|
+
});
|
|
12
|
+
order.push("before");
|
|
13
|
+
await tracker.change(item);
|
|
14
|
+
order.push("after");
|
|
15
|
+
expect(order.length).is(3);
|
|
16
|
+
expect(order[0]).is("before");
|
|
17
|
+
expect(order[1]).is("effect");
|
|
18
|
+
expect(order[2]).is("after");
|
|
19
|
+
}),
|
|
20
|
+
"circularity forbidden": test(async () => {
|
|
21
|
+
const tracker = new Tracker();
|
|
22
|
+
const item = {};
|
|
23
|
+
// effect re-publishes the same change, creating a cycle
|
|
24
|
+
tracker.changed(item, async () => {
|
|
25
|
+
await tracker.change(item);
|
|
26
|
+
});
|
|
27
|
+
expect(async () => {
|
|
28
|
+
await tracker.change(item);
|
|
29
|
+
}).throwsAsync();
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=tracker.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.test.js","sourceRoot":"","sources":["../../s/tracker/tracker.test.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAC,MAAM,eAAe,CAAA;AACnD,OAAO,EAAC,OAAO,EAAC,MAAM,cAAc,CAAA;AAEpC,eAAe,OAAO,CAAC,KAAK,CAAC;IAC5B,+CAA+C,EAAE,IAAI,CAAC,KAAK,IAAG,EAAE;QAC/D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;QAC7B,IAAI,KAAK,GAAa,EAAE,CAAA;QAExB,MAAM,IAAI,GAAG,EAAE,CAAA;QACf,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAChC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;YACvB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACrB,CAAC,CAAC,CAAA;QAEF,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACpB,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC1B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAEnB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC1B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAA;QAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAA;QAC7B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAA;IAC7B,CAAC,CAAC;IAEF,uBAAuB,EAAE,IAAI,CAAC,KAAK,IAAG,EAAE;QACvC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;QAC7B,MAAM,IAAI,GAAG,EAAE,CAAA;QAEf,wDAAwD;QACxD,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,IAAG,EAAE;YAC/B,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC3B,CAAC,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,IAAG,EAAE;YAChB,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC3B,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;IACjB,CAAC,CAAC;CACF,CAAC,CAAA"}
|
package/x/tree/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../s/tree/index.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAA;AACjC,cAAc,yBAAyB,CAAA;AACvC,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Branchstate, Immutable, Mutator, Options, Selector, Tree } from "./types.js";
|
|
2
|
+
export declare class Branch<S extends Branchstate, ParentState extends Branchstate = any> implements Tree<S> {
|
|
3
|
+
#private;
|
|
4
|
+
private parent;
|
|
5
|
+
private selector;
|
|
6
|
+
private options;
|
|
7
|
+
constructor(parent: Tree<ParentState>, selector: Selector<S, ParentState>, options: Options);
|
|
8
|
+
get state(): Immutable<S>;
|
|
9
|
+
get on(): import("@e280/stz").Sub<[Immutable<S>]>;
|
|
10
|
+
mutate(mutator: Mutator<S>): Promise<Immutable<S>>;
|
|
11
|
+
branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { deep } from "@e280/stz";
|
|
2
|
+
import { signal } from "../../signals/parts/signal.js";
|
|
3
|
+
export class Branch {
|
|
4
|
+
parent;
|
|
5
|
+
selector;
|
|
6
|
+
options;
|
|
7
|
+
#immutable;
|
|
8
|
+
constructor(parent, selector, options) {
|
|
9
|
+
this.parent = parent;
|
|
10
|
+
this.selector = selector;
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.#immutable = signal.derive(() => {
|
|
13
|
+
const state = selector(parent.state);
|
|
14
|
+
return deep.freeze(options.clone(state));
|
|
15
|
+
}, { compare: deep.equal });
|
|
16
|
+
}
|
|
17
|
+
get state() {
|
|
18
|
+
return this.#immutable.get();
|
|
19
|
+
}
|
|
20
|
+
get on() {
|
|
21
|
+
return this.#immutable.on;
|
|
22
|
+
}
|
|
23
|
+
async mutate(mutator) {
|
|
24
|
+
await this.parent.mutate(parentState => mutator(this.selector(parentState)));
|
|
25
|
+
return this.#immutable.get();
|
|
26
|
+
}
|
|
27
|
+
branch(selector) {
|
|
28
|
+
return new Branch(this, selector, this.options);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=branch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch.js","sourceRoot":"","sources":["../../../s/tree/parts/branch.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAC9B,OAAO,EAAC,MAAM,EAAC,MAAM,+BAA+B,CAAA;AAIpD,MAAM,OAAO,MAAM;IAIR;IACA;IACA;IALV,UAAU,CAA6B;IAEvC,YACU,MAAyB,EACzB,QAAkC,EAClC,OAAgB;QAFhB,WAAM,GAAN,MAAM,CAAmB;QACzB,aAAQ,GAAR,QAAQ,CAA0B;QAClC,YAAO,GAAP,OAAO,CAAS;QAGzB,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;YACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAY,CAAC,CAAA;YAC3C,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAiB,CAAA;QACzD,CAAC,EAAE,EAAC,OAAO,EAAE,IAAI,CAAC,KAAK,EAAC,CAAC,CAAA;IAC1B,CAAC;IAED,IAAI,KAAK;QACR,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAA;IAC7B,CAAC;IAED,IAAI,EAAE;QACL,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAA;IAC1B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAmB;QAC/B,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CACtC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CACnC,CAAA;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAA;IAC7B,CAAC;IAED,MAAM,CAA0B,QAA0B;QACzD,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,CAAC;CACD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Branch } from "./branch.js";
|
|
2
|
+
import { Branchstate, Chronicle, Immutable, Mutator, Options, Selector, Tree } from "./types.js";
|
|
3
|
+
export declare class Chronobranch<S extends Branchstate, ParentState extends Branchstate = any> implements Tree<S> {
|
|
4
|
+
#private;
|
|
5
|
+
limit: number;
|
|
6
|
+
parent: Tree<ParentState>;
|
|
7
|
+
selector: Selector<Chronicle<S>, ParentState>;
|
|
8
|
+
options: Options;
|
|
9
|
+
constructor(limit: number, parent: Tree<ParentState>, selector: Selector<Chronicle<S>, ParentState>, options: Options);
|
|
10
|
+
get state(): Immutable<S>;
|
|
11
|
+
get undoable(): number;
|
|
12
|
+
get redoable(): number;
|
|
13
|
+
on(fn: (state: Immutable<S>) => void): () => void;
|
|
14
|
+
/** progress forwards in history */
|
|
15
|
+
mutate(mutator: Mutator<S>): Promise<Immutable<S>>;
|
|
16
|
+
/** step backwards into the past, by n steps */
|
|
17
|
+
undo(n?: number): Promise<void>;
|
|
18
|
+
/** step forwards into the future, by n steps */
|
|
19
|
+
redo(n?: number): Promise<void>;
|
|
20
|
+
/** wipe past and future snapshots */
|
|
21
|
+
wipe(): Promise<void>;
|
|
22
|
+
branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Branch } from "./branch.js";
|
|
2
|
+
export class Chronobranch {
|
|
3
|
+
limit;
|
|
4
|
+
parent;
|
|
5
|
+
selector;
|
|
6
|
+
options;
|
|
7
|
+
#branch;
|
|
8
|
+
constructor(limit, parent, selector, options) {
|
|
9
|
+
this.limit = limit;
|
|
10
|
+
this.parent = parent;
|
|
11
|
+
this.selector = selector;
|
|
12
|
+
this.options = options;
|
|
13
|
+
this.#branch = parent.branch(selector);
|
|
14
|
+
}
|
|
15
|
+
get state() {
|
|
16
|
+
return this.#branch.state.present;
|
|
17
|
+
}
|
|
18
|
+
get undoable() {
|
|
19
|
+
return this.#branch.state.past.length;
|
|
20
|
+
}
|
|
21
|
+
get redoable() {
|
|
22
|
+
return this.#branch.state.future.length;
|
|
23
|
+
}
|
|
24
|
+
on(fn) {
|
|
25
|
+
return this.#branch.on(chronicle => fn(chronicle.present));
|
|
26
|
+
}
|
|
27
|
+
/** progress forwards in history */
|
|
28
|
+
async mutate(mutator) {
|
|
29
|
+
const limit = Math.max(0, this.limit);
|
|
30
|
+
const snapshot = this.options.clone(this.#branch.state.present);
|
|
31
|
+
await this.#branch.mutate(chronicle => {
|
|
32
|
+
mutator(chronicle.present);
|
|
33
|
+
chronicle.past.push(snapshot);
|
|
34
|
+
chronicle.past = chronicle.past.slice(-limit);
|
|
35
|
+
chronicle.future = [];
|
|
36
|
+
});
|
|
37
|
+
return this.state;
|
|
38
|
+
}
|
|
39
|
+
/** step backwards into the past, by n steps */
|
|
40
|
+
async undo(n = 1) {
|
|
41
|
+
await this.#branch.mutate(chronicle => {
|
|
42
|
+
const snapshots = chronicle.past.slice(-n);
|
|
43
|
+
if (snapshots.length >= n) {
|
|
44
|
+
const oldPresent = chronicle.present;
|
|
45
|
+
chronicle.present = snapshots.shift();
|
|
46
|
+
chronicle.past = chronicle.past.slice(0, -n);
|
|
47
|
+
chronicle.future.unshift(oldPresent, ...snapshots);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/** step forwards into the future, by n steps */
|
|
52
|
+
async redo(n = 1) {
|
|
53
|
+
await this.#branch.mutate(chronicle => {
|
|
54
|
+
const snapshots = chronicle.future.slice(0, n);
|
|
55
|
+
if (snapshots.length >= n) {
|
|
56
|
+
const oldPresent = chronicle.present;
|
|
57
|
+
chronicle.present = snapshots.shift();
|
|
58
|
+
chronicle.past.push(oldPresent, ...snapshots);
|
|
59
|
+
chronicle.future = chronicle.future.slice(n);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/** wipe past and future snapshots */
|
|
64
|
+
async wipe() {
|
|
65
|
+
await this.#branch.mutate(chronicle => {
|
|
66
|
+
chronicle.past = [];
|
|
67
|
+
chronicle.future = [];
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
branch(selector) {
|
|
71
|
+
return new Branch(this, selector, this.options);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=chronobranch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chronobranch.js","sourceRoot":"","sources":["../../../s/tree/parts/chronobranch.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,aAAa,CAAA;AAGlC,MAAM,OAAO,YAAY;IAIf;IACA;IACA;IACA;IANT,OAAO,CAAmC;IAE1C,YACS,KAAa,EACb,MAAyB,EACzB,QAA6C,EAC7C,OAAgB;QAHhB,UAAK,GAAL,KAAK,CAAQ;QACb,WAAM,GAAN,MAAM,CAAmB;QACzB,aAAQ,GAAR,QAAQ,CAAqC;QAC7C,YAAO,GAAP,OAAO,CAAS;QAExB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACvC,CAAC;IAED,IAAI,KAAK;QACR,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAA;IAClC,CAAC;IAED,IAAI,QAAQ;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAA;IACtC,CAAC;IAED,IAAI,QAAQ;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAA;IACxC,CAAC;IAED,EAAE,CAAC,EAAiC;QACnC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;IAC3D,CAAC;IAED,mCAAmC;IACnC,KAAK,CAAC,MAAM,CAAC,OAAmB;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAM,CAAA;QACpE,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;YACrC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;YAC1B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC7B,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAA;YAC7C,SAAS,CAAC,MAAM,GAAG,EAAE,CAAA;QACtB,CAAC,CAAC,CAAA;QACF,OAAO,IAAI,CAAC,KAAK,CAAA;IAClB,CAAC;IAED,+CAA+C;IAC/C,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;QACf,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;YACrC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;YAC1C,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAC3B,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAA;gBACpC,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,KAAK,EAAG,CAAA;gBACtC,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;gBAC5C,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,SAAS,CAAC,CAAA;YACnD,CAAC;QACF,CAAC,CAAC,CAAA;IACH,CAAC;IAED,gDAAgD;IAChD,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;QACf,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;YACrC,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YAC9C,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAC3B,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAA;gBACpC,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,KAAK,EAAG,CAAA;gBACtC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,SAAS,CAAC,CAAA;gBAC7C,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YAC7C,CAAC;QACF,CAAC,CAAC,CAAA;IACH,CAAC;IAED,qCAAqC;IACrC,KAAK,CAAC,IAAI;QACT,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;YACrC,SAAS,CAAC,IAAI,GAAG,EAAE,CAAA;YACnB,SAAS,CAAC,MAAM,GAAG,EAAE,CAAA;QACtB,CAAC,CAAC,CAAA;IACH,CAAC;IAED,MAAM,CAA0B,QAA0B;QACzD,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,CAAC;CACD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const localPersistence = (key, storage = window.localStorage) => ({
|
|
2
|
+
store: {
|
|
3
|
+
async get() {
|
|
4
|
+
const json = storage.getItem(key);
|
|
5
|
+
return json
|
|
6
|
+
? JSON.parse(json)
|
|
7
|
+
: undefined;
|
|
8
|
+
},
|
|
9
|
+
async set(state) {
|
|
10
|
+
const json = JSON.stringify(state);
|
|
11
|
+
storage.setItem(key, json);
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
onChange: (fn) => {
|
|
15
|
+
const listener = (event) => {
|
|
16
|
+
if (event.storageArea === storage && event.key === key)
|
|
17
|
+
fn();
|
|
18
|
+
};
|
|
19
|
+
window.addEventListener("storage", listener);
|
|
20
|
+
return () => window.removeEventListener("storage", listener);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../../../s/tree/parts/persistence.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,GAAW,EACX,UAAmB,MAAM,CAAC,YAAY,EACrB,EAAE,CAAC,CAAC;IAEtB,KAAK,EAAE;QACN,KAAK,CAAC,GAAG;YACR,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACjC,OAAO,IAAI;gBACV,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBAClB,CAAC,CAAC,SAAS,CAAA;QACb,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,KAAK;YACd,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YAClC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC3B,CAAC;KACD;IAED,QAAQ,EAAE,CAAC,EAAc,EAAE,EAAE;QAC5B,MAAM,QAAQ,GAAG,CAAC,KAAmB,EAAE,EAAE;YACxC,IAAI,KAAK,CAAC,WAAW,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG;gBACrD,EAAE,EAAE,CAAA;QACN,CAAC,CAAA;QACD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;QAC5C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IAC7D,CAAC;CACD,CAAC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Branch } from "./branch.js";
|
|
2
|
+
import { trunkSetup } from "./utils/setup.js";
|
|
3
|
+
import { Chronobranch } from "./chronobranch.js";
|
|
4
|
+
import { Branchstate, Chronicle, Immutable, Mutator, Options, Selector, Tree, Trunkstate } from "./types.js";
|
|
5
|
+
export declare class Trunk<S extends Trunkstate> implements Tree<S> {
|
|
6
|
+
#private;
|
|
7
|
+
static setup: typeof trunkSetup;
|
|
8
|
+
static chronicle: <S_1 extends Branchstate>(state: S_1) => Chronicle<S_1>;
|
|
9
|
+
options: Options;
|
|
10
|
+
constructor(state: S, options?: Partial<Options>);
|
|
11
|
+
get state(): Immutable<S>;
|
|
12
|
+
get on(): import("@e280/stz").Sub<[Immutable<S>]>;
|
|
13
|
+
mutate(mutator: Mutator<S>): Promise<Immutable<S>>;
|
|
14
|
+
overwrite(state: S): Promise<void>;
|
|
15
|
+
branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S>;
|
|
16
|
+
chronobranch<Sub extends Branchstate>(limit: number, selector: Selector<Chronicle<Sub>, S>): Chronobranch<Sub, S>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { deep } from "@e280/stz";
|
|
2
|
+
import { Branch } from "./branch.js";
|
|
3
|
+
import { trunkSetup } from "./utils/setup.js";
|
|
4
|
+
import { Chronobranch } from "./chronobranch.js";
|
|
5
|
+
import { processOptions } from "./utils/process-options.js";
|
|
6
|
+
import { signal } from "../../signals/parts/signal.js";
|
|
7
|
+
export class Trunk {
|
|
8
|
+
static setup = trunkSetup;
|
|
9
|
+
static chronicle = (state) => ({
|
|
10
|
+
present: state,
|
|
11
|
+
past: [],
|
|
12
|
+
future: [],
|
|
13
|
+
});
|
|
14
|
+
options;
|
|
15
|
+
#immutable;
|
|
16
|
+
#mutable;
|
|
17
|
+
#mutationLock = 0;
|
|
18
|
+
constructor(state, options = {}) {
|
|
19
|
+
this.options = processOptions(options);
|
|
20
|
+
this.#mutable = signal(state);
|
|
21
|
+
this.#immutable = signal.derive(() => deep.freeze(this.options.clone(this.#mutable.get())));
|
|
22
|
+
}
|
|
23
|
+
get state() {
|
|
24
|
+
return this.#immutable.get();
|
|
25
|
+
}
|
|
26
|
+
get on() {
|
|
27
|
+
return this.#immutable.on;
|
|
28
|
+
}
|
|
29
|
+
async mutate(mutator) {
|
|
30
|
+
const oldState = this.options.clone(this.#mutable.get());
|
|
31
|
+
if (this.#mutationLock > 0)
|
|
32
|
+
throw new Error("nested mutations are forbidden");
|
|
33
|
+
try {
|
|
34
|
+
this.#mutationLock++;
|
|
35
|
+
mutator(this.#mutable());
|
|
36
|
+
const newState = this.#mutable.get();
|
|
37
|
+
const isChanged = !deep.equal(newState, oldState);
|
|
38
|
+
if (isChanged)
|
|
39
|
+
await this.overwrite(newState);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
this.#mutationLock--;
|
|
43
|
+
}
|
|
44
|
+
return this.#immutable.get();
|
|
45
|
+
}
|
|
46
|
+
async overwrite(state) {
|
|
47
|
+
await this.#mutable.publish(state);
|
|
48
|
+
}
|
|
49
|
+
branch(selector) {
|
|
50
|
+
return new Branch(this, selector, this.options);
|
|
51
|
+
}
|
|
52
|
+
chronobranch(limit, selector) {
|
|
53
|
+
return new Chronobranch(limit, this, selector, this.options);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=trunk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trunk.js","sourceRoot":"","sources":["../../../s/tree/parts/trunk.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAC9B,OAAO,EAAC,MAAM,EAAC,MAAM,aAAa,CAAA;AAClC,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAA;AAEzD,OAAO,EAAC,MAAM,EAAS,MAAM,+BAA+B,CAAA;AAG5D,MAAM,OAAO,KAAK;IACjB,MAAM,CAAC,KAAK,GAAG,UAAU,CAAA;IACzB,MAAM,CAAC,SAAS,GAAG,CAAwB,KAAQ,EAAgB,EAAE,CAAC,CAAC;QACtE,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,EAAE;QACR,MAAM,EAAE,EAAE;KACV,CAAC,CAAA;IAEF,OAAO,CAAS;IAEhB,UAAU,CAA6B;IACvC,QAAQ,CAAW;IACnB,aAAa,GAAG,CAAC,CAAA;IAEjB,YAAY,KAAQ,EAAE,UAA4B,EAAE;QACnD,IAAI,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;QACtC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAC7B,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CACpC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAiB,CACpE,CAAA;IACF,CAAC;IAED,IAAI,KAAK;QACR,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAA;IAC7B,CAAC;IAED,IAAI,EAAE;QACL,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAA;IAC1B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAmB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAA;QACxD,IAAI,IAAI,CAAC,aAAa,GAAG,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QAClD,IAAI,CAAC;YACJ,IAAI,CAAC,aAAa,EAAE,CAAA;YACpB,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;YACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAA;YACpC,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;YACjD,IAAI,SAAS;gBACZ,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QAChC,CAAC;gBACO,CAAC;YAAC,IAAI,CAAC,aAAa,EAAE,CAAA;QAAC,CAAC;QAChC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAQ;QACvB,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACnC,CAAC;IAED,MAAM,CAA0B,QAA0B;QACzD,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,CAAC;IAED,YAAY,CACV,KAAa,EACb,QAAqC;QAEtC,OAAO,IAAI,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAC7D,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Branch } from "./branch.js";
|
|
2
|
+
export type Options = {
|
|
3
|
+
clone: <X>(x: X) => X;
|
|
4
|
+
};
|
|
5
|
+
export type Selector<Sub, S> = (state: S) => Sub;
|
|
6
|
+
export type Mutator<S> = (state: S) => void;
|
|
7
|
+
export type Trunkstate = {};
|
|
8
|
+
export type Branchstate = {} | null | undefined;
|
|
9
|
+
export type Versioned<S extends Trunkstate> = {
|
|
10
|
+
state: S;
|
|
11
|
+
version: number;
|
|
12
|
+
};
|
|
13
|
+
export type Immutable<T> = T extends (...args: any[]) => any ? T : T extends readonly any[] ? ReadonlyArray<Immutable<T[number]>> : T extends object ? {
|
|
14
|
+
readonly [K in keyof T]: Immutable<T[K]>;
|
|
15
|
+
} : T;
|
|
16
|
+
export type Mutable<T> = T extends (...args: any[]) => any ? T : T extends ReadonlyArray<infer U> ? Mutable<U>[] : T extends object ? {
|
|
17
|
+
-readonly [K in keyof T]: Mutable<T[K]>;
|
|
18
|
+
} : T;
|
|
19
|
+
export type Tree<S extends Branchstate> = {
|
|
20
|
+
get state(): Immutable<S>;
|
|
21
|
+
on(fn: (state: Immutable<S>) => void): () => void;
|
|
22
|
+
mutate(mutator: Mutator<S>): Promise<Immutable<S>>;
|
|
23
|
+
branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S>;
|
|
24
|
+
};
|
|
25
|
+
export type SetupOptions<S extends Trunkstate> = {
|
|
26
|
+
version: number;
|
|
27
|
+
initialState: S;
|
|
28
|
+
saveDebounceTime?: number;
|
|
29
|
+
persistence?: Persistence<Versioned<S>>;
|
|
30
|
+
};
|
|
31
|
+
export type Chronicle<S extends Branchstate> = {
|
|
32
|
+
past: S[];
|
|
33
|
+
present: S;
|
|
34
|
+
future: S[];
|
|
35
|
+
};
|
|
36
|
+
export type EzStore<X> = {
|
|
37
|
+
get(): Promise<X | undefined>;
|
|
38
|
+
set(state: X | undefined): Promise<void>;
|
|
39
|
+
};
|
|
40
|
+
export type Persistence<X> = {
|
|
41
|
+
store: EzStore<X>;
|
|
42
|
+
onChange: (fn: () => void) => (() => void);
|
|
43
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../s/tree/parts/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-options.js","sourceRoot":"","sources":["../../../../s/tree/parts/utils/process-options.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAyB,EAAW,EAAE,CAAC,CAAC;IACtE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,CAAI,CAAI,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CACzD,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Trunk } from "../trunk.js";
|
|
2
|
+
import { SetupOptions, Trunkstate } from "../types.js";
|
|
3
|
+
export declare function trunkSetup<S extends Trunkstate>(options: SetupOptions<S>): Promise<{
|
|
4
|
+
trunk: Trunk<S>;
|
|
5
|
+
load: () => Promise<void>;
|
|
6
|
+
save: import("@e280/stz").DebounceReturn<() => Promise<void>>;
|
|
7
|
+
dispose: () => void;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { debounce } from "@e280/stz";
|
|
2
|
+
import { Trunk } from "../trunk.js";
|
|
3
|
+
import { localPersistence } from "../persistence.js";
|
|
4
|
+
export async function trunkSetup(options) {
|
|
5
|
+
const { version, initialState, saveDebounceTime = 500, persistence = localPersistence("strataTree"), } = options;
|
|
6
|
+
const trunk = new Trunk(initialState);
|
|
7
|
+
async function load() {
|
|
8
|
+
const pickle = await persistence.store.get();
|
|
9
|
+
if (pickle && pickle.version === version)
|
|
10
|
+
await trunk.overwrite(pickle.state);
|
|
11
|
+
}
|
|
12
|
+
const save = debounce(saveDebounceTime, async () => persistence.store.set({
|
|
13
|
+
version,
|
|
14
|
+
state: trunk.state,
|
|
15
|
+
}));
|
|
16
|
+
// persistence: initial load from store
|
|
17
|
+
await load();
|
|
18
|
+
// persistence: save to store
|
|
19
|
+
trunk.on(save);
|
|
20
|
+
// cross-tab sync
|
|
21
|
+
const dispose = persistence.onChange(load);
|
|
22
|
+
return { trunk, load, save, dispose };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../../s/tree/parts/utils/setup.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAC,MAAM,WAAW,CAAA;AAElC,OAAO,EAAC,KAAK,EAAC,MAAM,aAAa,CAAA;AACjC,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAA;AAGlD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAuB,OAAwB;IAC9E,MAAM,EACL,OAAO,EACP,YAAY,EACZ,gBAAgB,GAAG,GAAG,EACtB,WAAW,GAAG,gBAAgB,CAAC,YAAY,CAAC,GAC5C,GAAG,OAAO,CAAA;IAEX,MAAM,KAAK,GAAG,IAAI,KAAK,CAAI,YAAY,CAAC,CAAA;IAExC,KAAK,UAAU,IAAI;QAClB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;QAC5C,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO;YACvC,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACrC,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,gBAAgB,EAAE,KAAK,IAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC;QACxE,OAAO;QACP,KAAK,EAAE,KAAK,CAAC,KAAY;KACzB,CAAC,CAAC,CAAA;IAEH,uCAAuC;IACvC,MAAM,IAAI,EAAE,CAAA;IAEZ,6BAA6B;IAC7B,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;IAEd,iBAAiB;IACjB,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAE1C,OAAO,EAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAC,CAAA;AACpC,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Science } from "@e280/science";
|
|
2
|
+
declare const _default: {
|
|
3
|
+
trunk: {
|
|
4
|
+
"get state": Science.Test;
|
|
5
|
+
"state is immutable": Science.Test;
|
|
6
|
+
"run a proper mutation": Science.Test;
|
|
7
|
+
"forbidden mutation nesting": Science.Test;
|
|
8
|
+
"state after mutation is frozen": Science.Test;
|
|
9
|
+
"effect reacts to trunk mutation": Science.Test;
|
|
10
|
+
"signal.on is debounced": Science.Test;
|
|
11
|
+
"listeners are fired when array item is pushed": Science.Test;
|
|
12
|
+
"prevent mutation loops": Science.Test;
|
|
13
|
+
};
|
|
14
|
+
branch: {
|
|
15
|
+
"get state": Science.Test;
|
|
16
|
+
"nullable selector": Science.Test;
|
|
17
|
+
composition: Science.Test;
|
|
18
|
+
"deep mutations": Science.Test;
|
|
19
|
+
"signal.on ignores outside mutations": Science.Test;
|
|
20
|
+
"forbid submutation in mutation": Science.Test;
|
|
21
|
+
"forbid mutation in submutation": Science.Test;
|
|
22
|
+
};
|
|
23
|
+
chronobranch: {
|
|
24
|
+
"get state": Science.Test;
|
|
25
|
+
mutate: Science.Test;
|
|
26
|
+
"undoable/redoable": Science.Test;
|
|
27
|
+
undo: Science.Test;
|
|
28
|
+
redo: Science.Test;
|
|
29
|
+
"undo/redo well ordered": Science.Test;
|
|
30
|
+
"undo nothing does nothing": Science.Test;
|
|
31
|
+
"redo nothing does nothing": Science.Test;
|
|
32
|
+
"undo 2x": Science.Test;
|
|
33
|
+
"redo 2x": Science.Test;
|
|
34
|
+
"substrata mutations are tracked": Science.Test;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export default _default;
|