coaction 1.3.0 → 1.4.1

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.js CHANGED
@@ -247,9 +247,6 @@ var getInitialState = (store, createState, internal) => {
247
247
  ) : makeState(createState);
248
248
  };
249
249
 
250
- // src/getRawState.ts
251
- var import_mutative = require("mutative");
252
-
253
250
  // src/utils.ts
254
251
  var isEqual = (x, y) => {
255
252
  if (x === y) {
@@ -308,13 +305,224 @@ var uuid = () => {
308
305
  let timestamp = (/* @__PURE__ */ new Date()).getTime();
309
306
  const uuidTemplate = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
310
307
  const uuid2 = uuidTemplate.replace(/[xy]/g, (char) => {
311
- let randomNum = (timestamp + Math.random() * 16) % 16 | 0;
308
+ const randomNum = (timestamp + Math.random() * 16) % 16 | 0;
312
309
  timestamp = Math.floor(timestamp / 16);
313
310
  return (char === "x" ? randomNum : randomNum & 3 | 8).toString(16);
314
311
  });
315
312
  return uuid2;
316
313
  };
317
314
 
315
+ // src/getRawStateClientAction.ts
316
+ var transportErrorMarker = "__coactionTransportError__";
317
+ var isTransportErrorEnvelope = (value) => {
318
+ if (typeof value !== "object" || value === null) {
319
+ return false;
320
+ }
321
+ return value[transportErrorMarker] === true && typeof value.message === "string";
322
+ };
323
+ var isLegacyTransportErrorEnvelope = (value) => {
324
+ if (typeof value !== "object" || value === null) {
325
+ return false;
326
+ }
327
+ const candidate = value;
328
+ return typeof candidate.$$Error === "string" && candidate.$$Error.length > 0 && Object.keys(candidate).length === 1;
329
+ };
330
+ var createClientAction = ({
331
+ clientExecuteSyncTimeoutMs,
332
+ internal,
333
+ key,
334
+ store,
335
+ sliceKey
336
+ }) => {
337
+ return (...args) => {
338
+ let actionId;
339
+ let done;
340
+ if (store.trace) {
341
+ actionId = uuid();
342
+ store.trace({
343
+ method: key,
344
+ parameters: args,
345
+ id: actionId,
346
+ sliceKey
347
+ });
348
+ done = (result) => {
349
+ store.trace({
350
+ method: key,
351
+ id: actionId,
352
+ result,
353
+ sliceKey
354
+ });
355
+ };
356
+ }
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
367
+ );
368
+ }
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");
402
+ }
403
+ store.apply(JSON.parse(next.state));
404
+ internal.sequence = next.sequence;
405
+ if (internal.sequence >= sequence) {
406
+ finishResolve();
407
+ return;
408
+ }
409
+ finishReject(
410
+ new Error(
411
+ `Stale fullSync sequence: expected >= ${sequence}, got ${internal.sequence}`
412
+ )
413
+ );
414
+ }).catch((error) => {
415
+ finishReject(error);
416
+ });
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
+ });
431
+ };
432
+ };
433
+
434
+ // src/getRawStateLocalAction.ts
435
+ var import_mutative = require("mutative");
436
+ var getActionTarget = (store, sliceKey) => {
437
+ return sliceKey ? store.getState()[sliceKey] : store.getState();
438
+ };
439
+ var createLocalAction = ({
440
+ fn,
441
+ internal,
442
+ key,
443
+ options,
444
+ store,
445
+ sliceKey
446
+ }) => {
447
+ return (...args) => {
448
+ let actionId;
449
+ let done;
450
+ if (store.trace) {
451
+ actionId = uuid();
452
+ store.trace({
453
+ method: key,
454
+ parameters: args,
455
+ id: actionId,
456
+ sliceKey
457
+ });
458
+ done = (result2) => {
459
+ store.trace({
460
+ method: key,
461
+ id: actionId,
462
+ result: result2,
463
+ sliceKey
464
+ });
465
+ };
466
+ }
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;
479
+ }
480
+ };
481
+ const isDrafted = (0, import_mutative.isDraft)(internal.rootState);
482
+ if (isDrafted) {
483
+ handleResult();
484
+ }
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;
496
+ }
497
+ } finally {
498
+ if (asyncResult) {
499
+ } else {
500
+ handleResult(isDrafted);
501
+ }
502
+ }
503
+ if (asyncResult) {
504
+ return asyncResult.finally(() => {
505
+ const result3 = handleResult(isDrafted);
506
+ done?.(result3);
507
+ return result3;
508
+ });
509
+ }
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;
523
+ };
524
+ };
525
+
318
526
  // src/computed.ts
319
527
  var Computed = class {
320
528
  constructor(deps, fn) {
@@ -348,23 +556,76 @@ var createSelectorCreatorWithArray = (memoize = defaultMemoize) => {
348
556
  };
349
557
  var createSelectorWithArray = createSelectorCreatorWithArray();
350
558
 
351
- // src/getRawState.ts
352
- var clientExecuteSyncTimeoutMs = 1500;
353
- var transportErrorMarker = "__coactionTransportError__";
354
- var isTransportErrorEnvelope = (value) => {
355
- if (typeof value !== "object" || value === null) {
356
- return false;
559
+ // src/getRawStateStateProperty.ts
560
+ var prepareStateDescriptor = ({
561
+ descriptor,
562
+ internal,
563
+ key,
564
+ rawState,
565
+ sliceKey
566
+ }) => {
567
+ const isComputed = descriptor.value instanceof Computed;
568
+ if (internal.mutableInstance) {
569
+ Object.defineProperty(rawState, key, {
570
+ get: () => internal.mutableInstance[key],
571
+ set: (value) => {
572
+ internal.mutableInstance[key] = value;
573
+ },
574
+ enumerable: true
575
+ });
576
+ } else if (!isComputed) {
577
+ rawState[key] = descriptor.value;
357
578
  }
358
- return value[transportErrorMarker] === true && typeof value.message === "string";
579
+ if (isComputed) {
580
+ if (internal.mutableInstance) {
581
+ throw new Error("Computed is not supported with mutable instance");
582
+ }
583
+ const { deps, fn } = descriptor.value;
584
+ const depsCallbackSelector = createSelectorWithArray(
585
+ // the root state should be updated, and the computed property will be updated.
586
+ () => [internal.rootState],
587
+ () => {
588
+ return deps(internal.module);
589
+ }
590
+ );
591
+ const selector = createSelectorWithArray(
592
+ (that) => depsCallbackSelector.call(that),
593
+ fn
594
+ );
595
+ descriptor.get = function() {
596
+ return selector.call(this);
597
+ };
598
+ } else if (sliceKey) {
599
+ descriptor.get = () => internal.rootState[sliceKey][key];
600
+ descriptor.set = (value) => {
601
+ internal.rootState[sliceKey][key] = value;
602
+ };
603
+ } else {
604
+ descriptor.get = () => internal.rootState[key];
605
+ descriptor.set = (value) => {
606
+ internal.rootState[key] = value;
607
+ };
608
+ }
609
+ delete descriptor.value;
610
+ delete descriptor.writable;
359
611
  };
360
- var isLegacyTransportErrorEnvelope = (value) => {
361
- if (typeof value !== "object" || value === null) {
362
- return false;
612
+
613
+ // src/getRawState.ts
614
+ var defaultClientExecuteSyncTimeoutMs = 1500;
615
+ var getClientExecuteSyncTimeoutMs = (options) => {
616
+ const timeout = options.executeSyncTimeoutMs;
617
+ if (typeof timeout === "undefined") {
618
+ return defaultClientExecuteSyncTimeoutMs;
363
619
  }
364
- const candidate = value;
365
- return typeof candidate.$$Error === "string" && candidate.$$Error.length > 0 && Object.keys(candidate).length === 1;
620
+ if (!Number.isFinite(timeout) || timeout < 0) {
621
+ throw new Error(
622
+ "executeSyncTimeoutMs must be a finite number greater than or equal to 0"
623
+ );
624
+ }
625
+ return timeout;
366
626
  };
367
627
  var getRawState = (store, internal, initialState, options) => {
628
+ const clientExecuteSyncTimeoutMs = getClientExecuteSyncTimeoutMs(options);
368
629
  const rawState = {};
369
630
  const handle = (_rawState, _initialState, sliceKey) => {
370
631
  internal.mutableInstance = internal.toMutableRaw?.(_initialState);
@@ -372,234 +633,30 @@ var getRawState = (store, internal, initialState, options) => {
372
633
  Object.entries(descriptors).forEach(([key, descriptor]) => {
373
634
  if (Object.prototype.hasOwnProperty.call(descriptor, "value")) {
374
635
  if (typeof descriptor.value !== "function") {
375
- const isComputed = descriptor.value instanceof Computed;
376
- if (internal.mutableInstance) {
377
- Object.defineProperty(_rawState, key, {
378
- get: () => internal.mutableInstance[key],
379
- set: (value) => {
380
- internal.mutableInstance[key] = value;
381
- },
382
- enumerable: true
383
- });
384
- } else if (!isComputed) {
385
- _rawState[key] = descriptor.value;
386
- }
387
- if (isComputed) {
388
- if (internal.mutableInstance) {
389
- throw new Error(
390
- "Computed is not supported with mutable instance"
391
- );
392
- }
393
- const { deps, fn } = descriptor.value;
394
- const depsCallbackSelector = createSelectorWithArray(
395
- // the root state should be updated, and the computed property will be updated.
396
- () => [internal.rootState],
397
- () => {
398
- return deps(internal.module);
399
- }
400
- );
401
- const selector = createSelectorWithArray(
402
- (that) => depsCallbackSelector.call(that),
403
- fn
404
- );
405
- descriptor.get = function() {
406
- return selector.call(this);
407
- };
408
- } else {
409
- if (sliceKey) {
410
- descriptor.get = () => internal.rootState[sliceKey][key];
411
- descriptor.set = (value) => {
412
- internal.rootState[sliceKey][key] = value;
413
- };
414
- } else {
415
- descriptor.get = () => internal.rootState[key];
416
- descriptor.set = (value) => {
417
- internal.rootState[key] = value;
418
- };
419
- }
420
- }
421
- delete descriptor.value;
422
- delete descriptor.writable;
636
+ prepareStateDescriptor({
637
+ descriptor,
638
+ internal,
639
+ key,
640
+ rawState: _rawState,
641
+ sliceKey
642
+ });
423
643
  } else if (store.share === "client") {
424
- descriptor.value = (...args) => {
425
- let actionId;
426
- let done;
427
- if (store.trace) {
428
- actionId = uuid();
429
- store.trace({
430
- method: key,
431
- parameters: args,
432
- id: actionId,
433
- sliceKey
434
- });
435
- done = (result) => {
436
- store.trace({
437
- method: key,
438
- id: actionId,
439
- result,
440
- sliceKey
441
- });
442
- };
443
- }
444
- const keys = sliceKey ? [sliceKey, key] : [key];
445
- return store.transport.emit("execute", keys, args).then(async (response) => {
446
- const result = Array.isArray(response) ? response[0] : response;
447
- const sequence = Array.isArray(response) ? typeof response[1] === "number" ? response[1] : internal.sequence : internal.sequence;
448
- if (internal.sequence < sequence) {
449
- if (process.env.NODE_ENV === "development") {
450
- console.warn(
451
- `The sequence of the action is not consistent.`,
452
- sequence,
453
- internal.sequence
454
- );
455
- }
456
- await new Promise((resolve, reject) => {
457
- let settled = false;
458
- let unsubscribe = () => {
459
- };
460
- let timeoutId;
461
- const cleanup = () => {
462
- unsubscribe();
463
- if (typeof timeoutId !== "undefined") {
464
- clearTimeout(timeoutId);
465
- }
466
- };
467
- const finishResolve = () => {
468
- if (settled) return;
469
- settled = true;
470
- cleanup();
471
- resolve();
472
- };
473
- const finishReject = (error) => {
474
- if (settled) return;
475
- settled = true;
476
- cleanup();
477
- reject(error);
478
- };
479
- unsubscribe = store.subscribe(() => {
480
- if (internal.sequence >= sequence) {
481
- finishResolve();
482
- }
483
- });
484
- timeoutId = setTimeout(() => {
485
- void store.transport.emit("fullSync").then((latest) => {
486
- const next = latest;
487
- if (typeof next.state !== "string" || typeof next.sequence !== "number") {
488
- throw new Error("Invalid fullSync payload");
489
- }
490
- store.apply(JSON.parse(next.state));
491
- internal.sequence = next.sequence;
492
- if (internal.sequence >= sequence) {
493
- finishResolve();
494
- return;
495
- }
496
- finishReject(
497
- new Error(
498
- `Stale fullSync sequence: expected >= ${sequence}, got ${internal.sequence}`
499
- )
500
- );
501
- }).catch((error) => {
502
- finishReject(error);
503
- });
504
- }, clientExecuteSyncTimeoutMs);
505
- });
506
- }
507
- if (isTransportErrorEnvelope(result)) {
508
- done?.(result);
509
- throw new Error(result.message);
510
- }
511
- if (isLegacyTransportErrorEnvelope(result)) {
512
- done?.(result);
513
- throw new Error(result.$$Error);
514
- }
515
- done?.(result);
516
- return result;
517
- });
518
- };
644
+ descriptor.value = createClientAction({
645
+ clientExecuteSyncTimeoutMs,
646
+ internal,
647
+ key,
648
+ store,
649
+ sliceKey
650
+ });
519
651
  } else {
520
- const fn = descriptor.value;
521
- descriptor.value = (...args) => {
522
- let actionId;
523
- let done;
524
- if (store.trace) {
525
- actionId = uuid();
526
- store.trace({
527
- method: key,
528
- parameters: args,
529
- id: actionId,
530
- sliceKey
531
- });
532
- done = (result2) => {
533
- store.trace({
534
- method: key,
535
- id: actionId,
536
- result: result2,
537
- sliceKey
538
- });
539
- };
540
- }
541
- const enablePatches = store.transport ?? options.enablePatches;
542
- if (internal.mutableInstance && !internal.isBatching && enablePatches) {
543
- let result2;
544
- const handleResult = (isDrafted2) => {
545
- handleDraft(store, internal);
546
- if (isDrafted2) {
547
- internal.backupState = internal.rootState;
548
- const [draft2, finalize2] = (0, import_mutative.create)(
549
- internal.rootState,
550
- {
551
- enablePatches: true
552
- }
553
- );
554
- internal.finalizeDraft = finalize2;
555
- internal.rootState = draft2;
556
- }
557
- };
558
- const isDrafted = (0, import_mutative.isDraft)(internal.rootState);
559
- if (isDrafted) {
560
- handleResult();
561
- }
562
- internal.backupState = internal.rootState;
563
- const [draft, finalize] = (0, import_mutative.create)(internal.rootState, {
564
- enablePatches: true
565
- });
566
- internal.finalizeDraft = finalize;
567
- internal.rootState = draft;
568
- try {
569
- result2 = fn.apply(
570
- sliceKey ? store.getState()[sliceKey] : store.getState(),
571
- args
572
- );
573
- } finally {
574
- if (result2 instanceof Promise) {
575
- return result2.finally(() => {
576
- const result3 = handleResult(isDrafted);
577
- done?.(result3);
578
- return result3;
579
- });
580
- }
581
- handleResult(isDrafted);
582
- }
583
- done?.(result2);
584
- return result2;
585
- }
586
- if (internal.mutableInstance && internal.actMutable) {
587
- const result2 = internal.actMutable(() => {
588
- return fn.apply(
589
- sliceKey ? store.getState()[sliceKey] : store.getState(),
590
- args
591
- );
592
- });
593
- done?.(result2);
594
- return result2;
595
- }
596
- const result = fn.apply(
597
- sliceKey ? store.getState()[sliceKey] : store.getState(),
598
- args
599
- );
600
- done?.(result);
601
- return result;
602
- };
652
+ descriptor.value = createLocalAction({
653
+ fn: descriptor.value,
654
+ internal,
655
+ key,
656
+ options,
657
+ store,
658
+ sliceKey
659
+ });
603
660
  }
604
661
  }
605
662
  });
@@ -842,6 +899,21 @@ var handleMainTransport = (store, internal, storeTransport, workerType, checkEna
842
899
 
843
900
  // src/create.ts
844
901
  var namespaceMap = /* @__PURE__ */ new Map();
902
+ var hasWarnedAmbiguousFunctionMap = false;
903
+ var warnAmbiguousFunctionMap = () => {
904
+ if (hasWarnedAmbiguousFunctionMap || process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test") {
905
+ return;
906
+ }
907
+ hasWarnedAmbiguousFunctionMap = true;
908
+ console.warn(
909
+ [
910
+ `sliceMode: 'auto' inferred slices from an object of functions.`,
911
+ `This shape is ambiguous with a single store that only contains methods.`,
912
+ `Use create({ ping() {} }, { sliceMode: 'single' }) for a plain method store,`,
913
+ `or create({ counter: (set) => ({ count: 0 }) }, { sliceMode: 'slices' }) for slices.`
914
+ ].join(" ")
915
+ );
916
+ };
845
917
  var create = (createState, options = {}) => {
846
918
  const checkEnablePatches = Object.hasOwnProperty.call(options, "enablePatches") && !options.enablePatches;
847
919
  const workerType = options.workerType ?? WorkerType;
@@ -897,34 +969,31 @@ var create = (createState, options = {}) => {
897
969
  }
898
970
  };
899
971
  const getPureState = () => internal2.rootState;
900
- const inferSliceStore = () => {
972
+ const isFunctionMapObject = () => {
901
973
  if (typeof createState === "object" && createState !== null) {
902
974
  const values = Object.values(createState);
903
975
  return values.length > 0 && values.every((value) => typeof value === "function");
904
976
  }
905
977
  return false;
906
978
  };
907
- const isValidSliceObject = () => {
908
- if (typeof createState !== "object" || createState === null) {
909
- return false;
910
- }
911
- const values = Object.values(createState);
912
- return values.length > 0 && values.every((value) => typeof value === "function");
913
- };
914
979
  const getIsSliceStore = () => {
915
980
  const sliceMode = options.sliceMode ?? "auto";
916
981
  if (sliceMode === "single") {
917
982
  return false;
918
983
  }
919
984
  if (sliceMode === "slices") {
920
- if (!isValidSliceObject()) {
985
+ if (!isFunctionMapObject()) {
921
986
  throw new Error(
922
987
  `sliceMode: 'slices' requires createState to be an object of slice functions.`
923
988
  );
924
989
  }
925
990
  return true;
926
991
  }
927
- return inferSliceStore();
992
+ if (isFunctionMapObject()) {
993
+ warnAmbiguousFunctionMap();
994
+ return true;
995
+ }
996
+ return false;
928
997
  };
929
998
  const isSliceStore = getIsSliceStore();
930
999
  Object.assign(store2, {