coaction 1.4.1 → 1.5.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.d.mts CHANGED
@@ -67,7 +67,7 @@ interface Store<T extends ISlices = ISlices> {
67
67
  *
68
68
  * @remarks
69
69
  * Pass a deep-partial object to merge fields, or pass an updater to edit a
70
- * Mutative draft. Client-side shared stores intentionally reject direct
70
+ * Mutative draft. Passing `null` is a no-op. Client-side shared stores intentionally reject direct
71
71
  * `setState()` calls; trigger a store method instead.
72
72
  */
73
73
  setState: (
@@ -375,6 +375,8 @@ type Creator = {
375
375
  * - When `clientTransport` or `worker` is provided, returned store methods
376
376
  * become promise-returning methods because execution happens on the main
377
377
  * shared store.
378
+ * - New semantics should prefer explicit helpers or variants over adding more
379
+ * ambiguous `create()` input forms.
378
380
  */
379
381
  declare const create: Creator;
380
382
 
package/dist/index.d.ts CHANGED
@@ -67,7 +67,7 @@ interface Store<T extends ISlices = ISlices> {
67
67
  *
68
68
  * @remarks
69
69
  * Pass a deep-partial object to merge fields, or pass an updater to edit a
70
- * Mutative draft. Client-side shared stores intentionally reject direct
70
+ * Mutative draft. Passing `null` is a no-op. Client-side shared stores intentionally reject direct
71
71
  * `setState()` calls; trigger a store method instead.
72
72
  */
73
73
  setState: (
@@ -375,6 +375,8 @@ type Creator = {
375
375
  * - When `clientTransport` or `worker` is provided, returned store methods
376
376
  * become promise-returning methods because execution happens on the main
377
377
  * shared store.
378
+ * - New semantics should prefer explicit helpers or variants over adding more
379
+ * ambiguous `create()` input forms.
378
380
  */
379
381
  declare const create: Creator;
380
382
 
package/dist/index.js CHANGED
@@ -255,14 +255,22 @@ var isEqual = (x, y) => {
255
255
  return x !== x && y !== y;
256
256
  };
257
257
  var isUnsafeKey = (key) => key === "__proto__" || key === "prototype" || key === "constructor";
258
+ var setOwnEnumerable = (target, key, value) => {
259
+ if (typeof key === "string" && isUnsafeKey(key)) {
260
+ return;
261
+ }
262
+ target[key] = value;
263
+ };
258
264
  var assignOwnEnumerable = (target, source) => {
259
265
  for (const key of Object.keys(source)) {
260
- if (isUnsafeKey(key)) {
261
- continue;
262
- }
263
- target[key] = source[key];
266
+ setOwnEnumerable(target, key, source[key]);
264
267
  }
265
268
  };
269
+ var cloneOwnEnumerable = (source) => {
270
+ const target = {};
271
+ assignOwnEnumerable(target, source);
272
+ return target;
273
+ };
266
274
  var areShallowEqualWithArray = (prev, next) => {
267
275
  if (prev === null || next === null || prev.length !== next.length) {
268
276
  return false;
@@ -354,80 +362,101 @@ var createClientAction = ({
354
362
  });
355
363
  };
356
364
  }
357
- const keys = sliceKey ? [sliceKey, key] : [key];
358
- return store.transport.emit("execute", keys, args).then(async (response) => {
359
- const result = Array.isArray(response) ? response[0] : response;
360
- const sequence = Array.isArray(response) ? typeof response[1] === "number" ? response[1] : internal.sequence : internal.sequence;
361
- if (internal.sequence < sequence) {
362
- if (process.env.NODE_ENV === "development") {
363
- console.warn(
364
- `The sequence of the action is not consistent.`,
365
- sequence,
366
- internal.sequence
365
+ const traceAction = (run) => {
366
+ try {
367
+ const result = run();
368
+ if (result instanceof Promise) {
369
+ return result.then(
370
+ (value) => {
371
+ done?.(value);
372
+ return value;
373
+ },
374
+ (error) => {
375
+ done?.(error);
376
+ throw error;
377
+ }
367
378
  );
368
379
  }
369
- await new Promise((resolve, reject) => {
370
- let settled = false;
371
- let unsubscribe = () => {
372
- };
373
- const timeoutRef = {};
374
- const cleanup = () => {
375
- unsubscribe();
376
- if (typeof timeoutRef.current !== "undefined") {
377
- clearTimeout(timeoutRef.current);
378
- }
379
- };
380
- const finishResolve = () => {
381
- if (settled) return;
382
- settled = true;
383
- cleanup();
384
- resolve();
385
- };
386
- const finishReject = (error) => {
387
- if (settled) return;
388
- settled = true;
389
- cleanup();
390
- reject(error);
391
- };
392
- unsubscribe = store.subscribe(() => {
393
- if (internal.sequence >= sequence) {
394
- finishResolve();
395
- }
396
- });
397
- timeoutRef.current = setTimeout(() => {
398
- void store.transport.emit("fullSync").then((latest) => {
399
- const next = latest;
400
- if (typeof next.state !== "string" || typeof next.sequence !== "number") {
401
- throw new Error("Invalid fullSync payload");
380
+ done?.(result);
381
+ return result;
382
+ } catch (error) {
383
+ done?.(error);
384
+ throw error;
385
+ }
386
+ };
387
+ const keys = sliceKey ? [sliceKey, key] : [key];
388
+ return traceAction(
389
+ () => store.transport.emit("execute", keys, args).then(async (response) => {
390
+ const result = Array.isArray(response) ? response[0] : response;
391
+ const sequence = Array.isArray(response) ? typeof response[1] === "number" ? response[1] : internal.sequence : internal.sequence;
392
+ if (internal.sequence < sequence) {
393
+ if (process.env.NODE_ENV === "development") {
394
+ console.warn(
395
+ `The sequence of the action is not consistent.`,
396
+ sequence,
397
+ internal.sequence
398
+ );
399
+ }
400
+ await new Promise((resolve, reject) => {
401
+ let settled = false;
402
+ let unsubscribe = () => {
403
+ };
404
+ const timeoutRef = {};
405
+ const cleanup = () => {
406
+ unsubscribe();
407
+ if (typeof timeoutRef.current !== "undefined") {
408
+ clearTimeout(timeoutRef.current);
402
409
  }
403
- store.apply(JSON.parse(next.state));
404
- internal.sequence = next.sequence;
410
+ };
411
+ const finishResolve = () => {
412
+ if (settled) return;
413
+ settled = true;
414
+ cleanup();
415
+ resolve();
416
+ };
417
+ const finishReject = (error) => {
418
+ if (settled) return;
419
+ settled = true;
420
+ cleanup();
421
+ reject(error);
422
+ };
423
+ unsubscribe = store.subscribe(() => {
405
424
  if (internal.sequence >= sequence) {
406
425
  finishResolve();
407
- return;
408
426
  }
409
- finishReject(
410
- new Error(
411
- `Stale fullSync sequence: expected >= ${sequence}, got ${internal.sequence}`
412
- )
413
- );
414
- }).catch((error) => {
415
- finishReject(error);
416
427
  });
417
- }, clientExecuteSyncTimeoutMs);
418
- });
419
- }
420
- if (isTransportErrorEnvelope(result)) {
421
- done?.(result);
422
- throw new Error(result.message);
423
- }
424
- if (isLegacyTransportErrorEnvelope(result)) {
425
- done?.(result);
426
- throw new Error(result.$$Error);
427
- }
428
- done?.(result);
429
- return result;
430
- });
428
+ timeoutRef.current = setTimeout(() => {
429
+ void store.transport.emit("fullSync").then((latest) => {
430
+ const next = latest;
431
+ if (typeof next.state !== "string" || typeof next.sequence !== "number") {
432
+ throw new Error("Invalid fullSync payload");
433
+ }
434
+ if (next.sequence >= sequence) {
435
+ store.apply(JSON.parse(next.state));
436
+ internal.sequence = next.sequence;
437
+ finishResolve();
438
+ return;
439
+ }
440
+ finishReject(
441
+ new Error(
442
+ `Stale fullSync sequence: expected >= ${sequence}, got ${next.sequence}`
443
+ )
444
+ );
445
+ }).catch((error) => {
446
+ finishReject(error);
447
+ });
448
+ }, clientExecuteSyncTimeoutMs);
449
+ });
450
+ }
451
+ if (isTransportErrorEnvelope(result)) {
452
+ throw new Error(result.message);
453
+ }
454
+ if (isLegacyTransportErrorEnvelope(result)) {
455
+ throw new Error(result.$$Error);
456
+ }
457
+ return result;
458
+ })
459
+ );
431
460
  };
432
461
  };
433
462
 
@@ -455,71 +484,94 @@ var createLocalAction = ({
455
484
  id: actionId,
456
485
  sliceKey
457
486
  });
458
- done = (result2) => {
487
+ done = (result) => {
459
488
  store.trace({
460
489
  method: key,
461
490
  id: actionId,
462
- result: result2,
491
+ result,
463
492
  sliceKey
464
493
  });
465
494
  };
466
495
  }
467
- const enablePatches = store.transport ?? options.enablePatches;
468
- if (internal.mutableInstance && !internal.isBatching && enablePatches) {
469
- let result2;
470
- const handleResult = (isDrafted2) => {
471
- handleDraft(store, internal);
472
- if (isDrafted2) {
473
- internal.backupState = internal.rootState;
474
- const [draft2, finalize2] = (0, import_mutative.create)(internal.rootState, {
475
- enablePatches: true
476
- });
477
- internal.finalizeDraft = finalize2;
478
- internal.rootState = draft2;
496
+ const traceAction = (run) => {
497
+ try {
498
+ const result = run();
499
+ if (result instanceof Promise) {
500
+ return result.then(
501
+ (value) => {
502
+ done?.(value);
503
+ return value;
504
+ },
505
+ (error) => {
506
+ done?.(error);
507
+ throw error;
508
+ }
509
+ );
479
510
  }
480
- };
481
- const isDrafted = (0, import_mutative.isDraft)(internal.rootState);
482
- if (isDrafted) {
483
- handleResult();
511
+ done?.(result);
512
+ return result;
513
+ } catch (error) {
514
+ done?.(error);
515
+ throw error;
484
516
  }
485
- internal.backupState = internal.rootState;
486
- const [draft, finalize] = (0, import_mutative.create)(internal.rootState, {
487
- enablePatches: true
488
- });
489
- internal.finalizeDraft = finalize;
490
- internal.rootState = draft;
491
- let asyncResult;
492
- try {
493
- result2 = fn.apply(getActionTarget(store, sliceKey), args);
494
- if (result2 instanceof Promise) {
495
- asyncResult = result2;
517
+ };
518
+ const enablePatches = store.transport ?? options.enablePatches;
519
+ return traceAction(() => {
520
+ if (internal.mutableInstance && !internal.isBatching && enablePatches) {
521
+ let result;
522
+ const handleResult = (isDrafted2) => {
523
+ handleDraft(store, internal);
524
+ if (isDrafted2) {
525
+ internal.backupState = internal.rootState;
526
+ const [draft2, finalize2] = (0, import_mutative.create)(internal.rootState, {
527
+ enablePatches: true
528
+ });
529
+ internal.finalizeDraft = finalize2;
530
+ internal.rootState = draft2;
531
+ }
532
+ };
533
+ const isDrafted = (0, import_mutative.isDraft)(internal.rootState);
534
+ if (isDrafted) {
535
+ handleResult();
536
+ }
537
+ internal.backupState = internal.rootState;
538
+ const [draft, finalize] = (0, import_mutative.create)(internal.rootState, {
539
+ enablePatches: true
540
+ });
541
+ internal.finalizeDraft = finalize;
542
+ internal.rootState = draft;
543
+ let asyncResult;
544
+ try {
545
+ result = fn.apply(getActionTarget(store, sliceKey), args);
546
+ if (result instanceof Promise) {
547
+ asyncResult = result;
548
+ }
549
+ } finally {
550
+ if (!asyncResult) {
551
+ handleResult(isDrafted);
552
+ }
496
553
  }
497
- } finally {
498
554
  if (asyncResult) {
499
- } else {
500
- handleResult(isDrafted);
555
+ return asyncResult.then(
556
+ (value) => {
557
+ handleResult(isDrafted);
558
+ return value;
559
+ },
560
+ (error) => {
561
+ handleResult(isDrafted);
562
+ throw error;
563
+ }
564
+ );
501
565
  }
566
+ return result;
502
567
  }
503
- if (asyncResult) {
504
- return asyncResult.finally(() => {
505
- const result3 = handleResult(isDrafted);
506
- done?.(result3);
507
- return result3;
568
+ if (internal.mutableInstance && internal.actMutable) {
569
+ return internal.actMutable(() => {
570
+ return fn.apply(getActionTarget(store, sliceKey), args);
508
571
  });
509
572
  }
510
- done?.(result2);
511
- return result2;
512
- }
513
- if (internal.mutableInstance && internal.actMutable) {
514
- const result2 = internal.actMutable(() => {
515
- return fn.apply(getActionTarget(store, sliceKey), args);
516
- });
517
- done?.(result2);
518
- return result2;
519
- }
520
- const result = fn.apply(getActionTarget(store, sliceKey), args);
521
- done?.(result);
522
- return result;
573
+ return fn.apply(getActionTarget(store, sliceKey), args);
574
+ });
523
575
  };
524
576
  };
525
577
 
@@ -574,7 +626,7 @@ var prepareStateDescriptor = ({
574
626
  enumerable: true
575
627
  });
576
628
  } else if (!isComputed) {
577
- rawState[key] = descriptor.value;
629
+ setOwnEnumerable(rawState, key, descriptor.value);
578
630
  }
579
631
  if (isComputed) {
580
632
  if (internal.mutableInstance) {
@@ -629,8 +681,19 @@ var getRawState = (store, internal, initialState, options) => {
629
681
  const rawState = {};
630
682
  const handle = (_rawState, _initialState, sliceKey) => {
631
683
  internal.mutableInstance = internal.toMutableRaw?.(_initialState);
684
+ const safeDescriptors = {};
632
685
  const descriptors = Object.getOwnPropertyDescriptors(_initialState);
633
- Object.entries(descriptors).forEach(([key, descriptor]) => {
686
+ Reflect.ownKeys(descriptors).forEach((key) => {
687
+ if (typeof key === "string" && isUnsafeKey(key)) {
688
+ return;
689
+ }
690
+ safeDescriptors[key] = Reflect.get(descriptors, key);
691
+ });
692
+ Reflect.ownKeys(safeDescriptors).forEach((key) => {
693
+ const descriptor = safeDescriptors[key];
694
+ if (typeof descriptor === "undefined") {
695
+ return;
696
+ }
634
697
  if (Object.prototype.hasOwnProperty.call(descriptor, "value")) {
635
698
  if (typeof descriptor.value !== "function") {
636
699
  prepareStateDescriptor({
@@ -640,7 +703,12 @@ var getRawState = (store, internal, initialState, options) => {
640
703
  rawState: _rawState,
641
704
  sliceKey
642
705
  });
643
- } else if (store.share === "client") {
706
+ return;
707
+ }
708
+ if (typeof key !== "string") {
709
+ return;
710
+ }
711
+ if (store.share === "client") {
644
712
  descriptor.value = createClientAction({
645
713
  clientExecuteSyncTimeoutMs,
646
714
  internal,
@@ -660,14 +728,22 @@ var getRawState = (store, internal, initialState, options) => {
660
728
  }
661
729
  }
662
730
  });
663
- const slice = Object.defineProperties({}, descriptors);
731
+ const slice = Object.defineProperties({}, safeDescriptors);
664
732
  return slice;
665
733
  };
666
734
  if (store.isSliceStore) {
667
735
  internal.module = {};
668
736
  Object.entries(initialState).forEach(([key, value]) => {
669
- rawState[key] = {};
670
- internal.module[key] = handle(rawState[key], value, key);
737
+ if (isUnsafeKey(key)) {
738
+ return;
739
+ }
740
+ const sliceRawState = {};
741
+ setOwnEnumerable(rawState, key, sliceRawState);
742
+ setOwnEnumerable(
743
+ internal.module,
744
+ key,
745
+ handle(sliceRawState, value, key)
746
+ );
671
747
  });
672
748
  } else {
673
749
  internal.module = handle(rawState, initialState);
@@ -726,13 +802,6 @@ var handleState = (store, internal, options) => {
726
802
  const finalPatches = store.patch ? store.patch({ patches, inversePatches }) : { patches, inversePatches };
727
803
  if (finalPatches.patches.length) {
728
804
  store.apply(internal.rootState, finalPatches.patches);
729
- if (!internal.mutableInstance) {
730
- if (internal.updateImmutable) {
731
- internal.updateImmutable(internal.rootState);
732
- } else {
733
- internal.listeners.forEach((listener) => listener());
734
- }
735
- }
736
805
  }
737
806
  return [
738
807
  internal.rootState,
@@ -748,6 +817,9 @@ var handleState = (store, internal, options) => {
748
817
  if (internal.isBatching) {
749
818
  throw new Error("setState cannot be called within the updater");
750
819
  }
820
+ if (next === null) {
821
+ return [];
822
+ }
751
823
  internal.isBatching = true;
752
824
  if (!store.share && !options.enablePatches && !internal.mutableInstance) {
753
825
  if (typeof next === "function") {
@@ -766,13 +838,28 @@ var handleState = (store, internal, options) => {
766
838
  throw error;
767
839
  }
768
840
  } else {
769
- const copy = {};
770
- const rootState = internal.rootState;
771
- for (const key of Object.keys(rootState)) {
772
- copy[key] = rootState[key];
773
- }
774
- for (const key of Object.keys(next)) {
775
- copy[key] = next[key];
841
+ const copy = cloneOwnEnumerable(internal.rootState);
842
+ if (store.isSliceStore) {
843
+ for (const key of Object.keys(next)) {
844
+ if (!Object.prototype.hasOwnProperty.call(copy, key)) {
845
+ continue;
846
+ }
847
+ const sourceValue = next[key];
848
+ if (typeof sourceValue !== "object" || sourceValue === null) {
849
+ continue;
850
+ }
851
+ const targetValue = copy[key];
852
+ if (typeof targetValue !== "object" || targetValue === null) {
853
+ continue;
854
+ }
855
+ const sliceCopy = cloneOwnEnumerable(
856
+ targetValue
857
+ );
858
+ mergeObject(sliceCopy, sourceValue);
859
+ setOwnEnumerable(copy, key, sliceCopy);
860
+ }
861
+ } else {
862
+ mergeObject(copy, next);
776
863
  }
777
864
  internal.rootState = copy;
778
865
  }
@@ -900,6 +987,37 @@ var handleMainTransport = (store, internal, storeTransport, workerType, checkEna
900
987
  // src/create.ts
901
988
  var namespaceMap = /* @__PURE__ */ new Map();
902
989
  var hasWarnedAmbiguousFunctionMap = false;
990
+ var isMainWorkerType = (workerType) => workerType === "SharedWorkerInternal" || workerType === "WebWorkerInternal";
991
+ var isClientWorkerType = (workerType) => workerType === "SharedWorkerClient" || workerType === "WebWorkerClient";
992
+ var validateCreateModeOptions = (options) => {
993
+ const storeTransport = options.transport;
994
+ const clientTransport = options.clientTransport;
995
+ const worker = options.worker;
996
+ const explicitWorkerType = options.workerType;
997
+ if (storeTransport && clientTransport) {
998
+ throw new Error(
999
+ "transport and clientTransport cannot be used together, please use one authority model per store."
1000
+ );
1001
+ }
1002
+ if (storeTransport && worker) {
1003
+ throw new Error(
1004
+ "transport and worker cannot be used together, please use one authority model per store."
1005
+ );
1006
+ }
1007
+ if (clientTransport && worker) {
1008
+ throw new Error(
1009
+ "clientTransport and worker cannot be used together, please use one client transport source."
1010
+ );
1011
+ }
1012
+ if (isMainWorkerType(explicitWorkerType) && (clientTransport || worker)) {
1013
+ throw new Error(
1014
+ "main workerType cannot be combined with client transport settings."
1015
+ );
1016
+ }
1017
+ if (isClientWorkerType(explicitWorkerType) && storeTransport) {
1018
+ throw new Error("client workerType cannot be combined with transport.");
1019
+ }
1020
+ };
903
1021
  var warnAmbiguousFunctionMap = () => {
904
1022
  if (hasWarnedAmbiguousFunctionMap || process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test") {
905
1023
  return;
@@ -916,12 +1034,8 @@ var warnAmbiguousFunctionMap = () => {
916
1034
  };
917
1035
  var create = (createState, options = {}) => {
918
1036
  const checkEnablePatches = Object.hasOwnProperty.call(options, "enablePatches") && !options.enablePatches;
1037
+ validateCreateModeOptions(options);
919
1038
  const workerType = options.workerType ?? WorkerType;
920
- if (process.env.NODE_ENV === "development" && options.transport && options.clientTransport) {
921
- throw new Error(
922
- `transport and clientTransport cannot be used together, please use one of them.`
923
- );
924
- }
925
1039
  const storeTransport = options.transport;
926
1040
  const share = workerType === "WebWorkerInternal" || workerType === "SharedWorkerInternal" || storeTransport ? "main" : void 0;
927
1041
  const createStore = ({ share: share2 }) => {
package/dist/index.mjs CHANGED
@@ -227,14 +227,22 @@ var isEqual = (x, y) => {
227
227
  return x !== x && y !== y;
228
228
  };
229
229
  var isUnsafeKey = (key) => key === "__proto__" || key === "prototype" || key === "constructor";
230
+ var setOwnEnumerable = (target, key, value) => {
231
+ if (typeof key === "string" && isUnsafeKey(key)) {
232
+ return;
233
+ }
234
+ target[key] = value;
235
+ };
230
236
  var assignOwnEnumerable = (target, source) => {
231
237
  for (const key of Object.keys(source)) {
232
- if (isUnsafeKey(key)) {
233
- continue;
234
- }
235
- target[key] = source[key];
238
+ setOwnEnumerable(target, key, source[key]);
236
239
  }
237
240
  };
241
+ var cloneOwnEnumerable = (source) => {
242
+ const target = {};
243
+ assignOwnEnumerable(target, source);
244
+ return target;
245
+ };
238
246
  var areShallowEqualWithArray = (prev, next) => {
239
247
  if (prev === null || next === null || prev.length !== next.length) {
240
248
  return false;
@@ -326,80 +334,101 @@ var createClientAction = ({
326
334
  });
327
335
  };
328
336
  }
329
- const keys = sliceKey ? [sliceKey, key] : [key];
330
- return store.transport.emit("execute", keys, args).then(async (response) => {
331
- const result = Array.isArray(response) ? response[0] : response;
332
- const sequence = Array.isArray(response) ? typeof response[1] === "number" ? response[1] : internal.sequence : internal.sequence;
333
- if (internal.sequence < sequence) {
334
- if (process.env.NODE_ENV === "development") {
335
- console.warn(
336
- `The sequence of the action is not consistent.`,
337
- sequence,
338
- internal.sequence
337
+ const traceAction = (run) => {
338
+ try {
339
+ const result = run();
340
+ if (result instanceof Promise) {
341
+ return result.then(
342
+ (value) => {
343
+ done?.(value);
344
+ return value;
345
+ },
346
+ (error) => {
347
+ done?.(error);
348
+ throw error;
349
+ }
339
350
  );
340
351
  }
341
- await new Promise((resolve, reject) => {
342
- let settled = false;
343
- let unsubscribe = () => {
344
- };
345
- const timeoutRef = {};
346
- const cleanup = () => {
347
- unsubscribe();
348
- if (typeof timeoutRef.current !== "undefined") {
349
- clearTimeout(timeoutRef.current);
350
- }
351
- };
352
- const finishResolve = () => {
353
- if (settled) return;
354
- settled = true;
355
- cleanup();
356
- resolve();
357
- };
358
- const finishReject = (error) => {
359
- if (settled) return;
360
- settled = true;
361
- cleanup();
362
- reject(error);
363
- };
364
- unsubscribe = store.subscribe(() => {
365
- if (internal.sequence >= sequence) {
366
- finishResolve();
367
- }
368
- });
369
- timeoutRef.current = setTimeout(() => {
370
- void store.transport.emit("fullSync").then((latest) => {
371
- const next = latest;
372
- if (typeof next.state !== "string" || typeof next.sequence !== "number") {
373
- throw new Error("Invalid fullSync payload");
352
+ done?.(result);
353
+ return result;
354
+ } catch (error) {
355
+ done?.(error);
356
+ throw error;
357
+ }
358
+ };
359
+ const keys = sliceKey ? [sliceKey, key] : [key];
360
+ return traceAction(
361
+ () => store.transport.emit("execute", keys, args).then(async (response) => {
362
+ const result = Array.isArray(response) ? response[0] : response;
363
+ const sequence = Array.isArray(response) ? typeof response[1] === "number" ? response[1] : internal.sequence : internal.sequence;
364
+ if (internal.sequence < sequence) {
365
+ if (process.env.NODE_ENV === "development") {
366
+ console.warn(
367
+ `The sequence of the action is not consistent.`,
368
+ sequence,
369
+ internal.sequence
370
+ );
371
+ }
372
+ await new Promise((resolve, reject) => {
373
+ let settled = false;
374
+ let unsubscribe = () => {
375
+ };
376
+ const timeoutRef = {};
377
+ const cleanup = () => {
378
+ unsubscribe();
379
+ if (typeof timeoutRef.current !== "undefined") {
380
+ clearTimeout(timeoutRef.current);
374
381
  }
375
- store.apply(JSON.parse(next.state));
376
- internal.sequence = next.sequence;
382
+ };
383
+ const finishResolve = () => {
384
+ if (settled) return;
385
+ settled = true;
386
+ cleanup();
387
+ resolve();
388
+ };
389
+ const finishReject = (error) => {
390
+ if (settled) return;
391
+ settled = true;
392
+ cleanup();
393
+ reject(error);
394
+ };
395
+ unsubscribe = store.subscribe(() => {
377
396
  if (internal.sequence >= sequence) {
378
397
  finishResolve();
379
- return;
380
398
  }
381
- finishReject(
382
- new Error(
383
- `Stale fullSync sequence: expected >= ${sequence}, got ${internal.sequence}`
384
- )
385
- );
386
- }).catch((error) => {
387
- finishReject(error);
388
399
  });
389
- }, clientExecuteSyncTimeoutMs);
390
- });
391
- }
392
- if (isTransportErrorEnvelope(result)) {
393
- done?.(result);
394
- throw new Error(result.message);
395
- }
396
- if (isLegacyTransportErrorEnvelope(result)) {
397
- done?.(result);
398
- throw new Error(result.$$Error);
399
- }
400
- done?.(result);
401
- return result;
402
- });
400
+ timeoutRef.current = setTimeout(() => {
401
+ void store.transport.emit("fullSync").then((latest) => {
402
+ const next = latest;
403
+ if (typeof next.state !== "string" || typeof next.sequence !== "number") {
404
+ throw new Error("Invalid fullSync payload");
405
+ }
406
+ if (next.sequence >= sequence) {
407
+ store.apply(JSON.parse(next.state));
408
+ internal.sequence = next.sequence;
409
+ finishResolve();
410
+ return;
411
+ }
412
+ finishReject(
413
+ new Error(
414
+ `Stale fullSync sequence: expected >= ${sequence}, got ${next.sequence}`
415
+ )
416
+ );
417
+ }).catch((error) => {
418
+ finishReject(error);
419
+ });
420
+ }, clientExecuteSyncTimeoutMs);
421
+ });
422
+ }
423
+ if (isTransportErrorEnvelope(result)) {
424
+ throw new Error(result.message);
425
+ }
426
+ if (isLegacyTransportErrorEnvelope(result)) {
427
+ throw new Error(result.$$Error);
428
+ }
429
+ return result;
430
+ })
431
+ );
403
432
  };
404
433
  };
405
434
 
@@ -430,71 +459,94 @@ var createLocalAction = ({
430
459
  id: actionId,
431
460
  sliceKey
432
461
  });
433
- done = (result2) => {
462
+ done = (result) => {
434
463
  store.trace({
435
464
  method: key,
436
465
  id: actionId,
437
- result: result2,
466
+ result,
438
467
  sliceKey
439
468
  });
440
469
  };
441
470
  }
442
- const enablePatches = store.transport ?? options.enablePatches;
443
- if (internal.mutableInstance && !internal.isBatching && enablePatches) {
444
- let result2;
445
- const handleResult = (isDrafted2) => {
446
- handleDraft(store, internal);
447
- if (isDrafted2) {
448
- internal.backupState = internal.rootState;
449
- const [draft2, finalize2] = createWithMutative(internal.rootState, {
450
- enablePatches: true
451
- });
452
- internal.finalizeDraft = finalize2;
453
- internal.rootState = draft2;
471
+ const traceAction = (run) => {
472
+ try {
473
+ const result = run();
474
+ if (result instanceof Promise) {
475
+ return result.then(
476
+ (value) => {
477
+ done?.(value);
478
+ return value;
479
+ },
480
+ (error) => {
481
+ done?.(error);
482
+ throw error;
483
+ }
484
+ );
454
485
  }
455
- };
456
- const isDrafted = isDraft(internal.rootState);
457
- if (isDrafted) {
458
- handleResult();
486
+ done?.(result);
487
+ return result;
488
+ } catch (error) {
489
+ done?.(error);
490
+ throw error;
459
491
  }
460
- internal.backupState = internal.rootState;
461
- const [draft, finalize] = createWithMutative(internal.rootState, {
462
- enablePatches: true
463
- });
464
- internal.finalizeDraft = finalize;
465
- internal.rootState = draft;
466
- let asyncResult;
467
- try {
468
- result2 = fn.apply(getActionTarget(store, sliceKey), args);
469
- if (result2 instanceof Promise) {
470
- asyncResult = result2;
492
+ };
493
+ const enablePatches = store.transport ?? options.enablePatches;
494
+ return traceAction(() => {
495
+ if (internal.mutableInstance && !internal.isBatching && enablePatches) {
496
+ let result;
497
+ const handleResult = (isDrafted2) => {
498
+ handleDraft(store, internal);
499
+ if (isDrafted2) {
500
+ internal.backupState = internal.rootState;
501
+ const [draft2, finalize2] = createWithMutative(internal.rootState, {
502
+ enablePatches: true
503
+ });
504
+ internal.finalizeDraft = finalize2;
505
+ internal.rootState = draft2;
506
+ }
507
+ };
508
+ const isDrafted = isDraft(internal.rootState);
509
+ if (isDrafted) {
510
+ handleResult();
511
+ }
512
+ internal.backupState = internal.rootState;
513
+ const [draft, finalize] = createWithMutative(internal.rootState, {
514
+ enablePatches: true
515
+ });
516
+ internal.finalizeDraft = finalize;
517
+ internal.rootState = draft;
518
+ let asyncResult;
519
+ try {
520
+ result = fn.apply(getActionTarget(store, sliceKey), args);
521
+ if (result instanceof Promise) {
522
+ asyncResult = result;
523
+ }
524
+ } finally {
525
+ if (!asyncResult) {
526
+ handleResult(isDrafted);
527
+ }
471
528
  }
472
- } finally {
473
529
  if (asyncResult) {
474
- } else {
475
- handleResult(isDrafted);
530
+ return asyncResult.then(
531
+ (value) => {
532
+ handleResult(isDrafted);
533
+ return value;
534
+ },
535
+ (error) => {
536
+ handleResult(isDrafted);
537
+ throw error;
538
+ }
539
+ );
476
540
  }
541
+ return result;
477
542
  }
478
- if (asyncResult) {
479
- return asyncResult.finally(() => {
480
- const result3 = handleResult(isDrafted);
481
- done?.(result3);
482
- return result3;
543
+ if (internal.mutableInstance && internal.actMutable) {
544
+ return internal.actMutable(() => {
545
+ return fn.apply(getActionTarget(store, sliceKey), args);
483
546
  });
484
547
  }
485
- done?.(result2);
486
- return result2;
487
- }
488
- if (internal.mutableInstance && internal.actMutable) {
489
- const result2 = internal.actMutable(() => {
490
- return fn.apply(getActionTarget(store, sliceKey), args);
491
- });
492
- done?.(result2);
493
- return result2;
494
- }
495
- const result = fn.apply(getActionTarget(store, sliceKey), args);
496
- done?.(result);
497
- return result;
548
+ return fn.apply(getActionTarget(store, sliceKey), args);
549
+ });
498
550
  };
499
551
  };
500
552
 
@@ -549,7 +601,7 @@ var prepareStateDescriptor = ({
549
601
  enumerable: true
550
602
  });
551
603
  } else if (!isComputed) {
552
- rawState[key] = descriptor.value;
604
+ setOwnEnumerable(rawState, key, descriptor.value);
553
605
  }
554
606
  if (isComputed) {
555
607
  if (internal.mutableInstance) {
@@ -604,8 +656,19 @@ var getRawState = (store, internal, initialState, options) => {
604
656
  const rawState = {};
605
657
  const handle = (_rawState, _initialState, sliceKey) => {
606
658
  internal.mutableInstance = internal.toMutableRaw?.(_initialState);
659
+ const safeDescriptors = {};
607
660
  const descriptors = Object.getOwnPropertyDescriptors(_initialState);
608
- Object.entries(descriptors).forEach(([key, descriptor]) => {
661
+ Reflect.ownKeys(descriptors).forEach((key) => {
662
+ if (typeof key === "string" && isUnsafeKey(key)) {
663
+ return;
664
+ }
665
+ safeDescriptors[key] = Reflect.get(descriptors, key);
666
+ });
667
+ Reflect.ownKeys(safeDescriptors).forEach((key) => {
668
+ const descriptor = safeDescriptors[key];
669
+ if (typeof descriptor === "undefined") {
670
+ return;
671
+ }
609
672
  if (Object.prototype.hasOwnProperty.call(descriptor, "value")) {
610
673
  if (typeof descriptor.value !== "function") {
611
674
  prepareStateDescriptor({
@@ -615,7 +678,12 @@ var getRawState = (store, internal, initialState, options) => {
615
678
  rawState: _rawState,
616
679
  sliceKey
617
680
  });
618
- } else if (store.share === "client") {
681
+ return;
682
+ }
683
+ if (typeof key !== "string") {
684
+ return;
685
+ }
686
+ if (store.share === "client") {
619
687
  descriptor.value = createClientAction({
620
688
  clientExecuteSyncTimeoutMs,
621
689
  internal,
@@ -635,14 +703,22 @@ var getRawState = (store, internal, initialState, options) => {
635
703
  }
636
704
  }
637
705
  });
638
- const slice = Object.defineProperties({}, descriptors);
706
+ const slice = Object.defineProperties({}, safeDescriptors);
639
707
  return slice;
640
708
  };
641
709
  if (store.isSliceStore) {
642
710
  internal.module = {};
643
711
  Object.entries(initialState).forEach(([key, value]) => {
644
- rawState[key] = {};
645
- internal.module[key] = handle(rawState[key], value, key);
712
+ if (isUnsafeKey(key)) {
713
+ return;
714
+ }
715
+ const sliceRawState = {};
716
+ setOwnEnumerable(rawState, key, sliceRawState);
717
+ setOwnEnumerable(
718
+ internal.module,
719
+ key,
720
+ handle(sliceRawState, value, key)
721
+ );
646
722
  });
647
723
  } else {
648
724
  internal.module = handle(rawState, initialState);
@@ -704,13 +780,6 @@ var handleState = (store, internal, options) => {
704
780
  const finalPatches = store.patch ? store.patch({ patches, inversePatches }) : { patches, inversePatches };
705
781
  if (finalPatches.patches.length) {
706
782
  store.apply(internal.rootState, finalPatches.patches);
707
- if (!internal.mutableInstance) {
708
- if (internal.updateImmutable) {
709
- internal.updateImmutable(internal.rootState);
710
- } else {
711
- internal.listeners.forEach((listener) => listener());
712
- }
713
- }
714
783
  }
715
784
  return [
716
785
  internal.rootState,
@@ -726,6 +795,9 @@ var handleState = (store, internal, options) => {
726
795
  if (internal.isBatching) {
727
796
  throw new Error("setState cannot be called within the updater");
728
797
  }
798
+ if (next === null) {
799
+ return [];
800
+ }
729
801
  internal.isBatching = true;
730
802
  if (!store.share && !options.enablePatches && !internal.mutableInstance) {
731
803
  if (typeof next === "function") {
@@ -744,13 +816,28 @@ var handleState = (store, internal, options) => {
744
816
  throw error;
745
817
  }
746
818
  } else {
747
- const copy = {};
748
- const rootState = internal.rootState;
749
- for (const key of Object.keys(rootState)) {
750
- copy[key] = rootState[key];
751
- }
752
- for (const key of Object.keys(next)) {
753
- copy[key] = next[key];
819
+ const copy = cloneOwnEnumerable(internal.rootState);
820
+ if (store.isSliceStore) {
821
+ for (const key of Object.keys(next)) {
822
+ if (!Object.prototype.hasOwnProperty.call(copy, key)) {
823
+ continue;
824
+ }
825
+ const sourceValue = next[key];
826
+ if (typeof sourceValue !== "object" || sourceValue === null) {
827
+ continue;
828
+ }
829
+ const targetValue = copy[key];
830
+ if (typeof targetValue !== "object" || targetValue === null) {
831
+ continue;
832
+ }
833
+ const sliceCopy = cloneOwnEnumerable(
834
+ targetValue
835
+ );
836
+ mergeObject(sliceCopy, sourceValue);
837
+ setOwnEnumerable(copy, key, sliceCopy);
838
+ }
839
+ } else {
840
+ mergeObject(copy, next);
754
841
  }
755
842
  internal.rootState = copy;
756
843
  }
@@ -878,6 +965,37 @@ var handleMainTransport = (store, internal, storeTransport, workerType, checkEna
878
965
  // src/create.ts
879
966
  var namespaceMap = /* @__PURE__ */ new Map();
880
967
  var hasWarnedAmbiguousFunctionMap = false;
968
+ var isMainWorkerType = (workerType) => workerType === "SharedWorkerInternal" || workerType === "WebWorkerInternal";
969
+ var isClientWorkerType = (workerType) => workerType === "SharedWorkerClient" || workerType === "WebWorkerClient";
970
+ var validateCreateModeOptions = (options) => {
971
+ const storeTransport = options.transport;
972
+ const clientTransport = options.clientTransport;
973
+ const worker = options.worker;
974
+ const explicitWorkerType = options.workerType;
975
+ if (storeTransport && clientTransport) {
976
+ throw new Error(
977
+ "transport and clientTransport cannot be used together, please use one authority model per store."
978
+ );
979
+ }
980
+ if (storeTransport && worker) {
981
+ throw new Error(
982
+ "transport and worker cannot be used together, please use one authority model per store."
983
+ );
984
+ }
985
+ if (clientTransport && worker) {
986
+ throw new Error(
987
+ "clientTransport and worker cannot be used together, please use one client transport source."
988
+ );
989
+ }
990
+ if (isMainWorkerType(explicitWorkerType) && (clientTransport || worker)) {
991
+ throw new Error(
992
+ "main workerType cannot be combined with client transport settings."
993
+ );
994
+ }
995
+ if (isClientWorkerType(explicitWorkerType) && storeTransport) {
996
+ throw new Error("client workerType cannot be combined with transport.");
997
+ }
998
+ };
881
999
  var warnAmbiguousFunctionMap = () => {
882
1000
  if (hasWarnedAmbiguousFunctionMap || process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test") {
883
1001
  return;
@@ -894,12 +1012,8 @@ var warnAmbiguousFunctionMap = () => {
894
1012
  };
895
1013
  var create = (createState, options = {}) => {
896
1014
  const checkEnablePatches = Object.hasOwnProperty.call(options, "enablePatches") && !options.enablePatches;
1015
+ validateCreateModeOptions(options);
897
1016
  const workerType = options.workerType ?? WorkerType;
898
- if (process.env.NODE_ENV === "development" && options.transport && options.clientTransport) {
899
- throw new Error(
900
- `transport and clientTransport cannot be used together, please use one of them.`
901
- );
902
- }
903
1017
  const storeTransport = options.transport;
904
1018
  const share = workerType === "WebWorkerInternal" || workerType === "SharedWorkerInternal" || storeTransport ? "main" : void 0;
905
1019
  const createStore = ({ share: share2 }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coaction",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "A sleek JavaScript library designed for high-performance and multithreading web apps.",
5
5
  "keywords": [
6
6
  "coaction"