@async/framework 0.9.0 → 0.10.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 +12 -0
- package/README.md +115 -0
- package/browser.d.ts +69 -18
- package/browser.js +733 -71
- package/browser.min.js +1 -1
- package/browser.ts +733 -71
- package/browser.umd.js +733 -71
- package/browser.umd.min.js +1 -1
- package/package.json +1 -1
- package/server.d.ts +69 -18
- package/src/app.js +314 -46
- package/src/browser.js +2 -0
- package/src/component.js +19 -2
- package/src/elements.js +63 -0
- package/src/handlers.js +19 -2
- package/src/index.js +2 -0
- package/src/lazy-registry.js +204 -0
- package/src/loader.js +23 -5
- package/src/partials.js +19 -2
- package/src/registry-store.js +15 -9
- package/src/signals.js +46 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.10.0 - 2026-06-17
|
|
6
|
+
|
|
7
|
+
- Added rootless browser startup plus `attachRoot`, `detachRoot`,
|
|
8
|
+
`inspectRoots`, and streamed `applySnapshot(...)` support for advanced
|
|
9
|
+
build-step bootstrapping.
|
|
10
|
+
- Added compact lazy registry descriptors with `_async` asset resolution,
|
|
11
|
+
inferred exports, and lazy handler, partial, component, and async-signal
|
|
12
|
+
materialization.
|
|
13
|
+
- Added optional `async-container` and `async-suspense` custom elements while
|
|
14
|
+
preserving the existing `async:container`, `async:boundary`, and
|
|
15
|
+
`this.suspense(...)` Layer 1 APIs.
|
|
16
|
+
|
|
5
17
|
## 0.9.0 - 2026-06-17
|
|
6
18
|
|
|
7
19
|
- Added `createBoundaryReceiver(...)` for optional out-of-order boundary patch
|
package/README.md
CHANGED
|
@@ -187,6 +187,117 @@ You can also use an import map so app code imports `@async/framework` by name:
|
|
|
187
187
|
</script>
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
## Advanced Build-Step Runtime
|
|
191
|
+
|
|
192
|
+
Layer 1 still works with no build step. A build step can optimize the same
|
|
193
|
+
runtime by emitting SSR HTML plus compact registry descriptors. The browser can
|
|
194
|
+
start in the document head, apply snapshots, and wait for a root to appear:
|
|
195
|
+
|
|
196
|
+
```html
|
|
197
|
+
<script type="importmap">
|
|
198
|
+
{
|
|
199
|
+
"imports": {
|
|
200
|
+
"@async/framework": "https://unpkg.com/@async/framework@latest/browser.js"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<script type="application/json" async:snapshot>
|
|
206
|
+
{
|
|
207
|
+
"signal": {
|
|
208
|
+
"productId": "sku-1"
|
|
209
|
+
},
|
|
210
|
+
"handler": {
|
|
211
|
+
"cart.add": { "url": "cart.add.js" }
|
|
212
|
+
},
|
|
213
|
+
"component": {
|
|
214
|
+
"ProductCard": { "url": "ProductCard.js" }
|
|
215
|
+
},
|
|
216
|
+
"asyncSignal": {
|
|
217
|
+
"product.load": { "url": "product.load.js" }
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
</script>
|
|
221
|
+
|
|
222
|
+
<script type="module">
|
|
223
|
+
import {
|
|
224
|
+
Async,
|
|
225
|
+
defineAsyncContainerElement,
|
|
226
|
+
defineAsyncSuspenseElement,
|
|
227
|
+
readSnapshot
|
|
228
|
+
} from "@async/framework";
|
|
229
|
+
|
|
230
|
+
Async.start({
|
|
231
|
+
snapshot: readSnapshot(document),
|
|
232
|
+
registryAssets: { baseUrl: "_async" }
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
defineAsyncContainerElement();
|
|
236
|
+
defineAsyncSuspenseElement();
|
|
237
|
+
</script>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`Async.start()` defaults to rootless browser startup. It creates registries,
|
|
241
|
+
applies snapshots, and prepares the scheduler/server proxy context without
|
|
242
|
+
scanning DOM. Attach a root later with `Async.attachRoot(root)` or by using
|
|
243
|
+
`<async-container>`:
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<async-container>
|
|
247
|
+
<button type="button" on:click="cart.add">Add</button>
|
|
248
|
+
</async-container>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Descriptor URLs are relative to a type folder under `registryAssets.baseUrl`.
|
|
252
|
+
The default is:
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
{
|
|
256
|
+
baseUrl: "_async",
|
|
257
|
+
paths: {
|
|
258
|
+
component: "component",
|
|
259
|
+
handler: "handler",
|
|
260
|
+
asyncSignal: "asyncSignal",
|
|
261
|
+
partial: "partial",
|
|
262
|
+
route: "route"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
So this descriptor:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{ "url": "ProductCard.js#ProductCard" }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
resolves as:
|
|
274
|
+
|
|
275
|
+
```txt
|
|
276
|
+
/_async/component/ProductCard.js#ProductCard
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
If `#export` is omitted, Async tries the registry id leaf, then the file
|
|
280
|
+
basename, then `default`.
|
|
281
|
+
|
|
282
|
+
For declarative async boundaries, use `<async-suspense>` or keep using
|
|
283
|
+
`this.suspense(...)` inside components:
|
|
284
|
+
|
|
285
|
+
```html
|
|
286
|
+
<async-suspense for="product.load">
|
|
287
|
+
<template loading>Loading...</template>
|
|
288
|
+
<template ready>
|
|
289
|
+
<h1 signal:text="product.load.title"></h1>
|
|
290
|
+
</template>
|
|
291
|
+
<template error>
|
|
292
|
+
<p signal:text="product.load.$error.message"></p>
|
|
293
|
+
</template>
|
|
294
|
+
</async-suspense>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The build layer can hide `createBoundaryReceiver(...)` setup, but streaming is
|
|
298
|
+
still explicit boundary patches: boundary id, sequence number, HTML, signal
|
|
299
|
+
patches, and browser-cache patches. Async does not ship a component resume graph.
|
|
300
|
+
|
|
190
301
|
## Core API
|
|
191
302
|
|
|
192
303
|
For npm consumers, `@async/framework` uses conditional exports: browser-aware
|
|
@@ -202,6 +313,7 @@ import {
|
|
|
202
313
|
createApp,
|
|
203
314
|
createCacheRegistry,
|
|
204
315
|
createComponentRegistry,
|
|
316
|
+
createLazyRegistry,
|
|
205
317
|
component,
|
|
206
318
|
computed,
|
|
207
319
|
createSignal,
|
|
@@ -213,10 +325,13 @@ import {
|
|
|
213
325
|
createScheduler,
|
|
214
326
|
createServerProxy,
|
|
215
327
|
createSignalRegistry,
|
|
328
|
+
defineAsyncContainerElement,
|
|
329
|
+
defineAsyncSuspenseElement,
|
|
216
330
|
defineAttributeConfig,
|
|
217
331
|
defineApp,
|
|
218
332
|
defineCache,
|
|
219
333
|
defineComponent,
|
|
334
|
+
defineRegistrySnapshot,
|
|
220
335
|
defineRoute,
|
|
221
336
|
delay,
|
|
222
337
|
effect,
|
package/browser.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type RegistryType =
|
|
|
13
13
|
| "partial"
|
|
14
14
|
| "route"
|
|
15
15
|
| "component"
|
|
16
|
+
| "asyncSignal"
|
|
16
17
|
| "cache.browser"
|
|
17
18
|
| "cache.server"
|
|
18
19
|
| "cache.browser.entries"
|
|
@@ -34,6 +35,20 @@ export interface NormalizedAttributeConfig {
|
|
|
34
35
|
|
|
35
36
|
export type TemplatePrimitive = string | number | boolean | null | undefined;
|
|
36
37
|
export type TemplateLike = TemplateResult | TemplatePrimitive | Node | TemplateLike[];
|
|
38
|
+
export interface LazyDescriptor {
|
|
39
|
+
url: string;
|
|
40
|
+
[key: string]: unknown;
|
|
41
|
+
}
|
|
42
|
+
export interface RegistryAssetsConfig {
|
|
43
|
+
baseUrl?: string;
|
|
44
|
+
paths?: Partial<Record<"component" | "handler" | "asyncSignal" | "partial" | "route", string>>;
|
|
45
|
+
}
|
|
46
|
+
export interface LazyRegistry {
|
|
47
|
+
registryAssets: Required<Pick<RegistryAssetsConfig, "baseUrl">> & { paths: Record<string, string> };
|
|
48
|
+
resolveUrl(type: "component" | "handler" | "asyncSignal" | "partial" | "route", id: string, descriptor: LazyDescriptor): { moduleUrl: string; exportNames: string[]; url: string };
|
|
49
|
+
resolve<T = unknown>(type: "component" | "handler" | "asyncSignal" | "partial" | "route", id: string, descriptor: LazyDescriptor): Promise<T>;
|
|
50
|
+
inspect(): { registryAssets: unknown; modules: string[]; exports: string[] };
|
|
51
|
+
}
|
|
37
52
|
|
|
38
53
|
export interface TemplateResult {
|
|
39
54
|
readonly strings: TemplateStringsArray;
|
|
@@ -209,8 +224,8 @@ export interface HandlerContext {
|
|
|
209
224
|
export type HandlerFunction = (this: HandlerContext, context: HandlerContext) => MaybePromise<unknown>;
|
|
210
225
|
|
|
211
226
|
export interface HandlerRegistry extends RegistryInspection<HandlerFunction> {
|
|
212
|
-
register(id: string, fn: HandlerFunction): string;
|
|
213
|
-
registerMany(map?: Record<string, HandlerFunction>): this;
|
|
227
|
+
register(id: string, fn: HandlerFunction | LazyDescriptor): string;
|
|
228
|
+
registerMany(map?: Record<string, HandlerFunction | LazyDescriptor>): this;
|
|
214
229
|
unregister(id: string): boolean;
|
|
215
230
|
resolve(id: string): HandlerFunction | undefined;
|
|
216
231
|
run(ref: string, context?: Partial<HandlerContext>): Promise<unknown[]>;
|
|
@@ -322,8 +337,8 @@ export interface PartialContext {
|
|
|
322
337
|
export type PartialFunction = (this: PartialContext, props: Record<string, unknown>) => MaybePromise<TemplateLike | ServerEnvelope>;
|
|
323
338
|
|
|
324
339
|
export interface PartialRegistry extends RegistryInspection<PartialFunction> {
|
|
325
|
-
register(id: string, fn: PartialFunction): string;
|
|
326
|
-
registerMany(map?: Record<string, PartialFunction>): this;
|
|
340
|
+
register(id: string, fn: PartialFunction | LazyDescriptor): string;
|
|
341
|
+
registerMany(map?: Record<string, PartialFunction | LazyDescriptor>): this;
|
|
327
342
|
unregister(id: string): boolean;
|
|
328
343
|
resolve(id: string): PartialFunction | undefined;
|
|
329
344
|
render(id: string, props?: Record<string, unknown>, context?: Partial<PartialContext>): Promise<ServerEnvelope>;
|
|
@@ -423,8 +438,8 @@ export interface SuspenseViews {
|
|
|
423
438
|
}
|
|
424
439
|
|
|
425
440
|
export interface ComponentRegistry extends RegistryInspection<ComponentFunction> {
|
|
426
|
-
register(id: string, Component: ComponentFunction): string;
|
|
427
|
-
registerMany(map?: Record<string, ComponentFunction>): this;
|
|
441
|
+
register(id: string, Component: ComponentFunction | LazyDescriptor): string;
|
|
442
|
+
registerMany(map?: Record<string, ComponentFunction | LazyDescriptor>): this;
|
|
428
443
|
unregister(id: string): boolean;
|
|
429
444
|
resolve(id: string): ComponentFunction | undefined;
|
|
430
445
|
}
|
|
@@ -524,22 +539,24 @@ export interface RegistryStore {
|
|
|
524
539
|
|
|
525
540
|
export interface RegistrySnapshot {
|
|
526
541
|
signal: Record<string, unknown>;
|
|
527
|
-
handler: Record<string, { id
|
|
528
|
-
server: Record<string, { id
|
|
529
|
-
partial: Record<string, { id
|
|
542
|
+
handler: Record<string, { id?: string } | LazyDescriptor>;
|
|
543
|
+
server: Record<string, { id?: string } | LazyDescriptor>;
|
|
544
|
+
partial: Record<string, { id?: string } | LazyDescriptor>;
|
|
530
545
|
route: Record<string, RouteDefinition>;
|
|
531
|
-
component: Record<string, { id
|
|
546
|
+
component: Record<string, { id?: string } | LazyDescriptor>;
|
|
547
|
+
asyncSignal: Record<string, { id?: string } | LazyDescriptor>;
|
|
532
548
|
cache: { browser: Record<string, CacheDefinition>; server: Record<string, CacheDefinition> };
|
|
533
549
|
entries: { browser: Record<string, unknown>; server: Record<string, unknown> };
|
|
534
550
|
}
|
|
535
551
|
|
|
536
552
|
export interface AppDefinition {
|
|
537
553
|
signal?: SignalMap;
|
|
538
|
-
handler?: Record<string, HandlerFunction>;
|
|
554
|
+
handler?: Record<string, HandlerFunction | LazyDescriptor>;
|
|
539
555
|
server?: Record<string, ServerFunction>;
|
|
540
|
-
partial?: Record<string, PartialFunction>;
|
|
556
|
+
partial?: Record<string, PartialFunction | LazyDescriptor>;
|
|
541
557
|
route?: Record<string, RouteDefinition | string>;
|
|
542
|
-
component?: Record<string, ComponentFunction>;
|
|
558
|
+
component?: Record<string, ComponentFunction | LazyDescriptor>;
|
|
559
|
+
asyncSignal?: Record<string, AsyncSignalFunction | LazyDescriptor>;
|
|
543
560
|
cache?: {
|
|
544
561
|
browser?: Record<string, CacheDefinition | CacheDefinitionOptions>;
|
|
545
562
|
server?: Record<string, CacheDefinition | CacheDefinitionOptions>;
|
|
@@ -547,25 +564,43 @@ export interface AppDefinition {
|
|
|
547
564
|
entries?: { browser?: Record<string, unknown>; server?: Record<string, unknown> };
|
|
548
565
|
}
|
|
549
566
|
|
|
567
|
+
export interface RegistryRuntimeSnapshot extends AppDefinition {
|
|
568
|
+
signals?: Record<string, unknown>;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export interface RootInspection {
|
|
572
|
+
count: number;
|
|
573
|
+
roots: Array<{ root: Document | Element | DocumentFragment; loader: LoaderInstance; primary: boolean }>;
|
|
574
|
+
}
|
|
575
|
+
|
|
550
576
|
export interface AppHub {
|
|
551
577
|
registry: RegistryStore;
|
|
552
578
|
runtime?: AppRuntime;
|
|
553
579
|
use(type: "signal", entries: SignalMap): this;
|
|
554
|
-
use(type: "handler", entries: Record<string, HandlerFunction>): this;
|
|
580
|
+
use(type: "handler", entries: Record<string, HandlerFunction | LazyDescriptor>): this;
|
|
555
581
|
use(type: "server", entries: Record<string, ServerFunction>): this;
|
|
556
|
-
use(type: "partial", entries: Record<string, PartialFunction>): this;
|
|
582
|
+
use(type: "partial", entries: Record<string, PartialFunction | LazyDescriptor>): this;
|
|
557
583
|
use(type: "route", entries: Record<string, RouteDefinition | string>): this;
|
|
558
|
-
use(type: "component", entries: Record<string, ComponentFunction>): this;
|
|
584
|
+
use(type: "component", entries: Record<string, ComponentFunction | LazyDescriptor>): this;
|
|
585
|
+
use(type: "asyncSignal", entries: Record<string, AsyncSignalFunction | LazyDescriptor>): this;
|
|
559
586
|
use(moduleObject: AppDefinition): this;
|
|
560
587
|
snapshot(): AppDefinition;
|
|
561
588
|
start(options?: CreateAppOptions): AppRuntime;
|
|
589
|
+
attachRoot(root: Document | Element | DocumentFragment): AppRuntime;
|
|
590
|
+
detachRoot(root?: Document | Element | DocumentFragment): this;
|
|
591
|
+
applySnapshot(snapshot: RegistryRuntimeSnapshot, options?: { strict?: boolean }): this;
|
|
592
|
+
inspectRoots(): RootInspection;
|
|
562
593
|
}
|
|
563
594
|
|
|
564
595
|
export interface CreateAppOptions extends LoaderOptions {
|
|
565
596
|
target?: RuntimeTarget;
|
|
566
597
|
mode?: RouterMode;
|
|
567
598
|
boundary?: string;
|
|
568
|
-
snapshot?:
|
|
599
|
+
snapshot?: RegistryRuntimeSnapshot;
|
|
600
|
+
registryAssets?: RegistryAssetsConfig;
|
|
601
|
+
importModule?: (url: string) => MaybePromise<Record<string, unknown>>;
|
|
602
|
+
lazyRegistry?: LazyRegistry;
|
|
603
|
+
strictSnapshots?: boolean;
|
|
569
604
|
registry?: RegistryStore;
|
|
570
605
|
loader?: LoaderInstance;
|
|
571
606
|
router?: Router | false;
|
|
@@ -604,6 +639,10 @@ export interface AppRuntime {
|
|
|
604
639
|
attributes: NormalizedAttributeConfig;
|
|
605
640
|
start(): this;
|
|
606
641
|
use(type: Parameters<AppHub["use"]>[0], entries?: unknown): this;
|
|
642
|
+
attachRoot(root: Document | Element | DocumentFragment): this;
|
|
643
|
+
detachRoot(root?: Document | Element | DocumentFragment): this;
|
|
644
|
+
applySnapshot(snapshot: RegistryRuntimeSnapshot, options?: { strict?: boolean }): this;
|
|
645
|
+
inspectRoots(): RootInspection;
|
|
607
646
|
render(url: string | URL): Promise<RenderResult>;
|
|
608
647
|
destroy(): void;
|
|
609
648
|
}
|
|
@@ -614,6 +653,10 @@ export interface AsyncNamespace extends AppHub {
|
|
|
614
653
|
createApp: typeof createApp;
|
|
615
654
|
defineApp: typeof defineApp;
|
|
616
655
|
readSnapshot: typeof readSnapshot;
|
|
656
|
+
attachRoot: AppHub["attachRoot"];
|
|
657
|
+
detachRoot: AppHub["detachRoot"];
|
|
658
|
+
applySnapshot: AppHub["applySnapshot"];
|
|
659
|
+
inspectRoots: AppHub["inspectRoots"];
|
|
617
660
|
attributeName: typeof attributeName;
|
|
618
661
|
defineAttributeConfig: typeof defineAttributeConfig;
|
|
619
662
|
createBoundaryReceiver: typeof createBoundaryReceiver;
|
|
@@ -622,6 +665,10 @@ export interface AsyncNamespace extends AppHub {
|
|
|
622
665
|
component: typeof component;
|
|
623
666
|
createComponentRegistry: typeof createComponentRegistry;
|
|
624
667
|
defineComponent: typeof defineComponent;
|
|
668
|
+
defineAsyncContainerElement: typeof defineAsyncContainerElement;
|
|
669
|
+
defineAsyncSuspenseElement: typeof defineAsyncSuspenseElement;
|
|
670
|
+
defineRegistrySnapshot: typeof defineRegistrySnapshot;
|
|
671
|
+
createLazyRegistry: typeof createLazyRegistry;
|
|
625
672
|
delay: typeof delay;
|
|
626
673
|
createHandlerRegistry: typeof createHandlerRegistry;
|
|
627
674
|
html: typeof html;
|
|
@@ -649,7 +696,7 @@ export declare function asyncSignal<T = unknown>(id: string, fn: AsyncSignalFunc
|
|
|
649
696
|
export declare const Async: AppHub;
|
|
650
697
|
export declare function createApp(appOrDefinition?: AppHub | AppDefinition, options?: CreateAppOptions): AppRuntime;
|
|
651
698
|
export declare function defineApp(initial?: AppDefinition): AppHub;
|
|
652
|
-
export declare function readSnapshot(root?: Document | Element, options?: { attributes?: AttributeConfig }):
|
|
699
|
+
export declare function readSnapshot(root?: Document | Element, options?: { attributes?: AttributeConfig }): RegistryRuntimeSnapshot;
|
|
653
700
|
export declare function attributeName(attributes: AttributeConfig | undefined, type: keyof NormalizedAttributeConfig, name: string): string;
|
|
654
701
|
export declare function defineAttributeConfig(config?: AttributeConfig): NormalizedAttributeConfig;
|
|
655
702
|
export declare function createBoundaryReceiver(options: BoundaryReceiverOptions): BoundaryReceiver;
|
|
@@ -658,6 +705,10 @@ export declare function defineCache(options?: CacheDefinitionOptions): CacheDefi
|
|
|
658
705
|
export declare function component<TProps extends Record<string, unknown> = Record<string, unknown>>(fn: ComponentFunction<TProps>): ComponentFunction<TProps>;
|
|
659
706
|
export declare function createComponentRegistry(initialMap?: Record<string, ComponentFunction>, options?: { registry?: RegistryStore; type?: "component" }): ComponentRegistry;
|
|
660
707
|
export declare function defineComponent<TProps extends Record<string, unknown> = Record<string, unknown>>(fn: ComponentFunction<TProps>): ComponentFunction<TProps>;
|
|
708
|
+
export declare function defineAsyncContainerElement(options?: { tagName?: string; app?: AppHub; Async?: AppHub; customElements?: CustomElementRegistry; HTMLElement?: typeof HTMLElement; window?: Window }): CustomElementConstructor;
|
|
709
|
+
export declare function defineAsyncSuspenseElement(options?: { tagName?: string; customElements?: CustomElementRegistry; HTMLElement?: typeof HTMLElement; window?: Window }): CustomElementConstructor;
|
|
710
|
+
export declare function defineRegistrySnapshot<T extends RegistryRuntimeSnapshot>(snapshot?: T): T;
|
|
711
|
+
export declare function createLazyRegistry(options?: { registryAssets?: RegistryAssetsConfig; assets?: RegistryAssetsConfig; importModule?: (url: string) => MaybePromise<Record<string, unknown>> }): LazyRegistry;
|
|
661
712
|
export declare function delay(ms: number, signal?: AbortSignal): Promise<void>;
|
|
662
713
|
export declare function createHandlerRegistry(initialMap?: Record<string, HandlerFunction>, options?: { registry?: RegistryStore; type?: "handler" }): HandlerRegistry;
|
|
663
714
|
export declare function html(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult;
|