@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 +25 -10
- package/package.json +1 -1
- package/s/index.ts +1 -0
- package/s/parts/persistence.ts +31 -0
- package/s/parts/strata.ts +15 -15
- package/s/parts/types.ts +22 -0
- package/s/parts/utils/setup.ts +40 -0
- package/x/index.d.ts +1 -0
- package/x/index.js +1 -0
- package/x/index.js.map +1 -1
- package/x/parts/persistence.d.ts +2 -0
- package/x/parts/persistence.js +23 -0
- package/x/parts/persistence.js.map +1 -0
- package/x/parts/strata.d.ts +3 -0
- package/x/parts/strata.js +18 -18
- package/x/parts/strata.js.map +1 -1
- package/x/parts/types.d.ts +18 -0
- package/x/parts/utils/setup.d.ts +8 -0
- package/x/parts/utils/setup.js +24 -0
- package/x/parts/utils/setup.js.map +1 -0
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
package/s/index.ts
CHANGED
|
@@ -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
package/x/index.js
CHANGED
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,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"}
|
package/x/parts/strata.d.ts
CHANGED
|
@@ -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
|
|
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
|
package/x/parts/strata.js.map
CHANGED
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"strata.js","sourceRoot":"","sources":["../../s/parts/strata.ts"],"names":[],"mappings":"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"}
|
package/x/parts/types.d.ts
CHANGED
|
@@ -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"}
|