@async/framework 0.5.0 → 0.7.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 CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 - 2026-06-17
4
+
5
+ - Added router navigation abort/version guards so stale route partials cannot
6
+ clobber newer navigations, and route partial contexts receive `this.abort`.
7
+ - Added ranked route matching so static and dynamic routes win over wildcard
8
+ fallbacks regardless of registration order.
9
+ - Added `readSnapshot(...)` and automatic browser activation from SSR snapshot
10
+ scripts.
11
+ - Fixed repeated server result application when proxy/server envelopes pass
12
+ through namespace or handler callers.
13
+ - Added in-flight `cache.getOrSet(...)` deduplication and clear proxy errors for
14
+ `File`, `Blob`, and `FormData` values that the JSON transport cannot send.
15
+ - Changed `framework.ts` from a source facade to a bundled TypeScript source
16
+ entrypoint.
17
+
18
+ ## 0.6.0 - 2026-06-17
19
+
20
+ - Added `Loader` as the canonical public loader factory, including
21
+ `Async.Loader(...)` for UMD script-tag usage, while keeping `AsyncLoader` as
22
+ a compatibility alias.
23
+ - Added generated root CDN artifacts: `framework.min.js`, `framework.umd.js`,
24
+ `framework.umd.min.js`, `framework.ts`, and `framework.d.ts`.
25
+ - Added package exports and docs for ESM, compact ESM, UMD, compact UMD, and
26
+ TypeScript source/types entrypoints.
27
+ - Added exported helpers to the UMD-only `globalThis.Async` object for
28
+ script-tag CDN usage while keeping ESM `Async` as the app hub export.
29
+ - Added UMD namespace conflict checks so generated helpers cannot silently
30
+ overwrite app-hub fields such as `use`, `start`, or `registry`.
31
+ - Added `registry:lint`, a cached package linter that emits a local registry
32
+ manifest and detects conflicting signal, handler, server, partial, route, or
33
+ component declarations while skipping generated root bundles.
34
+
3
35
  ## 0.5.0 - 2026-06-17
4
36
 
5
37
  - Added `this.suspense(signalRef, views)` for component-owned async boundary
package/README.md CHANGED
@@ -90,9 +90,18 @@ and package lifecycle tooling. Browser consumers import ESM directly.
90
90
 
91
91
  ## CDN
92
92
 
93
- The package ships a root `framework.js` ESM bundle for UNPKG and can be loaded
94
- without a build step. Use `@latest` for quick prototypes, and pin an exact
95
- version in production:
93
+ The package ships root CDN artifacts for UNPKG and can be loaded without a
94
+ build step. Use `@latest` for quick prototypes, and pin an exact version in
95
+ production:
96
+
97
+ | File | Format | Use |
98
+ | --- | --- | --- |
99
+ | `framework.js` | ESM | Readable browser module bundle |
100
+ | `framework.min.js` | ESM | Compact browser module bundle |
101
+ | `framework.umd.js` | UMD | Readable script-tag/CommonJS-style bundle |
102
+ | `framework.umd.min.js` | UMD | Compact script-tag/CommonJS-style bundle and default CDN file |
103
+ | `framework.ts` | Bundled TypeScript source | TS-aware runtimes and higher-layer tooling |
104
+ | `framework.d.ts` | Type declarations | TypeScript declarations for the public API |
96
105
 
97
106
  ```html
98
107
  <main async:container>
@@ -121,6 +130,29 @@ version in production:
121
130
  </script>
122
131
  ```
123
132
 
133
+ For a plain script tag, use the UMD bundle. In this UMD-only global form,
134
+ `globalThis.Async` is the app hub plus the exported helper functions, with
135
+ `globalThis.AsyncFramework` kept as an alias. Lower-level bootloader code can
136
+ call `Async.Loader(...)` directly.
137
+
138
+ ```html
139
+ <script src="https://unpkg.com/@async/framework@latest/framework.umd.min.js"></script>
140
+ <script>
141
+ Async.use({
142
+ signal: {
143
+ count: Async.createSignal(0)
144
+ },
145
+ handler: {
146
+ increment() {
147
+ this.signals.update("count", (count) => count + 1);
148
+ }
149
+ }
150
+ });
151
+
152
+ Async.start({ root: document });
153
+ </script>
154
+ ```
155
+
124
156
  You can also use an import map so app code imports `@async/framework` by name:
125
157
 
126
158
  ```html
@@ -157,8 +189,8 @@ You can also use an import map so app code imports `@async/framework` by name:
157
189
 
158
190
  ```js
159
191
  import {
160
- AsyncLoader,
161
192
  Async,
193
+ Loader,
162
194
  attributeName,
163
195
  asyncSignal,
164
196
  createApp,
@@ -183,11 +215,15 @@ import {
183
215
  delay,
184
216
  effect,
185
217
  html,
218
+ readSnapshot,
186
219
  route,
187
220
  signal
188
221
  } from "@async/framework";
189
222
  ```
190
223
 
224
+ `Loader` is the canonical loader factory. `AsyncLoader` remains as a
225
+ compatibility alias for older code.
226
+
191
227
  ### App Hub
192
228
 
193
229
  `Async` is an exported app hub singleton. It is not installed on `globalThis`
@@ -376,9 +412,13 @@ await delay(250, this.abort);
376
412
  If a dependency read through `this.signals.get(...)` changes, the async signal
377
413
  reruns and the previous run is aborted.
378
414
 
415
+ Dependency reads are captured while the async signal function starts running.
416
+ Read signal dependencies before the first `await`; reads that happen later are
417
+ ordinary reads and do not create refresh subscriptions.
418
+
379
419
  ## HTML Protocol
380
420
 
381
- AsyncLoader scans regular HTML attributes:
421
+ Loader scans regular HTML attributes:
382
422
 
383
423
  | Attribute | Behavior |
384
424
  | --- | --- |
@@ -784,15 +824,21 @@ hydrate, diff, patch, or rerender:
784
824
  ```js
785
825
  createApp(browserApp, {
786
826
  root: document,
787
- snapshot,
788
827
  server: createServerProxy({ endpoint: "/__async/server" })
789
828
  }).start();
790
829
  ```
791
830
 
831
+ If an `async:snapshot` script is present under the root or document,
832
+ `createApp(...)` reads it automatically. You can also inspect it directly:
833
+
834
+ ```js
835
+ const snapshot = readSnapshot(document);
836
+ ```
837
+
792
838
  ## Components
793
839
 
794
840
  Components are scoped fragment functions. They return strings or `html`
795
- templates; AsyncLoader inserts and scans the result. There is no virtual node
841
+ templates; Loader inserts and scans the result. There is no virtual node
796
842
  type and no rerender loop.
797
843
 
798
844
  ```js
@@ -822,7 +868,7 @@ const Toggle = defineComponent(function Toggle() {
822
868
  `;
823
869
  });
824
870
 
825
- const loader = AsyncLoader({ root: document });
871
+ const loader = Loader({ root: document });
826
872
  loader.mount(document.querySelector("#app"), Toggle);
827
873
  ```
828
874
 
@@ -845,7 +891,7 @@ Component helpers:
845
891
  | `this.onMount(fn)` | Compatibility alias for `this.on("attach", fn)` |
846
892
  | `this.onVisible(fn)` | Compatibility alias for `this.on("visible", fn)` |
847
893
 
848
- `this.suspense(...)` is sugar for AsyncLoader boundaries:
894
+ `this.suspense(...)` is sugar for Loader boundaries:
849
895
  `asyncSignal + async:boundary + async:* templates`. It emits only templates. The
850
896
  caller owns the boundary element, and the loader chooses the loading, ready, or
851
897
  error template from the async signal status.
@@ -967,10 +1013,19 @@ Useful commands:
967
1013
  ```bash
968
1014
  pnpm run pipeline:verify
969
1015
  pnpm run pipeline:pages
1016
+ pnpm run registry:lint
970
1017
  pnpm run pipeline:release:doctor
971
1018
  pnpm run release:check
972
1019
  ```
973
1020
 
1021
+ `registry:lint` scans package source and examples for declared registry ids
1022
+ such as signals, handlers, server functions, partials, routes, and components.
1023
+ It writes `.async/registry-manifest.json` plus a per-file cache at
1024
+ `.async/registry-lint-cache.json`, skips generated root bundles such as
1025
+ `framework.umd.min.js`, and fails only when the same registry type and id are
1026
+ declared with different normalized content. Duplicate declarations with the
1027
+ same content are reported as dedupe candidates, not errors.
1028
+
974
1029
  GitHub Pages builds through the generated `pages` job. This private repository
975
1030
  needs GitHub Pages support enabled before the generated job can deploy.
976
1031
 
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>AsyncLoader Cache</title>
6
+ <title>Async Loader Cache</title>
7
7
  </head>
8
8
  <body>
9
9
  <main async:container>
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <title>AsyncLoader Components</title>
5
+ <title>Async Loader Components</title>
6
6
  </head>
7
7
  <body>
8
8
  <main id="app"></main>
@@ -1,4 +1,4 @@
1
- import { AsyncLoader, defineComponent, html } from "../../src/index.js";
1
+ import { Loader, defineComponent, html } from "../../src/index.js";
2
2
 
3
3
  const Toggle = defineComponent(function Toggle() {
4
4
  const selected = this.signal(false);
@@ -22,5 +22,5 @@ const Toggle = defineComponent(function Toggle() {
22
22
  `;
23
23
  });
24
24
 
25
- const loader = AsyncLoader({ root: document });
25
+ const loader = Loader({ root: document });
26
26
  loader.mount(document.querySelector("#app"), Toggle);
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <title>AsyncLoader Counter</title>
5
+ <title>Async Loader Counter</title>
6
6
  </head>
7
7
  <body async:container>
8
8
  <main>
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>AsyncLoader Partials</title>
6
+ <title>Async Loader Partials</title>
7
7
  </head>
8
8
  <body>
9
9
  <main async:container>
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <title>AsyncLoader Product</title>
5
+ <title>Async Loader Product</title>
6
6
  </head>
7
7
  <body async:container>
8
8
  <main>
@@ -1,4 +1,4 @@
1
- import { AsyncLoader, createSignal, createSignalRegistry, delay } from "../../src/index.js";
1
+ import { Loader, createSignal, createSignalRegistry, delay } from "../../src/index.js";
2
2
 
3
3
  const products = {
4
4
  "sku-1": {
@@ -21,4 +21,4 @@ signals.asyncSignal("product", async function () {
21
21
  return products[id];
22
22
  });
23
23
 
24
- AsyncLoader({ root: document.body, signals }).start();
24
+ Loader({ root: document.body, signals }).start();
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>AsyncLoader CSR Router</title>
6
+ <title>Async Loader CSR Router</title>
7
7
  </head>
8
8
  <body>
9
9
  <main async:container>
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>AsyncLoader Server Call</title>
6
+ <title>Async Loader Server Call</title>
7
7
  </head>
8
8
  <body>
9
9
  <main async:container>
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>AsyncLoader SSR Activation</title>
6
+ <title>Async Loader SSR Activation</title>
7
7
  </head>
8
8
  <body>
9
9
  <main id="app" async:container></main>
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <title>AsyncLoader Streaming</title>
5
+ <title>Async Loader Streaming</title>
6
6
  </head>
7
7
  <body async:container>
8
8
  <main>