@async/framework 0.10.2 → 0.11.0
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 +20 -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/scheduler.js
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
const defaultPhases = ["binding", "lifecycle", "effect", "async", "post"];
|
|
2
|
-
|
|
3
|
-
export function createScheduler(options = {}) {
|
|
4
|
-
const phases = [...(options.phases ?? defaultPhases)];
|
|
5
|
-
const queues = new Map(phases.map((phase) => [phase, []]));
|
|
6
|
-
const keyedJobs = new Map();
|
|
7
|
-
const destroyedScopes = new Set();
|
|
8
|
-
const objectScopeIds = new WeakMap();
|
|
9
|
-
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
10
|
-
const maxDepth = options.maxDepth ?? 100;
|
|
11
|
-
const strategy = options.strategy ?? "microtask";
|
|
12
|
-
let destroyed = false;
|
|
13
|
-
let flushing = false;
|
|
14
|
-
let scheduled = false;
|
|
15
|
-
let batchDepth = 0;
|
|
16
|
-
let jobCounter = 0;
|
|
17
|
-
let scopeCounter = 0;
|
|
18
|
-
|
|
19
|
-
const api = {
|
|
20
|
-
strategy,
|
|
21
|
-
phases,
|
|
22
|
-
|
|
23
|
-
batch(fn) {
|
|
24
|
-
if (typeof fn !== "function") {
|
|
25
|
-
throw new TypeError("scheduler.batch(fn) requires a function.");
|
|
26
|
-
}
|
|
27
|
-
assertActive();
|
|
28
|
-
batchDepth += 1;
|
|
29
|
-
let asyncBatch = false;
|
|
30
|
-
try {
|
|
31
|
-
const value = fn();
|
|
32
|
-
if (value && typeof value.then === "function") {
|
|
33
|
-
asyncBatch = true;
|
|
34
|
-
return value.finally(() => {
|
|
35
|
-
batchDepth -= 1;
|
|
36
|
-
requestFlush();
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return value;
|
|
40
|
-
} finally {
|
|
41
|
-
if (!asyncBatch && batchDepth > 0) {
|
|
42
|
-
batchDepth -= 1;
|
|
43
|
-
requestFlush();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
enqueue(phase, fn, options = {}) {
|
|
49
|
-
assertActive();
|
|
50
|
-
assertPhase(phase);
|
|
51
|
-
if (typeof fn !== "function") {
|
|
52
|
-
throw new TypeError("scheduler.enqueue(phase, fn) requires a function.");
|
|
53
|
-
}
|
|
54
|
-
const scope = options.scope;
|
|
55
|
-
if (scope !== undefined && destroyedScopes.has(scope)) {
|
|
56
|
-
return noop;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const dedupeKey = options.key === undefined ? undefined : `${phase}:${scopeKey(scope)}:${String(options.key)}`;
|
|
60
|
-
if (dedupeKey && keyedJobs.has(dedupeKey)) {
|
|
61
|
-
return keyedJobs.get(dedupeKey).cancel;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const job = {
|
|
65
|
-
id: ++jobCounter,
|
|
66
|
-
phase,
|
|
67
|
-
fn,
|
|
68
|
-
scope,
|
|
69
|
-
boundary: options.boundary,
|
|
70
|
-
key: dedupeKey,
|
|
71
|
-
canceled: false,
|
|
72
|
-
cancel() {
|
|
73
|
-
job.canceled = true;
|
|
74
|
-
if (job.key) {
|
|
75
|
-
keyedJobs.delete(job.key);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
queues.get(phase).push(job);
|
|
80
|
-
if (job.key) {
|
|
81
|
-
keyedJobs.set(job.key, job);
|
|
82
|
-
}
|
|
83
|
-
requestFlush();
|
|
84
|
-
return job.cancel;
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
afterFlush(fn, options = {}) {
|
|
88
|
-
return api.enqueue("post", fn, options);
|
|
89
|
-
},
|
|
90
|
-
|
|
91
|
-
async flush() {
|
|
92
|
-
assertActive();
|
|
93
|
-
if (flushing) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
scheduled = false;
|
|
97
|
-
flushing = true;
|
|
98
|
-
let depth = 0;
|
|
99
|
-
try {
|
|
100
|
-
while (hasJobs()) {
|
|
101
|
-
depth += 1;
|
|
102
|
-
if (depth > maxDepth) {
|
|
103
|
-
throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
|
|
104
|
-
}
|
|
105
|
-
for (const phase of phases) {
|
|
106
|
-
await flushPhase(phase);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
} finally {
|
|
110
|
-
flushing = false;
|
|
111
|
-
if (hasJobs()) {
|
|
112
|
-
requestFlush();
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
async flushScope(scope) {
|
|
118
|
-
assertActive();
|
|
119
|
-
if (flushing) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
scheduled = false;
|
|
123
|
-
flushing = true;
|
|
124
|
-
let depth = 0;
|
|
125
|
-
try {
|
|
126
|
-
while (hasJobsForScope(scope)) {
|
|
127
|
-
depth += 1;
|
|
128
|
-
if (depth > maxDepth) {
|
|
129
|
-
throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
|
|
130
|
-
}
|
|
131
|
-
for (const phase of phases) {
|
|
132
|
-
await flushPhase(phase, scope);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
} finally {
|
|
136
|
-
flushing = false;
|
|
137
|
-
if (hasJobs()) {
|
|
138
|
-
requestFlush();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
cancelScope(scope) {
|
|
144
|
-
if (scope === undefined) {
|
|
145
|
-
return api;
|
|
146
|
-
}
|
|
147
|
-
for (const queue of queues.values()) {
|
|
148
|
-
for (const job of queue) {
|
|
149
|
-
if (job.scope === scope) {
|
|
150
|
-
job.cancel();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return api;
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
markScopeDestroyed(scope) {
|
|
158
|
-
if (scope !== undefined) {
|
|
159
|
-
destroyedScopes.add(scope);
|
|
160
|
-
api.cancelScope(scope);
|
|
161
|
-
}
|
|
162
|
-
return api;
|
|
163
|
-
},
|
|
164
|
-
|
|
165
|
-
isScopeDestroyed(scope) {
|
|
166
|
-
return scope !== undefined && destroyedScopes.has(scope);
|
|
167
|
-
},
|
|
168
|
-
|
|
169
|
-
inspect() {
|
|
170
|
-
const counts = {};
|
|
171
|
-
for (const [phase, queue] of queues) {
|
|
172
|
-
counts[phase] = queue.filter((job) => !job.canceled).length;
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
strategy,
|
|
176
|
-
phases: [...phases],
|
|
177
|
-
pending: counts,
|
|
178
|
-
scopesDestroyed: destroyedScopes.size,
|
|
179
|
-
flushing,
|
|
180
|
-
scheduled
|
|
181
|
-
};
|
|
182
|
-
},
|
|
183
|
-
|
|
184
|
-
destroy() {
|
|
185
|
-
destroyed = true;
|
|
186
|
-
for (const queue of queues.values()) {
|
|
187
|
-
for (const job of queue) {
|
|
188
|
-
job.cancel();
|
|
189
|
-
}
|
|
190
|
-
queue.length = 0;
|
|
191
|
-
}
|
|
192
|
-
keyedJobs.clear();
|
|
193
|
-
destroyedScopes.clear();
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
return api;
|
|
198
|
-
|
|
199
|
-
function requestFlush() {
|
|
200
|
-
if (strategy === "manual" || destroyed || flushing || batchDepth > 0 || scheduled) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
scheduled = true;
|
|
204
|
-
scheduleMicrotask(() => {
|
|
205
|
-
if (!destroyed) {
|
|
206
|
-
void api.flush();
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async function flushPhase(phase, scope) {
|
|
212
|
-
const queue = queues.get(phase);
|
|
213
|
-
const remaining = [];
|
|
214
|
-
const runnable = [];
|
|
215
|
-
|
|
216
|
-
for (const job of queue.splice(0)) {
|
|
217
|
-
if (job.canceled) {
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (scope !== undefined && job.scope !== scope) {
|
|
221
|
-
remaining.push(job);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
runnable.push(job);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
queue.push(...remaining);
|
|
228
|
-
|
|
229
|
-
for (const job of runnable) {
|
|
230
|
-
if (job.key) {
|
|
231
|
-
keyedJobs.delete(job.key);
|
|
232
|
-
}
|
|
233
|
-
if (job.canceled || (job.scope !== undefined && destroyedScopes.has(job.scope))) {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
try {
|
|
237
|
-
await job.fn();
|
|
238
|
-
} catch (error) {
|
|
239
|
-
if (onError) {
|
|
240
|
-
onError(error, job);
|
|
241
|
-
} else {
|
|
242
|
-
throw error;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function hasJobs() {
|
|
249
|
-
for (const queue of queues.values()) {
|
|
250
|
-
if (queue.some((job) => !job.canceled)) {
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function hasJobsForScope(scope) {
|
|
258
|
-
for (const queue of queues.values()) {
|
|
259
|
-
if (queue.some((job) => !job.canceled && job.scope === scope)) {
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function assertActive() {
|
|
267
|
-
if (destroyed) {
|
|
268
|
-
throw new Error("Scheduler has been destroyed.");
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function assertPhase(phase) {
|
|
273
|
-
if (!queues.has(phase)) {
|
|
274
|
-
throw new Error(`Unknown scheduler phase "${phase}".`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function scopeKey(scope) {
|
|
279
|
-
if (scope === undefined) {
|
|
280
|
-
return "global";
|
|
281
|
-
}
|
|
282
|
-
if ((typeof scope === "object" && scope !== null) || typeof scope === "function") {
|
|
283
|
-
if (!objectScopeIds.has(scope)) {
|
|
284
|
-
objectScopeIds.set(scope, `scope:${++scopeCounter}`);
|
|
285
|
-
}
|
|
286
|
-
return objectScopeIds.get(scope);
|
|
287
|
-
}
|
|
288
|
-
return String(scope);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function scheduleMicrotask(fn) {
|
|
293
|
-
if (typeof queueMicrotask === "function") {
|
|
294
|
-
queueMicrotask(fn);
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
Promise.resolve().then(fn);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function noop() {}
|
package/src/server-entry.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createApp as createBaseApp,
|
|
3
|
-
defineApp as defineBaseApp,
|
|
4
|
-
readSnapshot
|
|
5
|
-
} from "./app.js";
|
|
6
|
-
import { createServerRegistry } from "./server-registry.js";
|
|
7
|
-
|
|
8
|
-
export function createApp(appOrDefinition = Async, options = {}) {
|
|
9
|
-
return createBaseApp(appOrDefinition, {
|
|
10
|
-
serverFactory: createServerRegistry,
|
|
11
|
-
...options
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function defineApp(initial) {
|
|
16
|
-
return defineBaseApp(initial, { createRuntime: createApp });
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const Async = defineApp();
|
|
20
|
-
export { readSnapshot };
|
package/src/server-registry.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { readRequestContext } from "./request-context.js";
|
|
2
|
-
import { attachRegistryInspection, createRegistryStore } from "./registry-store.js";
|
|
3
|
-
import { assertServerId, createServerNamespace, createSignalReader } from "./server.js";
|
|
4
|
-
|
|
5
|
-
export function createServerRegistry(initialMap = {}, options = {}) {
|
|
6
|
-
const registryStore = options.registry ?? createRegistryStore();
|
|
7
|
-
const type = options.type ?? "server";
|
|
8
|
-
const entries = registryStore._map(type);
|
|
9
|
-
const defaults = {};
|
|
10
|
-
|
|
11
|
-
const registry = attachRegistryInspection({
|
|
12
|
-
register(id, fn) {
|
|
13
|
-
assertServerId(id);
|
|
14
|
-
if (typeof fn !== "function") {
|
|
15
|
-
throw new TypeError(`Server function "${id}" must be a function.`);
|
|
16
|
-
}
|
|
17
|
-
if (entries.has(id)) {
|
|
18
|
-
throw new Error(`Server function "${id}" is already registered.`);
|
|
19
|
-
}
|
|
20
|
-
entries.set(id, fn);
|
|
21
|
-
return id;
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
registerMany(map) {
|
|
25
|
-
for (const [id, fn] of Object.entries(map ?? {})) {
|
|
26
|
-
registry.register(id, fn);
|
|
27
|
-
}
|
|
28
|
-
return registry;
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
unregister(id) {
|
|
32
|
-
assertServerId(id);
|
|
33
|
-
return entries.delete(id);
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
resolve(id) {
|
|
37
|
-
assertServerId(id);
|
|
38
|
-
return entries.get(id);
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
async run(id, args = [], context = {}) {
|
|
42
|
-
assertServerId(id);
|
|
43
|
-
const fn = registry.resolve(id);
|
|
44
|
-
if (!fn) {
|
|
45
|
-
throw new Error(`Server function "${id}" is not registered.`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
let runContext;
|
|
49
|
-
const server = createServerNamespace((childId, childArgs, childContext = {}) => {
|
|
50
|
-
return registry.run(childId, childArgs, { ...runContext, ...childContext });
|
|
51
|
-
}, {}, () => runContext);
|
|
52
|
-
|
|
53
|
-
const mergedContext = mergeRequestContext({
|
|
54
|
-
...defaults,
|
|
55
|
-
...context,
|
|
56
|
-
cache: defaults.cache ?? context.cache
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
runContext = {
|
|
60
|
-
...mergedContext,
|
|
61
|
-
id,
|
|
62
|
-
args,
|
|
63
|
-
input: mergedContext.input,
|
|
64
|
-
signals: createSignalReader(mergedContext.signals),
|
|
65
|
-
abort: mergedContext.abort,
|
|
66
|
-
cache: mergedContext.cache,
|
|
67
|
-
server
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
return fn.call(runContext, ...args);
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
_setContext(context = {}) {
|
|
74
|
-
Object.assign(defaults, context);
|
|
75
|
-
return registry;
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
_adoptMany() {
|
|
79
|
-
return registry;
|
|
80
|
-
}
|
|
81
|
-
}, registryStore, type);
|
|
82
|
-
|
|
83
|
-
registry.registerMany(initialMap);
|
|
84
|
-
return createServerNamespace((id, args, context) => registry.run(id, args, context), registry, () => defaults);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function mergeRequestContext(context) {
|
|
88
|
-
const requestContext = readRequestContext(context.requestContext);
|
|
89
|
-
return {
|
|
90
|
-
...context,
|
|
91
|
-
requestContext,
|
|
92
|
-
request: requestContext.request ?? context.request,
|
|
93
|
-
headers: requestContext.headers ?? context.headers,
|
|
94
|
-
cookies: requestContext.cookies ?? context.cookies,
|
|
95
|
-
locals: requestContext.locals ?? context.locals
|
|
96
|
-
};
|
|
97
|
-
}
|