@async/framework 0.10.2 → 0.11.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 +20 -0
- package/README.md +23 -7
- package/browser.d.ts +4 -7
- package/browser.js +17 -66
- package/browser.min.js +1 -1
- package/browser.ts +17 -66
- package/browser.umd.js +17 -66
- package/browser.umd.min.js +1 -1
- package/{server.d.ts → framework.d.ts} +4 -7
- package/framework.ts +5946 -0
- package/package.json +25 -17
- package/server.js +5945 -0
- package/examples/cache/index.html +0 -16
- package/examples/cache/main.js +0 -47
- package/examples/components/index.html +0 -11
- package/examples/components/main.js +0 -26
- package/examples/counter/index.html +0 -15
- package/examples/counter/main.js +0 -17
- package/examples/partials/index.html +0 -15
- package/examples/partials/main.js +0 -43
- package/examples/product/index.html +0 -32
- package/examples/product/main.js +0 -24
- package/examples/router/index.html +0 -18
- package/examples/router/main.js +0 -52
- package/examples/server-call/index.html +0 -21
- package/examples/server-call/main.js +0 -22
- package/examples/ssr/index.html +0 -12
- package/examples/ssr/main.js +0 -89
- package/examples/streaming/index.html +0 -16
- package/examples/streaming/main.js +0 -30
- package/src/app.js +0 -802
- package/src/async-signal.js +0 -277
- package/src/attributes.js +0 -52
- package/src/boundary-receiver.js +0 -302
- package/src/browser.js +0 -18
- package/src/cache.js +0 -193
- package/src/component.js +0 -373
- package/src/delay.js +0 -30
- package/src/elements.js +0 -63
- package/src/handlers.js +0 -219
- package/src/html.js +0 -158
- package/src/index.js +0 -20
- package/src/lazy-registry.js +0 -218
- package/src/loader.js +0 -772
- package/src/partials.js +0 -133
- package/src/registry-store.js +0 -267
- package/src/request-context.js +0 -40
- package/src/router.js +0 -617
- package/src/scheduler.js +0 -300
- package/src/server-entry.js +0 -20
- package/src/server-registry.js +0 -97
- package/src/server.js +0 -362
- package/src/signals.js +0 -592
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0 - 2026-06-17
|
|
4
|
+
|
|
5
|
+
- Removed the networked `ssr-spa` router mode and route-fragment fetching so
|
|
6
|
+
`ssr` activates server-rendered HTML and snapshots without client route
|
|
7
|
+
fetches.
|
|
8
|
+
- Changed browser navigation to render registered SPA partials locally in
|
|
9
|
+
`spa` and `csr` modes while leaving same-origin document navigation alone in
|
|
10
|
+
`ssr` and `mpa` modes.
|
|
11
|
+
- Replaced the server proxy's implicit `globalThis.fetch` default with an
|
|
12
|
+
explicit `transport` callback supplied by application code.
|
|
13
|
+
- Published only generated runtime artifacts and declarations:
|
|
14
|
+
`browser.*`, `server.js`, `framework.ts`, and `framework.d.ts`; source,
|
|
15
|
+
tests, and examples are no longer included in the package tarball.
|
|
16
|
+
- Added regression coverage for SSR snapshot activation without fetch, SPA
|
|
17
|
+
stale/error navigation behavior, explicit server proxy transports, and static
|
|
18
|
+
generated-bundle scans for implicit global fetch access.
|
|
19
|
+
- Bundle size from bundled TypeScript source: `browser.ts` 171,908 B raw /
|
|
20
|
+
32,421 B gzip -> `browser.min.js` 72,827 B raw / 21,845 B gzip
|
|
21
|
+
(-99,081 B raw, -10,576 B gzip).
|
|
22
|
+
|
|
3
23
|
## 0.10.2 - 2026-06-17
|
|
4
24
|
|
|
5
25
|
- Fixed intercepted router link, form, and popstate navigation failures so they
|
package/README.md
CHANGED
|
@@ -102,8 +102,11 @@ production:
|
|
|
102
102
|
| `browser.min.js` | ESM | Compact browser module bundle |
|
|
103
103
|
| `browser.umd.js` | UMD | Readable script-tag/CommonJS-style bundle |
|
|
104
104
|
| `browser.umd.min.js` | UMD | Compact script-tag/CommonJS-style bundle and default CDN file |
|
|
105
|
-
| `browser.ts` | Bundled TypeScript source | TS-aware runtimes and higher-layer tooling |
|
|
105
|
+
| `browser.ts` | Bundled browser TypeScript source | TS-aware runtimes and higher-layer tooling |
|
|
106
106
|
| `browser.d.ts` | Type declarations | TypeScript declarations for the browser API |
|
|
107
|
+
| `server.js` | ESM | Server-capable Node.js bundle |
|
|
108
|
+
| `framework.ts` | Bundled server-capable TypeScript source | TS-aware runtimes and higher-layer tooling |
|
|
109
|
+
| `framework.d.ts` | Type declarations | TypeScript declarations for the server-capable API |
|
|
107
110
|
|
|
108
111
|
```html
|
|
109
112
|
<main async:container>
|
|
@@ -789,8 +792,9 @@ handlers.register("addToCart", async function () {
|
|
|
789
792
|
|
|
790
793
|
### Server Calls
|
|
791
794
|
|
|
792
|
-
Server registries run locally on the server
|
|
793
|
-
|
|
795
|
+
Server registries run locally on the server. Browser proxies use an explicit
|
|
796
|
+
transport supplied by the app, so network access is opt-in. Both expose the same
|
|
797
|
+
dotted call shape.
|
|
794
798
|
|
|
795
799
|
```js
|
|
796
800
|
import {
|
|
@@ -818,6 +822,7 @@ import {
|
|
|
818
822
|
|
|
819
823
|
const server = createServerProxy({
|
|
820
824
|
endpoint: "/__async/server",
|
|
825
|
+
transport: httpTransport,
|
|
821
826
|
signals,
|
|
822
827
|
loader,
|
|
823
828
|
router
|
|
@@ -884,8 +889,7 @@ Router modes:
|
|
|
884
889
|
| --- | --- |
|
|
885
890
|
| `csr` | Client renders local partial into boundary | Client renders local partial and swaps |
|
|
886
891
|
| `spa` | Existing HTML may already contain route | Client renders local partial and swaps |
|
|
887
|
-
| `ssr` | Server
|
|
888
|
-
| `ssr-spa` | Server rendered document/route boundary | Fetch route partial, apply effects, swap |
|
|
892
|
+
| `ssr` | Server-rendered document plus snapshot activation | Browser navigates normally |
|
|
889
893
|
| `mpa` | Any document source | Browser navigates normally |
|
|
890
894
|
|
|
891
895
|
CSR startup can use an empty route boundary:
|
|
@@ -1001,12 +1005,24 @@ The returned HTML includes a route boundary plus a JSON snapshot:
|
|
|
1001
1005
|
```
|
|
1002
1006
|
|
|
1003
1007
|
Browser activation scans the existing HTML and attaches events. It does not
|
|
1004
|
-
hydrate, diff, patch, or
|
|
1008
|
+
hydrate, diff, patch, rerender, or fetch route fragments:
|
|
1009
|
+
|
|
1010
|
+
```js
|
|
1011
|
+
createApp(browserApp, {
|
|
1012
|
+
root: document
|
|
1013
|
+
}).start();
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
If browser handlers or async signals need server commands, pass a server proxy
|
|
1017
|
+
with an explicit transport:
|
|
1005
1018
|
|
|
1006
1019
|
```js
|
|
1007
1020
|
createApp(browserApp, {
|
|
1008
1021
|
root: document,
|
|
1009
|
-
server: createServerProxy({
|
|
1022
|
+
server: createServerProxy({
|
|
1023
|
+
endpoint: "/__async/server",
|
|
1024
|
+
transport: httpTransport
|
|
1025
|
+
})
|
|
1010
1026
|
}).start();
|
|
1011
1027
|
```
|
|
1012
1028
|
|
package/browser.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Browser type declarations for @async/framework/browser.
|
|
3
3
|
|
|
4
4
|
export type RuntimeTarget = "browser" | "server";
|
|
5
|
-
export type RouterMode = "csr" | "spa" | "ssr" | "
|
|
5
|
+
export type RouterMode = "csr" | "spa" | "ssr" | "mpa";
|
|
6
6
|
export type AsyncSignalStatus = "idle" | "loading" | "ready" | "error";
|
|
7
7
|
export type MaybePromise<T> = T | Promise<T>;
|
|
8
8
|
export type Cleanup = () => void;
|
|
@@ -261,6 +261,7 @@ export interface ServerContext {
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
export type ServerFunction<T = unknown> = (this: ServerContext, ...args: unknown[]) => MaybePromise<ServerResult<T>>;
|
|
264
|
+
export type ServerProxyTransport = (url: string, init: RequestInit) => MaybePromise<Response>;
|
|
264
265
|
|
|
265
266
|
export interface ServerNamespace {
|
|
266
267
|
run<T = unknown>(id: string, args?: unknown[], context?: Partial<ServerContext>): Promise<T>;
|
|
@@ -275,7 +276,7 @@ export interface ServerNamespace {
|
|
|
275
276
|
|
|
276
277
|
export interface ServerProxyOptions {
|
|
277
278
|
endpoint?: string;
|
|
278
|
-
|
|
279
|
+
transport: ServerProxyTransport;
|
|
279
280
|
signals?: SignalRegistry;
|
|
280
281
|
loader?: LoaderInstance;
|
|
281
282
|
router?: Router;
|
|
@@ -378,8 +379,6 @@ export interface RouterOptions {
|
|
|
378
379
|
server?: ServerNamespace;
|
|
379
380
|
cache?: CacheRegistry;
|
|
380
381
|
partials?: PartialRegistry;
|
|
381
|
-
fetch?: typeof fetch;
|
|
382
|
-
routeEndpoint?: string;
|
|
383
382
|
attributes?: AttributeConfig;
|
|
384
383
|
scheduler?: Scheduler;
|
|
385
384
|
}
|
|
@@ -607,8 +606,6 @@ export interface CreateAppOptions extends LoaderOptions {
|
|
|
607
606
|
routes?: RouteRegistry;
|
|
608
607
|
partials?: PartialRegistry;
|
|
609
608
|
components?: ComponentRegistry;
|
|
610
|
-
fetch?: typeof fetch;
|
|
611
|
-
routeEndpoint?: string;
|
|
612
609
|
request?: Request;
|
|
613
610
|
locals?: unknown;
|
|
614
611
|
requestContext?: RequestContextStore;
|
|
@@ -722,7 +719,7 @@ export declare function createScheduler(options?: SchedulerOptions): Scheduler;
|
|
|
722
719
|
export declare function defineRoute(partial: string, options?: Omit<RouteDefinition, "partial">): RouteDefinition;
|
|
723
720
|
export declare const route: typeof defineRoute;
|
|
724
721
|
export declare function applyServerResult(result: unknown, context?: Record<string, unknown>): Promise<unknown>;
|
|
725
|
-
export declare function createServerProxy(options
|
|
722
|
+
export declare function createServerProxy(options: ServerProxyOptions): ServerNamespace;
|
|
726
723
|
export declare function resolveServerCommandArguments(args: Array<{ type: "local"; name: string } | { type: "signal"; path: string }>, context?: Record<string, unknown>): { args: unknown[]; signalValues: Record<string, unknown>; signalPaths: string[] };
|
|
727
724
|
export declare function unwrapServerResult<T = unknown>(result: ServerResult<T>): T | ServerResult<T>;
|
|
728
725
|
export declare function computed<T = unknown>(fn: (this: { signals: SignalRegistry; id: string; server?: ServerNamespace; router?: Router; loader?: LoaderInstance; cache?: CacheRegistry; scheduler?: Scheduler }) => T): ComputedSignal<T>;
|
package/browser.js
CHANGED
|
@@ -2165,7 +2165,7 @@ const __serverModule = (() => {
|
|
|
2165
2165
|
|
|
2166
2166
|
function createServerProxy({
|
|
2167
2167
|
endpoint = "/__async/server",
|
|
2168
|
-
|
|
2168
|
+
transport,
|
|
2169
2169
|
signals,
|
|
2170
2170
|
loader,
|
|
2171
2171
|
router,
|
|
@@ -2173,8 +2173,8 @@ const __serverModule = (() => {
|
|
|
2173
2173
|
scheduler,
|
|
2174
2174
|
headers = {}
|
|
2175
2175
|
} = {}) {
|
|
2176
|
-
if (typeof
|
|
2177
|
-
throw new TypeError("createServerProxy(...) requires
|
|
2176
|
+
if (typeof transport !== "function") {
|
|
2177
|
+
throw new TypeError("createServerProxy(...) requires a transport function.");
|
|
2178
2178
|
}
|
|
2179
2179
|
|
|
2180
2180
|
const defaults = { signals, loader, router, cache, scheduler };
|
|
@@ -2189,7 +2189,7 @@ const __serverModule = (() => {
|
|
|
2189
2189
|
};
|
|
2190
2190
|
assertJsonTransportable(body);
|
|
2191
2191
|
|
|
2192
|
-
const response = await
|
|
2192
|
+
const response = await transport(joinEndpoint(endpoint, id), {
|
|
2193
2193
|
method: "POST",
|
|
2194
2194
|
headers: {
|
|
2195
2195
|
"content-type": "application/json",
|
|
@@ -3973,6 +3973,8 @@ const __routerModule = (() => {
|
|
|
3973
3973
|
|
|
3974
3974
|
const route = defineRoute;
|
|
3975
3975
|
|
|
3976
|
+
const routerModes = new Set(["csr", "spa", "ssr", "mpa"]);
|
|
3977
|
+
|
|
3976
3978
|
function createRouteRegistry(initialMap = {}, options = {}) {
|
|
3977
3979
|
const registryStore = options.registry ?? createRegistryStore();
|
|
3978
3980
|
const type = options.type ?? "route";
|
|
@@ -4068,7 +4070,7 @@ const __routerModule = (() => {
|
|
|
4068
4070
|
}
|
|
4069
4071
|
|
|
4070
4072
|
function createRouter({
|
|
4071
|
-
mode = "ssr
|
|
4073
|
+
mode = "ssr",
|
|
4072
4074
|
root,
|
|
4073
4075
|
boundary = "route",
|
|
4074
4076
|
routes = createRouteRegistry(),
|
|
@@ -4078,11 +4080,10 @@ const __routerModule = (() => {
|
|
|
4078
4080
|
server,
|
|
4079
4081
|
cache,
|
|
4080
4082
|
partials,
|
|
4081
|
-
fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
|
|
4082
|
-
routeEndpoint = "/__async/route",
|
|
4083
4083
|
attributes,
|
|
4084
4084
|
scheduler
|
|
4085
4085
|
} = {}) {
|
|
4086
|
+
assertRouterMode(mode);
|
|
4086
4087
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
4087
4088
|
const rootNode = root ?? documentRef;
|
|
4088
4089
|
const signalRegistry = signals ?? loader?.signals ?? createSignalRegistry();
|
|
@@ -4151,16 +4152,13 @@ const __routerModule = (() => {
|
|
|
4151
4152
|
|
|
4152
4153
|
prefetch(url) {
|
|
4153
4154
|
assertActive();
|
|
4154
|
-
if (mode === "
|
|
4155
|
-
return
|
|
4155
|
+
if (mode === "mpa" || mode === "ssr") {
|
|
4156
|
+
return Promise.resolve(null);
|
|
4156
4157
|
}
|
|
4157
4158
|
const matched = api.match(url);
|
|
4158
4159
|
if (matched?.route?.partial && partials?.resolve?.(matched.route.partial)) {
|
|
4159
4160
|
return partials.render(matched.route.partial, matched.params, contextFor(matched));
|
|
4160
4161
|
}
|
|
4161
|
-
if (typeof fetchImpl === "function") {
|
|
4162
|
-
return fetchRoute(url, { prefetch: true });
|
|
4163
|
-
}
|
|
4164
4162
|
return Promise.resolve(null);
|
|
4165
4163
|
},
|
|
4166
4164
|
|
|
@@ -4172,9 +4170,6 @@ const __routerModule = (() => {
|
|
|
4172
4170
|
}
|
|
4173
4171
|
|
|
4174
4172
|
const target = resolveUrl(url);
|
|
4175
|
-
if (mode === "ssr-spa") {
|
|
4176
|
-
return fetchRoutePartial(target, options);
|
|
4177
|
-
}
|
|
4178
4173
|
return renderLocalRoutePartial(target, options);
|
|
4179
4174
|
},
|
|
4180
4175
|
|
|
@@ -4262,31 +4257,6 @@ const __routerModule = (() => {
|
|
|
4262
4257
|
}
|
|
4263
4258
|
}
|
|
4264
4259
|
|
|
4265
|
-
async function fetchRoutePartial(target, options = {}) {
|
|
4266
|
-
const matched = api.match(target);
|
|
4267
|
-
const navigation = beginNavigation(target, matched);
|
|
4268
|
-
setMatchedRouterState(target, matched, { pending: true, error: null });
|
|
4269
|
-
|
|
4270
|
-
try {
|
|
4271
|
-
const result = await fetchRoute(target.href, { signal: navigation.abort });
|
|
4272
|
-
if (!isActiveNavigation(navigation)) {
|
|
4273
|
-
return null;
|
|
4274
|
-
}
|
|
4275
|
-
await applyNavigationResult(result, target, options, navigation);
|
|
4276
|
-
if (!isActiveNavigation(navigation)) {
|
|
4277
|
-
return null;
|
|
4278
|
-
}
|
|
4279
|
-
setRouterState({ pending: false, error: null });
|
|
4280
|
-
return result;
|
|
4281
|
-
} catch (error) {
|
|
4282
|
-
if (!isActiveNavigation(navigation)) {
|
|
4283
|
-
return null;
|
|
4284
|
-
}
|
|
4285
|
-
setRouterState({ pending: false, error });
|
|
4286
|
-
throw error;
|
|
4287
|
-
}
|
|
4288
|
-
}
|
|
4289
|
-
|
|
4290
4260
|
async function applyNavigationResult(result, target, options, navigation) {
|
|
4291
4261
|
if (!isActiveNavigation(navigation)) {
|
|
4292
4262
|
return;
|
|
@@ -4317,29 +4287,6 @@ const __routerModule = (() => {
|
|
|
4317
4287
|
documentRef.defaultView?.history?.pushState?.({}, "", target.href);
|
|
4318
4288
|
}
|
|
4319
4289
|
|
|
4320
|
-
async function fetchRoute(url, { prefetch = false, signal } = {}) {
|
|
4321
|
-
if (typeof fetchImpl !== "function") {
|
|
4322
|
-
throw new Error("Router navigation requires a partial registry or fetch.");
|
|
4323
|
-
}
|
|
4324
|
-
const response = await fetchImpl(`${routeEndpoint}?to=${encodeURIComponent(String(url))}`, {
|
|
4325
|
-
headers: {
|
|
4326
|
-
accept: "application/json, text/html"
|
|
4327
|
-
},
|
|
4328
|
-
signal
|
|
4329
|
-
});
|
|
4330
|
-
if (!response.ok) {
|
|
4331
|
-
throw new Error(`Route "${url}" failed with ${response.status}.`);
|
|
4332
|
-
}
|
|
4333
|
-
if (prefetch) {
|
|
4334
|
-
return response;
|
|
4335
|
-
}
|
|
4336
|
-
const type = response.headers.get("content-type") ?? "";
|
|
4337
|
-
if (type.includes("application/json")) {
|
|
4338
|
-
return response.json();
|
|
4339
|
-
}
|
|
4340
|
-
return { boundary, html: await response.text() };
|
|
4341
|
-
}
|
|
4342
|
-
|
|
4343
4290
|
function contextFor(matched, navigation) {
|
|
4344
4291
|
return {
|
|
4345
4292
|
params: matched.params,
|
|
@@ -4529,6 +4476,12 @@ const __routerModule = (() => {
|
|
|
4529
4476
|
return target.hash !== current.hash || anchor.getAttribute?.("href")?.startsWith("#") === true;
|
|
4530
4477
|
}
|
|
4531
4478
|
|
|
4479
|
+
function assertRouterMode(mode) {
|
|
4480
|
+
if (!routerModes.has(mode)) {
|
|
4481
|
+
throw new TypeError(`Unknown router mode "${mode}".`);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
|
|
4532
4485
|
function dispatchAsyncError(element, error) {
|
|
4533
4486
|
const EventCtor = element.ownerDocument?.defaultView?.CustomEvent ?? globalThis.CustomEvent;
|
|
4534
4487
|
if (typeof EventCtor !== "function") {
|
|
@@ -4927,7 +4880,7 @@ const __appModule = (() => {
|
|
|
4927
4880
|
return;
|
|
4928
4881
|
}
|
|
4929
4882
|
router = router ?? createRouter({
|
|
4930
|
-
mode: options.mode ?? "ssr
|
|
4883
|
+
mode: options.mode ?? "ssr",
|
|
4931
4884
|
root,
|
|
4932
4885
|
boundary: options.boundary ?? "route",
|
|
4933
4886
|
routes,
|
|
@@ -4938,8 +4891,6 @@ const __appModule = (() => {
|
|
|
4938
4891
|
cache: browserCache,
|
|
4939
4892
|
partials,
|
|
4940
4893
|
scheduler,
|
|
4941
|
-
fetch: options.fetch,
|
|
4942
|
-
routeEndpoint: options.routeEndpoint,
|
|
4943
4894
|
attributes
|
|
4944
4895
|
});
|
|
4945
4896
|
runtime.router = router;
|