@async/framework 0.11.14 → 0.11.15
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 +30 -3
- package/README.md +92 -1
- package/browser.d.ts +42 -2
- package/browser.js +288 -16
- package/browser.min.js +1 -1
- package/browser.ts +288 -16
- package/browser.umd.js +288 -16
- package/browser.umd.min.js +1 -1
- package/framework.d.ts +42 -2
- package/framework.ts +288 -16
- package/package.json +5 -72
- package/server.js +288 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.15 - 2026-06-19
|
|
4
|
+
|
|
5
|
+
- Made the source package private and kept its public surface to the minimal
|
|
6
|
+
export spec for root, `/browser`, `/server`, and `/package.json` only.
|
|
7
|
+
- Moved publish staging to generated `dist/package.json` so npm and release
|
|
8
|
+
automation publish from `dist/` while package consumers still receive
|
|
9
|
+
root-level artifacts without `dist/` paths.
|
|
10
|
+
- Removed legacy direct artifact subpath exports plus top-level
|
|
11
|
+
`main`/`module`/`browser`/`types` and generated file lists from the source
|
|
12
|
+
manifest.
|
|
13
|
+
- Updated pack, size, pipeline, and installed-package coverage to verify the
|
|
14
|
+
browser and server entrypoints remain split after packing.
|
|
15
|
+
- Bundle size from bundled TypeScript source: `browser.ts` 197,173 B raw /
|
|
16
|
+
37,198 B gzip -> `browser.min.js` 84,013 B raw / 24,894 B gzip
|
|
17
|
+
(-113,160 B raw, -12,304 B gzip).
|
|
18
|
+
|
|
3
19
|
## 0.11.14 - 2026-06-18
|
|
4
20
|
|
|
5
21
|
- Added condition-specific root declaration targets so browser-conditioned root
|
|
@@ -10,9 +26,20 @@
|
|
|
10
26
|
- Added packed-artifact export-map, declaration/runtime parity, and static
|
|
11
27
|
import checks for root browser, root Node, explicit `/browser`, and explicit
|
|
12
28
|
`/server` entrypoints.
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
- Added component-scoped continuous intersection helpers with
|
|
30
|
+
`this.intersect(...)` and `this.on("intersect", options?, fn)`, preserving
|
|
31
|
+
`on:visible` as a one-shot visibility lifecycle hook.
|
|
32
|
+
- Added declarative `on:intersect` with `intersect:threshold`,
|
|
33
|
+
`intersect:root-margin`, and `intersect:once` pseudo-event options, including
|
|
34
|
+
custom `intersect` attribute prefix support.
|
|
35
|
+
- Added observer cleanup coverage for component teardown, boundary swaps,
|
|
36
|
+
fallback scheduling, repeated entries, and existing visible compatibility.
|
|
37
|
+
- Moved root release artifacts to an ignored generated-output workflow:
|
|
38
|
+
tests, bundle checks, pack checks, and generated CI tasks materialize the
|
|
39
|
+
current package surface before verification or publish.
|
|
40
|
+
- Bundle size from bundled TypeScript source: `browser.ts` 197,173 B raw /
|
|
41
|
+
37,198 B gzip -> `browser.min.js` 84,013 B raw / 24,894 B gzip
|
|
42
|
+
(-113,160 B raw, -12,304 B gzip).
|
|
16
43
|
|
|
17
44
|
## 0.11.13 - 2026-06-18
|
|
18
45
|
|
package/README.md
CHANGED
|
@@ -610,6 +610,10 @@ Loader scans regular HTML attributes:
|
|
|
610
610
|
| `on:click="server.cart.add(productId)"` | Server command with signal args |
|
|
611
611
|
| `on:attach="setup"` | Component root attach lifecycle pseudo-event |
|
|
612
612
|
| `on:visible="trackView"` | Component root visible lifecycle pseudo-event |
|
|
613
|
+
| `on:intersect="trackSection"` | Continuous intersection lifecycle pseudo-event |
|
|
614
|
+
| `intersect:threshold="0,0.5,1"` | Intersection threshold option for `on:intersect` |
|
|
615
|
+
| `intersect:root-margin="-20% 0px -55% 0px"` | Intersection root margin option for `on:intersect` |
|
|
616
|
+
| `intersect:once="true"` | Disconnect `on:intersect` after the first intersecting entry |
|
|
613
617
|
| `signal:text="product.title"` | Text binding |
|
|
614
618
|
| `signal:value="productId"` | Form value binding with writeback |
|
|
615
619
|
| `signal:attr:disabled="product.$loading"` | Attribute binding |
|
|
@@ -644,6 +648,7 @@ Async.start({
|
|
|
644
648
|
attributes: {
|
|
645
649
|
async: "data-async-",
|
|
646
650
|
class: "data-class-",
|
|
651
|
+
intersect: "data-intersect-",
|
|
647
652
|
signal: "data-signal-",
|
|
648
653
|
on: "data-on-"
|
|
649
654
|
}
|
|
@@ -651,7 +656,8 @@ Async.start({
|
|
|
651
656
|
```
|
|
652
657
|
|
|
653
658
|
That maps to `data-async-container`, `data-on-click="save"`,
|
|
654
|
-
`data-signal-text="product.title"`,
|
|
659
|
+
`data-signal-text="product.title"`, `data-class-selected="selected"`, and
|
|
660
|
+
`data-intersect-threshold="0.5"`.
|
|
655
661
|
|
|
656
662
|
Inside `html` templates, signal refs can be passed directly to binding
|
|
657
663
|
attributes:
|
|
@@ -1102,6 +1108,8 @@ Component helpers:
|
|
|
1102
1108
|
| `this.on(event, fn)` | Fragment lifecycle fallback for `attach`, `visible`, and `destroy` |
|
|
1103
1109
|
| `this.onMount(fn)` | Compatibility alias for `this.on("attach", fn)` |
|
|
1104
1110
|
| `this.onVisible(fn)` | Compatibility alias for `this.on("visible", fn)` |
|
|
1111
|
+
| `this.on("intersect", options?, fn)` | Continuous intersection lifecycle for the mounted component scope |
|
|
1112
|
+
| `this.intersect(element, options?, fn)` | Component-owned continuous intersection observer for a direct element |
|
|
1105
1113
|
|
|
1106
1114
|
`this.suspense(...)` is sugar for Loader boundaries:
|
|
1107
1115
|
`asyncSignal + async:boundary + async:* templates`. It emits only templates. The
|
|
@@ -1176,6 +1184,73 @@ this.on("destroy", () => {
|
|
|
1176
1184
|
the component root first becomes visible. Lifecycle events do not drive
|
|
1177
1185
|
component rerenders.
|
|
1178
1186
|
|
|
1187
|
+
Use `on:intersect` when markup should receive continuous intersection updates
|
|
1188
|
+
through a registered handler:
|
|
1189
|
+
|
|
1190
|
+
```html
|
|
1191
|
+
<section
|
|
1192
|
+
on:intersect="trackSection"
|
|
1193
|
+
intersect:threshold="0,0.25,0.5,0.75,1"
|
|
1194
|
+
intersect:root-margin="-20% 0px -55% 0px"
|
|
1195
|
+
>
|
|
1196
|
+
...
|
|
1197
|
+
</section>
|
|
1198
|
+
```
|
|
1199
|
+
|
|
1200
|
+
The handler receives `element`, `entry`, `entries`, `observer`,
|
|
1201
|
+
`isIntersecting`, `intersectionRatio`, and `unsupported`. Custom roots are not
|
|
1202
|
+
selector-based; use `this.intersect(...)` with a direct root element when a
|
|
1203
|
+
custom observer root is needed.
|
|
1204
|
+
|
|
1205
|
+
Use `this.on("intersect", ...)` when a component needs continuous visibility
|
|
1206
|
+
state:
|
|
1207
|
+
|
|
1208
|
+
```js
|
|
1209
|
+
const Card = defineComponent(function Card() {
|
|
1210
|
+
const visible = this.signal(false);
|
|
1211
|
+
|
|
1212
|
+
this.on("intersect", { threshold: 0.5 }, ({ isIntersecting }) => {
|
|
1213
|
+
visible.set(isIntersecting);
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
return html`<article class:visible="${visible}">...</article>`;
|
|
1217
|
+
});
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
Use `this.intersect(...)` with a direct element when a parent owns scroll-spy or
|
|
1221
|
+
active-section state:
|
|
1222
|
+
|
|
1223
|
+
```js
|
|
1224
|
+
const Section = defineComponent(function Section({ id, observeSection }) {
|
|
1225
|
+
const attach = this.handler("attach", function ({ element }) {
|
|
1226
|
+
return observeSection(id, element);
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
return html`<section on:attach="${attach}"><h2>${id}</h2></section>`;
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
const Page = defineComponent(function Page() {
|
|
1233
|
+
const active = this.signal("intro");
|
|
1234
|
+
const ratios = new Map();
|
|
1235
|
+
const options = {
|
|
1236
|
+
rootMargin: "-20% 0px -55% 0px",
|
|
1237
|
+
threshold: [0, 0.25, 0.5, 0.75, 1]
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
const observeSection = (id, element) => this.intersect(element, options, ({ entry }) => {
|
|
1241
|
+
ratios.set(id, entry.isIntersecting ? entry.intersectionRatio : 0);
|
|
1242
|
+
const best = [...ratios.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
1243
|
+
active.set(best?.[0] ?? id);
|
|
1244
|
+
});
|
|
1245
|
+
|
|
1246
|
+
return html`
|
|
1247
|
+
<nav signal:text="${active}"></nav>
|
|
1248
|
+
${this.render(Section, { id: "intro", observeSection })}
|
|
1249
|
+
${this.render(Section, { id: "runtime", observeSection })}
|
|
1250
|
+
`;
|
|
1251
|
+
});
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1179
1254
|
## Streaming
|
|
1180
1255
|
|
|
1181
1256
|
Out-of-order HTML can target a boundary and keep delegated handlers working:
|
|
@@ -1264,6 +1339,8 @@ pnpm run pipeline:github:check
|
|
|
1264
1339
|
Useful commands:
|
|
1265
1340
|
|
|
1266
1341
|
```bash
|
|
1342
|
+
pnpm run bundle
|
|
1343
|
+
pnpm run bundle:clean
|
|
1267
1344
|
pnpm run pipeline:verify
|
|
1268
1345
|
pnpm run pipeline:pages
|
|
1269
1346
|
pnpm run registry:lint
|
|
@@ -1271,6 +1348,20 @@ pnpm run pipeline:release:doctor
|
|
|
1271
1348
|
pnpm run release:check
|
|
1272
1349
|
```
|
|
1273
1350
|
|
|
1351
|
+
Release artifacts such as `browser.js`, `browser.min.js`,
|
|
1352
|
+
`browser.umd.min.js`, `browser.ts`, `browser.d.ts`, `framework.ts`,
|
|
1353
|
+
`framework.d.ts`, and `server.js` are generated into `dist/`. The generated
|
|
1354
|
+
`dist/` directory is the package root for `npm pack` and release publishing, so
|
|
1355
|
+
the published package and CDN surface still expose those files at package root
|
|
1356
|
+
rather than under `dist/`. The source `package.json` stays private and owns the
|
|
1357
|
+
minimal public export spec, while omitting legacy `main`/`module`/`browser`
|
|
1358
|
+
entry fields and generated package file lists. `scripts/build-framework-bundle.js`
|
|
1359
|
+
derives the generated `dist/package.json` and staged artifact names from that
|
|
1360
|
+
spec. Feature branches should edit source files and let `pnpm run bundle`,
|
|
1361
|
+
`pnpm test`, `pnpm run pack:check`, or the generated release workflow
|
|
1362
|
+
materialize the publish tree. Use `pnpm run bundle:clean` to remove local
|
|
1363
|
+
generated artifacts after inspection.
|
|
1364
|
+
|
|
1274
1365
|
`registry:lint` scans package source and examples for declared registry ids
|
|
1275
1366
|
such as signals, handlers, server functions, partials, routes, and components.
|
|
1276
1367
|
It writes `.async/registry-manifest.json` plus a per-file cache at
|
package/browser.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface AttributeConfig {
|
|
|
23
23
|
async?: string | string[];
|
|
24
24
|
class?: string | string[];
|
|
25
25
|
signal?: string | string[];
|
|
26
|
+
intersect?: string | string[];
|
|
26
27
|
on?: string | string[];
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -30,6 +31,7 @@ export interface NormalizedAttributeConfig {
|
|
|
30
31
|
async: string[];
|
|
31
32
|
class: string[];
|
|
32
33
|
signal: string[];
|
|
34
|
+
intersect: string[];
|
|
33
35
|
on: string[];
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -403,7 +405,41 @@ export interface Router {
|
|
|
403
405
|
destroy(): void;
|
|
404
406
|
}
|
|
405
407
|
|
|
406
|
-
export type LifecycleEventName = "attach" | "mount" | "visible" | "destroy";
|
|
408
|
+
export type LifecycleEventName = "attach" | "mount" | "visible" | "intersect" | "destroy";
|
|
409
|
+
|
|
410
|
+
export interface IntersectionFallbackEntry {
|
|
411
|
+
target: Element;
|
|
412
|
+
isIntersecting: boolean;
|
|
413
|
+
intersectionRatio: number;
|
|
414
|
+
time: number;
|
|
415
|
+
rootBounds: DOMRectReadOnly | null;
|
|
416
|
+
boundingClientRect: DOMRect | DOMRectReadOnly | null;
|
|
417
|
+
intersectionRect: DOMRect | DOMRectReadOnly | null;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export interface IntersectionEvent {
|
|
421
|
+
target: Element;
|
|
422
|
+
element: Element;
|
|
423
|
+
el: Element;
|
|
424
|
+
root: Document | Element | DocumentFragment;
|
|
425
|
+
entry: IntersectionObserverEntry | IntersectionFallbackEntry;
|
|
426
|
+
entries: Array<IntersectionObserverEntry | IntersectionFallbackEntry>;
|
|
427
|
+
observer: IntersectionObserver | null;
|
|
428
|
+
isIntersecting: boolean;
|
|
429
|
+
intersectionRatio: number;
|
|
430
|
+
unsupported: boolean;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export interface IntersectionOptions {
|
|
434
|
+
root?: Element | Document | null;
|
|
435
|
+
rootMargin?: string;
|
|
436
|
+
threshold?: number | number[];
|
|
437
|
+
once?: boolean;
|
|
438
|
+
schedule?: "lifecycle" | "sync";
|
|
439
|
+
key?: string;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export type IntersectionCallback = (this: ComponentContext, event: IntersectionEvent) => unknown;
|
|
407
443
|
|
|
408
444
|
export interface ComponentContext {
|
|
409
445
|
scope: string;
|
|
@@ -423,9 +459,13 @@ export interface ComponentContext {
|
|
|
423
459
|
handler(name: string, fn: HandlerFunction): string;
|
|
424
460
|
render<TProps extends Record<string, unknown> = Record<string, unknown>>(Child: ComponentFunction<TProps>, props?: TProps): TemplateLike;
|
|
425
461
|
suspense(signalRef: Pick<SignalRef, "id">, views: SuspenseViews | SuspenseReadyView): TemplateLike;
|
|
426
|
-
on(eventName:
|
|
462
|
+
on(eventName: "intersect", fn: IntersectionCallback): void;
|
|
463
|
+
on(eventName: "intersect", options: IntersectionOptions | undefined | null, fn: IntersectionCallback): void;
|
|
464
|
+
on(eventName: Exclude<LifecycleEventName, "intersect">, fn: (this: ComponentContext, target?: Element) => unknown): void;
|
|
427
465
|
onMount(fn: (this: ComponentContext, target?: Element) => unknown): void;
|
|
428
466
|
onVisible(fn: (this: ComponentContext, target?: Element) => unknown): void;
|
|
467
|
+
intersect(target: Element, fn: IntersectionCallback): Cleanup;
|
|
468
|
+
intersect(target: Element, options: IntersectionOptions | undefined | null, fn: IntersectionCallback): Cleanup;
|
|
429
469
|
}
|
|
430
470
|
|
|
431
471
|
export type ComponentFunction<TProps extends Record<string, unknown> = Record<string, unknown>> = (this: ComponentContext, props: TProps) => TemplateLike;
|