@zenithbuild/cli 0.7.10 → 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.
Files changed (111) hide show
  1. package/README.md +14 -2
  2. package/dist/adapters/adapter-netlify-static.d.ts +2 -5
  3. package/dist/adapters/adapter-netlify.d.ts +2 -5
  4. package/dist/adapters/adapter-netlify.js +22 -5
  5. package/dist/adapters/adapter-types.d.ts +32 -13
  6. package/dist/adapters/adapter-types.js +0 -59
  7. package/dist/adapters/adapter-vercel-static.d.ts +2 -5
  8. package/dist/adapters/adapter-vercel.d.ts +2 -5
  9. package/dist/adapters/adapter-vercel.js +21 -6
  10. package/dist/adapters/copy-hosted-page-runtime.d.ts +2 -1
  11. package/dist/adapters/copy-hosted-page-runtime.js +68 -3
  12. package/dist/adapters/resolve-adapter.d.ts +6 -4
  13. package/dist/build/compiler-runtime.js +3 -0
  14. package/dist/build/expression-rewrites.d.ts +3 -1
  15. package/dist/build/expression-rewrites.js +14 -2
  16. package/dist/build/page-component-loop.d.ts +1 -0
  17. package/dist/build/page-component-loop.js +66 -6
  18. package/dist/build/page-ir-normalization.js +7 -0
  19. package/dist/build/page-loop-state.d.ts +2 -4
  20. package/dist/build/page-loop-state.js +17 -9
  21. package/dist/build/page-loop.js +18 -8
  22. package/dist/build/scoped-expression-context.d.ts +5 -0
  23. package/dist/build/scoped-expression-context.js +133 -0
  24. package/dist/build/server-script.js +13 -36
  25. package/dist/build/type-declarations.d.ts +2 -1
  26. package/dist/build/type-declarations.js +29 -52
  27. package/dist/build-output-manifest.d.ts +10 -6
  28. package/dist/build-output-manifest.js +4 -1
  29. package/dist/build.js +11 -2
  30. package/dist/component-instance-ir.js +1 -0
  31. package/dist/component-occurrences.d.ts +9 -0
  32. package/dist/component-occurrences.js +18 -0
  33. package/dist/config-plugins.d.ts +12 -0
  34. package/dist/config-plugins.js +100 -0
  35. package/dist/config.d.ts +1 -0
  36. package/dist/config.js +56 -5
  37. package/dist/dev-build-session/helpers.js +27 -7
  38. package/dist/dev-build-session/session.js +19 -10
  39. package/dist/dev-server/build-error-response.d.ts +21 -0
  40. package/dist/dev-server/build-error-response.js +48 -0
  41. package/dist/dev-server/port-fallback.d.ts +15 -0
  42. package/dist/dev-server/port-fallback.js +61 -0
  43. package/dist/dev-server/request-handler.js +58 -5
  44. package/dist/dev-server/watcher.js +15 -0
  45. package/dist/dev-server.d.ts +5 -2
  46. package/dist/dev-server.js +129 -49
  47. package/dist/global-middleware-runtime-source.d.ts +15 -0
  48. package/dist/global-middleware-runtime-source.js +62 -0
  49. package/dist/global-middleware.d.ts +13 -0
  50. package/dist/global-middleware.js +252 -0
  51. package/dist/images/remote-fetch.d.ts +12 -0
  52. package/dist/images/remote-fetch.js +257 -0
  53. package/dist/images/service.d.ts +10 -0
  54. package/dist/images/service.js +9 -46
  55. package/dist/index.js +12 -2
  56. package/dist/manifest.d.ts +9 -1
  57. package/dist/manifest.js +70 -25
  58. package/dist/preview/request-handler.js +78 -5
  59. package/dist/preview/server-runner.d.ts +7 -2
  60. package/dist/preview/server-runner.js +19 -6
  61. package/dist/preview/server-script-runner-template.js +97 -29
  62. package/dist/resource-response.js +25 -8
  63. package/dist/resource-route-module.js +5 -22
  64. package/dist/route-classification.d.ts +11 -0
  65. package/dist/route-classification.js +21 -0
  66. package/dist/route-handler-export-analysis.d.ts +22 -0
  67. package/dist/route-handler-export-analysis.js +41 -0
  68. package/dist/scoped-server-data/analyze-owner-file.d.ts +3 -0
  69. package/dist/scoped-server-data/analyze-owner-file.js +149 -0
  70. package/dist/scoped-server-data/diagnostics.d.ts +18 -0
  71. package/dist/scoped-server-data/diagnostics.js +32 -0
  72. package/dist/scoped-server-data/lowering.d.ts +27 -0
  73. package/dist/scoped-server-data/lowering.js +242 -0
  74. package/dist/scoped-server-data/manifest-integration.d.ts +4 -0
  75. package/dist/scoped-server-data/manifest-integration.js +125 -0
  76. package/dist/scoped-server-data/owner-scanner.d.ts +6 -0
  77. package/dist/scoped-server-data/owner-scanner.js +55 -0
  78. package/dist/scoped-server-data/parse-owner-server-block.d.ts +12 -0
  79. package/dist/scoped-server-data/parse-owner-server-block.js +35 -0
  80. package/dist/scoped-server-data/runtime.d.ts +24 -0
  81. package/dist/scoped-server-data/runtime.js +121 -0
  82. package/dist/scoped-server-data/serialization-set.d.ts +2 -0
  83. package/dist/scoped-server-data/serialization-set.js +52 -0
  84. package/dist/scoped-server-data/static-props.d.ts +12 -0
  85. package/dist/scoped-server-data/static-props.js +307 -0
  86. package/dist/scoped-server-data/type-declarations.d.ts +10 -0
  87. package/dist/scoped-server-data/type-declarations.js +368 -0
  88. package/dist/scoped-server-data/types.d.ts +74 -0
  89. package/dist/scoped-server-data/types.js +1 -0
  90. package/dist/server-contract/auth-control-flow.d.ts +1 -0
  91. package/dist/server-contract/auth-control-flow.js +10 -0
  92. package/dist/server-contract/resolve.d.ts +19 -0
  93. package/dist/server-contract/resolve.js +85 -13
  94. package/dist/server-contract/resolved-envelope.d.ts +9 -0
  95. package/dist/server-contract/resolved-envelope.js +14 -0
  96. package/dist/server-contract/stage.js +1 -10
  97. package/dist/server-module-output.d.ts +9 -0
  98. package/dist/server-module-output.js +250 -0
  99. package/dist/server-output.d.ts +7 -1
  100. package/dist/server-output.js +144 -195
  101. package/dist/server-route-names.d.ts +2 -0
  102. package/dist/server-route-names.js +38 -0
  103. package/dist/server-runtime/matched-route-pipeline.d.ts +1 -0
  104. package/dist/server-runtime/matched-route-pipeline.js +1 -0
  105. package/dist/server-runtime/node-server.js +26 -3
  106. package/dist/server-runtime/route-render.d.ts +12 -3
  107. package/dist/server-runtime/route-render.js +67 -13
  108. package/dist/types/generate-env-dts.js +2 -44
  109. package/dist/types/zenith-env-dts.d.ts +4 -0
  110. package/dist/types/zenith-env-dts.js +96 -0
  111. package/package.json +3 -6
package/README.md CHANGED
@@ -19,7 +19,7 @@ The command-line interface for developing and building Zenith applications.
19
19
  - `zenith build`
20
20
  - `zenith preview`
21
21
 
22
- It does not ship a public plugin-management surface.
22
+ It ships a minimal V1 plugin surface for config-time normalization only.
23
23
 
24
24
  ## Features
25
25
 
@@ -41,9 +41,18 @@ Current top-level `zenith.config.js` keys:
41
41
  - `adapter`
42
42
  - `strictDomLints`
43
43
  - `images`
44
+ - `plugins`
44
45
 
45
46
  There is no separate `assetPrefix` config. Public framework asset URLs follow `basePath`.
46
47
 
48
+ `plugins` behavior:
49
+
50
+ - Plugins are added in `zenith.config.js` with `plugins: [authPlugin(), mdxPlugin()]`.
51
+ - V1 plugins must be named objects and may only provide a `config()` hook.
52
+ - V1 plugin config patches are shallow top-level patches; nested objects such as `images` replace that config object instead of deep-merging.
53
+ - V1 plugins cannot transform files, register middleware, mutate routes/security, or install compiler/bundler/dev-server hooks.
54
+ - Global middleware is separate Lane 2 work.
55
+
47
56
  `pagesDir` resolution:
48
57
 
49
58
  - If `pagesDir` is set, the CLI uses that path relative to the project root.
@@ -83,7 +92,8 @@ Current limitations:
83
92
 
84
93
  - There is no separate `assetPrefix` knob. Assets intentionally follow `basePath`.
85
94
  - `static-export` does not expose deployed `/_zenith/image` or `/__zenith/route-check` endpoints. A plain static file server is the contract.
86
- - `vercel` and `netlify` do not yet emit a deployed `/_zenith/image` endpoint. The `node` target does.
95
+ - `node`, `vercel`, and `netlify` expose deployed `/_zenith/image` endpoints on the packaged image contract.
96
+ - Hosted `vercel` and `netlify` targets skip advisory `/__zenith/route-check`; direct HTML requests remain the server-side route boundary.
87
97
  - Image materialization is route-artifact-driven. Bundler owns final build/static HTML image materialization, while preview and server render still materialize at runtime from structured `image_materialization` metadata. No path executes page assets, and dynamic image props are currently unsupported until the compiler emits a dedicated image-props artifact.
88
98
  - There is no shipped plugin install/remove command surface in this CLI.
89
99
 
@@ -92,6 +102,8 @@ Current limitations:
92
102
  ### `zenith dev`
93
103
  Starts the development server on `localhost:3000`.
94
104
 
105
+ Changes to `zenith.config.js` or `zenith.config.ts` require restarting `zenith dev`. If the dev watcher observes a config-file edit, it reports the restart policy instead of rebuilding with stale config.
106
+
95
107
  ### `zenith build`
96
108
  Compiles and bundles your application for production.
97
109
 
@@ -1,5 +1,2 @@
1
- export namespace netlifyStaticAdapter {
2
- let name: string;
3
- function validateRoutes(manifest: any): void;
4
- function adapt(options: any): Promise<void>;
5
- }
1
+ import type { AdapterDriver } from './adapter-types.js';
2
+ export declare const netlifyStaticAdapter: AdapterDriver;
@@ -1,5 +1,2 @@
1
- export namespace netlifyAdapter {
2
- let name: string;
3
- function validateRoutes(manifest: any): void;
4
- function adapt(options: any): Promise<void>;
5
- }
1
+ import type { AdapterDriver } from './adapter-types.js';
2
+ export declare const netlifyAdapter: AdapterDriver;
@@ -2,7 +2,7 @@ import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { prependBasePath } from '../base-path.js';
4
4
  import { compareRouteSpecificity } from '../server/resolve-request-route.js';
5
- import { copyHostedPageRuntime } from './copy-hosted-page-runtime.js';
5
+ import { copyHostedGlobalMiddlewareRuntime, copyHostedPageRuntime } from './copy-hosted-page-runtime.js';
6
6
  import { createNetlifyBasePathAssetRules, createNetlifyImageEndpointRule, createNetlifyRewriteRules } from './route-rules.js';
7
7
  import { validateHostedResourceRoutes } from './validate-hosted-resource-routes.js';
8
8
  function buildNetlifyServerRules(route, basePath = '/') {
@@ -32,13 +32,17 @@ function buildNetlifyServerRules(route, basePath = '/') {
32
32
  const query = route.params.map((param) => `__zenith_param_${param}=:${param}`).join('&');
33
33
  return [`${prependBasePath(basePath, sourcePath)} ${destination}?${query} 200!`];
34
34
  }
35
- function createFunctionSource(route) {
35
+ function createFunctionSource(route, globalMiddlewareModulePath) {
36
+ const globalMiddlewarePathExpression = globalMiddlewareModulePath
37
+ ? "join(__dirname, '_zenith', 'global-middleware', 'entry.js')"
38
+ : 'null';
36
39
  return [
37
40
  "import { fileURLToPath } from 'node:url';",
38
41
  "import { dirname, join } from 'node:path';",
39
42
  "import { renderResourceRouteRequest, renderRouteRequest, extractInternalParams } from './_zenith/runtime/route-render.js';",
40
43
  '',
41
44
  'const __dirname = dirname(fileURLToPath(import.meta.url));',
45
+ `const globalMiddlewareModulePath = ${globalMiddlewarePathExpression};`,
42
46
  `const route = ${JSON.stringify(route, null, 2)};`,
43
47
  '',
44
48
  'function createHostedUnsupportedResponse(message) {',
@@ -52,7 +56,8 @@ function createFunctionSource(route) {
52
56
  ' request,',
53
57
  ' route,',
54
58
  ' params,',
55
- ` routeModulePath: join(__dirname, '_zenith', 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js')`,
59
+ ` routeModulePath: join(__dirname, '_zenith', 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js'),`,
60
+ ' globalMiddlewareModulePath',
56
61
  ' });',
57
62
  " if (response.headers.has('content-disposition')) {",
58
63
  " return createHostedUnsupportedResponse('Hosted resource downloads are unsupported in this milestone');",
@@ -64,6 +69,7 @@ function createFunctionSource(route) {
64
69
  ' route,',
65
70
  ' params,',
66
71
  ` routeModulePath: join(__dirname, '_zenith', 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js'),`,
72
+ ' globalMiddlewareModulePath,',
67
73
  ` shellHtmlPath: join(__dirname, '_zenith', 'routes', ${JSON.stringify(route.name)}, 'route', 'page.html'),`,
68
74
  ` pageAssetPath: ${route.page_asset_file ? "join(__dirname, '_zenith', 'routes', " + JSON.stringify(route.name) + ", 'route', " + JSON.stringify(route.page_asset_file) + ')' : 'null'},`,
69
75
  ` imageManifestPath: ${route.image_manifest_file ? "join(__dirname, '_zenith', 'routes', " + JSON.stringify(route.name) + ", 'route', " + JSON.stringify(route.image_manifest_file) + ')' : 'null'},`,
@@ -91,6 +97,12 @@ function createImageFunctionSource(imagesConfig) {
91
97
  ''
92
98
  ].join('\n');
93
99
  }
100
+ function hasHostedScopedServerData(route) {
101
+ return route.route_kind !== 'resource' &&
102
+ route.has_scoped_server_data === true &&
103
+ Array.isArray(route.scoped_server_data) &&
104
+ route.scoped_server_data.length > 0;
105
+ }
94
106
  async function loadServerManifest(coreOutput) {
95
107
  try {
96
108
  const parsed = JSON.parse(await readFile(join(coreOutput, 'server', 'manifest.json'), 'utf8'));
@@ -144,11 +156,16 @@ export const netlifyAdapter = {
144
156
  await mkdir(functionsDir, { recursive: true });
145
157
  await cp(staticDir, publishDir, { recursive: true, force: true });
146
158
  await writeFile(join(functionsDir, 'package.json'), '{\n "type": "module"\n}\n', 'utf8');
147
- await copyHostedPageRuntime(options.coreOutput, join(functionsDir, '_zenith'));
159
+ await copyHostedPageRuntime(options.coreOutput, join(functionsDir, '_zenith'), {
160
+ includeScopedServerData: serverRoutes.some(hasHostedScopedServerData)
161
+ });
162
+ const globalMiddlewareModulePath = serverRoutes.length > 0
163
+ ? await copyHostedGlobalMiddlewareRuntime(options.coreOutput, join(functionsDir, '_zenith'))
164
+ : null;
148
165
  await writeFile(join(functionsDir, '__zenith_image.mjs'), createImageFunctionSource(options.config?.images || {}), 'utf8');
149
166
  for (const route of serverRoutes) {
150
167
  await cp(join(options.coreOutput, 'server', 'routes', route.name), join(functionsDir, '_zenith', 'routes', route.name), { recursive: true, force: true });
151
- await writeFile(join(functionsDir, `__zenith_${route.name}.mjs`), createFunctionSource(route), 'utf8');
168
+ await writeFile(join(functionsDir, `__zenith_${route.name}.mjs`), createFunctionSource(route, globalMiddlewareModulePath), 'utf8');
152
169
  }
153
170
  await writeFile(join(publishDir, '_redirects'), buildRedirectsFile(options.manifest, serverRoutes), 'utf8');
154
171
  await writeFile(join(options.outDir, 'netlify.toml'), [
@@ -1,21 +1,24 @@
1
- export const KNOWN_TARGETS: string[];
2
- export type ZenithTarget = "static" | "static-export" | "vercel-static" | "netlify-static" | "vercel" | "netlify" | "node";
3
- export type ZenithRenderMode = "prerender" | "server";
4
- export type ZenithPathKind = "static" | "dynamic";
5
- export type RouteManifestEntry = {
1
+ export declare const KNOWN_TARGETS: string[];
2
+ export type ZenithTarget = 'static' | 'static-export' | 'vercel-static' | 'netlify-static' | 'vercel' | 'netlify' | 'node';
3
+ export type ZenithRenderMode = 'prerender' | 'server';
4
+ export type ZenithPathKind = 'static' | 'dynamic';
5
+ export interface RouteManifestEntry {
6
6
  path: string;
7
7
  file: string;
8
8
  path_kind: ZenithPathKind;
9
9
  render_mode: ZenithRenderMode;
10
10
  params: string[];
11
11
  export_paths?: string[];
12
- };
13
- export type BuildManifest = {
12
+ }
13
+ export interface BuildManifest {
14
14
  schema_version: number;
15
15
  zenith_version: string;
16
16
  target: string;
17
17
  base_path: string;
18
18
  content_hash: string;
19
+ global_middleware?: {
20
+ source_file: string;
21
+ };
19
22
  routes: Array<{
20
23
  path: string;
21
24
  file: string;
@@ -32,15 +35,31 @@ export type BuildManifest = {
32
35
  css: string[];
33
36
  vendor: string | null;
34
37
  };
35
- };
36
- export type AdaptOptions = {
38
+ }
39
+ export interface AdaptOptions {
37
40
  coreOutput: string;
38
41
  outDir: string;
39
42
  manifest: BuildManifest;
40
43
  config: object;
41
- };
42
- export type ZenithAdapter = {
44
+ }
45
+ export type AdapterResolutionMode = 'adapter' | 'target' | 'legacy';
46
+ export interface AdapterManifestEntry extends RouteManifestEntry {
47
+ route_kind?: 'page' | 'resource';
48
+ server_script?: string;
49
+ server_script_path?: string;
50
+ has_guard?: boolean;
51
+ has_load?: boolean;
52
+ has_action?: boolean;
53
+ }
54
+ export type AdapterRouteManifest = AdapterManifestEntry[];
55
+ export interface AdapterDriver {
43
56
  name: string;
44
- validateRoutes: (manifest: RouteManifestEntry[]) => void;
57
+ validateRoutes: (manifest: AdapterRouteManifest) => void;
45
58
  adapt: (options: AdaptOptions) => Promise<void>;
46
- };
59
+ }
60
+ export interface ResolvedBuildAdapter {
61
+ target: string;
62
+ adapter: AdapterDriver;
63
+ mode: AdapterResolutionMode;
64
+ }
65
+ export type ZenithAdapter = AdapterDriver;
@@ -7,62 +7,3 @@ export const KNOWN_TARGETS = [
7
7
  'netlify',
8
8
  'node'
9
9
  ];
10
- /**
11
- * @typedef {'static' | 'static-export' | 'vercel-static' | 'netlify-static' | 'vercel' | 'netlify' | 'node'} ZenithTarget
12
- */
13
- /**
14
- * @typedef {'prerender' | 'server'} ZenithRenderMode
15
- */
16
- /**
17
- * @typedef {'static' | 'dynamic'} ZenithPathKind
18
- */
19
- /**
20
- * @typedef {{
21
- * path: string,
22
- * file: string,
23
- * path_kind: ZenithPathKind,
24
- * render_mode: ZenithRenderMode,
25
- * params: string[],
26
- * export_paths?: string[]
27
- * }} RouteManifestEntry
28
- */
29
- /**
30
- * @typedef {{
31
- * schema_version: number,
32
- * zenith_version: string,
33
- * target: string,
34
- * base_path: string,
35
- * content_hash: string,
36
- * routes: Array<{
37
- * path: string,
38
- * file: string,
39
- * path_kind: ZenithPathKind,
40
- * render_mode: ZenithRenderMode,
41
- * requires_hydration: boolean,
42
- * params: string[],
43
- * export_paths?: string[],
44
- * html: string,
45
- * assets: string[]
46
- * }>,
47
- * assets: {
48
- * js: string[],
49
- * css: string[],
50
- * vendor: string | null
51
- * }
52
- * }} BuildManifest
53
- */
54
- /**
55
- * @typedef {{
56
- * coreOutput: string,
57
- * outDir: string,
58
- * manifest: BuildManifest,
59
- * config: object
60
- * }} AdaptOptions
61
- */
62
- /**
63
- * @typedef {{
64
- * name: string,
65
- * validateRoutes: (manifest: RouteManifestEntry[]) => void,
66
- * adapt: (options: AdaptOptions) => Promise<void>
67
- * }} ZenithAdapter
68
- */
@@ -1,5 +1,2 @@
1
- export namespace vercelStaticAdapter {
2
- let name: string;
3
- function validateRoutes(manifest: any): void;
4
- function adapt(options: any): Promise<void>;
5
- }
1
+ import type { AdapterDriver } from './adapter-types.js';
2
+ export declare const vercelStaticAdapter: AdapterDriver;
@@ -1,5 +1,2 @@
1
- export namespace vercelAdapter {
2
- let name: string;
3
- function validateRoutes(manifest: any): void;
4
- function adapt(options: any): Promise<void>;
5
- }
1
+ import type { AdapterDriver } from './adapter-types.js';
2
+ export declare const vercelAdapter: AdapterDriver;
@@ -1,7 +1,7 @@
1
1
  import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
- import { basename, join } from 'node:path';
2
+ import { join } from 'node:path';
3
3
  import { compareRouteSpecificity } from '../server/resolve-request-route.js';
4
- import { copyHostedPageRuntime } from './copy-hosted-page-runtime.js';
4
+ import { copyHostedGlobalMiddlewareRuntime, copyHostedPageRuntime } from './copy-hosted-page-runtime.js';
5
5
  import { createVercelBasePathAssetRoutes, createVercelImageEndpointRoute, createVercelRouteSource } from './route-rules.js';
6
6
  import { validateHostedResourceRoutes } from './validate-hosted-resource-routes.js';
7
7
  function buildVercelServerDest(route) {
@@ -53,13 +53,17 @@ function createImageFunctionSource(imagesConfig) {
53
53
  ''
54
54
  ].join('\n');
55
55
  }
56
- function createFunctionSource(route) {
56
+ function createFunctionSource(route, globalMiddlewareModulePath) {
57
+ const globalMiddlewarePathExpression = globalMiddlewareModulePath
58
+ ? "join(__dirname, 'global-middleware', 'entry.js')"
59
+ : 'null';
57
60
  return [
58
61
  "import { fileURLToPath } from 'node:url';",
59
62
  "import { dirname, join } from 'node:path';",
60
63
  "import { renderResourceRouteRequest, renderRouteRequest, extractInternalParams } from './runtime/route-render.js';",
61
64
  '',
62
65
  'const __dirname = dirname(fileURLToPath(import.meta.url));',
66
+ `const globalMiddlewareModulePath = ${globalMiddlewarePathExpression};`,
63
67
  `const route = ${JSON.stringify(route, null, 2)};`,
64
68
  '',
65
69
  'function createHostedUnsupportedResponse(message) {',
@@ -74,7 +78,8 @@ function createFunctionSource(route) {
74
78
  ' request,',
75
79
  ' route,',
76
80
  ' params,',
77
- ` routeModulePath: join(__dirname, 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js')`,
81
+ ` routeModulePath: join(__dirname, 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js'),`,
82
+ ' globalMiddlewareModulePath',
78
83
  ' });',
79
84
  " if (response.headers.has('content-disposition')) {",
80
85
  " return createHostedUnsupportedResponse('Hosted resource downloads are unsupported in this milestone');",
@@ -86,6 +91,7 @@ function createFunctionSource(route) {
86
91
  ' route,',
87
92
  ' params,',
88
93
  ` routeModulePath: join(__dirname, 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js'),`,
94
+ ' globalMiddlewareModulePath,',
89
95
  ` shellHtmlPath: join(__dirname, 'routes', ${JSON.stringify(route.name)}, 'route', 'page.html'),`,
90
96
  ` pageAssetPath: ${route.page_asset_file ? "join(__dirname, 'routes', " + JSON.stringify(route.name) + ", 'route', " + JSON.stringify(route.page_asset_file) + ')' : 'null'},`,
91
97
  ` imageManifestPath: ${route.image_manifest_file ? "join(__dirname, 'routes', " + JSON.stringify(route.name) + ", 'route', " + JSON.stringify(route.image_manifest_file) + ')' : 'null'},`,
@@ -96,6 +102,12 @@ function createFunctionSource(route) {
96
102
  ''
97
103
  ].join('\n');
98
104
  }
105
+ function hasHostedScopedServerData(route) {
106
+ return route.route_kind !== 'resource' &&
107
+ route.has_scoped_server_data === true &&
108
+ Array.isArray(route.scoped_server_data) &&
109
+ route.scoped_server_data.length > 0;
110
+ }
99
111
  async function loadServerManifest(coreOutput) {
100
112
  try {
101
113
  const parsed = JSON.parse(await readFile(join(coreOutput, 'server', 'manifest.json'), 'utf8'));
@@ -137,10 +149,13 @@ export const vercelAdapter = {
137
149
  for (const route of serverRoutes) {
138
150
  const functionDir = join(options.outDir, 'functions', '__zenith', `${route.name}.func`);
139
151
  await mkdir(functionDir, { recursive: true });
140
- await copyHostedPageRuntime(options.coreOutput, functionDir);
152
+ await copyHostedPageRuntime(options.coreOutput, functionDir, {
153
+ includeScopedServerData: hasHostedScopedServerData(route)
154
+ });
155
+ const globalMiddlewareModulePath = await copyHostedGlobalMiddlewareRuntime(options.coreOutput, functionDir);
141
156
  await cp(join(options.coreOutput, 'server', 'routes', route.name), join(functionDir, 'routes', route.name), { recursive: true, force: true });
142
157
  await writeFile(join(functionDir, 'package.json'), '{\n "type": "module"\n}\n', 'utf8');
143
- await writeFile(join(functionDir, 'index.js'), createFunctionSource(route), 'utf8');
158
+ await writeFile(join(functionDir, 'index.js'), createFunctionSource(route, globalMiddlewareModulePath), 'utf8');
144
159
  await writeFile(join(functionDir, '.vc-config.json'), vercelFunctionConfig(), 'utf8');
145
160
  }
146
161
  await writeFile(join(options.outDir, 'config.json'), `${JSON.stringify(buildVercelConfig(options.manifest, serverRoutes), null, 2)}\n`, 'utf8');
@@ -1 +1,2 @@
1
- export function copyHostedPageRuntime(coreOutput: any, targetDir: any): Promise<void>;
1
+ export function copyHostedPageRuntime(coreOutput: any, targetDir: any, options?: {}): Promise<void>;
2
+ export function copyHostedGlobalMiddlewareRuntime(coreOutput: any, targetDir: any): Promise<string | null>;
@@ -1,6 +1,6 @@
1
- import { cp, mkdir, readFile, writeFile } from 'node:fs/promises';
1
+ import { cp, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
2
2
  import { createRequire } from 'node:module';
3
- import { join } from 'node:path';
3
+ import { isAbsolute, join, normalize } from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
5
  const PACKAGE_REQUIRE = createRequire(import.meta.url);
6
6
  const HOSTED_PAGE_RUNTIME_DIRS = ['runtime', 'images', 'auth', 'server-contract'];
@@ -12,6 +12,10 @@ const HOSTED_PAGE_RUNTIME_FILES = [
12
12
  'resource-response.js',
13
13
  'download-result.js'
14
14
  ];
15
+ const INVALID_GLOBAL_MIDDLEWARE_MODULE_PATH_ERROR = '[Zenith:Middleware] Invalid global middleware module path in server manifest.';
16
+ const MISSING_GLOBAL_MIDDLEWARE_RUNTIME_ERROR = '[Zenith:Middleware] Compiled global middleware runtime is missing from server output.';
17
+ const MISSING_SCOPED_RUNTIME_ERROR = '[Zenith:ScopedServerData] Compiled scoped server data runtime is missing from server output.';
18
+ const MISSING_SCOPED_MODULES_ERROR = '[Zenith:ScopedServerData] Compiled scoped server data modules are missing from server output.';
15
19
  function createSharpRuntimeSource() {
16
20
  const sharpPath = PACKAGE_REQUIRE.resolve('sharp');
17
21
  const fallbackUrl = pathToFileURL(sharpPath).href;
@@ -31,7 +35,48 @@ function createSharpRuntimeSource() {
31
35
  ''
32
36
  ].join('\n');
33
37
  }
34
- export async function copyHostedPageRuntime(coreOutput, targetDir) {
38
+ async function readServerManifest(coreOutput) {
39
+ try {
40
+ return JSON.parse(await readFile(join(coreOutput, 'server', 'manifest.json'), 'utf8'));
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ function normalizeGlobalMiddlewareModulePath(modulePath) {
47
+ if (typeof modulePath !== 'string' || modulePath.length === 0 || isAbsolute(modulePath) || /^[A-Za-z]:[\\/]/.test(modulePath)) {
48
+ throw new Error(INVALID_GLOBAL_MIDDLEWARE_MODULE_PATH_ERROR);
49
+ }
50
+ if (modulePath.split(/[\\/]+/).includes('..')) {
51
+ throw new Error(INVALID_GLOBAL_MIDDLEWARE_MODULE_PATH_ERROR);
52
+ }
53
+ const normalized = normalize(modulePath).replaceAll('\\', '/');
54
+ if (normalized === '.' ||
55
+ normalized.startsWith('../') ||
56
+ normalized.includes('/../') ||
57
+ !normalized.startsWith('global-middleware/')) {
58
+ throw new Error(INVALID_GLOBAL_MIDDLEWARE_MODULE_PATH_ERROR);
59
+ }
60
+ return normalized;
61
+ }
62
+ async function assertPathExists(filePath, message) {
63
+ try {
64
+ await stat(filePath);
65
+ }
66
+ catch {
67
+ throw new Error(message);
68
+ }
69
+ }
70
+ async function copyHostedScopedServerDataRuntime(coreOutput, targetDir) {
71
+ const serverDir = join(coreOutput, 'server');
72
+ const runtimeRoot = join(serverDir, 'scoped-server-data');
73
+ const scopedRoot = join(serverDir, 'scoped');
74
+ await assertPathExists(runtimeRoot, MISSING_SCOPED_RUNTIME_ERROR);
75
+ await assertPathExists(scopedRoot, MISSING_SCOPED_MODULES_ERROR);
76
+ await cp(runtimeRoot, join(targetDir, 'scoped-server-data'), { recursive: true, force: true });
77
+ await cp(scopedRoot, join(targetDir, 'scoped'), { recursive: true, force: true });
78
+ }
79
+ export async function copyHostedPageRuntime(coreOutput, targetDir, options = {}) {
35
80
  const serverDir = join(coreOutput, 'server');
36
81
  await mkdir(targetDir, { recursive: true });
37
82
  for (const name of HOSTED_PAGE_RUNTIME_DIRS) {
@@ -47,4 +92,24 @@ export async function copyHostedPageRuntime(coreOutput, targetDir) {
47
92
  const imageServiceSource = await readFile(imageServicePath, 'utf8');
48
93
  await writeFile(imageServicePath, imageServiceSource.replace("import sharp from 'sharp';", "import sharp from './sharp-runtime.js';"), 'utf8');
49
94
  await writeFile(join(targetDir, 'images', 'sharp-runtime.js'), createSharpRuntimeSource(), 'utf8');
95
+ if (options.includeScopedServerData === true) {
96
+ await copyHostedScopedServerDataRuntime(coreOutput, targetDir);
97
+ }
98
+ }
99
+ export async function copyHostedGlobalMiddlewareRuntime(coreOutput, targetDir) {
100
+ const manifest = await readServerManifest(coreOutput);
101
+ const modulePath = manifest?.global_middleware?.module;
102
+ if (modulePath == null) {
103
+ return null;
104
+ }
105
+ const normalizedModulePath = normalizeGlobalMiddlewareModulePath(modulePath);
106
+ const serverDir = join(coreOutput, 'server');
107
+ const middlewareRoot = join(serverDir, 'global-middleware');
108
+ await assertPathExists(middlewareRoot, MISSING_GLOBAL_MIDDLEWARE_RUNTIME_ERROR);
109
+ await assertPathExists(join(serverDir, normalizedModulePath), MISSING_GLOBAL_MIDDLEWARE_RUNTIME_ERROR);
110
+ await cp(middlewareRoot, join(targetDir, 'global-middleware'), {
111
+ recursive: true,
112
+ force: true
113
+ });
114
+ return normalizedModulePath;
50
115
  }
@@ -1,5 +1,7 @@
1
- export function resolveBuildAdapter(config?: {}): {
2
- target: any;
3
- adapter: any;
4
- mode: string;
1
+ import type { ResolvedBuildAdapter } from './adapter-types.js';
2
+ type BuildAdapterConfig = {
3
+ target?: unknown;
4
+ adapter?: unknown;
5
5
  };
6
+ export declare function resolveBuildAdapter(config?: BuildAdapterConfig): ResolvedBuildAdapter;
7
+ export {};
@@ -91,6 +91,9 @@ export function runCompiler(filePath, stdinSource, compilerOpts = {}, compilerRu
91
91
  if (compilerOpts?.strictDomLints) {
92
92
  args.push('--strict-dom-lints');
93
93
  }
94
+ if (compilerOpts?.internalAllowUnboundMarkup) {
95
+ args.push('--internal-allow-unbound-markup');
96
+ }
94
97
  const opts = {
95
98
  encoding: 'utf8',
96
99
  maxBuffer: COMPILER_SPAWN_MAX_BUFFER
@@ -9,7 +9,8 @@
9
9
  * signal_indices: number[],
10
10
  * state_index: number | null,
11
11
  * component_instance: string | null,
12
- * component_binding: string | null
12
+ * component_binding: string | null,
13
+ * scoped_data_key: string | null
13
14
  * }>,
14
15
  * signals: Array<{ id?: number, kind?: string, state_index?: number }>,
15
16
  * stateBindings: Array<{ key?: string, value?: string }>,
@@ -26,6 +27,7 @@ export function buildComponentExpressionRewrite(compIr: object, rewriteMetrics?:
26
27
  state_index: number | null;
27
28
  component_instance: string | null;
28
29
  component_binding: string | null;
30
+ scoped_data_key: string | null;
29
31
  }>;
30
32
  signals: Array<{
31
33
  id?: number;
@@ -11,7 +11,8 @@ import { rewriteCompilerSignalMapReferences } from './compiler-signal-expression
11
11
  * signal_indices: number[],
12
12
  * state_index: number | null,
13
13
  * component_instance: string | null,
14
- * component_binding: string | null
14
+ * component_binding: string | null,
15
+ * scoped_data_key: string | null
15
16
  * }>,
16
17
  * signals: Array<{ id?: number, kind?: string, state_index?: number }>,
17
18
  * stateBindings: Array<{ key?: string, value?: string }>,
@@ -60,6 +61,11 @@ export function buildComponentExpressionRewrite(compIr, rewriteMetrics = null) {
60
61
  component_binding: typeof binding.component_binding === 'string' ? binding.component_binding : null
61
62
  }
62
63
  : null;
64
+ if (normalizedBinding &&
65
+ typeof binding.scoped_data_key === 'string' &&
66
+ binding.scoped_data_key.length > 0) {
67
+ normalizedBinding.scoped_data_key = binding.scoped_data_key;
68
+ }
63
69
  out.sequence.push({ raw, rewritten, binding: normalizedBinding });
64
70
  if (!out.ambiguous.has(raw) && normalizedBinding) {
65
71
  const existingBinding = out.bindings.get(raw);
@@ -197,7 +203,10 @@ export function resolveRewrittenBindingMetadata(pageBindingContext, componentRew
197
203
  signal_indices: [],
198
204
  state_index: null,
199
205
  component_instance: typeof binding.component_instance === 'string' ? binding.component_instance : null,
200
- component_binding: typeof binding.component_binding === 'string' ? binding.component_binding : null
206
+ component_binding: typeof binding.component_binding === 'string' ? binding.component_binding : null,
207
+ scoped_data_key: typeof binding.scoped_data_key === 'string' && binding.scoped_data_key.length > 0
208
+ ? binding.scoped_data_key
209
+ : null
201
210
  };
202
211
  if (typeof next.compiled_expr === 'string' && next.compiled_expr.includes('signalMap.get(')) {
203
212
  const remapStartedAt = performance.now();
@@ -334,6 +343,9 @@ function measureBindingSpecificity(binding, raw) {
334
343
  if (typeof binding.component_binding === 'string' && binding.component_binding.length > 0) {
335
344
  score += 1;
336
345
  }
346
+ if (typeof binding.scoped_data_key === 'string' && binding.scoped_data_key.length > 0) {
347
+ score += 1;
348
+ }
337
349
  return score;
338
350
  }
339
351
  /**
@@ -29,6 +29,7 @@ export function buildPageOwnerContext({ componentOccurrences, sourceFile, pageOw
29
29
  state_index: number | null;
30
30
  component_instance: string | null;
31
31
  component_binding: string | null;
32
+ scoped_data_key: string | null;
32
33
  }>;
33
34
  signals: Array<{
34
35
  id?: number;