@@ -10,9 +10,9 @@ pnpm add @async/framework
1010```
1111
1212``` html
13- <main data- async- container >
13+ <main async: container >
1414 <button type =" button" on:click =" decrement" >-</button >
15- <strong data-async- text =" count" ></strong >
15+ <strong signal: text =" count" ></strong >
1616 <button type =" button" on:click =" increment" >+</button >
1717</main >
1818<script type =" module" src =" ./main.js" ></script >
@@ -69,12 +69,78 @@ pnpm add @async/framework
6969The package is ESM-only and supports Node.js 24 and newer for tests, examples,
7070and package lifecycle tooling. Browser consumers import ESM directly.
7171
72+ ## CDN
73+
74+ The package is browser-ready ESM and can be loaded from UNPKG without a build
75+ step. Use ` @latest ` for quick prototypes, and pin an exact version in
76+ production:
77+
78+ ``` html
79+ <main async:container >
80+ <button type =" button" on:click =" increment" >+</button >
81+ <strong signal:text =" count" ></strong >
82+ </main >
83+
84+ <script type =" module" >
85+ import {
86+ Async ,
87+ createSignal
88+ } from " https://unpkg.com/@async/framework@latest" ;
89+
90+ Async .use ({
91+ signal: {
92+ count: createSignal (0 )
93+ },
94+ handler: {
95+ increment () {
96+ this .signals .update (" count" , (count ) => count + 1 );
97+ }
98+ }
99+ });
100+
101+ Async .start ({ root: document });
102+ </script >
103+ ```
104+
105+ You can also use an import map so app code imports ` @async/framework ` by name:
106+
107+ ``` html
108+ <script type =" importmap" >
109+ {
110+ " imports" : {
111+ " @async/framework" : " https://unpkg.com/@async/framework@latest"
112+ }
113+ }
114+ </script >
115+
116+ <script type =" module" >
117+ import {
118+ Async ,
119+ createSignal
120+ } from " @async/framework" ;
121+
122+ Async .use ({
123+ signal: {
124+ count: createSignal (0 )
125+ },
126+ handler: {
127+ increment () {
128+ this .signals .update (" count" , (count ) => count + 1 );
129+ }
130+ }
131+ });
132+
133+ Async .start ({ root: document });
134+ </script >
135+ ```
136+
72137## Core API
73138
74139``` js
75140import {
76141 AsyncLoader ,
77142 Async ,
143+ attributeName ,
78144 asyncSignal ,
79145 createApp ,
80146 createCacheRegistry ,
@@ -84,11 +150,13 @@ import {
84150 createSignal ,
85151 createHandlerRegistry ,
86152 createPartialRegistry ,
153+ createRegistryStore ,
87154 createRouteRegistry ,
88155 createRouter ,
89156 createServerProxy ,
90157 createServerRegistry ,
91158 createSignalRegistry ,
159+ defineAttributeConfig ,
92160 defineApp ,
93161 defineCache ,
94162 defineComponent ,
@@ -170,6 +238,50 @@ Naming rules:
170238Singular registry keys are canonical: ` signal ` , ` handler ` , ` server ` ,
171239` partial ` , ` route ` , ` component ` , and nested ` cache.browser ` / ` cache.server ` .
172240
241+ ### Registry Inspection
242+
243+ ` Async.registry ` is the global inspection surface for registered app pieces.
244+ Every runtime and concrete registry also points at the same backing store:
245+
246+ ``` js
247+ Async .registry .keys (" signal" );
248+ Async .registry .entries (" route" );
249+ Async .registry .snapshot ();
250+
251+ const runtime = Async .start ({ root: document });
252+
253+ runtime .registry .keys (" handler" );
254+ runtime .signals .registry === runtime .registry ;
255+ runtime .browser .cache .registry === runtime .registry ;
256+ ```
257+
258+ Supported inspection types:
259+
260+ ``` txt
261+ signal
262+ handler
263+ server
264+ partial
265+ route
266+ component
267+ cache.browser
268+ cache.server
269+ cache.browser.entries
270+ cache.server.entries
271+ ```
272+
273+ Browser runtime inspection exposes server ids as descriptors, not executable
274+ server functions, and does not expose server cache contents:
275+
276+ ``` js
277+ runtime .registry .keys (" server" );
278+ runtime .registry .get (" server" , " products.get" );
279+ // { id: "products.get", kind: "server" }
280+
281+ runtime .registry .snapshot ().entries .server ;
282+ // {}
283+ ```
284+
173285### Signals
174286
175287``` js
@@ -249,33 +361,50 @@ AsyncLoader scans regular HTML attributes:
249361
250362| Attribute | Behavior |
251363| --- | --- |
252- | ` data- async- container` | Marks a scannable app root |
364+ | ` async: container ` | Marks a scannable app root |
253365| ` on:click="selectProduct" ` | Delegated command event |
254366| ` on:submit="preventDefault; save" ` | Sequential command chain |
255367| ` on:click="server.cart.add(productId)" ` | Server command with signal args |
256- | ` data-async- text="product.title"` | Text binding |
257- | ` data-async- value="productId"` | Form value binding with writeback |
258- | ` data-async- attr:disabled="product.$loading"` | Attribute binding |
259- | ` data-async- class:selected="selected"` | Class toggle |
260- | ` data- async- boundary="product"` | Async or streamed replacement boundary |
261- | ` data- async- loading="product"` | Boundary loading template |
262- | ` data- async- ready="product"` | Boundary ready template |
263- | ` data- async- error="product"` | Boundary error template |
368+ | ` signal: text="product.title"` | Text binding |
369+ | ` signal: value="productId"` | Form value binding with writeback |
370+ | ` signal: attr:disabled="product.$loading"` | Attribute binding |
371+ | ` signal: class:selected="selected"` | Class toggle |
372+ | ` async: boundary="product" ` | Async or streamed replacement boundary |
373+ | ` async: loading="product" ` | Boundary loading template |
374+ | ` async: ready="product" ` | Boundary ready template |
375+ | ` async: error="product" ` | Boundary error template |
264376
265377``` html
266- <section data- async- boundary =" product" >
267- <template data- async- loading =" product" >
378+ <section async: boundary =" product" >
379+ <template async: loading =" product" >
268380 <p >Loading...</p >
269381 </template >
270- <template data- async- ready =" product" >
271- <h1 data-async- text =" product.title" ></h1 >
382+ <template async: ready =" product" >
383+ <h1 signal: text =" product.title" ></h1 >
272384 </template >
273- <template data- async- error =" product" >
274- <p data-async- text =" product.$error.message" ></p >
385+ <template async: error =" product" >
386+ <p signal: text =" product.$error.message" ></p >
275387 </template >
276388</section >
277389```
278390
391+ The default prefixes are ` async: ` , ` signal: ` , and ` on: ` . You can switch to
392+ data attributes when a host needs that shape:
393+
394+ ``` js
395+ Async .start ({
396+ root: document ,
397+ attributes: {
398+ async: " data-async-" ,
399+ signal: " data-signal-" ,
400+ on: " data-on-"
401+ }
402+ });
403+ ```
404+
405+ That maps to ` data-async-container ` , ` data-on-click="save" ` , and
406+ ` data-signal-text="product.title" ` .
407+
279408### Command Events
280409
281410` on:* ` works with any native DOM event name. ` on:mount ` and ` on:visible ` are
@@ -417,13 +546,13 @@ Router modes:
417546CSR startup can use an empty route boundary:
418547
419548``` html
420- <main data- async- container >
549+ <main async: container >
421550 <nav >
422551 <a href =" /" >Home</a >
423552 <a href =" /products/sku-1" >Product</a >
424553 </nav >
425554
426- <section data- async- boundary =" route" ></section >
555+ <section async: boundary =" route" ></section >
427556</main >
428557```
429558
@@ -520,10 +649,10 @@ const response = await serverRuntime.render("/products/123");
520649The returned HTML includes a route boundary plus a JSON snapshot:
521650
522651``` html
523- <section data- async- boundary =" route" >
652+ <section async: boundary =" route" >
524653 <!-- server-rendered route partial -->
525654</section >
526- <script type =" application/json" data- async- snapshot >{} </script >
655+ <script type =" application/json" async: snapshot >{} </script >
527656```
528657
529658Browser activation scans the existing HTML and attaches events. It does not
@@ -558,8 +687,8 @@ const Toggle = defineComponent(function Toggle() {
558687 <button
559688 type =" button"
560689 on:click =" ${toggle}"
561- data-async- class:selected =" ${selected.id}"
562- data-async- attr:aria-pressed =" ${selected.id}"
690+ signal: class:selected =" ${selected.id}"
691+ signal: attr:aria-pressed =" ${selected.id}"
563692 >
564693 Toggle
565694 </button >
@@ -597,7 +726,7 @@ loader.swap(
597726 " product" ,
598727 `
599728 <article>
600- <h1 data-async- text="product.title"></h1>
729+ <h1 signal: text="product.title"></h1>
601730 <button type="button" on:click="selectProduct">Select</button>
602731 </article>
603732 `
0 commit comments