@async/framework 0.11.11 → 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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.11.12 - 2026-06-18
4
+
5
+ - Deferred boundary receiver sequence commits until patch effects complete so
6
+ failed DOM swaps, scheduler flushes, redirects, or missing capabilities no
7
+ longer make the same streamed sequence stale.
8
+ - Serialized same-boundary patch application to keep concurrent patches from
9
+ committing out of order while preserving stale rejection after successful
10
+ commits.
11
+ - Added regression coverage for retryable DOM, scheduler, redirect, and
12
+ capability failures plus idempotent partial-effect replay.
13
+ - Bundle size from bundled TypeScript source: `browser.ts` 186,064 B raw /
14
+ 35,039 B gzip -> `browser.min.js` 79,042 B raw / 23,445 B gzip
15
+ (-107,022 B raw, -11,594 B gzip).
16
+
3
17
  ## 0.11.11 - 2026-06-18
4
18
 
5
19
  - Serialized rejected async-signal snapshot errors to stable `name`, `message`,
package/browser.js CHANGED
@@ -5815,101 +5815,25 @@ const __boundaryReceiverModule = (() => {
5815
5815
 
5816
5816
  const normalized = validatePatch(patch);
5817
5817
  const record = boundaryRecord(normalized.boundary);
5818
- if (normalized.seq <= record.lastSeq) {
5819
- const result = {
5820
- status: "ignored-stale",
5821
- boundary: normalized.boundary,
5822
- seq: normalized.seq,
5823
- lastSeq: record.lastSeq
5824
- };
5825
- record.ignored += 1;
5826
- record.lastStatus = result.status;
5827
- remember(result);
5828
- onIgnore?.(result, patch);
5829
- return result;
5830
- }
5831
-
5832
- if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
5833
- const result = {
5834
- status: "ignored-destroyed",
5835
- boundary: normalized.boundary,
5836
- seq: normalized.seq,
5837
- parentScope: normalized.parentScope
5838
- };
5839
- record.ignored += 1;
5840
- record.lastStatus = result.status;
5841
- remember(result);
5842
- onIgnore?.(result, patch);
5843
- return result;
5844
- }
5845
-
5846
- record.lastSeq = normalized.seq;
5847
-
5848
- if (Object.hasOwn(normalized, "error")) {
5849
- const error = toStableError(normalized.error);
5850
- const result = {
5851
- status: "errored",
5852
- boundary: normalized.boundary,
5853
- seq: normalized.seq,
5854
- error
5855
- };
5856
- record.errored += 1;
5857
- record.lastStatus = result.status;
5858
- remember(result);
5859
- onError?.(error, result, patch);
5860
- if (throwOnError) {
5861
- throw error;
5862
- }
5863
- return result;
5864
- }
5818
+ let releasePending;
5819
+ const previousPending = record.pending ?? Promise.resolve();
5820
+ const pending = new Promise((resolve) => {
5821
+ releasePending = resolve;
5822
+ });
5823
+ record.pending = pending;
5865
5824
 
5866
- if (normalized.signals) {
5867
- if (!signals || typeof signals.set !== "function") {
5868
- throw new Error("Boundary patch includes signals, but no signal registry is available.");
5869
- }
5870
- for (const [path, value] of Object.entries(normalized.signals)) {
5871
- signals.set(path, value);
5825
+ try {
5826
+ await previousPending;
5827
+ if (destroyed) {
5828
+ throw new Error("Boundary receiver has been destroyed.");
5872
5829
  }
5873
- }
5874
-
5875
- if (normalized.cache?.browser) {
5876
- if (!cache || typeof cache.restore !== "function") {
5877
- throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
5830
+ return await applyBoundaryPatch(record, normalized, patch);
5831
+ } finally {
5832
+ releasePending();
5833
+ if (record.pending === pending) {
5834
+ record.pending = undefined;
5878
5835
  }
5879
- cache.restore(normalized.cache.browser);
5880
- }
5881
-
5882
- if (normalized.html != null) {
5883
- loader.swap(normalized.boundary, normalized.html);
5884
- }
5885
-
5886
- await flushScheduler(scheduler, normalized.scope);
5887
-
5888
- if (normalized.redirect) {
5889
- await followRedirect(normalized.redirect, router, loader);
5890
- const result = {
5891
- status: "redirected",
5892
- boundary: normalized.boundary,
5893
- seq: normalized.seq,
5894
- redirect: normalized.redirect
5895
- };
5896
- record.applied += 1;
5897
- record.lastStatus = result.status;
5898
- remember(result);
5899
- onApply?.(result, patch);
5900
- return result;
5901
5836
  }
5902
-
5903
- const result = {
5904
- status: "applied",
5905
- boundary: normalized.boundary,
5906
- seq: normalized.seq
5907
- };
5908
- record.applied += 1;
5909
- record.lastStatus = result.status;
5910
- remember(result);
5911
- onApply?.(result, patch);
5912
- return result;
5913
5837
  },
5914
5838
 
5915
5839
  inspect() {
@@ -5957,6 +5881,105 @@ const __boundaryReceiverModule = (() => {
5957
5881
 
5958
5882
  return receiver;
5959
5883
 
5884
+ async function applyBoundaryPatch(record, normalized, patch) {
5885
+ if (normalized.seq <= record.lastSeq) {
5886
+ const result = {
5887
+ status: "ignored-stale",
5888
+ boundary: normalized.boundary,
5889
+ seq: normalized.seq,
5890
+ lastSeq: record.lastSeq
5891
+ };
5892
+ record.ignored += 1;
5893
+ record.lastStatus = result.status;
5894
+ remember(result);
5895
+ onIgnore?.(result, patch);
5896
+ return result;
5897
+ }
5898
+
5899
+ if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
5900
+ const result = {
5901
+ status: "ignored-destroyed",
5902
+ boundary: normalized.boundary,
5903
+ seq: normalized.seq,
5904
+ parentScope: normalized.parentScope
5905
+ };
5906
+ record.ignored += 1;
5907
+ record.lastStatus = result.status;
5908
+ remember(result);
5909
+ onIgnore?.(result, patch);
5910
+ return result;
5911
+ }
5912
+
5913
+ if (Object.hasOwn(normalized, "error")) {
5914
+ const error = toStableError(normalized.error);
5915
+ const result = {
5916
+ status: "errored",
5917
+ boundary: normalized.boundary,
5918
+ seq: normalized.seq,
5919
+ error
5920
+ };
5921
+ record.lastSeq = normalized.seq;
5922
+ record.errored += 1;
5923
+ record.lastStatus = result.status;
5924
+ remember(result);
5925
+ onError?.(error, result, patch);
5926
+ if (throwOnError) {
5927
+ throw error;
5928
+ }
5929
+ return result;
5930
+ }
5931
+
5932
+ if (normalized.signals) {
5933
+ if (!signals || typeof signals.set !== "function") {
5934
+ throw new Error("Boundary patch includes signals, but no signal registry is available.");
5935
+ }
5936
+ for (const [path, value] of Object.entries(normalized.signals)) {
5937
+ signals.set(path, value);
5938
+ }
5939
+ }
5940
+
5941
+ if (normalized.cache?.browser) {
5942
+ if (!cache || typeof cache.restore !== "function") {
5943
+ throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
5944
+ }
5945
+ cache.restore(normalized.cache.browser);
5946
+ }
5947
+
5948
+ if (normalized.html != null) {
5949
+ loader.swap(normalized.boundary, normalized.html);
5950
+ }
5951
+
5952
+ await flushScheduler(scheduler, normalized.scope);
5953
+
5954
+ if (normalized.redirect) {
5955
+ const result = {
5956
+ status: "redirected",
5957
+ boundary: normalized.boundary,
5958
+ seq: normalized.seq,
5959
+ redirect: normalized.redirect
5960
+ };
5961
+ await followRedirect(normalized.redirect, router, loader);
5962
+ record.applied += 1;
5963
+ record.lastSeq = normalized.seq;
5964
+ record.lastStatus = result.status;
5965
+ remember(result);
5966
+ onApply?.(result, patch);
5967
+ return result;
5968
+ }
5969
+
5970
+ const result = {
5971
+ status: "applied",
5972
+ boundary: normalized.boundary,
5973
+ seq: normalized.seq
5974
+ };
5975
+ record.applied += 1;
5976
+ record.lastSeq = normalized.seq;
5977
+ record.lastStatus = result.status;
5978
+ remember(result);
5979
+ onApply?.(result, patch);
5980
+ return result;
5981
+ }
5982
+
5960
5983
  function boundaryRecord(boundary) {
5961
5984
  if (!boundaries.has(boundary)) {
5962
5985
  boundaries.set(boundary, {
@@ -5964,7 +5987,8 @@ const __boundaryReceiverModule = (() => {
5964
5987
  applied: 0,
5965
5988
  ignored: 0,
5966
5989
  errored: 0,
5967
- lastStatus: undefined
5990
+ lastStatus: undefined,
5991
+ pending: undefined
5968
5992
  });
5969
5993
  }
5970
5994
  return boundaries.get(boundary);