@async/framework 0.11.10 → 0.11.12

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/browser.ts CHANGED
@@ -132,9 +132,9 @@ const __asyncSignalModule = (() => {
132
132
 
133
133
  snapshot() {
134
134
  return {
135
- value,
135
+ value: value === undefined && error !== null ? null : value,
136
136
  loading,
137
- error,
137
+ error: serializeAsyncError(error),
138
138
  status,
139
139
  version
140
140
  };
@@ -151,7 +151,7 @@ const __asyncSignalModule = (() => {
151
151
  cancelCurrentRun(new Error(`Async signal "${registeredId}" restored from snapshot.`));
152
152
  value = snapshot.value;
153
153
  loading = Boolean(snapshot.loading);
154
- error = snapshot.error ?? null;
154
+ error = restoreAsyncError(snapshot.error);
155
155
  status = typeof snapshot.status === "string" ? snapshot.status : inferStatus({ value, loading, error });
156
156
  if (Number.isFinite(snapshot.version)) {
157
157
  version = snapshot.version;
@@ -403,6 +403,51 @@ const __asyncSignalModule = (() => {
403
403
  return value === undefined ? "idle" : "ready";
404
404
  }
405
405
 
406
+ function serializeAsyncError(value) {
407
+ if (value == null) {
408
+ return null;
409
+ }
410
+
411
+ const record = {
412
+ name: readErrorName(value),
413
+ message: readErrorMessage(value)
414
+ };
415
+ const code = readErrorCode(value);
416
+ if (code !== undefined) {
417
+ record.code = code;
418
+ }
419
+ return record;
420
+ }
421
+
422
+ function restoreAsyncError(value) {
423
+ return serializeAsyncError(value);
424
+ }
425
+
426
+ function readErrorName(value) {
427
+ if (value && typeof value === "object" && typeof value.name === "string" && value.name.length > 0) {
428
+ return value.name;
429
+ }
430
+ return "Error";
431
+ }
432
+
433
+ function readErrorMessage(value) {
434
+ if (value instanceof Error) {
435
+ return value.message;
436
+ }
437
+ if (value && typeof value === "object" && typeof value.message === "string") {
438
+ return value.message;
439
+ }
440
+ return String(value);
441
+ }
442
+
443
+ function readErrorCode(value) {
444
+ if (!value || typeof value !== "object" || !Object.hasOwn(value, "code")) {
445
+ return undefined;
446
+ }
447
+ const code = value.code;
448
+ return typeof code === "string" || typeof code === "number" ? code : undefined;
449
+ }
450
+
406
451
  function attachCancel(signal, controller, onCancel) {
407
452
  Object.defineProperty(signal, "cancel", {
408
453
  configurable: true,
@@ -5771,101 +5816,25 @@ const __boundaryReceiverModule = (() => {
5771
5816
 
5772
5817
  const normalized = validatePatch(patch);
5773
5818
  const record = boundaryRecord(normalized.boundary);
5774
- if (normalized.seq <= record.lastSeq) {
5775
- const result = {
5776
- status: "ignored-stale",
5777
- boundary: normalized.boundary,
5778
- seq: normalized.seq,
5779
- lastSeq: record.lastSeq
5780
- };
5781
- record.ignored += 1;
5782
- record.lastStatus = result.status;
5783
- remember(result);
5784
- onIgnore?.(result, patch);
5785
- return result;
5786
- }
5787
-
5788
- if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
5789
- const result = {
5790
- status: "ignored-destroyed",
5791
- boundary: normalized.boundary,
5792
- seq: normalized.seq,
5793
- parentScope: normalized.parentScope
5794
- };
5795
- record.ignored += 1;
5796
- record.lastStatus = result.status;
5797
- remember(result);
5798
- onIgnore?.(result, patch);
5799
- return result;
5800
- }
5801
-
5802
- record.lastSeq = normalized.seq;
5803
-
5804
- if (Object.hasOwn(normalized, "error")) {
5805
- const error = toStableError(normalized.error);
5806
- const result = {
5807
- status: "errored",
5808
- boundary: normalized.boundary,
5809
- seq: normalized.seq,
5810
- error
5811
- };
5812
- record.errored += 1;
5813
- record.lastStatus = result.status;
5814
- remember(result);
5815
- onError?.(error, result, patch);
5816
- if (throwOnError) {
5817
- throw error;
5818
- }
5819
- return result;
5820
- }
5819
+ let releasePending;
5820
+ const previousPending = record.pending ?? Promise.resolve();
5821
+ const pending = new Promise((resolve) => {
5822
+ releasePending = resolve;
5823
+ });
5824
+ record.pending = pending;
5821
5825
 
5822
- if (normalized.signals) {
5823
- if (!signals || typeof signals.set !== "function") {
5824
- throw new Error("Boundary patch includes signals, but no signal registry is available.");
5825
- }
5826
- for (const [path, value] of Object.entries(normalized.signals)) {
5827
- signals.set(path, value);
5826
+ try {
5827
+ await previousPending;
5828
+ if (destroyed) {
5829
+ throw new Error("Boundary receiver has been destroyed.");
5828
5830
  }
5829
- }
5830
-
5831
- if (normalized.cache?.browser) {
5832
- if (!cache || typeof cache.restore !== "function") {
5833
- throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
5831
+ return await applyBoundaryPatch(record, normalized, patch);
5832
+ } finally {
5833
+ releasePending();
5834
+ if (record.pending === pending) {
5835
+ record.pending = undefined;
5834
5836
  }
5835
- cache.restore(normalized.cache.browser);
5836
- }
5837
-
5838
- if (normalized.html != null) {
5839
- loader.swap(normalized.boundary, normalized.html);
5840
5837
  }
5841
-
5842
- await flushScheduler(scheduler, normalized.scope);
5843
-
5844
- if (normalized.redirect) {
5845
- await followRedirect(normalized.redirect, router, loader);
5846
- const result = {
5847
- status: "redirected",
5848
- boundary: normalized.boundary,
5849
- seq: normalized.seq,
5850
- redirect: normalized.redirect
5851
- };
5852
- record.applied += 1;
5853
- record.lastStatus = result.status;
5854
- remember(result);
5855
- onApply?.(result, patch);
5856
- return result;
5857
- }
5858
-
5859
- const result = {
5860
- status: "applied",
5861
- boundary: normalized.boundary,
5862
- seq: normalized.seq
5863
- };
5864
- record.applied += 1;
5865
- record.lastStatus = result.status;
5866
- remember(result);
5867
- onApply?.(result, patch);
5868
- return result;
5869
5838
  },
5870
5839
 
5871
5840
  inspect() {
@@ -5913,6 +5882,105 @@ const __boundaryReceiverModule = (() => {
5913
5882
 
5914
5883
  return receiver;
5915
5884
 
5885
+ async function applyBoundaryPatch(record, normalized, patch) {
5886
+ if (normalized.seq <= record.lastSeq) {
5887
+ const result = {
5888
+ status: "ignored-stale",
5889
+ boundary: normalized.boundary,
5890
+ seq: normalized.seq,
5891
+ lastSeq: record.lastSeq
5892
+ };
5893
+ record.ignored += 1;
5894
+ record.lastStatus = result.status;
5895
+ remember(result);
5896
+ onIgnore?.(result, patch);
5897
+ return result;
5898
+ }
5899
+
5900
+ if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
5901
+ const result = {
5902
+ status: "ignored-destroyed",
5903
+ boundary: normalized.boundary,
5904
+ seq: normalized.seq,
5905
+ parentScope: normalized.parentScope
5906
+ };
5907
+ record.ignored += 1;
5908
+ record.lastStatus = result.status;
5909
+ remember(result);
5910
+ onIgnore?.(result, patch);
5911
+ return result;
5912
+ }
5913
+
5914
+ if (Object.hasOwn(normalized, "error")) {
5915
+ const error = toStableError(normalized.error);
5916
+ const result = {
5917
+ status: "errored",
5918
+ boundary: normalized.boundary,
5919
+ seq: normalized.seq,
5920
+ error
5921
+ };
5922
+ record.lastSeq = normalized.seq;
5923
+ record.errored += 1;
5924
+ record.lastStatus = result.status;
5925
+ remember(result);
5926
+ onError?.(error, result, patch);
5927
+ if (throwOnError) {
5928
+ throw error;
5929
+ }
5930
+ return result;
5931
+ }
5932
+
5933
+ if (normalized.signals) {
5934
+ if (!signals || typeof signals.set !== "function") {
5935
+ throw new Error("Boundary patch includes signals, but no signal registry is available.");
5936
+ }
5937
+ for (const [path, value] of Object.entries(normalized.signals)) {
5938
+ signals.set(path, value);
5939
+ }
5940
+ }
5941
+
5942
+ if (normalized.cache?.browser) {
5943
+ if (!cache || typeof cache.restore !== "function") {
5944
+ throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
5945
+ }
5946
+ cache.restore(normalized.cache.browser);
5947
+ }
5948
+
5949
+ if (normalized.html != null) {
5950
+ loader.swap(normalized.boundary, normalized.html);
5951
+ }
5952
+
5953
+ await flushScheduler(scheduler, normalized.scope);
5954
+
5955
+ if (normalized.redirect) {
5956
+ const result = {
5957
+ status: "redirected",
5958
+ boundary: normalized.boundary,
5959
+ seq: normalized.seq,
5960
+ redirect: normalized.redirect
5961
+ };
5962
+ await followRedirect(normalized.redirect, router, loader);
5963
+ record.applied += 1;
5964
+ record.lastSeq = normalized.seq;
5965
+ record.lastStatus = result.status;
5966
+ remember(result);
5967
+ onApply?.(result, patch);
5968
+ return result;
5969
+ }
5970
+
5971
+ const result = {
5972
+ status: "applied",
5973
+ boundary: normalized.boundary,
5974
+ seq: normalized.seq
5975
+ };
5976
+ record.applied += 1;
5977
+ record.lastSeq = normalized.seq;
5978
+ record.lastStatus = result.status;
5979
+ remember(result);
5980
+ onApply?.(result, patch);
5981
+ return result;
5982
+ }
5983
+
5916
5984
  function boundaryRecord(boundary) {
5917
5985
  if (!boundaries.has(boundary)) {
5918
5986
  boundaries.set(boundary, {
@@ -5920,7 +5988,8 @@ const __boundaryReceiverModule = (() => {
5920
5988
  applied: 0,
5921
5989
  ignored: 0,
5922
5990
  errored: 0,
5923
- lastStatus: undefined
5991
+ lastStatus: undefined,
5992
+ pending: undefined
5924
5993
  });
5925
5994
  }
5926
5995
  return boundaries.get(boundary);
package/browser.umd.js CHANGED
@@ -142,9 +142,9 @@
142
142
 
143
143
  snapshot() {
144
144
  return {
145
- value,
145
+ value: value === undefined && error !== null ? null : value,
146
146
  loading,
147
- error,
147
+ error: serializeAsyncError(error),
148
148
  status,
149
149
  version
150
150
  };
@@ -161,7 +161,7 @@
161
161
  cancelCurrentRun(new Error(`Async signal "${registeredId}" restored from snapshot.`));
162
162
  value = snapshot.value;
163
163
  loading = Boolean(snapshot.loading);
164
- error = snapshot.error ?? null;
164
+ error = restoreAsyncError(snapshot.error);
165
165
  status = typeof snapshot.status === "string" ? snapshot.status : inferStatus({ value, loading, error });
166
166
  if (Number.isFinite(snapshot.version)) {
167
167
  version = snapshot.version;
@@ -413,6 +413,51 @@
413
413
  return value === undefined ? "idle" : "ready";
414
414
  }
415
415
 
416
+ function serializeAsyncError(value) {
417
+ if (value == null) {
418
+ return null;
419
+ }
420
+
421
+ const record = {
422
+ name: readErrorName(value),
423
+ message: readErrorMessage(value)
424
+ };
425
+ const code = readErrorCode(value);
426
+ if (code !== undefined) {
427
+ record.code = code;
428
+ }
429
+ return record;
430
+ }
431
+
432
+ function restoreAsyncError(value) {
433
+ return serializeAsyncError(value);
434
+ }
435
+
436
+ function readErrorName(value) {
437
+ if (value && typeof value === "object" && typeof value.name === "string" && value.name.length > 0) {
438
+ return value.name;
439
+ }
440
+ return "Error";
441
+ }
442
+
443
+ function readErrorMessage(value) {
444
+ if (value instanceof Error) {
445
+ return value.message;
446
+ }
447
+ if (value && typeof value === "object" && typeof value.message === "string") {
448
+ return value.message;
449
+ }
450
+ return String(value);
451
+ }
452
+
453
+ function readErrorCode(value) {
454
+ if (!value || typeof value !== "object" || !Object.hasOwn(value, "code")) {
455
+ return undefined;
456
+ }
457
+ const code = value.code;
458
+ return typeof code === "string" || typeof code === "number" ? code : undefined;
459
+ }
460
+
416
461
  function attachCancel(signal, controller, onCancel) {
417
462
  Object.defineProperty(signal, "cancel", {
418
463
  configurable: true,
@@ -5781,101 +5826,25 @@
5781
5826
 
5782
5827
  const normalized = validatePatch(patch);
5783
5828
  const record = boundaryRecord(normalized.boundary);
5784
- if (normalized.seq <= record.lastSeq) {
5785
- const result = {
5786
- status: "ignored-stale",
5787
- boundary: normalized.boundary,
5788
- seq: normalized.seq,
5789
- lastSeq: record.lastSeq
5790
- };
5791
- record.ignored += 1;
5792
- record.lastStatus = result.status;
5793
- remember(result);
5794
- onIgnore?.(result, patch);
5795
- return result;
5796
- }
5797
-
5798
- if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
5799
- const result = {
5800
- status: "ignored-destroyed",
5801
- boundary: normalized.boundary,
5802
- seq: normalized.seq,
5803
- parentScope: normalized.parentScope
5804
- };
5805
- record.ignored += 1;
5806
- record.lastStatus = result.status;
5807
- remember(result);
5808
- onIgnore?.(result, patch);
5809
- return result;
5810
- }
5811
-
5812
- record.lastSeq = normalized.seq;
5813
-
5814
- if (Object.hasOwn(normalized, "error")) {
5815
- const error = toStableError(normalized.error);
5816
- const result = {
5817
- status: "errored",
5818
- boundary: normalized.boundary,
5819
- seq: normalized.seq,
5820
- error
5821
- };
5822
- record.errored += 1;
5823
- record.lastStatus = result.status;
5824
- remember(result);
5825
- onError?.(error, result, patch);
5826
- if (throwOnError) {
5827
- throw error;
5828
- }
5829
- return result;
5830
- }
5829
+ let releasePending;
5830
+ const previousPending = record.pending ?? Promise.resolve();
5831
+ const pending = new Promise((resolve) => {
5832
+ releasePending = resolve;
5833
+ });
5834
+ record.pending = pending;
5831
5835
 
5832
- if (normalized.signals) {
5833
- if (!signals || typeof signals.set !== "function") {
5834
- throw new Error("Boundary patch includes signals, but no signal registry is available.");
5835
- }
5836
- for (const [path, value] of Object.entries(normalized.signals)) {
5837
- signals.set(path, value);
5836
+ try {
5837
+ await previousPending;
5838
+ if (destroyed) {
5839
+ throw new Error("Boundary receiver has been destroyed.");
5838
5840
  }
5839
- }
5840
-
5841
- if (normalized.cache?.browser) {
5842
- if (!cache || typeof cache.restore !== "function") {
5843
- throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
5841
+ return await applyBoundaryPatch(record, normalized, patch);
5842
+ } finally {
5843
+ releasePending();
5844
+ if (record.pending === pending) {
5845
+ record.pending = undefined;
5844
5846
  }
5845
- cache.restore(normalized.cache.browser);
5846
- }
5847
-
5848
- if (normalized.html != null) {
5849
- loader.swap(normalized.boundary, normalized.html);
5850
5847
  }
5851
-
5852
- await flushScheduler(scheduler, normalized.scope);
5853
-
5854
- if (normalized.redirect) {
5855
- await followRedirect(normalized.redirect, router, loader);
5856
- const result = {
5857
- status: "redirected",
5858
- boundary: normalized.boundary,
5859
- seq: normalized.seq,
5860
- redirect: normalized.redirect
5861
- };
5862
- record.applied += 1;
5863
- record.lastStatus = result.status;
5864
- remember(result);
5865
- onApply?.(result, patch);
5866
- return result;
5867
- }
5868
-
5869
- const result = {
5870
- status: "applied",
5871
- boundary: normalized.boundary,
5872
- seq: normalized.seq
5873
- };
5874
- record.applied += 1;
5875
- record.lastStatus = result.status;
5876
- remember(result);
5877
- onApply?.(result, patch);
5878
- return result;
5879
5848
  },
5880
5849
 
5881
5850
  inspect() {
@@ -5923,6 +5892,105 @@
5923
5892
 
5924
5893
  return receiver;
5925
5894
 
5895
+ async function applyBoundaryPatch(record, normalized, patch) {
5896
+ if (normalized.seq <= record.lastSeq) {
5897
+ const result = {
5898
+ status: "ignored-stale",
5899
+ boundary: normalized.boundary,
5900
+ seq: normalized.seq,
5901
+ lastSeq: record.lastSeq
5902
+ };
5903
+ record.ignored += 1;
5904
+ record.lastStatus = result.status;
5905
+ remember(result);
5906
+ onIgnore?.(result, patch);
5907
+ return result;
5908
+ }
5909
+
5910
+ if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
5911
+ const result = {
5912
+ status: "ignored-destroyed",
5913
+ boundary: normalized.boundary,
5914
+ seq: normalized.seq,
5915
+ parentScope: normalized.parentScope
5916
+ };
5917
+ record.ignored += 1;
5918
+ record.lastStatus = result.status;
5919
+ remember(result);
5920
+ onIgnore?.(result, patch);
5921
+ return result;
5922
+ }
5923
+
5924
+ if (Object.hasOwn(normalized, "error")) {
5925
+ const error = toStableError(normalized.error);
5926
+ const result = {
5927
+ status: "errored",
5928
+ boundary: normalized.boundary,
5929
+ seq: normalized.seq,
5930
+ error
5931
+ };
5932
+ record.lastSeq = normalized.seq;
5933
+ record.errored += 1;
5934
+ record.lastStatus = result.status;
5935
+ remember(result);
5936
+ onError?.(error, result, patch);
5937
+ if (throwOnError) {
5938
+ throw error;
5939
+ }
5940
+ return result;
5941
+ }
5942
+
5943
+ if (normalized.signals) {
5944
+ if (!signals || typeof signals.set !== "function") {
5945
+ throw new Error("Boundary patch includes signals, but no signal registry is available.");
5946
+ }
5947
+ for (const [path, value] of Object.entries(normalized.signals)) {
5948
+ signals.set(path, value);
5949
+ }
5950
+ }
5951
+
5952
+ if (normalized.cache?.browser) {
5953
+ if (!cache || typeof cache.restore !== "function") {
5954
+ throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
5955
+ }
5956
+ cache.restore(normalized.cache.browser);
5957
+ }
5958
+
5959
+ if (normalized.html != null) {
5960
+ loader.swap(normalized.boundary, normalized.html);
5961
+ }
5962
+
5963
+ await flushScheduler(scheduler, normalized.scope);
5964
+
5965
+ if (normalized.redirect) {
5966
+ const result = {
5967
+ status: "redirected",
5968
+ boundary: normalized.boundary,
5969
+ seq: normalized.seq,
5970
+ redirect: normalized.redirect
5971
+ };
5972
+ await followRedirect(normalized.redirect, router, loader);
5973
+ record.applied += 1;
5974
+ record.lastSeq = normalized.seq;
5975
+ record.lastStatus = result.status;
5976
+ remember(result);
5977
+ onApply?.(result, patch);
5978
+ return result;
5979
+ }
5980
+
5981
+ const result = {
5982
+ status: "applied",
5983
+ boundary: normalized.boundary,
5984
+ seq: normalized.seq
5985
+ };
5986
+ record.applied += 1;
5987
+ record.lastSeq = normalized.seq;
5988
+ record.lastStatus = result.status;
5989
+ remember(result);
5990
+ onApply?.(result, patch);
5991
+ return result;
5992
+ }
5993
+
5926
5994
  function boundaryRecord(boundary) {
5927
5995
  if (!boundaries.has(boundary)) {
5928
5996
  boundaries.set(boundary, {
@@ -5930,7 +5998,8 @@
5930
5998
  applied: 0,
5931
5999
  ignored: 0,
5932
6000
  errored: 0,
5933
- lastStatus: undefined
6001
+ lastStatus: undefined,
6002
+ pending: undefined
5934
6003
  });
5935
6004
  }
5936
6005
  return boundaries.get(boundary);