@zenithbuild/cli 0.6.6 → 0.6.9

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 (43) hide show
  1. package/dist/build.d.ts +32 -0
  2. package/dist/build.js +193 -548
  3. package/dist/compiler-bridge-runner.d.ts +5 -0
  4. package/dist/compiler-bridge-runner.js +70 -0
  5. package/dist/component-instance-ir.d.ts +6 -0
  6. package/dist/component-instance-ir.js +0 -20
  7. package/dist/component-occurrences.d.ts +6 -0
  8. package/dist/component-occurrences.js +6 -28
  9. package/dist/dev-server.d.ts +18 -0
  10. package/dist/dev-server.js +65 -114
  11. package/dist/dev-watch.d.ts +1 -0
  12. package/dist/dev-watch.js +2 -2
  13. package/dist/index.d.ts +8 -0
  14. package/dist/index.js +6 -28
  15. package/dist/manifest.d.ts +23 -0
  16. package/dist/manifest.js +22 -48
  17. package/dist/preview.d.ts +100 -0
  18. package/dist/preview.js +418 -488
  19. package/dist/resolve-components.d.ts +39 -0
  20. package/dist/resolve-components.js +30 -104
  21. package/dist/server/resolve-request-route.d.ts +39 -0
  22. package/dist/server/resolve-request-route.js +104 -113
  23. package/dist/server-contract.d.ts +39 -0
  24. package/dist/server-contract.js +15 -67
  25. package/dist/toolchain-paths.d.ts +23 -0
  26. package/dist/toolchain-paths.js +139 -39
  27. package/dist/toolchain-runner.d.ts +33 -0
  28. package/dist/toolchain-runner.js +194 -0
  29. package/dist/types/generate-env-dts.d.ts +5 -0
  30. package/dist/types/generate-env-dts.js +4 -2
  31. package/dist/types/generate-routes-dts.d.ts +8 -0
  32. package/dist/types/generate-routes-dts.js +7 -5
  33. package/dist/types/index.d.ts +14 -0
  34. package/dist/types/index.js +16 -7
  35. package/dist/ui/env.d.ts +18 -0
  36. package/dist/ui/env.js +0 -12
  37. package/dist/ui/format.d.ts +33 -0
  38. package/dist/ui/format.js +8 -46
  39. package/dist/ui/logger.d.ts +59 -0
  40. package/dist/ui/logger.js +3 -32
  41. package/dist/version-check.d.ts +54 -0
  42. package/dist/version-check.js +41 -98
  43. package/package.json +6 -4
@@ -8,100 +8,89 @@
8
8
  * @returns {number}
9
9
  */
10
10
  export function compareRouteSpecificity(a, b) {
11
- if (a === '/' && b !== '/') return -1;
12
- if (b === '/' && a !== '/') return 1;
13
-
14
- const aSegs = splitPath(a);
15
- const bSegs = splitPath(b);
16
- const aClass = routeClass(aSegs);
17
- const bClass = routeClass(bSegs);
18
- if (aClass !== bClass) {
19
- return bClass - aClass;
20
- }
21
-
22
- const max = Math.min(aSegs.length, bSegs.length);
23
- for (let i = 0; i < max; i++) {
24
- const aWeight = segmentWeight(aSegs[i]);
25
- const bWeight = segmentWeight(bSegs[i]);
26
- if (aWeight !== bWeight) {
27
- return bWeight - aWeight;
11
+ if (a === '/' && b !== '/')
12
+ return -1;
13
+ if (b === '/' && a !== '/')
14
+ return 1;
15
+ const aSegs = splitPath(a);
16
+ const bSegs = splitPath(b);
17
+ const aClass = routeClass(aSegs);
18
+ const bClass = routeClass(bSegs);
19
+ if (aClass !== bClass) {
20
+ return bClass - aClass;
28
21
  }
29
- }
30
-
31
- if (aSegs.length !== bSegs.length) {
32
- return bSegs.length - aSegs.length;
33
- }
34
-
35
- return a.localeCompare(b);
22
+ const max = Math.min(aSegs.length, bSegs.length);
23
+ for (let i = 0; i < max; i++) {
24
+ const aWeight = segmentWeight(aSegs[i]);
25
+ const bWeight = segmentWeight(bSegs[i]);
26
+ if (aWeight !== bWeight) {
27
+ return bWeight - aWeight;
28
+ }
29
+ }
30
+ if (aSegs.length !== bSegs.length) {
31
+ return bSegs.length - aSegs.length;
32
+ }
33
+ return a.localeCompare(b);
36
34
  }
37
-
38
35
  /**
39
36
  * @param {string} pathname
40
37
  * @param {Array<{ path: string }>} routes
41
38
  * @returns {{ entry: { path: string }, params: Record<string, string> } | null}
42
39
  */
43
40
  export function matchRoute(pathname, routes) {
44
- const target = splitPath(pathname);
45
- const ordered = [...routes].sort((a, b) => compareRouteSpecificity(a.path, b.path));
46
- for (const entry of ordered) {
47
- const pattern = splitPath(entry.path);
48
- const params = Object.create(null);
49
- let patternIndex = 0;
50
- let valueIndex = 0;
51
- let matched = true;
52
-
53
- while (patternIndex < pattern.length) {
54
- const segment = pattern[patternIndex];
55
- if (segment.startsWith('*')) {
56
- const optionalCatchAll = segment.endsWith('?');
57
- const key = optionalCatchAll ? segment.slice(1, -1) : segment.slice(1);
58
- if (patternIndex !== pattern.length - 1) {
59
- matched = false;
60
- break;
41
+ const target = splitPath(pathname);
42
+ const ordered = [...routes].sort((a, b) => compareRouteSpecificity(a.path, b.path));
43
+ for (const entry of ordered) {
44
+ const pattern = splitPath(entry.path);
45
+ const params = Object.create(null);
46
+ let patternIndex = 0;
47
+ let valueIndex = 0;
48
+ let matched = true;
49
+ while (patternIndex < pattern.length) {
50
+ const segment = pattern[patternIndex];
51
+ if (segment.startsWith('*')) {
52
+ const optionalCatchAll = segment.endsWith('?');
53
+ const key = optionalCatchAll ? segment.slice(1, -1) : segment.slice(1);
54
+ if (patternIndex !== pattern.length - 1) {
55
+ matched = false;
56
+ break;
57
+ }
58
+ const rest = target.slice(valueIndex);
59
+ const rootRequiredCatchAll = !optionalCatchAll && pattern.length === 1;
60
+ if (rest.length === 0 && !optionalCatchAll && !rootRequiredCatchAll) {
61
+ matched = false;
62
+ break;
63
+ }
64
+ params[key] = normalizeCatchAll(rest);
65
+ valueIndex = target.length;
66
+ patternIndex = pattern.length;
67
+ break;
68
+ }
69
+ if (valueIndex >= target.length) {
70
+ matched = false;
71
+ break;
72
+ }
73
+ const value = target[valueIndex];
74
+ if (segment.startsWith(':')) {
75
+ params[segment.slice(1)] = value;
76
+ }
77
+ else if (segment !== value) {
78
+ matched = false;
79
+ break;
80
+ }
81
+ patternIndex += 1;
82
+ valueIndex += 1;
61
83
  }
62
- const rest = target.slice(valueIndex);
63
- const rootRequiredCatchAll = !optionalCatchAll && pattern.length === 1;
64
- if (rest.length === 0 && !optionalCatchAll && !rootRequiredCatchAll) {
65
- matched = false;
66
- break;
84
+ if (!matched) {
85
+ continue;
67
86
  }
68
- params[key] = normalizeCatchAll(rest);
69
- valueIndex = target.length;
70
- patternIndex = pattern.length;
71
- break;
72
- }
73
-
74
- if (valueIndex >= target.length) {
75
- matched = false;
76
- break;
77
- }
78
-
79
- const value = target[valueIndex];
80
- if (segment.startsWith(':')) {
81
- params[segment.slice(1)] = value;
82
- } else if (segment !== value) {
83
- matched = false;
84
- break;
85
- }
86
-
87
- patternIndex += 1;
88
- valueIndex += 1;
89
- }
90
-
91
- if (!matched) {
92
- continue;
93
- }
94
-
95
- if (valueIndex !== target.length || patternIndex !== pattern.length) {
96
- continue;
87
+ if (valueIndex !== target.length || patternIndex !== pattern.length) {
88
+ continue;
89
+ }
90
+ return { entry, params: { ...params } };
97
91
  }
98
-
99
- return { entry, params: { ...params } };
100
- }
101
-
102
- return null;
92
+ return null;
103
93
  }
104
-
105
94
  /**
106
95
  * Resolve an incoming request URL against a manifest route list.
107
96
  *
@@ -110,60 +99,62 @@ export function matchRoute(pathname, routes) {
110
99
  * @returns {{ matched: boolean, route: { path: string } | null, params: Record<string, string> }}
111
100
  */
112
101
  export function resolveRequestRoute(reqUrl, manifest) {
113
- const url = reqUrl instanceof URL ? reqUrl : new URL(String(reqUrl), 'http://localhost');
114
- const matched = matchRoute(url.pathname, manifest);
115
- if (!matched) {
116
- return { matched: false, route: null, params: {} };
117
- }
118
- return {
119
- matched: true,
120
- route: matched.entry,
121
- params: matched.params
122
- };
102
+ const url = reqUrl instanceof URL ? reqUrl : new URL(String(reqUrl), 'http://localhost');
103
+ const matched = matchRoute(url.pathname, manifest);
104
+ if (!matched) {
105
+ return { matched: false, route: null, params: {} };
106
+ }
107
+ return {
108
+ matched: true,
109
+ route: matched.entry,
110
+ params: matched.params
111
+ };
123
112
  }
124
-
125
113
  /**
126
114
  * @param {string[]} segments
127
115
  * @returns {number}
128
116
  */
129
117
  function routeClass(segments) {
130
- let hasParam = false;
131
- let hasCatchAll = false;
132
- for (const segment of segments) {
133
- if (segment.startsWith('*')) {
134
- hasCatchAll = true;
135
- } else if (segment.startsWith(':')) {
136
- hasParam = true;
118
+ let hasParam = false;
119
+ let hasCatchAll = false;
120
+ for (const segment of segments) {
121
+ if (segment.startsWith('*')) {
122
+ hasCatchAll = true;
123
+ }
124
+ else if (segment.startsWith(':')) {
125
+ hasParam = true;
126
+ }
137
127
  }
138
- }
139
- if (!hasParam && !hasCatchAll) return 3;
140
- if (hasCatchAll) return 1;
141
- return 2;
128
+ if (!hasParam && !hasCatchAll)
129
+ return 3;
130
+ if (hasCatchAll)
131
+ return 1;
132
+ return 2;
142
133
  }
143
-
144
134
  /**
145
135
  * @param {string | undefined} segment
146
136
  * @returns {number}
147
137
  */
148
138
  function segmentWeight(segment) {
149
- if (!segment) return 0;
150
- if (segment.startsWith('*')) return 1;
151
- if (segment.startsWith(':')) return 2;
152
- return 3;
139
+ if (!segment)
140
+ return 0;
141
+ if (segment.startsWith('*'))
142
+ return 1;
143
+ if (segment.startsWith(':'))
144
+ return 2;
145
+ return 3;
153
146
  }
154
-
155
147
  /**
156
148
  * @param {string} pathname
157
149
  * @returns {string[]}
158
150
  */
159
151
  function splitPath(pathname) {
160
- return pathname.split('/').filter(Boolean);
152
+ return pathname.split('/').filter(Boolean);
161
153
  }
162
-
163
154
  /**
164
155
  * @param {string[]} segments
165
156
  * @returns {string}
166
157
  */
167
158
  function normalizeCatchAll(segments) {
168
- return segments.filter(Boolean).join('/');
159
+ return segments.filter(Boolean).join('/');
169
160
  }
@@ -0,0 +1,39 @@
1
+ export function allow(): {
2
+ kind: string;
3
+ };
4
+ export function redirect(location: any, status?: number): {
5
+ kind: string;
6
+ location: string;
7
+ status: number;
8
+ };
9
+ export function deny(status?: number, message?: undefined): {
10
+ kind: string;
11
+ status: number;
12
+ message: undefined;
13
+ };
14
+ export function data(payload: any): {
15
+ kind: string;
16
+ data: any;
17
+ };
18
+ export function validateServerExports({ exports, filePath }: {
19
+ exports: any;
20
+ filePath: any;
21
+ }): void;
22
+ export function assertJsonSerializable(value: any, where?: string): void;
23
+ export function resolveRouteResult({ exports, ctx, filePath, guardOnly }: {
24
+ exports: any;
25
+ ctx: any;
26
+ filePath: any;
27
+ guardOnly?: boolean | undefined;
28
+ }): Promise<{
29
+ result: any;
30
+ trace: {
31
+ guard: string;
32
+ load: string;
33
+ };
34
+ }>;
35
+ export function resolveServerPayload({ exports, ctx, filePath }: {
36
+ exports: any;
37
+ ctx: any;
38
+ filePath: any;
39
+ }): Promise<any>;
@@ -1,17 +1,13 @@
1
1
  // server-contract.js — Zenith CLI V0
2
2
  // ---------------------------------------------------------------------------
3
3
  // Shared validation and payload resolution logic for <script server> blocks.
4
-
5
4
  const NEW_KEYS = new Set(['data', 'load', 'guard', 'prerender']);
6
5
  const LEGACY_KEYS = new Set(['ssr_data', 'props', 'ssr', 'prerender']);
7
6
  const ALLOWED_KEYS = new Set(['data', 'load', 'guard', 'prerender', 'ssr_data', 'props', 'ssr']);
8
-
9
7
  const ROUTE_RESULT_KINDS = new Set(['allow', 'redirect', 'deny', 'data']);
10
-
11
8
  export function allow() {
12
9
  return { kind: 'allow' };
13
10
  }
14
-
15
11
  export function redirect(location, status = 302) {
16
12
  return {
17
13
  kind: 'redirect',
@@ -19,7 +15,6 @@ export function redirect(location, status = 302) {
19
15
  status: Number.isInteger(status) ? status : 302
20
16
  };
21
17
  }
22
-
23
18
  export function deny(status = 403, message = undefined) {
24
19
  return {
25
20
  kind: 'deny',
@@ -27,11 +22,9 @@ export function deny(status = 403, message = undefined) {
27
22
  message: typeof message === 'string' ? message : undefined
28
23
  };
29
24
  }
30
-
31
25
  export function data(payload) {
32
26
  return { kind: 'data', data: payload };
33
27
  }
34
-
35
28
  function isRouteResultLike(value) {
36
29
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
37
30
  return false;
@@ -39,18 +32,14 @@ function isRouteResultLike(value) {
39
32
  const kind = value.kind;
40
33
  return typeof kind === 'string' && ROUTE_RESULT_KINDS.has(kind);
41
34
  }
42
-
43
35
  function assertValidRouteResultShape(value, where, allowedKinds) {
44
36
  if (!isRouteResultLike(value)) {
45
37
  throw new Error(`[Zenith] ${where}: invalid route result. Expected object with kind.`);
46
38
  }
47
39
  const kind = value.kind;
48
40
  if (!allowedKinds.has(kind)) {
49
- throw new Error(
50
- `[Zenith] ${where}: kind "${kind}" is not allowed here (allowed: ${Array.from(allowedKinds).join(', ')}).`
51
- );
41
+ throw new Error(`[Zenith] ${where}: kind "${kind}" is not allowed here (allowed: ${Array.from(allowedKinds).join(', ')}).`);
52
42
  }
53
-
54
43
  if (kind === 'redirect') {
55
44
  if (typeof value.location !== 'string' || value.location.length === 0) {
56
45
  throw new Error(`[Zenith] ${where}: redirect requires non-empty string location.`);
@@ -59,7 +48,6 @@ function assertValidRouteResultShape(value, where, allowedKinds) {
59
48
  throw new Error(`[Zenith] ${where}: redirect status must be an integer 3xx.`);
60
49
  }
61
50
  }
62
-
63
51
  if (kind === 'deny') {
64
52
  if (!Number.isInteger(value.status) || (value.status !== 401 && value.status !== 403)) {
65
53
  throw new Error(`[Zenith] ${where}: deny status must be 401 or 403.`);
@@ -69,36 +57,26 @@ function assertValidRouteResultShape(value, where, allowedKinds) {
69
57
  }
70
58
  }
71
59
  }
72
-
73
60
  export function validateServerExports({ exports, filePath }) {
74
61
  const exportKeys = Object.keys(exports);
75
62
  const illegalKeys = exportKeys.filter(k => !ALLOWED_KEYS.has(k));
76
-
77
63
  if (illegalKeys.length > 0) {
78
64
  throw new Error(`[Zenith] ${filePath}: illegal export(s): ${illegalKeys.join(', ')}`);
79
65
  }
80
-
81
66
  const hasData = 'data' in exports;
82
67
  const hasLoad = 'load' in exports;
83
68
  const hasGuard = 'guard' in exports;
84
-
85
69
  const hasNew = hasData || hasLoad;
86
70
  const hasLegacy = ('ssr_data' in exports) || ('props' in exports) || ('ssr' in exports);
87
-
88
71
  if (hasData && hasLoad) {
89
72
  throw new Error(`[Zenith] ${filePath}: cannot export both "data" and "load". Choose one.`);
90
73
  }
91
-
92
74
  if (hasNew && hasLegacy) {
93
- throw new Error(
94
- `[Zenith] ${filePath}: cannot mix new ("data"/"load") with legacy ("ssr_data"/"props"/"ssr") exports.`
95
- );
75
+ throw new Error(`[Zenith] ${filePath}: cannot mix new ("data"/"load") with legacy ("ssr_data"/"props"/"ssr") exports.`);
96
76
  }
97
-
98
77
  if ('prerender' in exports && typeof exports.prerender !== 'boolean') {
99
78
  throw new Error(`[Zenith] ${filePath}: "prerender" must be a boolean.`);
100
79
  }
101
-
102
80
  if (hasLoad && typeof exports.load !== 'function') {
103
81
  throw new Error(`[Zenith] ${filePath}: "load" must be a function.`);
104
82
  }
@@ -112,7 +90,6 @@ export function validateServerExports({ exports, filePath }) {
112
90
  throw new Error(`[Zenith] ${filePath}: "load(ctx)" must not contain rest parameters.`);
113
91
  }
114
92
  }
115
-
116
93
  if (hasGuard && typeof exports.guard !== 'function') {
117
94
  throw new Error(`[Zenith] ${filePath}: "guard" must be a function.`);
118
95
  }
@@ -127,53 +104,45 @@ export function validateServerExports({ exports, filePath }) {
127
104
  }
128
105
  }
129
106
  }
130
-
131
107
  export function assertJsonSerializable(value, where = 'payload') {
132
108
  const seen = new Set();
133
-
134
109
  function walk(v, path) {
135
110
  const t = typeof v;
136
-
137
- if (v === null) return;
138
- if (t === 'string' || t === 'number' || t === 'boolean') return;
139
-
111
+ if (v === null)
112
+ return;
113
+ if (t === 'string' || t === 'number' || t === 'boolean')
114
+ return;
140
115
  if (t === 'bigint' || t === 'function' || t === 'symbol') {
141
116
  throw new Error(`[Zenith] ${where}: non-serializable ${t} at ${path}`);
142
117
  }
143
-
144
118
  if (t === 'undefined') {
145
119
  throw new Error(`[Zenith] ${where}: undefined is not allowed at ${path}`);
146
120
  }
147
-
148
121
  if (v instanceof Date) {
149
122
  throw new Error(`[Zenith] ${where}: Date is not allowed at ${path} (convert to ISO string)`);
150
123
  }
151
-
152
124
  if (v instanceof Map || v instanceof Set) {
153
125
  throw new Error(`[Zenith] ${where}: Map/Set not allowed at ${path}`);
154
126
  }
155
-
156
127
  if (t === 'object') {
157
- if (seen.has(v)) throw new Error(`[Zenith] ${where}: circular reference at ${path}`);
128
+ if (seen.has(v))
129
+ throw new Error(`[Zenith] ${where}: circular reference at ${path}`);
158
130
  seen.add(v);
159
-
160
131
  if (Array.isArray(v)) {
161
132
  if (path === '$') {
162
133
  throw new Error(`[Zenith] ${where}: top-level payload must be a plain object, not an array at ${path}`);
163
134
  }
164
- for (let i = 0; i < v.length; i++) walk(v[i], `${path}[${i}]`);
135
+ for (let i = 0; i < v.length; i++)
136
+ walk(v[i], `${path}[${i}]`);
165
137
  return;
166
138
  }
167
-
168
139
  const proto = Object.getPrototypeOf(v);
169
140
  const isPlainObject = proto === null ||
170
141
  proto === Object.prototype ||
171
142
  (proto && proto.constructor && proto.constructor.name === 'Object');
172
-
173
143
  if (!isPlainObject) {
174
144
  throw new Error(`[Zenith] ${where}: non-plain object at ${path}`);
175
145
  }
176
-
177
146
  for (const k of Object.keys(v)) {
178
147
  if (k === '__proto__' || k === 'constructor' || k === 'prototype') {
179
148
  throw new Error(`[Zenith] ${where}: forbidden prototype pollution key "${k}" at ${path}.${k}`);
@@ -182,54 +151,40 @@ export function assertJsonSerializable(value, where = 'payload') {
182
151
  }
183
152
  return;
184
153
  }
185
-
186
154
  throw new Error(`[Zenith] ${where}: unsupported type at ${path}`);
187
155
  }
188
-
189
156
  walk(value, '$');
190
157
  }
191
-
192
158
  export async function resolveRouteResult({ exports, ctx, filePath, guardOnly = false }) {
193
159
  validateServerExports({ exports, filePath });
194
-
195
160
  const trace = {
196
161
  guard: 'none',
197
162
  load: 'none'
198
163
  };
199
-
200
164
  if ('guard' in exports) {
201
165
  const guardRaw = await exports.guard(ctx);
202
166
  const guardResult = guardRaw == null ? allow() : guardRaw;
203
167
  if (guardResult.kind === 'data') {
204
168
  throw new Error(`[Zenith] ${filePath}: guard(ctx) returned data(payload) which is a critical invariant violation. guard() can only return allow(), redirect(), or deny(). Use load(ctx) for data injection.`);
205
169
  }
206
- assertValidRouteResultShape(
207
- guardResult,
208
- `${filePath}: guard(ctx) return`,
209
- new Set(['allow', 'redirect', 'deny'])
210
- );
170
+ assertValidRouteResultShape(guardResult, `${filePath}: guard(ctx) return`, new Set(['allow', 'redirect', 'deny']));
211
171
  trace.guard = guardResult.kind;
212
172
  if (guardResult.kind === 'redirect' || guardResult.kind === 'deny') {
213
173
  return { result: guardResult, trace };
214
174
  }
215
175
  }
216
-
217
176
  if (guardOnly) {
218
177
  return { result: allow(), trace };
219
178
  }
220
-
221
179
  let payload;
222
180
  if ('load' in exports) {
223
181
  const loadRaw = await exports.load(ctx);
224
182
  let loadResult = null;
225
183
  if (isRouteResultLike(loadRaw)) {
226
184
  loadResult = loadRaw;
227
- assertValidRouteResultShape(
228
- loadResult,
229
- `${filePath}: load(ctx) return`,
230
- new Set(['data', 'redirect', 'deny'])
231
- );
232
- } else {
185
+ assertValidRouteResultShape(loadResult, `${filePath}: load(ctx) return`, new Set(['data', 'redirect', 'deny']));
186
+ }
187
+ else {
233
188
  assertJsonSerializable(loadRaw, `${filePath}: load(ctx) return`);
234
189
  loadResult = data(loadRaw);
235
190
  }
@@ -242,7 +197,6 @@ export async function resolveRouteResult({ exports, ctx, filePath, guardOnly = f
242
197
  trace.load = 'data';
243
198
  return { result: data(payload), trace };
244
199
  }
245
-
246
200
  // legacy fallback
247
201
  if ('ssr_data' in exports) {
248
202
  payload = exports.ssr_data;
@@ -262,24 +216,18 @@ export async function resolveRouteResult({ exports, ctx, filePath, guardOnly = f
262
216
  trace.load = 'data';
263
217
  return { result: data(payload), trace };
264
218
  }
265
-
266
219
  return { result: data({}), trace };
267
220
  }
268
-
269
221
  export async function resolveServerPayload({ exports, ctx, filePath }) {
270
222
  const resolved = await resolveRouteResult({ exports, ctx, filePath });
271
223
  if (!resolved || !resolved.result || typeof resolved.result !== 'object') {
272
224
  return {};
273
225
  }
274
-
275
226
  if (resolved.result.kind === 'data') {
276
227
  return resolved.result.data;
277
228
  }
278
229
  if (resolved.result.kind === 'allow') {
279
230
  return {};
280
231
  }
281
-
282
- throw new Error(
283
- `[Zenith] ${filePath}: resolveServerPayload() expected data but received ${resolved.result.kind}. Use resolveRouteResult() for guard/load flows.`
284
- );
232
+ throw new Error(`[Zenith] ${filePath}: resolveServerPayload() expected data but received ${resolved.result.kind}. Use resolveRouteResult() for guard/load flows.`);
285
233
  }
@@ -0,0 +1,23 @@
1
+ export type ToolchainTool = 'compiler' | 'bundler';
2
+ export type ToolchainMode = 'binary' | 'node-bridge';
3
+ export interface ToolchainCandidate {
4
+ tool: ToolchainTool;
5
+ mode: ToolchainMode;
6
+ source: string;
7
+ sourceKey: string;
8
+ label: string;
9
+ path: string;
10
+ command: string;
11
+ argsPrefix: string[];
12
+ explicit?: boolean;
13
+ }
14
+ export declare function resolveBinary(candidates: Array<string | ToolchainCandidate>): string;
15
+ export declare function resolvePackageRoot(packageName: string, projectRoot?: string | null): string | null;
16
+ export declare function readInstalledPackageVersion(packageName: string, projectRoot?: string | null): string | null;
17
+ export declare function readCliPackageVersion(): string;
18
+ export declare function compilerCommandCandidates(projectRoot?: string | null, env?: NodeJS.ProcessEnv): ToolchainCandidate[];
19
+ export declare function compilerBinCandidates(projectRoot?: string | null, env?: NodeJS.ProcessEnv): string[];
20
+ export declare function resolveCompilerBin(projectRoot?: string | null, env?: NodeJS.ProcessEnv): string;
21
+ export declare function bundlerCommandCandidates(projectRoot?: string | null, env?: NodeJS.ProcessEnv): ToolchainCandidate[];
22
+ export declare function resolveBundlerBin(projectRoot?: string | null, env?: NodeJS.ProcessEnv): string;
23
+ export declare function bundlerBinCandidates(projectRoot?: string | null, env?: NodeJS.ProcessEnv): string[];