coaction 2.1.0 → 3.1.0
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 +28 -4
- package/adapter.d.ts +1 -0
- package/adapter.js +3 -0
- package/dist/adapter.d.mts +512 -0
- package/dist/adapter.d.ts +512 -0
- package/dist/adapter.js +686 -0
- package/dist/adapter.mjs +640 -0
- package/dist/index.d.mts +36 -216
- package/dist/index.d.ts +36 -216
- package/dist/index.js +1350 -1025
- package/dist/index.mjs +1353 -991
- package/dist/local.d.mts +344 -0
- package/dist/local.d.ts +344 -0
- package/dist/local.js +1421 -0
- package/dist/local.mjs +1352 -0
- package/dist/shared.d.mts +446 -0
- package/dist/shared.d.ts +446 -0
- package/dist/shared.js +2190 -0
- package/dist/shared.mjs +2114 -0
- package/local.d.ts +1 -0
- package/local.js +3 -0
- package/package.json +41 -2
- package/shared.d.ts +1 -0
- package/shared.js +3 -0
package/dist/shared.js
ADDED
|
@@ -0,0 +1,2190 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let data_transport = require("data-transport");
|
|
3
|
+
let alien_signals = require("alien-signals");
|
|
4
|
+
let mutative = require("mutative");
|
|
5
|
+
//#region packages/core/src/lifecycle.ts
|
|
6
|
+
const reportLifecycleError = (error) => {
|
|
7
|
+
if (process.env.NODE_ENV === "development") console.error(error);
|
|
8
|
+
};
|
|
9
|
+
const tryDestroyStore = (store) => {
|
|
10
|
+
try {
|
|
11
|
+
store.destroy?.();
|
|
12
|
+
} catch (error) {
|
|
13
|
+
reportLifecycleError(error);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const failStoreSetup = (store, error) => {
|
|
17
|
+
tryDestroyStore(store);
|
|
18
|
+
throw error;
|
|
19
|
+
};
|
|
20
|
+
const failTransportInitialization = (transport, error) => {
|
|
21
|
+
try {
|
|
22
|
+
transport?.dispose?.();
|
|
23
|
+
} catch (disposeError) {
|
|
24
|
+
reportLifecycleError(disposeError);
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
};
|
|
28
|
+
const storeReadyRuntimeSymbol = Symbol.for("coaction.lifecycle.ready");
|
|
29
|
+
const getStoreReadyRuntime = (store, create = false) => {
|
|
30
|
+
const target = store;
|
|
31
|
+
const existing = target[storeReadyRuntimeSymbol];
|
|
32
|
+
if (existing || !create) return existing;
|
|
33
|
+
const runtime = {
|
|
34
|
+
callbacks: /* @__PURE__ */ new Set(),
|
|
35
|
+
ready: false
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(target, storeReadyRuntimeSymbol, {
|
|
38
|
+
configurable: true,
|
|
39
|
+
enumerable: true,
|
|
40
|
+
value: runtime,
|
|
41
|
+
writable: true
|
|
42
|
+
});
|
|
43
|
+
return runtime;
|
|
44
|
+
};
|
|
45
|
+
const onStoreReady = (store, callback) => {
|
|
46
|
+
const runtime = getStoreReadyRuntime(store, true);
|
|
47
|
+
if (runtime.ready) {
|
|
48
|
+
callback();
|
|
49
|
+
return () => void 0;
|
|
50
|
+
}
|
|
51
|
+
runtime.callbacks.add(callback);
|
|
52
|
+
return () => {
|
|
53
|
+
runtime.callbacks.delete(callback);
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
const markStoreReady = (store) => {
|
|
57
|
+
const runtime = getStoreReadyRuntime(store, true);
|
|
58
|
+
if (runtime.ready) return;
|
|
59
|
+
runtime.ready = true;
|
|
60
|
+
const callbacks = [...runtime.callbacks];
|
|
61
|
+
runtime.callbacks.clear();
|
|
62
|
+
callbacks.forEach((callback) => callback());
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region packages/core/src/utils.ts
|
|
66
|
+
const isEqual = (x, y) => {
|
|
67
|
+
if (x === y) return x !== 0 || y !== 0 || 1 / x === 1 / y;
|
|
68
|
+
return x !== x && y !== y;
|
|
69
|
+
};
|
|
70
|
+
const isUnsafeKey = (key) => key === "__proto__" || key === "prototype" || key === "constructor";
|
|
71
|
+
const isUnsafePathSegment = (segment) => typeof segment === "string" && isUnsafeKey(segment);
|
|
72
|
+
var UnsafePatchPathError = class extends Error {
|
|
73
|
+
name = "UnsafePatchPathError";
|
|
74
|
+
};
|
|
75
|
+
var StateSchemaError = class extends Error {
|
|
76
|
+
name = "StateSchemaError";
|
|
77
|
+
};
|
|
78
|
+
const isStateSchemaError = (error) => error instanceof StateSchemaError || error instanceof Error && error.name === "StateSchemaError";
|
|
79
|
+
const hasUnsafePatchPath = (path) => {
|
|
80
|
+
return (Array.isArray(path) ? path : typeof path === "string" ? path.split("/").filter(Boolean).map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~")) : []).some(isUnsafePathSegment);
|
|
81
|
+
};
|
|
82
|
+
const formatPatchPath = (path) => Array.isArray(path) ? path.map((segment) => String(segment)).join(".") : String(path);
|
|
83
|
+
const getUnsafePatchPaths = (patches) => patches?.filter((patch) => hasUnsafePatchPath(patch.path)) ?? [];
|
|
84
|
+
const assertSafePatches = (patches, source = "patches") => {
|
|
85
|
+
const unsafePatches = getUnsafePatchPaths(patches);
|
|
86
|
+
if (!unsafePatches.length) return;
|
|
87
|
+
const paths = unsafePatches.map((patch) => `'${formatPatchPath(patch.path)}'`).join(", ");
|
|
88
|
+
throw new UnsafePatchPathError(`Unsafe patch path${unsafePatches.length > 1 ? "s" : ""} ${paths} cannot be applied from ${source}.`);
|
|
89
|
+
};
|
|
90
|
+
const warnDroppedUnsafePatches = (unsafePatches, source) => {
|
|
91
|
+
if (process.env.NODE_ENV !== "development" || !unsafePatches.length) return;
|
|
92
|
+
const paths = unsafePatches.map((patch) => `'${formatPatchPath(patch.path)}'`).join(", ");
|
|
93
|
+
console.warn(`Coaction dropped unsafe patch path${unsafePatches.length > 1 ? "s" : ""} ${paths} from ${source}.`);
|
|
94
|
+
};
|
|
95
|
+
const sanitizePatches = (patches, options = {}) => {
|
|
96
|
+
if (options.warnOnDropped) warnDroppedUnsafePatches(getUnsafePatchPaths(patches), options.source ?? "patches");
|
|
97
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
98
|
+
return patches?.filter((patch) => !hasUnsafePatchPath(patch.path)).map((patch) => Object.prototype.hasOwnProperty.call(patch, "value") ? {
|
|
99
|
+
...patch,
|
|
100
|
+
value: sanitizeReplacementState(patch.value, seen)
|
|
101
|
+
} : patch);
|
|
102
|
+
};
|
|
103
|
+
const sanitizeCheckedPatches = (patches, source) => {
|
|
104
|
+
assertSafePatches(patches, source);
|
|
105
|
+
return sanitizePatches(patches) ?? [];
|
|
106
|
+
};
|
|
107
|
+
const createRootReplacementPatches = (currentState, nextState) => {
|
|
108
|
+
const patches = [];
|
|
109
|
+
const inversePatches = [];
|
|
110
|
+
const nextKeys = new Set(getOwnEnumerableKeys(nextState));
|
|
111
|
+
for (const key of getOwnEnumerableKeys(currentState)) {
|
|
112
|
+
if (typeof key === "string" && isUnsafeKey(key)) continue;
|
|
113
|
+
if (nextKeys.has(key)) continue;
|
|
114
|
+
patches.push({
|
|
115
|
+
op: "remove",
|
|
116
|
+
path: [key]
|
|
117
|
+
});
|
|
118
|
+
inversePatches.push({
|
|
119
|
+
op: "add",
|
|
120
|
+
path: [key],
|
|
121
|
+
value: currentState[key]
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
for (const key of nextKeys) {
|
|
125
|
+
if (typeof key === "string" && isUnsafeKey(key)) continue;
|
|
126
|
+
if (!Object.prototype.hasOwnProperty.call(currentState, key)) {
|
|
127
|
+
patches.push({
|
|
128
|
+
op: "add",
|
|
129
|
+
path: [key],
|
|
130
|
+
value: nextState[key]
|
|
131
|
+
});
|
|
132
|
+
inversePatches.push({
|
|
133
|
+
op: "remove",
|
|
134
|
+
path: [key]
|
|
135
|
+
});
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (Object.is(currentState[key], nextState[key])) continue;
|
|
139
|
+
patches.push({
|
|
140
|
+
op: "replace",
|
|
141
|
+
path: [key],
|
|
142
|
+
value: nextState[key]
|
|
143
|
+
});
|
|
144
|
+
inversePatches.push({
|
|
145
|
+
op: "replace",
|
|
146
|
+
path: [key],
|
|
147
|
+
value: currentState[key]
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
patches,
|
|
152
|
+
inversePatches
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
const setOwnEnumerable = (target, key, value) => {
|
|
156
|
+
if (typeof key === "string" && isUnsafeKey(key)) return;
|
|
157
|
+
target[key] = value;
|
|
158
|
+
};
|
|
159
|
+
const getOwnEnumerableKeys = (source) => {
|
|
160
|
+
if (typeof source !== "object" || source === null) return [];
|
|
161
|
+
return Reflect.ownKeys(source).filter((key) => Object.prototype.propertyIsEnumerable.call(source, key));
|
|
162
|
+
};
|
|
163
|
+
const getOwnSchemaKeys = (source) => {
|
|
164
|
+
if (typeof source !== "object" || source === null) return [];
|
|
165
|
+
return Reflect.ownKeys(source).filter((key) => !(typeof key === "string" && isUnsafeKey(key)));
|
|
166
|
+
};
|
|
167
|
+
const formatSchemaPath = (path) => path.length ? path.map((key) => String(key)).join(".") : "<root>";
|
|
168
|
+
const assertKnownSchemaKey = (knownKeys, key, path) => {
|
|
169
|
+
if (typeof key === "string" && isUnsafeKey(key)) return;
|
|
170
|
+
if (knownKeys.has(key)) return;
|
|
171
|
+
throw new StateSchemaError(`Unknown state key '${formatSchemaPath([...path, key])}' cannot be added after store initialization. Coaction state schema is fixed.`);
|
|
172
|
+
};
|
|
173
|
+
const assertKnownSliceObject = (key, value) => {
|
|
174
|
+
if (typeof value === "object" && value !== null) return;
|
|
175
|
+
throw new StateSchemaError(`State slice '${String(key)}' must remain an object after store initialization. Coaction slice schema is fixed.`);
|
|
176
|
+
};
|
|
177
|
+
const assertKnownSlicePresent = (source, key) => {
|
|
178
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) return;
|
|
179
|
+
throw new StateSchemaError(`State slice '${String(key)}' cannot be removed after store initialization. Coaction slice schema is fixed.`);
|
|
180
|
+
};
|
|
181
|
+
const createStateSchema = (rootState, isSliceStore) => {
|
|
182
|
+
const rootKeys = new Set(getOwnSchemaKeys(rootState));
|
|
183
|
+
if (!isSliceStore) return { rootKeys };
|
|
184
|
+
const sliceKeys = /* @__PURE__ */ new Map();
|
|
185
|
+
if (typeof rootState === "object" && rootState !== null) {
|
|
186
|
+
const rootRecord = rootState;
|
|
187
|
+
rootKeys.forEach((key) => {
|
|
188
|
+
const slice = rootRecord[key];
|
|
189
|
+
if (typeof slice === "object" && slice !== null) sliceKeys.set(key, new Set(getOwnSchemaKeys(slice)));
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
rootKeys,
|
|
194
|
+
sliceKeys
|
|
195
|
+
};
|
|
196
|
+
};
|
|
197
|
+
const assertKnownStateShape = (source, rootState, schema, isSliceStore, options = {}) => {
|
|
198
|
+
if (typeof source !== "object" || source === null) return;
|
|
199
|
+
const rootKeys = schema?.rootKeys ?? new Set(getOwnSchemaKeys(rootState));
|
|
200
|
+
const sourceRecord = source;
|
|
201
|
+
const knownSliceEntries = schema?.sliceKeys;
|
|
202
|
+
if (isSliceStore && options.requireSliceRoots && knownSliceEntries) knownSliceEntries.forEach((_, key) => {
|
|
203
|
+
assertKnownSlicePresent(sourceRecord, key);
|
|
204
|
+
});
|
|
205
|
+
for (const key of getOwnEnumerableKeys(source)) {
|
|
206
|
+
assertKnownSchemaKey(rootKeys, key, []);
|
|
207
|
+
if (!isSliceStore) continue;
|
|
208
|
+
const slice = sourceRecord[key];
|
|
209
|
+
const knownSliceKeys = schema?.sliceKeys?.get(key) ?? (typeof rootState === "object" && rootState !== null && typeof rootState[key] === "object" && rootState[key] !== null ? new Set(getOwnSchemaKeys(rootState[key])) : void 0);
|
|
210
|
+
if (!knownSliceKeys) continue;
|
|
211
|
+
assertKnownSliceObject(key, slice);
|
|
212
|
+
for (const sliceKey of getOwnEnumerableKeys(slice)) assertKnownSchemaKey(knownSliceKeys, sliceKey, [key]);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const isArrayIndexKey = (key) => {
|
|
216
|
+
if (typeof key !== "string") return false;
|
|
217
|
+
const index = Number(key);
|
|
218
|
+
return Number.isInteger(index) && index >= 0 && index < 2 ** 32 - 1 && String(index) === key;
|
|
219
|
+
};
|
|
220
|
+
const assignOwnEnumerable = (target, source, seen = /* @__PURE__ */ new WeakMap()) => {
|
|
221
|
+
if (!seen.has(source)) seen.set(source, target);
|
|
222
|
+
for (const key of getOwnEnumerableKeys(source)) setOwnEnumerable(target, key, sanitizeReplacementState(source[key], seen));
|
|
223
|
+
};
|
|
224
|
+
const cloneOwnEnumerable = (source) => {
|
|
225
|
+
const target = {};
|
|
226
|
+
assignOwnEnumerable(target, source);
|
|
227
|
+
return target;
|
|
228
|
+
};
|
|
229
|
+
const sanitizeReplacementState = (source, seen = /* @__PURE__ */ new WeakMap()) => {
|
|
230
|
+
if (typeof source !== "object" || source === null) return source;
|
|
231
|
+
const cached = seen.get(source);
|
|
232
|
+
if (cached) return cached;
|
|
233
|
+
if (Array.isArray(source)) {
|
|
234
|
+
const target = [];
|
|
235
|
+
target.length = source.length;
|
|
236
|
+
seen.set(source, target);
|
|
237
|
+
for (let index = 0; index < source.length; index += 1) if (Object.prototype.hasOwnProperty.call(source, index)) target[index] = sanitizeReplacementState(source[index], seen);
|
|
238
|
+
for (const key of getOwnEnumerableKeys(source)) {
|
|
239
|
+
if (isArrayIndexKey(key) || typeof key === "string" && isUnsafeKey(key)) continue;
|
|
240
|
+
const value = source[key];
|
|
241
|
+
if (typeof value === "function") continue;
|
|
242
|
+
setOwnEnumerable(target, key, sanitizeReplacementState(value, seen));
|
|
243
|
+
}
|
|
244
|
+
return target;
|
|
245
|
+
}
|
|
246
|
+
const prototype = Object.getPrototypeOf(source);
|
|
247
|
+
if (prototype !== Object.prototype && prototype !== null) return source;
|
|
248
|
+
const target = Object.create(prototype);
|
|
249
|
+
seen.set(source, target);
|
|
250
|
+
for (const key of getOwnEnumerableKeys(source)) {
|
|
251
|
+
if (typeof key === "string" && isUnsafeKey(key)) continue;
|
|
252
|
+
const value = source[key];
|
|
253
|
+
if (typeof value === "function") continue;
|
|
254
|
+
setOwnEnumerable(target, key, sanitizeReplacementState(value, seen));
|
|
255
|
+
}
|
|
256
|
+
return target;
|
|
257
|
+
};
|
|
258
|
+
const sanitizeInitialStateValue = (source, seen = /* @__PURE__ */ new WeakMap()) => {
|
|
259
|
+
if (typeof source !== "object" || source === null) return source;
|
|
260
|
+
const cached = seen.get(source);
|
|
261
|
+
if (cached) return cached;
|
|
262
|
+
if (Array.isArray(source)) {
|
|
263
|
+
const target = [];
|
|
264
|
+
target.length = source.length;
|
|
265
|
+
seen.set(source, target);
|
|
266
|
+
for (let index = 0; index < source.length; index += 1) if (Object.prototype.hasOwnProperty.call(source, index)) target[index] = sanitizeInitialStateValue(source[index], seen);
|
|
267
|
+
for (const key of getOwnEnumerableKeys(source)) {
|
|
268
|
+
if (isArrayIndexKey(key) || typeof key === "string" && isUnsafeKey(key)) continue;
|
|
269
|
+
setOwnEnumerable(target, key, sanitizeInitialStateValue(source[key], seen));
|
|
270
|
+
}
|
|
271
|
+
return target;
|
|
272
|
+
}
|
|
273
|
+
const prototype = Object.getPrototypeOf(source);
|
|
274
|
+
if (prototype !== Object.prototype && prototype !== null) return source;
|
|
275
|
+
const target = Object.create(prototype);
|
|
276
|
+
seen.set(source, target);
|
|
277
|
+
for (const key of getOwnEnumerableKeys(source)) {
|
|
278
|
+
if (typeof key === "string" && isUnsafeKey(key)) continue;
|
|
279
|
+
setOwnEnumerable(target, key, sanitizeInitialStateValue(source[key], seen));
|
|
280
|
+
}
|
|
281
|
+
return target;
|
|
282
|
+
};
|
|
283
|
+
const areShallowEqualWithArray = (prev, next) => {
|
|
284
|
+
if (prev === null || next === null || prev.length !== next.length) return false;
|
|
285
|
+
const { length } = prev;
|
|
286
|
+
for (let i = 0; i < length; i += 1) {
|
|
287
|
+
if (Object.prototype.hasOwnProperty.call(prev, i) !== Object.prototype.hasOwnProperty.call(next, i)) return false;
|
|
288
|
+
if (!isEqual(prev[i], next[i])) return false;
|
|
289
|
+
}
|
|
290
|
+
return true;
|
|
291
|
+
};
|
|
292
|
+
const mergeObject = (target, source, isSlice) => {
|
|
293
|
+
if (isSlice) {
|
|
294
|
+
if (typeof source === "object" && source !== null) for (const key of getOwnEnumerableKeys(source)) {
|
|
295
|
+
if (typeof key === "string" && isUnsafeKey(key)) continue;
|
|
296
|
+
if (!Object.prototype.hasOwnProperty.call(target, key)) continue;
|
|
297
|
+
const sourceValue = source[key];
|
|
298
|
+
if (typeof sourceValue !== "object" || sourceValue === null) continue;
|
|
299
|
+
const targetValue = target[key];
|
|
300
|
+
if (typeof targetValue === "object" && targetValue !== null) assignOwnEnumerable(targetValue, sourceValue);
|
|
301
|
+
}
|
|
302
|
+
} else if (typeof source === "object" && source !== null) assignOwnEnumerable(target, source);
|
|
303
|
+
};
|
|
304
|
+
const uuid = () => {
|
|
305
|
+
let timestamp = (/* @__PURE__ */ new Date()).getTime();
|
|
306
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
|
|
307
|
+
const randomNum = (timestamp + Math.random() * 16) % 16 | 0;
|
|
308
|
+
timestamp = Math.floor(timestamp / 16);
|
|
309
|
+
return (char === "x" ? randomNum : randomNum & 3 | 8).toString(16);
|
|
310
|
+
});
|
|
311
|
+
};
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region packages/core/src/computed.ts
|
|
314
|
+
const isObjectLike = (value) => typeof value === "object" && value !== null;
|
|
315
|
+
const runComputedRead = (internal, read) => {
|
|
316
|
+
internal.computedReadDepth = (internal.computedReadDepth ?? 0) + 1;
|
|
317
|
+
try {
|
|
318
|
+
return read();
|
|
319
|
+
} finally {
|
|
320
|
+
internal.computedReadDepth -= 1;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
var Computed = class {
|
|
324
|
+
deps;
|
|
325
|
+
fn;
|
|
326
|
+
constructor(deps, fn) {
|
|
327
|
+
this.deps = deps;
|
|
328
|
+
this.fn = fn;
|
|
329
|
+
}
|
|
330
|
+
createGetter({ internal }) {
|
|
331
|
+
const memoByReceiver = /* @__PURE__ */ new WeakMap();
|
|
332
|
+
const lastArgs = /* @__PURE__ */ new WeakMap();
|
|
333
|
+
const lastResult = /* @__PURE__ */ new WeakMap();
|
|
334
|
+
const fallbackReceiver = {};
|
|
335
|
+
const evaluate = (receiver) => {
|
|
336
|
+
const args = this.deps(internal.module);
|
|
337
|
+
if (!lastArgs.has(receiver) || !areShallowEqualWithArray(lastArgs.get(receiver), args)) lastResult.set(receiver, this.fn.apply(receiver, args));
|
|
338
|
+
lastArgs.set(receiver, args);
|
|
339
|
+
return lastResult.get(receiver);
|
|
340
|
+
};
|
|
341
|
+
return function() {
|
|
342
|
+
const receiver = typeof this === "object" && this !== null ? this : fallbackReceiver;
|
|
343
|
+
if (internal.isBatching) return evaluate(receiver);
|
|
344
|
+
let accessor = memoByReceiver.get(receiver);
|
|
345
|
+
if (!accessor) {
|
|
346
|
+
accessor = (0, alien_signals.computed)(() => runComputedRead(internal, () => evaluate(receiver)));
|
|
347
|
+
memoByReceiver.set(receiver, accessor);
|
|
348
|
+
}
|
|
349
|
+
return accessor();
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
const createCachedGetter = (internal, getter) => {
|
|
354
|
+
const accessors = /* @__PURE__ */ new WeakMap();
|
|
355
|
+
const fallbackReceiver = {};
|
|
356
|
+
return function() {
|
|
357
|
+
const receiver = typeof this === "object" && this !== null ? this : fallbackReceiver;
|
|
358
|
+
if (internal.isBatching) return getter.call(receiver);
|
|
359
|
+
let accessor = accessors.get(receiver);
|
|
360
|
+
if (!accessor) {
|
|
361
|
+
accessor = (0, alien_signals.computed)(() => runComputedRead(internal, () => getter.call(receiver)));
|
|
362
|
+
accessors.set(receiver, accessor);
|
|
363
|
+
}
|
|
364
|
+
return accessor();
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
const createTrackedStateReader = (internal, read, initialValue) => {
|
|
368
|
+
const slotSignal = (0, alien_signals.signal)(initialValue);
|
|
369
|
+
const slotVersionSignal = (0, alien_signals.signal)(0);
|
|
370
|
+
let slotVersion = 0;
|
|
371
|
+
(internal.signalSlots ??= /* @__PURE__ */ new Set()).add({ refresh: () => {
|
|
372
|
+
const nextValue = read();
|
|
373
|
+
slotSignal(nextValue);
|
|
374
|
+
if (internal.mutableInstance && isObjectLike(nextValue)) {
|
|
375
|
+
slotVersion += 1;
|
|
376
|
+
slotVersionSignal(slotVersion);
|
|
377
|
+
}
|
|
378
|
+
} });
|
|
379
|
+
return () => {
|
|
380
|
+
const currentValue = slotSignal();
|
|
381
|
+
if (internal.mutableInstance && isObjectLike(currentValue)) slotVersionSignal();
|
|
382
|
+
return read();
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
const refreshSignalSlots = (internal) => {
|
|
386
|
+
if (!internal.signalSlots?.size) return;
|
|
387
|
+
(0, alien_signals.startBatch)();
|
|
388
|
+
try {
|
|
389
|
+
internal.signalSlots.forEach((slot) => slot.refresh());
|
|
390
|
+
} finally {
|
|
391
|
+
(0, alien_signals.endBatch)();
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region packages/core/src/sharedState.ts
|
|
396
|
+
const formatPath = (path) => path.length ? path.map((key) => String(key)).join(".") : "<root>";
|
|
397
|
+
const unsupported = (label, path) => {
|
|
398
|
+
throw new TypeError(`${label} is not supported in shared store mode because transport synchronization uses JSON. Found unsupported value at ${formatPath(path)}.`);
|
|
399
|
+
};
|
|
400
|
+
const getDescriptors = (value, path) => {
|
|
401
|
+
try {
|
|
402
|
+
return Object.getOwnPropertyDescriptors(value);
|
|
403
|
+
} catch {
|
|
404
|
+
return unsupported("Uninspectable state", path);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
const getPrototype = (value, path) => {
|
|
408
|
+
try {
|
|
409
|
+
return Object.getPrototypeOf(value);
|
|
410
|
+
} catch {
|
|
411
|
+
return unsupported("Uninspectable state prototype", path);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
const assertNoInheritedToJson = (prototype, path) => {
|
|
415
|
+
let current = prototype;
|
|
416
|
+
while (current) {
|
|
417
|
+
let descriptor;
|
|
418
|
+
try {
|
|
419
|
+
descriptor = Object.getOwnPropertyDescriptor(current, "toJSON");
|
|
420
|
+
} catch {
|
|
421
|
+
unsupported("Uninspectable inherited toJSON state", path);
|
|
422
|
+
}
|
|
423
|
+
if (descriptor) {
|
|
424
|
+
if (!Object.prototype.hasOwnProperty.call(descriptor, "value") || typeof descriptor.value === "function") unsupported("Inherited toJSON state", path);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
current = getPrototype(current, path);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
const isArrayIndex = (key, length) => {
|
|
431
|
+
const index = Number(key);
|
|
432
|
+
return key !== "" && Number.isSafeInteger(index) && index >= 0 && index < length && String(index) === key;
|
|
433
|
+
};
|
|
434
|
+
const pushDataProperty = (work, descriptor, key, path, actionRoot = false) => {
|
|
435
|
+
const nextPath = [...path, key];
|
|
436
|
+
if (!descriptor) return unsupported("Sparse array state", nextPath);
|
|
437
|
+
if (!descriptor.enumerable) return unsupported("Non-enumerable data state", nextPath);
|
|
438
|
+
if (!Object.prototype.hasOwnProperty.call(descriptor, "value")) return unsupported("Accessor-backed state", nextPath);
|
|
439
|
+
work.push({
|
|
440
|
+
actionRoot,
|
|
441
|
+
path: nextPath,
|
|
442
|
+
value: descriptor.value
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
const assertSharedJsonWork = (work, isSliceStore = false) => {
|
|
446
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
447
|
+
while (work.length) {
|
|
448
|
+
const { actionRoot = false, path, value } = work.pop();
|
|
449
|
+
if (value === null) continue;
|
|
450
|
+
switch (typeof value) {
|
|
451
|
+
case "string":
|
|
452
|
+
case "boolean": continue;
|
|
453
|
+
case "number":
|
|
454
|
+
if (!Number.isFinite(value)) unsupported("NaN or infinite number state", path);
|
|
455
|
+
if (Object.is(value, -0)) unsupported("Negative zero state", path);
|
|
456
|
+
continue;
|
|
457
|
+
case "bigint": unsupported("BigInt-valued state", path);
|
|
458
|
+
case "undefined": unsupported("Undefined-valued state", path);
|
|
459
|
+
case "function": unsupported("Function-valued state", path);
|
|
460
|
+
case "symbol": throw new TypeError(`Symbol-valued state is not supported in shared store mode because transport synchronization uses JSON. Found symbol value at ${formatPath(path)}.`);
|
|
461
|
+
default: break;
|
|
462
|
+
}
|
|
463
|
+
const object = value;
|
|
464
|
+
if (seen.has(object)) unsupported("Repeated state reference", path);
|
|
465
|
+
seen.add(object);
|
|
466
|
+
const descriptors = getDescriptors(object, path);
|
|
467
|
+
if (Array.isArray(object)) {
|
|
468
|
+
const prototype = getPrototype(object, path);
|
|
469
|
+
if (prototype !== Array.prototype) unsupported("Non-plain array state", path);
|
|
470
|
+
assertNoInheritedToJson(prototype, path);
|
|
471
|
+
const length = descriptors.length?.value;
|
|
472
|
+
if (!Number.isSafeInteger(length) || length < 0) unsupported("Invalid array state", path);
|
|
473
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
474
|
+
if (key === "length") continue;
|
|
475
|
+
if (typeof key === "symbol") throw new TypeError(`Symbol-keyed state is not supported in shared store mode because transport synchronization uses JSON and string action paths. Found symbol key at ${formatPath([...path, key])}.`);
|
|
476
|
+
if (!isArrayIndex(key, length)) unsupported("Non-index array property state", [...path, key]);
|
|
477
|
+
}
|
|
478
|
+
for (let index = 0; index < length; index += 1) pushDataProperty(work, descriptors[index], index, path);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const prototype = getPrototype(object, path);
|
|
482
|
+
if (prototype !== Object.prototype && prototype !== null) unsupported("Non-plain object state", path);
|
|
483
|
+
assertNoInheritedToJson(prototype, path);
|
|
484
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
485
|
+
if (typeof key === "symbol") throw new TypeError(`Symbol-keyed state is not supported in shared store mode because transport synchronization uses JSON and string action paths. Found symbol key at ${formatPath([...path, key])}.`);
|
|
486
|
+
if (isUnsafeKey(key)) unsupported("Unsafe-keyed state", [...path, key]);
|
|
487
|
+
const descriptor = descriptors[key];
|
|
488
|
+
if (actionRoot && descriptor) {
|
|
489
|
+
const isDataProperty = Object.prototype.hasOwnProperty.call(descriptor, "value");
|
|
490
|
+
if (isDataProperty && typeof descriptor.value === "function") continue;
|
|
491
|
+
if (actionRoot === "initial" && (!isDataProperty || descriptor.value instanceof Computed)) continue;
|
|
492
|
+
}
|
|
493
|
+
pushDataProperty(work, descriptor, key, path, isSliceStore && path.length === 0 ? "initial" : false);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const assertSharedJsonValue = (root) => {
|
|
498
|
+
assertSharedJsonWork([{
|
|
499
|
+
path: [],
|
|
500
|
+
value: root
|
|
501
|
+
}]);
|
|
502
|
+
};
|
|
503
|
+
const validateSharedInitialState = (root, isSliceStore = false) => {
|
|
504
|
+
assertSharedJsonWork([{
|
|
505
|
+
actionRoot: isSliceStore ? false : "initial",
|
|
506
|
+
path: [],
|
|
507
|
+
value: root
|
|
508
|
+
}], isSliceStore);
|
|
509
|
+
};
|
|
510
|
+
const validateSharedReplacementSource = (root) => {
|
|
511
|
+
if (typeof root !== "object" || root === null || Array.isArray(root)) unsupported("Non-record replacement state", []);
|
|
512
|
+
assertSharedJsonWork([{
|
|
513
|
+
actionRoot: "replacement",
|
|
514
|
+
path: [],
|
|
515
|
+
value: root
|
|
516
|
+
}]);
|
|
517
|
+
};
|
|
518
|
+
const encodeSharedJson = (value) => {
|
|
519
|
+
assertSharedJsonValue(value);
|
|
520
|
+
const encoded = JSON.stringify(value);
|
|
521
|
+
if (typeof encoded !== "string") throw new TypeError("Shared transport value could not be encoded as JSON.");
|
|
522
|
+
return encoded;
|
|
523
|
+
};
|
|
524
|
+
const decodeSharedJson = (encoded) => {
|
|
525
|
+
if (typeof encoded !== "string") throw new TypeError("Shared transport payload must be a JSON string.");
|
|
526
|
+
let value;
|
|
527
|
+
try {
|
|
528
|
+
value = JSON.parse(encoded);
|
|
529
|
+
} catch {
|
|
530
|
+
throw new TypeError("Shared transport payload is not valid JSON.");
|
|
531
|
+
}
|
|
532
|
+
assertSharedJsonValue(value);
|
|
533
|
+
return value;
|
|
534
|
+
};
|
|
535
|
+
const validateSharedActionPaths = (state, isSliceStore = false) => {
|
|
536
|
+
const actions = /* @__PURE__ */ new Set();
|
|
537
|
+
const work = [{
|
|
538
|
+
actionRoot: !isSliceStore,
|
|
539
|
+
path: [],
|
|
540
|
+
value: state
|
|
541
|
+
}];
|
|
542
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
543
|
+
while (work.length) {
|
|
544
|
+
const { actionRoot, path, value } = work.pop();
|
|
545
|
+
if (typeof value !== "object" || value === null || seen.has(value)) continue;
|
|
546
|
+
seen.add(value);
|
|
547
|
+
const descriptors = getDescriptors(value, path);
|
|
548
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
549
|
+
if (typeof key === "symbol") throw new TypeError(`Symbol-keyed state is not supported in shared store mode because transport synchronization uses JSON and string action paths. Found symbol key at ${formatPath([...path, key])}.`);
|
|
550
|
+
const descriptor = descriptors[key];
|
|
551
|
+
if (descriptor && Object.prototype.hasOwnProperty.call(descriptor, "value")) {
|
|
552
|
+
const nextPath = [...path, key];
|
|
553
|
+
if (actionRoot && typeof descriptor.value === "function") {
|
|
554
|
+
actions.add(JSON.stringify(nextPath));
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
work.push({
|
|
558
|
+
actionRoot: isSliceStore && path.length === 0,
|
|
559
|
+
path: nextPath,
|
|
560
|
+
value: descriptor.value
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return actions;
|
|
566
|
+
};
|
|
567
|
+
const validateSharedStateSerializable = assertSharedJsonValue;
|
|
568
|
+
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
569
|
+
const asRecord = (value, message) => {
|
|
570
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError(message);
|
|
571
|
+
return value;
|
|
572
|
+
};
|
|
573
|
+
const decodeMessage = (encoded, type) => {
|
|
574
|
+
const message = asRecord(decodeSharedJson(encoded), "Invalid transport message");
|
|
575
|
+
if (message.v !== 1 || message.type !== type) throw new TypeError("Invalid transport message");
|
|
576
|
+
return message;
|
|
577
|
+
};
|
|
578
|
+
const readEpoch = (message) => {
|
|
579
|
+
if (typeof message.epoch !== "string" || message.epoch.length === 0) throw new TypeError("Invalid transport epoch");
|
|
580
|
+
return message.epoch;
|
|
581
|
+
};
|
|
582
|
+
const readSequence = (message) => {
|
|
583
|
+
if (typeof message.sequence !== "number" || !Number.isSafeInteger(message.sequence) || message.sequence < 0) throw new TypeError("Invalid transport sequence");
|
|
584
|
+
return message.sequence;
|
|
585
|
+
};
|
|
586
|
+
const readAction = (value) => {
|
|
587
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((key) => typeof key !== "string" || isUnsafePathSegment(key))) throw new TypeError("Invalid transport action");
|
|
588
|
+
return [...value];
|
|
589
|
+
};
|
|
590
|
+
const readPath = (value, { allowUnsafe = false } = {}) => {
|
|
591
|
+
if (!Array.isArray(value) || value.length === 0) throw new TypeError("Invalid transport patch path");
|
|
592
|
+
const path = [];
|
|
593
|
+
for (const segment of value) {
|
|
594
|
+
if (typeof segment !== "string" && (typeof segment !== "number" || !Number.isSafeInteger(segment) || segment < 0) || !allowUnsafe && isUnsafePathSegment(segment)) throw new TypeError("Invalid transport patch path");
|
|
595
|
+
path.push(segment);
|
|
596
|
+
}
|
|
597
|
+
return path;
|
|
598
|
+
};
|
|
599
|
+
const readPatches = (value, options = {}) => {
|
|
600
|
+
if (!Array.isArray(value)) throw new TypeError("Invalid transport patches");
|
|
601
|
+
return value.map((candidate) => {
|
|
602
|
+
const patch = asRecord(candidate, "Invalid transport patch");
|
|
603
|
+
const path = readPath(patch.path, { allowUnsafe: options.allowUnsafePaths });
|
|
604
|
+
if (patch.op === "remove") {
|
|
605
|
+
if (hasOwn(patch, "value")) throw new TypeError("Invalid remove patch");
|
|
606
|
+
return {
|
|
607
|
+
op: "remove",
|
|
608
|
+
path
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
if (patch.op !== "add" && patch.op !== "replace" || !hasOwn(patch, "value")) throw new TypeError("Invalid transport patch");
|
|
612
|
+
return {
|
|
613
|
+
op: patch.op,
|
|
614
|
+
path,
|
|
615
|
+
value: patch.value
|
|
616
|
+
};
|
|
617
|
+
});
|
|
618
|
+
};
|
|
619
|
+
const validateUpdatePatches = (patches) => {
|
|
620
|
+
assertSharedJsonValue(patches);
|
|
621
|
+
readPatches(patches, { allowUnsafePaths: true });
|
|
622
|
+
};
|
|
623
|
+
const encodeExecuteRequest = (action, args) => encodeSharedJson({
|
|
624
|
+
action: readAction([...action]),
|
|
625
|
+
args,
|
|
626
|
+
type: "execute",
|
|
627
|
+
v: 1
|
|
628
|
+
});
|
|
629
|
+
const decodeExecuteRequest = (encoded) => {
|
|
630
|
+
const message = decodeMessage(encoded, "execute");
|
|
631
|
+
if (!Array.isArray(message.args)) throw new TypeError("Invalid transport arguments");
|
|
632
|
+
return {
|
|
633
|
+
action: readAction(message.action),
|
|
634
|
+
args: [...message.args]
|
|
635
|
+
};
|
|
636
|
+
};
|
|
637
|
+
const encodeExecuteResponse = (response) => encodeSharedJson({
|
|
638
|
+
...response,
|
|
639
|
+
type: "execute-result",
|
|
640
|
+
v: 1
|
|
641
|
+
});
|
|
642
|
+
const decodeExecuteResponse = (encoded) => {
|
|
643
|
+
const message = decodeMessage(encoded, "execute-result");
|
|
644
|
+
const base = {
|
|
645
|
+
epoch: readEpoch(message),
|
|
646
|
+
sequence: readSequence(message)
|
|
647
|
+
};
|
|
648
|
+
if (message.ok === true) return hasOwn(message, "value") ? {
|
|
649
|
+
...base,
|
|
650
|
+
ok: true,
|
|
651
|
+
value: message.value
|
|
652
|
+
} : {
|
|
653
|
+
...base,
|
|
654
|
+
ok: true
|
|
655
|
+
};
|
|
656
|
+
if (message.ok !== false || typeof message.error !== "string" || message.error.length === 0) throw new TypeError("Invalid execute response");
|
|
657
|
+
return {
|
|
658
|
+
...base,
|
|
659
|
+
error: message.error,
|
|
660
|
+
ok: false
|
|
661
|
+
};
|
|
662
|
+
};
|
|
663
|
+
const encodeFullSyncRequest = () => encodeSharedJson({
|
|
664
|
+
type: "full-sync",
|
|
665
|
+
v: 1
|
|
666
|
+
});
|
|
667
|
+
const decodeFullSyncRequest = (encoded) => {
|
|
668
|
+
decodeMessage(encoded, "full-sync");
|
|
669
|
+
};
|
|
670
|
+
const encodeFullSyncResponse = (response) => encodeSharedJson({
|
|
671
|
+
...response,
|
|
672
|
+
type: "full-sync-result",
|
|
673
|
+
v: 1
|
|
674
|
+
});
|
|
675
|
+
const decodeFullSyncResponse = (encoded) => {
|
|
676
|
+
const message = decodeMessage(encoded, "full-sync-result");
|
|
677
|
+
return {
|
|
678
|
+
epoch: readEpoch(message),
|
|
679
|
+
sequence: readSequence(message),
|
|
680
|
+
state: asRecord(message.state, "Invalid fullSync state")
|
|
681
|
+
};
|
|
682
|
+
};
|
|
683
|
+
const encodeUpdateMessage = (epoch, sequence, patches) => {
|
|
684
|
+
return encodeSharedJson({
|
|
685
|
+
epoch,
|
|
686
|
+
patches: readPatches(patches.map((patch) => patch.op === "remove" ? {
|
|
687
|
+
op: patch.op,
|
|
688
|
+
path: patch.path
|
|
689
|
+
} : {
|
|
690
|
+
op: patch.op,
|
|
691
|
+
path: patch.path,
|
|
692
|
+
value: patch.value
|
|
693
|
+
})),
|
|
694
|
+
sequence,
|
|
695
|
+
type: "update",
|
|
696
|
+
v: 1
|
|
697
|
+
});
|
|
698
|
+
};
|
|
699
|
+
const decodeUpdateMessage = (encoded) => {
|
|
700
|
+
const message = decodeMessage(encoded, "update");
|
|
701
|
+
return {
|
|
702
|
+
epoch: readEpoch(message),
|
|
703
|
+
patches: readPatches(message.patches),
|
|
704
|
+
sequence: readSequence(message)
|
|
705
|
+
};
|
|
706
|
+
};
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region packages/core/src/wrapStore.ts
|
|
709
|
+
/**
|
|
710
|
+
* Convert a store object into Coaction's callable store shape.
|
|
711
|
+
*
|
|
712
|
+
* @remarks
|
|
713
|
+
* Framework bindings use this to attach selector-aware readers while
|
|
714
|
+
* preserving the underlying store API on the returned function object. Most
|
|
715
|
+
* applications should use a public `create` entry instead of calling
|
|
716
|
+
* `wrapStore()` directly. Framework authors import this helper from
|
|
717
|
+
* `coaction/local` or `coaction/adapter`.
|
|
718
|
+
*/
|
|
719
|
+
const wrapStore = (store, getState = () => store.getState()) => {
|
|
720
|
+
const { name, ..._store } = store;
|
|
721
|
+
return Object.assign({ [name]: (...args) => getState(...args) }[name], _store);
|
|
722
|
+
};
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region packages/core/src/asyncClientStore.ts
|
|
725
|
+
const clientApplyErrorMessage = "apply() cannot be called in the client store. Client stores are mirrors; use a store method to update the main store instead.";
|
|
726
|
+
const createAsyncClientStore = (createStore, options) => {
|
|
727
|
+
let createdStore;
|
|
728
|
+
try {
|
|
729
|
+
createdStore = createStore({ share: "client" });
|
|
730
|
+
} catch (error) {
|
|
731
|
+
return failTransportInitialization(options.clientTransport, error);
|
|
732
|
+
}
|
|
733
|
+
const { store, internal } = createdStore;
|
|
734
|
+
let canApplyClientState = false;
|
|
735
|
+
const previousAssertMutationAllowed = internal.assertMutationAllowed;
|
|
736
|
+
internal.assertMutationAllowed = (operation) => {
|
|
737
|
+
if (operation === "apply") {
|
|
738
|
+
if (!canApplyClientState) throw new Error(clientApplyErrorMessage);
|
|
739
|
+
canApplyClientState = false;
|
|
740
|
+
}
|
|
741
|
+
previousAssertMutationAllowed?.(operation);
|
|
742
|
+
};
|
|
743
|
+
const baseApply = store.apply.bind(store);
|
|
744
|
+
store.apply = () => {
|
|
745
|
+
throw new Error(clientApplyErrorMessage);
|
|
746
|
+
};
|
|
747
|
+
internal.applyClientState = (...args) => {
|
|
748
|
+
canApplyClientState = true;
|
|
749
|
+
try {
|
|
750
|
+
baseApply(...args);
|
|
751
|
+
} finally {
|
|
752
|
+
canApplyClientState = false;
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
const isSharedWorker = typeof SharedWorker !== "undefined" && options.worker instanceof SharedWorker;
|
|
756
|
+
let transport;
|
|
757
|
+
try {
|
|
758
|
+
transport = options.worker ? (0, data_transport.createTransport)(isSharedWorker ? "SharedWorkerClient" : "WebWorkerClient", {
|
|
759
|
+
worker: options.worker,
|
|
760
|
+
prefix: store.name
|
|
761
|
+
}) : options.clientTransport;
|
|
762
|
+
} catch (error) {
|
|
763
|
+
return failStoreSetup(store, error);
|
|
764
|
+
}
|
|
765
|
+
if (!transport) return failStoreSetup(store, /* @__PURE__ */ new Error("transport is required"));
|
|
766
|
+
try {
|
|
767
|
+
store.transport = transport;
|
|
768
|
+
if (typeof transport.onConnect !== "function") throw new Error("transport.onConnect is required");
|
|
769
|
+
} catch (error) {
|
|
770
|
+
return failStoreSetup(store, error);
|
|
771
|
+
}
|
|
772
|
+
const destroyedMarker = Symbol("destroyed client transport");
|
|
773
|
+
let resolveDestroyed;
|
|
774
|
+
const destroyedSignal = new Promise((resolve) => {
|
|
775
|
+
resolveDestroyed = () => resolve(destroyedMarker);
|
|
776
|
+
});
|
|
777
|
+
const disposers = /* @__PURE__ */ new Set();
|
|
778
|
+
let destroyed = false;
|
|
779
|
+
let connectGeneration = 0;
|
|
780
|
+
let connectSync = null;
|
|
781
|
+
let syncTail = Promise.resolve();
|
|
782
|
+
const registerDisposer = (value) => {
|
|
783
|
+
if (typeof value === "function") disposers.add(value);
|
|
784
|
+
};
|
|
785
|
+
const cleanup = () => {
|
|
786
|
+
if (destroyed) return;
|
|
787
|
+
destroyed = true;
|
|
788
|
+
connectGeneration += 1;
|
|
789
|
+
resolveDestroyed();
|
|
790
|
+
const callbacks = [...disposers];
|
|
791
|
+
disposers.clear();
|
|
792
|
+
for (const dispose of callbacks) try {
|
|
793
|
+
dispose();
|
|
794
|
+
} catch (error) {
|
|
795
|
+
reportLifecycleError(error);
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const awaitActive = async (value) => {
|
|
799
|
+
const result = await Promise.race([Promise.resolve(value), destroyedSignal]);
|
|
800
|
+
if (result === destroyedMarker) throw new Error("Client transport was destroyed");
|
|
801
|
+
return result;
|
|
802
|
+
};
|
|
803
|
+
internal.awaitClientTransport = awaitActive;
|
|
804
|
+
const applyFullSync = (state, epoch, sequence) => {
|
|
805
|
+
const previousEpoch = internal.transportEpoch;
|
|
806
|
+
const previousSequence = internal.sequence;
|
|
807
|
+
internal.transportEpoch = epoch;
|
|
808
|
+
internal.sequence = sequence;
|
|
809
|
+
try {
|
|
810
|
+
internal.applyClientState(state);
|
|
811
|
+
} catch (error) {
|
|
812
|
+
internal.transportEpoch = previousEpoch;
|
|
813
|
+
internal.sequence = previousSequence;
|
|
814
|
+
throw error;
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
const fullSync = (expectedEpoch, minimumSequence = 0, generation = connectGeneration) => {
|
|
818
|
+
const execute = async () => {
|
|
819
|
+
if (destroyed || generation !== connectGeneration) return;
|
|
820
|
+
const encoded = await awaitActive(transport.emit("fullSync", encodeFullSyncRequest()));
|
|
821
|
+
if (destroyed || generation !== connectGeneration) return;
|
|
822
|
+
const snapshot = decodeFullSyncResponse(encoded);
|
|
823
|
+
if (expectedEpoch && snapshot.epoch !== expectedEpoch) throw new Error("Mismatched fullSync epoch");
|
|
824
|
+
if (snapshot.sequence < minimumSequence) throw new Error("Stale fullSync sequence");
|
|
825
|
+
if (snapshot.epoch === internal.transportEpoch && snapshot.sequence < internal.sequence) return;
|
|
826
|
+
applyFullSync(snapshot.state, snapshot.epoch, snapshot.sequence);
|
|
827
|
+
};
|
|
828
|
+
const run = syncTail.then(execute, execute);
|
|
829
|
+
syncTail = run.then(() => void 0, () => void 0);
|
|
830
|
+
return run;
|
|
831
|
+
};
|
|
832
|
+
internal.syncClientState = (expectedEpoch, minimumSequence) => fullSync(expectedEpoch, minimumSequence);
|
|
833
|
+
const applyUpdate = (update) => {
|
|
834
|
+
const previousEpoch = internal.transportEpoch;
|
|
835
|
+
const previousSequence = internal.sequence;
|
|
836
|
+
internal.transportEpoch = update.epoch;
|
|
837
|
+
internal.sequence = update.sequence;
|
|
838
|
+
try {
|
|
839
|
+
internal.applyClientState(void 0, update.patches);
|
|
840
|
+
} catch (error) {
|
|
841
|
+
internal.transportEpoch = previousEpoch;
|
|
842
|
+
internal.sequence = previousSequence;
|
|
843
|
+
throw error;
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
const handleUpdate = async (encoded) => {
|
|
847
|
+
if (destroyed) return;
|
|
848
|
+
const generation = connectGeneration;
|
|
849
|
+
const update = decodeUpdateMessage(encoded);
|
|
850
|
+
if (connectSync) await connectSync;
|
|
851
|
+
if (destroyed || generation !== connectGeneration) return;
|
|
852
|
+
if (update.epoch !== internal.transportEpoch) await fullSync(update.epoch, 0, generation);
|
|
853
|
+
if (destroyed || generation !== connectGeneration) return;
|
|
854
|
+
if (update.epoch !== internal.transportEpoch) throw new Error("Mismatched update epoch");
|
|
855
|
+
if (update.sequence <= internal.sequence) return;
|
|
856
|
+
if (update.sequence === internal.sequence + 1) {
|
|
857
|
+
applyUpdate(update);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
await fullSync(update.epoch, update.sequence, generation);
|
|
861
|
+
};
|
|
862
|
+
internal.destroyCallbacks?.add(cleanup);
|
|
863
|
+
try {
|
|
864
|
+
registerDisposer(transport.listen("update", async (encoded) => {
|
|
865
|
+
try {
|
|
866
|
+
await handleUpdate(encoded);
|
|
867
|
+
} catch (error) {
|
|
868
|
+
if (!destroyed) {
|
|
869
|
+
try {
|
|
870
|
+
await fullSync();
|
|
871
|
+
} catch (syncError) {
|
|
872
|
+
reportLifecycleError(syncError);
|
|
873
|
+
}
|
|
874
|
+
reportLifecycleError(error);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}));
|
|
878
|
+
registerDisposer(transport.onConnect(() => {
|
|
879
|
+
const pending = fullSync(void 0, 0, ++connectGeneration).finally(() => {
|
|
880
|
+
if (connectSync === pending) connectSync = null;
|
|
881
|
+
});
|
|
882
|
+
connectSync = pending;
|
|
883
|
+
pending.catch(reportLifecycleError);
|
|
884
|
+
return pending;
|
|
885
|
+
}));
|
|
886
|
+
markStoreReady(store);
|
|
887
|
+
internal.assertAlive?.("store initialization");
|
|
888
|
+
} catch (error) {
|
|
889
|
+
return failStoreSetup(store, error);
|
|
890
|
+
}
|
|
891
|
+
return wrapStore(store, () => store.getState());
|
|
892
|
+
};
|
|
893
|
+
const emit = (store, internal, patches) => {
|
|
894
|
+
if (!store.transport || !patches?.length || !internal.transportEpoch) return;
|
|
895
|
+
const sequence = internal.sequence + 1;
|
|
896
|
+
const encoded = encodeUpdateMessage(internal.transportEpoch, sequence, patches);
|
|
897
|
+
internal.sequence = sequence;
|
|
898
|
+
try {
|
|
899
|
+
const pending = store.transport.emit({
|
|
900
|
+
name: "update",
|
|
901
|
+
respond: false
|
|
902
|
+
}, encoded);
|
|
903
|
+
Promise.resolve(pending).catch(reportLifecycleError);
|
|
904
|
+
} catch (error) {
|
|
905
|
+
reportLifecycleError(error);
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
//#endregion
|
|
909
|
+
//#region packages/core/src/global.ts
|
|
910
|
+
const getGlobal = () => {
|
|
911
|
+
let _global;
|
|
912
|
+
if (typeof window !== "undefined") _global = window;
|
|
913
|
+
else if (typeof global !== "undefined") _global = global;
|
|
914
|
+
else if (typeof self !== "undefined") _global = self;
|
|
915
|
+
else _global = {};
|
|
916
|
+
return _global;
|
|
917
|
+
};
|
|
918
|
+
//#endregion
|
|
919
|
+
//#region packages/core/src/constant.ts
|
|
920
|
+
const WorkerType = getGlobal().SharedWorkerGlobalScope ? "SharedWorkerInternal" : globalThis.WorkerGlobalScope ? "WebWorkerInternal" : null;
|
|
921
|
+
const bindSymbol = Symbol("bind");
|
|
922
|
+
//#endregion
|
|
923
|
+
//#region packages/core/src/getRawStateClientAction.ts
|
|
924
|
+
/**
|
|
925
|
+
* The authority changed while a remote action was in flight, so its side-effect
|
|
926
|
+
* outcome cannot be determined safely from the current client mirror.
|
|
927
|
+
*/
|
|
928
|
+
var ActionAuthorityChangedError = class extends Error {
|
|
929
|
+
code = "COACTION_ACTION_AUTHORITY_CHANGED";
|
|
930
|
+
outcome = "unknown";
|
|
931
|
+
constructor(action) {
|
|
932
|
+
super(`The authority changed while action '${action}' was in flight. The action may have completed on the previous authority; retry only if it is idempotent.`);
|
|
933
|
+
this.name = "ActionAuthorityChangedError";
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
const createClientAction = ({ clientExecuteSyncTimeoutMs, internal, key, store, sliceKey }) => {
|
|
937
|
+
return (...args) => {
|
|
938
|
+
internal.assertAlive?.(`action ${key}`);
|
|
939
|
+
let actionId;
|
|
940
|
+
let done;
|
|
941
|
+
if (store.trace) {
|
|
942
|
+
actionId = uuid();
|
|
943
|
+
store.trace({
|
|
944
|
+
method: key,
|
|
945
|
+
parameters: args,
|
|
946
|
+
id: actionId,
|
|
947
|
+
sliceKey
|
|
948
|
+
});
|
|
949
|
+
done = (result) => {
|
|
950
|
+
store.trace({
|
|
951
|
+
method: key,
|
|
952
|
+
id: actionId,
|
|
953
|
+
result,
|
|
954
|
+
sliceKey
|
|
955
|
+
});
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
const traceAction = (run) => {
|
|
959
|
+
try {
|
|
960
|
+
const result = run();
|
|
961
|
+
if (result instanceof Promise) return result.then((value) => {
|
|
962
|
+
done?.(value);
|
|
963
|
+
return value;
|
|
964
|
+
}, (error) => {
|
|
965
|
+
done?.(error);
|
|
966
|
+
throw error;
|
|
967
|
+
});
|
|
968
|
+
done?.(result);
|
|
969
|
+
return result;
|
|
970
|
+
} catch (error) {
|
|
971
|
+
done?.(error);
|
|
972
|
+
throw error;
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
if (typeof sliceKey === "symbol") throw new Error("Symbol-keyed slice actions are not supported in client store mode.");
|
|
976
|
+
const encoded = encodeExecuteRequest(typeof sliceKey === "undefined" ? [key] : [String(sliceKey), key], args);
|
|
977
|
+
const requestEpoch = internal.transportEpoch;
|
|
978
|
+
return traceAction(() => {
|
|
979
|
+
const emitted = store.transport.emit("execute", encoded);
|
|
980
|
+
return (internal.awaitClientTransport ? internal.awaitClientTransport(emitted) : emitted).then(async (payload) => {
|
|
981
|
+
const response = decodeExecuteResponse(payload);
|
|
982
|
+
internal.assertAlive?.(`action ${key}`);
|
|
983
|
+
const syncClientState = internal.syncClientState;
|
|
984
|
+
if (!syncClientState) throw new Error("Client fullSync is not available");
|
|
985
|
+
if (internal.transportEpoch !== requestEpoch && response.epoch !== internal.transportEpoch) throw new ActionAuthorityChangedError(key);
|
|
986
|
+
if (response.epoch !== internal.transportEpoch) {
|
|
987
|
+
await syncClientState(response.epoch, response.sequence);
|
|
988
|
+
internal.assertAlive?.(`action ${key}`);
|
|
989
|
+
} else if (response.sequence > internal.sequence) {
|
|
990
|
+
await new Promise((resolve, reject) => {
|
|
991
|
+
let settled = false;
|
|
992
|
+
let unsubscribe = () => {};
|
|
993
|
+
let timeout;
|
|
994
|
+
const cancel = () => finish(/* @__PURE__ */ new Error("Client transport was destroyed"));
|
|
995
|
+
const finish = (error) => {
|
|
996
|
+
if (settled) return;
|
|
997
|
+
settled = true;
|
|
998
|
+
unsubscribe();
|
|
999
|
+
internal.destroyCallbacks?.delete(cancel);
|
|
1000
|
+
if (timeout) clearTimeout(timeout);
|
|
1001
|
+
if (typeof error === "undefined") resolve();
|
|
1002
|
+
else reject(error);
|
|
1003
|
+
};
|
|
1004
|
+
unsubscribe = store.subscribe(() => {
|
|
1005
|
+
if (internal.transportEpoch === response.epoch && internal.sequence >= response.sequence) finish();
|
|
1006
|
+
});
|
|
1007
|
+
internal.destroyCallbacks?.add(cancel);
|
|
1008
|
+
timeout = setTimeout(() => {
|
|
1009
|
+
syncClientState(response.epoch, response.sequence).then(() => finish(), (error) => finish(error));
|
|
1010
|
+
}, clientExecuteSyncTimeoutMs);
|
|
1011
|
+
if (internal.transportEpoch === response.epoch && internal.sequence >= response.sequence) finish();
|
|
1012
|
+
});
|
|
1013
|
+
internal.assertAlive?.(`action ${key}`);
|
|
1014
|
+
}
|
|
1015
|
+
if (!response.ok) throw new Error(response.error);
|
|
1016
|
+
return response.value;
|
|
1017
|
+
});
|
|
1018
|
+
});
|
|
1019
|
+
};
|
|
1020
|
+
};
|
|
1021
|
+
//#endregion
|
|
1022
|
+
//#region packages/core/src/handleMainTransport.ts
|
|
1023
|
+
const publicErrorMessages = /* @__PURE__ */ new Set([
|
|
1024
|
+
"Remote action is not allowed",
|
|
1025
|
+
"The function is not found",
|
|
1026
|
+
"Transport request is not authorized",
|
|
1027
|
+
"Transport request was cancelled after store destroy"
|
|
1028
|
+
]);
|
|
1029
|
+
const getErrorMessage = async (error, request, policy) => {
|
|
1030
|
+
if (error instanceof Error && publicErrorMessages.has(error.message)) return error.message;
|
|
1031
|
+
if (request && policy?.mapError) try {
|
|
1032
|
+
const message = await policy.mapError(error, request);
|
|
1033
|
+
if (typeof message === "string" && message) return message;
|
|
1034
|
+
} catch (mapError) {
|
|
1035
|
+
if (process.env.NODE_ENV === "development") console.error(mapError);
|
|
1036
|
+
}
|
|
1037
|
+
return "Remote action failed";
|
|
1038
|
+
};
|
|
1039
|
+
const handleMainTransport = (store, internal, storeTransport, workerType, checkEnablePatches, policy) => {
|
|
1040
|
+
const transport = storeTransport ?? (workerType === "SharedWorkerInternal" || workerType === "WebWorkerInternal" ? (0, data_transport.createTransport)(workerType, { prefix: store.name }) : void 0);
|
|
1041
|
+
if (!transport) return;
|
|
1042
|
+
if (checkEnablePatches) throw new Error("enablePatches: true is required for the transport");
|
|
1043
|
+
const epoch = uuid();
|
|
1044
|
+
internal.transportEpoch = epoch;
|
|
1045
|
+
let destroyed = false;
|
|
1046
|
+
const disposers = /* @__PURE__ */ new Set();
|
|
1047
|
+
const registerDisposer = (value) => {
|
|
1048
|
+
if (typeof value === "function") disposers.add(value);
|
|
1049
|
+
};
|
|
1050
|
+
const cleanup = () => {
|
|
1051
|
+
if (destroyed) return;
|
|
1052
|
+
destroyed = true;
|
|
1053
|
+
const callbacks = [...disposers];
|
|
1054
|
+
disposers.clear();
|
|
1055
|
+
for (const dispose of callbacks) try {
|
|
1056
|
+
dispose();
|
|
1057
|
+
} catch (error) {
|
|
1058
|
+
if (process.env.NODE_ENV === "development") console.error(error);
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
const assertActive = () => {
|
|
1062
|
+
if (destroyed) throw new Error("Transport request was cancelled after store destroy");
|
|
1063
|
+
};
|
|
1064
|
+
store.transport = transport;
|
|
1065
|
+
internal.emitPatches = (patches) => emit(store, internal, patches);
|
|
1066
|
+
internal.destroyCallbacks?.add(cleanup);
|
|
1067
|
+
try {
|
|
1068
|
+
registerDisposer(transport.listen("execute", async (encoded) => {
|
|
1069
|
+
let policyRequest;
|
|
1070
|
+
try {
|
|
1071
|
+
assertActive();
|
|
1072
|
+
const request = decodeExecuteRequest(encoded);
|
|
1073
|
+
if (!internal.sharedActionPaths?.has(JSON.stringify(request.action))) throw new Error("Remote action is not allowed");
|
|
1074
|
+
if (policy?.allowedActions && !policy.allowedActions.some((allowed) => allowed.length === request.action.length && allowed.every((key, index) => key === request.action[index]))) throw new Error("Remote action is not allowed");
|
|
1075
|
+
policyRequest = {
|
|
1076
|
+
...request,
|
|
1077
|
+
type: "execute"
|
|
1078
|
+
};
|
|
1079
|
+
if (policy?.authorize && await policy.authorize(policyRequest) !== true) throw new Error("Transport request is not authorized");
|
|
1080
|
+
assertActive();
|
|
1081
|
+
let action = store.getState();
|
|
1082
|
+
let receiver;
|
|
1083
|
+
for (const key of request.action) {
|
|
1084
|
+
if (isUnsafePathSegment(key) || typeof action !== "object" && typeof action !== "function" || action === null || !Object.prototype.hasOwnProperty.call(action, key)) throw new Error("The function is not found");
|
|
1085
|
+
receiver = action;
|
|
1086
|
+
action = action[key];
|
|
1087
|
+
}
|
|
1088
|
+
if (typeof action !== "function") throw new Error("The function is not found");
|
|
1089
|
+
const value = await Reflect.apply(action, receiver, request.args);
|
|
1090
|
+
assertActive();
|
|
1091
|
+
return encodeExecuteResponse({
|
|
1092
|
+
epoch,
|
|
1093
|
+
ok: true,
|
|
1094
|
+
sequence: internal.sequence,
|
|
1095
|
+
...typeof value === "undefined" ? {} : { value }
|
|
1096
|
+
});
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
if (process.env.NODE_ENV === "development") console.error(error);
|
|
1099
|
+
return encodeExecuteResponse({
|
|
1100
|
+
epoch,
|
|
1101
|
+
error: await getErrorMessage(error, policyRequest, policy),
|
|
1102
|
+
ok: false,
|
|
1103
|
+
sequence: internal.sequence
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
}));
|
|
1107
|
+
registerDisposer(transport.listen("fullSync", async (encoded) => {
|
|
1108
|
+
assertActive();
|
|
1109
|
+
decodeFullSyncRequest(encoded);
|
|
1110
|
+
if (policy?.authorize && await policy.authorize({ type: "fullSync" }) !== true) throw new Error("Transport request is not authorized");
|
|
1111
|
+
assertActive();
|
|
1112
|
+
const state = internal.getTransportState?.() ?? internal.rootState;
|
|
1113
|
+
validateSharedStateSerializable(state);
|
|
1114
|
+
if (typeof state !== "object" || state === null || Array.isArray(state)) throw new TypeError("Shared store state must be a JSON object");
|
|
1115
|
+
return encodeFullSyncResponse({
|
|
1116
|
+
epoch,
|
|
1117
|
+
sequence: internal.sequence,
|
|
1118
|
+
state
|
|
1119
|
+
});
|
|
1120
|
+
}));
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
internal.destroyCallbacks?.delete(cleanup);
|
|
1123
|
+
cleanup();
|
|
1124
|
+
store.transport = void 0;
|
|
1125
|
+
try {
|
|
1126
|
+
transport.dispose?.();
|
|
1127
|
+
} catch (disposeError) {
|
|
1128
|
+
if (process.env.NODE_ENV === "development") console.error(disposeError);
|
|
1129
|
+
}
|
|
1130
|
+
throw error;
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
//#endregion
|
|
1134
|
+
//#region packages/core/src/applyMiddlewares.ts
|
|
1135
|
+
const isStoreLike = (value) => {
|
|
1136
|
+
if (!value || typeof value !== "object") return false;
|
|
1137
|
+
const candidate = value;
|
|
1138
|
+
return typeof candidate.setState === "function" && typeof candidate.getState === "function" && typeof candidate.subscribe === "function" && typeof candidate.destroy === "function" && typeof candidate.apply === "function" && typeof candidate.getPureState === "function";
|
|
1139
|
+
};
|
|
1140
|
+
const applyMiddlewares = (store, middlewares) => {
|
|
1141
|
+
return middlewares.reduce((store, middleware, index) => {
|
|
1142
|
+
if (process.env.NODE_ENV === "development") {
|
|
1143
|
+
if (typeof middleware !== "function") throw new Error(`middlewares[${index}] should be a function`);
|
|
1144
|
+
}
|
|
1145
|
+
const nextStore = middleware(store);
|
|
1146
|
+
if (process.env.NODE_ENV === "development") {
|
|
1147
|
+
if (!isStoreLike(nextStore)) throw new Error(`middlewares[${index}] should return a store-like object`);
|
|
1148
|
+
}
|
|
1149
|
+
return nextStore;
|
|
1150
|
+
}, store);
|
|
1151
|
+
};
|
|
1152
|
+
//#endregion
|
|
1153
|
+
//#region packages/core/src/getInitialState.ts
|
|
1154
|
+
const isObject = (value) => typeof value === "object" && value !== null;
|
|
1155
|
+
const isStateFactory = (value) => typeof value === "function";
|
|
1156
|
+
const hasGetState = (value) => (typeof value === "object" || typeof value === "function") && value !== null && typeof value.getState === "function";
|
|
1157
|
+
const hasBindState = (value) => isObject(value) && !!value[bindSymbol];
|
|
1158
|
+
const formatInvalidStateMessage = (type, stateOrFn, key) => `Invalid state ${type} encountered in makeState: ${typeof key !== "undefined" ? `for key ${String(key)}, ` : ""}${typeof stateOrFn}`;
|
|
1159
|
+
const getInitialState = (store, createState, internal) => {
|
|
1160
|
+
const makeState = (stateOrFn, key) => {
|
|
1161
|
+
let state;
|
|
1162
|
+
if (isStateFactory(stateOrFn)) state = stateOrFn(store.setState, store.getState, store);
|
|
1163
|
+
else if (isObject(stateOrFn)) state = stateOrFn;
|
|
1164
|
+
else {
|
|
1165
|
+
if (process.env.NODE_ENV !== "production") throw new Error(formatInvalidStateMessage("value", stateOrFn, key));
|
|
1166
|
+
return {};
|
|
1167
|
+
}
|
|
1168
|
+
if (hasGetState(state)) state = state.getState();
|
|
1169
|
+
else if (typeof state === "function") state = state();
|
|
1170
|
+
if (hasBindState(state)) {
|
|
1171
|
+
if (store.isSliceStore) throw new Error("Third-party state binding does not support Slices mode. Please inject a whole store instead.");
|
|
1172
|
+
const binder = state[bindSymbol];
|
|
1173
|
+
const rawState = binder.bind(state);
|
|
1174
|
+
binder.handleStore(store, rawState, state, internal, key);
|
|
1175
|
+
delete state[bindSymbol];
|
|
1176
|
+
return rawState;
|
|
1177
|
+
}
|
|
1178
|
+
if (!isObject(state)) {
|
|
1179
|
+
if (process.env.NODE_ENV !== "production") throw new Error(formatInvalidStateMessage("result", state, key));
|
|
1180
|
+
return {};
|
|
1181
|
+
}
|
|
1182
|
+
return state;
|
|
1183
|
+
};
|
|
1184
|
+
if (!store.isSliceStore) return makeState(createState);
|
|
1185
|
+
return getOwnEnumerableKeys(createState).reduce((stateTree, key) => {
|
|
1186
|
+
if (typeof key === "string" && isUnsafeKey(key)) return stateTree;
|
|
1187
|
+
setOwnEnumerable(stateTree, key, makeState(createState[key], key));
|
|
1188
|
+
return stateTree;
|
|
1189
|
+
}, {});
|
|
1190
|
+
};
|
|
1191
|
+
//#endregion
|
|
1192
|
+
//#region packages/core/src/storeCommit.ts
|
|
1193
|
+
const storeCommitRuntimeSymbol = Symbol.for("coaction.storeCommit.runtime");
|
|
1194
|
+
const getStoreCommitRuntime = (store, create = false) => {
|
|
1195
|
+
const target = store;
|
|
1196
|
+
const existing = target[storeCommitRuntimeSymbol];
|
|
1197
|
+
if (existing || !create) return existing;
|
|
1198
|
+
const runtime = {
|
|
1199
|
+
disposed: false,
|
|
1200
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
1201
|
+
prepareListeners: /* @__PURE__ */ new Set()
|
|
1202
|
+
};
|
|
1203
|
+
Object.defineProperty(target, storeCommitRuntimeSymbol, {
|
|
1204
|
+
configurable: true,
|
|
1205
|
+
enumerable: true,
|
|
1206
|
+
value: runtime,
|
|
1207
|
+
writable: true
|
|
1208
|
+
});
|
|
1209
|
+
return runtime;
|
|
1210
|
+
};
|
|
1211
|
+
/** @internal */
|
|
1212
|
+
const hasStoreCommitListeners = (store) => Boolean(getStoreCommitRuntime(store)?.listeners.size);
|
|
1213
|
+
/** @internal */
|
|
1214
|
+
const publishStoreCommit = (store, commit) => {
|
|
1215
|
+
const runtime = getStoreCommitRuntime(store);
|
|
1216
|
+
if (!runtime || runtime.disposed || !runtime.listeners.size) return;
|
|
1217
|
+
for (const listener of [...runtime.listeners]) listener(commit);
|
|
1218
|
+
};
|
|
1219
|
+
/** @internal */
|
|
1220
|
+
const prepareStoreCommit = (store, commit) => {
|
|
1221
|
+
const runtime = getStoreCommitRuntime(store);
|
|
1222
|
+
if (!runtime || runtime.disposed || !runtime.prepareListeners.size) return false;
|
|
1223
|
+
let replace = false;
|
|
1224
|
+
for (const listener of runtime.prepareListeners) replace = listener(commit) === true || replace;
|
|
1225
|
+
return replace;
|
|
1226
|
+
};
|
|
1227
|
+
/** @internal */
|
|
1228
|
+
const getStoreCommitSource = (store, fallback) => getStoreCommitRuntime(store)?.source ?? fallback;
|
|
1229
|
+
/** @internal */
|
|
1230
|
+
const runWithStoreCommitSource = (store, source, callback) => {
|
|
1231
|
+
const runtime = getStoreCommitRuntime(store, true);
|
|
1232
|
+
const previousSource = runtime.source;
|
|
1233
|
+
runtime.source = source;
|
|
1234
|
+
try {
|
|
1235
|
+
return callback();
|
|
1236
|
+
} finally {
|
|
1237
|
+
runtime.source = previousSource;
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
/** @internal */
|
|
1241
|
+
const registerStorePatchReplayer = (store, replay) => {
|
|
1242
|
+
const runtime = getStoreCommitRuntime(store, true);
|
|
1243
|
+
runtime.replay = replay;
|
|
1244
|
+
};
|
|
1245
|
+
/** @internal */
|
|
1246
|
+
const disposeStoreCommitRuntime = (store) => {
|
|
1247
|
+
const runtime = getStoreCommitRuntime(store);
|
|
1248
|
+
if (!runtime) return;
|
|
1249
|
+
runtime.disposed = true;
|
|
1250
|
+
runtime.listeners.clear();
|
|
1251
|
+
runtime.prepareListeners.clear();
|
|
1252
|
+
runtime.source = void 0;
|
|
1253
|
+
runtime.replay = void 0;
|
|
1254
|
+
};
|
|
1255
|
+
//#endregion
|
|
1256
|
+
//#region packages/core/src/handleDraft.ts
|
|
1257
|
+
const handleDraft = (store, internal) => {
|
|
1258
|
+
internal.rootState = internal.backupState;
|
|
1259
|
+
const [, patches, inversePatches] = internal.finalizeDraft();
|
|
1260
|
+
const finalPatches = store.patch ? store.patch({
|
|
1261
|
+
patches,
|
|
1262
|
+
inversePatches
|
|
1263
|
+
}) : {
|
|
1264
|
+
patches,
|
|
1265
|
+
inversePatches
|
|
1266
|
+
};
|
|
1267
|
+
const safePatches = sanitizeCheckedPatches(finalPatches.patches, "store.patch()");
|
|
1268
|
+
const safeInversePatches = sanitizeCheckedPatches(finalPatches.inversePatches, "store.patch() inverse patches");
|
|
1269
|
+
if (safePatches.length) {
|
|
1270
|
+
store.apply(internal.rootState, safePatches);
|
|
1271
|
+
internal.emitPatches?.(safePatches);
|
|
1272
|
+
publishStoreCommit(store, {
|
|
1273
|
+
state: internal.rootState,
|
|
1274
|
+
patches: safePatches,
|
|
1275
|
+
inversePatches: safeInversePatches,
|
|
1276
|
+
source: "mutableAction"
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
//#endregion
|
|
1281
|
+
//#region packages/core/src/getRawStateLocalAction.ts
|
|
1282
|
+
const getActionTarget = (store, sliceKey) => {
|
|
1283
|
+
return typeof sliceKey !== "undefined" ? store.getState()[sliceKey] : store.getState();
|
|
1284
|
+
};
|
|
1285
|
+
const createLocalAction = ({ fn, internal, key, options, store, sliceKey }) => {
|
|
1286
|
+
return (...args) => {
|
|
1287
|
+
internal.assertAlive?.(`action ${String(key)}`);
|
|
1288
|
+
let actionId;
|
|
1289
|
+
let done;
|
|
1290
|
+
if (store.trace) {
|
|
1291
|
+
actionId = uuid();
|
|
1292
|
+
store.trace({
|
|
1293
|
+
method: String(key),
|
|
1294
|
+
parameters: args,
|
|
1295
|
+
id: actionId,
|
|
1296
|
+
sliceKey
|
|
1297
|
+
});
|
|
1298
|
+
done = (result) => {
|
|
1299
|
+
store.trace({
|
|
1300
|
+
method: String(key),
|
|
1301
|
+
id: actionId,
|
|
1302
|
+
result,
|
|
1303
|
+
sliceKey
|
|
1304
|
+
});
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
const traceAction = (run) => {
|
|
1308
|
+
try {
|
|
1309
|
+
const result = run();
|
|
1310
|
+
if (result instanceof Promise) return result.then((value) => {
|
|
1311
|
+
done?.(value);
|
|
1312
|
+
return value;
|
|
1313
|
+
}, (error) => {
|
|
1314
|
+
done?.(error);
|
|
1315
|
+
throw error;
|
|
1316
|
+
});
|
|
1317
|
+
done?.(result);
|
|
1318
|
+
return result;
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
done?.(error);
|
|
1321
|
+
throw error;
|
|
1322
|
+
}
|
|
1323
|
+
};
|
|
1324
|
+
const enablePatches = Boolean(store.transport ?? options.enablePatches) || hasStoreCommitListeners(store);
|
|
1325
|
+
return traceAction(() => {
|
|
1326
|
+
if (internal.mutableInstance && !internal.isBatching && enablePatches) {
|
|
1327
|
+
let result;
|
|
1328
|
+
const handleResult = (isDrafted) => {
|
|
1329
|
+
handleDraft(store, internal);
|
|
1330
|
+
if (isDrafted) {
|
|
1331
|
+
internal.backupState = internal.rootState;
|
|
1332
|
+
const [draft, finalize] = (0, mutative.create)(internal.rootState, { enablePatches: true });
|
|
1333
|
+
internal.finalizeDraft = finalize;
|
|
1334
|
+
internal.rootState = draft;
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
const isDrafted = (0, mutative.isDraft)(internal.rootState);
|
|
1338
|
+
if (isDrafted) handleResult();
|
|
1339
|
+
internal.backupState = internal.rootState;
|
|
1340
|
+
const [draft, finalize] = (0, mutative.create)(internal.rootState, { enablePatches: true });
|
|
1341
|
+
internal.finalizeDraft = finalize;
|
|
1342
|
+
internal.rootState = draft;
|
|
1343
|
+
let asyncResult;
|
|
1344
|
+
try {
|
|
1345
|
+
result = fn.apply(getActionTarget(store, sliceKey), args);
|
|
1346
|
+
if (result instanceof Promise) asyncResult = result;
|
|
1347
|
+
} finally {
|
|
1348
|
+
if (!asyncResult) handleResult(isDrafted);
|
|
1349
|
+
}
|
|
1350
|
+
if (asyncResult) return asyncResult.then((value) => {
|
|
1351
|
+
handleResult(isDrafted);
|
|
1352
|
+
return value;
|
|
1353
|
+
}, (error) => {
|
|
1354
|
+
handleResult(isDrafted);
|
|
1355
|
+
throw error;
|
|
1356
|
+
});
|
|
1357
|
+
return result;
|
|
1358
|
+
}
|
|
1359
|
+
if (internal.mutableInstance && internal.actMutable) return internal.actMutable(() => {
|
|
1360
|
+
return fn.apply(getActionTarget(store, sliceKey), args);
|
|
1361
|
+
});
|
|
1362
|
+
return fn.apply(getActionTarget(store, sliceKey), args);
|
|
1363
|
+
});
|
|
1364
|
+
};
|
|
1365
|
+
};
|
|
1366
|
+
//#endregion
|
|
1367
|
+
//#region packages/core/src/immutableState.ts
|
|
1368
|
+
const isImmutableStateObject = (value) => {
|
|
1369
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1370
|
+
if (Array.isArray(value)) return true;
|
|
1371
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1372
|
+
return prototype === Object.prototype || prototype === null;
|
|
1373
|
+
};
|
|
1374
|
+
const getImmutableStateSnapshot = (value, cache) => {
|
|
1375
|
+
if (!isImmutableStateObject(value)) return value;
|
|
1376
|
+
const cached = cache.get(value);
|
|
1377
|
+
if (cached) return cached;
|
|
1378
|
+
const isArray = Array.isArray(value);
|
|
1379
|
+
const snapshot = isArray ? new Array(value.length) : Object.create(Object.getPrototypeOf(value));
|
|
1380
|
+
cache.set(value, snapshot);
|
|
1381
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1382
|
+
if (isArray && key === "length") continue;
|
|
1383
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1384
|
+
if (Object.prototype.hasOwnProperty.call(descriptor, "value")) descriptor.value = getImmutableStateSnapshot(descriptor.value, cache);
|
|
1385
|
+
Object.defineProperty(snapshot, key, descriptor);
|
|
1386
|
+
}
|
|
1387
|
+
return Object.freeze(snapshot);
|
|
1388
|
+
};
|
|
1389
|
+
const createImmutableSnapshotPatches = (patches, cache) => patches.map((patch) => Object.prototype.hasOwnProperty.call(patch, "value") ? {
|
|
1390
|
+
...patch,
|
|
1391
|
+
value: getImmutableStateSnapshot(patch.value, cache)
|
|
1392
|
+
} : patch);
|
|
1393
|
+
const finalizeImmutableStateSnapshot = (state, snapshot, patches, cache, sources) => {
|
|
1394
|
+
const mapPair = (value, snapshotValue) => {
|
|
1395
|
+
if (isImmutableStateObject(value) && isImmutableStateObject(snapshotValue)) {
|
|
1396
|
+
cache.set(value, snapshotValue);
|
|
1397
|
+
sources?.set(snapshotValue, value);
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
mapPair(state, snapshot);
|
|
1401
|
+
for (const patch of patches) {
|
|
1402
|
+
let value = state;
|
|
1403
|
+
let snapshotValue = snapshot;
|
|
1404
|
+
const ancestors = [];
|
|
1405
|
+
if (isImmutableStateObject(snapshotValue)) ancestors.push(snapshotValue);
|
|
1406
|
+
for (const key of patch.path) {
|
|
1407
|
+
if (!isImmutableStateObject(value) || !isImmutableStateObject(snapshotValue)) break;
|
|
1408
|
+
value = value[key];
|
|
1409
|
+
snapshotValue = snapshotValue[key];
|
|
1410
|
+
mapPair(value, snapshotValue);
|
|
1411
|
+
if (isImmutableStateObject(snapshotValue)) ancestors.push(snapshotValue);
|
|
1412
|
+
}
|
|
1413
|
+
for (let index = ancestors.length - 1; index >= 0; index -= 1) if (!Object.isFrozen(ancestors[index])) Object.freeze(ancestors[index]);
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
const indexImmutableStateSnapshot = (state, snapshot, sources, seen = /* @__PURE__ */ new WeakSet()) => {
|
|
1417
|
+
if (!isImmutableStateObject(state) || !isImmutableStateObject(snapshot) || seen.has(snapshot)) return;
|
|
1418
|
+
seen.add(snapshot);
|
|
1419
|
+
sources.set(snapshot, state);
|
|
1420
|
+
for (const key of Reflect.ownKeys(state)) {
|
|
1421
|
+
const stateDescriptor = Object.getOwnPropertyDescriptor(state, key);
|
|
1422
|
+
const snapshotDescriptor = Object.getOwnPropertyDescriptor(snapshot, key);
|
|
1423
|
+
if (stateDescriptor && snapshotDescriptor && Object.prototype.hasOwnProperty.call(stateDescriptor, "value") && Object.prototype.hasOwnProperty.call(snapshotDescriptor, "value")) indexImmutableStateSnapshot(stateDescriptor.value, snapshotDescriptor.value, sources, seen);
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
//#endregion
|
|
1427
|
+
//#region packages/core/src/getRawStateStateProperty.ts
|
|
1428
|
+
const assertImmutableStateMutationAllowed = (internal) => {
|
|
1429
|
+
if (internal.mutableInstance || internal.isBatching) return;
|
|
1430
|
+
throw new Error("Direct state mutation is not allowed in immutable Coaction stores. Wrap mutations in set(() => { ... }).");
|
|
1431
|
+
};
|
|
1432
|
+
const readonlyProxyCache = /* @__PURE__ */ new WeakMap();
|
|
1433
|
+
const getReadonlyProxyCache = (internal) => {
|
|
1434
|
+
let cache = readonlyProxyCache.get(internal);
|
|
1435
|
+
if (!cache) {
|
|
1436
|
+
cache = /* @__PURE__ */ new WeakMap();
|
|
1437
|
+
readonlyProxyCache.set(internal, cache);
|
|
1438
|
+
}
|
|
1439
|
+
return cache;
|
|
1440
|
+
};
|
|
1441
|
+
const getPublicStateObject = (internal, value, sliceKey) => {
|
|
1442
|
+
if (value === internal.rootState) return internal.module;
|
|
1443
|
+
if (typeof sliceKey === "undefined" || typeof internal.rootState !== "object" || internal.rootState === null || typeof internal.module !== "object" || internal.module === null) return;
|
|
1444
|
+
const rootState = internal.rootState;
|
|
1445
|
+
const module = internal.module;
|
|
1446
|
+
if (rootState[sliceKey] === value) return module[sliceKey];
|
|
1447
|
+
};
|
|
1448
|
+
const toReadonlyStateValue = (internal, value, sliceKey) => {
|
|
1449
|
+
if (internal.mutableInstance || internal.isBatching || !isImmutableStateObject(value)) return value;
|
|
1450
|
+
if (internal.computedReadDepth) {
|
|
1451
|
+
const cache = internal.computedSnapshotCache ??= /* @__PURE__ */ new WeakMap();
|
|
1452
|
+
if (isImmutableStateObject(internal.rootState) && !cache.has(internal.rootState)) getImmutableStateSnapshot(internal.rootState, cache);
|
|
1453
|
+
return getImmutableStateSnapshot(value, cache);
|
|
1454
|
+
}
|
|
1455
|
+
const publicValue = getPublicStateObject(internal, value, sliceKey);
|
|
1456
|
+
if (publicValue) return publicValue;
|
|
1457
|
+
const cache = getReadonlyProxyCache(internal);
|
|
1458
|
+
const cached = cache.get(value);
|
|
1459
|
+
if (cached) return cached;
|
|
1460
|
+
const proxy = new Proxy(value, {
|
|
1461
|
+
get(target, key, receiver) {
|
|
1462
|
+
return toReadonlyStateValue(internal, Reflect.get(target, key, receiver), sliceKey);
|
|
1463
|
+
},
|
|
1464
|
+
set() {
|
|
1465
|
+
assertImmutableStateMutationAllowed(internal);
|
|
1466
|
+
return false;
|
|
1467
|
+
},
|
|
1468
|
+
deleteProperty() {
|
|
1469
|
+
assertImmutableStateMutationAllowed(internal);
|
|
1470
|
+
return false;
|
|
1471
|
+
},
|
|
1472
|
+
defineProperty() {
|
|
1473
|
+
assertImmutableStateMutationAllowed(internal);
|
|
1474
|
+
return false;
|
|
1475
|
+
},
|
|
1476
|
+
setPrototypeOf() {
|
|
1477
|
+
assertImmutableStateMutationAllowed(internal);
|
|
1478
|
+
return false;
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
cache.set(value, proxy);
|
|
1482
|
+
return proxy;
|
|
1483
|
+
};
|
|
1484
|
+
const toPublicComputedValue = (internal, value, sliceKey) => {
|
|
1485
|
+
if (!isImmutableStateObject(value)) return value;
|
|
1486
|
+
const rootSnapshot = internal.computedSnapshotCache?.get(internal.rootState);
|
|
1487
|
+
const sources = internal.computedSnapshotSources ??= /* @__PURE__ */ new WeakMap();
|
|
1488
|
+
let source = sources.get(value);
|
|
1489
|
+
if (!source && Object.isFrozen(value) && rootSnapshot) {
|
|
1490
|
+
indexImmutableStateSnapshot(internal.rootState, rootSnapshot, sources);
|
|
1491
|
+
source = sources.get(value);
|
|
1492
|
+
}
|
|
1493
|
+
if (source) internal.computedIdentityRequired = true;
|
|
1494
|
+
return source ? toReadonlyStateValue(internal, source, sliceKey) : value;
|
|
1495
|
+
};
|
|
1496
|
+
const prepareStateDescriptor = ({ descriptor, initialStateSeen, internal, key, rawState, sliceKey }) => {
|
|
1497
|
+
const isComputed = descriptor.value instanceof Computed;
|
|
1498
|
+
const readStateValue = () => typeof sliceKey !== "undefined" ? internal.rootState[sliceKey][key] : internal.rootState[key];
|
|
1499
|
+
const initialValue = isComputed ? descriptor.value : sanitizeInitialStateValue(descriptor.value, initialStateSeen);
|
|
1500
|
+
if (internal.mutableInstance) Object.defineProperty(rawState, key, {
|
|
1501
|
+
get: () => internal.mutableInstance[key],
|
|
1502
|
+
set: (value) => {
|
|
1503
|
+
internal.mutableInstance[key] = value;
|
|
1504
|
+
},
|
|
1505
|
+
configurable: true,
|
|
1506
|
+
enumerable: descriptor.enumerable
|
|
1507
|
+
});
|
|
1508
|
+
else if (!isComputed) Object.defineProperty(rawState, key, {
|
|
1509
|
+
value: initialValue,
|
|
1510
|
+
configurable: true,
|
|
1511
|
+
enumerable: descriptor.enumerable,
|
|
1512
|
+
writable: true
|
|
1513
|
+
});
|
|
1514
|
+
if (isComputed) {
|
|
1515
|
+
if (internal.mutableInstance) throw new Error("Computed is not supported with mutable instance");
|
|
1516
|
+
const getComputed = descriptor.value.createGetter({ internal });
|
|
1517
|
+
descriptor.get = function() {
|
|
1518
|
+
return toPublicComputedValue(internal, getComputed.call(this), sliceKey);
|
|
1519
|
+
};
|
|
1520
|
+
} else if (typeof sliceKey !== "undefined") {
|
|
1521
|
+
const read = createTrackedStateReader(internal, readStateValue, initialValue);
|
|
1522
|
+
descriptor.get = () => toReadonlyStateValue(internal, read(), sliceKey);
|
|
1523
|
+
descriptor.set = (value) => {
|
|
1524
|
+
assertImmutableStateMutationAllowed(internal);
|
|
1525
|
+
internal.rootState[sliceKey][key] = value;
|
|
1526
|
+
};
|
|
1527
|
+
} else {
|
|
1528
|
+
const read = createTrackedStateReader(internal, readStateValue, initialValue);
|
|
1529
|
+
descriptor.get = () => toReadonlyStateValue(internal, read());
|
|
1530
|
+
descriptor.set = (value) => {
|
|
1531
|
+
assertImmutableStateMutationAllowed(internal);
|
|
1532
|
+
internal.rootState[key] = value;
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
delete descriptor.value;
|
|
1536
|
+
delete descriptor.writable;
|
|
1537
|
+
};
|
|
1538
|
+
const prepareAccessorDescriptor = ({ descriptor, internal, sliceKey }) => {
|
|
1539
|
+
if (internal.mutableInstance || typeof descriptor.get !== "function") return;
|
|
1540
|
+
const getComputed = createCachedGetter(internal, descriptor.get);
|
|
1541
|
+
descriptor.get = function() {
|
|
1542
|
+
return toPublicComputedValue(internal, getComputed.call(this), sliceKey);
|
|
1543
|
+
};
|
|
1544
|
+
};
|
|
1545
|
+
//#endregion
|
|
1546
|
+
//#region packages/core/src/getRawState.ts
|
|
1547
|
+
const defaultClientExecuteSyncTimeoutMs = 1500;
|
|
1548
|
+
const lockPublicStateObject = (state) => {
|
|
1549
|
+
Object.freeze(state);
|
|
1550
|
+
return state;
|
|
1551
|
+
};
|
|
1552
|
+
const getClientExecuteSyncTimeoutMs = (options) => {
|
|
1553
|
+
const timeout = options.executeSyncTimeoutMs;
|
|
1554
|
+
if (typeof timeout === "undefined") return defaultClientExecuteSyncTimeoutMs;
|
|
1555
|
+
if (!Number.isFinite(timeout) || timeout < 0) throw new Error("executeSyncTimeoutMs must be a finite number greater than or equal to 0");
|
|
1556
|
+
return timeout;
|
|
1557
|
+
};
|
|
1558
|
+
const getRawState = (store, internal, initialState, options, createClientAction) => {
|
|
1559
|
+
const clientExecuteSyncTimeoutMs = getClientExecuteSyncTimeoutMs(options);
|
|
1560
|
+
const rawState = {};
|
|
1561
|
+
const handle = (_rawState, _initialState, sliceKey) => {
|
|
1562
|
+
internal.mutableInstance = internal.toMutableRaw?.(_initialState);
|
|
1563
|
+
const initialStateSeen = /* @__PURE__ */ new WeakMap();
|
|
1564
|
+
initialStateSeen.set(_initialState, _rawState);
|
|
1565
|
+
const safeDescriptors = {};
|
|
1566
|
+
const descriptors = Object.getOwnPropertyDescriptors(_initialState);
|
|
1567
|
+
Reflect.ownKeys(descriptors).forEach((key) => {
|
|
1568
|
+
if (typeof key === "string" && isUnsafeKey(key)) return;
|
|
1569
|
+
safeDescriptors[key] = Reflect.get(descriptors, key);
|
|
1570
|
+
});
|
|
1571
|
+
Reflect.ownKeys(safeDescriptors).forEach((key) => {
|
|
1572
|
+
const descriptor = safeDescriptors[key];
|
|
1573
|
+
if (typeof descriptor === "undefined") return;
|
|
1574
|
+
if (!Object.prototype.hasOwnProperty.call(descriptor, "value")) {
|
|
1575
|
+
prepareAccessorDescriptor({
|
|
1576
|
+
descriptor,
|
|
1577
|
+
internal,
|
|
1578
|
+
sliceKey
|
|
1579
|
+
});
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
if (Object.prototype.hasOwnProperty.call(descriptor, "value")) {
|
|
1583
|
+
if (typeof descriptor.value !== "function") {
|
|
1584
|
+
prepareStateDescriptor({
|
|
1585
|
+
descriptor,
|
|
1586
|
+
initialStateSeen,
|
|
1587
|
+
internal,
|
|
1588
|
+
key,
|
|
1589
|
+
rawState: _rawState,
|
|
1590
|
+
sliceKey
|
|
1591
|
+
});
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
if (store.share === "client") {
|
|
1595
|
+
if (typeof key !== "string") return;
|
|
1596
|
+
if (!createClientAction) throw new Error("Client action runtime is not configured");
|
|
1597
|
+
descriptor.value = createClientAction({
|
|
1598
|
+
clientExecuteSyncTimeoutMs,
|
|
1599
|
+
internal,
|
|
1600
|
+
key,
|
|
1601
|
+
store,
|
|
1602
|
+
sliceKey
|
|
1603
|
+
});
|
|
1604
|
+
} else descriptor.value = createLocalAction({
|
|
1605
|
+
fn: descriptor.value,
|
|
1606
|
+
internal,
|
|
1607
|
+
key,
|
|
1608
|
+
options,
|
|
1609
|
+
store,
|
|
1610
|
+
sliceKey
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
});
|
|
1614
|
+
return lockPublicStateObject(Object.defineProperties({}, safeDescriptors));
|
|
1615
|
+
};
|
|
1616
|
+
if (store.isSliceStore) {
|
|
1617
|
+
internal.module = {};
|
|
1618
|
+
getOwnEnumerableKeys(initialState).forEach((key) => {
|
|
1619
|
+
if (typeof key === "string" && isUnsafeKey(key)) return;
|
|
1620
|
+
const sliceRawState = {};
|
|
1621
|
+
setOwnEnumerable(rawState, key, sliceRawState);
|
|
1622
|
+
setOwnEnumerable(internal.module, key, handle(sliceRawState, initialState[key], key));
|
|
1623
|
+
});
|
|
1624
|
+
lockPublicStateObject(internal.module);
|
|
1625
|
+
} else internal.module = handle(rawState, initialState);
|
|
1626
|
+
return rawState;
|
|
1627
|
+
};
|
|
1628
|
+
//#endregion
|
|
1629
|
+
//#region packages/core/src/handleState.ts
|
|
1630
|
+
const handleState = (store, internal, options) => {
|
|
1631
|
+
let defaultResultValidated = false;
|
|
1632
|
+
let pendingCommitSource;
|
|
1633
|
+
const defaultUpdater = (next) => {
|
|
1634
|
+
defaultResultValidated = false;
|
|
1635
|
+
let producedState;
|
|
1636
|
+
const merge = (_next = next) => {
|
|
1637
|
+
if (_next !== next) internal.validateState?.(_next);
|
|
1638
|
+
assertKnownStateShape(_next, internal.rootState, internal.stateSchema, store.isSliceStore);
|
|
1639
|
+
mergeObject(internal.rootState, _next, store.isSliceStore);
|
|
1640
|
+
};
|
|
1641
|
+
const fn = typeof next === "function" ? () => {
|
|
1642
|
+
const returnValue = next(internal.module);
|
|
1643
|
+
if (returnValue instanceof Promise) {
|
|
1644
|
+
returnValue.catch(() => void 0);
|
|
1645
|
+
throw new Error("setState with async function is not supported");
|
|
1646
|
+
}
|
|
1647
|
+
if (typeof returnValue === "object" && returnValue !== null) merge(returnValue);
|
|
1648
|
+
} : merge;
|
|
1649
|
+
if (!(Boolean(store.transport ?? options.enablePatches) || hasStoreCommitListeners(store)) && internal.mutableInstance) {
|
|
1650
|
+
if (internal.actMutable) {
|
|
1651
|
+
internal.actMutable(() => {
|
|
1652
|
+
fn.apply(null);
|
|
1653
|
+
});
|
|
1654
|
+
defaultResultValidated = true;
|
|
1655
|
+
return [];
|
|
1656
|
+
}
|
|
1657
|
+
fn.apply(null);
|
|
1658
|
+
defaultResultValidated = true;
|
|
1659
|
+
return [];
|
|
1660
|
+
}
|
|
1661
|
+
internal.backupState = internal.rootState;
|
|
1662
|
+
let patches;
|
|
1663
|
+
let inversePatches;
|
|
1664
|
+
try {
|
|
1665
|
+
const result = (0, mutative.create)(internal.rootState, (draft) => {
|
|
1666
|
+
internal.rootState = draft;
|
|
1667
|
+
return fn.apply(null);
|
|
1668
|
+
}, { enablePatches: true });
|
|
1669
|
+
assertKnownStateShape(result[0], internal.backupState, internal.stateSchema, store.isSliceStore, { requireSliceRoots: true });
|
|
1670
|
+
internal.validateState?.(internal.getTransportState?.() ?? result[0]);
|
|
1671
|
+
producedState = result[0];
|
|
1672
|
+
patches = result[1];
|
|
1673
|
+
inversePatches = result[2];
|
|
1674
|
+
} finally {
|
|
1675
|
+
internal.rootState = internal.backupState;
|
|
1676
|
+
}
|
|
1677
|
+
const patch = store.patch;
|
|
1678
|
+
const finalPatches = patch ? patch({
|
|
1679
|
+
patches,
|
|
1680
|
+
inversePatches
|
|
1681
|
+
}) : {
|
|
1682
|
+
patches,
|
|
1683
|
+
inversePatches
|
|
1684
|
+
};
|
|
1685
|
+
if (!patch) internal.validatePatches?.(finalPatches.patches);
|
|
1686
|
+
const safePatches = sanitizeCheckedPatches(finalPatches.patches, "store.patch()");
|
|
1687
|
+
const safeInversePatches = sanitizeCheckedPatches(finalPatches.inversePatches, "store.patch() inverse patches");
|
|
1688
|
+
if (producedState && safePatches.some((patch) => typeof patch.value === "object" && patch.value !== null) && prepareStoreCommit(store, {
|
|
1689
|
+
state: producedState,
|
|
1690
|
+
patches: safePatches,
|
|
1691
|
+
inversePatches: safeInversePatches,
|
|
1692
|
+
source: "setState"
|
|
1693
|
+
})) {
|
|
1694
|
+
runWithStoreCommitSource(store, "setState", () => {
|
|
1695
|
+
store.apply(producedState);
|
|
1696
|
+
});
|
|
1697
|
+
defaultResultValidated = true;
|
|
1698
|
+
return [];
|
|
1699
|
+
}
|
|
1700
|
+
if (safePatches.length) {
|
|
1701
|
+
defaultResultValidated = internal.applyValidatedPatches?.(internal.rootState, safePatches, !patch) ?? false;
|
|
1702
|
+
if (!internal.applyValidatedPatches) store.apply(internal.rootState, safePatches);
|
|
1703
|
+
} else defaultResultValidated = true;
|
|
1704
|
+
return [
|
|
1705
|
+
internal.rootState,
|
|
1706
|
+
safePatches,
|
|
1707
|
+
safeInversePatches
|
|
1708
|
+
];
|
|
1709
|
+
};
|
|
1710
|
+
const setState = (next, updater = defaultUpdater) => {
|
|
1711
|
+
const commitSource = pendingCommitSource ?? "setState";
|
|
1712
|
+
pendingCommitSource = void 0;
|
|
1713
|
+
internal.assertAlive?.("setState");
|
|
1714
|
+
internal.assertMutationAllowed?.("setState");
|
|
1715
|
+
if (store.share === "client") throw new Error(`setState() cannot be called in the client store. To update the state, please trigger a store method with setState() instead.`);
|
|
1716
|
+
if (internal.isBatching) throw new Error("setState cannot be called within the updater");
|
|
1717
|
+
if (next === null) return [];
|
|
1718
|
+
if (typeof next === "object") {
|
|
1719
|
+
internal.validateState?.(next);
|
|
1720
|
+
assertKnownStateShape(next, internal.rootState, internal.stateSchema, store.isSliceStore);
|
|
1721
|
+
}
|
|
1722
|
+
internal.isBatching = true;
|
|
1723
|
+
if (!store.share && !options.enablePatches && !hasStoreCommitListeners(store) && !internal.mutableInstance && updater === defaultUpdater) try {
|
|
1724
|
+
if (typeof next === "function") try {
|
|
1725
|
+
internal.backupState = internal.rootState;
|
|
1726
|
+
const snapshotCache = internal.computedSnapshotCache;
|
|
1727
|
+
const snapshotSources = internal.computedIdentityRequired ? internal.computedSnapshotSources : void 0;
|
|
1728
|
+
const snapshot = snapshotCache?.get(internal.rootState);
|
|
1729
|
+
const updateSnapshot = Boolean(snapshot && snapshotCache);
|
|
1730
|
+
const produced = (0, mutative.create)(internal.rootState, (draft) => {
|
|
1731
|
+
internal.rootState = draft;
|
|
1732
|
+
const returnValue = next(internal.module);
|
|
1733
|
+
if (returnValue instanceof Promise) {
|
|
1734
|
+
returnValue.catch(() => void 0);
|
|
1735
|
+
throw new Error("setState with async function is not supported");
|
|
1736
|
+
}
|
|
1737
|
+
if (typeof returnValue === "object" && returnValue !== null) {
|
|
1738
|
+
assertKnownStateShape(returnValue, internal.rootState, internal.stateSchema, store.isSliceStore);
|
|
1739
|
+
mergeObject(internal.rootState, returnValue, store.isSliceStore);
|
|
1740
|
+
}
|
|
1741
|
+
}, { enablePatches: updateSnapshot });
|
|
1742
|
+
const nextState = updateSnapshot ? produced[0] : produced;
|
|
1743
|
+
assertKnownStateShape(nextState, internal.backupState, internal.stateSchema, store.isSliceStore, { requireSliceRoots: true });
|
|
1744
|
+
if (updateSnapshot) {
|
|
1745
|
+
const patches = produced[1];
|
|
1746
|
+
finalizeImmutableStateSnapshot(nextState, (0, mutative.apply)(snapshot, createImmutableSnapshotPatches(patches, snapshotCache)), patches, snapshotCache, snapshotSources);
|
|
1747
|
+
}
|
|
1748
|
+
internal.rootState = nextState;
|
|
1749
|
+
} catch (error) {
|
|
1750
|
+
internal.rootState = internal.backupState;
|
|
1751
|
+
throw error;
|
|
1752
|
+
}
|
|
1753
|
+
else {
|
|
1754
|
+
const copy = cloneOwnEnumerable(internal.rootState);
|
|
1755
|
+
if (store.isSliceStore) {
|
|
1756
|
+
const nextRecord = next;
|
|
1757
|
+
const copyRecord = copy;
|
|
1758
|
+
for (const key of getOwnEnumerableKeys(nextRecord)) {
|
|
1759
|
+
if (!Object.prototype.hasOwnProperty.call(copyRecord, key)) continue;
|
|
1760
|
+
const sourceValue = nextRecord[key];
|
|
1761
|
+
if (typeof sourceValue !== "object" || sourceValue === null) continue;
|
|
1762
|
+
const targetValue = copyRecord[key];
|
|
1763
|
+
if (typeof targetValue !== "object" || targetValue === null) continue;
|
|
1764
|
+
const sliceCopy = cloneOwnEnumerable(targetValue);
|
|
1765
|
+
mergeObject(sliceCopy, sourceValue);
|
|
1766
|
+
setOwnEnumerable(copyRecord, key, sliceCopy);
|
|
1767
|
+
}
|
|
1768
|
+
} else mergeObject(copy, next);
|
|
1769
|
+
assertKnownStateShape(copy, internal.rootState, internal.stateSchema, store.isSliceStore, { requireSliceRoots: true });
|
|
1770
|
+
internal.rootState = copy;
|
|
1771
|
+
}
|
|
1772
|
+
refreshSignalSlots(internal);
|
|
1773
|
+
if (internal.updateImmutable) internal.updateImmutable(internal.rootState);
|
|
1774
|
+
else internal.listeners.forEach((listener) => listener());
|
|
1775
|
+
return [];
|
|
1776
|
+
} finally {
|
|
1777
|
+
internal.isBatching = false;
|
|
1778
|
+
}
|
|
1779
|
+
let result;
|
|
1780
|
+
try {
|
|
1781
|
+
const isDrafted = internal.mutableInstance && (0, mutative.isDraft)(internal.rootState);
|
|
1782
|
+
if (isDrafted) handleDraft(store, internal);
|
|
1783
|
+
result = updater(next);
|
|
1784
|
+
if (internal.mutableInstance) assertKnownStateShape(internal.rootState, internal.backupState ?? internal.rootState, internal.stateSchema, store.isSliceStore, { requireSliceRoots: true });
|
|
1785
|
+
if (!(updater === defaultUpdater && defaultResultValidated)) internal.validateState?.(internal.getTransportState?.() ?? internal.rootState);
|
|
1786
|
+
if (isDrafted) {
|
|
1787
|
+
internal.backupState = internal.rootState;
|
|
1788
|
+
const [draft, finalize] = (0, mutative.create)(internal.rootState, { enablePatches: true });
|
|
1789
|
+
internal.finalizeDraft = finalize;
|
|
1790
|
+
internal.rootState = draft;
|
|
1791
|
+
}
|
|
1792
|
+
} finally {
|
|
1793
|
+
internal.isBatching = false;
|
|
1794
|
+
}
|
|
1795
|
+
const trustedDefaultResult = updater === defaultUpdater && defaultResultValidated;
|
|
1796
|
+
if (result?.length && !trustedDefaultResult) {
|
|
1797
|
+
internal.validatePatches?.(result[1]);
|
|
1798
|
+
result = [
|
|
1799
|
+
result[0],
|
|
1800
|
+
sanitizeCheckedPatches(result[1], "setState updater result"),
|
|
1801
|
+
sanitizeCheckedPatches(result[2], "setState updater inverse result")
|
|
1802
|
+
];
|
|
1803
|
+
}
|
|
1804
|
+
if (result?.length === 3) {
|
|
1805
|
+
const [, patches, inversePatches] = result;
|
|
1806
|
+
internal.emitPatches?.(patches);
|
|
1807
|
+
if (patches.length || inversePatches.length) publishStoreCommit(store, {
|
|
1808
|
+
state: internal.rootState,
|
|
1809
|
+
patches,
|
|
1810
|
+
inversePatches,
|
|
1811
|
+
source: commitSource
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
return result;
|
|
1815
|
+
};
|
|
1816
|
+
const replayPatches = ({ patches, inversePatches }, replaySetState = setState) => {
|
|
1817
|
+
const previousSource = pendingCommitSource;
|
|
1818
|
+
pendingCommitSource = "replay";
|
|
1819
|
+
try {
|
|
1820
|
+
replaySetState(internal.rootState, () => {
|
|
1821
|
+
const inputPatches = patches.map((patch) => ({
|
|
1822
|
+
...patch,
|
|
1823
|
+
path: Array.isArray(patch.path) ? [...patch.path] : patch.path
|
|
1824
|
+
}));
|
|
1825
|
+
const inputInversePatches = inversePatches.map((patch) => ({
|
|
1826
|
+
...patch,
|
|
1827
|
+
path: Array.isArray(patch.path) ? [...patch.path] : patch.path
|
|
1828
|
+
}));
|
|
1829
|
+
const finalPatches = store.patch ? store.patch({
|
|
1830
|
+
patches: inputPatches,
|
|
1831
|
+
inversePatches: inputInversePatches
|
|
1832
|
+
}) : {
|
|
1833
|
+
patches: inputPatches,
|
|
1834
|
+
inversePatches: inputInversePatches
|
|
1835
|
+
};
|
|
1836
|
+
const safePatches = sanitizeCheckedPatches(finalPatches.patches, "store.patch()");
|
|
1837
|
+
const safeInversePatches = sanitizeCheckedPatches(finalPatches.inversePatches, "store.patch() inverse patches");
|
|
1838
|
+
if (safePatches.length) {
|
|
1839
|
+
internal.applyValidatedPatches?.(internal.rootState, safePatches, false);
|
|
1840
|
+
if (!internal.applyValidatedPatches) store.apply(internal.rootState, safePatches);
|
|
1841
|
+
}
|
|
1842
|
+
return [
|
|
1843
|
+
internal.rootState,
|
|
1844
|
+
safePatches,
|
|
1845
|
+
safeInversePatches
|
|
1846
|
+
];
|
|
1847
|
+
});
|
|
1848
|
+
return internal.rootState;
|
|
1849
|
+
} finally {
|
|
1850
|
+
pendingCommitSource = previousSource;
|
|
1851
|
+
}
|
|
1852
|
+
};
|
|
1853
|
+
const getState = (deps, selector) => deps && selector ? new Computed(deps, selector) : internal.module;
|
|
1854
|
+
return {
|
|
1855
|
+
setState,
|
|
1856
|
+
getState,
|
|
1857
|
+
replayPatches
|
|
1858
|
+
};
|
|
1859
|
+
};
|
|
1860
|
+
//#endregion
|
|
1861
|
+
//#region packages/core/src/storeFactory.ts
|
|
1862
|
+
const namespaceMap = /* @__PURE__ */ new Map();
|
|
1863
|
+
let hasWarnedAmbiguousFunctionMap = false;
|
|
1864
|
+
const warnAmbiguousFunctionMap = () => {
|
|
1865
|
+
if (hasWarnedAmbiguousFunctionMap || process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test") return;
|
|
1866
|
+
hasWarnedAmbiguousFunctionMap = true;
|
|
1867
|
+
console.warn([
|
|
1868
|
+
`sliceMode: 'auto' inferred slices from an object of functions.`,
|
|
1869
|
+
`This shape is ambiguous with a single store that only contains methods.`,
|
|
1870
|
+
`Use create({ ping() {} }, { sliceMode: 'single' }) for a plain method store,`,
|
|
1871
|
+
`or create({ counter: (set) => ({ count: 0 }) }, { sliceMode: 'slices' }) for slices.`
|
|
1872
|
+
].join(" "));
|
|
1873
|
+
};
|
|
1874
|
+
const createStore = (createState, options, runtime = {}) => {
|
|
1875
|
+
const { share, validatePatches, validateReplacementSource, validateState } = runtime;
|
|
1876
|
+
const store = {};
|
|
1877
|
+
const internal = {
|
|
1878
|
+
sequence: 0,
|
|
1879
|
+
isBatching: false,
|
|
1880
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
1881
|
+
destroyCallbacks: /* @__PURE__ */ new Set(),
|
|
1882
|
+
validatePatches,
|
|
1883
|
+
validateReplacementSource,
|
|
1884
|
+
validateState
|
|
1885
|
+
};
|
|
1886
|
+
internal.notifyStateChange = () => {
|
|
1887
|
+
refreshSignalSlots(internal);
|
|
1888
|
+
internal.listeners.forEach((listener) => listener());
|
|
1889
|
+
};
|
|
1890
|
+
const name = options.name ?? "default";
|
|
1891
|
+
const shouldTrackName = share === "main" && process.env.NODE_ENV !== "test";
|
|
1892
|
+
const releaseStoreName = () => {
|
|
1893
|
+
if (shouldTrackName) namespaceMap.delete(name);
|
|
1894
|
+
};
|
|
1895
|
+
if (shouldTrackName) {
|
|
1896
|
+
if (namespaceMap.get(name)) throw new Error(`Store name '${name}' is not unique.`);
|
|
1897
|
+
namespaceMap.set(name, true);
|
|
1898
|
+
}
|
|
1899
|
+
try {
|
|
1900
|
+
const { setState, getState, replayPatches } = handleState(store, internal, options);
|
|
1901
|
+
const subscribe = (listener) => {
|
|
1902
|
+
internal.assertAlive?.("subscribe");
|
|
1903
|
+
internal.listeners.add(listener);
|
|
1904
|
+
return () => internal.listeners.delete(listener);
|
|
1905
|
+
};
|
|
1906
|
+
let isDestroyed = false;
|
|
1907
|
+
internal.assertAlive = (operation) => {
|
|
1908
|
+
if (isDestroyed) throw new Error(`${operation} cannot be called after store.destroy().`);
|
|
1909
|
+
};
|
|
1910
|
+
const destroy = () => {
|
|
1911
|
+
if (isDestroyed) return;
|
|
1912
|
+
isDestroyed = true;
|
|
1913
|
+
let firstError;
|
|
1914
|
+
const callbacks = [...internal.destroyCallbacks ?? []];
|
|
1915
|
+
internal.destroyCallbacks?.clear();
|
|
1916
|
+
for (const callback of callbacks) try {
|
|
1917
|
+
callback();
|
|
1918
|
+
} catch (error) {
|
|
1919
|
+
firstError ??= error;
|
|
1920
|
+
}
|
|
1921
|
+
internal.listeners.clear();
|
|
1922
|
+
disposeStoreCommitRuntime(store);
|
|
1923
|
+
try {
|
|
1924
|
+
store.transport?.dispose();
|
|
1925
|
+
} catch (error) {
|
|
1926
|
+
firstError ??= error;
|
|
1927
|
+
} finally {
|
|
1928
|
+
releaseStoreName();
|
|
1929
|
+
}
|
|
1930
|
+
if (firstError) throw firstError;
|
|
1931
|
+
};
|
|
1932
|
+
const applyState = (state, patches, prepared = false, skipFinalValidation = false) => {
|
|
1933
|
+
internal.assertAlive?.("apply");
|
|
1934
|
+
internal.assertMutationAllowed?.("apply");
|
|
1935
|
+
if (patches && !prepared) validatePatches?.(patches);
|
|
1936
|
+
if (!prepared) assertSafePatches(patches, "store.apply()");
|
|
1937
|
+
const safePatches = prepared ? patches : sanitizePatches(patches);
|
|
1938
|
+
const baseState = state === internal.module ? internal.rootState : state;
|
|
1939
|
+
if (baseState !== internal.rootState) validateReplacementSource?.(baseState);
|
|
1940
|
+
const appliedState = safePatches ? (0, mutative.apply)(baseState, safePatches) : baseState;
|
|
1941
|
+
const nextState = prepared ? appliedState : sanitizeReplacementState(appliedState);
|
|
1942
|
+
if (!skipFinalValidation) {
|
|
1943
|
+
assertKnownStateShape(nextState, internal.rootState, internal.stateSchema, store.isSliceStore, { requireSliceRoots: true });
|
|
1944
|
+
validateState?.(internal.getTransportState?.() ?? nextState);
|
|
1945
|
+
}
|
|
1946
|
+
internal.rootState = nextState;
|
|
1947
|
+
refreshSignalSlots(internal);
|
|
1948
|
+
if (internal.updateImmutable) internal.updateImmutable(internal.rootState);
|
|
1949
|
+
else internal.listeners.forEach((listener) => listener());
|
|
1950
|
+
};
|
|
1951
|
+
const apply = (state = internal.rootState, patches) => {
|
|
1952
|
+
const observeReplacement = patches === void 0 && hasStoreCommitListeners(store);
|
|
1953
|
+
const previousState = internal.rootState;
|
|
1954
|
+
applyState(state, patches);
|
|
1955
|
+
if (!observeReplacement) return;
|
|
1956
|
+
const replacement = createRootReplacementPatches(previousState, internal.rootState);
|
|
1957
|
+
const safePatches = sanitizeCheckedPatches(replacement.patches, "store.apply() replacement");
|
|
1958
|
+
const safeInversePatches = sanitizeCheckedPatches(replacement.inversePatches, "store.apply() replacement inverse patches");
|
|
1959
|
+
if (!safePatches.length && !safeInversePatches.length) return;
|
|
1960
|
+
internal.emitPatches?.(safePatches);
|
|
1961
|
+
publishStoreCommit(store, {
|
|
1962
|
+
state: internal.rootState,
|
|
1963
|
+
patches: safePatches,
|
|
1964
|
+
inversePatches: safeInversePatches,
|
|
1965
|
+
source: getStoreCommitSource(store, "external")
|
|
1966
|
+
});
|
|
1967
|
+
};
|
|
1968
|
+
internal.applyValidatedPatches = (state, patches, skipFinalValidation) => {
|
|
1969
|
+
if (store.apply !== apply) {
|
|
1970
|
+
store.apply(state, patches);
|
|
1971
|
+
return false;
|
|
1972
|
+
}
|
|
1973
|
+
applyState(state, patches, true, skipFinalValidation);
|
|
1974
|
+
return true;
|
|
1975
|
+
};
|
|
1976
|
+
const getPureState = () => internal.rootState;
|
|
1977
|
+
const isFunctionMapObject = () => {
|
|
1978
|
+
if (typeof createState !== "object" || createState === null) return false;
|
|
1979
|
+
const values = getOwnEnumerableKeys(createState).map((key) => createState[key]);
|
|
1980
|
+
return values.length > 0 && values.every((value) => typeof value === "function");
|
|
1981
|
+
};
|
|
1982
|
+
const getIsSliceStore = () => {
|
|
1983
|
+
const sliceMode = options.sliceMode ?? "auto";
|
|
1984
|
+
if (sliceMode === "single") return false;
|
|
1985
|
+
if (sliceMode === "slices") {
|
|
1986
|
+
if (!isFunctionMapObject()) throw new Error(`sliceMode: 'slices' requires createState to be an object of slice functions.`);
|
|
1987
|
+
return true;
|
|
1988
|
+
}
|
|
1989
|
+
if (isFunctionMapObject()) {
|
|
1990
|
+
warnAmbiguousFunctionMap();
|
|
1991
|
+
return true;
|
|
1992
|
+
}
|
|
1993
|
+
return false;
|
|
1994
|
+
};
|
|
1995
|
+
const isSliceStore = getIsSliceStore();
|
|
1996
|
+
Object.assign(store, {
|
|
1997
|
+
name,
|
|
1998
|
+
share: share ?? false,
|
|
1999
|
+
setState,
|
|
2000
|
+
getState,
|
|
2001
|
+
subscribe,
|
|
2002
|
+
destroy,
|
|
2003
|
+
apply,
|
|
2004
|
+
isSliceStore,
|
|
2005
|
+
getPureState
|
|
2006
|
+
});
|
|
2007
|
+
const middlewareStore = applyMiddlewares(store, options.middlewares ?? []);
|
|
2008
|
+
if (middlewareStore !== store) Object.assign(store, middlewareStore);
|
|
2009
|
+
registerStorePatchReplayer(store, replayPatches);
|
|
2010
|
+
internal.assertAlive?.("store initialization");
|
|
2011
|
+
if (validatePatches && store.patch) {
|
|
2012
|
+
const patch = store.patch.bind(store);
|
|
2013
|
+
store.patch = (options) => {
|
|
2014
|
+
const result = patch(options);
|
|
2015
|
+
validatePatches(result.patches);
|
|
2016
|
+
return result;
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
const initialState = getInitialState(store, createState, internal);
|
|
2020
|
+
internal.assertAlive?.("store initialization");
|
|
2021
|
+
internal.sharedActionPaths = runtime.collectActionPaths?.(initialState, store.isSliceStore);
|
|
2022
|
+
if (!internal.getTransportState) runtime.validateInitialState?.(initialState, store.isSliceStore);
|
|
2023
|
+
store.getInitialState = () => initialState;
|
|
2024
|
+
internal.rootState = getRawState(store, internal, initialState, options, runtime.clientAction);
|
|
2025
|
+
if (validatePatches && store.apply !== apply) {
|
|
2026
|
+
const applyWithAdapter = store.apply.bind(store);
|
|
2027
|
+
store.apply = (state, patches) => {
|
|
2028
|
+
internal.assertAlive?.("apply");
|
|
2029
|
+
internal.assertMutationAllowed?.("apply");
|
|
2030
|
+
if (typeof state !== "undefined" && state !== internal.rootState && state !== internal.module) validateReplacementSource?.(state);
|
|
2031
|
+
if (patches) {
|
|
2032
|
+
validatePatches(patches);
|
|
2033
|
+
assertSafePatches(patches, "store.apply()");
|
|
2034
|
+
}
|
|
2035
|
+
applyWithAdapter(state, patches);
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
internal.stateSchema = createStateSchema(internal.rootState, store.isSliceStore);
|
|
2039
|
+
validateState?.(internal.getTransportState?.() ?? internal.rootState);
|
|
2040
|
+
return {
|
|
2041
|
+
store,
|
|
2042
|
+
internal
|
|
2043
|
+
};
|
|
2044
|
+
} catch (error) {
|
|
2045
|
+
try {
|
|
2046
|
+
store.destroy?.();
|
|
2047
|
+
} catch (destroyError) {
|
|
2048
|
+
if (process.env.NODE_ENV === "development") console.error(destroyError);
|
|
2049
|
+
}
|
|
2050
|
+
releaseStoreName();
|
|
2051
|
+
throw error;
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
//#endregion
|
|
2055
|
+
//#region packages/core/src/create.ts
|
|
2056
|
+
const isMainWorkerType = (workerType) => workerType === "SharedWorkerInternal" || workerType === "WebWorkerInternal";
|
|
2057
|
+
const isClientWorkerType = (workerType) => workerType === "SharedWorkerClient" || workerType === "WebWorkerClient";
|
|
2058
|
+
const validateCreateModeOptions = (options) => {
|
|
2059
|
+
const storeTransport = options.transport;
|
|
2060
|
+
const clientTransport = options.clientTransport;
|
|
2061
|
+
const worker = options.worker;
|
|
2062
|
+
const explicitWorkerType = options.workerType;
|
|
2063
|
+
if (storeTransport && clientTransport) throw new Error("transport and clientTransport cannot be used together, please use one authority model per store.");
|
|
2064
|
+
if (storeTransport && worker) throw new Error("transport and worker cannot be used together, please use one authority model per store.");
|
|
2065
|
+
if (clientTransport && worker) throw new Error("clientTransport and worker cannot be used together, please use one client transport source.");
|
|
2066
|
+
if (isMainWorkerType(explicitWorkerType) && (clientTransport || worker)) throw new Error("main workerType cannot be combined with client transport settings.");
|
|
2067
|
+
if (isClientWorkerType(explicitWorkerType) && storeTransport) throw new Error("client workerType cannot be combined with transport.");
|
|
2068
|
+
};
|
|
2069
|
+
/**
|
|
2070
|
+
* Create a local store, the main side of a shared store, or a client mirror of
|
|
2071
|
+
* a shared store.
|
|
2072
|
+
*
|
|
2073
|
+
* @remarks
|
|
2074
|
+
* Prefer the static `coaction/local` entry when transport support is not
|
|
2075
|
+
* required. It excludes the JSON protocol and reconnect runtime from the
|
|
2076
|
+
* consumer dependency graph.
|
|
2077
|
+
*/
|
|
2078
|
+
const create = (createState, options = {}) => {
|
|
2079
|
+
const checkEnablePatches = Object.hasOwnProperty.call(options, "enablePatches") && !options.enablePatches;
|
|
2080
|
+
validateCreateModeOptions(options);
|
|
2081
|
+
const workerType = options.workerType ?? WorkerType;
|
|
2082
|
+
const storeTransport = options.transport;
|
|
2083
|
+
const share = isMainWorkerType(workerType) || storeTransport ? "main" : void 0;
|
|
2084
|
+
const buildStore = ({ share }) => createStore(createState, options, {
|
|
2085
|
+
share,
|
|
2086
|
+
clientAction: share === "client" ? createClientAction : void 0,
|
|
2087
|
+
collectActionPaths: share === "main" ? validateSharedActionPaths : void 0,
|
|
2088
|
+
validateInitialState: share ? validateSharedInitialState : void 0,
|
|
2089
|
+
validatePatches: share === "main" ? validateUpdatePatches : void 0,
|
|
2090
|
+
validateReplacementSource: share ? validateSharedReplacementSource : void 0,
|
|
2091
|
+
validateState: share ? validateSharedStateSerializable : void 0
|
|
2092
|
+
});
|
|
2093
|
+
if (options.clientTransport || options.worker || isClientWorkerType(options.workerType)) {
|
|
2094
|
+
if (checkEnablePatches) throw new Error("enablePatches: true is required for the async store");
|
|
2095
|
+
return wrapStore(createAsyncClientStore(buildStore, options));
|
|
2096
|
+
}
|
|
2097
|
+
if (share === "main" && checkEnablePatches) throw new Error("enablePatches: true is required for the transport");
|
|
2098
|
+
let builtStore;
|
|
2099
|
+
try {
|
|
2100
|
+
builtStore = buildStore({ share });
|
|
2101
|
+
} catch (error) {
|
|
2102
|
+
return failTransportInitialization(storeTransport, error);
|
|
2103
|
+
}
|
|
2104
|
+
const { store, internal } = builtStore;
|
|
2105
|
+
try {
|
|
2106
|
+
handleMainTransport(store, internal, storeTransport, workerType, checkEnablePatches, options.transportPolicy);
|
|
2107
|
+
markStoreReady(store);
|
|
2108
|
+
internal.assertAlive?.("store initialization");
|
|
2109
|
+
} catch (error) {
|
|
2110
|
+
return failStoreSetup(store, error);
|
|
2111
|
+
}
|
|
2112
|
+
return wrapStore(store);
|
|
2113
|
+
};
|
|
2114
|
+
//#endregion
|
|
2115
|
+
exports.ActionAuthorityChangedError = ActionAuthorityChangedError;
|
|
2116
|
+
exports.StateSchemaError = StateSchemaError;
|
|
2117
|
+
exports.UnsafePatchPathError = UnsafePatchPathError;
|
|
2118
|
+
exports.assertSafePatches = assertSafePatches;
|
|
2119
|
+
Object.defineProperty(exports, "computed", {
|
|
2120
|
+
enumerable: true,
|
|
2121
|
+
get: function() {
|
|
2122
|
+
return alien_signals.computed;
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
2125
|
+
exports.create = create;
|
|
2126
|
+
Object.defineProperty(exports, "effect", {
|
|
2127
|
+
enumerable: true,
|
|
2128
|
+
get: function() {
|
|
2129
|
+
return alien_signals.effect;
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
Object.defineProperty(exports, "effectScope", {
|
|
2133
|
+
enumerable: true,
|
|
2134
|
+
get: function() {
|
|
2135
|
+
return alien_signals.effectScope;
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
Object.defineProperty(exports, "endBatch", {
|
|
2139
|
+
enumerable: true,
|
|
2140
|
+
get: function() {
|
|
2141
|
+
return alien_signals.endBatch;
|
|
2142
|
+
}
|
|
2143
|
+
});
|
|
2144
|
+
Object.defineProperty(exports, "isComputed", {
|
|
2145
|
+
enumerable: true,
|
|
2146
|
+
get: function() {
|
|
2147
|
+
return alien_signals.isComputed;
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2150
|
+
Object.defineProperty(exports, "isEffect", {
|
|
2151
|
+
enumerable: true,
|
|
2152
|
+
get: function() {
|
|
2153
|
+
return alien_signals.isEffect;
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
Object.defineProperty(exports, "isEffectScope", {
|
|
2157
|
+
enumerable: true,
|
|
2158
|
+
get: function() {
|
|
2159
|
+
return alien_signals.isEffectScope;
|
|
2160
|
+
}
|
|
2161
|
+
});
|
|
2162
|
+
Object.defineProperty(exports, "isSignal", {
|
|
2163
|
+
enumerable: true,
|
|
2164
|
+
get: function() {
|
|
2165
|
+
return alien_signals.isSignal;
|
|
2166
|
+
}
|
|
2167
|
+
});
|
|
2168
|
+
exports.isStateSchemaError = isStateSchemaError;
|
|
2169
|
+
exports.onStoreReady = onStoreReady;
|
|
2170
|
+
exports.sanitizeInitialStateValue = sanitizeInitialStateValue;
|
|
2171
|
+
exports.sanitizePatches = sanitizePatches;
|
|
2172
|
+
exports.sanitizeReplacementState = sanitizeReplacementState;
|
|
2173
|
+
Object.defineProperty(exports, "signal", {
|
|
2174
|
+
enumerable: true,
|
|
2175
|
+
get: function() {
|
|
2176
|
+
return alien_signals.signal;
|
|
2177
|
+
}
|
|
2178
|
+
});
|
|
2179
|
+
Object.defineProperty(exports, "startBatch", {
|
|
2180
|
+
enumerable: true,
|
|
2181
|
+
get: function() {
|
|
2182
|
+
return alien_signals.startBatch;
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
Object.defineProperty(exports, "trigger", {
|
|
2186
|
+
enumerable: true,
|
|
2187
|
+
get: function() {
|
|
2188
|
+
return alien_signals.trigger;
|
|
2189
|
+
}
|
|
2190
|
+
});
|