@async/framework 0.7.0 → 0.8.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 +16 -0
- package/README.md +81 -14
- package/{framework.d.ts → browser.d.ts} +68 -4
- package/{framework.js → browser.js} +587 -127
- package/browser.min.js +1 -0
- package/{framework.ts → browser.ts} +588 -128
- package/{framework.umd.js → browser.umd.js} +587 -127
- package/browser.umd.min.js +1 -0
- package/package.json +45 -30
- package/server.d.ts +640 -0
- package/src/app.js +110 -11
- package/src/async-signal.js +32 -4
- package/src/browser.js +15 -0
- package/src/component.js +42 -7
- package/src/index.js +5 -2
- package/src/loader.js +42 -10
- package/src/request-context.js +40 -0
- package/src/router.js +15 -2
- package/src/scheduler.js +296 -0
- package/src/server-entry.js +20 -0
- package/src/server-registry.js +97 -0
- package/src/server.js +5 -88
- package/src/signals.js +38 -6
- package/framework.min.js +0 -3820
- package/framework.umd.min.js +0 -3843
package/src/app.js
CHANGED
|
@@ -4,16 +4,18 @@ import { createHandlerRegistry } from "./handlers.js";
|
|
|
4
4
|
import { Loader } from "./loader.js";
|
|
5
5
|
import { createPartialRegistry } from "./partials.js";
|
|
6
6
|
import { createRouteRegistry, createRouter } from "./router.js";
|
|
7
|
-
import {
|
|
7
|
+
import { createScheduler } from "./scheduler.js";
|
|
8
|
+
import { createServerNamespace } from "./server.js";
|
|
8
9
|
import { createSignal, createSignalRegistry } from "./signals.js";
|
|
9
10
|
import { createRegistryStore } from "./registry-store.js";
|
|
10
11
|
import { attributeName, normalizeAttributeConfig } from "./attributes.js";
|
|
11
12
|
|
|
12
13
|
const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component"]);
|
|
13
14
|
|
|
14
|
-
export function defineApp(initial) {
|
|
15
|
+
export function defineApp(initial, options = {}) {
|
|
15
16
|
const registry = createRegistryStore(undefined, { target: "browser" });
|
|
16
17
|
const runtimes = new Set();
|
|
18
|
+
const createRuntime = options.createRuntime ?? createApp;
|
|
17
19
|
|
|
18
20
|
const app = {
|
|
19
21
|
registry,
|
|
@@ -32,7 +34,7 @@ export function defineApp(initial) {
|
|
|
32
34
|
},
|
|
33
35
|
|
|
34
36
|
start(options = {}) {
|
|
35
|
-
const runtime =
|
|
37
|
+
const runtime = createRuntime(app, options).start();
|
|
36
38
|
app.runtime = runtime;
|
|
37
39
|
return runtime;
|
|
38
40
|
},
|
|
@@ -57,13 +59,18 @@ export function defineApp(initial) {
|
|
|
57
59
|
export function createApp(appOrDefinition = Async, options = {}) {
|
|
58
60
|
const app = isAppHub(appOrDefinition) ? appOrDefinition : defineApp(appOrDefinition ?? {});
|
|
59
61
|
const target = options.target ?? "browser";
|
|
62
|
+
const scheduler = options.scheduler ?? options.loader?.scheduler ?? createScheduler({
|
|
63
|
+
strategy: target === "server" ? "manual" : "microtask"
|
|
64
|
+
});
|
|
65
|
+
const ownsScheduler = !options.scheduler && !options.loader?.scheduler;
|
|
60
66
|
const attributes = normalizeAttributeConfig(options.attributes);
|
|
61
67
|
const registry = options.registry ?? app.registry.view({ target });
|
|
62
68
|
const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
|
|
63
69
|
const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler" });
|
|
64
70
|
const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
|
|
65
71
|
const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
|
|
66
|
-
const
|
|
72
|
+
const serverFactory = options.serverFactory ?? createServerReferenceRegistry;
|
|
73
|
+
const server = options.server ?? serverFactory(undefined, { registry, type: "server" });
|
|
67
74
|
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
|
|
68
75
|
const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
|
|
69
76
|
const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component" });
|
|
@@ -91,6 +98,7 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
91
98
|
},
|
|
92
99
|
loader,
|
|
93
100
|
router,
|
|
101
|
+
scheduler,
|
|
94
102
|
attributes,
|
|
95
103
|
|
|
96
104
|
start() {
|
|
@@ -107,12 +115,13 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
107
115
|
handlers,
|
|
108
116
|
server,
|
|
109
117
|
cache: browserCache,
|
|
118
|
+
scheduler,
|
|
110
119
|
attributes
|
|
111
120
|
});
|
|
112
121
|
runtime.loader = loader;
|
|
113
122
|
|
|
114
123
|
configureServerContext({ cache: browserCache });
|
|
115
|
-
signals._setContext?.({ server, loader, cache: browserCache });
|
|
124
|
+
signals._setContext?.({ server, loader, cache: browserCache, scheduler });
|
|
116
125
|
|
|
117
126
|
loader.start();
|
|
118
127
|
|
|
@@ -128,6 +137,7 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
128
137
|
server,
|
|
129
138
|
cache: browserCache,
|
|
130
139
|
partials,
|
|
140
|
+
scheduler,
|
|
131
141
|
fetch: options.fetch,
|
|
132
142
|
routeEndpoint: options.routeEndpoint,
|
|
133
143
|
attributes
|
|
@@ -139,7 +149,7 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
139
149
|
}
|
|
140
150
|
} else {
|
|
141
151
|
configureServerContext({ cache: serverCache });
|
|
142
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
152
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
return runtime;
|
|
@@ -153,9 +163,10 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
153
163
|
async render(url) {
|
|
154
164
|
assertActive();
|
|
155
165
|
configureServerContext({ cache: serverCache });
|
|
156
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
166
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
157
167
|
const matched = routes.match(url);
|
|
158
168
|
if (!matched) {
|
|
169
|
+
await scheduler.flush();
|
|
159
170
|
return {
|
|
160
171
|
html: renderDocument("", { status: 404, signals, browserCache, boundary: options.boundary ?? "route", attributes }),
|
|
161
172
|
status: 404,
|
|
@@ -175,8 +186,8 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
175
186
|
cache: serverCache,
|
|
176
187
|
browserCache,
|
|
177
188
|
partials,
|
|
178
|
-
|
|
179
|
-
|
|
189
|
+
scheduler,
|
|
190
|
+
...currentRequestContext()
|
|
180
191
|
})
|
|
181
192
|
: { html: "" };
|
|
182
193
|
|
|
@@ -189,6 +200,8 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
189
200
|
browserCache.restore(result.cache.browser);
|
|
190
201
|
}
|
|
191
202
|
|
|
203
|
+
await scheduler.flush();
|
|
204
|
+
|
|
192
205
|
const status = result.status ?? 200;
|
|
193
206
|
return {
|
|
194
207
|
html: renderDocument(result.html, { status, signals, browserCache, boundary: result.boundary ?? options.boundary ?? "route", attributes }),
|
|
@@ -207,6 +220,9 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
207
220
|
router?.destroy?.();
|
|
208
221
|
loader?.destroy?.();
|
|
209
222
|
signals.destroy?.();
|
|
223
|
+
if (ownsScheduler) {
|
|
224
|
+
scheduler.destroy();
|
|
225
|
+
}
|
|
210
226
|
},
|
|
211
227
|
|
|
212
228
|
_applyUse(normalized) {
|
|
@@ -227,11 +243,23 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
227
243
|
loader,
|
|
228
244
|
router,
|
|
229
245
|
cache,
|
|
230
|
-
|
|
231
|
-
|
|
246
|
+
scheduler,
|
|
247
|
+
requestContext: options.requestContext,
|
|
248
|
+
...currentRequestContext()
|
|
232
249
|
});
|
|
233
250
|
}
|
|
234
251
|
|
|
252
|
+
function currentRequestContext() {
|
|
253
|
+
const context = readRequestContextLike(options.requestContext);
|
|
254
|
+
return {
|
|
255
|
+
requestContext: context,
|
|
256
|
+
request: context.request ?? options.request,
|
|
257
|
+
headers: context.headers ?? options.headers,
|
|
258
|
+
cookies: context.cookies ?? options.cookies,
|
|
259
|
+
locals: context.locals ?? options.locals
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
235
263
|
function assertActive() {
|
|
236
264
|
if (destroyed) {
|
|
237
265
|
throw new Error("Async app runtime has been destroyed.");
|
|
@@ -385,6 +413,77 @@ function attachServerCache(server, cache) {
|
|
|
385
413
|
}
|
|
386
414
|
}
|
|
387
415
|
|
|
416
|
+
function createServerReferenceRegistry(initialMap = {}, options = {}) {
|
|
417
|
+
const registry = options.registry ?? createRegistryStore();
|
|
418
|
+
const type = options.type ?? "server";
|
|
419
|
+
const defaults = {};
|
|
420
|
+
|
|
421
|
+
const reference = {
|
|
422
|
+
registry,
|
|
423
|
+
|
|
424
|
+
register(id, value) {
|
|
425
|
+
registry.register(type, id, value);
|
|
426
|
+
return id;
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
registerMany(map) {
|
|
430
|
+
for (const [id, value] of Object.entries(map ?? {})) {
|
|
431
|
+
reference.register(id, value);
|
|
432
|
+
}
|
|
433
|
+
return reference;
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
unregister(id) {
|
|
437
|
+
return registry.unregister(type, id);
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
resolve() {
|
|
441
|
+
return undefined;
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
async run(id) {
|
|
445
|
+
throw new Error(`Server command "${id}" cannot run without a server proxy or server registry.`);
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
keys() {
|
|
449
|
+
return registry.keys(type);
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
entries() {
|
|
453
|
+
return registry.entries(type);
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
inspect() {
|
|
457
|
+
return registry.entries(type);
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
_setContext(context = {}) {
|
|
461
|
+
Object.assign(defaults, context);
|
|
462
|
+
return reference;
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
_adoptMany() {
|
|
466
|
+
return reference;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
reference.registerMany(initialMap);
|
|
471
|
+
return createServerNamespace((id, args, context) => reference.run(id, args, context), reference, () => defaults);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function readRequestContextLike(store) {
|
|
475
|
+
if (!store) {
|
|
476
|
+
return {};
|
|
477
|
+
}
|
|
478
|
+
if (typeof store.get === "function") {
|
|
479
|
+
return store.get() ?? {};
|
|
480
|
+
}
|
|
481
|
+
if (typeof store.getStore === "function") {
|
|
482
|
+
return store.getStore() ?? {};
|
|
483
|
+
}
|
|
484
|
+
return {};
|
|
485
|
+
}
|
|
486
|
+
|
|
388
487
|
function normalizeEntries(type, entries = {}) {
|
|
389
488
|
if (type !== "signal") {
|
|
390
489
|
return { ...(entries ?? {}) };
|
package/src/async-signal.js
CHANGED
|
@@ -90,7 +90,8 @@ export function asyncSignal(id, fn) {
|
|
|
90
90
|
router: context.router,
|
|
91
91
|
loader: context.loader,
|
|
92
92
|
cache: context.cache,
|
|
93
|
-
abort: activeAbort
|
|
93
|
+
abort: activeAbort,
|
|
94
|
+
scheduler: context.scheduler
|
|
94
95
|
});
|
|
95
96
|
}
|
|
96
97
|
return server;
|
|
@@ -104,6 +105,9 @@ export function asyncSignal(id, fn) {
|
|
|
104
105
|
get cache() {
|
|
105
106
|
return registry._context?.().cache;
|
|
106
107
|
},
|
|
108
|
+
get scheduler() {
|
|
109
|
+
return registry._context?.().scheduler;
|
|
110
|
+
},
|
|
107
111
|
get version() {
|
|
108
112
|
return runVersion;
|
|
109
113
|
},
|
|
@@ -180,11 +184,20 @@ export function asyncSignal(id, fn) {
|
|
|
180
184
|
_bindRegistry(nextRegistry, nextId) {
|
|
181
185
|
registry = nextRegistry;
|
|
182
186
|
registeredId = nextId;
|
|
183
|
-
|
|
187
|
+
const start = () => {
|
|
184
188
|
if (registry === nextRegistry && status === "idle") {
|
|
185
189
|
state.refresh();
|
|
186
190
|
}
|
|
187
|
-
}
|
|
191
|
+
};
|
|
192
|
+
const scheduler = registry._context?.().scheduler;
|
|
193
|
+
if (scheduler) {
|
|
194
|
+
scheduler.enqueue("async", start, {
|
|
195
|
+
scope: registeredId,
|
|
196
|
+
key: `asyncSignal:${registeredId}:initial`
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
queueMicrotask(start);
|
|
200
|
+
}
|
|
188
201
|
},
|
|
189
202
|
|
|
190
203
|
_dispose() {
|
|
@@ -220,11 +233,26 @@ export function asyncSignal(id, fn) {
|
|
|
220
233
|
for (const dependency of dependencies) {
|
|
221
234
|
const dependencyId = String(dependency).split(".")[0];
|
|
222
235
|
if (dependencyId && dependencyId !== registeredId) {
|
|
223
|
-
dependencyCleanups.add(registry.subscribe(dependency, () =>
|
|
236
|
+
dependencyCleanups.add(registry.subscribe(dependency, () => scheduleRefresh()));
|
|
224
237
|
}
|
|
225
238
|
}
|
|
226
239
|
}
|
|
227
240
|
|
|
241
|
+
function scheduleRefresh() {
|
|
242
|
+
if (activeAbort && !activeAbort.aborted) {
|
|
243
|
+
activeAbort.cancel(new Error(`Async signal "${registeredId}" dependency changed.`));
|
|
244
|
+
}
|
|
245
|
+
const scheduler = registry?._context?.().scheduler;
|
|
246
|
+
if (!scheduler) {
|
|
247
|
+
state.refresh();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
scheduler.enqueue("async", () => state.refresh(), {
|
|
251
|
+
scope: registeredId,
|
|
252
|
+
key: `asyncSignal:${registeredId}:refresh`
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
228
256
|
function notify() {
|
|
229
257
|
for (const subscriber of [...subscribers]) {
|
|
230
258
|
subscriber(state);
|
package/src/browser.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { asyncSignal } from "./async-signal.js";
|
|
2
|
+
export { Async, createApp, defineApp, readSnapshot } from "./app.js";
|
|
3
|
+
export { attributeName, defineAttributeConfig } from "./attributes.js";
|
|
4
|
+
export { createCacheRegistry, defineCache } from "./cache.js";
|
|
5
|
+
export { component, createComponentRegistry, defineComponent } from "./component.js";
|
|
6
|
+
export { delay } from "./delay.js";
|
|
7
|
+
export { createHandlerRegistry } from "./handlers.js";
|
|
8
|
+
export { html } from "./html.js";
|
|
9
|
+
export { Loader, AsyncLoader } from "./loader.js";
|
|
10
|
+
export { createPartialRegistry } from "./partials.js";
|
|
11
|
+
export { createRegistryStore } from "./registry-store.js";
|
|
12
|
+
export { createRouteRegistry, createRouter, defineRoute, route } from "./router.js";
|
|
13
|
+
export { createScheduler } from "./scheduler.js";
|
|
14
|
+
export { applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult } from "./server.js";
|
|
15
|
+
export { computed, createSignal, createSignalRegistry, effect, signal } from "./signals.js";
|
package/src/component.js
CHANGED
|
@@ -113,10 +113,15 @@ export function renderComponent(Component, props = {}, runtime, parentScope = "c
|
|
|
113
113
|
html,
|
|
114
114
|
attach(target) {
|
|
115
115
|
for (const hook of attachHooks) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
runtime.scheduler?.enqueue("lifecycle", () => {
|
|
117
|
+
const cleanup = hook(target);
|
|
118
|
+
if (typeof cleanup === "function") {
|
|
119
|
+
cleanups.push(cleanup);
|
|
120
|
+
}
|
|
121
|
+
}, {
|
|
122
|
+
scope,
|
|
123
|
+
key: `attach:${attachHooks.indexOf(hook)}`
|
|
124
|
+
}) ?? runAttachHook(hook, target);
|
|
120
125
|
}
|
|
121
126
|
},
|
|
122
127
|
mount(target) {
|
|
@@ -124,7 +129,17 @@ export function renderComponent(Component, props = {}, runtime, parentScope = "c
|
|
|
124
129
|
},
|
|
125
130
|
visible(target, observeVisible) {
|
|
126
131
|
for (const hook of visibleHooks) {
|
|
127
|
-
const cleanup = observeVisible(target,
|
|
132
|
+
const cleanup = observeVisible(target, () => {
|
|
133
|
+
runtime.scheduler?.enqueue("lifecycle", () => {
|
|
134
|
+
const hookCleanup = hook(target);
|
|
135
|
+
if (typeof hookCleanup === "function") {
|
|
136
|
+
cleanups.push(hookCleanup);
|
|
137
|
+
}
|
|
138
|
+
}, {
|
|
139
|
+
scope,
|
|
140
|
+
key: `visible:${visibleHooks.indexOf(hook)}`
|
|
141
|
+
}) ?? runVisibleHook(hook, target);
|
|
142
|
+
});
|
|
128
143
|
if (typeof cleanup === "function") {
|
|
129
144
|
cleanups.push(cleanup);
|
|
130
145
|
}
|
|
@@ -134,6 +149,7 @@ export function renderComponent(Component, props = {}, runtime, parentScope = "c
|
|
|
134
149
|
while (destroyHooks.length > 0) {
|
|
135
150
|
destroyHooks.pop()?.();
|
|
136
151
|
}
|
|
152
|
+
runtime.scheduler?.markScopeDestroyed(scope);
|
|
137
153
|
while (cleanups.length > 0) {
|
|
138
154
|
cleanups.pop()?.();
|
|
139
155
|
}
|
|
@@ -142,10 +158,24 @@ export function renderComponent(Component, props = {}, runtime, parentScope = "c
|
|
|
142
158
|
}
|
|
143
159
|
}
|
|
144
160
|
};
|
|
161
|
+
|
|
162
|
+
function runAttachHook(hook, target) {
|
|
163
|
+
const cleanup = hook(target);
|
|
164
|
+
if (typeof cleanup === "function") {
|
|
165
|
+
cleanups.push(cleanup);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function runVisibleHook(hook, target) {
|
|
170
|
+
const cleanup = hook(target);
|
|
171
|
+
if (typeof cleanup === "function") {
|
|
172
|
+
cleanups.push(cleanup);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
145
175
|
}
|
|
146
176
|
|
|
147
177
|
function createComponentContext({ runtime, scope, cleanups, attachHooks, visibleHooks, destroyHooks, renderScopedTemplate }) {
|
|
148
|
-
const { signals, handlers, loader, server, router, cache } = runtime;
|
|
178
|
+
const { signals, handlers, loader, server, router, cache, scheduler } = runtime;
|
|
149
179
|
const generatedHandlers = new WeakMap();
|
|
150
180
|
let generatedHandlerCounter = 0;
|
|
151
181
|
let generatedSignalCounter = 0;
|
|
@@ -157,6 +187,7 @@ function createComponentContext({ runtime, scope, cleanups, attachHooks, visible
|
|
|
157
187
|
server,
|
|
158
188
|
router,
|
|
159
189
|
cache,
|
|
190
|
+
scheduler,
|
|
160
191
|
|
|
161
192
|
signal(name, initial) {
|
|
162
193
|
if (arguments.length === 1) {
|
|
@@ -201,7 +232,11 @@ function createComponentContext({ runtime, scope, cleanups, attachHooks, visible
|
|
|
201
232
|
},
|
|
202
233
|
|
|
203
234
|
effect(fn) {
|
|
204
|
-
const cleanup = signals.effect(() => fn.call(context)
|
|
235
|
+
const cleanup = signals.effect(() => fn.call(context), {
|
|
236
|
+
scheduler,
|
|
237
|
+
phase: "effect",
|
|
238
|
+
scope
|
|
239
|
+
});
|
|
205
240
|
cleanups.push(cleanup);
|
|
206
241
|
return cleanup;
|
|
207
242
|
},
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { asyncSignal } from "./async-signal.js";
|
|
2
|
-
export { Async, createApp, defineApp, readSnapshot } from "./
|
|
2
|
+
export { Async, createApp, defineApp, readSnapshot } from "./server-entry.js";
|
|
3
3
|
export { attributeName, defineAttributeConfig } from "./attributes.js";
|
|
4
4
|
export { createCacheRegistry, defineCache } from "./cache.js";
|
|
5
5
|
export { component, createComponentRegistry, defineComponent } from "./component.js";
|
|
@@ -10,5 +10,8 @@ export { Loader, AsyncLoader } from "./loader.js";
|
|
|
10
10
|
export { createPartialRegistry } from "./partials.js";
|
|
11
11
|
export { createRegistryStore } from "./registry-store.js";
|
|
12
12
|
export { createRouteRegistry, createRouter, defineRoute, route } from "./router.js";
|
|
13
|
-
export {
|
|
13
|
+
export { createScheduler } from "./scheduler.js";
|
|
14
|
+
export { createRequestContextStore } from "./request-context.js";
|
|
15
|
+
export { applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult } from "./server.js";
|
|
16
|
+
export { createServerRegistry } from "./server-registry.js";
|
|
14
17
|
export { computed, createSignal, createSignalRegistry, effect, signal } from "./signals.js";
|
package/src/loader.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { renderComponent } from "./component.js";
|
|
2
2
|
import { createHandlerRegistry } from "./handlers.js";
|
|
3
|
+
import { createScheduler } from "./scheduler.js";
|
|
3
4
|
import { createSignalRegistry, isSignalRef } from "./signals.js";
|
|
4
5
|
import { matchAttribute, normalizeAttributeConfig, readAttribute } from "./attributes.js";
|
|
5
6
|
|
|
6
7
|
const inlineBindingPrefix = "__async:inline:";
|
|
7
8
|
|
|
8
|
-
export function Loader({ root, signals, handlers, server, router, cache, attributes } = {}) {
|
|
9
|
+
export function Loader({ root, signals, handlers, server, router, cache, attributes, scheduler } = {}) {
|
|
9
10
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
10
11
|
const rootNode = root ?? documentRef;
|
|
11
12
|
const signalRegistry = signals ?? createSignalRegistry();
|
|
12
13
|
const handlerRegistry = handlers ?? createHandlerRegistry();
|
|
14
|
+
const schedulerInstance = scheduler ?? createScheduler();
|
|
15
|
+
const ownsScheduler = !scheduler;
|
|
13
16
|
const attributeConfig = normalizeAttributeConfig(attributes);
|
|
14
17
|
const cleanups = new Set();
|
|
15
18
|
const eventBindings = new WeakMap();
|
|
@@ -30,6 +33,7 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
30
33
|
server,
|
|
31
34
|
router,
|
|
32
35
|
cache,
|
|
36
|
+
scheduler: schedulerInstance,
|
|
33
37
|
attributes: attributeConfig,
|
|
34
38
|
|
|
35
39
|
start() {
|
|
@@ -69,6 +73,7 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
69
73
|
server: api.server,
|
|
70
74
|
router: api.router,
|
|
71
75
|
cache: api.cache,
|
|
76
|
+
scheduler: schedulerInstance,
|
|
72
77
|
attributes: attributeConfig
|
|
73
78
|
});
|
|
74
79
|
cleanupChildren(target);
|
|
@@ -89,6 +94,9 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
89
94
|
runCleanup(cleanup);
|
|
90
95
|
}
|
|
91
96
|
cleanups.clear();
|
|
97
|
+
if (ownsScheduler) {
|
|
98
|
+
schedulerInstance.destroy();
|
|
99
|
+
}
|
|
92
100
|
},
|
|
93
101
|
|
|
94
102
|
_observeVisible(target, fn) {
|
|
@@ -106,13 +114,14 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
106
114
|
}
|
|
107
115
|
};
|
|
108
116
|
|
|
109
|
-
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache });
|
|
117
|
+
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache, scheduler: schedulerInstance });
|
|
110
118
|
api.server?._setContext?.({
|
|
111
119
|
signals: signalRegistry,
|
|
112
120
|
handlers: handlerRegistry,
|
|
113
121
|
loader: api,
|
|
114
122
|
router: api.router,
|
|
115
|
-
cache: api.cache
|
|
123
|
+
cache: api.cache,
|
|
124
|
+
scheduler: schedulerInstance
|
|
116
125
|
});
|
|
117
126
|
|
|
118
127
|
function bindEventAttributes(scope) {
|
|
@@ -144,18 +153,19 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
144
153
|
|
|
145
154
|
const listener = async (event) => {
|
|
146
155
|
try {
|
|
147
|
-
await handlerRegistry.run(ref, {
|
|
156
|
+
await schedulerInstance.batch(() => handlerRegistry.run(ref, {
|
|
148
157
|
signals: signalRegistry,
|
|
149
158
|
handlers: handlerRegistry,
|
|
150
159
|
loader: api,
|
|
151
160
|
server: api.server,
|
|
152
161
|
router: api.router,
|
|
153
162
|
cache: api.cache,
|
|
163
|
+
scheduler: schedulerInstance,
|
|
154
164
|
event,
|
|
155
165
|
element,
|
|
156
166
|
el: element,
|
|
157
167
|
root: rootNode
|
|
158
|
-
});
|
|
168
|
+
}));
|
|
159
169
|
} catch (error) {
|
|
160
170
|
dispatchAsyncError(element, error);
|
|
161
171
|
}
|
|
@@ -271,7 +281,12 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
271
281
|
|
|
272
282
|
const read = () => readBinding(path, options);
|
|
273
283
|
apply(read());
|
|
274
|
-
addCleanup(subscribeBinding(path, () =>
|
|
284
|
+
addCleanup(subscribeBinding(path, () => {
|
|
285
|
+
schedulerInstance.enqueue("binding", () => apply(read()), {
|
|
286
|
+
scope: element,
|
|
287
|
+
key
|
|
288
|
+
});
|
|
289
|
+
}), element);
|
|
275
290
|
}
|
|
276
291
|
|
|
277
292
|
function bindValueWriter(element, path) {
|
|
@@ -332,7 +347,12 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
332
347
|
const state = {
|
|
333
348
|
id,
|
|
334
349
|
templates,
|
|
335
|
-
cleanup: signalRegistry.subscribe(`${id}.$status`, () =>
|
|
350
|
+
cleanup: signalRegistry.subscribe(`${id}.$status`, () => {
|
|
351
|
+
schedulerInstance.enqueue("binding", () => renderBoundary(boundary), {
|
|
352
|
+
scope: boundary,
|
|
353
|
+
key: `boundary:${id}`
|
|
354
|
+
});
|
|
355
|
+
})
|
|
336
356
|
};
|
|
337
357
|
boundaryState.set(boundary, state);
|
|
338
358
|
addCleanup(state.cleanup, boundary);
|
|
@@ -372,7 +392,7 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
372
392
|
}
|
|
373
393
|
mountedElements.add(element);
|
|
374
394
|
for (const ref of refs) {
|
|
375
|
-
runPseudo(element, ref);
|
|
395
|
+
scheduleLifecycle(element, () => runPseudo(element, ref), `attach:${ref}`);
|
|
376
396
|
}
|
|
377
397
|
}
|
|
378
398
|
|
|
@@ -385,7 +405,7 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
385
405
|
continue;
|
|
386
406
|
}
|
|
387
407
|
visibleElements.add(element);
|
|
388
|
-
addCleanup(observeVisible(element, () => runPseudo(element, ref)), element);
|
|
408
|
+
addCleanup(observeVisible(element, () => scheduleLifecycle(element, () => runPseudo(element, ref), `visible:${ref}`)), element);
|
|
389
409
|
}
|
|
390
410
|
}
|
|
391
411
|
|
|
@@ -409,6 +429,7 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
409
429
|
server: api.server,
|
|
410
430
|
router: api.router,
|
|
411
431
|
cache: api.cache,
|
|
432
|
+
scheduler: schedulerInstance,
|
|
412
433
|
element,
|
|
413
434
|
el: element,
|
|
414
435
|
root: rootNode
|
|
@@ -427,10 +448,13 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
427
448
|
const ownerWindow = target.ownerDocument?.defaultView ?? globalThis;
|
|
428
449
|
const Observer = ownerWindow.IntersectionObserver ?? globalThis.IntersectionObserver;
|
|
429
450
|
if (!Observer) {
|
|
430
|
-
|
|
451
|
+
schedulerInstance.enqueue("lifecycle", () => {
|
|
431
452
|
if (!destroyed) {
|
|
432
453
|
fn(target);
|
|
433
454
|
}
|
|
455
|
+
}, {
|
|
456
|
+
scope: target,
|
|
457
|
+
key: "visible:fallback"
|
|
434
458
|
});
|
|
435
459
|
return () => {};
|
|
436
460
|
}
|
|
@@ -485,6 +509,7 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
485
509
|
}
|
|
486
510
|
for (const element of elementsIn(node)) {
|
|
487
511
|
runScopedCleanups(element);
|
|
512
|
+
schedulerInstance.markScopeDestroyed(element);
|
|
488
513
|
}
|
|
489
514
|
}
|
|
490
515
|
|
|
@@ -508,6 +533,13 @@ export function Loader({ root, signals, handlers, server, router, cache, attribu
|
|
|
508
533
|
}
|
|
509
534
|
}
|
|
510
535
|
|
|
536
|
+
function scheduleLifecycle(element, fn, key) {
|
|
537
|
+
schedulerInstance.enqueue("lifecycle", fn, {
|
|
538
|
+
scope: element,
|
|
539
|
+
key
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
511
543
|
return api;
|
|
512
544
|
}
|
|
513
545
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
|
|
3
|
+
export function createRequestContextStore() {
|
|
4
|
+
const storage = new AsyncLocalStorage();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
storage,
|
|
8
|
+
|
|
9
|
+
run(context, fn, ...args) {
|
|
10
|
+
if (typeof fn !== "function") {
|
|
11
|
+
throw new TypeError("requestContext.run(context, fn) requires a function.");
|
|
12
|
+
}
|
|
13
|
+
return storage.run(context ?? {}, fn, ...args);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
get() {
|
|
17
|
+
return storage.getStore();
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
snapshot() {
|
|
21
|
+
return { ...(storage.getStore() ?? {}) };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function readRequestContext(store) {
|
|
27
|
+
if (!store) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
if (typeof store.get === "function") {
|
|
31
|
+
return store.get() ?? {};
|
|
32
|
+
}
|
|
33
|
+
if (typeof store.getStore === "function") {
|
|
34
|
+
return store.getStore() ?? {};
|
|
35
|
+
}
|
|
36
|
+
if (typeof store === "object") {
|
|
37
|
+
return store;
|
|
38
|
+
}
|
|
39
|
+
return {};
|
|
40
|
+
}
|