@async/framework 0.6.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 +31 -0
- package/README.md +93 -15
- package/{framework.d.ts → browser.d.ts} +71 -4
- package/{framework.js → browser.js} +791 -147
- package/browser.min.js +1 -0
- package/browser.ts +4781 -0
- package/{framework.umd.js → browser.umd.js} +791 -147
- package/browser.umd.min.js +1 -0
- package/package.json +45 -30
- package/server.d.ts +640 -0
- package/src/app.js +143 -12
- package/src/async-signal.js +32 -4
- package/src/browser.js +15 -0
- package/src/cache.js +27 -3
- 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 +113 -16
- package/src/scheduler.js +296 -0
- package/src/server-entry.js +20 -0
- package/src/server-registry.js +97 -0
- package/src/server.js +49 -89
- package/src/signals.js +38 -6
- package/framework.min.js +0 -3648
- package/framework.ts +0 -3
- package/framework.umd.min.js +0 -3671
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" });
|
|
@@ -73,7 +80,7 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
73
80
|
let started = false;
|
|
74
81
|
let destroyed = false;
|
|
75
82
|
|
|
76
|
-
applySnapshot(signals, browserCache, options.snapshot);
|
|
83
|
+
applySnapshot(signals, browserCache, options.snapshot ?? (target === "browser" ? readSnapshot(options.root, { attributes }) : undefined));
|
|
77
84
|
attachServerCache(server, serverCache);
|
|
78
85
|
|
|
79
86
|
const runtime = {
|
|
@@ -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.");
|
|
@@ -241,6 +269,38 @@ export function createApp(appOrDefinition = Async, options = {}) {
|
|
|
241
269
|
|
|
242
270
|
export const Async = defineApp();
|
|
243
271
|
|
|
272
|
+
export function readSnapshot(root = globalThis.document, { attributes } = {}) {
|
|
273
|
+
const attributeConfig = normalizeAttributeConfig(attributes);
|
|
274
|
+
const snapshotAttr = attributeName(attributeConfig, "async", "snapshot");
|
|
275
|
+
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
276
|
+
const rootNode = root ?? documentRef;
|
|
277
|
+
if (!rootNode?.querySelectorAll && !documentRef?.querySelectorAll) {
|
|
278
|
+
return {};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
for (const searchRoot of new Set([rootNode, documentRef])) {
|
|
282
|
+
if (!searchRoot?.querySelectorAll) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
for (const script of searchRoot.querySelectorAll("script[type='application/json'], script")) {
|
|
286
|
+
if (!script.hasAttribute?.(snapshotAttr)) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const source = script.textContent?.trim() ?? "";
|
|
290
|
+
if (!source) {
|
|
291
|
+
return {};
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
return JSON.parse(source);
|
|
295
|
+
} catch (cause) {
|
|
296
|
+
throw new Error(`Could not parse Async snapshot: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {};
|
|
302
|
+
}
|
|
303
|
+
|
|
244
304
|
function applyUseToRuntime(runtime, normalized) {
|
|
245
305
|
applyRegistryUse(runtime.signals, runtime.registry, normalized.signal);
|
|
246
306
|
applyRegistryUse(runtime.handlers, runtime.registry, normalized.handler);
|
|
@@ -353,6 +413,77 @@ function attachServerCache(server, cache) {
|
|
|
353
413
|
}
|
|
354
414
|
}
|
|
355
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
|
+
|
|
356
487
|
function normalizeEntries(type, entries = {}) {
|
|
357
488
|
if (type !== "signal") {
|
|
358
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/cache.js
CHANGED
|
@@ -15,6 +15,7 @@ export function createCacheRegistry(initialMap = {}, { now = () => Date.now(), r
|
|
|
15
15
|
const registryStore = registry ?? createRegistryStore();
|
|
16
16
|
const definitions = registryStore._map(type);
|
|
17
17
|
const entries = registryStore._map(`${type}.entries`);
|
|
18
|
+
const pending = new Map();
|
|
18
19
|
|
|
19
20
|
const registryApi = attachRegistryInspection({
|
|
20
21
|
register(id, definition = defineCache()) {
|
|
@@ -76,19 +77,37 @@ export function createCacheRegistry(initialMap = {}, { now = () => Date.now(), r
|
|
|
76
77
|
if (cached !== undefined) {
|
|
77
78
|
return cached;
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
if (pending.has(key)) {
|
|
81
|
+
return pending.get(key);
|
|
82
|
+
}
|
|
83
|
+
let promise;
|
|
84
|
+
promise = Promise.resolve()
|
|
85
|
+
.then(fn)
|
|
86
|
+
.then((value) => {
|
|
87
|
+
if (pending.get(key) === promise) {
|
|
88
|
+
registryApi.set(key, value, options);
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
})
|
|
92
|
+
.finally(() => {
|
|
93
|
+
if (pending.get(key) === promise) {
|
|
94
|
+
pending.delete(key);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
pending.set(key, promise);
|
|
98
|
+
return promise;
|
|
82
99
|
},
|
|
83
100
|
|
|
84
101
|
delete(key) {
|
|
85
102
|
assertKey(key);
|
|
103
|
+
pending.delete(key);
|
|
86
104
|
return entries.delete(key);
|
|
87
105
|
},
|
|
88
106
|
|
|
89
107
|
clear(prefix) {
|
|
90
108
|
if (prefix === undefined) {
|
|
91
109
|
entries.clear();
|
|
110
|
+
pending.clear();
|
|
92
111
|
return registryApi;
|
|
93
112
|
}
|
|
94
113
|
for (const key of [...entries.keys()]) {
|
|
@@ -96,6 +115,11 @@ export function createCacheRegistry(initialMap = {}, { now = () => Date.now(), r
|
|
|
96
115
|
entries.delete(key);
|
|
97
116
|
}
|
|
98
117
|
}
|
|
118
|
+
for (const key of [...pending.keys()]) {
|
|
119
|
+
if (key.startsWith(prefix)) {
|
|
120
|
+
pending.delete(key);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
99
123
|
return registryApi;
|
|
100
124
|
},
|
|
101
125
|
|
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 } 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";
|