@cmj/juice 0.0.1
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/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/cli/commands.d.ts +29 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +102 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/create.d.ts +35 -0
- package/dist/cli/create.d.ts.map +1 -0
- package/dist/cli/create.js +108 -0
- package/dist/cli/create.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +97 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/templates.d.ts +14 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +154 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/compiler/errors.d.ts +91 -0
- package/dist/compiler/errors.d.ts.map +1 -0
- package/dist/compiler/errors.js +110 -0
- package/dist/compiler/errors.js.map +1 -0
- package/dist/compiler/manifest.d.ts +39 -0
- package/dist/compiler/manifest.d.ts.map +1 -0
- package/dist/compiler/manifest.js +78 -0
- package/dist/compiler/manifest.js.map +1 -0
- package/dist/compiler/parse.d.ts +126 -0
- package/dist/compiler/parse.d.ts.map +1 -0
- package/dist/compiler/parse.js +246 -0
- package/dist/compiler/parse.js.map +1 -0
- package/dist/compiler/plugin.d.ts +43 -0
- package/dist/compiler/plugin.d.ts.map +1 -0
- package/dist/compiler/plugin.js +281 -0
- package/dist/compiler/plugin.js.map +1 -0
- package/dist/compiler/proxy.d.ts +42 -0
- package/dist/compiler/proxy.d.ts.map +1 -0
- package/dist/compiler/proxy.js +80 -0
- package/dist/compiler/proxy.js.map +1 -0
- package/dist/compiler/registry.d.ts +58 -0
- package/dist/compiler/registry.d.ts.map +1 -0
- package/dist/compiler/registry.js +79 -0
- package/dist/compiler/registry.js.map +1 -0
- package/dist/compiler/server-action-registry.d.ts +57 -0
- package/dist/compiler/server-action-registry.d.ts.map +1 -0
- package/dist/compiler/server-action-registry.js +76 -0
- package/dist/compiler/server-action-registry.js.map +1 -0
- package/dist/compiler/server-actions.d.ts +49 -0
- package/dist/compiler/server-actions.d.ts.map +1 -0
- package/dist/compiler/server-actions.js +89 -0
- package/dist/compiler/server-actions.js.map +1 -0
- package/dist/compiler/types.d.ts +188 -0
- package/dist/compiler/types.d.ts.map +1 -0
- package/dist/compiler/types.js +9 -0
- package/dist/compiler/types.js.map +1 -0
- package/dist/runtime/actions.d.ts +37 -0
- package/dist/runtime/actions.d.ts.map +1 -0
- package/dist/runtime/actions.js +167 -0
- package/dist/runtime/actions.js.map +1 -0
- package/dist/runtime/dev.d.ts +43 -0
- package/dist/runtime/dev.d.ts.map +1 -0
- package/dist/runtime/dev.js +260 -0
- package/dist/runtime/dev.js.map +1 -0
- package/dist/runtime/errors.d.ts +98 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +124 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/matcher.d.ts +44 -0
- package/dist/runtime/matcher.d.ts.map +1 -0
- package/dist/runtime/matcher.js +83 -0
- package/dist/runtime/matcher.js.map +1 -0
- package/dist/runtime/render.d.ts +25 -0
- package/dist/runtime/render.d.ts.map +1 -0
- package/dist/runtime/render.js +141 -0
- package/dist/runtime/render.js.map +1 -0
- package/dist/runtime/resolve.d.ts +24 -0
- package/dist/runtime/resolve.d.ts.map +1 -0
- package/dist/runtime/resolve.js +41 -0
- package/dist/runtime/resolve.js.map +1 -0
- package/dist/runtime/router.d.ts +74 -0
- package/dist/runtime/router.d.ts.map +1 -0
- package/dist/runtime/router.js +367 -0
- package/dist/runtime/router.js.map +1 -0
- package/dist/runtime/types.d.ts +245 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/types.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FlightManifest, RouterOptions, _RouteMatch } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The core GET render pipeline.
|
|
4
|
+
*
|
|
5
|
+
* 1. Dynamically imports the matched route's server component module.
|
|
6
|
+
* 2. Creates a React element with extracted `params` and `searchParams`.
|
|
7
|
+
* 3. Streams it via `renderToReadableStream`.
|
|
8
|
+
* 4. **Critical:** If the user component throws a `Response` object,
|
|
9
|
+
* the stream is aborted and that `Response` is returned directly.
|
|
10
|
+
*
|
|
11
|
+
* @param req - The incoming `Request`.
|
|
12
|
+
* @param match - The matched route entry and extracted URL params.
|
|
13
|
+
* @param manifest - The flight manifest for client module resolution.
|
|
14
|
+
* @param options - Router options (hooks).
|
|
15
|
+
* @returns A streaming `Response` (200) or a thrown `Response` (any status).
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
export declare function _renderPipeline(req: Request, match: _RouteMatch, manifest: FlightManifest, options: Required<Pick<RouterOptions, 'onError'>> & RouterOptions & {
|
|
20
|
+
isDev?: boolean;
|
|
21
|
+
hmrUrl?: string;
|
|
22
|
+
assetPrefix?: string;
|
|
23
|
+
clientEntry?: string;
|
|
24
|
+
}): Promise<Response>;
|
|
25
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/runtime/render.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,cAAc,EAEd,aAAa,EACb,WAAW,EACZ,MAAM,YAAY,CAAC;AA2BpB;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,GAAG,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACnJ,OAAO,CAAC,QAAQ,CAAC,CAwHnB"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// ── juice/runtime render pipeline ──────────────────────────────────
|
|
2
|
+
// Handles GET requests: React 19 renderToReadableStream with
|
|
3
|
+
// AbortController for the thrown Response catch/abort pattern.
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────
|
|
5
|
+
import { createElement } from 'react';
|
|
6
|
+
import { renderToReadableStream } from 'react-dom/server';
|
|
7
|
+
import { ModuleLoadError } from './errors.js';
|
|
8
|
+
import { _resolveModulePath } from './resolve.js';
|
|
9
|
+
import { _bustModuleCache, _generateHmrClientScript, _generateDevErrorOverlay } from './dev.js';
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the client bootstrap module paths from the manifest.
|
|
12
|
+
* These are injected as `<script>` tags by React's streaming renderer
|
|
13
|
+
* to hydrate client components on the browser.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
function _getClientBootstrap(manifest, assetPrefix = '/') {
|
|
18
|
+
const prefix = assetPrefix.endsWith('/') ? assetPrefix : `${assetPrefix}/`;
|
|
19
|
+
const chunks = new Set();
|
|
20
|
+
for (const ref of Object.values(manifest.clientModules)) {
|
|
21
|
+
for (const chunk of ref.chunks) {
|
|
22
|
+
// Prepend assetPrefix so the browser can load the chunk
|
|
23
|
+
chunks.add(`${prefix}${chunk}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return Array.from(chunks);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The core GET render pipeline.
|
|
30
|
+
*
|
|
31
|
+
* 1. Dynamically imports the matched route's server component module.
|
|
32
|
+
* 2. Creates a React element with extracted `params` and `searchParams`.
|
|
33
|
+
* 3. Streams it via `renderToReadableStream`.
|
|
34
|
+
* 4. **Critical:** If the user component throws a `Response` object,
|
|
35
|
+
* the stream is aborted and that `Response` is returned directly.
|
|
36
|
+
*
|
|
37
|
+
* @param req - The incoming `Request`.
|
|
38
|
+
* @param match - The matched route entry and extracted URL params.
|
|
39
|
+
* @param manifest - The flight manifest for client module resolution.
|
|
40
|
+
* @param options - Router options (hooks).
|
|
41
|
+
* @returns A streaming `Response` (200) or a thrown `Response` (any status).
|
|
42
|
+
*
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export async function _renderPipeline(req, match, manifest, options) {
|
|
46
|
+
const { entry, params } = match;
|
|
47
|
+
// ── 1. Dynamic import of the page module ─────────────────────
|
|
48
|
+
let importPath = _resolveModulePath(entry.moduleId, options.root);
|
|
49
|
+
if (options.isDev) {
|
|
50
|
+
importPath = _bustModuleCache(importPath);
|
|
51
|
+
}
|
|
52
|
+
let mod;
|
|
53
|
+
try {
|
|
54
|
+
mod = await import(importPath);
|
|
55
|
+
}
|
|
56
|
+
catch (cause) {
|
|
57
|
+
throw new ModuleLoadError(entry.moduleId, `route "${new URL(req.url).pathname}"` +
|
|
58
|
+
(options.root
|
|
59
|
+
? ` (resolved to "${importPath}" from root "${options.root}")`
|
|
60
|
+
: ' (no root option set — pass { root: import.meta.url } to createRouter)'), cause);
|
|
61
|
+
}
|
|
62
|
+
const exportName = entry.exportName ?? 'default';
|
|
63
|
+
const Component = mod[exportName];
|
|
64
|
+
if (typeof Component !== 'function') {
|
|
65
|
+
throw new Error(`[juice/runtime] Route module "${entry.moduleId}" does not export ` +
|
|
66
|
+
`a valid component at "${exportName}". ` +
|
|
67
|
+
`Ensure the file has a \`export default function Page()\` or ` +
|
|
68
|
+
`the exportName in flight-manifest.json points to a valid React component.`);
|
|
69
|
+
}
|
|
70
|
+
// ── 2. Build props ───────────────────────────────────────────
|
|
71
|
+
const url = new URL(req.url);
|
|
72
|
+
const searchParams = Object.fromEntries(url.searchParams.entries());
|
|
73
|
+
// ── 3. Create the React element ──────────────────────────────
|
|
74
|
+
const element = createElement(Component, { params, searchParams });
|
|
75
|
+
// ── 4. Stream with AbortController ───────────────────────────
|
|
76
|
+
const abortController = new AbortController();
|
|
77
|
+
// Track thrown Response objects from React's onError callback.
|
|
78
|
+
// React's onError fires asynchronously during streaming — we store
|
|
79
|
+
// the thrown Response and check after awaiting the stream.
|
|
80
|
+
let thrownResponse = null;
|
|
81
|
+
try {
|
|
82
|
+
const stream = await renderToReadableStream(element, {
|
|
83
|
+
signal: abortController.signal,
|
|
84
|
+
bootstrapModules: [
|
|
85
|
+
// 1. Client entry (hydrateRoot) — must load first
|
|
86
|
+
...(options.clientEntry ? [options.clientEntry] : []),
|
|
87
|
+
// 2. Client component chunks (with asset prefix)
|
|
88
|
+
..._getClientBootstrap(manifest, options.assetPrefix),
|
|
89
|
+
// 3. HMR client (dev only)
|
|
90
|
+
...(options.isDev && options.hmrUrl ? [options.hmrUrl] : []),
|
|
91
|
+
],
|
|
92
|
+
onError(error) {
|
|
93
|
+
// React calls this for errors during streaming.
|
|
94
|
+
// If it's a thrown Response, capture it and abort.
|
|
95
|
+
if (error instanceof Response) {
|
|
96
|
+
thrownResponse = error;
|
|
97
|
+
abortController.abort();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// For real errors, log but let React's error boundary handle it.
|
|
101
|
+
console.error('[juice/runtime]', error);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
// If a Response was thrown during initial render (before streaming),
|
|
105
|
+
// React would have called onError synchronously. Check immediately.
|
|
106
|
+
if (thrownResponse) {
|
|
107
|
+
abortController.abort();
|
|
108
|
+
return thrownResponse;
|
|
109
|
+
}
|
|
110
|
+
return new Response(stream, {
|
|
111
|
+
status: 200,
|
|
112
|
+
headers: {
|
|
113
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
114
|
+
'Transfer-Encoding': 'chunked',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (thrown) {
|
|
119
|
+
// ── Thrown Response: user intentionally signaled a non-200 ──
|
|
120
|
+
if (thrown instanceof Response) {
|
|
121
|
+
abortController.abort();
|
|
122
|
+
return thrown;
|
|
123
|
+
}
|
|
124
|
+
// ── Check if onError captured a Response ───────────────────
|
|
125
|
+
if (thrownResponse) {
|
|
126
|
+
abortController.abort();
|
|
127
|
+
return thrownResponse;
|
|
128
|
+
}
|
|
129
|
+
// ── Unexpected error ───────────────────────────────────────
|
|
130
|
+
abortController.abort();
|
|
131
|
+
// Dev mode: return rich error overlay
|
|
132
|
+
if (options.isDev) {
|
|
133
|
+
return new Response(_generateDevErrorOverlay(thrown, req, `route "${entry.moduleId}"`), {
|
|
134
|
+
status: 500,
|
|
135
|
+
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return options.onError(thrown, req);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/runtime/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a module ID to its full import path, relative to the
|
|
3
|
+
* consumer's root URL.
|
|
4
|
+
*
|
|
5
|
+
* When `root` is provided (e.g., `import.meta.url` of the consumer's
|
|
6
|
+
* server entry), relative paths like `'./app/routes/home.tsx'` are
|
|
7
|
+
* resolved against that root. Without it, the path is returned as-is
|
|
8
|
+
* (and will resolve relative to the runtime package — usually wrong).
|
|
9
|
+
*
|
|
10
|
+
* @param moduleId - The module path from the manifest (e.g., `'./app/routes/home.tsx'`).
|
|
11
|
+
* @param root - The consumer's root URL (e.g., `import.meta.url`).
|
|
12
|
+
* @returns The resolved module path for `import()`.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // root = 'file:///Users/dev/my-app/server.ts'
|
|
17
|
+
* _resolveModulePath('./app/routes/home.tsx', root);
|
|
18
|
+
* // → '/Users/dev/my-app/app/routes/home.tsx'
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare function _resolveModulePath(moduleId: string, root: string | undefined): string;
|
|
24
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/runtime/resolve.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,CAaR"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ── juice/runtime module resolution ───────────────────────────────
|
|
2
|
+
// Shared utility for resolving module paths against a root URL.
|
|
3
|
+
// Separated to avoid circular dependencies (router ↔ render/actions).
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a module ID to its full import path, relative to the
|
|
7
|
+
* consumer's root URL.
|
|
8
|
+
*
|
|
9
|
+
* When `root` is provided (e.g., `import.meta.url` of the consumer's
|
|
10
|
+
* server entry), relative paths like `'./app/routes/home.tsx'` are
|
|
11
|
+
* resolved against that root. Without it, the path is returned as-is
|
|
12
|
+
* (and will resolve relative to the runtime package — usually wrong).
|
|
13
|
+
*
|
|
14
|
+
* @param moduleId - The module path from the manifest (e.g., `'./app/routes/home.tsx'`).
|
|
15
|
+
* @param root - The consumer's root URL (e.g., `import.meta.url`).
|
|
16
|
+
* @returns The resolved module path for `import()`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // root = 'file:///Users/dev/my-app/server.ts'
|
|
21
|
+
* _resolveModulePath('./app/routes/home.tsx', root);
|
|
22
|
+
* // → '/Users/dev/my-app/app/routes/home.tsx'
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export function _resolveModulePath(moduleId, root) {
|
|
28
|
+
if (!root) {
|
|
29
|
+
return moduleId;
|
|
30
|
+
}
|
|
31
|
+
// new URL('./app/page.tsx', 'file:///Users/dev/my-app/server.ts')
|
|
32
|
+
// → 'file:///Users/dev/my-app/app/page.tsx'
|
|
33
|
+
try {
|
|
34
|
+
return new URL(moduleId, root).href;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// If URL construction fails (e.g., non-URL root), fall back
|
|
38
|
+
return moduleId;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/runtime/resolve.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gEAAgE;AAChE,sEAAsE;AACtE,uEAAuE;AAEvE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAgB,EAChB,IAAwB;IAExB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,kEAAkE;IAClE,4CAA4C;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;QAC5D,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { FlightManifest, RouterOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a WinterCG-compatible fetch handler for the Juice edge runtime.
|
|
4
|
+
*
|
|
5
|
+
* The returned function has the signature `(req: Request) => Promise<Response>`,
|
|
6
|
+
* which is the universal handler interface for Cloudflare Workers, Bun,
|
|
7
|
+
* Deno, and any WinterCG-compliant runtime.
|
|
8
|
+
*
|
|
9
|
+
* **Architecture:**
|
|
10
|
+
* - `GET` requests are routed to React 19's `renderToReadableStream`.
|
|
11
|
+
* - `POST` requests execute server actions resolved from the manifest.
|
|
12
|
+
* - All other methods return `405 Method Not Allowed`.
|
|
13
|
+
*
|
|
14
|
+
* **Thrown Response pattern:**
|
|
15
|
+
* User components can `throw new Response(body, { status })` to signal
|
|
16
|
+
* non-200 outcomes (404, redirect, etc.). The router catches these,
|
|
17
|
+
* aborts the React stream, and returns the thrown `Response` directly.
|
|
18
|
+
*
|
|
19
|
+
* **Hardened hooks:**
|
|
20
|
+
* User-provided hooks (`onBeforeRequest`, `onNotFound`, `onError`) are
|
|
21
|
+
* wrapped in try/catch. If a hook throws, the error is logged and a
|
|
22
|
+
* safe fallback response is returned — the server never crashes.
|
|
23
|
+
*
|
|
24
|
+
* @param manifest - The flight manifest generated by the Juice compiler.
|
|
25
|
+
* This is the sole bridge between the build step and the runtime.
|
|
26
|
+
* @param options - Optional runtime configuration (hooks, base path, timeout).
|
|
27
|
+
* @returns A `(req: Request) => Promise<Response>` fetch handler.
|
|
28
|
+
*
|
|
29
|
+
* @throws {ManifestValidationError} If the manifest is structurally invalid.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* // ── Cloudflare Workers ──────────────────────────────
|
|
34
|
+
* import manifest from './flight-manifest.json';
|
|
35
|
+
* import { createRouter } from '@cmj/juice/runtime';
|
|
36
|
+
*
|
|
37
|
+
* export default { fetch: createRouter(manifest) };
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* // ── Bun ─────────────────────────────────────────────
|
|
43
|
+
* import manifest from './flight-manifest.json';
|
|
44
|
+
* import { createRouter } from '@cmj/juice/runtime';
|
|
45
|
+
*
|
|
46
|
+
* Bun.serve({ fetch: createRouter(manifest) });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* // ── With options ────────────────────────────────────
|
|
52
|
+
* import manifest from './flight-manifest.json';
|
|
53
|
+
* import { createRouter } from '@cmj/juice/runtime';
|
|
54
|
+
*
|
|
55
|
+
* export default {
|
|
56
|
+
* fetch: createRouter(manifest, {
|
|
57
|
+
* basePath: '/app',
|
|
58
|
+
* requestTimeout: 10_000, // 10 seconds
|
|
59
|
+
* onBeforeRequest: (req) => {
|
|
60
|
+
* if (!getSession(req)) {
|
|
61
|
+
* return Response.redirect('/login', 302);
|
|
62
|
+
* }
|
|
63
|
+
* },
|
|
64
|
+
* onNotFound: () => new Response('Custom 404', { status: 404 }),
|
|
65
|
+
* onError: (err) => {
|
|
66
|
+
* reportToSentry(err);
|
|
67
|
+
* return new Response('Oops', { status: 500 });
|
|
68
|
+
* },
|
|
69
|
+
* }),
|
|
70
|
+
* };
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function createRouter(manifest: FlightManifest, options?: RouterOptions): (req: Request) => Promise<Response>;
|
|
74
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/runtime/router.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEd,MAAM,YAAY,CAAC;AAwNpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,cAAc,EACxB,OAAO,CAAC,EAAE,aAAa,GACtB,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAiErC"}
|