@e280/strata 0.0.0-2 → 0.0.0-3

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 CHANGED
@@ -76,6 +76,31 @@
76
76
 
77
77
  ## only high-class discerning aristocrats permitted beyond this point
78
78
 
79
+ ### `Strata.setup` for localStorage persistence etc
80
+ - simple setup
81
+ ```ts
82
+ const {strata} = await Strata.setup({
83
+ version: 1, // 👈 bump whenever your change state schema!
84
+ initialState: {count: 0},
85
+ })
86
+ ```
87
+ - it's compatible with [`@e280/kv`](https://github.com/e280/kv)
88
+ ```ts
89
+ import {Kv, StorageDriver} from "@e280/kv"
90
+
91
+ const kv = new Kv(new StorageDriver())
92
+ const store = kv.store<any>("strata")
93
+
94
+ const {strata} = await Strata.setup({
95
+ version: 1,
96
+ initialState: {count: 0},
97
+ persistence: {
98
+ store,
99
+ onChange: StorageDriver.onStorageEvent,
100
+ },
101
+ })
102
+ ```
103
+
79
104
  ### `Chronstrata` for undo/redo history
80
105
  - first, put a `Chronicle` into your state tree
81
106
  ```ts
@@ -106,16 +131,6 @@
106
131
  ```
107
132
  - you can check how many undoable or redoable steps are available
108
133
  ```ts
109
- snacks.undoable // 0
110
-
111
- await snacks.mutate(s => s.peanuts = 101)
112
- await snacks.mutate(s => s.peanuts = 102)
113
- await snacks.mutate(s => s.peanuts = 103)
114
-
115
- snacks.undoable // 3
116
-
117
- await snacks.undo()
118
-
119
134
  snacks.undoable // 2
120
135
  snacks.redoable // 1
121
136
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e280/strata",
3
- "version": "0.0.0-2",
3
+ "version": "0.0.0-3",
4
4
  "description": "state management",
5
5
  "license": "MIT",
6
6
  "author": "Chase Moskal <chasemoskal@gmail.com>",
package/s/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
 
2
2
  export * from "./parts/chronstrata.js"
3
+ export * from "./parts/persistence.js"
3
4
  export * from "./parts/strata.js"
4
5
  export * from "./parts/substrata.js"
5
6
  export * from "./parts/types.js"
@@ -0,0 +1,31 @@
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
+
package/s/parts/strata.ts CHANGED
@@ -1,12 +1,14 @@
1
1
 
2
2
  import {debounce, deep, sub} from "@e280/stz"
3
3
 
4
+ import {strataSetup} from "./utils/setup.js"
4
5
  import {Substrata} from "./substrata.js"
5
6
  import {Chronstrata} from "./chronstrata.js"
6
7
  import {processOptions} from "./utils/process-options.js"
7
8
  import {Chronicle, Mutator, Options, Selector, State, Stratum, Substate} from "./types.js"
8
9
 
9
10
  export class Strata<S extends State> implements Stratum<S> {
11
+ static setup = strataSetup
10
12
  static chronicle = <S extends Substate>(state: S): Chronicle<S> => ({
11
13
  present: state,
12
14
  past: [],
@@ -19,11 +21,6 @@ export class Strata<S extends State> implements Stratum<S> {
19
21
  #mutable: S
20
22
  #immutable: S
21
23
  #mutationLock = 0
22
- #dispatchMutation = debounce(0, async(state: S) => {
23
- this.#mutationLock++
24
- try { await this.watch.pub(state) }
25
- finally { this.#mutationLock-- }
26
- })
27
24
 
28
25
  constructor(state: S, options: Partial<Options> = {}) {
29
26
  this.options = processOptions(options)
@@ -31,11 +28,6 @@ export class Strata<S extends State> implements Stratum<S> {
31
28
  this.#immutable = deep.freeze(this.options.clone(state))
32
29
  }
33
30
 
34
- #updateState(state: S) {
35
- this.#mutable = state
36
- this.#immutable = deep.freeze(this.options.clone(state))
37
- }
38
-
39
31
  get state() {
40
32
  return this.#immutable
41
33
  }
@@ -49,14 +41,16 @@ export class Strata<S extends State> implements Stratum<S> {
49
41
  finally { this.#mutationLock-- }
50
42
  const newState = this.#mutable
51
43
  const isChanged = !deep.equal(newState, oldState)
52
- if (isChanged) {
53
- this.#updateState(newState)
54
- const immutable = this.state
55
- await this.#dispatchMutation(immutable)
56
- }
44
+ if (isChanged) await this.overwrite(newState)
57
45
  return this.#immutable
58
46
  }
59
47
 
48
+ async overwrite(state: S) {
49
+ this.#mutable = state
50
+ this.#immutable = deep.freeze(this.options.clone(state))
51
+ await this.#dispatchMutation()
52
+ }
53
+
60
54
  substrata<Sub extends Substate>(selector: Selector<S, Sub>): Substrata<S, Sub> {
61
55
  return new Substrata(this, selector, this.options)
62
56
  }
@@ -67,5 +61,11 @@ export class Strata<S extends State> implements Stratum<S> {
67
61
  ) {
68
62
  return new Chronstrata(limit, this, selector, this.options)
69
63
  }
64
+
65
+ #dispatchMutation = debounce(0, async() => {
66
+ this.#mutationLock++
67
+ try { await this.watch.pub(this.#immutable) }
68
+ finally { this.#mutationLock-- }
69
+ })
70
70
  }
71
71
 
package/s/parts/types.ts CHANGED
@@ -11,6 +11,11 @@ export type Mutator<S> = (state: S) => void
11
11
  export type State = {}
12
12
  export type Substate = {} | null | undefined
13
13
 
14
+ export type Versioned<S extends State> = {
15
+ state: S
16
+ version: number
17
+ }
18
+
14
19
  export type Stratum<S extends Substate> = {
15
20
  readonly state: S
16
21
  watch(fn: (s: S) => void): () => void
@@ -18,6 +23,13 @@ export type Stratum<S extends Substate> = {
18
23
  substrata<Sub extends Substate>(selector: Selector<S, Sub>): Substrata<S, Sub>
19
24
  }
20
25
 
26
+ export type SetupOptions<S extends State> = {
27
+ version: number
28
+ initialState: S
29
+ saveDebounceTime?: number
30
+ persistence?: Persistence<Versioned<S>>
31
+ }
32
+
21
33
  export type Chronicle<S extends Substate> = {
22
34
  // [abc] d [efg]
23
35
  // \ \ \
@@ -29,3 +41,13 @@ export type Chronicle<S extends Substate> = {
29
41
  future: S[]
30
42
  }
31
43
 
44
+ export type EzStore<X> = {
45
+ get(): Promise<X | undefined>
46
+ set(state: X | undefined): Promise<void>
47
+ }
48
+
49
+ export type Persistence<X> = {
50
+ store: EzStore<X>
51
+ onChange: (fn: () => void) => (() => void)
52
+ }
53
+
@@ -0,0 +1,40 @@
1
+
2
+ import {debounce} from "@e280/stz"
3
+
4
+ import {Strata} from "../strata.js"
5
+ import {State, SetupOptions} from "../types.js"
6
+ import {localPersistence} from "../persistence.js"
7
+
8
+ export async function strataSetup<S extends State>(options: SetupOptions<S>) {
9
+ const {
10
+ version,
11
+ initialState,
12
+ saveDebounceTime = 500,
13
+ persistence = localPersistence("strata"),
14
+ } = options
15
+
16
+ const strata = new Strata<S>(initialState)
17
+
18
+ async function load() {
19
+ const pickle = await persistence.store.get()
20
+ if (pickle && pickle.version === version)
21
+ await strata.overwrite(pickle.state)
22
+ }
23
+
24
+ const save = debounce(saveDebounceTime, async() => persistence.store.set({
25
+ version,
26
+ state: strata.state,
27
+ }))
28
+
29
+ // persistence: initial load from store
30
+ await load()
31
+
32
+ // persistence: save to store
33
+ strata.watch(save)
34
+
35
+ // cross-tab sync
36
+ const dispose = persistence.onChange(load)
37
+
38
+ return {strata, load, save, dispose}
39
+ }
40
+
package/x/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./parts/chronstrata.js";
2
+ export * from "./parts/persistence.js";
2
3
  export * from "./parts/strata.js";
3
4
  export * from "./parts/substrata.js";
4
5
  export * from "./parts/types.js";
package/x/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./parts/chronstrata.js";
2
+ export * from "./parts/persistence.js";
2
3
  export * from "./parts/strata.js";
3
4
  export * from "./parts/substrata.js";
4
5
  export * from "./parts/types.js";
package/x/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../s/index.ts"],"names":[],"mappings":"AACA,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../s/index.ts"],"names":[],"mappings":"AACA,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { Persistence } from "./types.js";
2
+ export declare const localPersistence: <X>(key: string, storage?: Storage) => Persistence<X>;
@@ -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/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"}
@@ -1,14 +1,17 @@
1
+ import { strataSetup } from "./utils/setup.js";
1
2
  import { Substrata } from "./substrata.js";
2
3
  import { Chronstrata } from "./chronstrata.js";
3
4
  import { Chronicle, Mutator, Options, Selector, State, Stratum, Substate } from "./types.js";
4
5
  export declare class Strata<S extends State> implements Stratum<S> {
5
6
  #private;
7
+ static setup: typeof strataSetup;
6
8
  static chronicle: <S_1 extends Substate>(state: S_1) => Chronicle<S_1>;
7
9
  options: Options;
8
10
  watch: import("@e280/stz").Sub<[state: S]>;
9
11
  constructor(state: S, options?: Partial<Options>);
10
12
  get state(): S;
11
13
  mutate(mutator: Mutator<S>): Promise<S>;
14
+ overwrite(state: S): Promise<void>;
12
15
  substrata<Sub extends Substate>(selector: Selector<S, Sub>): Substrata<S, Sub>;
13
16
  chronstrata<Sub extends Substate>(limit: number, selector: Selector<S, Chronicle<Sub>>): Chronstrata<S, Sub>;
14
17
  }
package/x/parts/strata.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { debounce, deep, sub } from "@e280/stz";
2
+ import { strataSetup } from "./utils/setup.js";
2
3
  import { Substrata } from "./substrata.js";
3
4
  import { Chronstrata } from "./chronstrata.js";
4
5
  import { processOptions } from "./utils/process-options.js";
5
6
  export class Strata {
7
+ static setup = strataSetup;
6
8
  static chronicle = (state) => ({
7
9
  present: state,
8
10
  past: [],
@@ -13,24 +15,11 @@ export class Strata {
13
15
  #mutable;
14
16
  #immutable;
15
17
  #mutationLock = 0;
16
- #dispatchMutation = debounce(0, async (state) => {
17
- this.#mutationLock++;
18
- try {
19
- await this.watch.pub(state);
20
- }
21
- finally {
22
- this.#mutationLock--;
23
- }
24
- });
25
18
  constructor(state, options = {}) {
26
19
  this.options = processOptions(options);
27
20
  this.#mutable = state;
28
21
  this.#immutable = deep.freeze(this.options.clone(state));
29
22
  }
30
- #updateState(state) {
31
- this.#mutable = state;
32
- this.#immutable = deep.freeze(this.options.clone(state));
33
- }
34
23
  get state() {
35
24
  return this.#immutable;
36
25
  }
@@ -47,18 +36,29 @@ export class Strata {
47
36
  }
48
37
  const newState = this.#mutable;
49
38
  const isChanged = !deep.equal(newState, oldState);
50
- if (isChanged) {
51
- this.#updateState(newState);
52
- const immutable = this.state;
53
- await this.#dispatchMutation(immutable);
54
- }
39
+ if (isChanged)
40
+ await this.overwrite(newState);
55
41
  return this.#immutable;
56
42
  }
43
+ async overwrite(state) {
44
+ this.#mutable = state;
45
+ this.#immutable = deep.freeze(this.options.clone(state));
46
+ await this.#dispatchMutation();
47
+ }
57
48
  substrata(selector) {
58
49
  return new Substrata(this, selector, this.options);
59
50
  }
60
51
  chronstrata(limit, selector) {
61
52
  return new Chronstrata(limit, this, selector, this.options);
62
53
  }
54
+ #dispatchMutation = debounce(0, async () => {
55
+ this.#mutationLock++;
56
+ try {
57
+ await this.watch.pub(this.#immutable);
58
+ }
59
+ finally {
60
+ this.#mutationLock--;
61
+ }
62
+ });
63
63
  }
64
64
  //# sourceMappingURL=strata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"strata.js","sourceRoot":"","sources":["../../s/parts/strata.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAE7C,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAA;AAGzD,MAAM,OAAO,MAAM;IAClB,MAAM,CAAC,SAAS,GAAG,CAAqB,KAAQ,EAAgB,EAAE,CAAC,CAAC;QACnE,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,EAAE;QACR,MAAM,EAAE,EAAE;KACV,CAAC,CAAA;IAEF,OAAO,CAAS;IAChB,KAAK,GAAG,GAAG,EAAc,CAAA;IAEzB,QAAQ,CAAG;IACX,UAAU,CAAG;IACb,aAAa,GAAG,CAAC,CAAA;IACjB,iBAAiB,GAAG,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAC,KAAQ,EAAE,EAAE;QACjD,IAAI,CAAC,aAAa,EAAE,CAAA;QACpB,IAAI,CAAC;YAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAAC,CAAC;gBAC3B,CAAC;YAAC,IAAI,CAAC,aAAa,EAAE,CAAA;QAAC,CAAC;IACjC,CAAC,CAAC,CAAA;IAEF,YAAY,KAAQ,EAAE,UAA4B,EAAE;QACnD,IAAI,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;QACtC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAA;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IACzD,CAAC;IAED,YAAY,CAAC,KAAQ;QACpB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAA;QACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IACzD,CAAC;IAED,IAAI,KAAK;QACR,OAAO,IAAI,CAAC,UAAU,CAAA;IACvB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAmB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAClD,IAAI,IAAI,CAAC,aAAa,GAAG,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;QAClD,IAAI,CAAC,aAAa,EAAE,CAAA;QACpB,IAAI,CAAC;YAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAAC,CAAC;gBACtB,CAAC;YAAC,IAAI,CAAC,aAAa,EAAE,CAAA;QAAC,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;QAC9B,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QACjD,IAAI,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAA;YAC5B,MAAM,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAA;QACxC,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAA;IACvB,CAAC;IAED,SAAS,CAAuB,QAA0B;QACzD,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IACnD,CAAC;IAED,WAAW,CACT,KAAa,EACb,QAAqC;QAEtC,OAAO,IAAI,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IAC5D,CAAC"}
1
+ {"version":3,"file":"strata.js","sourceRoot":"","sources":["../../s/parts/strata.ts"],"names":[],"mappings":"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"}
@@ -6,14 +6,32 @@ export type Selector<S, Sub> = (state: S) => Sub;
6
6
  export type Mutator<S> = (state: S) => void;
7
7
  export type State = {};
8
8
  export type Substate = {} | null | undefined;
9
+ export type Versioned<S extends State> = {
10
+ state: S;
11
+ version: number;
12
+ };
9
13
  export type Stratum<S extends Substate> = {
10
14
  readonly state: S;
11
15
  watch(fn: (s: S) => void): () => void;
12
16
  mutate(mutator: Mutator<S>): Promise<S>;
13
17
  substrata<Sub extends Substate>(selector: Selector<S, Sub>): Substrata<S, Sub>;
14
18
  };
19
+ export type SetupOptions<S extends State> = {
20
+ version: number;
21
+ initialState: S;
22
+ saveDebounceTime?: number;
23
+ persistence?: Persistence<Versioned<S>>;
24
+ };
15
25
  export type Chronicle<S extends Substate> = {
16
26
  past: S[];
17
27
  present: S;
18
28
  future: S[];
19
29
  };
30
+ export type EzStore<X> = {
31
+ get(): Promise<X | undefined>;
32
+ set(state: X | undefined): Promise<void>;
33
+ };
34
+ export type Persistence<X> = {
35
+ store: EzStore<X>;
36
+ onChange: (fn: () => void) => (() => void);
37
+ };
@@ -0,0 +1,8 @@
1
+ import { Strata } from "../strata.js";
2
+ import { State, SetupOptions } from "../types.js";
3
+ export declare function strataSetup<S extends State>(options: SetupOptions<S>): Promise<{
4
+ strata: Strata<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 { Strata } from "../strata.js";
3
+ import { localPersistence } from "../persistence.js";
4
+ export async function strataSetup(options) {
5
+ const { version, initialState, saveDebounceTime = 500, persistence = localPersistence("strata"), } = options;
6
+ const strata = new Strata(initialState);
7
+ async function load() {
8
+ const pickle = await persistence.store.get();
9
+ if (pickle && pickle.version === version)
10
+ await strata.overwrite(pickle.state);
11
+ }
12
+ const save = debounce(saveDebounceTime, async () => persistence.store.set({
13
+ version,
14
+ state: strata.state,
15
+ }));
16
+ // persistence: initial load from store
17
+ await load();
18
+ // persistence: save to store
19
+ strata.watch(save);
20
+ // cross-tab sync
21
+ const dispose = persistence.onChange(load);
22
+ return { strata, load, save, dispose };
23
+ }
24
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../s/parts/utils/setup.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAC,MAAM,WAAW,CAAA;AAElC,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAA;AAEnC,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAA;AAElD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAkB,OAAwB;IAC1E,MAAM,EACL,OAAO,EACP,YAAY,EACZ,gBAAgB,GAAG,GAAG,EACtB,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,GACxC,GAAG,OAAO,CAAA;IAEX,MAAM,MAAM,GAAG,IAAI,MAAM,CAAI,YAAY,CAAC,CAAA;IAE1C,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,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACtC,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,gBAAgB,EAAE,KAAK,IAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC;QACxE,OAAO;QACP,KAAK,EAAE,MAAM,CAAC,KAAK;KACnB,CAAC,CAAC,CAAA;IAEH,uCAAuC;IACvC,MAAM,IAAI,EAAE,CAAA;IAEZ,6BAA6B;IAC7B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAElB,iBAAiB;IACjB,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAE1C,OAAO,EAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAC,CAAA;AACrC,CAAC"}