@zenithbuild/cli 0.7.11 → 0.7.12
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 +10 -1
- package/dist/adapters/adapter-netlify-static.d.ts +2 -5
- package/dist/adapters/adapter-netlify.d.ts +2 -5
- package/dist/adapters/adapter-netlify.js +22 -5
- package/dist/adapters/adapter-types.d.ts +32 -13
- package/dist/adapters/adapter-types.js +0 -59
- package/dist/adapters/adapter-vercel-static.d.ts +2 -5
- package/dist/adapters/adapter-vercel.d.ts +2 -5
- package/dist/adapters/adapter-vercel.js +21 -6
- package/dist/adapters/copy-hosted-page-runtime.d.ts +2 -1
- package/dist/adapters/copy-hosted-page-runtime.js +68 -3
- package/dist/adapters/resolve-adapter.d.ts +6 -4
- package/dist/build/expression-rewrites.d.ts +3 -1
- package/dist/build/expression-rewrites.js +14 -2
- package/dist/build/page-component-loop.d.ts +1 -0
- package/dist/build/page-component-loop.js +66 -6
- package/dist/build/page-ir-normalization.js +7 -0
- package/dist/build/page-loop-state.d.ts +2 -1
- package/dist/build/page-loop-state.js +9 -2
- package/dist/build/page-loop.js +10 -1
- package/dist/build/scoped-expression-context.d.ts +5 -0
- package/dist/build/scoped-expression-context.js +133 -0
- package/dist/build/type-declarations.d.ts +2 -1
- package/dist/build/type-declarations.js +31 -1
- package/dist/build-output-manifest.d.ts +10 -6
- package/dist/build-output-manifest.js +4 -1
- package/dist/build.js +11 -2
- package/dist/component-instance-ir.js +1 -0
- package/dist/component-occurrences.d.ts +9 -0
- package/dist/component-occurrences.js +18 -0
- package/dist/config-plugins.d.ts +12 -0
- package/dist/config-plugins.js +100 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +56 -5
- package/dist/dev-server/request-handler.js +46 -4
- package/dist/dev-server.js +92 -4
- package/dist/global-middleware-runtime-source.d.ts +15 -0
- package/dist/global-middleware-runtime-source.js +62 -0
- package/dist/global-middleware.d.ts +13 -0
- package/dist/global-middleware.js +252 -0
- package/dist/manifest.d.ts +9 -1
- package/dist/manifest.js +66 -26
- package/dist/preview/request-handler.js +78 -5
- package/dist/preview/server-runner.d.ts +7 -2
- package/dist/preview/server-runner.js +19 -6
- package/dist/preview/server-script-runner-template.js +97 -29
- package/dist/route-classification.d.ts +2 -1
- package/dist/route-classification.js +6 -2
- package/dist/scoped-server-data/analyze-owner-file.d.ts +3 -0
- package/dist/scoped-server-data/analyze-owner-file.js +149 -0
- package/dist/scoped-server-data/diagnostics.d.ts +18 -0
- package/dist/scoped-server-data/diagnostics.js +32 -0
- package/dist/scoped-server-data/lowering.d.ts +27 -0
- package/dist/scoped-server-data/lowering.js +242 -0
- package/dist/scoped-server-data/manifest-integration.d.ts +4 -0
- package/dist/scoped-server-data/manifest-integration.js +125 -0
- package/dist/scoped-server-data/owner-scanner.d.ts +6 -0
- package/dist/scoped-server-data/owner-scanner.js +55 -0
- package/dist/scoped-server-data/parse-owner-server-block.d.ts +12 -0
- package/dist/scoped-server-data/parse-owner-server-block.js +35 -0
- package/dist/scoped-server-data/runtime.d.ts +24 -0
- package/dist/scoped-server-data/runtime.js +121 -0
- package/dist/scoped-server-data/serialization-set.d.ts +2 -0
- package/dist/scoped-server-data/serialization-set.js +52 -0
- package/dist/scoped-server-data/static-props.d.ts +12 -0
- package/dist/scoped-server-data/static-props.js +307 -0
- package/dist/scoped-server-data/type-declarations.d.ts +10 -0
- package/dist/scoped-server-data/type-declarations.js +368 -0
- package/dist/scoped-server-data/types.d.ts +74 -0
- package/dist/scoped-server-data/types.js +1 -0
- package/dist/server-contract/auth-control-flow.d.ts +1 -0
- package/dist/server-contract/auth-control-flow.js +10 -0
- package/dist/server-contract/resolve.d.ts +19 -0
- package/dist/server-contract/resolve.js +85 -13
- package/dist/server-contract/resolved-envelope.d.ts +9 -0
- package/dist/server-contract/resolved-envelope.js +14 -0
- package/dist/server-contract/stage.js +1 -10
- package/dist/server-module-output.d.ts +9 -0
- package/dist/server-module-output.js +250 -0
- package/dist/server-output.d.ts +7 -1
- package/dist/server-output.js +138 -179
- package/dist/server-runtime/matched-route-pipeline.d.ts +1 -0
- package/dist/server-runtime/matched-route-pipeline.js +1 -0
- package/dist/server-runtime/node-server.js +21 -1
- package/dist/server-runtime/route-render.d.ts +12 -3
- package/dist/server-runtime/route-render.js +67 -13
- package/package.json +3 -3
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
+
const PACKAGE_REQUIRE = createRequire(import.meta.url);
|
|
6
|
+
const STATIC_MIDDLEWARE_TARGETS = new Set([
|
|
7
|
+
'static',
|
|
8
|
+
'static-export',
|
|
9
|
+
'vercel-static',
|
|
10
|
+
'netlify-static'
|
|
11
|
+
]);
|
|
12
|
+
function toPosixRelative(from, to) {
|
|
13
|
+
const relativePath = relative(from, to).replaceAll('\\', '/');
|
|
14
|
+
return relativePath || '.';
|
|
15
|
+
}
|
|
16
|
+
function middlewareError(sourceFile, message) {
|
|
17
|
+
return new Error(`[Zenith:Middleware] Invalid global middleware in ${sourceFile}: ${message}`);
|
|
18
|
+
}
|
|
19
|
+
function resolveTypeScriptApi(projectRoot) {
|
|
20
|
+
try {
|
|
21
|
+
const projectRequire = createRequire(join(projectRoot, '__zenith_middleware_parser__.js'));
|
|
22
|
+
return projectRequire('typescript');
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
try {
|
|
26
|
+
return PACKAGE_REQUIRE('typescript');
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
throw new Error('[Zenith:Middleware] Global middleware validation requires the `typescript` package to be installed.');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function hasModifier(ts, node, kind) {
|
|
34
|
+
return Boolean(node?.modifiers?.some((modifier) => modifier.kind === kind));
|
|
35
|
+
}
|
|
36
|
+
function isAllowedTypeOnlyNamedExport(ts, node) {
|
|
37
|
+
if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
|
|
38
|
+
return hasModifier(ts, node, ts.SyntaxKind.ExportKeyword)
|
|
39
|
+
&& !hasModifier(ts, node, ts.SyntaxKind.DefaultKeyword);
|
|
40
|
+
}
|
|
41
|
+
if (ts.isExportDeclaration(node)) {
|
|
42
|
+
if (node.isTypeOnly) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
const elements = node.exportClause && ts.isNamedExports(node.exportClause)
|
|
46
|
+
? node.exportClause.elements
|
|
47
|
+
: [];
|
|
48
|
+
return elements.length > 0 && elements.every((specifier) => specifier.isTypeOnly === true);
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function unwrapExpression(ts, expression) {
|
|
53
|
+
let current = expression;
|
|
54
|
+
while (current && ts.isParenthesizedExpression(current)) {
|
|
55
|
+
current = current.expression;
|
|
56
|
+
}
|
|
57
|
+
return current;
|
|
58
|
+
}
|
|
59
|
+
function isFunctionLikeDefault(ts, expression) {
|
|
60
|
+
const unwrapped = unwrapExpression(ts, expression);
|
|
61
|
+
return ts.isFunctionExpression(unwrapped) || ts.isArrowFunction(unwrapped)
|
|
62
|
+
? unwrapped
|
|
63
|
+
: null;
|
|
64
|
+
}
|
|
65
|
+
function propertyAccessPath(ts, node) {
|
|
66
|
+
const parts = [];
|
|
67
|
+
let current = node;
|
|
68
|
+
while (current && ts.isPropertyAccessExpression(current)) {
|
|
69
|
+
parts.unshift(current.name.text);
|
|
70
|
+
current = current.expression;
|
|
71
|
+
}
|
|
72
|
+
if (current && ts.isIdentifier(current)) {
|
|
73
|
+
parts.unshift(current.text);
|
|
74
|
+
}
|
|
75
|
+
return parts;
|
|
76
|
+
}
|
|
77
|
+
function hasCommonJsExport(ts, node) {
|
|
78
|
+
let found = false;
|
|
79
|
+
function visit(current) {
|
|
80
|
+
if (found) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (ts.isBinaryExpression(current)
|
|
84
|
+
&& current.operatorToken.kind === ts.SyntaxKind.EqualsToken
|
|
85
|
+
&& ts.isPropertyAccessExpression(current.left)) {
|
|
86
|
+
const parts = propertyAccessPath(ts, current.left);
|
|
87
|
+
if (parts[0] === 'module' && parts[1] === 'exports') {
|
|
88
|
+
found = true;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (parts[0] === 'exports') {
|
|
92
|
+
found = true;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
ts.forEachChild(current, visit);
|
|
97
|
+
}
|
|
98
|
+
ts.forEachChild(node, visit);
|
|
99
|
+
return found;
|
|
100
|
+
}
|
|
101
|
+
function assertTwoNonRestParams(fn, sourceFile) {
|
|
102
|
+
const params = Array.isArray(fn?.parameters) ? fn.parameters : [];
|
|
103
|
+
const hasRest = params.some((param) => param.dotDotDotToken);
|
|
104
|
+
if (params.length !== 2 || hasRest) {
|
|
105
|
+
throw middlewareError(sourceFile, 'default function must accept exactly two arguments: ctx and next.');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function validateGlobalMiddlewareSource(source, sourceFile, projectRoot = process.cwd()) {
|
|
109
|
+
const ts = resolveTypeScriptApi(projectRoot);
|
|
110
|
+
const parsed = ts.createSourceFile(sourceFile, String(source || ''), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
111
|
+
if (parsed.parseDiagnostics.length > 0) {
|
|
112
|
+
throw middlewareError(sourceFile, 'unable to parse middleware module.');
|
|
113
|
+
}
|
|
114
|
+
if (hasCommonJsExport(ts, parsed)) {
|
|
115
|
+
throw middlewareError(sourceFile, 'CommonJS middleware exports are not supported. Use `export default function middleware(ctx, next) { ... }`.');
|
|
116
|
+
}
|
|
117
|
+
let defaultExportCount = 0;
|
|
118
|
+
let defaultFunction = null;
|
|
119
|
+
let defaultExportWasNonFunction = false;
|
|
120
|
+
let hasNamedRuntimeExport = false;
|
|
121
|
+
for (const statement of parsed.statements) {
|
|
122
|
+
if (ts.isExportDeclaration(statement)) {
|
|
123
|
+
if (!isAllowedTypeOnlyNamedExport(ts, statement)) {
|
|
124
|
+
hasNamedRuntimeExport = true;
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (ts.isExportAssignment(statement)) {
|
|
129
|
+
if (statement.isExportEquals) {
|
|
130
|
+
throw middlewareError(sourceFile, 'CommonJS middleware exports are not supported. Use `export default function middleware(ctx, next) { ... }`.');
|
|
131
|
+
}
|
|
132
|
+
defaultExportCount += 1;
|
|
133
|
+
const fn = isFunctionLikeDefault(ts, statement.expression);
|
|
134
|
+
if (fn) {
|
|
135
|
+
defaultFunction = fn;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
defaultExportWasNonFunction = true;
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const hasExport = hasModifier(ts, statement, ts.SyntaxKind.ExportKeyword);
|
|
143
|
+
if (!hasExport) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const hasDefault = hasModifier(ts, statement, ts.SyntaxKind.DefaultKeyword);
|
|
147
|
+
if (hasDefault) {
|
|
148
|
+
defaultExportCount += 1;
|
|
149
|
+
if (ts.isFunctionDeclaration(statement)) {
|
|
150
|
+
defaultFunction = statement;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
defaultExportWasNonFunction = true;
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (!isAllowedTypeOnlyNamedExport(ts, statement)) {
|
|
158
|
+
hasNamedRuntimeExport = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (hasNamedRuntimeExport) {
|
|
162
|
+
throw middlewareError(sourceFile, 'named runtime exports are not supported. Export only `default function middleware(ctx, next)`.');
|
|
163
|
+
}
|
|
164
|
+
if (defaultExportCount !== 1) {
|
|
165
|
+
throw middlewareError(sourceFile, 'expected exactly one default export function.');
|
|
166
|
+
}
|
|
167
|
+
if (!defaultFunction || defaultExportWasNonFunction) {
|
|
168
|
+
throw middlewareError(sourceFile, 'default export must be a function. Use `export default function middleware(ctx, next) { ... }`.');
|
|
169
|
+
}
|
|
170
|
+
assertTwoNonRestParams(defaultFunction, sourceFile);
|
|
171
|
+
}
|
|
172
|
+
async function findNestedMiddlewareFiles(dir, projectRoot) {
|
|
173
|
+
const matches = [];
|
|
174
|
+
let entries;
|
|
175
|
+
try {
|
|
176
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return matches;
|
|
180
|
+
}
|
|
181
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
const fullPath = join(dir, entry.name);
|
|
184
|
+
if (entry.isDirectory()) {
|
|
185
|
+
if (entry.name === 'middleware' && existsSync(join(fullPath, 'index.ts'))) {
|
|
186
|
+
matches.push(join(fullPath, 'index.ts'));
|
|
187
|
+
}
|
|
188
|
+
matches.push(...await findNestedMiddlewareFiles(fullPath, projectRoot));
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (entry.isFile() && entry.name === 'middleware.ts') {
|
|
192
|
+
matches.push(fullPath);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return matches.sort((left, right) => (toPosixRelative(projectRoot, left).localeCompare(toPosixRelative(projectRoot, right))));
|
|
196
|
+
}
|
|
197
|
+
function createMetadata(sourceFile) {
|
|
198
|
+
return { source_file: sourceFile };
|
|
199
|
+
}
|
|
200
|
+
export function normalizeGlobalMiddlewareMetadata(globalMiddleware) {
|
|
201
|
+
const sourceFile = typeof globalMiddleware?.source_file === 'string'
|
|
202
|
+
? globalMiddleware.source_file
|
|
203
|
+
: typeof globalMiddleware?.sourceFile === 'string'
|
|
204
|
+
? globalMiddleware.sourceFile
|
|
205
|
+
: null;
|
|
206
|
+
return sourceFile ? createMetadata(sourceFile) : null;
|
|
207
|
+
}
|
|
208
|
+
export function assertGlobalMiddlewareTargetSupported(target, globalMiddleware) {
|
|
209
|
+
if (!globalMiddleware || !STATIC_MIDDLEWARE_TARGETS.has(target)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
throw new Error(`[Zenith:Middleware] target "${target}" cannot use global middleware. ` +
|
|
213
|
+
'Global middleware requires a server-capable target ("node", "vercel", or "netlify"). ' +
|
|
214
|
+
`File: ${globalMiddleware.sourceFile}.`);
|
|
215
|
+
}
|
|
216
|
+
export async function resolveGlobalMiddleware({ projectRoot, pagesDir, target } = {}) {
|
|
217
|
+
const resolvedProjectRoot = resolve(projectRoot || process.cwd());
|
|
218
|
+
const resolvedPagesDir = resolve(resolvedProjectRoot, pagesDir || 'pages');
|
|
219
|
+
const middlewareRoot = dirname(resolvedPagesDir);
|
|
220
|
+
const rootCandidates = [
|
|
221
|
+
join(middlewareRoot, 'middleware.ts'),
|
|
222
|
+
join(middlewareRoot, 'middleware', 'index.ts')
|
|
223
|
+
].filter((candidate) => existsSync(candidate));
|
|
224
|
+
if (rootCandidates.length > 1) {
|
|
225
|
+
throw new Error(`[Zenith:Middleware] Multiple global middleware files found in "${middlewareRoot}". ` +
|
|
226
|
+
'Keep exactly one of: middleware.ts, middleware/index.ts.');
|
|
227
|
+
}
|
|
228
|
+
const nestedMatches = await findNestedMiddlewareFiles(resolvedPagesDir, resolvedProjectRoot);
|
|
229
|
+
if (nestedMatches.length > 0) {
|
|
230
|
+
const relativePath = toPosixRelative(resolvedProjectRoot, nestedMatches[0]);
|
|
231
|
+
const middlewareRootRelative = toPosixRelative(resolvedProjectRoot, middlewareRoot);
|
|
232
|
+
const targetPath = middlewareRootRelative === '.'
|
|
233
|
+
? 'middleware.ts'
|
|
234
|
+
: `${middlewareRootRelative}/middleware.ts`;
|
|
235
|
+
throw new Error('[Zenith:Middleware] Nested middleware files are not supported in V1. ' +
|
|
236
|
+
`Move "${relativePath}" to "${targetPath}" or remove it.`);
|
|
237
|
+
}
|
|
238
|
+
if (rootCandidates.length === 0) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const sourcePath = rootCandidates[0];
|
|
242
|
+
const sourceFile = toPosixRelative(resolvedProjectRoot, sourcePath);
|
|
243
|
+
const globalMiddleware = {
|
|
244
|
+
sourcePath,
|
|
245
|
+
sourceFile,
|
|
246
|
+
root: middlewareRoot,
|
|
247
|
+
metadata: createMetadata(sourceFile)
|
|
248
|
+
};
|
|
249
|
+
assertGlobalMiddlewareTargetSupported(target, globalMiddleware);
|
|
250
|
+
validateGlobalMiddlewareSource(await readFile(sourcePath, 'utf8'), sourceFile, resolvedProjectRoot);
|
|
251
|
+
return globalMiddleware;
|
|
252
|
+
}
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export function analyzeRouteScopedServerMetadata(options: import("./scoped-server-data/types.js").AnalyzeRouteScopedServerMetadataOptions): import("./scoped-server-data/types.js").AnalyzeRouteScopedServerMetadataResult;
|
|
2
|
+
export function assertNoScopedServerBuildErrors(diagnostics: import("./scoped-server-data/types.js").ScopedServerDiagnostic[], contextFile: string): void;
|
|
1
3
|
/**
|
|
2
4
|
* @typedef {{
|
|
3
5
|
* path: string,
|
|
@@ -11,6 +13,8 @@
|
|
|
11
13
|
* has_guard?: boolean,
|
|
12
14
|
* has_load?: boolean,
|
|
13
15
|
* has_action?: boolean,
|
|
16
|
+
* has_scoped_server_data?: boolean,
|
|
17
|
+
* scoped_server_data?: import('./scoped-server-data/types.js').ManifestScopedServerDataEntry[],
|
|
14
18
|
* export_paths?: string[]
|
|
15
19
|
* }} ManifestEntry
|
|
16
20
|
*/
|
|
@@ -19,11 +23,13 @@
|
|
|
19
23
|
*
|
|
20
24
|
* @param {string} pagesDir - Absolute path to /pages directory
|
|
21
25
|
* @param {string} [extension='.zen'] - File extension to scan for
|
|
22
|
-
* @param {{ compilerOpts?: object }} [options]
|
|
26
|
+
* @param {{ compilerOpts?: object, srcDir?: string, registry?: Map<string, string> }} [options]
|
|
23
27
|
* @returns {Promise<ManifestEntry[]>}
|
|
24
28
|
*/
|
|
25
29
|
export function generateManifest(pagesDir: string, extension?: string, options?: {
|
|
26
30
|
compilerOpts?: object;
|
|
31
|
+
srcDir?: string;
|
|
32
|
+
registry?: Map<string, string>;
|
|
27
33
|
}): Promise<ManifestEntry[]>;
|
|
28
34
|
/**
|
|
29
35
|
* Generate a JavaScript module string from manifest entries.
|
|
@@ -45,5 +51,7 @@ export type ManifestEntry = {
|
|
|
45
51
|
has_guard?: boolean;
|
|
46
52
|
has_load?: boolean;
|
|
47
53
|
has_action?: boolean;
|
|
54
|
+
has_scoped_server_data?: boolean;
|
|
55
|
+
scoped_server_data?: import("./scoped-server-data/types.js").ManifestScopedServerDataEntry[];
|
|
48
56
|
export_paths?: string[];
|
|
49
57
|
};
|
package/dist/manifest.js
CHANGED
|
@@ -1,27 +1,41 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
// File-based manifest engine.
|
|
5
|
-
//
|
|
6
|
-
// Scans a /pages directory and produces a deterministic RouteManifest.
|
|
7
|
-
//
|
|
8
|
-
// Rules:
|
|
9
|
-
// - index.zen → parent directory path
|
|
10
|
-
// - [param].zen → :param dynamic segment
|
|
11
|
-
// - [...slug].zen → *slug catch-all segment (must be terminal, 1+ segments;
|
|
12
|
-
// root '/*slug' may match '/' in router matcher)
|
|
13
|
-
// - [[...slug]].zen → *slug? optional catch-all segment (must be terminal, 0+ segments)
|
|
14
|
-
// - Deterministic precedence: static > :param > *catchall
|
|
15
|
-
// - Tie-breaker: lexicographic route path
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
import { readFileSync } from 'node:fs';
|
|
1
|
+
// File-based manifest engine. Scans /pages and produces deterministic RouteManifest entries.
|
|
2
|
+
// Rules: static > :param > *catchall, then lexicographic tie-breaker.
|
|
3
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
18
4
|
import { readdir, stat } from 'node:fs/promises';
|
|
19
5
|
import { join, relative, sep, basename, extname, dirname, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
20
7
|
import { extractServerScript } from './build/server-script.js';
|
|
21
8
|
import { analyzeResourceRouteModule, isResourceRouteFile } from './resource-route-module.js';
|
|
22
9
|
import { composeServerScriptEnvelope, resolveAdjacentServerModules } from './server-script-composition.js';
|
|
23
10
|
import { validateStaticExportPaths } from './static-export-paths.js';
|
|
24
11
|
import { classifyPageRoute } from './route-classification.js';
|
|
12
|
+
import { buildComponentRegistry } from './resolve-components.js';
|
|
13
|
+
const SCOPED_SERVER_DATA_HELPER_UNAVAILABLE = '[Zenith:ScopedServerData] Manifest integration helper is unavailable. Run the CLI build step before using scoped server data manifest integration.';
|
|
14
|
+
function resolveManifestIntegrationPath() {
|
|
15
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
return [
|
|
17
|
+
join(moduleDir, 'scoped-server-data', 'manifest-integration.js'),
|
|
18
|
+
join(moduleDir, '..', 'dist', 'scoped-server-data', 'manifest-integration.js')
|
|
19
|
+
].find((candidate) => existsSync(candidate)) || null;
|
|
20
|
+
}
|
|
21
|
+
const manifestIntegrationPath = resolveManifestIntegrationPath();
|
|
22
|
+
const manifestIntegration = manifestIntegrationPath
|
|
23
|
+
? await import(pathToFileURL(manifestIntegrationPath).href)
|
|
24
|
+
: null;
|
|
25
|
+
function getManifestIntegration() {
|
|
26
|
+
if (!manifestIntegration) {
|
|
27
|
+
throw new Error(SCOPED_SERVER_DATA_HELPER_UNAVAILABLE);
|
|
28
|
+
}
|
|
29
|
+
return manifestIntegration;
|
|
30
|
+
}
|
|
31
|
+
/** @type {typeof import('./scoped-server-data/manifest-integration.js').analyzeRouteScopedServerMetadata} */
|
|
32
|
+
export function analyzeRouteScopedServerMetadata(options) {
|
|
33
|
+
return getManifestIntegration().analyzeRouteScopedServerMetadata(options);
|
|
34
|
+
}
|
|
35
|
+
/** @type {typeof import('./scoped-server-data/manifest-integration.js').assertNoScopedServerBuildErrors} */
|
|
36
|
+
export function assertNoScopedServerBuildErrors(diagnostics, contextFile) {
|
|
37
|
+
return getManifestIntegration().assertNoScopedServerBuildErrors(diagnostics, contextFile);
|
|
38
|
+
}
|
|
25
39
|
/**
|
|
26
40
|
* @typedef {{
|
|
27
41
|
* path: string,
|
|
@@ -35,6 +49,8 @@ import { classifyPageRoute } from './route-classification.js';
|
|
|
35
49
|
* has_guard?: boolean,
|
|
36
50
|
* has_load?: boolean,
|
|
37
51
|
* has_action?: boolean,
|
|
52
|
+
* has_scoped_server_data?: boolean,
|
|
53
|
+
* scoped_server_data?: import('./scoped-server-data/types.js').ManifestScopedServerDataEntry[],
|
|
38
54
|
* export_paths?: string[]
|
|
39
55
|
* }} ManifestEntry
|
|
40
56
|
*/
|
|
@@ -43,11 +59,15 @@ import { classifyPageRoute } from './route-classification.js';
|
|
|
43
59
|
*
|
|
44
60
|
* @param {string} pagesDir - Absolute path to /pages directory
|
|
45
61
|
* @param {string} [extension='.zen'] - File extension to scan for
|
|
46
|
-
* @param {{ compilerOpts?: object }} [options]
|
|
62
|
+
* @param {{ compilerOpts?: object, srcDir?: string, registry?: Map<string, string> }} [options]
|
|
47
63
|
* @returns {Promise<ManifestEntry[]>}
|
|
48
64
|
*/
|
|
49
65
|
export async function generateManifest(pagesDir, extension = '.zen', options = {}) {
|
|
50
|
-
const
|
|
66
|
+
const resolvedPagesDir = resolve(pagesDir);
|
|
67
|
+
const srcDir = resolve(options.srcDir || resolve(resolvedPagesDir, '..'));
|
|
68
|
+
const registry = options.registry || buildComponentRegistry(srcDir);
|
|
69
|
+
const scanContext = { srcDir, registry, compilerOpts: options.compilerOpts || {} };
|
|
70
|
+
const entries = await _scanDir(resolvedPagesDir, resolvedPagesDir, extension, scanContext);
|
|
51
71
|
const apiAliasState = _resolveSrcApiAliasState(pagesDir);
|
|
52
72
|
if (apiAliasState) {
|
|
53
73
|
const aliasEntries = await _scanResourceDir(apiAliasState.aliasDir, apiAliasState.srcDir);
|
|
@@ -69,7 +89,7 @@ export async function generateManifest(pagesDir, extension = '.zen', options = {
|
|
|
69
89
|
* @param {string} ext - Extension to match
|
|
70
90
|
* @returns {Promise<ManifestEntry[]>}
|
|
71
91
|
*/
|
|
72
|
-
async function _scanDir(dir, root, ext,
|
|
92
|
+
async function _scanDir(dir, root, ext, scanContext) {
|
|
73
93
|
/** @type {ManifestEntry[]} */
|
|
74
94
|
const entries = [];
|
|
75
95
|
let items;
|
|
@@ -85,7 +105,7 @@ async function _scanDir(dir, root, ext, compilerOpts) {
|
|
|
85
105
|
const fullPath = join(dir, item);
|
|
86
106
|
const info = await stat(fullPath);
|
|
87
107
|
if (info.isDirectory()) {
|
|
88
|
-
const nested = await _scanDir(fullPath, root, ext,
|
|
108
|
+
const nested = await _scanDir(fullPath, root, ext, scanContext);
|
|
89
109
|
entries.push(...nested);
|
|
90
110
|
}
|
|
91
111
|
else if (item.endsWith(ext)) {
|
|
@@ -94,7 +114,7 @@ async function _scanDir(dir, root, ext, compilerOpts) {
|
|
|
94
114
|
fullPath,
|
|
95
115
|
root,
|
|
96
116
|
routePath,
|
|
97
|
-
|
|
117
|
+
scanContext
|
|
98
118
|
}));
|
|
99
119
|
}
|
|
100
120
|
else if (isResourceRouteFile(item)) {
|
|
@@ -143,7 +163,8 @@ function _resolveSrcApiAliasState(pagesDir) {
|
|
|
143
163
|
srcDir
|
|
144
164
|
};
|
|
145
165
|
}
|
|
146
|
-
function buildPageManifestEntry({ fullPath, root, routePath,
|
|
166
|
+
function buildPageManifestEntry({ fullPath, root, routePath, scanContext }) {
|
|
167
|
+
const { srcDir, registry, compilerOpts } = scanContext;
|
|
147
168
|
const rawSource = readFileSync(fullPath, 'utf8');
|
|
148
169
|
const inlineServerScript = extractServerScript(rawSource, fullPath, compilerOpts).serverScript;
|
|
149
170
|
const { guardPath, loadPath, actionPath } = resolveAdjacentServerModules(fullPath);
|
|
@@ -154,20 +175,39 @@ function buildPageManifestEntry({ fullPath, root, routePath, compilerOpts }) {
|
|
|
154
175
|
adjacentLoadPath: loadPath,
|
|
155
176
|
adjacentActionPath: actionPath
|
|
156
177
|
});
|
|
178
|
+
const scopedMetadata = analyzeRouteScopedServerMetadata({
|
|
179
|
+
pageSource: rawSource,
|
|
180
|
+
pageFile: fullPath,
|
|
181
|
+
registry,
|
|
182
|
+
srcDir,
|
|
183
|
+
compilerOpts
|
|
184
|
+
});
|
|
185
|
+
const manifestFile = relative(root, fullPath);
|
|
186
|
+
assertNoScopedServerBuildErrors(scopedMetadata.diagnostics, manifestFile);
|
|
157
187
|
const exportPaths = Array.isArray(composed.serverScript?.export_paths)
|
|
158
188
|
? validateStaticExportPaths(routePath, composed.serverScript.export_paths, fullPath)
|
|
159
189
|
: [];
|
|
160
190
|
const classification = classifyPageRoute({
|
|
161
|
-
file:
|
|
162
|
-
serverScript: composed.serverScript
|
|
191
|
+
file: manifestFile,
|
|
192
|
+
serverScript: composed.serverScript,
|
|
193
|
+
hasScopedServerData: scopedMetadata.hasScopedServerData
|
|
163
194
|
});
|
|
164
195
|
return {
|
|
165
196
|
path: routePath,
|
|
166
|
-
file:
|
|
197
|
+
file: manifestFile,
|
|
167
198
|
route_kind: 'page',
|
|
168
199
|
path_kind: _isDynamic(routePath) ? 'dynamic' : 'static',
|
|
169
200
|
render_mode: classification.renderMode,
|
|
170
201
|
params: extractRouteParams(routePath),
|
|
202
|
+
has_guard: classification.hasGuard,
|
|
203
|
+
has_load: classification.hasLoad,
|
|
204
|
+
has_action: classification.hasAction,
|
|
205
|
+
...(scopedMetadata.hasScopedServerData
|
|
206
|
+
? {
|
|
207
|
+
has_scoped_server_data: true,
|
|
208
|
+
scoped_server_data: scopedMetadata.scopedServerData
|
|
209
|
+
}
|
|
210
|
+
: {}),
|
|
171
211
|
...(exportPaths.length > 0 ? { export_paths: exportPaths } : {})
|
|
172
212
|
};
|
|
173
213
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { extname, join } from 'node:path';
|
|
2
|
+
import { dirname, extname, join } from 'node:path';
|
|
3
3
|
import { appLocalRedirectLocation, imageEndpointPath, routeCheckPath, stripBasePath } from '../base-path.js';
|
|
4
4
|
import { materializeImageMarkup } from '../images/materialize.js';
|
|
5
5
|
import { createImageRuntimePayload, injectImageRuntimePayload } from '../images/payload.js';
|
|
@@ -8,6 +8,7 @@ import { readRequestBodyBuffer } from '../request-body.js';
|
|
|
8
8
|
import { buildResourceResponseDescriptor } from '../resource-response.js';
|
|
9
9
|
import { clientFacingRouteMessage, logServerException, sanitizeRouteResult } from '../server-error.js';
|
|
10
10
|
import { resolveRequestRoute } from '../server/resolve-request-route.js';
|
|
11
|
+
import { loadPreviewGlobalMiddlewareSource } from '../global-middleware-runtime-source.js';
|
|
11
12
|
import { loadRouteSurfaceState } from './manifest.js';
|
|
12
13
|
import { injectSsrPayload } from './payload.js';
|
|
13
14
|
import { fileExists, resolveWithinDist, toStaticFilePath } from './paths.js';
|
|
@@ -32,6 +33,47 @@ function appendSetCookieHeaders(headers, setCookies = []) {
|
|
|
32
33
|
}
|
|
33
34
|
return headers;
|
|
34
35
|
}
|
|
36
|
+
function respondWithMiddlewareSourceError(res, error) {
|
|
37
|
+
logServerException('preview server route execution failed', error);
|
|
38
|
+
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
39
|
+
res.end(clientFacingRouteMessage(500));
|
|
40
|
+
}
|
|
41
|
+
function hasRouteScopedServerData(route) {
|
|
42
|
+
return route?.has_scoped_server_data === true &&
|
|
43
|
+
Array.isArray(route?.scoped_server_data) &&
|
|
44
|
+
route.scoped_server_data.length > 0;
|
|
45
|
+
}
|
|
46
|
+
async function loadPreviewScopedServerRoutes(serverModuleBaseDir) {
|
|
47
|
+
try {
|
|
48
|
+
const parsed = JSON.parse(await readFile(join(serverModuleBaseDir, 'manifest.json'), 'utf8'));
|
|
49
|
+
return Array.isArray(parsed?.routes) ? parsed.routes : [];
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function mergePreviewScopedServerRoutes(routeState, serverRoutes) {
|
|
56
|
+
const scopedByPath = new Map((Array.isArray(serverRoutes) ? serverRoutes : [])
|
|
57
|
+
.filter((route) => hasRouteScopedServerData(route))
|
|
58
|
+
.map((route) => [route.path, route]));
|
|
59
|
+
if (scopedByPath.size === 0) {
|
|
60
|
+
return routeState;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...routeState,
|
|
64
|
+
pageRoutes: (Array.isArray(routeState.pageRoutes) ? routeState.pageRoutes : []).map((route) => {
|
|
65
|
+
const scoped = scopedByPath.get(route.path);
|
|
66
|
+
if (!scoped) {
|
|
67
|
+
return route;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
...route,
|
|
71
|
+
has_scoped_server_data: true,
|
|
72
|
+
scoped_server_data: scoped.scoped_server_data
|
|
73
|
+
};
|
|
74
|
+
})
|
|
75
|
+
};
|
|
76
|
+
}
|
|
35
77
|
export function createPreviewRequestHandler(options) {
|
|
36
78
|
const { distDir, projectRoot, config, logger, verboseLogging, configuredBasePath, routeCheckEnabled, isStaticExportTarget, serverOrigin } = options;
|
|
37
79
|
async function loadImageManifest() {
|
|
@@ -44,9 +86,14 @@ export function createPreviewRequestHandler(options) {
|
|
|
44
86
|
return {};
|
|
45
87
|
}
|
|
46
88
|
}
|
|
89
|
+
async function loadGlobalMiddlewareForRoute() {
|
|
90
|
+
return loadPreviewGlobalMiddlewareSource({ projectRoot, distDir });
|
|
91
|
+
}
|
|
47
92
|
return async function previewRequestHandler(req, res) {
|
|
48
93
|
const url = new URL(req.url, serverOrigin());
|
|
49
|
-
const
|
|
94
|
+
const serverModuleBaseDir = join(dirname(distDir), 'server');
|
|
95
|
+
const routeState = mergePreviewScopedServerRoutes(await loadRouteSurfaceState(distDir, configuredBasePath), await loadPreviewScopedServerRoutes(serverModuleBaseDir));
|
|
96
|
+
const { basePath, pageRoutes, resourceRoutes } = routeState;
|
|
50
97
|
const canonicalPath = stripBasePath(url.pathname, basePath);
|
|
51
98
|
try {
|
|
52
99
|
if (url.pathname === routeCheckPath(basePath)) {
|
|
@@ -171,6 +218,14 @@ export function createPreviewRequestHandler(options) {
|
|
|
171
218
|
canonicalUrl.pathname = canonicalPath;
|
|
172
219
|
const resolvedResource = resolveRequestRoute(canonicalUrl, resourceRoutes);
|
|
173
220
|
if (resolvedResource.matched && resolvedResource.route) {
|
|
221
|
+
let globalMiddleware = null;
|
|
222
|
+
try {
|
|
223
|
+
globalMiddleware = await loadGlobalMiddlewareForRoute();
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
respondWithMiddlewareSourceError(res, error);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
174
229
|
const requestMethod = req.method || 'GET';
|
|
175
230
|
const requestBodyBuffer = requestMethod === 'GET' || requestMethod === 'HEAD'
|
|
176
231
|
? null
|
|
@@ -186,7 +241,9 @@ export function createPreviewRequestHandler(options) {
|
|
|
186
241
|
routePattern: resolvedResource.route.path,
|
|
187
242
|
routeFile: resolvedResource.route.server_script_path || '',
|
|
188
243
|
routeId: resolvedResource.route.route_id || routeIdFromSourcePath(resolvedResource.route.server_script_path || ''),
|
|
189
|
-
routeKind: 'resource'
|
|
244
|
+
routeKind: 'resource',
|
|
245
|
+
globalMiddlewareSource: globalMiddleware?.source || '',
|
|
246
|
+
globalMiddlewareSourcePath: globalMiddleware?.sourcePath || ''
|
|
190
247
|
});
|
|
191
248
|
const descriptor = buildResourceResponseDescriptor(execution?.result, basePath, Array.isArray(execution?.setCookies) ? execution.setCookies : []);
|
|
192
249
|
res.writeHead(descriptor.status, appendSetCookieHeaders(descriptor.headers, descriptor.setCookies));
|
|
@@ -216,7 +273,17 @@ export function createPreviewRequestHandler(options) {
|
|
|
216
273
|
}
|
|
217
274
|
let ssrPayload = null;
|
|
218
275
|
let routeExecution = null;
|
|
219
|
-
if (resolved.matched &&
|
|
276
|
+
if (resolved.matched &&
|
|
277
|
+
resolved.route?.prerender !== true &&
|
|
278
|
+
(resolved.route?.server_script || hasRouteScopedServerData(resolved.route))) {
|
|
279
|
+
let globalMiddleware = null;
|
|
280
|
+
try {
|
|
281
|
+
globalMiddleware = await loadGlobalMiddlewareForRoute();
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
respondWithMiddlewareSourceError(res, error);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
220
287
|
try {
|
|
221
288
|
const requestMethod = req.method || 'GET';
|
|
222
289
|
const requestBodyBuffer = requestMethod === 'GET' || requestMethod === 'HEAD'
|
|
@@ -232,7 +299,13 @@ export function createPreviewRequestHandler(options) {
|
|
|
232
299
|
requestBodyBuffer,
|
|
233
300
|
routePattern: resolved.route.path,
|
|
234
301
|
routeFile: resolved.route.server_script_path || '',
|
|
235
|
-
routeId: resolved.route.route_id || routeIdFromSourcePath(resolved.route.server_script_path || '')
|
|
302
|
+
routeId: resolved.route.route_id || routeIdFromSourcePath(resolved.route.server_script_path || ''),
|
|
303
|
+
globalMiddlewareSource: globalMiddleware?.source || '',
|
|
304
|
+
globalMiddlewareSourcePath: globalMiddleware?.sourcePath || '',
|
|
305
|
+
scopedServerData: Array.isArray(resolved.route.scoped_server_data)
|
|
306
|
+
? resolved.route.scoped_server_data
|
|
307
|
+
: [],
|
|
308
|
+
scopedServerModuleBaseDir: serverModuleBaseDir
|
|
236
309
|
});
|
|
237
310
|
}
|
|
238
311
|
catch (error) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {{ source: string, sourcePath: string, params: Record<string, string>, requestUrl?: string, requestMethod?: string, requestHeaders?: Record<string, string | string[] | undefined>, requestBodyBuffer?: Buffer | null, routePattern?: string, routeFile?: string, routeId?: string, routeKind?: 'page' | 'resource' }} input
|
|
2
|
+
* @param {{ source: string, sourcePath: string, params: Record<string, string>, requestUrl?: string, requestMethod?: string, requestHeaders?: Record<string, string | string[] | undefined>, requestBodyBuffer?: Buffer | null, routePattern?: string, routeFile?: string, routeId?: string, routeKind?: 'page' | 'resource', globalMiddlewareSource?: string, globalMiddlewareSourcePath?: string, scopedServerData?: unknown[], scopedServerModuleBaseDir?: string, scopedServerModuleSources?: unknown[] }} input
|
|
3
3
|
* @returns {Promise<{ result: { kind: string, [key: string]: unknown }, trace: { guard: string, action: string, load: string }, status?: number, setCookies?: string[] }>}
|
|
4
4
|
*/
|
|
5
|
-
export function executeServerRoute({ source, sourcePath, params, requestUrl, requestMethod, requestHeaders, requestBodyBuffer, routePattern, routeFile, routeId, routeKind, guardOnly }: {
|
|
5
|
+
export function executeServerRoute({ source, sourcePath, params, requestUrl, requestMethod, requestHeaders, requestBodyBuffer, routePattern, routeFile, routeId, routeKind, guardOnly, globalMiddlewareSource, globalMiddlewareSourcePath, scopedServerData, scopedServerModuleBaseDir, scopedServerModuleSources }: {
|
|
6
6
|
source: string;
|
|
7
7
|
sourcePath: string;
|
|
8
8
|
params: Record<string, string>;
|
|
@@ -14,6 +14,11 @@ export function executeServerRoute({ source, sourcePath, params, requestUrl, req
|
|
|
14
14
|
routeFile?: string;
|
|
15
15
|
routeId?: string;
|
|
16
16
|
routeKind?: "page" | "resource";
|
|
17
|
+
globalMiddlewareSource?: string;
|
|
18
|
+
globalMiddlewareSourcePath?: string;
|
|
19
|
+
scopedServerData?: unknown[];
|
|
20
|
+
scopedServerModuleBaseDir?: string;
|
|
21
|
+
scopedServerModuleSources?: unknown[];
|
|
17
22
|
}): Promise<{
|
|
18
23
|
result: {
|
|
19
24
|
kind: string;
|
|
@@ -5,11 +5,12 @@ import { clientFacingRouteMessage, defaultRouteDenyMessage } from '../server-err
|
|
|
5
5
|
import { SERVER_SCRIPT_RUNNER } from './server-script-runner-template.js';
|
|
6
6
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
/**
|
|
8
|
-
* @param {{ source: string, sourcePath: string, params: Record<string, string>, requestUrl?: string, requestMethod?: string, requestHeaders?: Record<string, string | string[] | undefined>, requestBodyBuffer?: Buffer | null, routePattern?: string, routeFile?: string, routeId?: string, routeKind?: 'page' | 'resource' }} input
|
|
8
|
+
* @param {{ source: string, sourcePath: string, params: Record<string, string>, requestUrl?: string, requestMethod?: string, requestHeaders?: Record<string, string | string[] | undefined>, requestBodyBuffer?: Buffer | null, routePattern?: string, routeFile?: string, routeId?: string, routeKind?: 'page' | 'resource', globalMiddlewareSource?: string, globalMiddlewareSourcePath?: string, scopedServerData?: unknown[], scopedServerModuleBaseDir?: string, scopedServerModuleSources?: unknown[] }} input
|
|
9
9
|
* @returns {Promise<{ result: { kind: string, [key: string]: unknown }, trace: { guard: string, action: string, load: string }, status?: number, setCookies?: string[] }>}
|
|
10
10
|
*/
|
|
11
|
-
export async function executeServerRoute({ source, sourcePath, params, requestUrl, requestMethod, requestHeaders, requestBodyBuffer, routePattern, routeFile, routeId, routeKind = 'page', guardOnly = false }) {
|
|
12
|
-
|
|
11
|
+
export async function executeServerRoute({ source, sourcePath, params, requestUrl, requestMethod, requestHeaders, requestBodyBuffer, routePattern, routeFile, routeId, routeKind = 'page', guardOnly = false, globalMiddlewareSource = '', globalMiddlewareSourcePath = '', scopedServerData = [], scopedServerModuleBaseDir = '', scopedServerModuleSources = [] }) {
|
|
12
|
+
const hasScopedServerData = Array.isArray(scopedServerData) && scopedServerData.length > 0;
|
|
13
|
+
if ((!source || !String(source).trim()) && !hasScopedServerData) {
|
|
13
14
|
return {
|
|
14
15
|
result: { kind: 'data', data: {} },
|
|
15
16
|
trace: { guard: 'none', action: 'none', load: 'none' }
|
|
@@ -27,7 +28,12 @@ export async function executeServerRoute({ source, sourcePath, params, requestUr
|
|
|
27
28
|
routeFile: routeFile || sourcePath || '',
|
|
28
29
|
routeId: routeId || routeIdFromSourcePath(sourcePath || ''),
|
|
29
30
|
routeKind,
|
|
30
|
-
guardOnly
|
|
31
|
+
guardOnly,
|
|
32
|
+
globalMiddlewareSource,
|
|
33
|
+
globalMiddlewareSourcePath,
|
|
34
|
+
scopedServerData,
|
|
35
|
+
scopedServerModuleBaseDir,
|
|
36
|
+
scopedServerModuleSources
|
|
31
37
|
});
|
|
32
38
|
if (payload === null || payload === undefined) {
|
|
33
39
|
return {
|
|
@@ -110,7 +116,7 @@ export async function executeServerScript(input) {
|
|
|
110
116
|
return {};
|
|
111
117
|
}
|
|
112
118
|
/**
|
|
113
|
-
* @param {{ source: string, sourcePath: string, params: Record<string, string>, requestUrl: string, requestMethod: string, requestHeaders: Record<string, string>, requestBodyBuffer?: Buffer | null, routePattern: string, routeFile: string, routeId: string, routeKind?: 'page' | 'resource' }} input
|
|
119
|
+
* @param {{ source: string, sourcePath: string, params: Record<string, string>, requestUrl: string, requestMethod: string, requestHeaders: Record<string, string>, requestBodyBuffer?: Buffer | null, routePattern: string, routeFile: string, routeId: string, routeKind?: 'page' | 'resource', globalMiddlewareSource?: string, globalMiddlewareSourcePath?: string, scopedServerData?: unknown[], scopedServerModuleBaseDir?: string, scopedServerModuleSources?: unknown[] }} input
|
|
114
120
|
* @returns {Promise<unknown>}
|
|
115
121
|
*/
|
|
116
122
|
function spawnNodeServerRunner(input) {
|
|
@@ -130,7 +136,14 @@ function spawnNodeServerRunner(input) {
|
|
|
130
136
|
ZENITH_SERVER_ROUTE_KIND: input.routeKind || 'page',
|
|
131
137
|
ZENITH_SERVER_GUARD_ONLY: input.guardOnly ? '1' : '',
|
|
132
138
|
ZENITH_SERVER_CONTRACT_PATH: join(__dirname, '..', 'server-contract.js'),
|
|
133
|
-
ZENITH_SERVER_ROUTE_AUTH_PATH: join(__dirname, '..', 'auth', 'route-auth.js')
|
|
139
|
+
ZENITH_SERVER_ROUTE_AUTH_PATH: join(__dirname, '..', 'auth', 'route-auth.js'),
|
|
140
|
+
ZENITH_SERVER_MATCHED_ROUTE_PIPELINE_PATH: join(__dirname, '..', 'server-runtime', 'matched-route-pipeline.js'),
|
|
141
|
+
ZENITH_GLOBAL_MIDDLEWARE_SOURCE: input.globalMiddlewareSource || '',
|
|
142
|
+
ZENITH_GLOBAL_MIDDLEWARE_SOURCE_PATH: input.globalMiddlewareSourcePath || '',
|
|
143
|
+
ZENITH_SCOPED_SERVER_DATA: JSON.stringify(Array.isArray(input.scopedServerData) ? input.scopedServerData : []),
|
|
144
|
+
ZENITH_SCOPED_SERVER_MODULE_BASE_DIR: input.scopedServerModuleBaseDir || '',
|
|
145
|
+
ZENITH_SCOPED_SERVER_MODULE_SOURCES: JSON.stringify(Array.isArray(input.scopedServerModuleSources) ? input.scopedServerModuleSources : []),
|
|
146
|
+
ZENITH_SCOPED_SERVER_RUNTIME_PATH: join(__dirname, '..', 'scoped-server-data', 'runtime.js')
|
|
134
147
|
},
|
|
135
148
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
136
149
|
});
|