@async/framework 0.11.5 → 0.11.7
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 +29 -0
- package/README.md +1 -0
- package/browser.js +192 -104
- package/browser.min.js +1 -1
- package/browser.ts +192 -104
- package/browser.umd.js +192 -104
- package/browser.umd.min.js +1 -1
- package/framework.ts +194 -106
- package/package.json +1 -1
- package/server.js +194 -106
package/browser.ts
CHANGED
|
@@ -20,8 +20,9 @@ const __asyncSignalModule = (() => {
|
|
|
20
20
|
let version = 0;
|
|
21
21
|
let registry;
|
|
22
22
|
let registeredId = id;
|
|
23
|
-
let
|
|
24
|
-
let
|
|
23
|
+
let activeRun;
|
|
24
|
+
let executionToken = 0;
|
|
25
|
+
let disposed = false;
|
|
25
26
|
const subscribers = new Set();
|
|
26
27
|
const dependencyCleanups = new Set();
|
|
27
28
|
|
|
@@ -63,109 +64,62 @@ const __asyncSignalModule = (() => {
|
|
|
63
64
|
},
|
|
64
65
|
|
|
65
66
|
refresh() {
|
|
66
|
-
if (!registry) {
|
|
67
|
+
if (!registry || disposed) {
|
|
67
68
|
throw new Error(`Async signal "${registeredId}" is not registered.`);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
activeAbort.cancel(new Error(`Async signal "${registeredId}" refreshed.`));
|
|
72
|
-
}
|
|
71
|
+
cancelRun(activeRun, new Error(`Async signal "${registeredId}" refreshed.`));
|
|
73
72
|
|
|
73
|
+
const runRegistry = registry;
|
|
74
|
+
const runId = registeredId;
|
|
74
75
|
const runVersion = version + 1;
|
|
75
76
|
version = runVersion;
|
|
76
77
|
loading = true;
|
|
77
78
|
error = null;
|
|
78
79
|
status = "loading";
|
|
79
80
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
activeAbort = controller.signal;
|
|
83
|
-
attachCancel(activeAbort, controller);
|
|
81
|
+
const run = createRun(runRegistry, runId, runVersion);
|
|
82
|
+
activeRun = run;
|
|
84
83
|
notify();
|
|
85
84
|
|
|
86
|
-
const context =
|
|
87
|
-
signals: registry,
|
|
88
|
-
id: registeredId,
|
|
89
|
-
get server() {
|
|
90
|
-
const context = registry._context?.() ?? {};
|
|
91
|
-
const server = context.server;
|
|
92
|
-
if (typeof server?._withContext === "function") {
|
|
93
|
-
return server._withContext({
|
|
94
|
-
signals: registry,
|
|
95
|
-
router: context.router,
|
|
96
|
-
loader: context.loader,
|
|
97
|
-
cache: context.cache,
|
|
98
|
-
abort: activeAbort,
|
|
99
|
-
scheduler: context.scheduler
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
return server;
|
|
103
|
-
},
|
|
104
|
-
get router() {
|
|
105
|
-
return registry._context?.().router;
|
|
106
|
-
},
|
|
107
|
-
get loader() {
|
|
108
|
-
return registry._context?.().loader;
|
|
109
|
-
},
|
|
110
|
-
get cache() {
|
|
111
|
-
return registry._context?.().cache;
|
|
112
|
-
},
|
|
113
|
-
get scheduler() {
|
|
114
|
-
return registry._context?.().scheduler;
|
|
115
|
-
},
|
|
116
|
-
get version() {
|
|
117
|
-
return runVersion;
|
|
118
|
-
},
|
|
119
|
-
get abort() {
|
|
120
|
-
return activeAbort;
|
|
121
|
-
},
|
|
122
|
-
refresh() {
|
|
123
|
-
return state.refresh();
|
|
124
|
-
}
|
|
125
|
-
};
|
|
85
|
+
const context = createRunContext(run);
|
|
126
86
|
|
|
127
87
|
let outcome;
|
|
128
88
|
try {
|
|
129
|
-
outcome =
|
|
89
|
+
outcome = runRegistry._collectDependencies(() => fn.call(context));
|
|
130
90
|
} catch (cause) {
|
|
131
|
-
finishError(
|
|
91
|
+
finishError(run, cause);
|
|
132
92
|
return Promise.reject(cause);
|
|
133
93
|
}
|
|
134
94
|
|
|
135
|
-
syncDependencies(outcome.dependencies);
|
|
95
|
+
syncDependencies(outcome.dependencies, run);
|
|
136
96
|
|
|
137
97
|
return Promise.resolve(outcome.value).then(
|
|
138
98
|
(nextValue) => {
|
|
139
|
-
if (!
|
|
99
|
+
if (!isRunCurrent(run)) {
|
|
140
100
|
return value;
|
|
141
101
|
}
|
|
142
102
|
value = nextValue;
|
|
143
103
|
loading = false;
|
|
144
104
|
error = null;
|
|
145
105
|
status = "ready";
|
|
106
|
+
activeRun = undefined;
|
|
146
107
|
notify();
|
|
147
108
|
return value;
|
|
148
109
|
},
|
|
149
110
|
(cause) => {
|
|
150
|
-
if (!
|
|
111
|
+
if (!isRunCurrent(run)) {
|
|
151
112
|
return value;
|
|
152
113
|
}
|
|
153
|
-
|
|
154
|
-
loading = false;
|
|
155
|
-
status = value === undefined ? "idle" : "ready";
|
|
156
|
-
notify();
|
|
157
|
-
return value;
|
|
158
|
-
}
|
|
159
|
-
finishError(runVersion, cause);
|
|
114
|
+
finishError(run, cause);
|
|
160
115
|
return value;
|
|
161
116
|
}
|
|
162
117
|
);
|
|
163
118
|
},
|
|
164
119
|
|
|
165
120
|
cancel(reason) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
121
|
+
cancelCurrentRun(reason, { settle: true, notifyChange: true });
|
|
122
|
+
return value;
|
|
169
123
|
},
|
|
170
124
|
|
|
171
125
|
subscribe(fn) {
|
|
@@ -194,9 +148,7 @@ const __asyncSignalModule = (() => {
|
|
|
194
148
|
if (!isAsyncSignalSnapshot(snapshot)) {
|
|
195
149
|
return state.set(snapshot);
|
|
196
150
|
}
|
|
197
|
-
|
|
198
|
-
activeAbort.cancel(new Error(`Async signal "${registeredId}" restored from snapshot.`));
|
|
199
|
-
}
|
|
151
|
+
cancelCurrentRun(new Error(`Async signal "${registeredId}" restored from snapshot.`));
|
|
200
152
|
value = snapshot.value;
|
|
201
153
|
loading = Boolean(snapshot.loading);
|
|
202
154
|
error = snapshot.error ?? null;
|
|
@@ -212,7 +164,7 @@ const __asyncSignalModule = (() => {
|
|
|
212
164
|
registry = nextRegistry;
|
|
213
165
|
registeredId = nextId;
|
|
214
166
|
const start = () => {
|
|
215
|
-
if (registry === nextRegistry && status === "idle") {
|
|
167
|
+
if (!disposed && registry === nextRegistry && status === "idle") {
|
|
216
168
|
state.refresh();
|
|
217
169
|
}
|
|
218
170
|
};
|
|
@@ -228,30 +180,161 @@ const __asyncSignalModule = (() => {
|
|
|
228
180
|
},
|
|
229
181
|
|
|
230
182
|
_dispose() {
|
|
231
|
-
|
|
183
|
+
if (disposed) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
disposed = true;
|
|
187
|
+
cancelQueuedWork();
|
|
188
|
+
cancelCurrentRun(new Error(`Async signal "${registeredId}" disposed.`));
|
|
232
189
|
for (const cleanup of dependencyCleanups) {
|
|
233
190
|
cleanup();
|
|
234
191
|
}
|
|
235
192
|
dependencyCleanups.clear();
|
|
236
193
|
subscribers.clear();
|
|
194
|
+
registry = undefined;
|
|
237
195
|
}
|
|
238
196
|
};
|
|
239
197
|
|
|
240
|
-
function
|
|
241
|
-
|
|
198
|
+
function createRun(runRegistry, runId, runVersion) {
|
|
199
|
+
const controller = new AbortController();
|
|
200
|
+
const abort = controller.signal;
|
|
201
|
+
const runContext = captureRunContext(runRegistry, abort);
|
|
202
|
+
const run = {
|
|
203
|
+
token: ++executionToken,
|
|
204
|
+
version: runVersion,
|
|
205
|
+
registry: runRegistry,
|
|
206
|
+
id: runId,
|
|
207
|
+
controller,
|
|
208
|
+
abort,
|
|
209
|
+
...runContext
|
|
210
|
+
};
|
|
211
|
+
attachCancel(abort, controller, (reason) => {
|
|
212
|
+
cancelRun(run, reason, { settle: true, notifyChange: true });
|
|
213
|
+
});
|
|
214
|
+
return run;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function captureRunContext(runRegistry, abort) {
|
|
218
|
+
const context = runRegistry._context?.() ?? {};
|
|
219
|
+
const serverContext = {
|
|
220
|
+
signals: runRegistry,
|
|
221
|
+
router: context.router,
|
|
222
|
+
loader: context.loader,
|
|
223
|
+
cache: context.cache,
|
|
224
|
+
abort,
|
|
225
|
+
scheduler: context.scheduler
|
|
226
|
+
};
|
|
227
|
+
const server = typeof context.server?._withContext === "function"
|
|
228
|
+
? context.server._withContext(serverContext)
|
|
229
|
+
: context.server;
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
signals: runRegistry,
|
|
233
|
+
server,
|
|
234
|
+
router: context.router,
|
|
235
|
+
loader: context.loader,
|
|
236
|
+
cache: context.cache,
|
|
237
|
+
scheduler: context.scheduler
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function createRunContext(run) {
|
|
242
|
+
return {
|
|
243
|
+
signals: run.signals,
|
|
244
|
+
id: run.id,
|
|
245
|
+
get server() {
|
|
246
|
+
return run.server;
|
|
247
|
+
},
|
|
248
|
+
get router() {
|
|
249
|
+
return run.router;
|
|
250
|
+
},
|
|
251
|
+
get loader() {
|
|
252
|
+
return run.loader;
|
|
253
|
+
},
|
|
254
|
+
get cache() {
|
|
255
|
+
return run.cache;
|
|
256
|
+
},
|
|
257
|
+
get scheduler() {
|
|
258
|
+
return run.scheduler;
|
|
259
|
+
},
|
|
260
|
+
get version() {
|
|
261
|
+
return run.version;
|
|
262
|
+
},
|
|
263
|
+
get abort() {
|
|
264
|
+
return run.abort;
|
|
265
|
+
},
|
|
266
|
+
refresh() {
|
|
267
|
+
return state.refresh();
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function finishError(run, cause) {
|
|
273
|
+
if (!isRunCurrent(run)) {
|
|
242
274
|
return;
|
|
243
275
|
}
|
|
244
276
|
loading = false;
|
|
245
277
|
error = cause;
|
|
246
278
|
status = "error";
|
|
279
|
+
activeRun = undefined;
|
|
247
280
|
notify();
|
|
248
281
|
}
|
|
249
282
|
|
|
250
|
-
function
|
|
251
|
-
return
|
|
283
|
+
function isRunCurrent(run) {
|
|
284
|
+
return Boolean(run)
|
|
285
|
+
&& !disposed
|
|
286
|
+
&& activeRun === run
|
|
287
|
+
&& run.token === executionToken
|
|
288
|
+
&& run.registry === registry
|
|
289
|
+
&& run.id === registeredId
|
|
290
|
+
&& !run.abort.aborted;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function cancelCurrentRun(reason, options = {}) {
|
|
294
|
+
const run = activeRun;
|
|
295
|
+
const shouldSettle = Boolean(run) || loading;
|
|
296
|
+
if (run) {
|
|
297
|
+
cancelRun(run, reason);
|
|
298
|
+
} else if (shouldSettle) {
|
|
299
|
+
executionToken += 1;
|
|
300
|
+
}
|
|
301
|
+
if (options.settle && shouldSettle && !disposed) {
|
|
302
|
+
settleCanceled(options.notifyChange);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function cancelRun(run, reason, options = {}) {
|
|
307
|
+
if (!run) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const wasActive = activeRun === run;
|
|
311
|
+
if (wasActive) {
|
|
312
|
+
executionToken += 1;
|
|
313
|
+
activeRun = undefined;
|
|
314
|
+
}
|
|
315
|
+
if (!run.abort.aborted) {
|
|
316
|
+
run.controller.abort(reason);
|
|
317
|
+
}
|
|
318
|
+
if (wasActive && options.settle && !disposed) {
|
|
319
|
+
settleCanceled(options.notifyChange);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function settleCanceled(notifyChange = false) {
|
|
324
|
+
const nextStatus = value === undefined ? "idle" : "ready";
|
|
325
|
+
const changed = loading || error !== null || status !== nextStatus;
|
|
326
|
+
loading = false;
|
|
327
|
+
error = null;
|
|
328
|
+
status = nextStatus;
|
|
329
|
+
if (notifyChange && changed) {
|
|
330
|
+
notify();
|
|
331
|
+
}
|
|
252
332
|
}
|
|
253
333
|
|
|
254
|
-
function syncDependencies(dependencies) {
|
|
334
|
+
function syncDependencies(dependencies, run) {
|
|
335
|
+
if (!isRunCurrent(run)) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
255
338
|
for (const cleanup of dependencyCleanups) {
|
|
256
339
|
cleanup();
|
|
257
340
|
}
|
|
@@ -260,15 +343,16 @@ const __asyncSignalModule = (() => {
|
|
|
260
343
|
for (const dependency of dependencies) {
|
|
261
344
|
const dependencyId = String(dependency).split(".")[0];
|
|
262
345
|
if (dependencyId && dependencyId !== registeredId) {
|
|
263
|
-
dependencyCleanups.add(registry.subscribe(dependency, () => scheduleRefresh()));
|
|
346
|
+
dependencyCleanups.add(run.registry.subscribe(dependency, () => scheduleRefresh()));
|
|
264
347
|
}
|
|
265
348
|
}
|
|
266
349
|
}
|
|
267
350
|
|
|
268
351
|
function scheduleRefresh() {
|
|
269
|
-
if (
|
|
270
|
-
|
|
352
|
+
if (disposed || !registry) {
|
|
353
|
+
return;
|
|
271
354
|
}
|
|
355
|
+
cancelRun(activeRun, new Error(`Async signal "${registeredId}" dependency changed.`));
|
|
272
356
|
const scheduler = registry?._context?.().scheduler;
|
|
273
357
|
if (!scheduler) {
|
|
274
358
|
state.refresh();
|
|
@@ -280,6 +364,11 @@ const __asyncSignalModule = (() => {
|
|
|
280
364
|
});
|
|
281
365
|
}
|
|
282
366
|
|
|
367
|
+
function cancelQueuedWork() {
|
|
368
|
+
const scheduler = registry?._context?.().scheduler;
|
|
369
|
+
scheduler?.cancelScope?.(registeredId);
|
|
370
|
+
}
|
|
371
|
+
|
|
283
372
|
function notify() {
|
|
284
373
|
for (const subscriber of [...subscribers]) {
|
|
285
374
|
subscriber(state);
|
|
@@ -314,11 +403,15 @@ const __asyncSignalModule = (() => {
|
|
|
314
403
|
return value === undefined ? "idle" : "ready";
|
|
315
404
|
}
|
|
316
405
|
|
|
317
|
-
function attachCancel(signal, controller) {
|
|
406
|
+
function attachCancel(signal, controller, onCancel) {
|
|
318
407
|
Object.defineProperty(signal, "cancel", {
|
|
319
408
|
configurable: true,
|
|
320
409
|
enumerable: false,
|
|
321
410
|
value(reason) {
|
|
411
|
+
if (typeof onCancel === "function") {
|
|
412
|
+
onCancel(reason);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
322
415
|
controller.abort(reason);
|
|
323
416
|
}
|
|
324
417
|
});
|
|
@@ -2286,9 +2379,10 @@ const __componentModule = (() => {
|
|
|
2286
2379
|
})();
|
|
2287
2380
|
|
|
2288
2381
|
const __serverModule = (() => {
|
|
2289
|
-
const
|
|
2382
|
+
const serverEnvelopeKind = Symbol.for("@async/framework.serverResult");
|
|
2383
|
+
const serverEnvelopeWireKey = "__async_server_result__";
|
|
2384
|
+
const serverEnvelopeWireVersion = 1;
|
|
2290
2385
|
const appliedServerResult = Symbol.for("@async/framework.appliedServerResult");
|
|
2291
|
-
const appliedServerValues = new WeakSet();
|
|
2292
2386
|
|
|
2293
2387
|
function createServerProxy({
|
|
2294
2388
|
endpoint = "/__async/server",
|
|
@@ -2331,9 +2425,7 @@ const __serverModule = (() => {
|
|
|
2331
2425
|
throw new Error(`Server function "${id}" failed with ${response.status}.`);
|
|
2332
2426
|
}
|
|
2333
2427
|
|
|
2334
|
-
|
|
2335
|
-
await applyServerResult(result, runContext);
|
|
2336
|
-
return markAppliedServerValue(unwrapServerResult(result));
|
|
2428
|
+
return consumeServerResult(await readServerResponse(id, response), runContext);
|
|
2337
2429
|
}
|
|
2338
2430
|
|
|
2339
2431
|
return createServerNamespace(run, {
|
|
@@ -2368,7 +2460,7 @@ const __serverModule = (() => {
|
|
|
2368
2460
|
if (!isServerEnvelope(result)) {
|
|
2369
2461
|
return result;
|
|
2370
2462
|
}
|
|
2371
|
-
if (result[appliedServerResult]
|
|
2463
|
+
if (result[appliedServerResult]) {
|
|
2372
2464
|
return result;
|
|
2373
2465
|
}
|
|
2374
2466
|
|
|
@@ -2400,18 +2492,16 @@ const __serverModule = (() => {
|
|
|
2400
2492
|
return result;
|
|
2401
2493
|
}
|
|
2402
2494
|
|
|
2403
|
-
function
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
}
|
|
2407
|
-
return result;
|
|
2495
|
+
async function consumeServerResult(result, context = {}) {
|
|
2496
|
+
await applyServerResult(result, context);
|
|
2497
|
+
return unwrapServerResult(result);
|
|
2408
2498
|
}
|
|
2409
2499
|
|
|
2410
|
-
function
|
|
2411
|
-
if (
|
|
2412
|
-
|
|
2500
|
+
function unwrapServerResult(result) {
|
|
2501
|
+
if (isServerEnvelope(result)) {
|
|
2502
|
+
return Object.hasOwn(result, "value") ? result.value : undefined;
|
|
2413
2503
|
}
|
|
2414
|
-
return
|
|
2504
|
+
return result;
|
|
2415
2505
|
}
|
|
2416
2506
|
|
|
2417
2507
|
function markAppliedServerResult(result) {
|
|
@@ -2455,9 +2545,7 @@ const __serverModule = (() => {
|
|
|
2455
2545
|
throw new Error("Server namespace is not directly callable.");
|
|
2456
2546
|
}
|
|
2457
2547
|
const context = contextProvider() ?? {};
|
|
2458
|
-
|
|
2459
|
-
await applyServerResult(result, context);
|
|
2460
|
-
return unwrapServerResult(result);
|
|
2548
|
+
return run(parts.join("."), args, context);
|
|
2461
2549
|
};
|
|
2462
2550
|
|
|
2463
2551
|
const proxy = new Proxy(callable, {
|
|
@@ -2514,7 +2602,7 @@ const __serverModule = (() => {
|
|
|
2514
2602
|
|
|
2515
2603
|
async function readServerResponse(id, response) {
|
|
2516
2604
|
if (response.status === 204) {
|
|
2517
|
-
return
|
|
2605
|
+
return undefined;
|
|
2518
2606
|
}
|
|
2519
2607
|
const type = response.headers.get("content-type") ?? "";
|
|
2520
2608
|
if (type.includes("application/json")) {
|
|
@@ -2532,7 +2620,7 @@ const __serverModule = (() => {
|
|
|
2532
2620
|
if (typeof response.text !== "function") {
|
|
2533
2621
|
throw new Error(`Server function "${id}" transport returned an invalid response: missing text().`);
|
|
2534
2622
|
}
|
|
2535
|
-
return
|
|
2623
|
+
return response.text();
|
|
2536
2624
|
}
|
|
2537
2625
|
|
|
2538
2626
|
function snapshotSignalPaths(paths = [], signals) {
|
|
@@ -2677,7 +2765,8 @@ const __serverModule = (() => {
|
|
|
2677
2765
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2678
2766
|
return false;
|
|
2679
2767
|
}
|
|
2680
|
-
return
|
|
2768
|
+
return value[serverEnvelopeKind] === true
|
|
2769
|
+
|| value[serverEnvelopeWireKey] === serverEnvelopeWireVersion;
|
|
2681
2770
|
}
|
|
2682
2771
|
|
|
2683
2772
|
function toError(value) {
|
|
@@ -2699,11 +2788,11 @@ const __serverModule = (() => {
|
|
|
2699
2788
|
throw new TypeError("Server function id must be a non-empty string.");
|
|
2700
2789
|
}
|
|
2701
2790
|
}
|
|
2702
|
-
return { createServerProxy, resolveServerCommandArguments, applyServerResult, unwrapServerResult, defaultInput, createServerNamespace, createSignalReader, assertServerId };
|
|
2791
|
+
return { createServerProxy, resolveServerCommandArguments, applyServerResult, consumeServerResult, unwrapServerResult, defaultInput, createServerNamespace, createSignalReader, assertServerId };
|
|
2703
2792
|
})();
|
|
2704
2793
|
|
|
2705
2794
|
const __handlersModule = (() => {
|
|
2706
|
-
const {
|
|
2795
|
+
const { defaultInput, resolveServerCommandArguments } = __serverModule;
|
|
2707
2796
|
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
2708
2797
|
const { createLazyRegistry, isLazyDescriptor } = __lazyRegistryModule;
|
|
2709
2798
|
const builtInTokens = new Set(["prevent", "preventDefault", "stopPropagation", "stopImmediatePropagation"]);
|
|
@@ -2801,8 +2890,7 @@ const __handlersModule = (() => {
|
|
|
2801
2890
|
signalPaths: resolved.signalPaths,
|
|
2802
2891
|
signalValues: resolved.signalValues
|
|
2803
2892
|
});
|
|
2804
|
-
|
|
2805
|
-
results.push(unwrapServerResult(result));
|
|
2893
|
+
results.push(result);
|
|
2806
2894
|
continue;
|
|
2807
2895
|
}
|
|
2808
2896
|
|