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