@async/framework 0.11.5 → 0.11.6
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 +13 -0
- package/browser.js +172 -79
- package/browser.min.js +1 -1
- package/browser.ts +172 -79
- package/browser.umd.js +172 -79
- package/browser.umd.min.js +1 -1
- package/framework.ts +172 -79
- package/package.json +1 -1
- package/server.js +172 -79
package/framework.ts
CHANGED
|
@@ -22,8 +22,9 @@ const __asyncSignalModule = (() => {
|
|
|
22
22
|
let version = 0;
|
|
23
23
|
let registry;
|
|
24
24
|
let registeredId = id;
|
|
25
|
-
let
|
|
26
|
-
let
|
|
25
|
+
let activeRun;
|
|
26
|
+
let executionToken = 0;
|
|
27
|
+
let disposed = false;
|
|
27
28
|
const subscribers = new Set();
|
|
28
29
|
const dependencyCleanups = new Set();
|
|
29
30
|
|
|
@@ -65,109 +66,62 @@ const __asyncSignalModule = (() => {
|
|
|
65
66
|
},
|
|
66
67
|
|
|
67
68
|
refresh() {
|
|
68
|
-
if (!registry) {
|
|
69
|
+
if (!registry || disposed) {
|
|
69
70
|
throw new Error(`Async signal "${registeredId}" is not registered.`);
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
activeAbort.cancel(new Error(`Async signal "${registeredId}" refreshed.`));
|
|
74
|
-
}
|
|
73
|
+
cancelRun(activeRun, new Error(`Async signal "${registeredId}" refreshed.`));
|
|
75
74
|
|
|
75
|
+
const runRegistry = registry;
|
|
76
|
+
const runId = registeredId;
|
|
76
77
|
const runVersion = version + 1;
|
|
77
78
|
version = runVersion;
|
|
78
79
|
loading = true;
|
|
79
80
|
error = null;
|
|
80
81
|
status = "loading";
|
|
81
82
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
activeAbort = controller.signal;
|
|
85
|
-
attachCancel(activeAbort, controller);
|
|
83
|
+
const run = createRun(runRegistry, runId, runVersion);
|
|
84
|
+
activeRun = run;
|
|
86
85
|
notify();
|
|
87
86
|
|
|
88
|
-
const context =
|
|
89
|
-
signals: registry,
|
|
90
|
-
id: registeredId,
|
|
91
|
-
get server() {
|
|
92
|
-
const context = registry._context?.() ?? {};
|
|
93
|
-
const server = context.server;
|
|
94
|
-
if (typeof server?._withContext === "function") {
|
|
95
|
-
return server._withContext({
|
|
96
|
-
signals: registry,
|
|
97
|
-
router: context.router,
|
|
98
|
-
loader: context.loader,
|
|
99
|
-
cache: context.cache,
|
|
100
|
-
abort: activeAbort,
|
|
101
|
-
scheduler: context.scheduler
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return server;
|
|
105
|
-
},
|
|
106
|
-
get router() {
|
|
107
|
-
return registry._context?.().router;
|
|
108
|
-
},
|
|
109
|
-
get loader() {
|
|
110
|
-
return registry._context?.().loader;
|
|
111
|
-
},
|
|
112
|
-
get cache() {
|
|
113
|
-
return registry._context?.().cache;
|
|
114
|
-
},
|
|
115
|
-
get scheduler() {
|
|
116
|
-
return registry._context?.().scheduler;
|
|
117
|
-
},
|
|
118
|
-
get version() {
|
|
119
|
-
return runVersion;
|
|
120
|
-
},
|
|
121
|
-
get abort() {
|
|
122
|
-
return activeAbort;
|
|
123
|
-
},
|
|
124
|
-
refresh() {
|
|
125
|
-
return state.refresh();
|
|
126
|
-
}
|
|
127
|
-
};
|
|
87
|
+
const context = createRunContext(run);
|
|
128
88
|
|
|
129
89
|
let outcome;
|
|
130
90
|
try {
|
|
131
|
-
outcome =
|
|
91
|
+
outcome = runRegistry._collectDependencies(() => fn.call(context));
|
|
132
92
|
} catch (cause) {
|
|
133
|
-
finishError(
|
|
93
|
+
finishError(run, cause);
|
|
134
94
|
return Promise.reject(cause);
|
|
135
95
|
}
|
|
136
96
|
|
|
137
|
-
syncDependencies(outcome.dependencies);
|
|
97
|
+
syncDependencies(outcome.dependencies, run);
|
|
138
98
|
|
|
139
99
|
return Promise.resolve(outcome.value).then(
|
|
140
100
|
(nextValue) => {
|
|
141
|
-
if (!
|
|
101
|
+
if (!isRunCurrent(run)) {
|
|
142
102
|
return value;
|
|
143
103
|
}
|
|
144
104
|
value = nextValue;
|
|
145
105
|
loading = false;
|
|
146
106
|
error = null;
|
|
147
107
|
status = "ready";
|
|
108
|
+
activeRun = undefined;
|
|
148
109
|
notify();
|
|
149
110
|
return value;
|
|
150
111
|
},
|
|
151
112
|
(cause) => {
|
|
152
|
-
if (!
|
|
153
|
-
return value;
|
|
154
|
-
}
|
|
155
|
-
if (activeAbort?.aborted) {
|
|
156
|
-
loading = false;
|
|
157
|
-
status = value === undefined ? "idle" : "ready";
|
|
158
|
-
notify();
|
|
113
|
+
if (!isRunCurrent(run)) {
|
|
159
114
|
return value;
|
|
160
115
|
}
|
|
161
|
-
finishError(
|
|
116
|
+
finishError(run, cause);
|
|
162
117
|
return value;
|
|
163
118
|
}
|
|
164
119
|
);
|
|
165
120
|
},
|
|
166
121
|
|
|
167
122
|
cancel(reason) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
123
|
+
cancelCurrentRun(reason, { settle: true, notifyChange: true });
|
|
124
|
+
return value;
|
|
171
125
|
},
|
|
172
126
|
|
|
173
127
|
subscribe(fn) {
|
|
@@ -196,9 +150,7 @@ const __asyncSignalModule = (() => {
|
|
|
196
150
|
if (!isAsyncSignalSnapshot(snapshot)) {
|
|
197
151
|
return state.set(snapshot);
|
|
198
152
|
}
|
|
199
|
-
|
|
200
|
-
activeAbort.cancel(new Error(`Async signal "${registeredId}" restored from snapshot.`));
|
|
201
|
-
}
|
|
153
|
+
cancelCurrentRun(new Error(`Async signal "${registeredId}" restored from snapshot.`));
|
|
202
154
|
value = snapshot.value;
|
|
203
155
|
loading = Boolean(snapshot.loading);
|
|
204
156
|
error = snapshot.error ?? null;
|
|
@@ -214,7 +166,7 @@ const __asyncSignalModule = (() => {
|
|
|
214
166
|
registry = nextRegistry;
|
|
215
167
|
registeredId = nextId;
|
|
216
168
|
const start = () => {
|
|
217
|
-
if (registry === nextRegistry && status === "idle") {
|
|
169
|
+
if (!disposed && registry === nextRegistry && status === "idle") {
|
|
218
170
|
state.refresh();
|
|
219
171
|
}
|
|
220
172
|
};
|
|
@@ -230,30 +182,161 @@ const __asyncSignalModule = (() => {
|
|
|
230
182
|
},
|
|
231
183
|
|
|
232
184
|
_dispose() {
|
|
233
|
-
|
|
185
|
+
if (disposed) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
disposed = true;
|
|
189
|
+
cancelQueuedWork();
|
|
190
|
+
cancelCurrentRun(new Error(`Async signal "${registeredId}" disposed.`));
|
|
234
191
|
for (const cleanup of dependencyCleanups) {
|
|
235
192
|
cleanup();
|
|
236
193
|
}
|
|
237
194
|
dependencyCleanups.clear();
|
|
238
195
|
subscribers.clear();
|
|
196
|
+
registry = undefined;
|
|
239
197
|
}
|
|
240
198
|
};
|
|
241
199
|
|
|
242
|
-
function
|
|
243
|
-
|
|
200
|
+
function createRun(runRegistry, runId, runVersion) {
|
|
201
|
+
const controller = new AbortController();
|
|
202
|
+
const abort = controller.signal;
|
|
203
|
+
const runContext = captureRunContext(runRegistry, abort);
|
|
204
|
+
const run = {
|
|
205
|
+
token: ++executionToken,
|
|
206
|
+
version: runVersion,
|
|
207
|
+
registry: runRegistry,
|
|
208
|
+
id: runId,
|
|
209
|
+
controller,
|
|
210
|
+
abort,
|
|
211
|
+
...runContext
|
|
212
|
+
};
|
|
213
|
+
attachCancel(abort, controller, (reason) => {
|
|
214
|
+
cancelRun(run, reason, { settle: true, notifyChange: true });
|
|
215
|
+
});
|
|
216
|
+
return run;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function captureRunContext(runRegistry, abort) {
|
|
220
|
+
const context = runRegistry._context?.() ?? {};
|
|
221
|
+
const serverContext = {
|
|
222
|
+
signals: runRegistry,
|
|
223
|
+
router: context.router,
|
|
224
|
+
loader: context.loader,
|
|
225
|
+
cache: context.cache,
|
|
226
|
+
abort,
|
|
227
|
+
scheduler: context.scheduler
|
|
228
|
+
};
|
|
229
|
+
const server = typeof context.server?._withContext === "function"
|
|
230
|
+
? context.server._withContext(serverContext)
|
|
231
|
+
: context.server;
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
signals: runRegistry,
|
|
235
|
+
server,
|
|
236
|
+
router: context.router,
|
|
237
|
+
loader: context.loader,
|
|
238
|
+
cache: context.cache,
|
|
239
|
+
scheduler: context.scheduler
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function createRunContext(run) {
|
|
244
|
+
return {
|
|
245
|
+
signals: run.signals,
|
|
246
|
+
id: run.id,
|
|
247
|
+
get server() {
|
|
248
|
+
return run.server;
|
|
249
|
+
},
|
|
250
|
+
get router() {
|
|
251
|
+
return run.router;
|
|
252
|
+
},
|
|
253
|
+
get loader() {
|
|
254
|
+
return run.loader;
|
|
255
|
+
},
|
|
256
|
+
get cache() {
|
|
257
|
+
return run.cache;
|
|
258
|
+
},
|
|
259
|
+
get scheduler() {
|
|
260
|
+
return run.scheduler;
|
|
261
|
+
},
|
|
262
|
+
get version() {
|
|
263
|
+
return run.version;
|
|
264
|
+
},
|
|
265
|
+
get abort() {
|
|
266
|
+
return run.abort;
|
|
267
|
+
},
|
|
268
|
+
refresh() {
|
|
269
|
+
return state.refresh();
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function finishError(run, cause) {
|
|
275
|
+
if (!isRunCurrent(run)) {
|
|
244
276
|
return;
|
|
245
277
|
}
|
|
246
278
|
loading = false;
|
|
247
279
|
error = cause;
|
|
248
280
|
status = "error";
|
|
281
|
+
activeRun = undefined;
|
|
249
282
|
notify();
|
|
250
283
|
}
|
|
251
284
|
|
|
252
|
-
function
|
|
253
|
-
return
|
|
285
|
+
function isRunCurrent(run) {
|
|
286
|
+
return Boolean(run)
|
|
287
|
+
&& !disposed
|
|
288
|
+
&& activeRun === run
|
|
289
|
+
&& run.token === executionToken
|
|
290
|
+
&& run.registry === registry
|
|
291
|
+
&& run.id === registeredId
|
|
292
|
+
&& !run.abort.aborted;
|
|
254
293
|
}
|
|
255
294
|
|
|
256
|
-
function
|
|
295
|
+
function cancelCurrentRun(reason, options = {}) {
|
|
296
|
+
const run = activeRun;
|
|
297
|
+
const shouldSettle = Boolean(run) || loading;
|
|
298
|
+
if (run) {
|
|
299
|
+
cancelRun(run, reason);
|
|
300
|
+
} else if (shouldSettle) {
|
|
301
|
+
executionToken += 1;
|
|
302
|
+
}
|
|
303
|
+
if (options.settle && shouldSettle && !disposed) {
|
|
304
|
+
settleCanceled(options.notifyChange);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function cancelRun(run, reason, options = {}) {
|
|
309
|
+
if (!run) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const wasActive = activeRun === run;
|
|
313
|
+
if (wasActive) {
|
|
314
|
+
executionToken += 1;
|
|
315
|
+
activeRun = undefined;
|
|
316
|
+
}
|
|
317
|
+
if (!run.abort.aborted) {
|
|
318
|
+
run.controller.abort(reason);
|
|
319
|
+
}
|
|
320
|
+
if (wasActive && options.settle && !disposed) {
|
|
321
|
+
settleCanceled(options.notifyChange);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function settleCanceled(notifyChange = false) {
|
|
326
|
+
const nextStatus = value === undefined ? "idle" : "ready";
|
|
327
|
+
const changed = loading || error !== null || status !== nextStatus;
|
|
328
|
+
loading = false;
|
|
329
|
+
error = null;
|
|
330
|
+
status = nextStatus;
|
|
331
|
+
if (notifyChange && changed) {
|
|
332
|
+
notify();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function syncDependencies(dependencies, run) {
|
|
337
|
+
if (!isRunCurrent(run)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
257
340
|
for (const cleanup of dependencyCleanups) {
|
|
258
341
|
cleanup();
|
|
259
342
|
}
|
|
@@ -262,15 +345,16 @@ const __asyncSignalModule = (() => {
|
|
|
262
345
|
for (const dependency of dependencies) {
|
|
263
346
|
const dependencyId = String(dependency).split(".")[0];
|
|
264
347
|
if (dependencyId && dependencyId !== registeredId) {
|
|
265
|
-
dependencyCleanups.add(registry.subscribe(dependency, () => scheduleRefresh()));
|
|
348
|
+
dependencyCleanups.add(run.registry.subscribe(dependency, () => scheduleRefresh()));
|
|
266
349
|
}
|
|
267
350
|
}
|
|
268
351
|
}
|
|
269
352
|
|
|
270
353
|
function scheduleRefresh() {
|
|
271
|
-
if (
|
|
272
|
-
|
|
354
|
+
if (disposed || !registry) {
|
|
355
|
+
return;
|
|
273
356
|
}
|
|
357
|
+
cancelRun(activeRun, new Error(`Async signal "${registeredId}" dependency changed.`));
|
|
274
358
|
const scheduler = registry?._context?.().scheduler;
|
|
275
359
|
if (!scheduler) {
|
|
276
360
|
state.refresh();
|
|
@@ -282,6 +366,11 @@ const __asyncSignalModule = (() => {
|
|
|
282
366
|
});
|
|
283
367
|
}
|
|
284
368
|
|
|
369
|
+
function cancelQueuedWork() {
|
|
370
|
+
const scheduler = registry?._context?.().scheduler;
|
|
371
|
+
scheduler?.cancelScope?.(registeredId);
|
|
372
|
+
}
|
|
373
|
+
|
|
285
374
|
function notify() {
|
|
286
375
|
for (const subscriber of [...subscribers]) {
|
|
287
376
|
subscriber(state);
|
|
@@ -316,11 +405,15 @@ const __asyncSignalModule = (() => {
|
|
|
316
405
|
return value === undefined ? "idle" : "ready";
|
|
317
406
|
}
|
|
318
407
|
|
|
319
|
-
function attachCancel(signal, controller) {
|
|
408
|
+
function attachCancel(signal, controller, onCancel) {
|
|
320
409
|
Object.defineProperty(signal, "cancel", {
|
|
321
410
|
configurable: true,
|
|
322
411
|
enumerable: false,
|
|
323
412
|
value(reason) {
|
|
413
|
+
if (typeof onCancel === "function") {
|
|
414
|
+
onCancel(reason);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
324
417
|
controller.abort(reason);
|
|
325
418
|
}
|
|
326
419
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@async/framework",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.6",
|
|
4
4
|
"description": "No-build Loader app runtime with browser and server entrypoints, signals, command events, route partials, cache split, SSR activation, and streaming boundaries.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./server.js",
|