@async/framework 0.8.0 → 0.9.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/CHANGELOG.md +6 -0
- package/README.md +41 -0
- package/browser.d.ts +50 -0
- package/browser.js +312 -1
- package/browser.min.js +1 -1
- package/browser.ts +312 -1
- package/browser.umd.js +312 -1
- package/browser.umd.min.js +1 -1
- package/package.json +1 -1
- package/server.d.ts +50 -0
- package/src/boundary-receiver.js +302 -0
- package/src/browser.js +1 -0
- package/src/index.js +1 -0
- package/src/scheduler.js +4 -0
package/browser.ts
CHANGED
|
@@ -2595,6 +2595,10 @@ const __schedulerModule = (() => {
|
|
|
2595
2595
|
return api;
|
|
2596
2596
|
},
|
|
2597
2597
|
|
|
2598
|
+
isScopeDestroyed(scope) {
|
|
2599
|
+
return scope !== undefined && destroyedScopes.has(scope);
|
|
2600
|
+
},
|
|
2601
|
+
|
|
2598
2602
|
inspect() {
|
|
2599
2603
|
const counts = {};
|
|
2600
2604
|
for (const [phase, queue] of queues) {
|
|
@@ -4710,6 +4714,312 @@ const __appModule = (() => {
|
|
|
4710
4714
|
return { defineApp, createApp, readSnapshot, Async };
|
|
4711
4715
|
})();
|
|
4712
4716
|
|
|
4717
|
+
const __boundaryReceiverModule = (() => {
|
|
4718
|
+
const defaultRecentLimit = 50;
|
|
4719
|
+
|
|
4720
|
+
function createBoundaryReceiver(options = {}) {
|
|
4721
|
+
const loader = options.loader;
|
|
4722
|
+
const signals = options.signals ?? loader?.signals;
|
|
4723
|
+
const cache = options.cache ?? loader?.cache;
|
|
4724
|
+
const scheduler = options.scheduler ?? loader?.scheduler;
|
|
4725
|
+
const router = options.router ?? loader?.router;
|
|
4726
|
+
const recentLimit = options.recentLimit ?? defaultRecentLimit;
|
|
4727
|
+
const throwOnError = options.throwOnError === true;
|
|
4728
|
+
const onApply = typeof options.onApply === "function" ? options.onApply : undefined;
|
|
4729
|
+
const onIgnore = typeof options.onIgnore === "function" ? options.onIgnore : undefined;
|
|
4730
|
+
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
4731
|
+
const isScopeDestroyed = typeof options.isScopeDestroyed === "function"
|
|
4732
|
+
? options.isScopeDestroyed
|
|
4733
|
+
: (scope) => scheduler?.isScopeDestroyed?.(scope) ?? scheduler?.inspectDestroyed?.(scope) ?? false;
|
|
4734
|
+
|
|
4735
|
+
if (!loader || typeof loader.swap !== "function") {
|
|
4736
|
+
throw new TypeError("createBoundaryReceiver(...) requires a loader with swap(boundary, html).");
|
|
4737
|
+
}
|
|
4738
|
+
if (!Number.isInteger(recentLimit) || recentLimit < 0) {
|
|
4739
|
+
throw new TypeError("createBoundaryReceiver(...) recentLimit must be a non-negative integer.");
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4742
|
+
const boundaries = new Map();
|
|
4743
|
+
const recent = [];
|
|
4744
|
+
let destroyed = false;
|
|
4745
|
+
|
|
4746
|
+
const receiver = {
|
|
4747
|
+
async apply(patch) {
|
|
4748
|
+
if (destroyed) {
|
|
4749
|
+
throw new Error("Boundary receiver has been destroyed.");
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
const normalized = validatePatch(patch);
|
|
4753
|
+
const record = boundaryRecord(normalized.boundary);
|
|
4754
|
+
if (normalized.seq <= record.lastSeq) {
|
|
4755
|
+
const result = {
|
|
4756
|
+
status: "ignored-stale",
|
|
4757
|
+
boundary: normalized.boundary,
|
|
4758
|
+
seq: normalized.seq,
|
|
4759
|
+
lastSeq: record.lastSeq
|
|
4760
|
+
};
|
|
4761
|
+
record.ignored += 1;
|
|
4762
|
+
record.lastStatus = result.status;
|
|
4763
|
+
remember(result);
|
|
4764
|
+
onIgnore?.(result, patch);
|
|
4765
|
+
return result;
|
|
4766
|
+
}
|
|
4767
|
+
|
|
4768
|
+
if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
|
|
4769
|
+
const result = {
|
|
4770
|
+
status: "ignored-destroyed",
|
|
4771
|
+
boundary: normalized.boundary,
|
|
4772
|
+
seq: normalized.seq,
|
|
4773
|
+
parentScope: normalized.parentScope
|
|
4774
|
+
};
|
|
4775
|
+
record.ignored += 1;
|
|
4776
|
+
record.lastStatus = result.status;
|
|
4777
|
+
remember(result);
|
|
4778
|
+
onIgnore?.(result, patch);
|
|
4779
|
+
return result;
|
|
4780
|
+
}
|
|
4781
|
+
|
|
4782
|
+
record.lastSeq = normalized.seq;
|
|
4783
|
+
|
|
4784
|
+
if (Object.hasOwn(normalized, "error")) {
|
|
4785
|
+
const error = toStableError(normalized.error);
|
|
4786
|
+
const result = {
|
|
4787
|
+
status: "errored",
|
|
4788
|
+
boundary: normalized.boundary,
|
|
4789
|
+
seq: normalized.seq,
|
|
4790
|
+
error
|
|
4791
|
+
};
|
|
4792
|
+
record.errored += 1;
|
|
4793
|
+
record.lastStatus = result.status;
|
|
4794
|
+
remember(result);
|
|
4795
|
+
onError?.(error, result, patch);
|
|
4796
|
+
if (throwOnError) {
|
|
4797
|
+
throw error;
|
|
4798
|
+
}
|
|
4799
|
+
return result;
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
if (normalized.signals) {
|
|
4803
|
+
if (!signals || typeof signals.set !== "function") {
|
|
4804
|
+
throw new Error("Boundary patch includes signals, but no signal registry is available.");
|
|
4805
|
+
}
|
|
4806
|
+
for (const [path, value] of Object.entries(normalized.signals)) {
|
|
4807
|
+
signals.set(path, value);
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
|
|
4811
|
+
if (normalized.cache?.browser) {
|
|
4812
|
+
if (!cache || typeof cache.restore !== "function") {
|
|
4813
|
+
throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
|
|
4814
|
+
}
|
|
4815
|
+
cache.restore(normalized.cache.browser);
|
|
4816
|
+
}
|
|
4817
|
+
|
|
4818
|
+
if (normalized.html != null) {
|
|
4819
|
+
loader.swap(normalized.boundary, normalized.html);
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
await flushScheduler(scheduler, normalized.scope);
|
|
4823
|
+
|
|
4824
|
+
if (normalized.redirect) {
|
|
4825
|
+
await followRedirect(normalized.redirect, router, loader);
|
|
4826
|
+
const result = {
|
|
4827
|
+
status: "redirected",
|
|
4828
|
+
boundary: normalized.boundary,
|
|
4829
|
+
seq: normalized.seq,
|
|
4830
|
+
redirect: normalized.redirect
|
|
4831
|
+
};
|
|
4832
|
+
record.applied += 1;
|
|
4833
|
+
record.lastStatus = result.status;
|
|
4834
|
+
remember(result);
|
|
4835
|
+
onApply?.(result, patch);
|
|
4836
|
+
return result;
|
|
4837
|
+
}
|
|
4838
|
+
|
|
4839
|
+
const result = {
|
|
4840
|
+
status: "applied",
|
|
4841
|
+
boundary: normalized.boundary,
|
|
4842
|
+
seq: normalized.seq
|
|
4843
|
+
};
|
|
4844
|
+
record.applied += 1;
|
|
4845
|
+
record.lastStatus = result.status;
|
|
4846
|
+
remember(result);
|
|
4847
|
+
onApply?.(result, patch);
|
|
4848
|
+
return result;
|
|
4849
|
+
},
|
|
4850
|
+
|
|
4851
|
+
inspect() {
|
|
4852
|
+
const snapshot = {};
|
|
4853
|
+
for (const [boundary, record] of boundaries) {
|
|
4854
|
+
snapshot[boundary] = {
|
|
4855
|
+
lastSeq: record.lastSeq,
|
|
4856
|
+
applied: record.applied,
|
|
4857
|
+
ignored: record.ignored,
|
|
4858
|
+
lastStatus: record.lastStatus
|
|
4859
|
+
};
|
|
4860
|
+
if (record.errored > 0) {
|
|
4861
|
+
snapshot[boundary].errored = record.errored;
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
return {
|
|
4865
|
+
destroyed,
|
|
4866
|
+
boundaries: snapshot,
|
|
4867
|
+
recent: recent.map((entry) => ({ ...entry }))
|
|
4868
|
+
};
|
|
4869
|
+
},
|
|
4870
|
+
|
|
4871
|
+
reset(boundary) {
|
|
4872
|
+
if (boundary === undefined) {
|
|
4873
|
+
boundaries.clear();
|
|
4874
|
+
recent.length = 0;
|
|
4875
|
+
return receiver;
|
|
4876
|
+
}
|
|
4877
|
+
assertBoundary(boundary);
|
|
4878
|
+
boundaries.delete(boundary);
|
|
4879
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
4880
|
+
if (recent[index].boundary === boundary) {
|
|
4881
|
+
recent.splice(index, 1);
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
return receiver;
|
|
4885
|
+
},
|
|
4886
|
+
|
|
4887
|
+
destroy() {
|
|
4888
|
+
destroyed = true;
|
|
4889
|
+
boundaries.clear();
|
|
4890
|
+
recent.length = 0;
|
|
4891
|
+
}
|
|
4892
|
+
};
|
|
4893
|
+
|
|
4894
|
+
return receiver;
|
|
4895
|
+
|
|
4896
|
+
function boundaryRecord(boundary) {
|
|
4897
|
+
if (!boundaries.has(boundary)) {
|
|
4898
|
+
boundaries.set(boundary, {
|
|
4899
|
+
lastSeq: -Infinity,
|
|
4900
|
+
applied: 0,
|
|
4901
|
+
ignored: 0,
|
|
4902
|
+
errored: 0,
|
|
4903
|
+
lastStatus: undefined
|
|
4904
|
+
});
|
|
4905
|
+
}
|
|
4906
|
+
return boundaries.get(boundary);
|
|
4907
|
+
}
|
|
4908
|
+
|
|
4909
|
+
function remember(result) {
|
|
4910
|
+
if (recentLimit === 0) {
|
|
4911
|
+
return;
|
|
4912
|
+
}
|
|
4913
|
+
recent.push(toRecentEntry(result));
|
|
4914
|
+
while (recent.length > recentLimit) {
|
|
4915
|
+
recent.shift();
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
|
|
4920
|
+
function validatePatch(patch) {
|
|
4921
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
4922
|
+
throw new TypeError("receiver.apply(patch) requires a boundary patch object.");
|
|
4923
|
+
}
|
|
4924
|
+
|
|
4925
|
+
assertBoundary(patch.boundary);
|
|
4926
|
+
if (typeof patch.seq !== "number" || !Number.isFinite(patch.seq)) {
|
|
4927
|
+
throw new TypeError("Boundary patch seq must be a finite number.");
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
if (patch.signals !== undefined && !isPlainObject(patch.signals)) {
|
|
4931
|
+
throw new TypeError("Boundary patch signals must be an object.");
|
|
4932
|
+
}
|
|
4933
|
+
if (patch.cache !== undefined && !isPlainObject(patch.cache)) {
|
|
4934
|
+
throw new TypeError("Boundary patch cache must be an object.");
|
|
4935
|
+
}
|
|
4936
|
+
if (patch.cache?.browser !== undefined && !isPlainObject(patch.cache.browser)) {
|
|
4937
|
+
throw new TypeError("Boundary patch cache.browser must be an object.");
|
|
4938
|
+
}
|
|
4939
|
+
if (patch.redirect !== undefined && (typeof patch.redirect !== "string" || patch.redirect.length === 0)) {
|
|
4940
|
+
throw new TypeError("Boundary patch redirect must be a non-empty string.");
|
|
4941
|
+
}
|
|
4942
|
+
if (patch.parentScope !== undefined && typeof patch.parentScope !== "string") {
|
|
4943
|
+
throw new TypeError("Boundary patch parentScope must be a string.");
|
|
4944
|
+
}
|
|
4945
|
+
if (patch.scope !== undefined && typeof patch.scope !== "string") {
|
|
4946
|
+
throw new TypeError("Boundary patch scope must be a string.");
|
|
4947
|
+
}
|
|
4948
|
+
|
|
4949
|
+
const hasHtml = Object.hasOwn(patch, "html") && patch.html != null;
|
|
4950
|
+
const hasSignals = patch.signals && Object.keys(patch.signals).length > 0;
|
|
4951
|
+
const hasBrowserCache = patch.cache?.browser && Object.keys(patch.cache.browser).length > 0;
|
|
4952
|
+
const hasRedirect = Boolean(patch.redirect);
|
|
4953
|
+
const hasError = Object.hasOwn(patch, "error");
|
|
4954
|
+
if (!hasHtml && !hasSignals && !hasBrowserCache && !hasRedirect && !hasError) {
|
|
4955
|
+
throw new TypeError("Boundary patch must include html, signals, cache.browser, redirect, or error.");
|
|
4956
|
+
}
|
|
4957
|
+
|
|
4958
|
+
return patch;
|
|
4959
|
+
}
|
|
4960
|
+
|
|
4961
|
+
function assertBoundary(boundary) {
|
|
4962
|
+
if (typeof boundary !== "string" || boundary.length === 0) {
|
|
4963
|
+
throw new TypeError("Boundary patch boundary must be a non-empty string.");
|
|
4964
|
+
}
|
|
4965
|
+
}
|
|
4966
|
+
|
|
4967
|
+
async function flushScheduler(scheduler, scope) {
|
|
4968
|
+
if (!scheduler) {
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4971
|
+
if (scope !== undefined && typeof scheduler.flushScope === "function") {
|
|
4972
|
+
await scheduler.flushScope(scope);
|
|
4973
|
+
return;
|
|
4974
|
+
}
|
|
4975
|
+
if (typeof scheduler.flush === "function") {
|
|
4976
|
+
await scheduler.flush();
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
|
|
4980
|
+
async function followRedirect(redirect, router, loader) {
|
|
4981
|
+
if (router && typeof router.navigate === "function") {
|
|
4982
|
+
await router.navigate(redirect);
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4985
|
+
const location = loader?.root?.ownerDocument?.defaultView?.location ?? globalThis.location;
|
|
4986
|
+
location?.assign?.(redirect);
|
|
4987
|
+
}
|
|
4988
|
+
|
|
4989
|
+
function toStableError(value) {
|
|
4990
|
+
if (value instanceof Error) {
|
|
4991
|
+
return value;
|
|
4992
|
+
}
|
|
4993
|
+
if (value && typeof value === "object" && typeof value.message === "string") {
|
|
4994
|
+
return Object.assign(new Error(value.message), value);
|
|
4995
|
+
}
|
|
4996
|
+
return new Error(String(value));
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
function toRecentEntry(result) {
|
|
5000
|
+
const entry = {
|
|
5001
|
+
boundary: result.boundary,
|
|
5002
|
+
seq: result.seq,
|
|
5003
|
+
status: result.status
|
|
5004
|
+
};
|
|
5005
|
+
if (result.status === "ignored-stale") {
|
|
5006
|
+
entry.lastSeq = result.lastSeq;
|
|
5007
|
+
}
|
|
5008
|
+
if (result.status === "ignored-destroyed" && result.parentScope !== undefined) {
|
|
5009
|
+
entry.parentScope = result.parentScope;
|
|
5010
|
+
}
|
|
5011
|
+
if (result.status === "redirected") {
|
|
5012
|
+
entry.redirect = result.redirect;
|
|
5013
|
+
}
|
|
5014
|
+
return entry;
|
|
5015
|
+
}
|
|
5016
|
+
|
|
5017
|
+
function isPlainObject(value) {
|
|
5018
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5019
|
+
}
|
|
5020
|
+
return { createBoundaryReceiver };
|
|
5021
|
+
})();
|
|
5022
|
+
|
|
4713
5023
|
const __delayModule = (() => {
|
|
4714
5024
|
function delay(ms, signal) {
|
|
4715
5025
|
if (signal?.aborted) {
|
|
@@ -4751,6 +5061,7 @@ const { defineApp: defineApp } = __appModule;
|
|
|
4751
5061
|
const { readSnapshot: readSnapshot } = __appModule;
|
|
4752
5062
|
const { attributeName: attributeName } = __attributesModule;
|
|
4753
5063
|
const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
|
|
5064
|
+
const { createBoundaryReceiver: createBoundaryReceiver } = __boundaryReceiverModule;
|
|
4754
5065
|
const { createCacheRegistry: createCacheRegistry } = __cacheModule;
|
|
4755
5066
|
const { defineCache: defineCache } = __cacheModule;
|
|
4756
5067
|
const { component: component } = __componentModule;
|
|
@@ -4778,4 +5089,4 @@ const { createSignalRegistry: createSignalRegistry } = __signalsModule;
|
|
|
4778
5089
|
const { effect: effect } = __signalsModule;
|
|
4779
5090
|
const { signal: signal } = __signalsModule;
|
|
4780
5091
|
|
|
4781
|
-
export { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createScheduler, applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult, computed, createSignal, createSignalRegistry, effect, signal };
|
|
5092
|
+
export { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createBoundaryReceiver, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createScheduler, applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult, computed, createSignal, createSignalRegistry, effect, signal };
|
package/browser.umd.js
CHANGED
|
@@ -2605,6 +2605,10 @@
|
|
|
2605
2605
|
return api;
|
|
2606
2606
|
},
|
|
2607
2607
|
|
|
2608
|
+
isScopeDestroyed(scope) {
|
|
2609
|
+
return scope !== undefined && destroyedScopes.has(scope);
|
|
2610
|
+
},
|
|
2611
|
+
|
|
2608
2612
|
inspect() {
|
|
2609
2613
|
const counts = {};
|
|
2610
2614
|
for (const [phase, queue] of queues) {
|
|
@@ -4720,6 +4724,312 @@
|
|
|
4720
4724
|
return { defineApp, createApp, readSnapshot, Async };
|
|
4721
4725
|
})();
|
|
4722
4726
|
|
|
4727
|
+
const __boundaryReceiverModule = (() => {
|
|
4728
|
+
const defaultRecentLimit = 50;
|
|
4729
|
+
|
|
4730
|
+
function createBoundaryReceiver(options = {}) {
|
|
4731
|
+
const loader = options.loader;
|
|
4732
|
+
const signals = options.signals ?? loader?.signals;
|
|
4733
|
+
const cache = options.cache ?? loader?.cache;
|
|
4734
|
+
const scheduler = options.scheduler ?? loader?.scheduler;
|
|
4735
|
+
const router = options.router ?? loader?.router;
|
|
4736
|
+
const recentLimit = options.recentLimit ?? defaultRecentLimit;
|
|
4737
|
+
const throwOnError = options.throwOnError === true;
|
|
4738
|
+
const onApply = typeof options.onApply === "function" ? options.onApply : undefined;
|
|
4739
|
+
const onIgnore = typeof options.onIgnore === "function" ? options.onIgnore : undefined;
|
|
4740
|
+
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
4741
|
+
const isScopeDestroyed = typeof options.isScopeDestroyed === "function"
|
|
4742
|
+
? options.isScopeDestroyed
|
|
4743
|
+
: (scope) => scheduler?.isScopeDestroyed?.(scope) ?? scheduler?.inspectDestroyed?.(scope) ?? false;
|
|
4744
|
+
|
|
4745
|
+
if (!loader || typeof loader.swap !== "function") {
|
|
4746
|
+
throw new TypeError("createBoundaryReceiver(...) requires a loader with swap(boundary, html).");
|
|
4747
|
+
}
|
|
4748
|
+
if (!Number.isInteger(recentLimit) || recentLimit < 0) {
|
|
4749
|
+
throw new TypeError("createBoundaryReceiver(...) recentLimit must be a non-negative integer.");
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
const boundaries = new Map();
|
|
4753
|
+
const recent = [];
|
|
4754
|
+
let destroyed = false;
|
|
4755
|
+
|
|
4756
|
+
const receiver = {
|
|
4757
|
+
async apply(patch) {
|
|
4758
|
+
if (destroyed) {
|
|
4759
|
+
throw new Error("Boundary receiver has been destroyed.");
|
|
4760
|
+
}
|
|
4761
|
+
|
|
4762
|
+
const normalized = validatePatch(patch);
|
|
4763
|
+
const record = boundaryRecord(normalized.boundary);
|
|
4764
|
+
if (normalized.seq <= record.lastSeq) {
|
|
4765
|
+
const result = {
|
|
4766
|
+
status: "ignored-stale",
|
|
4767
|
+
boundary: normalized.boundary,
|
|
4768
|
+
seq: normalized.seq,
|
|
4769
|
+
lastSeq: record.lastSeq
|
|
4770
|
+
};
|
|
4771
|
+
record.ignored += 1;
|
|
4772
|
+
record.lastStatus = result.status;
|
|
4773
|
+
remember(result);
|
|
4774
|
+
onIgnore?.(result, patch);
|
|
4775
|
+
return result;
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4778
|
+
if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
|
|
4779
|
+
const result = {
|
|
4780
|
+
status: "ignored-destroyed",
|
|
4781
|
+
boundary: normalized.boundary,
|
|
4782
|
+
seq: normalized.seq,
|
|
4783
|
+
parentScope: normalized.parentScope
|
|
4784
|
+
};
|
|
4785
|
+
record.ignored += 1;
|
|
4786
|
+
record.lastStatus = result.status;
|
|
4787
|
+
remember(result);
|
|
4788
|
+
onIgnore?.(result, patch);
|
|
4789
|
+
return result;
|
|
4790
|
+
}
|
|
4791
|
+
|
|
4792
|
+
record.lastSeq = normalized.seq;
|
|
4793
|
+
|
|
4794
|
+
if (Object.hasOwn(normalized, "error")) {
|
|
4795
|
+
const error = toStableError(normalized.error);
|
|
4796
|
+
const result = {
|
|
4797
|
+
status: "errored",
|
|
4798
|
+
boundary: normalized.boundary,
|
|
4799
|
+
seq: normalized.seq,
|
|
4800
|
+
error
|
|
4801
|
+
};
|
|
4802
|
+
record.errored += 1;
|
|
4803
|
+
record.lastStatus = result.status;
|
|
4804
|
+
remember(result);
|
|
4805
|
+
onError?.(error, result, patch);
|
|
4806
|
+
if (throwOnError) {
|
|
4807
|
+
throw error;
|
|
4808
|
+
}
|
|
4809
|
+
return result;
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
if (normalized.signals) {
|
|
4813
|
+
if (!signals || typeof signals.set !== "function") {
|
|
4814
|
+
throw new Error("Boundary patch includes signals, but no signal registry is available.");
|
|
4815
|
+
}
|
|
4816
|
+
for (const [path, value] of Object.entries(normalized.signals)) {
|
|
4817
|
+
signals.set(path, value);
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
|
|
4821
|
+
if (normalized.cache?.browser) {
|
|
4822
|
+
if (!cache || typeof cache.restore !== "function") {
|
|
4823
|
+
throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
|
|
4824
|
+
}
|
|
4825
|
+
cache.restore(normalized.cache.browser);
|
|
4826
|
+
}
|
|
4827
|
+
|
|
4828
|
+
if (normalized.html != null) {
|
|
4829
|
+
loader.swap(normalized.boundary, normalized.html);
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4832
|
+
await flushScheduler(scheduler, normalized.scope);
|
|
4833
|
+
|
|
4834
|
+
if (normalized.redirect) {
|
|
4835
|
+
await followRedirect(normalized.redirect, router, loader);
|
|
4836
|
+
const result = {
|
|
4837
|
+
status: "redirected",
|
|
4838
|
+
boundary: normalized.boundary,
|
|
4839
|
+
seq: normalized.seq,
|
|
4840
|
+
redirect: normalized.redirect
|
|
4841
|
+
};
|
|
4842
|
+
record.applied += 1;
|
|
4843
|
+
record.lastStatus = result.status;
|
|
4844
|
+
remember(result);
|
|
4845
|
+
onApply?.(result, patch);
|
|
4846
|
+
return result;
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4849
|
+
const result = {
|
|
4850
|
+
status: "applied",
|
|
4851
|
+
boundary: normalized.boundary,
|
|
4852
|
+
seq: normalized.seq
|
|
4853
|
+
};
|
|
4854
|
+
record.applied += 1;
|
|
4855
|
+
record.lastStatus = result.status;
|
|
4856
|
+
remember(result);
|
|
4857
|
+
onApply?.(result, patch);
|
|
4858
|
+
return result;
|
|
4859
|
+
},
|
|
4860
|
+
|
|
4861
|
+
inspect() {
|
|
4862
|
+
const snapshot = {};
|
|
4863
|
+
for (const [boundary, record] of boundaries) {
|
|
4864
|
+
snapshot[boundary] = {
|
|
4865
|
+
lastSeq: record.lastSeq,
|
|
4866
|
+
applied: record.applied,
|
|
4867
|
+
ignored: record.ignored,
|
|
4868
|
+
lastStatus: record.lastStatus
|
|
4869
|
+
};
|
|
4870
|
+
if (record.errored > 0) {
|
|
4871
|
+
snapshot[boundary].errored = record.errored;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
return {
|
|
4875
|
+
destroyed,
|
|
4876
|
+
boundaries: snapshot,
|
|
4877
|
+
recent: recent.map((entry) => ({ ...entry }))
|
|
4878
|
+
};
|
|
4879
|
+
},
|
|
4880
|
+
|
|
4881
|
+
reset(boundary) {
|
|
4882
|
+
if (boundary === undefined) {
|
|
4883
|
+
boundaries.clear();
|
|
4884
|
+
recent.length = 0;
|
|
4885
|
+
return receiver;
|
|
4886
|
+
}
|
|
4887
|
+
assertBoundary(boundary);
|
|
4888
|
+
boundaries.delete(boundary);
|
|
4889
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
4890
|
+
if (recent[index].boundary === boundary) {
|
|
4891
|
+
recent.splice(index, 1);
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
return receiver;
|
|
4895
|
+
},
|
|
4896
|
+
|
|
4897
|
+
destroy() {
|
|
4898
|
+
destroyed = true;
|
|
4899
|
+
boundaries.clear();
|
|
4900
|
+
recent.length = 0;
|
|
4901
|
+
}
|
|
4902
|
+
};
|
|
4903
|
+
|
|
4904
|
+
return receiver;
|
|
4905
|
+
|
|
4906
|
+
function boundaryRecord(boundary) {
|
|
4907
|
+
if (!boundaries.has(boundary)) {
|
|
4908
|
+
boundaries.set(boundary, {
|
|
4909
|
+
lastSeq: -Infinity,
|
|
4910
|
+
applied: 0,
|
|
4911
|
+
ignored: 0,
|
|
4912
|
+
errored: 0,
|
|
4913
|
+
lastStatus: undefined
|
|
4914
|
+
});
|
|
4915
|
+
}
|
|
4916
|
+
return boundaries.get(boundary);
|
|
4917
|
+
}
|
|
4918
|
+
|
|
4919
|
+
function remember(result) {
|
|
4920
|
+
if (recentLimit === 0) {
|
|
4921
|
+
return;
|
|
4922
|
+
}
|
|
4923
|
+
recent.push(toRecentEntry(result));
|
|
4924
|
+
while (recent.length > recentLimit) {
|
|
4925
|
+
recent.shift();
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
function validatePatch(patch) {
|
|
4931
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
4932
|
+
throw new TypeError("receiver.apply(patch) requires a boundary patch object.");
|
|
4933
|
+
}
|
|
4934
|
+
|
|
4935
|
+
assertBoundary(patch.boundary);
|
|
4936
|
+
if (typeof patch.seq !== "number" || !Number.isFinite(patch.seq)) {
|
|
4937
|
+
throw new TypeError("Boundary patch seq must be a finite number.");
|
|
4938
|
+
}
|
|
4939
|
+
|
|
4940
|
+
if (patch.signals !== undefined && !isPlainObject(patch.signals)) {
|
|
4941
|
+
throw new TypeError("Boundary patch signals must be an object.");
|
|
4942
|
+
}
|
|
4943
|
+
if (patch.cache !== undefined && !isPlainObject(patch.cache)) {
|
|
4944
|
+
throw new TypeError("Boundary patch cache must be an object.");
|
|
4945
|
+
}
|
|
4946
|
+
if (patch.cache?.browser !== undefined && !isPlainObject(patch.cache.browser)) {
|
|
4947
|
+
throw new TypeError("Boundary patch cache.browser must be an object.");
|
|
4948
|
+
}
|
|
4949
|
+
if (patch.redirect !== undefined && (typeof patch.redirect !== "string" || patch.redirect.length === 0)) {
|
|
4950
|
+
throw new TypeError("Boundary patch redirect must be a non-empty string.");
|
|
4951
|
+
}
|
|
4952
|
+
if (patch.parentScope !== undefined && typeof patch.parentScope !== "string") {
|
|
4953
|
+
throw new TypeError("Boundary patch parentScope must be a string.");
|
|
4954
|
+
}
|
|
4955
|
+
if (patch.scope !== undefined && typeof patch.scope !== "string") {
|
|
4956
|
+
throw new TypeError("Boundary patch scope must be a string.");
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
const hasHtml = Object.hasOwn(patch, "html") && patch.html != null;
|
|
4960
|
+
const hasSignals = patch.signals && Object.keys(patch.signals).length > 0;
|
|
4961
|
+
const hasBrowserCache = patch.cache?.browser && Object.keys(patch.cache.browser).length > 0;
|
|
4962
|
+
const hasRedirect = Boolean(patch.redirect);
|
|
4963
|
+
const hasError = Object.hasOwn(patch, "error");
|
|
4964
|
+
if (!hasHtml && !hasSignals && !hasBrowserCache && !hasRedirect && !hasError) {
|
|
4965
|
+
throw new TypeError("Boundary patch must include html, signals, cache.browser, redirect, or error.");
|
|
4966
|
+
}
|
|
4967
|
+
|
|
4968
|
+
return patch;
|
|
4969
|
+
}
|
|
4970
|
+
|
|
4971
|
+
function assertBoundary(boundary) {
|
|
4972
|
+
if (typeof boundary !== "string" || boundary.length === 0) {
|
|
4973
|
+
throw new TypeError("Boundary patch boundary must be a non-empty string.");
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
|
|
4977
|
+
async function flushScheduler(scheduler, scope) {
|
|
4978
|
+
if (!scheduler) {
|
|
4979
|
+
return;
|
|
4980
|
+
}
|
|
4981
|
+
if (scope !== undefined && typeof scheduler.flushScope === "function") {
|
|
4982
|
+
await scheduler.flushScope(scope);
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4985
|
+
if (typeof scheduler.flush === "function") {
|
|
4986
|
+
await scheduler.flush();
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
|
|
4990
|
+
async function followRedirect(redirect, router, loader) {
|
|
4991
|
+
if (router && typeof router.navigate === "function") {
|
|
4992
|
+
await router.navigate(redirect);
|
|
4993
|
+
return;
|
|
4994
|
+
}
|
|
4995
|
+
const location = loader?.root?.ownerDocument?.defaultView?.location ?? globalThis.location;
|
|
4996
|
+
location?.assign?.(redirect);
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
function toStableError(value) {
|
|
5000
|
+
if (value instanceof Error) {
|
|
5001
|
+
return value;
|
|
5002
|
+
}
|
|
5003
|
+
if (value && typeof value === "object" && typeof value.message === "string") {
|
|
5004
|
+
return Object.assign(new Error(value.message), value);
|
|
5005
|
+
}
|
|
5006
|
+
return new Error(String(value));
|
|
5007
|
+
}
|
|
5008
|
+
|
|
5009
|
+
function toRecentEntry(result) {
|
|
5010
|
+
const entry = {
|
|
5011
|
+
boundary: result.boundary,
|
|
5012
|
+
seq: result.seq,
|
|
5013
|
+
status: result.status
|
|
5014
|
+
};
|
|
5015
|
+
if (result.status === "ignored-stale") {
|
|
5016
|
+
entry.lastSeq = result.lastSeq;
|
|
5017
|
+
}
|
|
5018
|
+
if (result.status === "ignored-destroyed" && result.parentScope !== undefined) {
|
|
5019
|
+
entry.parentScope = result.parentScope;
|
|
5020
|
+
}
|
|
5021
|
+
if (result.status === "redirected") {
|
|
5022
|
+
entry.redirect = result.redirect;
|
|
5023
|
+
}
|
|
5024
|
+
return entry;
|
|
5025
|
+
}
|
|
5026
|
+
|
|
5027
|
+
function isPlainObject(value) {
|
|
5028
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5029
|
+
}
|
|
5030
|
+
return { createBoundaryReceiver };
|
|
5031
|
+
})();
|
|
5032
|
+
|
|
4723
5033
|
const __delayModule = (() => {
|
|
4724
5034
|
function delay(ms, signal) {
|
|
4725
5035
|
if (signal?.aborted) {
|
|
@@ -4761,6 +5071,7 @@
|
|
|
4761
5071
|
const { readSnapshot: readSnapshot } = __appModule;
|
|
4762
5072
|
const { attributeName: attributeName } = __attributesModule;
|
|
4763
5073
|
const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
|
|
5074
|
+
const { createBoundaryReceiver: createBoundaryReceiver } = __boundaryReceiverModule;
|
|
4764
5075
|
const { createCacheRegistry: createCacheRegistry } = __cacheModule;
|
|
4765
5076
|
const { defineCache: defineCache } = __cacheModule;
|
|
4766
5077
|
const { component: component } = __componentModule;
|
|
@@ -4787,7 +5098,7 @@
|
|
|
4787
5098
|
const { createSignalRegistry: createSignalRegistry } = __signalsModule;
|
|
4788
5099
|
const { effect: effect } = __signalsModule;
|
|
4789
5100
|
const { signal: signal } = __signalsModule;
|
|
4790
|
-
const api = { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createScheduler, applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult, computed, createSignal, createSignalRegistry, effect, signal };
|
|
5101
|
+
const api = { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createBoundaryReceiver, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createScheduler, applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult, computed, createSignal, createSignalRegistry, effect, signal };
|
|
4791
5102
|
assertNoUmdNamespaceConflicts(api, Async);
|
|
4792
5103
|
Object.assign(Async, api);
|
|
4793
5104
|
Async.Async = Async;
|