@takazudo/zfb-runtime 0.1.0-next.2
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 +8 -0
- package/LICENSE +21 -0
- package/README.md +237 -0
- package/dist/client-router/cssesc.d.ts +9 -0
- package/dist/client-router/cssesc.d.ts.map +1 -0
- package/dist/client-router/cssesc.js +95 -0
- package/dist/client-router/cssesc.js.map +1 -0
- package/dist/client-router/events.d.ts +42 -0
- package/dist/client-router/events.d.ts.map +1 -0
- package/dist/client-router/events.js +114 -0
- package/dist/client-router/events.js.map +1 -0
- package/dist/client-router/index.d.ts +9 -0
- package/dist/client-router/index.d.ts.map +1 -0
- package/dist/client-router/index.js +18 -0
- package/dist/client-router/index.js.map +1 -0
- package/dist/client-router/prefetch.d.ts +29 -0
- package/dist/client-router/prefetch.d.ts.map +1 -0
- package/dist/client-router/prefetch.js +288 -0
- package/dist/client-router/prefetch.js.map +1 -0
- package/dist/client-router/router.d.ts +17 -0
- package/dist/client-router/router.d.ts.map +1 -0
- package/dist/client-router/router.js +739 -0
- package/dist/client-router/router.js.map +1 -0
- package/dist/client-router/swap-functions.d.ts +22 -0
- package/dist/client-router/swap-functions.d.ts.map +1 -0
- package/dist/client-router/swap-functions.js +252 -0
- package/dist/client-router/swap-functions.js.map +1 -0
- package/dist/client-router/types.d.ts +11 -0
- package/dist/client-router/types.d.ts.map +1 -0
- package/dist/client-router/types.js +3 -0
- package/dist/client-router/types.js.map +1 -0
- package/dist/client-router.d.ts +36 -0
- package/dist/client-router.d.ts.map +1 -0
- package/dist/client-router.js +117 -0
- package/dist/client-router.js.map +1 -0
- package/dist/framework.d.ts +17 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/framework.js +17 -0
- package/dist/framework.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +97 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +318 -0
- package/dist/router.js.map +1 -0
- package/dist/snapshot.d.ts +38 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +16 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/view-transitions.d.ts +34 -0
- package/dist/view-transitions.d.ts.map +1 -0
- package/dist/view-transitions.js +54 -0
- package/dist/view-transitions.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,8DAA8D;AAC9D,uEAAuE;AACvE,EAAE;AACF,8DAA8D;AAC9D,EAAE;AACF,sCAAsC;AACtC,oBAAoB;AACpB,4EAA4E;AAC5E,iFAAiF;AACjF,QAAQ;AACR,EAAE;AACF,sCAAsC;AACtC,EAAE;AACF,wEAAwE;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAU/C,OAAO,EAAE,eAAe,EAA+B,MAAM,uBAAuB,CAAC;AAErF,2FAA2F;AAC3F,2CAA2C;AAC3C,OAAO,EAAE,YAAY,EAA0B,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EACL,QAAQ,EACR,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AAEnC,kCAAkC;AAClC,8EAA8E;AAC9E,4BAA4B;AAC5B,OAAO,EAAE,QAAQ,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAM7E,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,6BAA6B,EAC7B,gCAAgC,EAChC,yBAAyB,EACzB,kCAAkC,EAClC,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAC"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { FrameworkAdapter } from "./framework.js";
|
|
2
|
+
import type { ContentSnapshot } from "./snapshot.js";
|
|
3
|
+
/**
|
|
4
|
+
* Heading metadata emitted by the MDX `headings` export (T4). Cross-ref:
|
|
5
|
+
* `crates/zfb-content/src/mdx_jsx_emit.rs`. Optional on the page-module
|
|
6
|
+
* shape — non-MDX pages won't carry it.
|
|
7
|
+
*/
|
|
8
|
+
export interface PageHeading {
|
|
9
|
+
readonly depth: number;
|
|
10
|
+
readonly slug: string;
|
|
11
|
+
readonly text: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The shape every page module must export.
|
|
15
|
+
*
|
|
16
|
+
* - `default`: the JSX page component. Called with the props returned by
|
|
17
|
+
* `getStaticProps` (if exported) or the `props` from the matching
|
|
18
|
+
* `paths()` entry (for dynamic routes). The return value is fed straight
|
|
19
|
+
* to the framework adapter's `renderToString`.
|
|
20
|
+
* - `prerender`: literal `false` opts a route OUT of build-time SSG (T5
|
|
21
|
+
* contract). The page router still serves it under the embedded V8 host
|
|
22
|
+
* so dev mode behaves identically; SSG callers filter the route list before
|
|
23
|
+
* driving the renderer.
|
|
24
|
+
* - `contentType`: optional override for non-HTML routes (e.g.
|
|
25
|
+
* `application/xml` for `rss.xml.tsx`). Default is
|
|
26
|
+
* `text/html; charset=utf-8`. Cross-ref shipped #49.
|
|
27
|
+
* - `headings`: optional list emitted by MDX (T4).
|
|
28
|
+
* - `paths`: optional dynamic-route enumerator. Called at build time by
|
|
29
|
+
* the `__paths__` synthetic endpoint to produce the concrete URL list
|
|
30
|
+
* for this route template. May be async. Returns an array of
|
|
31
|
+
* `{ params, props? }` objects identical in shape to the Astro/zfb
|
|
32
|
+
* `paths()` contract.
|
|
33
|
+
* - `getStaticProps`: optional async function for static routes that need
|
|
34
|
+
* to fetch data at build/render time. Called once per request (before
|
|
35
|
+
* `default`). Must return `{ props: Record<string, unknown> }`. The
|
|
36
|
+
* returned `props` are spread into the `default` component's props.
|
|
37
|
+
*/
|
|
38
|
+
export interface PageModule {
|
|
39
|
+
readonly default: (props: Record<string, unknown>) => unknown;
|
|
40
|
+
readonly prerender?: boolean;
|
|
41
|
+
readonly contentType?: string;
|
|
42
|
+
readonly headings?: readonly PageHeading[];
|
|
43
|
+
readonly paths?: () => unknown[] | Promise<unknown[]>;
|
|
44
|
+
readonly getStaticProps?: () => Promise<{
|
|
45
|
+
props: Record<string, unknown>;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One page registered with the router.
|
|
50
|
+
*
|
|
51
|
+
* `route` is a Hono path pattern (e.g. `/`, `/blog/:slug`,
|
|
52
|
+
* `/blog/page/:page`). `module` is a thunk so the bundle can use code
|
|
53
|
+
* splitting if it wants to — today the bundler emits everything as one
|
|
54
|
+
* ESM file and the thunk simply returns the already-loaded module.
|
|
55
|
+
*/
|
|
56
|
+
export interface PageDefinition {
|
|
57
|
+
readonly route: string;
|
|
58
|
+
readonly module: () => Promise<PageModule>;
|
|
59
|
+
}
|
|
60
|
+
/** Options accepted by [`createPageRouter`]. */
|
|
61
|
+
export interface CreatePageRouterOptions {
|
|
62
|
+
/** Pages to register. Order does not affect routing — Hono dispatches by path. */
|
|
63
|
+
readonly pages: readonly PageDefinition[];
|
|
64
|
+
/**
|
|
65
|
+
* In-memory content snapshot. Embedded into the bundle by T3; the
|
|
66
|
+
* router hands it to `zfb/content` so user pages reading content via
|
|
67
|
+
* `getCollection(...)` resolve synchronously from memory.
|
|
68
|
+
*/
|
|
69
|
+
readonly contentSnapshot: ContentSnapshot;
|
|
70
|
+
/** Framework adapter pinning the SSR call. */
|
|
71
|
+
readonly framework: FrameworkAdapter;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Fetch-handler shape returned by [`createPageRouter`]. Shaped as a plain
|
|
75
|
+
* function (not a Hono `app`) so the consumer's contract is exactly
|
|
76
|
+
* "Worker-style fetch handler" with no leaked framework types.
|
|
77
|
+
*/
|
|
78
|
+
export type PageRouter = (request: Request) => Promise<Response>;
|
|
79
|
+
/**
|
|
80
|
+
* Build a page router for the SSG-first architecture (ADR-005).
|
|
81
|
+
*
|
|
82
|
+
* Side effects:
|
|
83
|
+
* 1. Registers `opts.contentSnapshot` with the `zfb/content` module so
|
|
84
|
+
* `getCollection(name)` resolves from memory. Idempotent across
|
|
85
|
+
* calls — the latest snapshot wins (matches the documented dev-mode
|
|
86
|
+
* live-reload contract).
|
|
87
|
+
* 2. Constructs an internal Hono app and registers a GET handler per
|
|
88
|
+
* `pages[i].route`. The handler imports the page module, calls
|
|
89
|
+
* `framework.renderToString(module.default({}))`, and returns the
|
|
90
|
+
* string in a `Response` with the appropriate `Content-Type`.
|
|
91
|
+
*
|
|
92
|
+
* The returned function is a plain `(request) => Promise<Response>` so a
|
|
93
|
+
* Worker entry point can `export default { fetch: createPageRouter(...) }`
|
|
94
|
+
* directly.
|
|
95
|
+
*/
|
|
96
|
+
export declare function createPageRouter(opts: CreatePageRouterOptions): PageRouter;
|
|
97
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAMrD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC3C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;CAC7E;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;CAC5C;AAED,gDAAgD;AAChD,MAAM,WAAW,uBAAuB;IACtC,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAejE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,UAAU,CA0P1E"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// `@takazudo/zfb-runtime` — Hono-based page router.
|
|
2
|
+
//
|
|
3
|
+
// `createPageRouter` is the JS-side entry point for ADR-007's SSG-first
|
|
4
|
+
// architecture. The pipeline goes:
|
|
5
|
+
//
|
|
6
|
+
// user pages/ + content/ + layouts/ + components/
|
|
7
|
+
// → esbuild bundle (T3) // single ESM file
|
|
8
|
+
// → embedded V8 host (T6) // same WinterCG surface as CF Workers
|
|
9
|
+
// → createPageRouter({ pages, contentSnapshot, framework })
|
|
10
|
+
// → (request) => Promise<Response>
|
|
11
|
+
//
|
|
12
|
+
// The bundle's Worker entry point shape is documented in the package
|
|
13
|
+
// README. The contract is intentionally minimal: one factory call returns
|
|
14
|
+
// one fetch handler. Hono is an implementation detail — callers should
|
|
15
|
+
// not depend on the Hono types leaking through. The exposed surface is
|
|
16
|
+
// the four types here plus the `createPageRouter` function.
|
|
17
|
+
//
|
|
18
|
+
// Side effect on init: registers the supplied `ContentSnapshot` with
|
|
19
|
+
// `zfb/content`'s module-level snapshot bridge so any page module
|
|
20
|
+
// importing `getCollection("...")` resolves from memory rather than
|
|
21
|
+
// touching the Node `fs` API (the Worker runtime has no `fs`).
|
|
22
|
+
//
|
|
23
|
+
// ## Synthetic `__paths__` endpoint
|
|
24
|
+
//
|
|
25
|
+
// The Rust build pipeline needs to evaluate non-literal `paths()` exports
|
|
26
|
+
// (e.g. those that `await import("@takazudo/zfb/content")` and call `getCollection`)
|
|
27
|
+
// at runtime against the running embedded host. The router exposes a
|
|
28
|
+
// synthetic internal endpoint:
|
|
29
|
+
//
|
|
30
|
+
// GET /__paths__/<percent-encoded-route-key>
|
|
31
|
+
//
|
|
32
|
+
// When a page registered at `route` has a `paths` export, the handler calls
|
|
33
|
+
// it and returns the JSON-serialized array as `application/json`. If the
|
|
34
|
+
// `paths` export is missing or throws, the response is a descriptive 500.
|
|
35
|
+
// This endpoint is only meant for the build pipeline — it is safe to leave
|
|
36
|
+
// registered in production because no user-authored route should start with
|
|
37
|
+
// `/__paths__/` (the build pipeline rejects any route that conflicts).
|
|
38
|
+
import { Hono } from "hono";
|
|
39
|
+
import { setContentSnapshot } from "@takazudo/zfb/content";
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Implementation
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
/**
|
|
44
|
+
* Default content-type when a page module does not override.
|
|
45
|
+
*
|
|
46
|
+
* Aligned with #49's per-page `contentType` convention: the default
|
|
47
|
+
* served for HTML pages is `text/html; charset=utf-8`. Tests pin this
|
|
48
|
+
* verbatim because the embedded V8 host does NOT auto-set a charset.
|
|
49
|
+
*/
|
|
50
|
+
const DEFAULT_CONTENT_TYPE = "text/html; charset=utf-8";
|
|
51
|
+
/**
|
|
52
|
+
* Build a page router for the SSG-first architecture (ADR-005).
|
|
53
|
+
*
|
|
54
|
+
* Side effects:
|
|
55
|
+
* 1. Registers `opts.contentSnapshot` with the `zfb/content` module so
|
|
56
|
+
* `getCollection(name)` resolves from memory. Idempotent across
|
|
57
|
+
* calls — the latest snapshot wins (matches the documented dev-mode
|
|
58
|
+
* live-reload contract).
|
|
59
|
+
* 2. Constructs an internal Hono app and registers a GET handler per
|
|
60
|
+
* `pages[i].route`. The handler imports the page module, calls
|
|
61
|
+
* `framework.renderToString(module.default({}))`, and returns the
|
|
62
|
+
* string in a `Response` with the appropriate `Content-Type`.
|
|
63
|
+
*
|
|
64
|
+
* The returned function is a plain `(request) => Promise<Response>` so a
|
|
65
|
+
* Worker entry point can `export default { fetch: createPageRouter(...) }`
|
|
66
|
+
* directly.
|
|
67
|
+
*/
|
|
68
|
+
export function createPageRouter(opts) {
|
|
69
|
+
setContentSnapshot(opts.contentSnapshot);
|
|
70
|
+
const app = new Hono();
|
|
71
|
+
// Build a lookup map: Hono route pattern → PageDefinition, for the
|
|
72
|
+
// `__paths__` synthetic endpoint below. The map is keyed on the `route`
|
|
73
|
+
// string exactly as the caller supplied it (e.g. "/blog/:slug").
|
|
74
|
+
const pagesByRoute = new Map();
|
|
75
|
+
for (const page of opts.pages) {
|
|
76
|
+
pagesByRoute.set(page.route, page);
|
|
77
|
+
}
|
|
78
|
+
// Sanity check: a user-authored route that happens to start with
|
|
79
|
+
// `/__paths__` (or a top-level catchall like `/:slug{.+}`) would
|
|
80
|
+
// shadow the synthetic endpoint registered below if Hono dispatched
|
|
81
|
+
// by registration order. We register `/__paths__/:routeKey{.+}` first
|
|
82
|
+
// (see directly below) but still warn on collisions so users do not
|
|
83
|
+
// ship a page that conflicts with the build pipeline's wire format.
|
|
84
|
+
for (const page of opts.pages) {
|
|
85
|
+
if (routeShadowsPathsEndpoint(page.route)) {
|
|
86
|
+
// Use console.warn so the message reaches the host's tail logs
|
|
87
|
+
// without bringing down the worker. The build pipeline's
|
|
88
|
+
// /__paths__ requests still resolve correctly because the
|
|
89
|
+
// synthetic handler is registered first.
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.warn(`[zfb-runtime] route "${page.route}" may shadow the synthetic /__paths__ endpoint; rename the page or use a more specific pattern`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// -------------------------------------------------------------------------
|
|
95
|
+
// Synthetic `/__paths__/<encoded-route-key>` endpoint.
|
|
96
|
+
//
|
|
97
|
+
// Called by the Rust build pipeline (crates/zfb/src/render_pipeline.rs)
|
|
98
|
+
// to evaluate non-literal `paths()` exports at runtime. The route key is
|
|
99
|
+
// the Hono pattern for the page (e.g. `/blog/:slug`) percent-encoded so
|
|
100
|
+
// it survives in the URL path segment. The response is a JSON array of
|
|
101
|
+
// `{ params, props? }` objects identical to the `paths()` contract.
|
|
102
|
+
//
|
|
103
|
+
// Pattern: `/__paths__/:routeKey{.+}` — the `{.+}` quantifier (Hono's
|
|
104
|
+
// regex-segment syntax) allows slashes inside the route key so
|
|
105
|
+
// `/blog/:slug` decodes correctly from `/__paths__/%2Fblog%2F%3Aslug`.
|
|
106
|
+
//
|
|
107
|
+
// IMPORTANT: this handler is registered BEFORE user routes so a
|
|
108
|
+
// user-authored top-level catchall (e.g. `/:wildcard{.+}`) cannot
|
|
109
|
+
// shadow it — Hono dispatches in registration order.
|
|
110
|
+
// -------------------------------------------------------------------------
|
|
111
|
+
app.get("/__paths__/:routeKey{.+}", async (c) => {
|
|
112
|
+
// Hono's `c.req.param("routeKey")` already URL-decodes the captured
|
|
113
|
+
// segment when it contains a `%`, so a single decode is correct
|
|
114
|
+
// here — no explicit `decodeURIComponent` (that would be a
|
|
115
|
+
// double-decode and break literal `%` characters in route keys).
|
|
116
|
+
const routeKey = c.req.param("routeKey");
|
|
117
|
+
const page = pagesByRoute.get(routeKey);
|
|
118
|
+
if (!page) {
|
|
119
|
+
return c.body(`[zfb-runtime] /__paths__: no page registered for route key "${routeKey}"`, 404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
120
|
+
}
|
|
121
|
+
const mod = await page.module();
|
|
122
|
+
if (typeof mod.paths !== "function") {
|
|
123
|
+
return c.body(`[zfb-runtime] /__paths__: page module for "${routeKey}" has no paths() export`, 404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
124
|
+
}
|
|
125
|
+
let result;
|
|
126
|
+
try {
|
|
127
|
+
result = await mod.paths();
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
131
|
+
return c.body(`[zfb-runtime] /__paths__: paths() threw for "${routeKey}": ${msg}`, 500, {
|
|
132
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return c.body(JSON.stringify(result), 200, {
|
|
136
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
for (const page of opts.pages) {
|
|
140
|
+
// `app.all` (vs `app.get`) so SSR routes whose page handler dispatches
|
|
141
|
+
// by `request.method` (e.g. POST API endpoints like
|
|
142
|
+
// `pages/api/*.tsx`) actually reach the handler. The handler is then
|
|
143
|
+
// responsible for returning a method-appropriate status (e.g. 405 for
|
|
144
|
+
// an unsupported verb). With `app.get` the inner router would 404
|
|
145
|
+
// before the handler ever ran, leaving e.g. `POST /api/foo` looking
|
|
146
|
+
// identical to a missing route.
|
|
147
|
+
app.all(page.route, async (c) => {
|
|
148
|
+
const mod = await page.module();
|
|
149
|
+
if (typeof mod.default !== "function") {
|
|
150
|
+
// Surface as a 500 with a well-known message rather than letting
|
|
151
|
+
// Hono swallow the error into a generic body. T6's embedded V8
|
|
152
|
+
// host log-tail / source-map plumbing is what eventually projects
|
|
153
|
+
// page-side errors back to the user's TSX line; until then the
|
|
154
|
+
// pinned message is the contract this layer ships.
|
|
155
|
+
return c.body(`[zfb-runtime] page module for "${page.route}" did not export a default component`, 500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
156
|
+
}
|
|
157
|
+
// For dynamic routes that export `paths()`, look up the concrete
|
|
158
|
+
// entry for this URL by matching the URL params against the
|
|
159
|
+
// paths() results. This implements the ADR-002 contract:
|
|
160
|
+
// paths() → [{ params, props? }]
|
|
161
|
+
// render(url) → find matching entry → pass { params, props } to default()
|
|
162
|
+
//
|
|
163
|
+
// For static routes (no `paths()` export, no URL params), we pass
|
|
164
|
+
// an empty object — the component signature has no required props.
|
|
165
|
+
// For dynamic routes whose URL params do not match any paths()
|
|
166
|
+
// entry, we return a 404 rather than rendering with empty props.
|
|
167
|
+
const rawUrlParams = c.req.param();
|
|
168
|
+
const urlParams = (rawUrlParams ?? {});
|
|
169
|
+
const hasDynamicParams = Object.keys(urlParams).length > 0;
|
|
170
|
+
let componentInput = {};
|
|
171
|
+
if (hasDynamicParams && typeof mod.paths === "function") {
|
|
172
|
+
let pathsResult;
|
|
173
|
+
try {
|
|
174
|
+
pathsResult = await mod.paths();
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
178
|
+
return c.body(`[zfb-runtime] paths() threw for "${page.route}": ${msg}`, 500, {
|
|
179
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (!Array.isArray(pathsResult)) {
|
|
183
|
+
return c.body(`[zfb-runtime] paths() for "${page.route}" did not return an array`, 500, {
|
|
184
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// Validate each entry's shape per-entry rather than using a bare
|
|
188
|
+
// cast — matches the strictness the Rust pipeline applies on the
|
|
189
|
+
// same wire format. Any malformed entry surfaces as a 500.
|
|
190
|
+
for (const entry of pathsResult) {
|
|
191
|
+
if (!isPathsEntry(entry)) {
|
|
192
|
+
return c.body(`[zfb-runtime] paths() for "${page.route}" returned an entry without a valid params object`, 500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Find the entry whose params match the URL params for this
|
|
196
|
+
// request. For catchall params (e.g. slug for
|
|
197
|
+
// /docs/[...slug]), Hono returns a slash-joined string (e.g.
|
|
198
|
+
// "guides/install"), so we compare against the paths() entry's
|
|
199
|
+
// params.slug joined with "/".
|
|
200
|
+
const match = pathsResult.find((entry) => {
|
|
201
|
+
return Object.entries(urlParams).every(([k, v]) => {
|
|
202
|
+
const paramVal = entry.params[k];
|
|
203
|
+
if (Array.isArray(paramVal)) {
|
|
204
|
+
// catchall: paths() emits slug as string[] but Hono
|
|
205
|
+
// provides it as a "/"-joined string
|
|
206
|
+
return paramVal.join("/") === v;
|
|
207
|
+
}
|
|
208
|
+
return String(paramVal) === v;
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
if (!match) {
|
|
212
|
+
// The URL params do not correspond to any paths() entry —
|
|
213
|
+
// this is the dev-mode equivalent of a build-time miss.
|
|
214
|
+
// Hono's `c.notFound()` returns the framework's default 404,
|
|
215
|
+
// which is cleaner than fabricating an empty-props render.
|
|
216
|
+
return c.notFound();
|
|
217
|
+
}
|
|
218
|
+
// Pass the paths() entry's props directly as component props
|
|
219
|
+
// (spread to top level, matching the Astro/zfb convention).
|
|
220
|
+
// Also include `params` so components can access URL params if
|
|
221
|
+
// needed, but individual prop keys from `props` win on collision.
|
|
222
|
+
componentInput = {
|
|
223
|
+
params: match.params,
|
|
224
|
+
...(match.props ?? {}),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
else if (!hasDynamicParams && typeof mod.getStaticProps === "function") {
|
|
228
|
+
// Static route with `getStaticProps`: call it to fetch build-time
|
|
229
|
+
// data and pass the returned props to the default component. This
|
|
230
|
+
// supports the `export async function getStaticProps()` pattern
|
|
231
|
+
// used by static pages that need to query content collections (e.g.
|
|
232
|
+
// a homepage listing all blog posts via `getCollection("blog")`).
|
|
233
|
+
let staticPropsResult;
|
|
234
|
+
try {
|
|
235
|
+
staticPropsResult = await mod.getStaticProps();
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
239
|
+
return c.body(`[zfb-runtime] getStaticProps() threw for "${page.route}": ${msg}`, 500, {
|
|
240
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (typeof staticPropsResult !== "object" ||
|
|
244
|
+
staticPropsResult === null ||
|
|
245
|
+
!("props" in staticPropsResult)) {
|
|
246
|
+
return c.body(`[zfb-runtime] getStaticProps() for "${page.route}" must return { props: {...} }`, 500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
247
|
+
}
|
|
248
|
+
componentInput = staticPropsResult.props;
|
|
249
|
+
}
|
|
250
|
+
// `await` so async page modules (e.g. API routes typed as
|
|
251
|
+
// `(): Promise<Response>`) resolve before we inspect the value.
|
|
252
|
+
// For sync pages that return a VNode/string the await is a no-op.
|
|
253
|
+
const result = await mod.default(componentInput);
|
|
254
|
+
// API route short-circuit: a page module that returns a Response
|
|
255
|
+
// directly (e.g. `pages/api/*.tsx` handlers that use Web Fetch
|
|
256
|
+
// primitives instead of returning JSX) is responsible for its own
|
|
257
|
+
// status, headers, and body — return it as-is rather than running
|
|
258
|
+
// it through the framework SSR path.
|
|
259
|
+
if (result instanceof Response) {
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
// Non-HTML routes (e.g. `sitemap.xml.tsx`, `feed.xml.tsx`) commonly
|
|
263
|
+
// return their body as a pre-serialised `string` instead of a
|
|
264
|
+
// VNode. Routing those through `framework.renderToString` would
|
|
265
|
+
// HTML-escape the angle brackets and ampersands, producing
|
|
266
|
+
// garbage XML. Pass strings through verbatim; only wrap actual
|
|
267
|
+
// VNodes.
|
|
268
|
+
const html = typeof result === "string" ? result : opts.framework.renderToString(result);
|
|
269
|
+
const contentType = mod.contentType ?? DEFAULT_CONTENT_TYPE;
|
|
270
|
+
return c.body(html, 200, { "Content-Type": contentType });
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
// Hono's `app.fetch` returns `Response | Promise<Response>`. The
|
|
274
|
+
// public router contract is unconditionally async; using an `async`
|
|
275
|
+
// wrapper (rather than `Promise.resolve(...)`) ensures any
|
|
276
|
+
// synchronous throw inside `app.fetch` is converted to a rejected
|
|
277
|
+
// promise instead of escaping the caller's `await`.
|
|
278
|
+
return async (request) => await app.fetch(request);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Type guard for one `paths()` entry. Mirrors the strictness of the
|
|
282
|
+
* Rust pipeline's `paths()` resolver — every entry must be a non-null
|
|
283
|
+
* object whose `params` is itself a non-null object.
|
|
284
|
+
*/
|
|
285
|
+
function isPathsEntry(x) {
|
|
286
|
+
if (typeof x !== "object" || x === null)
|
|
287
|
+
return false;
|
|
288
|
+
const params = x.params;
|
|
289
|
+
return typeof params === "object" && params !== null;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Heuristic check for user-authored routes that may shadow the
|
|
293
|
+
* synthetic `/__paths__/<encoded-route-key>` endpoint.
|
|
294
|
+
*
|
|
295
|
+
* Returns true when the route literal contains `/__paths__` (an
|
|
296
|
+
* obvious collision) or when its first segment is a Hono catchall
|
|
297
|
+
* (`:name{.+}`) or a file-system catchall (`[...name]`) at the root —
|
|
298
|
+
* those would match `/__paths__/...` once Hono dispatches to them.
|
|
299
|
+
*/
|
|
300
|
+
function routeShadowsPathsEndpoint(route) {
|
|
301
|
+
if (route.includes("/__paths__")) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
// Strip the leading slash and look at the first segment only —
|
|
305
|
+
// anything deeper cannot match `/__paths__` because the literal
|
|
306
|
+
// first segment differs.
|
|
307
|
+
const firstSeg = route.replace(/^\/+/, "").split("/")[0] ?? "";
|
|
308
|
+
// Hono regex-quantifier catchall: `:name{.+}`.
|
|
309
|
+
if (/^:[A-Za-z_][\w]*\{\.[+*]\}$/.test(firstSeg)) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
// File-system catchall (pre-bracket-to-hono): `[...name]`.
|
|
313
|
+
if (/^\[\.\.\.[A-Za-z_][\w]*\]$/.test(firstSeg)) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One entry in a content collection, in the shape the JS bridge sees.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `crates/zfb-content/src/content_bridge.rs::EntrySnapshot`.
|
|
5
|
+
*
|
|
6
|
+
* - `slug`: filename stem (no extension).
|
|
7
|
+
* - `frontmatter`: parsed frontmatter — `null` when the source had none.
|
|
8
|
+
* Type-erased to `unknown` here; user pages narrow via the generic on
|
|
9
|
+
* `getCollection<T>()`.
|
|
10
|
+
* - `body`: markdown body for `.md` / `.mdx` entries; empty string for
|
|
11
|
+
* `.tsx` entries (TSX has no separate markdown body).
|
|
12
|
+
* - `module_specifier`: stable specifier addressing the compiled module
|
|
13
|
+
* (`mdx://collection/slug#hash` / `tsx://collection/slug#hash`). The
|
|
14
|
+
* bridge resolver matches either the full-with-hash form or the
|
|
15
|
+
* no-hash form `mdx://collection/slug`.
|
|
16
|
+
* - `rel_path`: path relative to the collection root, normalized to
|
|
17
|
+
* forward slashes so JSON is platform-stable.
|
|
18
|
+
*/
|
|
19
|
+
export interface EntrySnapshot {
|
|
20
|
+
readonly slug: string;
|
|
21
|
+
readonly frontmatter: unknown;
|
|
22
|
+
readonly body: string;
|
|
23
|
+
readonly module_specifier: string;
|
|
24
|
+
readonly rel_path: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Point-in-time snapshot of every configured collection.
|
|
28
|
+
*
|
|
29
|
+
* Mirrors `crates/zfb-content/src/content_bridge.rs::ContentSnapshot`.
|
|
30
|
+
*
|
|
31
|
+
* Iteration order is documented as deterministic on the Rust side
|
|
32
|
+
* (collections sorted by name, entries sorted by slug). The snapshot
|
|
33
|
+
* delivered to JS preserves that order via stable JSON serialization.
|
|
34
|
+
*/
|
|
35
|
+
export interface ContentSnapshot {
|
|
36
|
+
readonly collections: Readonly<Record<string, readonly EntrySnapshot[]>>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAeA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC,CAAC,CAAC;CAC1E"}
|
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// `@takazudo/zfb-runtime/snapshot` — TypeScript mirror of the Rust
|
|
2
|
+
// `ContentSnapshot` contract.
|
|
3
|
+
//
|
|
4
|
+
// The canonical shape lives in Rust at `crates/zfb-content/src/content_bridge.rs`
|
|
5
|
+
// (see `ContentSnapshot` and `EntrySnapshot`). The build-time pipeline
|
|
6
|
+
// constructs the snapshot, serializes it to JSON, and embeds it in the
|
|
7
|
+
// Worker bundle that the embedded V8 host loads. At Worker boot the embedded value
|
|
8
|
+
// is handed to [`createPageRouter`] (re-exported from the package root),
|
|
9
|
+
// which registers it with the `zfb/content` module so user pages calling
|
|
10
|
+
// `getCollection("blog")` resolve from memory rather than from the
|
|
11
|
+
// filesystem.
|
|
12
|
+
//
|
|
13
|
+
// Keep this in sync with the Rust struct. Field names are snake_case to
|
|
14
|
+
// match the JSON serialization (`module_specifier`, `rel_path`).
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,8BAA8B;AAC9B,EAAE;AACF,kFAAkF;AAClF,uEAAuE;AACvE,uEAAuE;AACvE,mFAAmF;AACnF,yEAAyE;AACzE,yEAAyE;AACzE,mEAAmE;AACnE,cAAc;AACd,EAAE;AACF,wEAAwE;AACxE,iEAAiE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public element shape — kept structural so consumers don't infer through
|
|
3
|
+
* either framework's internal VNode type. Matches the shape `Island`
|
|
4
|
+
* returns from `@takazudo/zfb`.
|
|
5
|
+
*/
|
|
6
|
+
export type ViewTransitionsElement = {
|
|
7
|
+
readonly type: string;
|
|
8
|
+
readonly props: Readonly<Record<string, unknown>>;
|
|
9
|
+
readonly key: unknown;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* `<ViewTransitions />` — DEPRECATED: this component is now a typed
|
|
13
|
+
* no-op. Cross-document View Transitions are opted in via the
|
|
14
|
+
* `@view-transition { navigation: auto; }` CSS at-rule, NOT via this
|
|
15
|
+
* component. Add the at-rule to your top-level stylesheet (outside
|
|
16
|
+
* any `@layer` block — see https://developer.mozilla.org/en-US/docs/Web/CSS/@view-transition).
|
|
17
|
+
*
|
|
18
|
+
* The export is kept so consumers' existing `<ViewTransitions />`
|
|
19
|
+
* mounts compile unchanged.
|
|
20
|
+
*
|
|
21
|
+
* The previous implementation injected an inline router IIFE that
|
|
22
|
+
* called `event.preventDefault()` and `document.startViewTransition`
|
|
23
|
+
* around `window.location.href = url`. That pattern is INCOMPATIBLE
|
|
24
|
+
* with cross-document VT: Chromium treats the script reload as
|
|
25
|
+
* excluded from `auto`, so no `::view-transition-*` pseudo-elements
|
|
26
|
+
* ever materialize. See
|
|
27
|
+
* https://developer.chrome.com/docs/web-platform/view-transitions/cross-document.
|
|
28
|
+
*
|
|
29
|
+
* @deprecated The runtime injection is removed. Use the
|
|
30
|
+
* `@view-transition` CSS at-rule on both source and destination
|
|
31
|
+
* documents.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ViewTransitions(): readonly ViewTransitionsElement[];
|
|
34
|
+
//# sourceMappingURL=view-transitions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-transitions.d.ts","sourceRoot":"","sources":["../src/view-transitions.ts"],"names":[],"mappings":"AA6BA;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,IAAI,SAAS,sBAAsB,EAAE,CAEnE"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// `@takazudo/zfb-runtime` — `<ViewTransitions />` component (typed no-op).
|
|
2
|
+
//
|
|
3
|
+
// For SPA soft-swap navigation with view-transition animation, use `<ClientRouter />`
|
|
4
|
+
// from this package — see `./client-router.ts`.
|
|
5
|
+
//
|
|
6
|
+
// Originally mirrored Astro's `<ViewTransitions />` integration by injecting
|
|
7
|
+
// (a) a `<meta name="view-transition" content="same-origin">` opt-in tag and
|
|
8
|
+
// (b) an inline client-router IIFE that intercepted same-origin `<a>` clicks
|
|
9
|
+
// and ran navigations through `document.startViewTransition`.
|
|
10
|
+
//
|
|
11
|
+
// Both pieces have since been deleted. The browser story moved on:
|
|
12
|
+
//
|
|
13
|
+
// - The meta-tag opt-in was a temporary experiment during Chromium's
|
|
14
|
+
// cross-document VT spec development. It has been superseded by the
|
|
15
|
+
// `@view-transition { navigation: auto; }` CSS at-rule (per MDN and
|
|
16
|
+
// https://developer.chrome.com/docs/web-platform/view-transitions/cross-document)
|
|
17
|
+
// and is no longer honoured by browsers that ship cross-document VT.
|
|
18
|
+
// - The click-intercept IIFE was actively harmful: it called
|
|
19
|
+
// `event.preventDefault()` and ran `window.location.href = url` inside
|
|
20
|
+
// `document.startViewTransition`. Chromium treats that script reload as
|
|
21
|
+
// EXCLUDED from the `auto` navigation type, so no `::view-transition-*`
|
|
22
|
+
// pseudo-elements ever materialise. The "router" prevented the very
|
|
23
|
+
// feature it claimed to enable.
|
|
24
|
+
//
|
|
25
|
+
// Cross-document View Transitions are now opted in via the
|
|
26
|
+
// `@view-transition { navigation: auto; }` CSS at-rule on both the source
|
|
27
|
+
// and destination documents. This component is a typed no-op kept so that
|
|
28
|
+
// existing `<ViewTransitions />` mounts compile unchanged.
|
|
29
|
+
/**
|
|
30
|
+
* `<ViewTransitions />` — DEPRECATED: this component is now a typed
|
|
31
|
+
* no-op. Cross-document View Transitions are opted in via the
|
|
32
|
+
* `@view-transition { navigation: auto; }` CSS at-rule, NOT via this
|
|
33
|
+
* component. Add the at-rule to your top-level stylesheet (outside
|
|
34
|
+
* any `@layer` block — see https://developer.mozilla.org/en-US/docs/Web/CSS/@view-transition).
|
|
35
|
+
*
|
|
36
|
+
* The export is kept so consumers' existing `<ViewTransitions />`
|
|
37
|
+
* mounts compile unchanged.
|
|
38
|
+
*
|
|
39
|
+
* The previous implementation injected an inline router IIFE that
|
|
40
|
+
* called `event.preventDefault()` and `document.startViewTransition`
|
|
41
|
+
* around `window.location.href = url`. That pattern is INCOMPATIBLE
|
|
42
|
+
* with cross-document VT: Chromium treats the script reload as
|
|
43
|
+
* excluded from `auto`, so no `::view-transition-*` pseudo-elements
|
|
44
|
+
* ever materialize. See
|
|
45
|
+
* https://developer.chrome.com/docs/web-platform/view-transitions/cross-document.
|
|
46
|
+
*
|
|
47
|
+
* @deprecated The runtime injection is removed. Use the
|
|
48
|
+
* `@view-transition` CSS at-rule on both source and destination
|
|
49
|
+
* documents.
|
|
50
|
+
*/
|
|
51
|
+
export function ViewTransitions() {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=view-transitions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-transitions.js","sourceRoot":"","sources":["../src/view-transitions.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,sFAAsF;AACtF,gDAAgD;AAChD,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,8DAA8D;AAC9D,EAAE;AACF,mEAAmE;AACnE,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,wEAAwE;AACxE,sFAAsF;AACtF,yEAAyE;AACzE,+DAA+D;AAC/D,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,wEAAwE;AACxE,oCAAoC;AACpC,EAAE;AACF,2DAA2D;AAC3D,0EAA0E;AAC1E,0EAA0E;AAC1E,2DAA2D;AAa3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,EAAE,CAAC;AACZ,CAAC"}
|