@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
@@ -2,51 +2,115 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { dirname, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
-
6
5
  const __filename = fileURLToPath(import.meta.url);
7
6
  const __dirname = dirname(__filename);
8
7
  const CLI_ROOT = resolve(__dirname, '..');
9
8
  const localRequire = createRequire(import.meta.url);
10
-
9
+ const IS_WINDOWS = process.platform === 'win32';
10
+ const COMPILER_BRIDGE_RUNNER = resolve(__dirname, 'compiler-bridge-runner.js');
11
+ const BUNDLER_PLATFORM_PACKAGES = {
12
+ 'darwin-arm64': {
13
+ packageName: '@zenithbuild/bundler-darwin-arm64',
14
+ binaryName: 'zenith-bundler'
15
+ },
16
+ 'darwin-x64': {
17
+ packageName: '@zenithbuild/bundler-darwin-x64',
18
+ binaryName: 'zenith-bundler'
19
+ },
20
+ 'linux-x64': {
21
+ packageName: '@zenithbuild/bundler-linux-x64',
22
+ binaryName: 'zenith-bundler'
23
+ },
24
+ 'win32-x64': {
25
+ packageName: '@zenithbuild/bundler-win32-x64',
26
+ binaryName: 'zenith-bundler.exe'
27
+ }
28
+ };
11
29
  function safeCreateRequire(projectRoot) {
12
30
  if (!projectRoot) {
13
31
  return localRequire;
14
32
  }
15
33
  try {
16
34
  return createRequire(resolve(projectRoot, 'package.json'));
17
- } catch {
35
+ }
36
+ catch {
18
37
  return localRequire;
19
38
  }
20
39
  }
21
-
22
40
  function safeResolve(requireFn, specifier) {
23
41
  try {
24
42
  return requireFn.resolve(specifier);
25
- } catch {
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ function resolveExecutablePath(candidatePath) {
49
+ if (typeof candidatePath !== 'string' || candidatePath.length === 0) {
50
+ return '';
51
+ }
52
+ if (!IS_WINDOWS || candidatePath.toLowerCase().endsWith('.exe')) {
53
+ return candidatePath;
54
+ }
55
+ if (existsSync(candidatePath)) {
56
+ return candidatePath;
57
+ }
58
+ const exePath = `${candidatePath}.exe`;
59
+ return existsSync(exePath) ? exePath : candidatePath;
60
+ }
61
+ function createBinaryCandidate(tool, source, candidatePath) {
62
+ const resolvedPath = resolveExecutablePath(candidatePath);
63
+ return {
64
+ tool,
65
+ mode: 'binary',
66
+ source,
67
+ sourceKey: `${tool}:${source}:${resolvedPath}`,
68
+ label: source,
69
+ path: resolvedPath,
70
+ command: resolvedPath,
71
+ argsPrefix: []
72
+ };
73
+ }
74
+ function createCompilerBridgeCandidate(modulePath) {
75
+ if (typeof modulePath !== 'string' || modulePath.length === 0) {
26
76
  return null;
27
77
  }
78
+ return {
79
+ tool: 'compiler',
80
+ mode: 'node-bridge',
81
+ source: 'JS bridge',
82
+ sourceKey: `compiler:js-bridge:${modulePath}`,
83
+ label: 'JS bridge',
84
+ path: modulePath,
85
+ command: process.execPath,
86
+ argsPrefix: [COMPILER_BRIDGE_RUNNER, '--bridge-module', modulePath]
87
+ };
88
+ }
89
+ function currentBundlerPlatformPackage() {
90
+ return BUNDLER_PLATFORM_PACKAGES[`${process.platform}-${process.arch}`] || null;
28
91
  }
29
-
30
92
  export function resolveBinary(candidates) {
31
93
  for (const candidate of candidates) {
32
- if (existsSync(candidate)) {
33
- return candidate;
94
+ const path = typeof candidate === 'string' ? candidate : candidate.path;
95
+ if (path && existsSync(path)) {
96
+ return path;
34
97
  }
35
98
  }
36
- return candidates[0] || '';
99
+ const first = candidates[0];
100
+ if (typeof first === 'string') {
101
+ return first;
102
+ }
103
+ return first?.path || '';
37
104
  }
38
-
39
105
  export function resolvePackageRoot(packageName, projectRoot = null) {
40
106
  const projectRequire = safeCreateRequire(projectRoot);
41
107
  const projectPath = safeResolve(projectRequire, `${packageName}/package.json`);
42
108
  if (projectPath) {
43
109
  return dirname(projectPath);
44
110
  }
45
-
46
111
  const localPath = safeResolve(localRequire, `${packageName}/package.json`);
47
112
  return localPath ? dirname(localPath) : null;
48
113
  }
49
-
50
114
  export function readInstalledPackageVersion(packageName, projectRoot = null) {
51
115
  const packageRoot = resolvePackageRoot(packageName, projectRoot);
52
116
  if (!packageRoot) {
@@ -55,56 +119,92 @@ export function readInstalledPackageVersion(packageName, projectRoot = null) {
55
119
  try {
56
120
  const pkg = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
57
121
  return typeof pkg.version === 'string' ? pkg.version : null;
58
- } catch {
122
+ }
123
+ catch {
59
124
  return null;
60
125
  }
61
126
  }
62
-
63
127
  export function readCliPackageVersion() {
64
128
  try {
65
129
  const pkg = JSON.parse(readFileSync(resolve(CLI_ROOT, 'package.json'), 'utf8'));
66
130
  return typeof pkg.version === 'string' ? pkg.version : '0.0.0';
67
- } catch {
131
+ }
132
+ catch {
68
133
  return '0.0.0';
69
134
  }
70
135
  }
71
-
72
- export function compilerBinCandidates(projectRoot = null) {
73
- const candidates = [
74
- resolve(CLI_ROOT, '../compiler/target/release/zenith-compiler'),
75
- resolve(CLI_ROOT, '../zenith-compiler/target/release/zenith-compiler')
136
+ function compilerWorkspaceBinaryCandidates() {
137
+ return [
138
+ createBinaryCandidate('compiler', 'workspace binary', resolve(CLI_ROOT, '../compiler/target/release/zenith-compiler')),
139
+ createBinaryCandidate('compiler', 'workspace binary', resolve(CLI_ROOT, '../zenith-compiler/target/release/zenith-compiler'))
76
140
  ];
141
+ }
142
+ function bundlerWorkspaceBinaryCandidates() {
143
+ return [
144
+ createBinaryCandidate('bundler', 'workspace binary', resolve(CLI_ROOT, '../bundler/target/release/zenith-bundler')),
145
+ createBinaryCandidate('bundler', 'workspace binary', resolve(CLI_ROOT, '../zenith-bundler/target/release/zenith-bundler'))
146
+ ];
147
+ }
148
+ export function compilerCommandCandidates(projectRoot = null, env = process.env) {
149
+ const candidates = [];
150
+ const envBin = env.ZENITH_COMPILER_BIN;
151
+ if (typeof envBin === 'string' && envBin.length > 0) {
152
+ candidates.push({
153
+ ...createBinaryCandidate('compiler', 'env override (ZENITH_COMPILER_BIN)', envBin),
154
+ explicit: true
155
+ });
156
+ }
77
157
  const installedRoot = resolvePackageRoot('@zenithbuild/compiler', projectRoot);
78
158
  if (installedRoot) {
79
- candidates.unshift(resolve(installedRoot, 'target/release/zenith-compiler'));
159
+ candidates.push(createBinaryCandidate('compiler', 'installed package binary', resolve(installedRoot, 'target/release/zenith-compiler')));
160
+ }
161
+ candidates.push(...compilerWorkspaceBinaryCandidates());
162
+ if (installedRoot) {
163
+ const bridgeCandidate = createCompilerBridgeCandidate(resolve(installedRoot, 'dist/index.js'));
164
+ if (bridgeCandidate) {
165
+ candidates.push(bridgeCandidate);
166
+ }
80
167
  }
81
168
  return candidates;
82
169
  }
83
-
84
- export function resolveCompilerBin(projectRoot = null) {
85
- return resolveBinary(compilerBinCandidates(projectRoot));
170
+ export function compilerBinCandidates(projectRoot = null, env = process.env) {
171
+ return compilerCommandCandidates(projectRoot, env)
172
+ .filter((candidate) => candidate.mode === 'binary')
173
+ .map((candidate) => candidate.path);
86
174
  }
87
-
88
- export function bundlerBinCandidates(projectRoot = null, env = process.env) {
175
+ export function resolveCompilerBin(projectRoot = null, env = process.env) {
176
+ return resolveBinary(compilerBinCandidates(projectRoot, env));
177
+ }
178
+ export function bundlerCommandCandidates(projectRoot = null, env = process.env) {
89
179
  const candidates = [];
90
- const envBin = env?.ZENITH_BUNDLER_BIN;
180
+ const envBin = env.ZENITH_BUNDLER_BIN;
91
181
  if (typeof envBin === 'string' && envBin.length > 0) {
92
- candidates.push(envBin);
182
+ candidates.push({
183
+ ...createBinaryCandidate('bundler', 'env override (ZENITH_BUNDLER_BIN)', envBin),
184
+ explicit: true
185
+ });
186
+ }
187
+ const platformPackage = currentBundlerPlatformPackage();
188
+ if (platformPackage) {
189
+ const platformPackageRoot = resolvePackageRoot(platformPackage.packageName, projectRoot);
190
+ if (platformPackageRoot) {
191
+ candidates.push(createBinaryCandidate('bundler', 'installed platform package binary', resolve(platformPackageRoot, 'bin', platformPackage.binaryName)));
192
+ }
93
193
  }
94
-
95
194
  const installedRoot = resolvePackageRoot('@zenithbuild/bundler', projectRoot);
96
195
  if (installedRoot) {
97
- candidates.push(resolve(installedRoot, 'target/release/zenith-bundler'));
98
- }
99
-
100
- candidates.push(
101
- resolve(CLI_ROOT, '../bundler/target/release/zenith-bundler'),
102
- resolve(CLI_ROOT, '../zenith-bundler/target/release/zenith-bundler')
103
- );
104
-
196
+ candidates.push(createBinaryCandidate('bundler', 'installed package binary', resolve(installedRoot, 'target/release/zenith-bundler')));
197
+ }
198
+ candidates.push(...bundlerWorkspaceBinaryCandidates());
105
199
  return candidates;
106
200
  }
107
-
108
201
  export function resolveBundlerBin(projectRoot = null, env = process.env) {
109
- return resolveBinary(bundlerBinCandidates(projectRoot, env));
202
+ return resolveBinary(bundlerCommandCandidates(projectRoot, env)
203
+ .filter((candidate) => candidate.mode === 'binary')
204
+ .map((candidate) => candidate.path));
205
+ }
206
+ export function bundlerBinCandidates(projectRoot = null, env = process.env) {
207
+ return bundlerCommandCandidates(projectRoot, env)
208
+ .filter((candidate) => candidate.mode === 'binary')
209
+ .map((candidate) => candidate.path);
110
210
  }
@@ -0,0 +1,33 @@
1
+ import type { SpawnSyncOptionsWithStringEncoding, SpawnSyncReturns } from 'node:child_process';
2
+ import { type ToolchainCandidate, type ToolchainTool } from './toolchain-paths.js';
3
+ export interface ToolchainLogger {
4
+ warn?: (message: string, options?: {
5
+ onceKey?: string;
6
+ }) => void;
7
+ }
8
+ export interface ToolchainState {
9
+ tool: ToolchainTool;
10
+ logger: ToolchainLogger | null;
11
+ candidates: ToolchainCandidate[];
12
+ activeIndex: number;
13
+ }
14
+ type SpawnResult = SpawnSyncReturns<string>;
15
+ export declare function createCompilerToolchain({ projectRoot, env, logger }?: {
16
+ projectRoot?: string | null;
17
+ env?: NodeJS.ProcessEnv;
18
+ logger?: ToolchainLogger | null;
19
+ }): ToolchainState;
20
+ export declare function createBundlerToolchain({ projectRoot, env, logger }?: {
21
+ projectRoot?: string | null;
22
+ env?: NodeJS.ProcessEnv;
23
+ logger?: ToolchainLogger | null;
24
+ }): ToolchainState;
25
+ export declare function createToolchainStateForTests(tool: ToolchainTool, candidates: ToolchainCandidate[], logger?: ToolchainLogger | null): ToolchainState;
26
+ export declare function resetToolchainWarningsForTests(): void;
27
+ export declare function getActiveToolchainCandidate(toolchain: ToolchainState): ToolchainCandidate | null;
28
+ export declare function ensureToolchainCompatibility(toolchain: ToolchainState, probeArgs?: string[]): ToolchainCandidate;
29
+ export declare function runToolchainSync(toolchain: ToolchainState, args: string[], spawnOptions?: SpawnSyncOptionsWithStringEncoding): {
30
+ result: SpawnResult;
31
+ candidate: ToolchainCandidate;
32
+ };
33
+ export {};
@@ -0,0 +1,194 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { bundlerCommandCandidates, compilerCommandCandidates } from './toolchain-paths.js';
4
+ const FALLBACK_LOG_KEYS = new Set();
5
+ const INCOMPATIBLE_ERROR_CODES = new Set(['ENOEXEC', 'EACCES']);
6
+ const INCOMPATIBLE_STDERR_PATTERNS = [
7
+ /exec format error/i,
8
+ /bad cpu type/i,
9
+ /cannot execute binary file/i,
10
+ /not a valid win32 application/i
11
+ ];
12
+ function currentPlatformLabel() {
13
+ return `${process.platform}-${process.arch}`;
14
+ }
15
+ function toolEnvVar(tool) {
16
+ return tool === 'bundler' ? 'ZENITH_BUNDLER_BIN' : 'ZENITH_COMPILER_BIN';
17
+ }
18
+ function candidateExists(candidate) {
19
+ if (!candidate) {
20
+ return false;
21
+ }
22
+ if (candidate.mode === 'node-bridge') {
23
+ const [runnerPath] = candidate.argsPrefix;
24
+ return existsSync(candidate.path) && typeof runnerPath === 'string' && existsSync(runnerPath);
25
+ }
26
+ return typeof candidate.path === 'string' && candidate.path.length > 0 && existsSync(candidate.path);
27
+ }
28
+ function candidateSupportsArgs(candidate, args) {
29
+ if (!candidate || candidate.mode !== 'node-bridge') {
30
+ return true;
31
+ }
32
+ return !args.includes('--embedded-markup-expressions') && !args.includes('--strict-dom-lints');
33
+ }
34
+ function isBinaryIncompatible(result) {
35
+ const error = result?.error;
36
+ const errorCode = error?.code;
37
+ if (typeof errorCode === 'string' && INCOMPATIBLE_ERROR_CODES.has(errorCode)) {
38
+ return true;
39
+ }
40
+ const stderr = `${result?.stderr || ''}\n${error?.message || ''}`;
41
+ return INCOMPATIBLE_STDERR_PATTERNS.some((pattern) => pattern.test(stderr));
42
+ }
43
+ function emitFallbackWarning(toolchain, nextCandidate) {
44
+ const message = `[zenith] ${toolchain.tool} binary incompatible for this platform; falling back to ${nextCandidate.label}`;
45
+ const onceKey = `toolchain-fallback:${toolchain.tool}:${nextCandidate.sourceKey}`;
46
+ if (toolchain.logger && typeof toolchain.logger.warn === 'function') {
47
+ toolchain.logger.warn(message, { onceKey });
48
+ return;
49
+ }
50
+ if (FALLBACK_LOG_KEYS.has(onceKey)) {
51
+ return;
52
+ }
53
+ FALLBACK_LOG_KEYS.add(onceKey);
54
+ console.warn(message);
55
+ }
56
+ function missingToolchainError(toolchain) {
57
+ if (toolchain.tool === 'bundler') {
58
+ return new Error(`[zenith] Bundler binary not installed for ${process.platform}/${process.arch}. ` +
59
+ 'Reinstall @zenithbuild/bundler or ensure optional dependency installed.');
60
+ }
61
+ return new Error(`[zenith] ${toolchain.tool} binary not installed for ${currentPlatformLabel()}; ` +
62
+ `reinstall or set ${toolEnvVar(toolchain.tool)}=...`);
63
+ }
64
+ function incompatibleBinaryError(toolchain) {
65
+ return new Error(`[zenith] ${toolchain.tool} binary is incompatible for ${currentPlatformLabel()}; ` +
66
+ `reinstall or set ${toolEnvVar(toolchain.tool)}=...`);
67
+ }
68
+ function toolchainProbeError(toolchain, result) {
69
+ const detail = result?.error?.message
70
+ || String(result?.stderr || '').trim()
71
+ || `exit code ${result?.status ?? 'unknown'}`;
72
+ return new Error(`[zenith] ${toolchain.tool} probe failed: ${detail}`);
73
+ }
74
+ function buildToolchainState(tool, candidates, logger = null) {
75
+ const explicitIndex = candidates.findIndex((candidate) => candidate.explicit === true);
76
+ const initialIndex = explicitIndex >= 0
77
+ ? explicitIndex
78
+ : candidates.findIndex((candidate) => candidateExists(candidate));
79
+ return {
80
+ tool,
81
+ logger,
82
+ candidates,
83
+ activeIndex: initialIndex >= 0 ? initialIndex : 0
84
+ };
85
+ }
86
+ function findNextFallbackIndex(toolchain, args) {
87
+ for (let index = toolchain.activeIndex + 1; index < toolchain.candidates.length; index += 1) {
88
+ const candidate = toolchain.candidates[index];
89
+ if (!candidateExists(candidate)) {
90
+ continue;
91
+ }
92
+ if (!candidateSupportsArgs(candidate, args)) {
93
+ continue;
94
+ }
95
+ return index;
96
+ }
97
+ return -1;
98
+ }
99
+ function activeCandidate(toolchain) {
100
+ return toolchain.candidates[toolchain.activeIndex] || null;
101
+ }
102
+ function runCandidateSync(candidate, args, spawnOptions) {
103
+ return spawnSync(candidate.command, [...candidate.argsPrefix, ...args], spawnOptions);
104
+ }
105
+ export function createCompilerToolchain({ projectRoot = null, env = process.env, logger = null } = {}) {
106
+ return buildToolchainState('compiler', compilerCommandCandidates(projectRoot, env), logger);
107
+ }
108
+ export function createBundlerToolchain({ projectRoot = null, env = process.env, logger = null } = {}) {
109
+ return buildToolchainState('bundler', bundlerCommandCandidates(projectRoot, env), logger);
110
+ }
111
+ export function createToolchainStateForTests(tool, candidates, logger = null) {
112
+ return buildToolchainState(tool, candidates, logger);
113
+ }
114
+ export function resetToolchainWarningsForTests() {
115
+ FALLBACK_LOG_KEYS.clear();
116
+ }
117
+ export function getActiveToolchainCandidate(toolchain) {
118
+ return activeCandidate(toolchain);
119
+ }
120
+ export function ensureToolchainCompatibility(toolchain, probeArgs = ['--version']) {
121
+ while (toolchain.activeIndex < toolchain.candidates.length) {
122
+ const candidate = activeCandidate(toolchain);
123
+ if (!candidate) {
124
+ break;
125
+ }
126
+ if (!candidateExists(candidate)) {
127
+ const nextIndex = findNextFallbackIndex(toolchain, probeArgs);
128
+ if (nextIndex === -1) {
129
+ throw missingToolchainError(toolchain);
130
+ }
131
+ toolchain.activeIndex = nextIndex;
132
+ continue;
133
+ }
134
+ if (!candidateSupportsArgs(candidate, probeArgs)) {
135
+ const nextIndex = findNextFallbackIndex(toolchain, probeArgs);
136
+ if (nextIndex === -1) {
137
+ throw incompatibleBinaryError(toolchain);
138
+ }
139
+ toolchain.activeIndex = nextIndex;
140
+ emitFallbackWarning(toolchain, toolchain.candidates[nextIndex]);
141
+ continue;
142
+ }
143
+ const result = runCandidateSync(candidate, probeArgs, { encoding: 'utf8' });
144
+ if (!isBinaryIncompatible(result)) {
145
+ if (result.error || result.status !== 0) {
146
+ throw toolchainProbeError(toolchain, result);
147
+ }
148
+ return candidate;
149
+ }
150
+ const nextIndex = findNextFallbackIndex(toolchain, probeArgs);
151
+ if (nextIndex === -1) {
152
+ throw incompatibleBinaryError(toolchain);
153
+ }
154
+ toolchain.activeIndex = nextIndex;
155
+ emitFallbackWarning(toolchain, toolchain.candidates[nextIndex]);
156
+ }
157
+ throw missingToolchainError(toolchain);
158
+ }
159
+ export function runToolchainSync(toolchain, args, spawnOptions = { encoding: 'utf8' }) {
160
+ while (toolchain.activeIndex < toolchain.candidates.length) {
161
+ const candidate = activeCandidate(toolchain);
162
+ if (!candidate) {
163
+ break;
164
+ }
165
+ if (!candidateExists(candidate)) {
166
+ const nextIndex = findNextFallbackIndex(toolchain, args);
167
+ if (nextIndex === -1) {
168
+ throw missingToolchainError(toolchain);
169
+ }
170
+ toolchain.activeIndex = nextIndex;
171
+ continue;
172
+ }
173
+ if (!candidateSupportsArgs(candidate, args)) {
174
+ const nextIndex = findNextFallbackIndex(toolchain, args);
175
+ if (nextIndex === -1) {
176
+ throw incompatibleBinaryError(toolchain);
177
+ }
178
+ toolchain.activeIndex = nextIndex;
179
+ emitFallbackWarning(toolchain, toolchain.candidates[nextIndex]);
180
+ continue;
181
+ }
182
+ const result = runCandidateSync(candidate, args, spawnOptions);
183
+ if (!isBinaryIncompatible(result)) {
184
+ return { result, candidate };
185
+ }
186
+ const nextIndex = findNextFallbackIndex(toolchain, args);
187
+ if (nextIndex === -1) {
188
+ throw incompatibleBinaryError(toolchain);
189
+ }
190
+ toolchain.activeIndex = nextIndex;
191
+ emitFallbackWarning(toolchain, toolchain.candidates[nextIndex]);
192
+ }
193
+ throw missingToolchainError(toolchain);
194
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @param {string} projectRoot
3
+ * @returns {Promise<void>}
4
+ */
5
+ export function generateEnvDts(projectRoot: string): Promise<void>;
@@ -1,6 +1,9 @@
1
1
  import { writeFile, mkdir } from 'node:fs/promises';
2
2
  import { join, dirname } from 'node:path';
3
-
3
+ /**
4
+ * @param {string} projectRoot
5
+ * @returns {Promise<void>}
6
+ */
4
7
  export async function generateEnvDts(projectRoot) {
5
8
  const content = `// .zenith/zenith-env.d.ts
6
9
  // Auto-generated by Zenith. Do not edit.
@@ -45,7 +48,6 @@ declare global {
45
48
  }
46
49
  }
47
50
  `;
48
-
49
51
  const outPath = join(projectRoot, '.zenith', 'zenith-env.d.ts');
50
52
  await mkdir(dirname(outPath), { recursive: true });
51
53
  await writeFile(outPath, content, 'utf8');
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @param {string} projectRoot
3
+ * @param {Array<{ path?: string | null }>} [manifest]
4
+ * @returns {Promise<void>}
5
+ */
6
+ export function generateRoutesDts(projectRoot: string, manifest?: Array<{
7
+ path?: string | null;
8
+ }>): Promise<void>;
@@ -1,12 +1,15 @@
1
1
  import { writeFile, mkdir } from 'node:fs/promises';
2
2
  import { join, dirname } from 'node:path';
3
-
3
+ /**
4
+ * @param {string} projectRoot
5
+ * @param {Array<{ path?: string | null }>} [manifest]
6
+ * @returns {Promise<void>}
7
+ */
4
8
  export async function generateRoutesDts(projectRoot, manifest) {
5
- const routes = (manifest || []).map(r => r.path).filter(Boolean);
9
+ const routes = (manifest || []).map((route) => route.path).filter(Boolean);
6
10
  const typeDef = routes.length > 0
7
- ? routes.map(r => '"' + r + '"').join(' | ')
11
+ ? routes.map((route) => `"${route}"`).join(' | ')
8
12
  : 'string';
9
-
10
13
  const content = '// .zenith/zenith-routes.d.ts\\n' +
11
14
  '// Auto-generated by Zenith. Do not edit.\\n\\n' +
12
15
  'export {};\\n\\n' +
@@ -15,7 +18,6 @@ export async function generateRoutesDts(projectRoot, manifest) {
15
18
  ' type RoutePattern = ' + typeDef + ';\\n' +
16
19
  ' }\\n' +
17
20
  '}\\n';
18
-
19
21
  const outPath = join(projectRoot, '.zenith', 'zenith-routes.d.ts');
20
22
  await mkdir(dirname(outPath), { recursive: true });
21
23
  await writeFile(outPath, content, 'utf8');
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @typedef {typeof globalThis & { __zenithTypesWarned?: boolean }} ZenithTypesGlobal
3
+ */
4
+ /**
5
+ * @param {string} projectRoot
6
+ * @param {Array<{ path?: string }>} [manifest]
7
+ * @returns {Promise<void>}
8
+ */
9
+ export function ensureZenithTypes(projectRoot: string, manifest?: Array<{
10
+ path?: string;
11
+ }>): Promise<void>;
12
+ export type ZenithTypesGlobal = typeof globalThis & {
13
+ __zenithTypesWarned?: boolean;
14
+ };
@@ -2,33 +2,42 @@ import { generateEnvDts } from './generate-env-dts.js';
2
2
  import { generateRoutesDts } from './generate-routes-dts.js';
3
3
  import { join } from 'node:path';
4
4
  import { access, constants } from 'node:fs/promises';
5
-
5
+ /**
6
+ * @typedef {typeof globalThis & { __zenithTypesWarned?: boolean }} ZenithTypesGlobal
7
+ */
8
+ /**
9
+ * @param {string} projectRoot
10
+ * @param {Array<{ path?: string }>} [manifest]
11
+ * @returns {Promise<void>}
12
+ */
6
13
  export async function ensureZenithTypes(projectRoot, manifest) {
7
14
  try {
8
15
  await generateEnvDts(projectRoot);
9
16
  if (manifest) {
10
17
  await generateRoutesDts(projectRoot, manifest);
11
18
  }
12
-
13
19
  // Check if tsconfig.json exists, if it does, check if .zenith is included
14
20
  const tsconfigPath = join(projectRoot, 'tsconfig.json');
15
21
  let hasTsConfig = false;
16
22
  try {
17
23
  await access(tsconfigPath, constants.F_OK);
18
24
  hasTsConfig = true;
19
- } catch {
25
+ }
26
+ catch {
20
27
  hasTsConfig = false;
21
28
  }
22
-
23
29
  if (hasTsConfig) {
24
30
  // In a real implementation this would parse the JSON and check "include".
25
31
  // For now, we simply inform the user to include it if they haven't.
26
- if (!globalThis.__zenithTypesWarned) {
32
+ /** @type {ZenithTypesGlobal} */
33
+ const globalScope = globalThis;
34
+ if (!globalScope.__zenithTypesWarned) {
27
35
  console.warn('\\x1b[33m[zenith]\\x1b[0m For the best TypeScript experience, ensure ".zenith/**/*.d.ts" is in your tsconfig.json "include" array.');
28
- globalThis.__zenithTypesWarned = true;
36
+ globalScope.__zenithTypesWarned = true;
29
37
  }
30
38
  }
31
- } catch (err) {
39
+ }
40
+ catch (err) {
32
41
  console.error('[zenith] Failed to generate type definitions:', err);
33
42
  }
34
43
  }
@@ -0,0 +1,18 @@
1
+ export type UiLogLevel = 'quiet' | 'normal' | 'verbose';
2
+ export interface UiMode {
3
+ plain: boolean;
4
+ color: boolean;
5
+ tty: boolean;
6
+ ci: boolean;
7
+ spinner: boolean;
8
+ debug: boolean;
9
+ logLevel: UiLogLevel;
10
+ }
11
+ export interface UiRuntime {
12
+ env?: Record<string, string | undefined>;
13
+ stdout?: {
14
+ isTTY?: boolean;
15
+ };
16
+ }
17
+ export declare function getUiMode(runtime?: UiRuntime): UiMode;
18
+ export declare function isUiPlain(runtime?: UiRuntime): boolean;
package/dist/ui/env.js CHANGED
@@ -1,7 +1,3 @@
1
- /**
2
- * UI environment mode detection for deterministic CLI output.
3
- */
4
-
5
1
  function flagEnabled(value) {
6
2
  if (value === undefined || value === null) {
7
3
  return false;
@@ -9,7 +5,6 @@ function flagEnabled(value) {
9
5
  const normalized = String(value).trim().toLowerCase();
10
6
  return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
11
7
  }
12
-
13
8
  function parseLogLevel(value) {
14
9
  const normalized = String(value || '').trim().toLowerCase();
15
10
  if (normalized === 'quiet' || normalized === 'verbose') {
@@ -17,10 +12,6 @@ function parseLogLevel(value) {
17
12
  }
18
13
  return 'normal';
19
14
  }
20
-
21
- /**
22
- * @param {{ env?: Record<string, string | undefined>, stdout?: { isTTY?: boolean } }} runtime
23
- */
24
15
  export function getUiMode(runtime = process) {
25
16
  const env = runtime.env || {};
26
17
  const tty = Boolean(runtime.stdout?.isTTY);
@@ -30,7 +21,6 @@ export function getUiMode(runtime = process) {
30
21
  const forceColor = flagEnabled(env.FORCE_COLOR);
31
22
  const debug = flagEnabled(env.ZENITH_DEBUG);
32
23
  let logLevel = parseLogLevel(env.ZENITH_LOG_LEVEL);
33
-
34
24
  const plain = noUi || ci || !tty;
35
25
  const color = !plain && !noColor && (forceColor || tty);
36
26
  const spinner = tty && !plain && !ci;
@@ -40,7 +30,6 @@ export function getUiMode(runtime = process) {
40
30
  if (debug && logLevel !== 'quiet') {
41
31
  logLevel = 'verbose';
42
32
  }
43
-
44
33
  return {
45
34
  plain,
46
35
  color,
@@ -51,7 +40,6 @@ export function getUiMode(runtime = process) {
51
40
  logLevel
52
41
  };
53
42
  }
54
-
55
43
  export function isUiPlain(runtime = process) {
56
44
  return getUiMode(runtime).plain;
57
45
  }