@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,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for the Juice Vite plugin compiler.
|
|
3
|
+
*
|
|
4
|
+
* @module types
|
|
5
|
+
* @internal These types are consumed internally by the compiler pipeline.
|
|
6
|
+
* Only {@link JuicePluginOptions} is part of the public API surface.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Configuration options for the Juice Vite plugin.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* v1 is zero-config. This interface exists as the extension point for future
|
|
13
|
+
* additive options, ensuring backward compatibility.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import juice from '@cmj/juice/plugin';
|
|
18
|
+
* // Zero-config (default)
|
|
19
|
+
* juice();
|
|
20
|
+
* // With options (future)
|
|
21
|
+
* juice({ manifestFileName: 'custom-manifest.json' });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface JuicePluginOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Custom filename for the Flight manifest.
|
|
27
|
+
* @defaultValue `'flight-manifest.json'`
|
|
28
|
+
*/
|
|
29
|
+
readonly manifestFileName?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Route map: URL patterns → server component modules.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* In a future version, routes will be auto-discovered from the filesystem.
|
|
35
|
+
* For v1, the consumer passes them explicitly.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* juice({
|
|
40
|
+
* routes: {
|
|
41
|
+
* '/': { moduleId: './app/routes/home.tsx' },
|
|
42
|
+
* '/product/:id': { moduleId: './app/routes/product.tsx' },
|
|
43
|
+
* },
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
readonly routes?: Readonly<Record<string, {
|
|
48
|
+
moduleId: string;
|
|
49
|
+
exportName?: string;
|
|
50
|
+
}>>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Represents a single export extracted from a `'use client'` module.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
export interface ExportRecord {
|
|
58
|
+
/** The exported name visible to importers (e.g., `'default'`, `'Badge'`). */
|
|
59
|
+
readonly exportedName: string;
|
|
60
|
+
/**
|
|
61
|
+
* The local binding name in the source module.
|
|
62
|
+
* May differ from `exportedName` for re-exports (e.g., `export { Foo as Bar }`).
|
|
63
|
+
*/
|
|
64
|
+
readonly localName: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Information extracted from a confirmed `'use client'` module.
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
export interface ClientModuleInfo {
|
|
72
|
+
/** Root-relative module specifier (e.g., `'components/Counter.tsx'`). */
|
|
73
|
+
readonly moduleSpecifier: string;
|
|
74
|
+
/** The absolute file-system path to the source module. */
|
|
75
|
+
readonly absolutePath: string;
|
|
76
|
+
/** All exports found in the module. */
|
|
77
|
+
readonly exports: readonly ExportRecord[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Information extracted from a confirmed `'use server'` module.
|
|
81
|
+
*
|
|
82
|
+
* @internal
|
|
83
|
+
*/
|
|
84
|
+
export interface ServerActionInfo {
|
|
85
|
+
/** Root-relative module specifier (e.g., `'actions/auth.ts'`). */
|
|
86
|
+
readonly moduleSpecifier: string;
|
|
87
|
+
/** The absolute file-system path to the source module. */
|
|
88
|
+
readonly absolutePath: string;
|
|
89
|
+
/** All exported server action functions. */
|
|
90
|
+
readonly exports: readonly ExportRecord[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A single export entry within the Flight manifest.
|
|
94
|
+
*
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
export interface ManifestExportEntry {
|
|
98
|
+
/** Globally unique ID: `moduleSpecifier#exportName`. */
|
|
99
|
+
readonly id: string;
|
|
100
|
+
/** The export name (e.g., `'default'`, `'Badge'`). */
|
|
101
|
+
readonly name: string;
|
|
102
|
+
/** Chunk files required to load this export. */
|
|
103
|
+
readonly chunks: readonly string[];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A module entry within the Flight manifest.
|
|
107
|
+
*
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
export interface ManifestModuleEntry {
|
|
111
|
+
/** All chunk files associated with this module. */
|
|
112
|
+
readonly chunks: readonly string[];
|
|
113
|
+
/** Per-export chunk mappings. */
|
|
114
|
+
readonly exports: Readonly<Record<string, ManifestExportEntry>>;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A server action entry within the Flight manifest.
|
|
118
|
+
*
|
|
119
|
+
* Field names are aligned with the runtime's `ServerActionRef` contract
|
|
120
|
+
* so that `flight-manifest.json` can be consumed directly by `createRouter()`.
|
|
121
|
+
*
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
export interface ManifestServerActionEntry {
|
|
125
|
+
/** Globally unique action ID: `moduleSpecifier#exportName`. */
|
|
126
|
+
readonly id: string;
|
|
127
|
+
/** The export name (e.g., `'submitForm'`, `'default'`). */
|
|
128
|
+
readonly exportName: string;
|
|
129
|
+
/** Root-relative module specifier containing this action. */
|
|
130
|
+
readonly moduleId: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Route entry mapping a URL pattern to its server component module.
|
|
134
|
+
*
|
|
135
|
+
* Aligned with the runtime's `RouteEntry` contract.
|
|
136
|
+
*
|
|
137
|
+
* @internal
|
|
138
|
+
*/
|
|
139
|
+
export interface ManifestRouteEntry {
|
|
140
|
+
/** Root-relative module specifier (e.g., `'./app/routes/home.tsx'`). */
|
|
141
|
+
readonly moduleId: string;
|
|
142
|
+
/**
|
|
143
|
+
* Named export to use as the page component.
|
|
144
|
+
* @default 'default'
|
|
145
|
+
*/
|
|
146
|
+
readonly exportName?: string;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The complete Flight manifest schema emitted as `flight-manifest.json`.
|
|
150
|
+
*
|
|
151
|
+
* This is the **sole bridge** between the compiler and the runtime.
|
|
152
|
+
* All field names are aligned with the runtime's `FlightManifest` type
|
|
153
|
+
* so that the output JSON can be passed directly to `createRouter()`.
|
|
154
|
+
*
|
|
155
|
+
* @see {@link https://juice.dev/schemas/flight-manifest.v1.json}
|
|
156
|
+
*/
|
|
157
|
+
export interface FlightManifest {
|
|
158
|
+
/** JSON Schema reference for tooling support. */
|
|
159
|
+
readonly $schema: string;
|
|
160
|
+
/** Schema version number. Always `1` for this release. */
|
|
161
|
+
readonly version: 1;
|
|
162
|
+
/** Maps URL patterns to their server component modules. */
|
|
163
|
+
readonly routes: Readonly<Record<string, ManifestRouteEntry>>;
|
|
164
|
+
/** Maps module specifiers to their client-side chunk metadata. */
|
|
165
|
+
readonly clientModules: Readonly<Record<string, ManifestModuleEntry>>;
|
|
166
|
+
/** Reserved for SSR-specific chunk resolution. Empty in v1. */
|
|
167
|
+
readonly ssrModules: Readonly<Record<string, ManifestModuleEntry>>;
|
|
168
|
+
/** Maps server action IDs to their module and export info. */
|
|
169
|
+
readonly serverActions: Readonly<Record<string, ManifestServerActionEntry>>;
|
|
170
|
+
/**
|
|
171
|
+
* Build-time metadata injected by the Vite plugin.
|
|
172
|
+
* The runtime reads these as defaults for `RouterOptions`, enabling
|
|
173
|
+
* zero-config `createRouter(manifest)`.
|
|
174
|
+
*/
|
|
175
|
+
readonly _meta?: {
|
|
176
|
+
/** Absolute file URL of the Vite project root. */
|
|
177
|
+
readonly root?: string;
|
|
178
|
+
/** Build mode: `'development'` or `'production'`. */
|
|
179
|
+
readonly mode?: 'development' | 'production';
|
|
180
|
+
/** Vite HMR client URL (only in dev). */
|
|
181
|
+
readonly hmrUrl?: string;
|
|
182
|
+
/** Public URL prefix for client assets. */
|
|
183
|
+
readonly assetPrefix?: string;
|
|
184
|
+
/** Client entry script that calls `hydrateRoot()`. */
|
|
185
|
+
readonly clientEntry?: string;
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/compiler/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core type definitions for the Juice Vite plugin compiler.
|
|
3
|
+
*
|
|
4
|
+
* @module types
|
|
5
|
+
* @internal These types are consumed internally by the compiler pipeline.
|
|
6
|
+
* Only {@link JuicePluginOptions} is part of the public API surface.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/compiler/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { FlightManifest, RouterOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts the server action ID from the request.
|
|
4
|
+
*
|
|
5
|
+
* Resolution order:
|
|
6
|
+
* 1. `x-juice-action-id` header (set by React Flight client)
|
|
7
|
+
* 2. `$ACTION_ID` hidden form field (progressive enhancement)
|
|
8
|
+
*
|
|
9
|
+
* @param req - The incoming POST `Request`.
|
|
10
|
+
* @returns The action ID string, or `null` if not found.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function _extractActionId(req: Request): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* The POST server action pipeline.
|
|
17
|
+
*
|
|
18
|
+
* 1. Extracts the action ID from the request.
|
|
19
|
+
* 2. Looks up the action in the manifest.
|
|
20
|
+
* 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
|
|
24
|
+
* a thrown `Response` directly.
|
|
25
|
+
*
|
|
26
|
+
* @param req - The incoming POST `Request`.
|
|
27
|
+
* @param manifest - The flight manifest for action resolution.
|
|
28
|
+
* @param options - Router options (hooks).
|
|
29
|
+
* @returns A `Response` with the action result or error.
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export declare function _serverActionPipeline(req: Request, manifest: FlightManifest, options: Required<Pick<RouterOptions, 'onError'>> & RouterOptions & {
|
|
34
|
+
isDev?: boolean;
|
|
35
|
+
hmrUrl?: string;
|
|
36
|
+
}): Promise<Response>;
|
|
37
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// ── juice/runtime server actions ───────────────────────────────────
|
|
2
|
+
// Handles POST requests: extracts the action ID, resolves the handler
|
|
3
|
+
// from the manifest, parses the request body, and executes the action.
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────
|
|
5
|
+
import { ActionNotFoundError, ModuleLoadError } from './errors.js';
|
|
6
|
+
import { _resolveModulePath } from './resolve.js';
|
|
7
|
+
import { _bustModuleCache } from './dev.js';
|
|
8
|
+
/**
|
|
9
|
+
* Extracts the server action ID from the request.
|
|
10
|
+
*
|
|
11
|
+
* Resolution order:
|
|
12
|
+
* 1. `x-juice-action-id` header (set by React Flight client)
|
|
13
|
+
* 2. `$ACTION_ID` hidden form field (progressive enhancement)
|
|
14
|
+
*
|
|
15
|
+
* @param req - The incoming POST `Request`.
|
|
16
|
+
* @returns The action ID string, or `null` if not found.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
export async function _extractActionId(req) {
|
|
21
|
+
// Priority 1: Header (set by React Flight client runtime)
|
|
22
|
+
const headerActionId = req.headers.get('x-juice-action-id');
|
|
23
|
+
if (headerActionId)
|
|
24
|
+
return headerActionId;
|
|
25
|
+
// Priority 2: Hidden form field (progressive enhancement / no-JS)
|
|
26
|
+
const contentType = req.headers.get('content-type') ?? '';
|
|
27
|
+
if (contentType.includes('multipart/form-data') ||
|
|
28
|
+
contentType.includes('application/x-www-form-urlencoded')) {
|
|
29
|
+
// Clone to avoid consuming the body — the action handler may need it.
|
|
30
|
+
const formData = await req.clone().formData();
|
|
31
|
+
const actionId = formData.get('$ACTION_ID');
|
|
32
|
+
if (typeof actionId === 'string' && actionId.length > 0) {
|
|
33
|
+
return actionId;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Parses the request body based on Content-Type.
|
|
40
|
+
*
|
|
41
|
+
* - `multipart/form-data` → `FormData`
|
|
42
|
+
* - `application/x-www-form-urlencoded` → `FormData`
|
|
43
|
+
* - `application/json` → parsed JSON object
|
|
44
|
+
* - Everything else → raw text
|
|
45
|
+
*
|
|
46
|
+
* @param req - The incoming POST `Request`.
|
|
47
|
+
* @returns The parsed body.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
async function _parseBody(req) {
|
|
52
|
+
const contentType = req.headers.get('content-type') ?? '';
|
|
53
|
+
if (contentType.includes('multipart/form-data') ||
|
|
54
|
+
contentType.includes('application/x-www-form-urlencoded')) {
|
|
55
|
+
return req.formData();
|
|
56
|
+
}
|
|
57
|
+
if (contentType.includes('application/json')) {
|
|
58
|
+
return req.json();
|
|
59
|
+
}
|
|
60
|
+
return req.text();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The POST server action pipeline.
|
|
64
|
+
*
|
|
65
|
+
* 1. Extracts the action ID from the request.
|
|
66
|
+
* 2. Looks up the action in the manifest.
|
|
67
|
+
* 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
|
|
71
|
+
* a thrown `Response` directly.
|
|
72
|
+
*
|
|
73
|
+
* @param req - The incoming POST `Request`.
|
|
74
|
+
* @param manifest - The flight manifest for action resolution.
|
|
75
|
+
* @param options - Router options (hooks).
|
|
76
|
+
* @returns A `Response` with the action result or error.
|
|
77
|
+
*
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
export async function _serverActionPipeline(req, manifest, options) {
|
|
81
|
+
// ── 1. Extract action ID ─────────────────────────────────────
|
|
82
|
+
const actionId = await _extractActionId(req);
|
|
83
|
+
if (!actionId) {
|
|
84
|
+
return new Response(JSON.stringify({
|
|
85
|
+
error: 'Bad Request',
|
|
86
|
+
message: 'Missing server action ID. ' +
|
|
87
|
+
'Ensure your form includes a "$ACTION_ID" field or the request ' +
|
|
88
|
+
'sends an "x-juice-action-id" header.',
|
|
89
|
+
}), {
|
|
90
|
+
status: 400,
|
|
91
|
+
headers: { 'Content-Type': 'application/json' },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// ── 2. Lookup in manifest ────────────────────────────────
|
|
95
|
+
const actionRef = manifest.serverActions[actionId];
|
|
96
|
+
if (!actionRef) {
|
|
97
|
+
// Return a clean 404 JSON response instead of throwing.
|
|
98
|
+
// Throwing would leak stack traces in production.
|
|
99
|
+
return new Response(JSON.stringify({
|
|
100
|
+
error: 'Not Found',
|
|
101
|
+
message: `Server Action "${actionId}" not found in the manifest. ` +
|
|
102
|
+
`Ensure the function is exported with 'use server' and the manifest is up to date.`,
|
|
103
|
+
}), {
|
|
104
|
+
status: 404,
|
|
105
|
+
headers: { 'Content-Type': 'application/json' },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
// ── 3. Dynamic import ────────────────────────────────────
|
|
110
|
+
let importPath = _resolveModulePath(actionRef.moduleId, options.root);
|
|
111
|
+
if (options.isDev) {
|
|
112
|
+
importPath = _bustModuleCache(importPath);
|
|
113
|
+
}
|
|
114
|
+
let mod;
|
|
115
|
+
try {
|
|
116
|
+
mod = await import(importPath);
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
throw new ModuleLoadError(actionRef.moduleId, `Server Action "${actionId}"` +
|
|
120
|
+
(options.root
|
|
121
|
+
? ` (resolved to "${importPath}" from root "${options.root}")`
|
|
122
|
+
: ' (no root option set — pass { root: import.meta.url } to createRouter)'), cause);
|
|
123
|
+
}
|
|
124
|
+
const actionFn = mod[actionRef.exportName];
|
|
125
|
+
if (typeof actionFn !== 'function') {
|
|
126
|
+
throw new Error(`[juice/runtime] Server Action "${actionId}" resolved to module ` +
|
|
127
|
+
`"${actionRef.moduleId}" but export "${actionRef.exportName}" ` +
|
|
128
|
+
`is not a function. Check your 'use server' exports.`);
|
|
129
|
+
}
|
|
130
|
+
// ── 4. Parse body ──────────────────────────────────────────
|
|
131
|
+
const body = await _parseBody(req);
|
|
132
|
+
// ── 5. Execute ─────────────────────────────────────────────
|
|
133
|
+
const result = await actionFn(body);
|
|
134
|
+
// ── 6. Return ──────────────────────────────────────────────
|
|
135
|
+
// If the action returns a Response directly, pass it through.
|
|
136
|
+
if (result instanceof Response) {
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
return new Response(JSON.stringify(result ?? null), {
|
|
140
|
+
status: 200,
|
|
141
|
+
headers: { 'Content-Type': 'application/json' },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (thrown) {
|
|
145
|
+
// ── Thrown Response: user intentionally signaled a non-200 ──
|
|
146
|
+
if (thrown instanceof Response) {
|
|
147
|
+
return thrown;
|
|
148
|
+
}
|
|
149
|
+
// ── Unexpected error ───────────────────────────────────────
|
|
150
|
+
// Dev mode: return rich error JSON with stack/cause details
|
|
151
|
+
if (options.isDev) {
|
|
152
|
+
const err = thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
153
|
+
return new Response(JSON.stringify({
|
|
154
|
+
error: err.name,
|
|
155
|
+
message: err.message,
|
|
156
|
+
stack: err.stack,
|
|
157
|
+
actionId,
|
|
158
|
+
cause: err.cause instanceof Error ? err.cause.message : undefined,
|
|
159
|
+
}, null, 2), {
|
|
160
|
+
status: 500,
|
|
161
|
+
headers: { 'Content-Type': 'application/json' },
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return options.onError(thrown, req);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/runtime/actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a rich HTML error overlay for development mode.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Dark theme with syntax-highlighted stack trace
|
|
6
|
+
* - Source file + line number parsed from Error stack
|
|
7
|
+
* - Request URL, method, and matched route context
|
|
8
|
+
* - Auto-refresh via HMR EventSource (if hmrUrl provided)
|
|
9
|
+
*
|
|
10
|
+
* @param error - The caught error (may be Error or unknown).
|
|
11
|
+
* @param req - The incoming request (for URL/method display).
|
|
12
|
+
* @param context - Optional context string (e.g., route path, action ID).
|
|
13
|
+
* @returns A full HTML string for the error overlay response.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare function _generateDevErrorOverlay(error: unknown, req: Request, context?: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Generates the Vite HMR client `<script>` tag for SSR injection.
|
|
20
|
+
*
|
|
21
|
+
* @param hmrUrl - The HMR client URL (default: `'/@vite/client'`).
|
|
22
|
+
* @returns A `<script type="module">` string.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export declare function _generateHmrClientScript(hmrUrl?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Appends a cache-busting timestamp query parameter to a module ID.
|
|
29
|
+
*
|
|
30
|
+
* In development mode, this forces the JS runtime to re-evaluate
|
|
31
|
+
* the module on every `import()` call, ensuring edits are picked up
|
|
32
|
+
* immediately without a server restart.
|
|
33
|
+
*
|
|
34
|
+
* In production, this function should NOT be called — modules are
|
|
35
|
+
* cached normally for performance.
|
|
36
|
+
*
|
|
37
|
+
* @param moduleId - The original module path (e.g., `'./app/routes/home.tsx'`).
|
|
38
|
+
* @returns The module path with `?t={timestamp}` appended.
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
export declare function _bustModuleCache(moduleId: string): string;
|
|
43
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/runtime/dev.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAgKR;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD"}
|