@e280/strata 0.2.8 → 0.3.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +11 -47
  2. package/package.json +3 -3
  3. package/s/index.ts +0 -1
  4. package/s/prism/prism.test.ts +2 -1
  5. package/s/prism/prism.ts +1 -1
  6. package/s/signals/derived/class.ts +34 -0
  7. package/s/signals/derived/fn.ts +35 -0
  8. package/s/signals/{tests/derived.test.ts → derived/test.ts} +25 -14
  9. package/s/signals/effect/effect.ts +7 -0
  10. package/s/signals/effect/test.ts +172 -0
  11. package/s/signals/effect/watch.ts +32 -0
  12. package/s/signals/index.ts +11 -7
  13. package/s/signals/lazy/class.ts +74 -0
  14. package/s/signals/lazy/fn.ts +22 -0
  15. package/s/signals/{tests/lazy.test.ts → lazy/test.ts} +23 -12
  16. package/s/signals/signal/class.ts +77 -0
  17. package/s/signals/signal/fn.ts +31 -0
  18. package/s/signals/{tests/signal.test.ts → signal/test.ts} +56 -59
  19. package/s/signals/signals.test.ts +8 -8
  20. package/s/signals/types.ts +4 -23
  21. package/s/signals/utils/default-compare.ts +1 -1
  22. package/s/signals/utils/symbols.ts +9 -0
  23. package/s/tests.test.ts +1 -57
  24. package/x/index.d.ts +0 -1
  25. package/x/index.js +0 -1
  26. package/x/index.js.map +1 -1
  27. package/x/prism/prism.js.map +1 -1
  28. package/x/prism/prism.test.js +1 -1
  29. package/x/prism/prism.test.js.map +1 -1
  30. package/x/signals/derived/class.d.ts +14 -0
  31. package/x/signals/derived/class.js +23 -0
  32. package/x/signals/derived/class.js.map +1 -0
  33. package/x/signals/derived/fn.d.ts +3 -0
  34. package/x/signals/derived/fn.js +27 -0
  35. package/x/signals/derived/fn.js.map +1 -0
  36. package/x/signals/{tests/derived.test.d.ts → derived/test.d.ts} +2 -3
  37. package/x/signals/{tests/derived.test.js → derived/test.js} +25 -15
  38. package/x/signals/derived/test.js.map +1 -0
  39. package/x/signals/effect/effect.d.ts +1 -0
  40. package/x/signals/effect/effect.js +5 -0
  41. package/x/signals/effect/effect.js.map +1 -0
  42. package/x/signals/{tests/effect.test.d.ts → effect/test.d.ts} +7 -0
  43. package/x/signals/effect/test.js +132 -0
  44. package/x/signals/effect/test.js.map +1 -0
  45. package/x/signals/effect/watch.d.ts +4 -0
  46. package/x/signals/effect/watch.js +25 -0
  47. package/x/signals/effect/watch.js.map +1 -0
  48. package/x/signals/index.d.ts +8 -7
  49. package/x/signals/index.js +8 -7
  50. package/x/signals/index.js.map +1 -1
  51. package/x/signals/lazy/class.d.ts +19 -0
  52. package/x/signals/lazy/class.js +59 -0
  53. package/x/signals/lazy/class.js.map +1 -0
  54. package/x/signals/lazy/fn.d.ts +3 -0
  55. package/x/signals/lazy/fn.js +17 -0
  56. package/x/signals/lazy/fn.js.map +1 -0
  57. package/x/signals/{tests/lazy.test.d.ts → lazy/test.d.ts} +2 -3
  58. package/x/signals/{tests/lazy.test.js → lazy/test.js} +23 -13
  59. package/x/signals/lazy/test.js.map +1 -0
  60. package/x/signals/signal/class.d.ts +20 -0
  61. package/x/signals/signal/class.js +57 -0
  62. package/x/signals/signal/class.js.map +1 -0
  63. package/x/signals/signal/fn.d.ts +7 -0
  64. package/x/signals/signal/fn.js +23 -0
  65. package/x/signals/signal/fn.js.map +1 -0
  66. package/x/signals/{tests/signal.test.d.ts → signal/test.d.ts} +8 -7
  67. package/x/signals/{tests/signal.test.js → signal/test.js} +50 -46
  68. package/x/signals/signal/test.js.map +1 -0
  69. package/x/signals/signals.test.d.ts +26 -20
  70. package/x/signals/signals.test.js +8 -8
  71. package/x/signals/signals.test.js.map +1 -1
  72. package/x/signals/types.d.ts +4 -19
  73. package/x/signals/utils/default-compare.js +1 -1
  74. package/x/signals/utils/default-compare.js.map +1 -1
  75. package/x/signals/utils/symbols.d.ts +7 -0
  76. package/x/signals/utils/symbols.js +8 -0
  77. package/x/signals/utils/symbols.js.map +1 -0
  78. package/x/tests.test.js +1 -45
  79. package/x/tests.test.js.map +1 -1
  80. package/s/signals/core/derived.ts +0 -65
  81. package/s/signals/core/effect.ts +0 -31
  82. package/s/signals/core/lazy.ts +0 -78
  83. package/s/signals/core/parts/reactive.ts +0 -12
  84. package/s/signals/core/parts/readable.ts +0 -16
  85. package/s/signals/core/signal.ts +0 -101
  86. package/s/signals/porcelain.ts +0 -30
  87. package/s/signals/tests/effect.test.ts +0 -89
  88. package/s/tree/index.ts +0 -7
  89. package/s/tree/parts/branch.ts +0 -55
  90. package/s/tree/parts/chronobranch.ts +0 -86
  91. package/s/tree/parts/persistence.ts +0 -31
  92. package/s/tree/parts/trunk.ts +0 -70
  93. package/s/tree/parts/types.ts +0 -70
  94. package/s/tree/parts/utils/immute.ts +0 -43
  95. package/s/tree/parts/utils/process-options.ts +0 -7
  96. package/s/tree/parts/utils/setup.ts +0 -40
  97. package/s/tree/tree.test.ts +0 -366
  98. package/x/signals/core/derived.d.ts +0 -10
  99. package/x/signals/core/derived.js +0 -52
  100. package/x/signals/core/derived.js.map +0 -1
  101. package/x/signals/core/effect.d.ts +0 -5
  102. package/x/signals/core/effect.js +0 -17
  103. package/x/signals/core/effect.js.map +0 -1
  104. package/x/signals/core/lazy.d.ts +0 -11
  105. package/x/signals/core/lazy.js +0 -60
  106. package/x/signals/core/lazy.js.map +0 -1
  107. package/x/signals/core/parts/reactive.d.ts +0 -5
  108. package/x/signals/core/parts/reactive.js +0 -9
  109. package/x/signals/core/parts/reactive.js.map +0 -1
  110. package/x/signals/core/parts/readable.d.ts +0 -6
  111. package/x/signals/core/parts/readable.js +0 -15
  112. package/x/signals/core/parts/readable.js.map +0 -1
  113. package/x/signals/core/signal.d.ts +0 -13
  114. package/x/signals/core/signal.js +0 -77
  115. package/x/signals/core/signal.js.map +0 -1
  116. package/x/signals/porcelain.d.ts +0 -8
  117. package/x/signals/porcelain.js +0 -15
  118. package/x/signals/porcelain.js.map +0 -1
  119. package/x/signals/tests/derived.test.js.map +0 -1
  120. package/x/signals/tests/effect.test.js +0 -72
  121. package/x/signals/tests/effect.test.js.map +0 -1
  122. package/x/signals/tests/lazy.test.js.map +0 -1
  123. package/x/signals/tests/signal.test.js.map +0 -1
  124. package/x/tree/index.d.ts +0 -5
  125. package/x/tree/index.js +0 -6
  126. package/x/tree/index.js.map +0 -1
  127. package/x/tree/parts/branch.d.ts +0 -14
  128. package/x/tree/parts/branch.js +0 -42
  129. package/x/tree/parts/branch.js.map +0 -1
  130. package/x/tree/parts/chronobranch.d.ts +0 -25
  131. package/x/tree/parts/chronobranch.js +0 -75
  132. package/x/tree/parts/chronobranch.js.map +0 -1
  133. package/x/tree/parts/persistence.d.ts +0 -2
  134. package/x/tree/parts/persistence.js +0 -23
  135. package/x/tree/parts/persistence.js.map +0 -1
  136. package/x/tree/parts/trunk.d.ts +0 -19
  137. package/x/tree/parts/trunk.js +0 -57
  138. package/x/tree/parts/trunk.js.map +0 -1
  139. package/x/tree/parts/types.d.ts +0 -28
  140. package/x/tree/parts/types.js +0 -2
  141. package/x/tree/parts/types.js.map +0 -1
  142. package/x/tree/parts/utils/immute.d.ts +0 -11
  143. package/x/tree/parts/utils/immute.js +0 -33
  144. package/x/tree/parts/utils/immute.js.map +0 -1
  145. package/x/tree/parts/utils/process-options.d.ts +0 -2
  146. package/x/tree/parts/utils/process-options.js +0 -4
  147. package/x/tree/parts/utils/process-options.js.map +0 -1
  148. package/x/tree/parts/utils/setup.d.ts +0 -8
  149. package/x/tree/parts/utils/setup.js +0 -24
  150. package/x/tree/parts/utils/setup.js.map +0 -1
  151. package/x/tree/tree.test.d.ts +0 -40
  152. package/x/tree/tree.test.js +0 -325
  153. package/x/tree/tree.test.js.map +0 -1
@@ -1,101 +0,0 @@
1
-
2
- import {Reactive} from "./parts/reactive.js"
3
- import {tracker} from "../../tracker/tracker.js"
4
- import {SignalFn, SignalOptions} from "../types.js"
5
- import {defaultCompare} from "../utils/default-compare.js"
6
-
7
- export class Signal<V> extends Reactive<V> {
8
- #lock = false
9
- #compare: (a: any, b: any) => boolean
10
-
11
- constructor(value: V, options?: Partial<SignalOptions>) {
12
- super(value)
13
- this.#compare = options?.compare ?? defaultCompare
14
- }
15
-
16
- toString() {
17
- return `($signal "${String(this.get())}")`
18
- }
19
-
20
- async set(v: V, forcePublish = false) {
21
- const previous = this.sneak
22
- this.sneak = v
23
-
24
- if (forcePublish || !this.#compare(previous, v))
25
- await this.publish()
26
-
27
- return v
28
- }
29
-
30
- get value() {
31
- return this.get()
32
- }
33
-
34
- set value(v: V) {
35
- void this.set(v)
36
- }
37
-
38
- async publish() {
39
- // only wizards are allowed beyond this point.
40
- // - the implementation is subtle
41
- // - it looks wrong, but it's right
42
- // - tarnished alchemists, take heed: lock engages only for sync activity of the async fns (think of the value setter!)
43
-
44
- if (this.#lock)
45
- throw new Error("forbid circularity")
46
-
47
- const value = this.sneak
48
- let promise: Promise<any> = Promise.resolve()
49
-
50
- try {
51
- this.#lock = true
52
- promise = Promise.all([
53
- tracker.notifyWrite(this),
54
- this.on.publish(value),
55
- ])
56
- }
57
- finally {
58
- this.#lock = false
59
- }
60
-
61
- await promise
62
- return value
63
- }
64
-
65
- get core() {
66
- return this
67
- }
68
-
69
- fn() {
70
- const that = this as Signal<V>
71
-
72
- function f(): V
73
- function f(v: V): Promise<V>
74
- function f(_v?: V): V | Promise<V> {
75
- return (arguments.length === 0)
76
- ? that.get()
77
- : that.set(arguments[0])
78
- }
79
-
80
- f.core = that
81
- f.get = that.get.bind(that)
82
- f.set = that.set.bind(that)
83
- f.on = that.on
84
- f.dispose = that.dispose.bind(that)
85
- f.publish = that.publish.bind(that)
86
- f.fn = that.fn.bind(that)
87
-
88
- Object.defineProperty(f, "value", {
89
- get: () => that.value,
90
- set: (v) => that.value = v,
91
- })
92
-
93
- Object.defineProperty(f, "sneak", {
94
- get: () => that.sneak,
95
- set: (v) => that.sneak = v,
96
- })
97
-
98
- return f as SignalFn<V>
99
- }
100
- }
101
-
@@ -1,30 +0,0 @@
1
-
2
- import {SignalOptions} from "./types.js"
3
- import {Lazy} from "./core/lazy.js"
4
- import {Signal} from "./core/signal.js"
5
- import {Derived} from "./core/derived.js"
6
-
7
- export function lazy<V>(
8
- formula: () => V,
9
- options?: Partial<SignalOptions>,
10
- ) {
11
- return new Lazy<V>(formula, options).fn()
12
- }
13
-
14
- export function derived<V>(
15
- formula: () => V,
16
- options?: Partial<SignalOptions>,
17
- ) {
18
- return new Derived<V>(formula, options).fn()
19
- }
20
-
21
- export function signal<V>(
22
- value: V,
23
- options?: Partial<SignalOptions>,
24
- ) {
25
- return new Signal<V>(value, options).fn()
26
- }
27
-
28
- signal.lazy = lazy
29
- signal.derived = derived
30
-
@@ -1,89 +0,0 @@
1
-
2
- import {Science, test, expect} from "@e280/science"
3
- import {signal} from "../porcelain.js"
4
- import {effect} from "../core/effect.js"
5
-
6
- export default Science.suite({
7
- "tracks signal changes": test(async() => {
8
- const count = signal(1)
9
- let doubled = 0
10
-
11
- effect(() => doubled = count.value * 2)
12
- expect(doubled).is(2)
13
-
14
- await count.set(3)
15
- expect(doubled).is(6)
16
- }),
17
-
18
- "is only called when signal actually changes": test(async() => {
19
- const count = signal(1)
20
- let runs = 0
21
- effect(() => {
22
- count.get()
23
- runs++
24
- })
25
- expect(runs).is(1)
26
- await count.set(999)
27
- expect(runs).is(2)
28
- await count.set(999)
29
- expect(runs).is(2)
30
- }),
31
-
32
- "debounced": test(async() => {
33
- const count = signal(1)
34
- let runs = 0
35
- effect(() => {
36
- count.get()
37
- runs++
38
- })
39
- expect(runs).is(1)
40
- count.value++
41
- count.value++
42
- await count.set(count.get() + 1)
43
- expect(runs).is(2)
44
- }),
45
-
46
- "can be disposed": test(async() => {
47
- const count = signal(1)
48
- let doubled = 0
49
-
50
- const dispose = effect(() => doubled = count.value * 2)
51
- expect(doubled).is(2)
52
-
53
- await count.set(3)
54
- expect(doubled).is(6)
55
-
56
- dispose()
57
- await count.set(4)
58
- expect(doubled).is(6) // old value
59
- }),
60
-
61
- "signal set promise waits for effects": test(async() => {
62
- const count = signal(1)
63
- let doubled = 0
64
-
65
- effect(() => doubled = count.value * 2)
66
- expect(doubled).is(2)
67
-
68
- await count.set(3)
69
- expect(doubled).is(6)
70
- }),
71
-
72
- "only runs on change": test(async() => {
73
- const sig = signal("a")
74
- let runs = 0
75
-
76
- effect(() => {
77
- sig.value
78
- runs++
79
- })
80
- expect(runs).is(1)
81
-
82
- await sig.set("a")
83
- expect(runs).is(1)
84
-
85
- await sig.set("b")
86
- expect(runs).is(2)
87
- }),
88
- })
89
-
package/s/tree/index.ts DELETED
@@ -1,7 +0,0 @@
1
-
2
- export * from "./parts/branch.js"
3
- export * from "./parts/chronobranch.js"
4
- export * from "./parts/persistence.js"
5
- export * from "./parts/trunk.js"
6
- export * from "./parts/types.js"
7
-
@@ -1,55 +0,0 @@
1
-
2
- import {deep} from "@e280/stz"
3
- import {Immutable} from "../../prism/types.js"
4
- import {SignalFn} from "../../signals/types.js"
5
- import {signal} from "../../signals/porcelain.js"
6
- import {Branchstate, Mutator, TreeOptions, Selector, Tree} from "./types.js"
7
-
8
- /** @deprecated tree stuff has been replaced by prism/lens stuff */
9
- export class Branch<S extends Branchstate, ParentState extends Branchstate = any> implements Tree<S> {
10
- #previous: Immutable<S>
11
- #$data: SignalFn<Immutable<S>>
12
-
13
- constructor(
14
- private parent: Tree<ParentState>,
15
- private selector: Selector<S, ParentState>,
16
- private options: TreeOptions,
17
- ) {
18
-
19
- this.#$data = signal(this.#pull())
20
- this.#previous = this.state
21
-
22
- this.parent.on(() => {
23
- const oldState = this.#previous
24
- const newState = this.#pull()
25
- if (!deep.equal(newState, oldState)) {
26
- this.#previous = newState
27
- this.#$data.set(newState, true)
28
- }
29
- })
30
- }
31
-
32
- #pull() {
33
- return this.selector(this.parent.state as ParentState) as Immutable<S>
34
- }
35
-
36
- get state() {
37
- return this.#$data.get()
38
- }
39
-
40
- get on() {
41
- return this.#$data.on
42
- }
43
-
44
- async mutate(mutator: Mutator<S>) {
45
- await this.parent.mutate(
46
- parentState => mutator(this.selector(parentState))
47
- )
48
- return this.state
49
- }
50
-
51
- branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S> {
52
- return new Branch(this, selector, this.options)
53
- }
54
- }
55
-
@@ -1,86 +0,0 @@
1
-
2
- import {Chronicle, Immutable} from "../../prism/index.js"
3
- import {Branch} from "./branch.js"
4
- import {Branchstate, Mutator, TreeOptions, Selector, Tree} from "./types.js"
5
-
6
- /** @deprecated tree stuff has been replaced by prism/lens stuff */
7
- export class Chronobranch<S extends Branchstate, ParentState extends Branchstate = any> implements Tree<S> {
8
- #branch: Branch<Chronicle<S>, ParentState>
9
-
10
- constructor(
11
- public limit: number,
12
- public parent: Tree<ParentState>,
13
- public selector: Selector<Chronicle<S>, ParentState>,
14
- public options: TreeOptions,
15
- ) {
16
- this.#branch = parent.branch(selector)
17
- }
18
-
19
- get state() {
20
- return this.#branch.state.present
21
- }
22
-
23
- get undoable() {
24
- return this.#branch.state.past.length
25
- }
26
-
27
- get redoable() {
28
- return this.#branch.state.future.length
29
- }
30
-
31
- on(fn: (state: Immutable<S>) => void) {
32
- return this.#branch.on(chronicle => fn(chronicle.present))
33
- }
34
-
35
- /** progress forwards in history */
36
- async mutate(mutator: Mutator<S>) {
37
- const limit = Math.max(0, this.limit)
38
- const snapshot = this.options.clone(this.#branch.state.present) as S
39
- await this.#branch.mutate(chronicle => {
40
- mutator(chronicle.present)
41
- chronicle.past.push(snapshot)
42
- chronicle.past = chronicle.past.slice(-limit)
43
- chronicle.future = []
44
- })
45
- return this.state
46
- }
47
-
48
- /** step backwards into the past, by n steps */
49
- async undo(n = 1) {
50
- await this.#branch.mutate(chronicle => {
51
- const snapshots = chronicle.past.slice(-n)
52
- if (snapshots.length >= n) {
53
- const oldPresent = chronicle.present
54
- chronicle.present = snapshots.shift()!
55
- chronicle.past = chronicle.past.slice(0, -n)
56
- chronicle.future.unshift(oldPresent, ...snapshots)
57
- }
58
- })
59
- }
60
-
61
- /** step forwards into the future, by n steps */
62
- async redo(n = 1) {
63
- await this.#branch.mutate(chronicle => {
64
- const snapshots = chronicle.future.slice(0, n)
65
- if (snapshots.length >= n) {
66
- const oldPresent = chronicle.present
67
- chronicle.present = snapshots.shift()!
68
- chronicle.past.push(oldPresent, ...snapshots)
69
- chronicle.future = chronicle.future.slice(n)
70
- }
71
- })
72
- }
73
-
74
- /** wipe past and future snapshots */
75
- async wipe() {
76
- await this.#branch.mutate(chronicle => {
77
- chronicle.past = []
78
- chronicle.future = []
79
- })
80
- }
81
-
82
- branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S> {
83
- return new Branch(this, selector, this.options)
84
- }
85
- }
86
-
@@ -1,31 +0,0 @@
1
-
2
- import {Persistence} from "./types.js"
3
-
4
- export const localPersistence = <X>(
5
- key: string,
6
- storage: Storage = window.localStorage,
7
- ): Persistence<X> => ({
8
-
9
- store: {
10
- async get() {
11
- const json = storage.getItem(key)
12
- return json
13
- ? JSON.parse(json)
14
- : undefined
15
- },
16
- async set(state) {
17
- const json = JSON.stringify(state)
18
- storage.setItem(key, json)
19
- },
20
- },
21
-
22
- onChange: (fn: () => void) => {
23
- const listener = (event: StorageEvent) => {
24
- if (event.storageArea === storage && event.key === key)
25
- fn()
26
- }
27
- window.addEventListener("storage", listener)
28
- return () => window.removeEventListener("storage", listener)
29
- },
30
- })
31
-
@@ -1,70 +0,0 @@
1
-
2
- import {deep} from "@e280/stz"
3
- import {Branch} from "./branch.js"
4
- import {Immute} from "./utils/immute.js"
5
- import {trunkSetup} from "./utils/setup.js"
6
- import {Chronobranch} from "./chronobranch.js"
7
- import {processOptions} from "./utils/process-options.js"
8
- import {Branchstate, Mutator, TreeOptions, Selector, Tree, Trunkstate} from "./types.js"
9
- import {Chronicle} from "../../prism/index.js"
10
-
11
- /** @deprecated tree stuff has been replaced by prism/lens stuff */
12
- export class Trunk<S extends Trunkstate> implements Tree<S> {
13
- static setup = trunkSetup
14
- static chronicle = <S extends Branchstate>(state: S): Chronicle<S> => ({
15
- present: state,
16
- past: [],
17
- future: [],
18
- })
19
-
20
- options: TreeOptions
21
- #mutationLock = 0
22
- #immute: Immute<S>
23
-
24
- constructor(state: S, options: Partial<TreeOptions> = {}) {
25
- this.options = processOptions(options)
26
- this.#immute = new Immute(state, this.options)
27
- }
28
-
29
- get state() {
30
- return this.#immute.immutable
31
- }
32
-
33
- get on() {
34
- return this.#immute.on
35
- }
36
-
37
- async mutate(mutator: Mutator<S>) {
38
- const oldState = this.#immute.get()
39
- if (this.#mutationLock > 0)
40
- throw new Error("nested mutations are forbidden")
41
- let promise = Promise.resolve()
42
- try {
43
- this.#mutationLock++
44
- const newState = this.options.clone(oldState)
45
- mutator(newState)
46
- const isChanged = !deep.equal(newState, oldState)
47
- if (isChanged)
48
- promise = this.overwrite(newState)
49
- }
50
- finally { this.#mutationLock-- }
51
- await promise
52
- return this.state
53
- }
54
-
55
- async overwrite(state: S) {
56
- await this.#immute.set(state)
57
- }
58
-
59
- branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S> {
60
- return new Branch(this, selector, this.options)
61
- }
62
-
63
- chronobranch<Sub extends Branchstate>(
64
- limit: number,
65
- selector: Selector<Chronicle<Sub>, S>,
66
- ) {
67
- return new Chronobranch(limit, this, selector, this.options)
68
- }
69
- }
70
-
@@ -1,70 +0,0 @@
1
-
2
- import { EzStore, Versioned } from "../../prism/index.js"
3
- import { Immutable } from "../../prism/types.js"
4
- import {Branch} from "./branch.js"
5
-
6
- export type TreeOptions = {
7
- clone: <X>(x: X) => X
8
- }
9
-
10
- /** @deprecated renamed to `TreeOptions` */
11
- export type Options = TreeOptions
12
-
13
- export type Selector<Sub, S> = (state: S) => Sub
14
- export type Mutator<S> = (state: S) => void
15
-
16
- export type Trunkstate = {}
17
- export type Branchstate = {} | null | undefined
18
-
19
- // export type Versioned<S extends Trunkstate> = {
20
- // state: S
21
- // version: number
22
- // }
23
- //
24
- // export type Immutable<T> =
25
- // T extends (...args: any[]) => any ? T :
26
- // T extends readonly any[] ? ReadonlyArray<Immutable<T[number]>> :
27
- // T extends object ? { readonly [K in keyof T]: Immutable<T[K]> } :
28
- // T
29
- //
30
- // export type Mutable<T> =
31
- // T extends (...args: any[]) => any ? T :
32
- // T extends ReadonlyArray<infer U> ? Mutable<U>[] :
33
- // T extends object ? { -readonly [K in keyof T]: Mutable<T[K]> } :
34
- // T
35
-
36
- export type Tree<S extends Branchstate> = {
37
- get state(): Immutable<S>
38
- on(fn: (state: Immutable<S>) => void): () => void
39
- mutate(mutator: Mutator<S>): Promise<Immutable<S>>
40
- branch<Sub extends Branchstate>(selector: Selector<Sub, S>): Branch<Sub, S>
41
- }
42
-
43
- export type SetupOptions<S extends Trunkstate> = {
44
- version: number
45
- initialState: S
46
- saveDebounceTime?: number
47
- persistence?: Persistence<Versioned<S>>
48
- }
49
-
50
- // export type Chronicle<S extends Branchstate> = {
51
- // // [abc] d [efg]
52
- // // \ \ \
53
- // // \ \ future
54
- // // \ present
55
- // // past
56
- // past: S[]
57
- // present: S
58
- // future: S[]
59
- // }
60
-
61
- // export type EzStore<X> = {
62
- // get(): Promise<X | undefined>
63
- // set(state: X | undefined): Promise<void>
64
- // }
65
-
66
- export type Persistence<X> = {
67
- store: EzStore<X>
68
- onChange: (fn: () => void) => (() => void)
69
- }
70
-
@@ -1,43 +0,0 @@
1
-
2
- import {deep, sub} from "@e280/stz"
3
- import {TreeOptions} from "../types.js"
4
- import {tracker} from "../../../tracker/tracker.js"
5
- import {Immutable} from "../../../prism/types.js"
6
-
7
- export class Immute<S> {
8
- #mutable: S
9
- #immutable: Immutable<S>
10
-
11
- constructor(mutable: S, private options: TreeOptions) {
12
- this.#mutable = mutable
13
- this.#immutable = this.#petrify(mutable)
14
- }
15
-
16
- #petrify<X>(x: X) {
17
- return deep.freeze(
18
- this.options.clone(x)
19
- ) as Immutable<X>
20
- }
21
-
22
- on = sub<[Immutable<S>]>()
23
-
24
- get() {
25
- tracker.notifyRead(this)
26
- return this.#mutable
27
- }
28
-
29
- async set(mutable: S) {
30
- this.#mutable = mutable
31
- this.#immutable = deep.freeze(this.options.clone(this.get())) as Immutable<S>
32
- await Promise.all([
33
- this.on.publish(this.#immutable),
34
- tracker.notifyWrite(this),
35
- ])
36
- }
37
-
38
- get immutable() {
39
- tracker.notifyRead(this)
40
- return this.#immutable
41
- }
42
- }
43
-
@@ -1,7 +0,0 @@
1
-
2
- import {TreeOptions} from "../types.js"
3
-
4
- export const processOptions = (options: Partial<TreeOptions>): TreeOptions => ({
5
- clone: options.clone ?? (<X>(x: X) => structuredClone(x)),
6
- })
7
-
@@ -1,40 +0,0 @@
1
-
2
- import {debounce} from "@e280/stz"
3
-
4
- import {Trunk} from "../trunk.js"
5
- import {localPersistence} from "../persistence.js"
6
- import {SetupOptions, Trunkstate} from "../types.js"
7
-
8
- export async function trunkSetup<S extends Trunkstate>(options: SetupOptions<S>) {
9
- const {
10
- version,
11
- initialState,
12
- saveDebounceTime = 500,
13
- persistence = localPersistence("strataTree"),
14
- } = options
15
-
16
- const trunk = new Trunk<S>(initialState)
17
-
18
- async function load() {
19
- const pickle = await persistence.store.get()
20
- if (pickle && pickle.version === version)
21
- await trunk.overwrite(pickle.state)
22
- }
23
-
24
- const save = debounce(saveDebounceTime, async() => persistence.store.set({
25
- version,
26
- state: trunk.state as any,
27
- }))
28
-
29
- // persistence: initial load from store
30
- await load()
31
-
32
- // persistence: save to store
33
- trunk.on(save)
34
-
35
- // cross-tab sync
36
- const dispose = persistence.onChange(load)
37
-
38
- return {trunk, load, save, dispose}
39
- }
40
-