@async/framework 0.4.0 → 0.5.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 +15 -0
- package/README.md +73 -0
- package/framework.js +313 -92
- package/package.json +1 -1
- package/src/async-signal.js +12 -1
- package/src/cache.js +5 -0
- package/src/component.js +79 -15
- package/src/handlers.js +12 -4
- package/src/loader.js +89 -7
- package/src/partials.js +5 -0
- package/src/registry-store.js +4 -0
- package/src/router.js +9 -0
- package/src/server.js +26 -6
- package/src/signals.js +16 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 - 2026-06-17
|
|
4
|
+
|
|
5
|
+
- Added `this.suspense(signalRef, views)` for component-owned async boundary
|
|
6
|
+
templates without adding a wrapper, rerender loop, hydration, or promise
|
|
7
|
+
throwing.
|
|
8
|
+
- Added `signal:prop:*` property bindings and tests for inline signal refs in
|
|
9
|
+
`signal:text`, `signal:attr:*`, and `signal:prop:*`.
|
|
10
|
+
- Added explicit `unregister(id)` APIs to runtime registries and component
|
|
11
|
+
cleanup for scoped signals, async signals, computed signals, and handlers.
|
|
12
|
+
- Added boundary swap cleanup for mounted component fragments and old DOM
|
|
13
|
+
bindings.
|
|
14
|
+
- Added server-call normalization for async signals, including returned signal
|
|
15
|
+
effects, proxy abort propagation, and stable server error messages.
|
|
16
|
+
- Added `prevent` as a command-event alias for `preventDefault`.
|
|
17
|
+
|
|
3
18
|
## 0.4.0 - 2026-06-17
|
|
4
19
|
|
|
5
20
|
- Added a generated root `framework.js` ESM bundle for UNPKG browser imports.
|
package/README.md
CHANGED
|
@@ -253,6 +253,7 @@ Naming rules:
|
|
|
253
253
|
| `create*` | Runtime instance or mutable runtime primitive |
|
|
254
254
|
| `Async.use(...)` | App-level declaration registration |
|
|
255
255
|
| `registry.register(...)` | Low-level registration on a concrete runtime registry |
|
|
256
|
+
| `registry.unregister(...)` | Low-level removal from a concrete runtime registry |
|
|
256
257
|
|
|
257
258
|
Singular registry keys are canonical: `signal`, `handler`, `server`,
|
|
258
259
|
`partial`, `route`, `component`, and nested `cache.browser` / `cache.server`.
|
|
@@ -314,6 +315,7 @@ signals.set("count", 1);
|
|
|
314
315
|
signals.update("count", (count) => count + 1);
|
|
315
316
|
signals.subscribe("count", (count) => console.log(count));
|
|
316
317
|
signals.ref("count").value;
|
|
318
|
+
signals.unregister("count");
|
|
317
319
|
```
|
|
318
320
|
|
|
319
321
|
Initializer maps are supported:
|
|
@@ -389,6 +391,7 @@ AsyncLoader scans regular HTML attributes:
|
|
|
389
391
|
| `signal:text="product.title"` | Text binding |
|
|
390
392
|
| `signal:value="productId"` | Form value binding with writeback |
|
|
391
393
|
| `signal:attr:disabled="product.$loading"` | Attribute binding |
|
|
394
|
+
| `signal:prop:checked="selected"` | DOM property binding |
|
|
392
395
|
| `class:selected="selected"` | Class toggle from a signal path |
|
|
393
396
|
| `signal:class="buttonClasses"` | Class set from a signal value: string, object, or array |
|
|
394
397
|
| `async:boundary="product"` | Async or streamed replacement boundary |
|
|
@@ -428,6 +431,24 @@ Async.start({
|
|
|
428
431
|
That maps to `data-async-container`, `data-on-click="save"`,
|
|
429
432
|
`data-signal-text="product.title"`, and `data-class-selected="selected"`.
|
|
430
433
|
|
|
434
|
+
Inside `html` templates, signal refs can be passed directly to binding
|
|
435
|
+
attributes:
|
|
436
|
+
|
|
437
|
+
```js
|
|
438
|
+
const title = this.signal("Keyboard");
|
|
439
|
+
const disabled = this.signal(false);
|
|
440
|
+
const checked = this.signal(true);
|
|
441
|
+
|
|
442
|
+
return html`
|
|
443
|
+
<h1 signal:text="${title}"></h1>
|
|
444
|
+
<button signal:attr:disabled="${disabled}">Save</button>
|
|
445
|
+
<input type="checkbox" signal:prop:checked="${checked}">
|
|
446
|
+
`;
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Use `signal:value` for form value binding with writeback. Use `signal:prop:*`
|
|
450
|
+
when you only need one-way DOM property updates.
|
|
451
|
+
|
|
431
452
|
Named class toggles use their own top-level namespace:
|
|
432
453
|
|
|
433
454
|
```html
|
|
@@ -518,6 +539,7 @@ Plain commands resolve through the handler registry. Built-ins are registered by
|
|
|
518
539
|
default:
|
|
519
540
|
|
|
520
541
|
```txt
|
|
542
|
+
prevent
|
|
521
543
|
preventDefault
|
|
522
544
|
stopPropagation
|
|
523
545
|
stopImmediatePropagation
|
|
@@ -584,6 +606,12 @@ await server.cart.add("sku-1", 2);
|
|
|
584
606
|
|
|
585
607
|
Server responses can include `value`, `signals`, `boundary`, `html`, `redirect`,
|
|
586
608
|
or `error`. Signal patches are applied before boundary swaps and redirects.
|
|
609
|
+
Namespace calls such as `server.cart.add(...)` return the unwrapped `value`.
|
|
610
|
+
|
|
611
|
+
When an async signal calls a server namespace function, the framework passes the
|
|
612
|
+
active abort signal through proxy calls. Returned server effects such as
|
|
613
|
+
`signals`, `cache.browser`, `boundary/html`, and `redirect` are applied before
|
|
614
|
+
the async signal stores the unwrapped `value`.
|
|
587
615
|
|
|
588
616
|
### Router And Partials
|
|
589
617
|
|
|
@@ -812,10 +840,55 @@ Component helpers:
|
|
|
812
840
|
| `this.handler(name, fn)` | Scoped named handler registry entry |
|
|
813
841
|
| `this.handler(fn)` | Generated scoped handler registry entry |
|
|
814
842
|
| `this.render(Component, props)` | Child fragment rendering |
|
|
843
|
+
| `this.suspense(signalRef, views)` | Async boundary template helper |
|
|
815
844
|
| `this.on(event, fn)` | Fragment lifecycle fallback for `attach`, `visible`, and `destroy` |
|
|
816
845
|
| `this.onMount(fn)` | Compatibility alias for `this.on("attach", fn)` |
|
|
817
846
|
| `this.onVisible(fn)` | Compatibility alias for `this.on("visible", fn)` |
|
|
818
847
|
|
|
848
|
+
`this.suspense(...)` is sugar for AsyncLoader boundaries:
|
|
849
|
+
`asyncSignal + async:boundary + async:* templates`. It emits only templates. The
|
|
850
|
+
caller owns the boundary element, and the loader chooses the loading, ready, or
|
|
851
|
+
error template from the async signal status.
|
|
852
|
+
|
|
853
|
+
```js
|
|
854
|
+
const Product = defineComponent(function Product() {
|
|
855
|
+
const product = this.asyncSignal("product", async function () {
|
|
856
|
+
return this.server.products.get("sku-1");
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
return html`
|
|
860
|
+
<article async:boundary="${product.id}">
|
|
861
|
+
${this.suspense(product, {
|
|
862
|
+
loading() {
|
|
863
|
+
return html`<p>Loading...</p>`;
|
|
864
|
+
},
|
|
865
|
+
ready(product) {
|
|
866
|
+
return html`<h1 signal:text="${product.id}.title"></h1>`;
|
|
867
|
+
},
|
|
868
|
+
error(product) {
|
|
869
|
+
return html`<p signal:text="${product.id}.$error.message"></p>`;
|
|
870
|
+
}
|
|
871
|
+
})}
|
|
872
|
+
</article>
|
|
873
|
+
`;
|
|
874
|
+
});
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
The shorthand form treats the callback as the ready template:
|
|
878
|
+
|
|
879
|
+
```js
|
|
880
|
+
this.suspense(product, (product) => html`
|
|
881
|
+
<h1 signal:text="${product.id}.title"></h1>
|
|
882
|
+
`);
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
`this.suspense(...)` is not React Suspense. It does not throw promises,
|
|
886
|
+
hydrate, diff, rerender a component tree, or emit a wrapper element.
|
|
887
|
+
|
|
888
|
+
Component-scoped signals and handlers are unregistered when the mounted
|
|
889
|
+
fragment is destroyed. `loader.swap(...)` cleans up old DOM bindings and mounted
|
|
890
|
+
component fragments under the swapped boundary before inserting the new HTML.
|
|
891
|
+
|
|
819
892
|
Put component lifecycle on the component root element when there is one:
|
|
820
893
|
|
|
821
894
|
```js
|