@oddkin/state 0.0.0-stage → 1.0.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +82 -2
- package/dist/context/context.d.ts +77 -0
- package/dist/context/context.d.ts.map +1 -0
- package/dist/context/context.js +335 -0
- package/dist/context/context.js.map +1 -0
- package/dist/context/index.d.ts +8 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +7 -0
- package/dist/context/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/inspect/index.d.ts +33 -0
- package/dist/inspect/index.d.ts.map +1 -0
- package/dist/inspect/index.js +63 -0
- package/dist/inspect/index.js.map +1 -0
- package/dist/internal/dev.d.ts +6 -0
- package/dist/internal/dev.d.ts.map +1 -0
- package/dist/internal/dev.js +6 -0
- package/dist/internal/dev.js.map +1 -0
- package/dist/persist/index.d.ts +74 -0
- package/dist/persist/index.d.ts.map +1 -0
- package/dist/persist/index.js +228 -0
- package/dist/persist/index.js.map +1 -0
- package/dist/prod/context/context.js +222 -0
- package/dist/prod/context/index.js +8 -0
- package/dist/prod/index.js +7 -0
- package/dist/prod/inspect/index.js +45 -0
- package/dist/prod/internal/dev.js +4 -0
- package/dist/prod/persist/index.js +140 -0
- package/dist/prod/store/index.js +6 -0
- package/dist/prod/store/store.js +167 -0
- package/dist/store/index.d.ts +7 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +6 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/store.d.ts +83 -0
- package/dist/store/store.d.ts.map +1 -0
- package/dist/store/store.js +235 -0
- package/dist/store/store.js.map +1 -0
- package/package.json +50 -4
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { OddkinError } from "@oddkin/core/diagnostics";
|
|
2
|
+
import { INTERNALS, liveCounts } from "../store/store.js";
|
|
3
|
+
function storeStats() {
|
|
4
|
+
return liveCounts();
|
|
5
|
+
}
|
|
6
|
+
function inspect(store, options) {
|
|
7
|
+
const internals = store[INTERNALS];
|
|
8
|
+
if (!internals)
|
|
9
|
+
throw new OddkinError("state.inspect.unsupported", "");
|
|
10
|
+
const limit = options?.limit ?? 50;
|
|
11
|
+
if (!Number.isInteger(limit) || limit < 0)
|
|
12
|
+
throw new OddkinError("state.inspect.limit", "");
|
|
13
|
+
const updates = [];
|
|
14
|
+
let recorded = 0, stopped = !1;
|
|
15
|
+
const stop = internals.observe({
|
|
16
|
+
update(record) {
|
|
17
|
+
recorded++, limit !== 0 && (updates.length === limit && updates.shift(), updates.push({ previous: record.previous, next: record.next, actions: record.actions }));
|
|
18
|
+
},
|
|
19
|
+
dispose() {
|
|
20
|
+
stopped = !0;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
store,
|
|
25
|
+
get updates() {
|
|
26
|
+
return [...updates];
|
|
27
|
+
},
|
|
28
|
+
get recorded() {
|
|
29
|
+
return recorded;
|
|
30
|
+
},
|
|
31
|
+
get subscribers() {
|
|
32
|
+
return internals.subscribers;
|
|
33
|
+
},
|
|
34
|
+
get stopped() {
|
|
35
|
+
return stopped || store.disposed;
|
|
36
|
+
},
|
|
37
|
+
dispose() {
|
|
38
|
+
stopped || (stopped = !0, stop());
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
inspect,
|
|
44
|
+
storeStats
|
|
45
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { OddkinError, reportOddkinError } from "@oddkin/core/diagnostics";
|
|
2
|
+
import { computed, signal } from "@oddkin/core/reactive";
|
|
3
|
+
import { action } from "../store/store.js";
|
|
4
|
+
const NOTHING = /* @__PURE__ */ Symbol("nothing written");
|
|
5
|
+
function isEnvelope(value) {
|
|
6
|
+
if (typeof value != "object" || value === null || !("data" in value))
|
|
7
|
+
return !1;
|
|
8
|
+
const version = value.version;
|
|
9
|
+
return typeof version == "number" && Number.isInteger(version) && version >= 1;
|
|
10
|
+
}
|
|
11
|
+
function persist(store, options) {
|
|
12
|
+
const { key, version } = options;
|
|
13
|
+
if (typeof key != "string" || key === "" || !Number.isInteger(version) || version < 1)
|
|
14
|
+
throw new OddkinError("state.persist.options", "");
|
|
15
|
+
if (store.disposed)
|
|
16
|
+
throw new OddkinError("state.store.disposed", "");
|
|
17
|
+
const select = options.select ?? ((state) => state), restore = options.restore ?? ((_state, saved) => saved), equals = options.equals ?? Object.is, serialize = options.serialize ?? JSON.stringify, deserialize = options.deserialize ?? JSON.parse, status = signal("empty"), fail = (code, message, cause) => {
|
|
18
|
+
const error = new OddkinError(code, message, cause === void 0 ? void 0 : { cause });
|
|
19
|
+
if (!options.onError)
|
|
20
|
+
return reportOddkinError(error, { code, origin: key });
|
|
21
|
+
try {
|
|
22
|
+
options.onError(error);
|
|
23
|
+
} catch (handlerError) {
|
|
24
|
+
reportOddkinError(handlerError, { code, origin: key });
|
|
25
|
+
}
|
|
26
|
+
}, reject = (code, message, cause) => {
|
|
27
|
+
status.value = "rejected", fail(code, message, cause);
|
|
28
|
+
};
|
|
29
|
+
let storage;
|
|
30
|
+
try {
|
|
31
|
+
const source = options.storage ?? (() => globalThis.localStorage);
|
|
32
|
+
storage = (typeof source == "function" ? source() : source) ?? void 0, storage || fail("state.persist.unavailable", "");
|
|
33
|
+
} catch (error) {
|
|
34
|
+
storage = void 0, fail("state.persist.unavailable", "", error);
|
|
35
|
+
}
|
|
36
|
+
let text = null;
|
|
37
|
+
if (storage)
|
|
38
|
+
try {
|
|
39
|
+
text = storage.getItem(key);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
storage = void 0, fail("state.persist.read", "", error);
|
|
42
|
+
}
|
|
43
|
+
storage || (status.value = "unavailable");
|
|
44
|
+
let blocked = !1;
|
|
45
|
+
if (storage && text !== null) {
|
|
46
|
+
let envelope, parsed = !0;
|
|
47
|
+
try {
|
|
48
|
+
envelope = deserialize(text);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
parsed = !1, reject("state.persist.parse", "", error);
|
|
51
|
+
}
|
|
52
|
+
if (parsed && !isEnvelope(envelope))
|
|
53
|
+
reject("state.persist.parse", "");
|
|
54
|
+
else if (parsed && isEnvelope(envelope) && envelope.version > version)
|
|
55
|
+
blocked = !0, status.value = "blocked", fail("state.persist.version", "");
|
|
56
|
+
else if (parsed && isEnvelope(envelope)) {
|
|
57
|
+
let data = envelope.data, migrated = !0;
|
|
58
|
+
for (let next = envelope.version + 1; next <= version && migrated; next++) {
|
|
59
|
+
const step = options.migrate?.[next];
|
|
60
|
+
if (!step) {
|
|
61
|
+
migrated = !1, reject("state.persist.migrate", "");
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
data = step(data);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
migrated = !1, reject("state.persist.migrate", "", error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (migrated) {
|
|
71
|
+
let valid = !0, cause;
|
|
72
|
+
try {
|
|
73
|
+
valid = options.validate ? options.validate(data) : !0;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
valid = !1, cause = error;
|
|
76
|
+
}
|
|
77
|
+
if (valid)
|
|
78
|
+
try {
|
|
79
|
+
action("persist/restore", () => store.update((state) => restore(state, data)))(), status.value = "restored";
|
|
80
|
+
} catch (error) {
|
|
81
|
+
valid = !1, cause = error;
|
|
82
|
+
}
|
|
83
|
+
valid || reject("state.persist.invalid", "", cause);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let written = select(store.getSnapshot()), pending = !1, failing = !1, disposed = !1;
|
|
88
|
+
const write = () => {
|
|
89
|
+
if (pending = !1, !storage || blocked)
|
|
90
|
+
return;
|
|
91
|
+
const selection = select(store.getSnapshot());
|
|
92
|
+
if (written !== NOTHING && equals(written, selection))
|
|
93
|
+
return;
|
|
94
|
+
let serialized;
|
|
95
|
+
try {
|
|
96
|
+
serialized = serialize({ version, data: selection });
|
|
97
|
+
} catch (error) {
|
|
98
|
+
failing || fail("state.persist.serialize", "", error), failing = !0;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
storage.setItem(key, serialized);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
failing || fail("state.persist.write", "", error), failing = !0;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
failing = !1, written = selection;
|
|
108
|
+
}, unsubscribe = store.subscribe(() => {
|
|
109
|
+
pending || disposed || (pending = !0, queueMicrotask(() => {
|
|
110
|
+
pending && !disposed && write();
|
|
111
|
+
}));
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
key,
|
|
115
|
+
status: computed(() => status.value),
|
|
116
|
+
get disposed() {
|
|
117
|
+
return disposed;
|
|
118
|
+
},
|
|
119
|
+
flush() {
|
|
120
|
+
disposed || write();
|
|
121
|
+
},
|
|
122
|
+
clear() {
|
|
123
|
+
if (!(disposed || !storage)) {
|
|
124
|
+
try {
|
|
125
|
+
storage.removeItem(key);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
fail("state.persist.write", "", error);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
blocked = !1, written = NOTHING, status.value = "empty";
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
dispose() {
|
|
134
|
+
disposed || (pending && write(), disposed = !0, unsubscribe());
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
persist
|
|
140
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { OddkinError } from "@oddkin/core/diagnostics";
|
|
2
|
+
import { batch, computed, signal, untracked } from "@oddkin/core/reactive";
|
|
3
|
+
const INTERNALS = /* @__PURE__ */ Symbol("oddkin.store.internals"), live = { stores: 0, subscriptions: 0 };
|
|
4
|
+
function liveCounts() {
|
|
5
|
+
return { ...live };
|
|
6
|
+
}
|
|
7
|
+
const running = [], noop = () => {
|
|
8
|
+
};
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
const proto = Object.getPrototypeOf(value);
|
|
11
|
+
return proto === null || Object.getPrototypeOf(proto) === null;
|
|
12
|
+
}
|
|
13
|
+
function freezeSnapshot(root) {
|
|
14
|
+
const pending = [root];
|
|
15
|
+
for (; pending.length > 0; ) {
|
|
16
|
+
const value = pending.pop();
|
|
17
|
+
if (!(typeof value != "object" || value === null || Object.isFrozen(value))) {
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
Object.freeze(value);
|
|
20
|
+
for (let i = 0; i < value.length; i++)
|
|
21
|
+
pending.push(value[i]);
|
|
22
|
+
} else if (isPlainObject(value)) {
|
|
23
|
+
Object.freeze(value);
|
|
24
|
+
for (const key of Object.keys(value)) {
|
|
25
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
26
|
+
descriptor && "value" in descriptor && pending.push(descriptor.value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
class StoreImpl {
|
|
33
|
+
name;
|
|
34
|
+
state;
|
|
35
|
+
#value;
|
|
36
|
+
#initial;
|
|
37
|
+
#freeze;
|
|
38
|
+
#subscriptions = /* @__PURE__ */ new Set();
|
|
39
|
+
#observers;
|
|
40
|
+
#updates = 0;
|
|
41
|
+
#updating = !1;
|
|
42
|
+
#disposed = !1;
|
|
43
|
+
constructor(initial, options) {
|
|
44
|
+
this.name = options?.name, this.#freeze = options?.freeze ?? !0, this.#freeze && freezeSnapshot(initial), this.#initial = initial;
|
|
45
|
+
const named = this.name === void 0 ? void 0 : { name: this.name };
|
|
46
|
+
this.#value = signal(initial, named), this.state = computed(() => this.#value.value, named), live.stores++;
|
|
47
|
+
}
|
|
48
|
+
get disposed() {
|
|
49
|
+
return this.#disposed;
|
|
50
|
+
}
|
|
51
|
+
getSnapshot = () => this.#value.peek();
|
|
52
|
+
subscribe = (listener) => {
|
|
53
|
+
if (this.#disposed)
|
|
54
|
+
return noop;
|
|
55
|
+
const off = this.#value.subscribe(listener);
|
|
56
|
+
let active = !0;
|
|
57
|
+
const release = () => {
|
|
58
|
+
active && (active = !1, this.#subscriptions.delete(release), live.subscriptions--, off());
|
|
59
|
+
};
|
|
60
|
+
return this.#subscriptions.add(release), live.subscriptions++, release;
|
|
61
|
+
};
|
|
62
|
+
update(updater) {
|
|
63
|
+
this.#check("update");
|
|
64
|
+
const previous = this.#value.peek();
|
|
65
|
+
let next;
|
|
66
|
+
this.#updating = !0;
|
|
67
|
+
try {
|
|
68
|
+
next = untracked(() => updater(previous));
|
|
69
|
+
} finally {
|
|
70
|
+
this.#updating = !1;
|
|
71
|
+
}
|
|
72
|
+
this.#commit(previous, next);
|
|
73
|
+
}
|
|
74
|
+
select(selector, options) {
|
|
75
|
+
return computed(() => selector(this.#value.value), options);
|
|
76
|
+
}
|
|
77
|
+
reset() {
|
|
78
|
+
this.#check("reset"), this.#commit(this.#value.peek(), this.#initial);
|
|
79
|
+
}
|
|
80
|
+
dispose() {
|
|
81
|
+
if (this.#disposed)
|
|
82
|
+
return;
|
|
83
|
+
this.#disposed = !0, live.stores--;
|
|
84
|
+
for (const release of [...this.#subscriptions])
|
|
85
|
+
release();
|
|
86
|
+
const observers = this.#observers;
|
|
87
|
+
if (this.#observers = void 0, observers)
|
|
88
|
+
for (const observer of observers)
|
|
89
|
+
observer.dispose();
|
|
90
|
+
}
|
|
91
|
+
get [INTERNALS]() {
|
|
92
|
+
const store = this;
|
|
93
|
+
return {
|
|
94
|
+
get subscribers() {
|
|
95
|
+
return store.#subscriptions.size;
|
|
96
|
+
},
|
|
97
|
+
get updates() {
|
|
98
|
+
return store.#updates;
|
|
99
|
+
},
|
|
100
|
+
observe(observer) {
|
|
101
|
+
return store.#disposed ? (observer.dispose(), noop) : ((store.#observers ??= /* @__PURE__ */ new Set()).add(observer), () => {
|
|
102
|
+
store.#observers?.delete(observer);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
#check(operation) {
|
|
108
|
+
if (this.#disposed)
|
|
109
|
+
throw new OddkinError("state.store.disposed", "");
|
|
110
|
+
if (this.#updating)
|
|
111
|
+
throw new OddkinError("state.update.reentrant", "");
|
|
112
|
+
}
|
|
113
|
+
#commit(previous, next) {
|
|
114
|
+
Object.is(previous, next) || (this.#freeze && freezeSnapshot(next), batch(() => {
|
|
115
|
+
this.#value.value = next, this.#updates++;
|
|
116
|
+
const observers = this.#observers;
|
|
117
|
+
if (!observers)
|
|
118
|
+
return;
|
|
119
|
+
const record = { previous, next, actions: [...running] };
|
|
120
|
+
for (const observer of [...observers])
|
|
121
|
+
observer.update(record);
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function createStore(initial, options) {
|
|
126
|
+
return new StoreImpl(initial, options);
|
|
127
|
+
}
|
|
128
|
+
function action(name, fn) {
|
|
129
|
+
return (...args) => {
|
|
130
|
+
running.push(name);
|
|
131
|
+
try {
|
|
132
|
+
return batch(() => fn(...args));
|
|
133
|
+
} finally {
|
|
134
|
+
running.pop();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function shallowEqual(a, b) {
|
|
139
|
+
if (Object.is(a, b))
|
|
140
|
+
return !0;
|
|
141
|
+
if (typeof a != "object" || typeof b != "object" || a === null || b === null)
|
|
142
|
+
return !1;
|
|
143
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
144
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length)
|
|
145
|
+
return !1;
|
|
146
|
+
for (let i = 0; i < a.length; i++)
|
|
147
|
+
if (!Object.is(a[i], b[i]))
|
|
148
|
+
return !1;
|
|
149
|
+
return !0;
|
|
150
|
+
}
|
|
151
|
+
if (!isPlainObject(a) || !isPlainObject(b))
|
|
152
|
+
return !1;
|
|
153
|
+
const keys = Object.keys(a);
|
|
154
|
+
if (keys.length !== Object.keys(b).length)
|
|
155
|
+
return !1;
|
|
156
|
+
for (const key of keys)
|
|
157
|
+
if (!Object.hasOwn(b, key) || !Object.is(a[key], b[key]))
|
|
158
|
+
return !1;
|
|
159
|
+
return !0;
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
INTERNALS,
|
|
163
|
+
action,
|
|
164
|
+
createStore,
|
|
165
|
+
liveCounts,
|
|
166
|
+
shallowEqual
|
|
167
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@oddkin/state/store` — optional shared stores on Oddkin's reactive graph. DOM-free.
|
|
3
|
+
* Contract: docs/architecture/core-contracts.md §7.
|
|
4
|
+
*/
|
|
5
|
+
export { action, createStore, shallowEqual } from './store.js';
|
|
6
|
+
export type { SelectOptions, Store, StoreOptions } from './store.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type ExternalSource, type ReactiveOptions, type Readable } from '@oddkin/core/reactive';
|
|
2
|
+
export interface StoreOptions {
|
|
3
|
+
/** Name used in diagnostics and inspection. */
|
|
4
|
+
name?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Freeze the plain objects and arrays of every snapshot, in development and production alike,
|
|
7
|
+
* so a mutation throws instead of going unnoticed. Default `true`. Class instances, maps, sets,
|
|
8
|
+
* dates, and other objects with their own prototype are never frozen.
|
|
9
|
+
*/
|
|
10
|
+
freeze?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Options for `select()`: `equals` decides whether a newly selected value is a change. */
|
|
13
|
+
export type SelectOptions<S> = ReactiveOptions<S>;
|
|
14
|
+
export interface Store<T> extends ExternalSource<T> {
|
|
15
|
+
readonly name: string | undefined;
|
|
16
|
+
/** The current snapshot as a read-only readable; `.value` in a tracked context registers a dependency. */
|
|
17
|
+
readonly state: Readable<T>;
|
|
18
|
+
/** True after `dispose()`. */
|
|
19
|
+
readonly disposed: boolean;
|
|
20
|
+
/** The current snapshot, the same value until the state changes. Bound, so it can be passed around. */
|
|
21
|
+
readonly getSnapshot: () => T;
|
|
22
|
+
/**
|
|
23
|
+
* Call `listener` with the new snapshot after each change: synchronously outside a batch, once at
|
|
24
|
+
* the outermost boundary inside one. Returns an unsubscribe function. Bound and stable.
|
|
25
|
+
*/
|
|
26
|
+
readonly subscribe: (listener: (state: T) => void) => () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Replace the snapshot with `updater(current)`. Returning the current snapshot is no change.
|
|
29
|
+
* The updater runs untracked and must not update this store itself.
|
|
30
|
+
*/
|
|
31
|
+
update(updater: (state: T) => T): void;
|
|
32
|
+
/** A memoized readable of part of the state that notifies only when the selected value changes. */
|
|
33
|
+
select<S>(selector: (state: T) => S, options?: SelectOptions<S>): Readable<S>;
|
|
34
|
+
/** Return to the initial snapshot. */
|
|
35
|
+
reset(): void;
|
|
36
|
+
/** Release every subscription. Reads keep the last snapshot; updates throw. Idempotent. */
|
|
37
|
+
dispose(): void;
|
|
38
|
+
}
|
|
39
|
+
/** @internal One committed update, as `@oddkin/state/inspect` records it. */
|
|
40
|
+
export interface UpdateRecord<T> {
|
|
41
|
+
readonly previous: T;
|
|
42
|
+
readonly next: T;
|
|
43
|
+
/** Names of the running actions, outermost first. */
|
|
44
|
+
readonly actions: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
/** @internal Receives a store's updates and its disposal (`@oddkin/state/inspect`). */
|
|
47
|
+
export interface StoreObserver<T> {
|
|
48
|
+
update(record: UpdateRecord<T>): void;
|
|
49
|
+
dispose(): void;
|
|
50
|
+
}
|
|
51
|
+
/** @internal Counters and update observation, reached through `store[INTERNALS]`. */
|
|
52
|
+
export interface StoreInternals<T> {
|
|
53
|
+
readonly subscribers: number;
|
|
54
|
+
readonly updates: number;
|
|
55
|
+
observe(observer: StoreObserver<T>): () => void;
|
|
56
|
+
}
|
|
57
|
+
/** @internal Key of a store's {@link StoreInternals}. */
|
|
58
|
+
export declare const INTERNALS: unique symbol;
|
|
59
|
+
/** @internal Live stores (created, not disposed) and live `subscribe()` listeners. */
|
|
60
|
+
export declare function liveCounts(): {
|
|
61
|
+
readonly stores: number;
|
|
62
|
+
readonly subscriptions: number;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Create a store holding `initial` as its first snapshot. Update it with `update()`, which takes the
|
|
66
|
+
* current snapshot and returns the next one; read it through `state`, `select()`, `getSnapshot()`,
|
|
67
|
+
* or `subscribe()`. No provider, registry, or reducer is needed.
|
|
68
|
+
*/
|
|
69
|
+
export declare function createStore<T>(initial: T, options?: StoreOptions): Store<T>;
|
|
70
|
+
/**
|
|
71
|
+
* Name a unit of work for inspection. `fn` runs inside `batch()`, so subscribers are notified once
|
|
72
|
+
* when it returns, and every store update it makes records the name; nested actions record the
|
|
73
|
+
* whole chain. Batching is not a transaction: if `fn` throws, updates it already made stay. Only
|
|
74
|
+
* synchronous work belongs to the action; updates after an `await` do not.
|
|
75
|
+
*/
|
|
76
|
+
export declare function action<A extends unknown[], R>(name: string, fn: (...args: A) => R): (...args: A) => R;
|
|
77
|
+
/**
|
|
78
|
+
* Equality for `select()` results built from several fields: the same value, or arrays or plain
|
|
79
|
+
* objects with the same entries compared with `Object.is`. Maps, sets, and class instances are
|
|
80
|
+
* equal only when identical.
|
|
81
|
+
*/
|
|
82
|
+
export declare function shallowEqual<V>(a: V, b: V): boolean;
|
|
83
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/store/store.ts"],"names":[],"mappings":"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"}
|