@async/framework 0.10.2 → 0.11.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/CHANGELOG.md +25 -0
- package/README.md +23 -7
- package/browser.d.ts +4 -7
- package/browser.js +17 -66
- package/browser.min.js +1 -1
- package/browser.ts +17 -66
- package/browser.umd.js +17 -66
- package/browser.umd.min.js +1 -1
- package/{server.d.ts → framework.d.ts} +4 -7
- package/framework.ts +5946 -0
- package/package.json +25 -17
- package/server.js +5945 -0
- package/examples/cache/index.html +0 -16
- package/examples/cache/main.js +0 -47
- package/examples/components/index.html +0 -11
- package/examples/components/main.js +0 -26
- package/examples/counter/index.html +0 -15
- package/examples/counter/main.js +0 -17
- package/examples/partials/index.html +0 -15
- package/examples/partials/main.js +0 -43
- package/examples/product/index.html +0 -32
- package/examples/product/main.js +0 -24
- package/examples/router/index.html +0 -18
- package/examples/router/main.js +0 -52
- package/examples/server-call/index.html +0 -21
- package/examples/server-call/main.js +0 -22
- package/examples/ssr/index.html +0 -12
- package/examples/ssr/main.js +0 -89
- package/examples/streaming/index.html +0 -16
- package/examples/streaming/main.js +0 -30
- package/src/app.js +0 -802
- package/src/async-signal.js +0 -277
- package/src/attributes.js +0 -52
- package/src/boundary-receiver.js +0 -302
- package/src/browser.js +0 -18
- package/src/cache.js +0 -193
- package/src/component.js +0 -373
- package/src/delay.js +0 -30
- package/src/elements.js +0 -63
- package/src/handlers.js +0 -219
- package/src/html.js +0 -158
- package/src/index.js +0 -20
- package/src/lazy-registry.js +0 -218
- package/src/loader.js +0 -772
- package/src/partials.js +0 -133
- package/src/registry-store.js +0 -267
- package/src/request-context.js +0 -40
- package/src/router.js +0 -617
- package/src/scheduler.js +0 -300
- package/src/server-entry.js +0 -20
- package/src/server-registry.js +0 -97
- package/src/server.js +0 -362
- package/src/signals.js +0 -592
package/src/async-signal.js
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
const asyncSignalKind = Symbol.for("@async/framework.asyncSignal");
|
|
2
|
-
|
|
3
|
-
export function asyncSignal(id, fn) {
|
|
4
|
-
if (typeof id !== "string" || id.length === 0) {
|
|
5
|
-
throw new TypeError("asyncSignal(id, fn) requires a non-empty string id.");
|
|
6
|
-
}
|
|
7
|
-
if (typeof fn !== "function") {
|
|
8
|
-
throw new TypeError("asyncSignal(id, fn) requires a function.");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let value;
|
|
12
|
-
let loading = false;
|
|
13
|
-
let error = null;
|
|
14
|
-
let status = "idle";
|
|
15
|
-
let version = 0;
|
|
16
|
-
let registry;
|
|
17
|
-
let registeredId = id;
|
|
18
|
-
let activeController;
|
|
19
|
-
let activeAbort;
|
|
20
|
-
const subscribers = new Set();
|
|
21
|
-
const dependencyCleanups = new Set();
|
|
22
|
-
|
|
23
|
-
const state = {
|
|
24
|
-
[asyncSignalKind]: true,
|
|
25
|
-
kind: "async-signal",
|
|
26
|
-
|
|
27
|
-
get id() {
|
|
28
|
-
return registeredId;
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
get value() {
|
|
32
|
-
return value;
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
get loading() {
|
|
36
|
-
return loading;
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
get error() {
|
|
40
|
-
return error;
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
get status() {
|
|
44
|
-
return status;
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
get version() {
|
|
48
|
-
return version;
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
set(nextValue) {
|
|
52
|
-
value = nextValue;
|
|
53
|
-
loading = false;
|
|
54
|
-
error = null;
|
|
55
|
-
status = "ready";
|
|
56
|
-
notify();
|
|
57
|
-
return value;
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
refresh() {
|
|
61
|
-
if (!registry) {
|
|
62
|
-
throw new Error(`Async signal "${registeredId}" is not registered.`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (activeAbort && !activeAbort.aborted) {
|
|
66
|
-
activeAbort.cancel(new Error(`Async signal "${registeredId}" refreshed.`));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const runVersion = version + 1;
|
|
70
|
-
version = runVersion;
|
|
71
|
-
loading = true;
|
|
72
|
-
error = null;
|
|
73
|
-
status = "loading";
|
|
74
|
-
|
|
75
|
-
const controller = new AbortController();
|
|
76
|
-
activeController = controller;
|
|
77
|
-
activeAbort = controller.signal;
|
|
78
|
-
attachCancel(activeAbort, controller);
|
|
79
|
-
notify();
|
|
80
|
-
|
|
81
|
-
const context = {
|
|
82
|
-
signals: registry,
|
|
83
|
-
id: registeredId,
|
|
84
|
-
get server() {
|
|
85
|
-
const context = registry._context?.() ?? {};
|
|
86
|
-
const server = context.server;
|
|
87
|
-
if (typeof server?._withContext === "function") {
|
|
88
|
-
return server._withContext({
|
|
89
|
-
signals: registry,
|
|
90
|
-
router: context.router,
|
|
91
|
-
loader: context.loader,
|
|
92
|
-
cache: context.cache,
|
|
93
|
-
abort: activeAbort,
|
|
94
|
-
scheduler: context.scheduler
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
return server;
|
|
98
|
-
},
|
|
99
|
-
get router() {
|
|
100
|
-
return registry._context?.().router;
|
|
101
|
-
},
|
|
102
|
-
get loader() {
|
|
103
|
-
return registry._context?.().loader;
|
|
104
|
-
},
|
|
105
|
-
get cache() {
|
|
106
|
-
return registry._context?.().cache;
|
|
107
|
-
},
|
|
108
|
-
get scheduler() {
|
|
109
|
-
return registry._context?.().scheduler;
|
|
110
|
-
},
|
|
111
|
-
get version() {
|
|
112
|
-
return runVersion;
|
|
113
|
-
},
|
|
114
|
-
get abort() {
|
|
115
|
-
return activeAbort;
|
|
116
|
-
},
|
|
117
|
-
refresh() {
|
|
118
|
-
return state.refresh();
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
let outcome;
|
|
123
|
-
try {
|
|
124
|
-
outcome = registry._collectDependencies(() => fn.call(context));
|
|
125
|
-
} catch (cause) {
|
|
126
|
-
finishError(runVersion, cause);
|
|
127
|
-
return Promise.reject(cause);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
syncDependencies(outcome.dependencies);
|
|
131
|
-
|
|
132
|
-
return Promise.resolve(outcome.value).then(
|
|
133
|
-
(nextValue) => {
|
|
134
|
-
if (!isCurrent(runVersion)) {
|
|
135
|
-
return value;
|
|
136
|
-
}
|
|
137
|
-
value = nextValue;
|
|
138
|
-
loading = false;
|
|
139
|
-
error = null;
|
|
140
|
-
status = "ready";
|
|
141
|
-
notify();
|
|
142
|
-
return value;
|
|
143
|
-
},
|
|
144
|
-
(cause) => {
|
|
145
|
-
if (!isCurrent(runVersion)) {
|
|
146
|
-
return value;
|
|
147
|
-
}
|
|
148
|
-
if (activeAbort?.aborted) {
|
|
149
|
-
loading = false;
|
|
150
|
-
status = value === undefined ? "idle" : "ready";
|
|
151
|
-
notify();
|
|
152
|
-
return value;
|
|
153
|
-
}
|
|
154
|
-
finishError(runVersion, cause);
|
|
155
|
-
return value;
|
|
156
|
-
}
|
|
157
|
-
);
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
cancel(reason) {
|
|
161
|
-
if (activeAbort && !activeAbort.aborted) {
|
|
162
|
-
activeAbort.cancel(reason);
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
subscribe(fn) {
|
|
167
|
-
if (typeof fn !== "function") {
|
|
168
|
-
throw new TypeError("subscribe(fn) requires a function.");
|
|
169
|
-
}
|
|
170
|
-
subscribers.add(fn);
|
|
171
|
-
return () => subscribers.delete(fn);
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
snapshot() {
|
|
175
|
-
return {
|
|
176
|
-
value,
|
|
177
|
-
loading,
|
|
178
|
-
error,
|
|
179
|
-
status,
|
|
180
|
-
version
|
|
181
|
-
};
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
_bindRegistry(nextRegistry, nextId) {
|
|
185
|
-
registry = nextRegistry;
|
|
186
|
-
registeredId = nextId;
|
|
187
|
-
const start = () => {
|
|
188
|
-
if (registry === nextRegistry && status === "idle") {
|
|
189
|
-
state.refresh();
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
const scheduler = registry._context?.().scheduler;
|
|
193
|
-
if (scheduler) {
|
|
194
|
-
scheduler.enqueue("async", start, {
|
|
195
|
-
scope: registeredId,
|
|
196
|
-
key: `asyncSignal:${registeredId}:initial`
|
|
197
|
-
});
|
|
198
|
-
} else {
|
|
199
|
-
queueMicrotask(start);
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
_dispose() {
|
|
204
|
-
state.cancel(new Error(`Async signal "${registeredId}" disposed.`));
|
|
205
|
-
for (const cleanup of dependencyCleanups) {
|
|
206
|
-
cleanup();
|
|
207
|
-
}
|
|
208
|
-
dependencyCleanups.clear();
|
|
209
|
-
subscribers.clear();
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
function finishError(runVersion, cause) {
|
|
214
|
-
if (!isCurrent(runVersion)) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
loading = false;
|
|
218
|
-
error = cause;
|
|
219
|
-
status = "error";
|
|
220
|
-
notify();
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function isCurrent(runVersion) {
|
|
224
|
-
return runVersion === version && activeController?.signal === activeAbort;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function syncDependencies(dependencies) {
|
|
228
|
-
for (const cleanup of dependencyCleanups) {
|
|
229
|
-
cleanup();
|
|
230
|
-
}
|
|
231
|
-
dependencyCleanups.clear();
|
|
232
|
-
|
|
233
|
-
for (const dependency of dependencies) {
|
|
234
|
-
const dependencyId = String(dependency).split(".")[0];
|
|
235
|
-
if (dependencyId && dependencyId !== registeredId) {
|
|
236
|
-
dependencyCleanups.add(registry.subscribe(dependency, () => scheduleRefresh()));
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function scheduleRefresh() {
|
|
242
|
-
if (activeAbort && !activeAbort.aborted) {
|
|
243
|
-
activeAbort.cancel(new Error(`Async signal "${registeredId}" dependency changed.`));
|
|
244
|
-
}
|
|
245
|
-
const scheduler = registry?._context?.().scheduler;
|
|
246
|
-
if (!scheduler) {
|
|
247
|
-
state.refresh();
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
scheduler.enqueue("async", () => state.refresh(), {
|
|
251
|
-
scope: registeredId,
|
|
252
|
-
key: `asyncSignal:${registeredId}:refresh`
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function notify() {
|
|
257
|
-
for (const subscriber of [...subscribers]) {
|
|
258
|
-
subscriber(state);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return state;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export function isAsyncSignal(value) {
|
|
266
|
-
return Boolean(value?.[asyncSignalKind]);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function attachCancel(signal, controller) {
|
|
270
|
-
Object.defineProperty(signal, "cancel", {
|
|
271
|
-
configurable: true,
|
|
272
|
-
enumerable: false,
|
|
273
|
-
value(reason) {
|
|
274
|
-
controller.abort(reason);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
}
|
package/src/attributes.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const defaultPrefixes = Object.freeze({
|
|
2
|
-
async: ["async:"],
|
|
3
|
-
class: ["class:"],
|
|
4
|
-
signal: ["signal:"],
|
|
5
|
-
on: ["on:"]
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export function defineAttributeConfig(config = {}) {
|
|
9
|
-
return normalizeAttributeConfig(config);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function normalizeAttributeConfig(config = {}) {
|
|
13
|
-
return {
|
|
14
|
-
async: normalizePrefixes(config.async, defaultPrefixes.async),
|
|
15
|
-
class: normalizePrefixes(config.class, defaultPrefixes.class),
|
|
16
|
-
signal: normalizePrefixes(config.signal, defaultPrefixes.signal),
|
|
17
|
-
on: normalizePrefixes(config.on, defaultPrefixes.on)
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function attributeName(attributes, type, name) {
|
|
22
|
-
return normalizeAttributeConfig(attributes)[type][0] + name;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function readAttribute(element, attributes, type, name) {
|
|
26
|
-
for (const prefix of normalizeAttributeConfig(attributes)[type]) {
|
|
27
|
-
const attr = `${prefix}${name}`;
|
|
28
|
-
if (element.hasAttribute?.(attr)) {
|
|
29
|
-
return element.getAttribute(attr);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function matchAttribute(name, attributes, type) {
|
|
36
|
-
for (const prefix of normalizeAttributeConfig(attributes)[type]) {
|
|
37
|
-
if (name.startsWith(prefix)) {
|
|
38
|
-
return name.slice(prefix.length);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function normalizePrefixes(value, fallback) {
|
|
45
|
-
const prefixes = value == null ? fallback : Array.isArray(value) ? value : [value];
|
|
46
|
-
return prefixes.map((prefix) => {
|
|
47
|
-
if (typeof prefix !== "string" || prefix.length === 0) {
|
|
48
|
-
throw new TypeError("Attribute prefixes must be non-empty strings.");
|
|
49
|
-
}
|
|
50
|
-
return prefix;
|
|
51
|
-
});
|
|
52
|
-
}
|
package/src/boundary-receiver.js
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
const defaultRecentLimit = 50;
|
|
2
|
-
|
|
3
|
-
export function createBoundaryReceiver(options = {}) {
|
|
4
|
-
const loader = options.loader;
|
|
5
|
-
const signals = options.signals ?? loader?.signals;
|
|
6
|
-
const cache = options.cache ?? loader?.cache;
|
|
7
|
-
const scheduler = options.scheduler ?? loader?.scheduler;
|
|
8
|
-
const router = options.router ?? loader?.router;
|
|
9
|
-
const recentLimit = options.recentLimit ?? defaultRecentLimit;
|
|
10
|
-
const throwOnError = options.throwOnError === true;
|
|
11
|
-
const onApply = typeof options.onApply === "function" ? options.onApply : undefined;
|
|
12
|
-
const onIgnore = typeof options.onIgnore === "function" ? options.onIgnore : undefined;
|
|
13
|
-
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
14
|
-
const isScopeDestroyed = typeof options.isScopeDestroyed === "function"
|
|
15
|
-
? options.isScopeDestroyed
|
|
16
|
-
: (scope) => scheduler?.isScopeDestroyed?.(scope) ?? scheduler?.inspectDestroyed?.(scope) ?? false;
|
|
17
|
-
|
|
18
|
-
if (!loader || typeof loader.swap !== "function") {
|
|
19
|
-
throw new TypeError("createBoundaryReceiver(...) requires a loader with swap(boundary, html).");
|
|
20
|
-
}
|
|
21
|
-
if (!Number.isInteger(recentLimit) || recentLimit < 0) {
|
|
22
|
-
throw new TypeError("createBoundaryReceiver(...) recentLimit must be a non-negative integer.");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const boundaries = new Map();
|
|
26
|
-
const recent = [];
|
|
27
|
-
let destroyed = false;
|
|
28
|
-
|
|
29
|
-
const receiver = {
|
|
30
|
-
async apply(patch) {
|
|
31
|
-
if (destroyed) {
|
|
32
|
-
throw new Error("Boundary receiver has been destroyed.");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const normalized = validatePatch(patch);
|
|
36
|
-
const record = boundaryRecord(normalized.boundary);
|
|
37
|
-
if (normalized.seq <= record.lastSeq) {
|
|
38
|
-
const result = {
|
|
39
|
-
status: "ignored-stale",
|
|
40
|
-
boundary: normalized.boundary,
|
|
41
|
-
seq: normalized.seq,
|
|
42
|
-
lastSeq: record.lastSeq
|
|
43
|
-
};
|
|
44
|
-
record.ignored += 1;
|
|
45
|
-
record.lastStatus = result.status;
|
|
46
|
-
remember(result);
|
|
47
|
-
onIgnore?.(result, patch);
|
|
48
|
-
return result;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
|
|
52
|
-
const result = {
|
|
53
|
-
status: "ignored-destroyed",
|
|
54
|
-
boundary: normalized.boundary,
|
|
55
|
-
seq: normalized.seq,
|
|
56
|
-
parentScope: normalized.parentScope
|
|
57
|
-
};
|
|
58
|
-
record.ignored += 1;
|
|
59
|
-
record.lastStatus = result.status;
|
|
60
|
-
remember(result);
|
|
61
|
-
onIgnore?.(result, patch);
|
|
62
|
-
return result;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
record.lastSeq = normalized.seq;
|
|
66
|
-
|
|
67
|
-
if (Object.hasOwn(normalized, "error")) {
|
|
68
|
-
const error = toStableError(normalized.error);
|
|
69
|
-
const result = {
|
|
70
|
-
status: "errored",
|
|
71
|
-
boundary: normalized.boundary,
|
|
72
|
-
seq: normalized.seq,
|
|
73
|
-
error
|
|
74
|
-
};
|
|
75
|
-
record.errored += 1;
|
|
76
|
-
record.lastStatus = result.status;
|
|
77
|
-
remember(result);
|
|
78
|
-
onError?.(error, result, patch);
|
|
79
|
-
if (throwOnError) {
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (normalized.signals) {
|
|
86
|
-
if (!signals || typeof signals.set !== "function") {
|
|
87
|
-
throw new Error("Boundary patch includes signals, but no signal registry is available.");
|
|
88
|
-
}
|
|
89
|
-
for (const [path, value] of Object.entries(normalized.signals)) {
|
|
90
|
-
signals.set(path, value);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (normalized.cache?.browser) {
|
|
95
|
-
if (!cache || typeof cache.restore !== "function") {
|
|
96
|
-
throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
|
|
97
|
-
}
|
|
98
|
-
cache.restore(normalized.cache.browser);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (normalized.html != null) {
|
|
102
|
-
loader.swap(normalized.boundary, normalized.html);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
await flushScheduler(scheduler, normalized.scope);
|
|
106
|
-
|
|
107
|
-
if (normalized.redirect) {
|
|
108
|
-
await followRedirect(normalized.redirect, router, loader);
|
|
109
|
-
const result = {
|
|
110
|
-
status: "redirected",
|
|
111
|
-
boundary: normalized.boundary,
|
|
112
|
-
seq: normalized.seq,
|
|
113
|
-
redirect: normalized.redirect
|
|
114
|
-
};
|
|
115
|
-
record.applied += 1;
|
|
116
|
-
record.lastStatus = result.status;
|
|
117
|
-
remember(result);
|
|
118
|
-
onApply?.(result, patch);
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const result = {
|
|
123
|
-
status: "applied",
|
|
124
|
-
boundary: normalized.boundary,
|
|
125
|
-
seq: normalized.seq
|
|
126
|
-
};
|
|
127
|
-
record.applied += 1;
|
|
128
|
-
record.lastStatus = result.status;
|
|
129
|
-
remember(result);
|
|
130
|
-
onApply?.(result, patch);
|
|
131
|
-
return result;
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
inspect() {
|
|
135
|
-
const snapshot = {};
|
|
136
|
-
for (const [boundary, record] of boundaries) {
|
|
137
|
-
snapshot[boundary] = {
|
|
138
|
-
lastSeq: record.lastSeq,
|
|
139
|
-
applied: record.applied,
|
|
140
|
-
ignored: record.ignored,
|
|
141
|
-
lastStatus: record.lastStatus
|
|
142
|
-
};
|
|
143
|
-
if (record.errored > 0) {
|
|
144
|
-
snapshot[boundary].errored = record.errored;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
destroyed,
|
|
149
|
-
boundaries: snapshot,
|
|
150
|
-
recent: recent.map((entry) => ({ ...entry }))
|
|
151
|
-
};
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
reset(boundary) {
|
|
155
|
-
if (boundary === undefined) {
|
|
156
|
-
boundaries.clear();
|
|
157
|
-
recent.length = 0;
|
|
158
|
-
return receiver;
|
|
159
|
-
}
|
|
160
|
-
assertBoundary(boundary);
|
|
161
|
-
boundaries.delete(boundary);
|
|
162
|
-
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
163
|
-
if (recent[index].boundary === boundary) {
|
|
164
|
-
recent.splice(index, 1);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return receiver;
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
destroy() {
|
|
171
|
-
destroyed = true;
|
|
172
|
-
boundaries.clear();
|
|
173
|
-
recent.length = 0;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
return receiver;
|
|
178
|
-
|
|
179
|
-
function boundaryRecord(boundary) {
|
|
180
|
-
if (!boundaries.has(boundary)) {
|
|
181
|
-
boundaries.set(boundary, {
|
|
182
|
-
lastSeq: -Infinity,
|
|
183
|
-
applied: 0,
|
|
184
|
-
ignored: 0,
|
|
185
|
-
errored: 0,
|
|
186
|
-
lastStatus: undefined
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
return boundaries.get(boundary);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function remember(result) {
|
|
193
|
-
if (recentLimit === 0) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
recent.push(toRecentEntry(result));
|
|
197
|
-
while (recent.length > recentLimit) {
|
|
198
|
-
recent.shift();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function validatePatch(patch) {
|
|
204
|
-
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
205
|
-
throw new TypeError("receiver.apply(patch) requires a boundary patch object.");
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
assertBoundary(patch.boundary);
|
|
209
|
-
if (typeof patch.seq !== "number" || !Number.isFinite(patch.seq)) {
|
|
210
|
-
throw new TypeError("Boundary patch seq must be a finite number.");
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (patch.signals !== undefined && !isPlainObject(patch.signals)) {
|
|
214
|
-
throw new TypeError("Boundary patch signals must be an object.");
|
|
215
|
-
}
|
|
216
|
-
if (patch.cache !== undefined && !isPlainObject(patch.cache)) {
|
|
217
|
-
throw new TypeError("Boundary patch cache must be an object.");
|
|
218
|
-
}
|
|
219
|
-
if (patch.cache?.browser !== undefined && !isPlainObject(patch.cache.browser)) {
|
|
220
|
-
throw new TypeError("Boundary patch cache.browser must be an object.");
|
|
221
|
-
}
|
|
222
|
-
if (patch.redirect !== undefined && (typeof patch.redirect !== "string" || patch.redirect.length === 0)) {
|
|
223
|
-
throw new TypeError("Boundary patch redirect must be a non-empty string.");
|
|
224
|
-
}
|
|
225
|
-
if (patch.parentScope !== undefined && typeof patch.parentScope !== "string") {
|
|
226
|
-
throw new TypeError("Boundary patch parentScope must be a string.");
|
|
227
|
-
}
|
|
228
|
-
if (patch.scope !== undefined && typeof patch.scope !== "string") {
|
|
229
|
-
throw new TypeError("Boundary patch scope must be a string.");
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const hasHtml = Object.hasOwn(patch, "html") && patch.html != null;
|
|
233
|
-
const hasSignals = patch.signals && Object.keys(patch.signals).length > 0;
|
|
234
|
-
const hasBrowserCache = patch.cache?.browser && Object.keys(patch.cache.browser).length > 0;
|
|
235
|
-
const hasRedirect = Boolean(patch.redirect);
|
|
236
|
-
const hasError = Object.hasOwn(patch, "error");
|
|
237
|
-
if (!hasHtml && !hasSignals && !hasBrowserCache && !hasRedirect && !hasError) {
|
|
238
|
-
throw new TypeError("Boundary patch must include html, signals, cache.browser, redirect, or error.");
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return patch;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function assertBoundary(boundary) {
|
|
245
|
-
if (typeof boundary !== "string" || boundary.length === 0) {
|
|
246
|
-
throw new TypeError("Boundary patch boundary must be a non-empty string.");
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async function flushScheduler(scheduler, scope) {
|
|
251
|
-
if (!scheduler) {
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
if (scope !== undefined && typeof scheduler.flushScope === "function") {
|
|
255
|
-
await scheduler.flushScope(scope);
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
if (typeof scheduler.flush === "function") {
|
|
259
|
-
await scheduler.flush();
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async function followRedirect(redirect, router, loader) {
|
|
264
|
-
if (router && typeof router.navigate === "function") {
|
|
265
|
-
await router.navigate(redirect);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const location = loader?.root?.ownerDocument?.defaultView?.location ?? globalThis.location;
|
|
269
|
-
location?.assign?.(redirect);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function toStableError(value) {
|
|
273
|
-
if (value instanceof Error) {
|
|
274
|
-
return value;
|
|
275
|
-
}
|
|
276
|
-
if (value && typeof value === "object" && typeof value.message === "string") {
|
|
277
|
-
return Object.assign(new Error(value.message), value);
|
|
278
|
-
}
|
|
279
|
-
return new Error(String(value));
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function toRecentEntry(result) {
|
|
283
|
-
const entry = {
|
|
284
|
-
boundary: result.boundary,
|
|
285
|
-
seq: result.seq,
|
|
286
|
-
status: result.status
|
|
287
|
-
};
|
|
288
|
-
if (result.status === "ignored-stale") {
|
|
289
|
-
entry.lastSeq = result.lastSeq;
|
|
290
|
-
}
|
|
291
|
-
if (result.status === "ignored-destroyed" && result.parentScope !== undefined) {
|
|
292
|
-
entry.parentScope = result.parentScope;
|
|
293
|
-
}
|
|
294
|
-
if (result.status === "redirected") {
|
|
295
|
-
entry.redirect = result.redirect;
|
|
296
|
-
}
|
|
297
|
-
return entry;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function isPlainObject(value) {
|
|
301
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
302
|
-
}
|
package/src/browser.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export { asyncSignal } from "./async-signal.js";
|
|
2
|
-
export { Async, createApp, defineApp, readSnapshot } from "./app.js";
|
|
3
|
-
export { attributeName, defineAttributeConfig } from "./attributes.js";
|
|
4
|
-
export { createBoundaryReceiver } from "./boundary-receiver.js";
|
|
5
|
-
export { createCacheRegistry, defineCache } from "./cache.js";
|
|
6
|
-
export { component, createComponentRegistry, defineComponent } from "./component.js";
|
|
7
|
-
export { delay } from "./delay.js";
|
|
8
|
-
export { defineAsyncContainerElement, defineAsyncSuspenseElement } from "./elements.js";
|
|
9
|
-
export { createHandlerRegistry } from "./handlers.js";
|
|
10
|
-
export { html } from "./html.js";
|
|
11
|
-
export { createLazyRegistry, defineRegistrySnapshot } from "./lazy-registry.js";
|
|
12
|
-
export { Loader, AsyncLoader } from "./loader.js";
|
|
13
|
-
export { createPartialRegistry } from "./partials.js";
|
|
14
|
-
export { createRegistryStore } from "./registry-store.js";
|
|
15
|
-
export { createRouteRegistry, createRouter, defineRoute, route } from "./router.js";
|
|
16
|
-
export { createScheduler } from "./scheduler.js";
|
|
17
|
-
export { applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult } from "./server.js";
|
|
18
|
-
export { computed, createSignal, createSignalRegistry, effect, signal } from "./signals.js";
|