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