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