@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/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 (!
|
|
151
|
-
return value;
|
|
152
|
-
}
|
|
153
|
-
if (activeAbort?.aborted) {
|
|
154
|
-
loading = false;
|
|
155
|
-
status = value === undefined ? "idle" : "ready";
|
|
156
|
-
notify();
|
|
111
|
+
if (!isRunCurrent(run)) {
|
|
157
112
|
return value;
|
|
158
113
|
}
|
|
159
|
-
finishError(
|
|
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;
|
|
252
291
|
}
|
|
253
292
|
|
|
254
|
-
function
|
|
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
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
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
|
});
|