@cmj/juice 0.3.1 → 0.5.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/README.md CHANGED
@@ -63,6 +63,34 @@ const handler = createRouter(manifest);
63
63
  // handler: (Request) => Promise<Response>
64
64
  ```
65
65
 
66
+ ### Server Actions (Two-Argument Pattern)
67
+
68
+ Simple form actions receive `FormData` as the first argument — React 19 native. When you need more power, use `ActionContext` as the opt-in second argument:
69
+
70
+ ```ts
71
+ 'use server';
72
+ import type { ActionContext } from '@cmj/juice/runtime';
73
+
74
+ // Simple: FormData-only (React 19 native)
75
+ export async function addToCart(formData: FormData) {
76
+ const id = formData.get('productId');
77
+ }
78
+
79
+ // Power: headers, cookies, params via second arg
80
+ export async function processWebhook(body: unknown, ctx: ActionContext) {
81
+ ctx.request.headers.get('x-signature');
82
+ ctx.cookies.get('session_id');
83
+ ctx.params.id;
84
+ }
85
+ ```
86
+
87
+ Test your actions with the public `createActionContext` factory:
88
+
89
+ ```ts
90
+ import { createActionContext } from '@cmj/juice/runtime';
91
+ const ctx = createActionContext(new Request('https://example.com'));
92
+ ```
93
+
66
94
  ## How It Works
67
95
 
68
96
  ```
@@ -76,12 +104,13 @@ Source (.tsx) → Vite Plugin → flight-manifest.json → Runtime → Response
76
104
  ## Features
77
105
 
78
106
  - **React 19 RSC** — Server Components, Suspense, streaming SSR
79
- - **Server Actions** — `'use server'` with FormData support
107
+ - **Server Actions** — `'use server'` with FormData + opt-in `ActionContext` for headers, cookies, params
80
108
  - **Zero config** — One plugin call, no magic files
81
109
  - **One dependency** — Only `urlpattern-polyfill` for cross-platform routing
82
110
  - **Multi-platform** — Bun, Node.js, Cloudflare Workers, Deno
83
111
  - **Empathic errors** — "What-Why-How" error messages for fast debugging
84
112
  - **HMR** — Full hot module replacement in development
113
+ - **Testable actions** — Public `createActionContext` factory for unit testing
85
114
 
86
115
  ## License
87
116
 
@@ -0,0 +1,57 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * Callback to update the React root with a new RSC payload.
4
+ * Set by `initNavigation()`.
5
+ */
6
+ type PageSetter = (node: ReactNode) => void;
7
+ /**
8
+ * Options for `initNavigation`.
9
+ */
10
+ export interface NavigationOptions {
11
+ /**
12
+ * Whether to use View Transitions API for page transitions.
13
+ * Falls back to instant swap if the API is not available.
14
+ * @default true
15
+ */
16
+ viewTransitions?: boolean;
17
+ /**
18
+ * Enable prefetching of RSC payloads on hover/focus of `<a>` tags.
19
+ * @default false
20
+ */
21
+ prefetch?: boolean;
22
+ /**
23
+ * Custom function to call server actions via RSC protocol.
24
+ * Used by `createFromFetch` for `'use server'` invocations.
25
+ */
26
+ callServer?: (id: string, args: unknown[]) => Promise<unknown>;
27
+ }
28
+ /**
29
+ * Initialize client-side SPA navigation using the Navigation API.
30
+ *
31
+ * Call this once after hydrating your React root. Subsequent navigations
32
+ * (clicking `<a>` tags, browser back/forward) will fetch RSC payloads
33
+ * from the server and update the React tree without full page reloads.
34
+ *
35
+ * @param setPage - Callback to update the React root with new content.
36
+ * Typically wraps a `useState` setter.
37
+ * @param options - Configuration options.
38
+ *
39
+ * @returns A cleanup function that removes the navigation listener.
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * import { hydrateRoot } from 'react-dom/client';
44
+ * import { initNavigation } from '@cmj/juice/client';
45
+ *
46
+ * function App() {
47
+ * const [page, setPage] = useState<ReactNode>(initialServerContent);
48
+ * useEffect(() => initNavigation(setPage), []);
49
+ * return <>{page}</>;
50
+ * }
51
+ *
52
+ * hydrateRoot(document, <App />);
53
+ * ```
54
+ */
55
+ export declare function initNavigation(setPage: PageSetter, options?: NavigationOptions): () => void;
56
+ export {};
57
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AASA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AASxD;;;GAGG;AACH,KAAK,UAAU,GAAG,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE;AAoGD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,UAAU,EACnB,OAAO,GAAE,iBAAsB,GAC9B,MAAM,IAAI,CAmFZ"}
@@ -0,0 +1,197 @@
1
+ // ── juice/client — Navigation API bridge ──────────────────────────
2
+ // Intercepts browser navigations via the Navigation API and fetches
3
+ // RSC payloads from the server for seamless SPA transitions.
4
+ //
5
+ // ~2KB minified. Zero dependencies beyond React 19.
6
+ // Progressive enhancement: falls back to MPA if Navigation API
7
+ // is not available or JavaScript is disabled.
8
+ // ────────────────────────────────────────────────────────────────────
9
+ import { startTransition } from 'react';
10
+ import { createFromFetch } from 'react-server-dom-webpack/client.browser';
11
+ /**
12
+ * The RSC content type used for payload negotiation.
13
+ * Must match the server's `RSC_CONTENT_TYPE` in `rsc.ts`.
14
+ */
15
+ const RSC_CONTENT_TYPE = 'text/x-component';
16
+ // ── Prefetch cache ────────────────────────────────────────────────
17
+ const prefetchCache = new Map();
18
+ function prefetchRSC(url) {
19
+ if (prefetchCache.has(url))
20
+ return;
21
+ const promise = fetch(url, {
22
+ headers: { 'Accept': RSC_CONTENT_TYPE },
23
+ priority: 'low',
24
+ });
25
+ prefetchCache.set(url, promise);
26
+ // Expire after 30 seconds to avoid stale data
27
+ setTimeout(() => prefetchCache.delete(url), 30_000);
28
+ }
29
+ // ── Prefetch observer ─────────────────────────────────────────────
30
+ function setupPrefetching() {
31
+ // Prefetch on hover (desktop) and focus (keyboard nav)
32
+ document.addEventListener('pointerenter', (e) => {
33
+ const anchor = e.target.closest('a[href]');
34
+ if (!anchor)
35
+ return;
36
+ const href = anchor.getAttribute('href');
37
+ if (href && isSameOrigin(href)) {
38
+ prefetchRSC(new URL(href, location.href).href);
39
+ }
40
+ }, { capture: true, passive: true });
41
+ document.addEventListener('focusin', (e) => {
42
+ const anchor = e.target.closest('a[href]');
43
+ if (!anchor)
44
+ return;
45
+ const href = anchor.getAttribute('href');
46
+ if (href && isSameOrigin(href)) {
47
+ prefetchRSC(new URL(href, location.href).href);
48
+ }
49
+ }, { capture: true, passive: true });
50
+ // IntersectionObserver for data-prefetch links
51
+ if ('IntersectionObserver' in globalThis) {
52
+ const observer = new IntersectionObserver((entries) => {
53
+ for (const entry of entries) {
54
+ if (entry.isIntersecting) {
55
+ const href = entry.target.href;
56
+ if (href)
57
+ prefetchRSC(href);
58
+ observer.unobserve(entry.target);
59
+ }
60
+ }
61
+ }, { rootMargin: '200px' });
62
+ // Observe current and future [data-prefetch] links
63
+ for (const el of document.querySelectorAll('a[data-prefetch]')) {
64
+ observer.observe(el);
65
+ }
66
+ // MutationObserver to catch dynamically added links
67
+ new MutationObserver((mutations) => {
68
+ for (const mutation of mutations) {
69
+ for (const node of mutation.addedNodes) {
70
+ if (node instanceof HTMLAnchorElement && node.hasAttribute('data-prefetch')) {
71
+ observer.observe(node);
72
+ }
73
+ if (node instanceof HTMLElement) {
74
+ for (const el of node.querySelectorAll('a[data-prefetch]')) {
75
+ observer.observe(el);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }).observe(document.body, { childList: true, subtree: true });
81
+ }
82
+ }
83
+ // ── Helpers ───────────────────────────────────────────────────────
84
+ function isSameOrigin(href) {
85
+ try {
86
+ const url = new URL(href, location.href);
87
+ return url.origin === location.origin;
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ }
93
+ // ── View Transitions wrapper ──────────────────────────────────────
94
+ function withViewTransition(enabled, fn) {
95
+ if (enabled && 'startViewTransition' in document) {
96
+ document.startViewTransition(fn);
97
+ }
98
+ else {
99
+ fn();
100
+ }
101
+ }
102
+ // ── Main: initNavigation ──────────────────────────────────────────
103
+ /**
104
+ * Initialize client-side SPA navigation using the Navigation API.
105
+ *
106
+ * Call this once after hydrating your React root. Subsequent navigations
107
+ * (clicking `<a>` tags, browser back/forward) will fetch RSC payloads
108
+ * from the server and update the React tree without full page reloads.
109
+ *
110
+ * @param setPage - Callback to update the React root with new content.
111
+ * Typically wraps a `useState` setter.
112
+ * @param options - Configuration options.
113
+ *
114
+ * @returns A cleanup function that removes the navigation listener.
115
+ *
116
+ * @example
117
+ * ```tsx
118
+ * import { hydrateRoot } from 'react-dom/client';
119
+ * import { initNavigation } from '@cmj/juice/client';
120
+ *
121
+ * function App() {
122
+ * const [page, setPage] = useState<ReactNode>(initialServerContent);
123
+ * useEffect(() => initNavigation(setPage), []);
124
+ * return <>{page}</>;
125
+ * }
126
+ *
127
+ * hydrateRoot(document, <App />);
128
+ * ```
129
+ */
130
+ export function initNavigation(setPage, options = {}) {
131
+ const { viewTransitions = true, prefetch = false, callServer, } = options;
132
+ // Check for Navigation API support
133
+ if (!('navigation' in globalThis)) {
134
+ if (process.env.NODE_ENV === 'development') {
135
+ console.warn('[juice/client] Navigation API not available. ' +
136
+ 'Client-side navigation disabled (MPA mode).');
137
+ }
138
+ return () => { };
139
+ }
140
+ const nav = globalThis.navigation;
141
+ // Set up prefetching if enabled
142
+ if (prefetch) {
143
+ setupPrefetching();
144
+ }
145
+ // ── Navigation interceptor ────────────────────────────────────
146
+ const handler = (event) => {
147
+ // Only intercept navigations we can handle
148
+ if (!event.canIntercept)
149
+ return;
150
+ // Skip hash-only navigations
151
+ if (event.hashChange)
152
+ return;
153
+ // Skip downloads
154
+ if (event.downloadRequest)
155
+ return;
156
+ // Skip form submissions (handled by server actions)
157
+ if (event.formData)
158
+ return;
159
+ // Only handle same-origin navigations
160
+ const destinationUrl = new URL(event.destination.url);
161
+ if (destinationUrl.origin !== location.origin)
162
+ return;
163
+ // Intercept the navigation
164
+ event.intercept({
165
+ // Enable automatic scroll restoration
166
+ scroll: 'after-transition',
167
+ async handler() {
168
+ const url = event.destination.url;
169
+ // Use prefetched response if available
170
+ const fetchPromise = prefetchCache.has(url)
171
+ ? prefetchCache.get(url)
172
+ : fetch(url, {
173
+ headers: { 'Accept': RSC_CONTENT_TYPE },
174
+ signal: event.signal,
175
+ });
176
+ // Clear the prefetch cache entry
177
+ prefetchCache.delete(url);
178
+ // Decode the RSC payload
179
+ const rscPayload = createFromFetch(fetchPromise, {
180
+ callServer,
181
+ });
182
+ // Update the React tree with View Transitions
183
+ withViewTransition(viewTransitions, () => {
184
+ startTransition(() => {
185
+ setPage(rscPayload);
186
+ });
187
+ });
188
+ },
189
+ });
190
+ };
191
+ nav.addEventListener('navigate', handler);
192
+ // Return cleanup function
193
+ return () => {
194
+ nav.removeEventListener('navigate', handler);
195
+ };
196
+ }
197
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { initNavigation, type NavigationOptions } from './client.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ // ── juice/client barrel export ────────────────────────────────────
2
+ export { initNavigation } from './client.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAA0B,MAAM,aAAa,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { FlightManifest, RouterOptions } from './types.js';
1
+ import type { FlightManifest, RouterOptions, ParsedBody, ActionContext, _CompiledRoute } from './types.js';
2
2
  /**
3
3
  * Extracts the server action ID from the request.
4
4
  *
@@ -12,20 +12,73 @@ import type { FlightManifest, RouterOptions } from './types.js';
12
12
  * @internal
13
13
  */
14
14
  export declare function _extractActionId(req: Request): Promise<string | null>;
15
+ /**
16
+ * Parses the request body based on Content-Type and returns a
17
+ * discriminated `ParsedBody` union.
18
+ *
19
+ * - `multipart/form-data` → `{ kind: 'form', data: FormData }`
20
+ * - `application/x-www-form-urlencoded` → `{ kind: 'form', data: FormData }`
21
+ * - `application/json` → `{ kind: 'json', data: unknown }`
22
+ * - Everything else → `{ kind: 'text', data: string }`
23
+ *
24
+ * The `kind` field enables runtime narrowing without casts:
25
+ * ```ts
26
+ * if (parsed.kind === 'form') parsed.data.get('name'); // FormData ✅
27
+ * if (parsed.kind === 'json') parsed.data; // unknown ✅
28
+ * if (parsed.kind === 'text') parsed.data.length; // string ✅
29
+ * ```
30
+ *
31
+ * @param req - The incoming POST `Request`.
32
+ * @returns The parsed body with discriminator.
33
+ *
34
+ * @internal
35
+ */
36
+ export declare function _parseBody(req: Request): Promise<ParsedBody>;
37
+ /**
38
+ * Parses cookies from the `Cookie` header into a `ReadonlyMap`.
39
+ *
40
+ * Zero dependencies. Handles edge cases:
41
+ * - Empty header → empty map
42
+ * - Malformed pairs (no `=`) → skipped
43
+ * - Values containing `=` → preserved
44
+ * - Leading/trailing whitespace → trimmed
45
+ *
46
+ * @param req - The incoming `Request`.
47
+ * @returns A readonly map of cookie name → value.
48
+ *
49
+ * @internal
50
+ */
51
+ export declare function _parseCookies(req: Request): ReadonlyMap<string, string>;
52
+ /**
53
+ * Constructs an `ActionContext` from the raw request.
54
+ *
55
+ * This is a **public API** — use it in tests to create mock contexts:
56
+ * ```ts
57
+ * import { createActionContext } from '@cmj/juice/runtime';
58
+ * const ctx = createActionContext(new Request('https://example.com'));
59
+ * ```
60
+ *
61
+ * The `cookies` property is lazy — only parsed on first access via
62
+ * a getter. This keeps the fast path (no cookie access) allocation-free.
63
+ */
64
+ export declare function createActionContext(req: Request, params?: Readonly<Record<string, string>>): ActionContext;
15
65
  /**
16
66
  * The POST server action pipeline.
17
67
  *
18
68
  * 1. Extracts the action ID from the request.
19
69
  * 2. Looks up the action in the manifest.
20
70
  * 3. Dynamically imports the action module.
21
- * 4. Parses the request body.
22
- * 5. Executes the action function.
23
- * 6. Returns the result as a JSON response, or passes through
71
+ * 4. Parses the request body into a `ParsedBody`.
72
+ * 5. Constructs an `ActionContext` (request, cookies, params).
73
+ * 6. Calls the action with **two arguments**: `(body, ctx)`
74
+ * preserving React 19 `<form>` compatibility.
75
+ * 7. Returns the result as a JSON response, or passes through
24
76
  * a thrown `Response` directly.
25
77
  *
26
- * @param req - The incoming POST `Request`.
27
- * @param manifest - The flight manifest for action resolution.
28
- * @param options - Router options (hooks).
78
+ * @param req - The incoming POST `Request`.
79
+ * @param manifest - The flight manifest for action resolution.
80
+ * @param options - Router options (hooks).
81
+ * @param compiledRoutes - Pre-compiled routes for URL→params extraction.
29
82
  * @returns A `Response` with the action result or error.
30
83
  *
31
84
  * @internal
@@ -33,5 +86,5 @@ export declare function _extractActionId(req: Request): Promise<string | null>;
33
86
  export declare function _serverActionPipeline(req: Request, manifest: FlightManifest, options: Required<Pick<RouterOptions, 'onError'>> & RouterOptions & {
34
87
  isDev?: boolean;
35
88
  hmrUrl?: string;
36
- }): Promise<Response>;
89
+ }, compiledRoutes?: readonly _CompiledRoute[]): Promise<Response>;
37
90
  //# sourceMappingURL=actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/runtime/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhE;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBxB;AAgCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,OAAO,EACZ,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,CAAA;CAAE,GACvG,OAAO,CAAC,QAAQ,CAAC,CAoHnB"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/runtime/actions.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAK3G;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAelE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAavE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAC5C,aAAa,CAkBf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,OAAO,EACZ,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,CAAA;CAAE,EACxG,cAAc,CAAC,EAAE,SAAS,cAAc,EAAE,GACzC,OAAO,CAAC,QAAQ,CAAC,CAoInB"}
@@ -5,6 +5,7 @@
5
5
  import { ActionNotFoundError, ModuleLoadError } from './errors.js';
6
6
  import { _resolveModulePath } from './resolve.js';
7
7
  import { _bustModuleCache } from './dev.js';
8
+ import { _matchRoute } from './matcher.js';
8
9
  /**
9
10
  * Extracts the server action ID from the request.
10
11
  *
@@ -36,28 +37,96 @@ export async function _extractActionId(req) {
36
37
  return null;
37
38
  }
38
39
  /**
39
- * Parses the request body based on Content-Type.
40
+ * Parses the request body based on Content-Type and returns a
41
+ * discriminated `ParsedBody` union.
40
42
  *
41
- * - `multipart/form-data` → `FormData`
42
- * - `application/x-www-form-urlencoded` → `FormData`
43
- * - `application/json` → parsed JSON object
44
- * - Everything else → raw text
43
+ * - `multipart/form-data` → `{ kind: 'form', data: FormData }`
44
+ * - `application/x-www-form-urlencoded` → `{ kind: 'form', data: FormData }`
45
+ * - `application/json` → `{ kind: 'json', data: unknown }`
46
+ * - Everything else → `{ kind: 'text', data: string }`
47
+ *
48
+ * The `kind` field enables runtime narrowing without casts:
49
+ * ```ts
50
+ * if (parsed.kind === 'form') parsed.data.get('name'); // FormData ✅
51
+ * if (parsed.kind === 'json') parsed.data; // unknown ✅
52
+ * if (parsed.kind === 'text') parsed.data.length; // string ✅
53
+ * ```
45
54
  *
46
55
  * @param req - The incoming POST `Request`.
47
- * @returns The parsed body.
56
+ * @returns The parsed body with discriminator.
48
57
  *
49
58
  * @internal
50
59
  */
51
- async function _parseBody(req) {
60
+ export async function _parseBody(req) {
52
61
  const contentType = req.headers.get('content-type') ?? '';
53
62
  if (contentType.includes('multipart/form-data') ||
54
63
  contentType.includes('application/x-www-form-urlencoded')) {
55
- return req.formData();
64
+ return { kind: 'form', data: await req.formData() };
56
65
  }
57
66
  if (contentType.includes('application/json')) {
58
- return req.json();
67
+ return { kind: 'json', data: await req.json() };
68
+ }
69
+ return { kind: 'text', data: await req.text() };
70
+ }
71
+ /**
72
+ * Parses cookies from the `Cookie` header into a `ReadonlyMap`.
73
+ *
74
+ * Zero dependencies. Handles edge cases:
75
+ * - Empty header → empty map
76
+ * - Malformed pairs (no `=`) → skipped
77
+ * - Values containing `=` → preserved
78
+ * - Leading/trailing whitespace → trimmed
79
+ *
80
+ * @param req - The incoming `Request`.
81
+ * @returns A readonly map of cookie name → value.
82
+ *
83
+ * @internal
84
+ */
85
+ export function _parseCookies(req) {
86
+ const header = req.headers.get('cookie') ?? '';
87
+ if (!header)
88
+ return new Map();
89
+ const entries = [];
90
+ for (const pair of header.split(';')) {
91
+ const eqIndex = pair.indexOf('=');
92
+ if (eqIndex === -1)
93
+ continue;
94
+ const key = pair.slice(0, eqIndex).trim();
95
+ const value = pair.slice(eqIndex + 1).trim();
96
+ if (key)
97
+ entries.push([key, value]);
59
98
  }
60
- return req.text();
99
+ return new Map(entries);
100
+ }
101
+ /**
102
+ * Constructs an `ActionContext` from the raw request.
103
+ *
104
+ * This is a **public API** — use it in tests to create mock contexts:
105
+ * ```ts
106
+ * import { createActionContext } from '@cmj/juice/runtime';
107
+ * const ctx = createActionContext(new Request('https://example.com'));
108
+ * ```
109
+ *
110
+ * The `cookies` property is lazy — only parsed on first access via
111
+ * a getter. This keeps the fast path (no cookie access) allocation-free.
112
+ */
113
+ export function createActionContext(req, params = {}) {
114
+ const url = new URL(req.url);
115
+ // Lazy cookie parsing via getter
116
+ let _cookies;
117
+ return Object.create(null, {
118
+ request: { value: req, enumerable: true },
119
+ url: { value: url, enumerable: true },
120
+ params: { value: params, enumerable: true },
121
+ cookies: {
122
+ get() {
123
+ if (!_cookies)
124
+ _cookies = _parseCookies(req);
125
+ return _cookies;
126
+ },
127
+ enumerable: true,
128
+ },
129
+ });
61
130
  }
62
131
  /**
63
132
  * The POST server action pipeline.
@@ -65,19 +134,22 @@ async function _parseBody(req) {
65
134
  * 1. Extracts the action ID from the request.
66
135
  * 2. Looks up the action in the manifest.
67
136
  * 3. Dynamically imports the action module.
68
- * 4. Parses the request body.
69
- * 5. Executes the action function.
70
- * 6. Returns the result as a JSON response, or passes through
137
+ * 4. Parses the request body into a `ParsedBody`.
138
+ * 5. Constructs an `ActionContext` (request, cookies, params).
139
+ * 6. Calls the action with **two arguments**: `(body, ctx)`
140
+ * preserving React 19 `<form>` compatibility.
141
+ * 7. Returns the result as a JSON response, or passes through
71
142
  * a thrown `Response` directly.
72
143
  *
73
- * @param req - The incoming POST `Request`.
74
- * @param manifest - The flight manifest for action resolution.
75
- * @param options - Router options (hooks).
144
+ * @param req - The incoming POST `Request`.
145
+ * @param manifest - The flight manifest for action resolution.
146
+ * @param options - Router options (hooks).
147
+ * @param compiledRoutes - Pre-compiled routes for URL→params extraction.
76
148
  * @returns A `Response` with the action result or error.
77
149
  *
78
150
  * @internal
79
151
  */
80
- export async function _serverActionPipeline(req, manifest, options) {
152
+ export async function _serverActionPipeline(req, manifest, options, compiledRoutes) {
81
153
  // ── 1. Extract action ID ─────────────────────────────────────
82
154
  const actionId = await _extractActionId(req);
83
155
  if (!actionId) {
@@ -92,7 +164,12 @@ export async function _serverActionPipeline(req, manifest, options) {
92
164
  });
93
165
  }
94
166
  // ── 2. Lookup in manifest ────────────────────────────────
95
- const actionRef = manifest.serverActions[actionId];
167
+ // SECURITY: Use Object.hasOwn() to prevent prototype pollution.
168
+ // Without this, action IDs like "constructor", "__proto__", "toString"
169
+ // would resolve to Object.prototype methods instead of returning 404.
170
+ const actionRef = Object.hasOwn(manifest.serverActions, actionId)
171
+ ? manifest.serverActions[actionId]
172
+ : undefined;
96
173
  if (!actionRef) {
97
174
  // Return a clean 404 JSON response instead of throwing.
98
175
  // Throwing would leak stack traces in production.
@@ -127,11 +204,20 @@ export async function _serverActionPipeline(req, manifest, options) {
127
204
  `"${actionRef.moduleId}" but export "${actionRef.exportName}" ` +
128
205
  `is not a function. Check your 'use server' exports.`);
129
206
  }
130
- // ── 4. Parse body ──────────────────────────────────────────
131
- const body = await _parseBody(req);
132
- // ── 5. Execute ─────────────────────────────────────────────
133
- const result = await actionFn(body);
134
- // ── 6. Return ──────────────────────────────────────────────
207
+ // ── 4. Parse body into discriminated union ─────────────────
208
+ const parsed = await _parseBody(req);
209
+ // ── 5. Construct ActionContext with route-matched params ───
210
+ // Match the POST URL against compiled routes to extract params
211
+ // (e.g., /product/42 → { id: '42' }). Falls back to {} if no match.
212
+ const routeMatch = compiledRoutes ? _matchRoute(req.url, compiledRoutes) : null;
213
+ const ctx = createActionContext(req, routeMatch?.params ?? {});
214
+ // ── 6. Execute with two arguments: (body, ctx) ────────────
215
+ // First arg: the parsed body (FormData, JSON, or text)
216
+ // → React 19 <form action={fn}> compatibility preserved
217
+ // Second arg: ActionContext (request, cookies, params)
218
+ // → opt-in power when the developer needs it
219
+ const result = await actionFn(parsed.data, ctx);
220
+ // ── 7. Return ──────────────────────────────────────────────
135
221
  // If the action returns a Response directly, pass it through.
136
222
  if (result instanceof Response) {
137
223
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/runtime/actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/runtime/actions.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
1
  export { createRouter } from './router.js';
2
- export type { FlightManifest, RouterOptions } from './types.js';
2
+ export { createActionContext } from './actions.js';
3
+ export { _isRSCRequest, _buildModuleMap, RSC_CONTENT_TYPE } from './rsc.js';
4
+ export type { FlightManifest, RouterOptions, ActionContext, ParsedBody } from './types.js';
3
5
  //# sourceMappingURL=index.d.ts.map