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