@visulima/vis 1.0.0-alpha.11 → 1.0.0-alpha.13
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/CHANGELOG.md +101 -0
- package/LICENSE.md +559 -186
- package/README.md +18 -0
- package/dist/bin.js +1 -9
- package/dist/config/index.d.ts +477 -556
- package/dist/config/index.js +1 -2
- package/dist/generate/index.js +1 -3
- package/dist/packem_chunks/applyDefaults.js +2 -336
- package/dist/packem_chunks/bin.js +234 -9552
- package/dist/packem_chunks/doctor-probe.js +2 -112
- package/dist/packem_chunks/fix.js +11 -234
- package/dist/packem_chunks/handler.js +1 -99
- package/dist/packem_chunks/handler10.js +2 -53
- package/dist/packem_chunks/handler11.js +1 -32
- package/dist/packem_chunks/handler12.js +5 -100
- package/dist/packem_chunks/handler13.js +1 -25
- package/dist/packem_chunks/handler14.js +18 -916
- package/dist/packem_chunks/handler15.js +15 -201
- package/dist/packem_chunks/handler16.js +1 -124
- package/dist/packem_chunks/handler17.js +1 -13
- package/dist/packem_chunks/handler18.js +1 -106
- package/dist/packem_chunks/handler19.js +1 -19
- package/dist/packem_chunks/handler2.js +2 -75
- package/dist/packem_chunks/handler20.js +5 -29
- package/dist/packem_chunks/handler21.js +1 -222
- package/dist/packem_chunks/handler22.js +1 -237
- package/dist/packem_chunks/handler23.js +5 -101
- package/dist/packem_chunks/handler24.js +1 -110
- package/dist/packem_chunks/handler25.js +3 -402
- package/dist/packem_chunks/handler26.js +1 -13
- package/dist/packem_chunks/handler27.js +1 -63
- package/dist/packem_chunks/handler28.js +7 -34
- package/dist/packem_chunks/handler29.js +21 -456
- package/dist/packem_chunks/handler3.js +4 -95
- package/dist/packem_chunks/handler30.js +3 -170
- package/dist/packem_chunks/handler31.js +1 -530
- package/dist/packem_chunks/handler32.js +2 -214
- package/dist/packem_chunks/handler33.js +25 -119
- package/dist/packem_chunks/handler34.js +2 -630
- package/dist/packem_chunks/handler35.js +3 -283
- package/dist/packem_chunks/handler36.js +22 -542
- package/dist/packem_chunks/handler37.js +410 -744
- package/dist/packem_chunks/handler38.js +22 -989
- package/dist/packem_chunks/handler39.js +22 -574
- package/dist/packem_chunks/handler4.js +2 -90
- package/dist/packem_chunks/handler40.js +22 -1685
- package/dist/packem_chunks/handler41.js +6 -1088
- package/dist/packem_chunks/handler42.js +5 -797
- package/dist/packem_chunks/handler43.js +10 -2658
- package/dist/packem_chunks/handler44.js +51 -3784
- package/dist/packem_chunks/handler45.js +25 -2574
- package/dist/packem_chunks/handler46.js +3 -3769
- package/dist/packem_chunks/handler47.js +21 -1485
- package/dist/packem_chunks/handler48.js +42 -0
- package/dist/packem_chunks/handler5.js +8 -174
- package/dist/packem_chunks/handler6.js +1 -95
- package/dist/packem_chunks/handler7.js +1 -115
- package/dist/packem_chunks/handler8.js +1 -12
- package/dist/packem_chunks/handler9.js +1 -29
- package/dist/packem_chunks/heal-accept.js +10 -522
- package/dist/packem_chunks/heal.js +14 -673
- package/dist/packem_chunks/index.js +7 -873
- package/dist/packem_chunks/loader.js +1 -23
- package/dist/packem_chunks/tar.js +3 -0
- package/dist/packem_shared/ai-analysis-hm8d2W7z.js +67 -0
- package/dist/packem_shared/ai-cache-DoiF80AR.js +1 -0
- package/dist/packem_shared/ai-fix-nn4zOE95.js +43 -0
- package/dist/packem_shared/cache-directory-CwHlJhgx.js +1 -0
- package/dist/packem_shared/dependency-scan-COr5n63B.js +2 -0
- package/dist/packem_shared/docker-D6OGr5_S.js +2 -0
- package/dist/packem_shared/failure-log-iUVLf6ts.js +2 -0
- package/dist/packem_shared/flakiness-D9wf0t56.js +1 -0
- package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
- package/dist/packem_shared/index-DH-5hsrC.js +1 -0
- package/dist/packem_shared/otel-DxDUPJJH.js +6 -0
- package/dist/packem_shared/otelPlugin-CQq6poq8.js +1 -0
- package/dist/packem_shared/registry-CkubDdiY.js +2 -0
- package/dist/packem_shared/run-summary-utils-BfBvjzhY.js +1 -0
- package/dist/packem_shared/runtime-check-BXZ43CBW.js +1 -0
- package/dist/packem_shared/selectors-BylODRiM.js +3 -0
- package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
- package/dist/packem_shared/toolchain-BgBOUHII.js +5 -0
- package/dist/packem_shared/typosquats-CcZl99B1.js +1 -0
- package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
- package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
- package/dist/packem_shared/verify-Baj5mFJ7.js +1 -0
- package/dist/packem_shared/vis-update-app-D1jl0UZZ.js +1 -0
- package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
- package/index.js +556 -727
- package/package.json +19 -29
- package/schemas/project.schema.json +739 -297
- package/schemas/vis-config.schema.json +3365 -384
- package/templates/buildkite-ci/template.yml +20 -20
- package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +0 -1316
- package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
- package/dist/packem_shared/ai-analysis-CHeB1joD.js +0 -367
- package/dist/packem_shared/ai-cache-Be_jexe4.js +0 -142
- package/dist/packem_shared/ai-fix-B9iQVcD2.js +0 -379
- package/dist/packem_shared/cache-directory-2qvs4goY.js +0 -98
- package/dist/packem_shared/catalog-BJTtyi-O.js +0 -1371
- package/dist/packem_shared/dependency-scan-A0KSklpG.js +0 -188
- package/dist/packem_shared/docker-2iZzc280.js +0 -181
- package/dist/packem_shared/failure-log-Cz3Z4SKL.js +0 -100
- package/dist/packem_shared/flakiness-goTxXuCX.js +0 -180
- package/dist/packem_shared/otel-DCvqCTz_.js +0 -158
- package/dist/packem_shared/otelPlugin-DFaLDvJf.js +0 -3
- package/dist/packem_shared/registry-CbqXI0rc.js +0 -272
- package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +0 -130
- package/dist/packem_shared/runtime-check-Cobi3p6l.js +0 -127
- package/dist/packem_shared/selectors-SM69TfqC.js +0 -194
- package/dist/packem_shared/symbols-Ta7g2nU-.js +0 -14
- package/dist/packem_shared/toolchain-BdZd9eBi.js +0 -975
- package/dist/packem_shared/typosquats-C-bCh3PX.js +0 -1210
- package/dist/packem_shared/use-measured-height-CNP0vT4M.js +0 -20
- package/dist/packem_shared/utils-CthVdBPS.js +0 -40
- package/dist/packem_shared/xxh3-Ck8mXNg1.js +0 -239
|
@@ -1,542 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
watch
|
|
24
|
-
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
25
|
-
import { isAccessible } from '@visulima/fs';
|
|
26
|
-
import { p as pail, Y as loadVisTaskConfigsForWorkspace, d as discoverWorkspace, Z as loadEnvFile } from './bin.js';
|
|
27
|
-
import { join } from '@visulima/path';
|
|
28
|
-
import { R as REGISTRY_FILE_MODE, w as withServiceLock, r as readEntry, i as isAlive, d as deleteEntry, g as getRegistryDir, s as slugify, a as writeEntry, b as runReadiness, p as pruneDead, S as ServiceReadinessError, c as readAllEntries } from '../packem_shared/registry-CbqXI0rc.js';
|
|
29
|
-
const {
|
|
30
|
-
spawn
|
|
31
|
-
} = __cjs_getBuiltinModule("node:child_process");
|
|
32
|
-
import { formatAge } from './handler14.js';
|
|
33
|
-
|
|
34
|
-
const isWindows = process.platform === "win32";
|
|
35
|
-
const spawnDetached = async (input) => {
|
|
36
|
-
const { command, cwd, env, logFile } = input;
|
|
37
|
-
const logHandle = await open(logFile, "a", REGISTRY_FILE_MODE);
|
|
38
|
-
let child;
|
|
39
|
-
try {
|
|
40
|
-
const shell = isWindows ? "cmd" : "/bin/sh";
|
|
41
|
-
const args = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
|
|
42
|
-
child = spawn(shell, args, {
|
|
43
|
-
cwd,
|
|
44
|
-
detached: true,
|
|
45
|
-
env: { ...process.env, ...env },
|
|
46
|
-
stdio: ["ignore", logHandle.fd, logHandle.fd],
|
|
47
|
-
// Windows: spawn in a new console so the child isn't tied
|
|
48
|
-
// to this terminal's lifetime.
|
|
49
|
-
windowsHide: true
|
|
50
|
-
});
|
|
51
|
-
} finally {
|
|
52
|
-
await logHandle.close().catch(() => {
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
if (child.pid === void 0) {
|
|
56
|
-
await new Promise((resolve, reject) => {
|
|
57
|
-
child.once("spawn", () => resolve());
|
|
58
|
-
child.once("error", (error) => reject(error));
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
if (child.pid === void 0) {
|
|
62
|
-
throw new Error(`Failed to spawn detached process for command: ${command}`);
|
|
63
|
-
}
|
|
64
|
-
child.unref();
|
|
65
|
-
return { pid: child.pid };
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const DEFAULT_KILL_GRACE_MS = 5e3;
|
|
69
|
-
const startService = async (input) => withServiceLock(input.workspaceRoot, input.id, async () => {
|
|
70
|
-
const existing = await readEntry(input.workspaceRoot, input.id);
|
|
71
|
-
if (existing && isAlive(existing.pid)) {
|
|
72
|
-
throw new Error(`Service ${input.id} is already running (pid ${String(existing.pid)})`);
|
|
73
|
-
}
|
|
74
|
-
if (existing) {
|
|
75
|
-
await deleteEntry(input.workspaceRoot, input.id, existing);
|
|
76
|
-
}
|
|
77
|
-
const registryDirectory = await getRegistryDir(input.workspaceRoot);
|
|
78
|
-
const slug = slugify(input.id);
|
|
79
|
-
const logFile = join(registryDirectory, `${slug}.log`);
|
|
80
|
-
const { pid } = await spawnDetached({
|
|
81
|
-
command: input.command,
|
|
82
|
-
cwd: input.cwd,
|
|
83
|
-
env: input.env,
|
|
84
|
-
logFile
|
|
85
|
-
});
|
|
86
|
-
const entry = {
|
|
87
|
-
command: input.command,
|
|
88
|
-
config: input.config,
|
|
89
|
-
cwd: input.cwd,
|
|
90
|
-
env: input.config.env ?? {},
|
|
91
|
-
id: input.id,
|
|
92
|
-
logFile,
|
|
93
|
-
pid,
|
|
94
|
-
slug,
|
|
95
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
96
|
-
visVersion: process.env["VIS_VERSION"] ?? "0.0.0"
|
|
97
|
-
};
|
|
98
|
-
await writeEntry(input.workspaceRoot, entry);
|
|
99
|
-
if (input.skipReadiness !== true) {
|
|
100
|
-
try {
|
|
101
|
-
await runReadiness(input.config, { timeoutMs: input.readinessTimeoutMs });
|
|
102
|
-
} catch (error) {
|
|
103
|
-
await stopServiceByPid(pid, input.config.killGracePeriodMs ?? DEFAULT_KILL_GRACE_MS).catch(() => {
|
|
104
|
-
});
|
|
105
|
-
await deleteEntry(input.workspaceRoot, input.id, entry).catch(() => {
|
|
106
|
-
});
|
|
107
|
-
throw error;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return { entry };
|
|
111
|
-
});
|
|
112
|
-
const stopService = async (input) => withServiceLock(input.workspaceRoot, input.id, async () => {
|
|
113
|
-
const entry = await readEntry(input.workspaceRoot, input.id);
|
|
114
|
-
if (!entry) {
|
|
115
|
-
return { stopped: false };
|
|
116
|
-
}
|
|
117
|
-
if (!isAlive(entry.pid)) {
|
|
118
|
-
await deleteEntry(input.workspaceRoot, input.id, entry);
|
|
119
|
-
return { stopped: false };
|
|
120
|
-
}
|
|
121
|
-
const graceMs = input.graceMs ?? entry.config.killGracePeriodMs ?? DEFAULT_KILL_GRACE_MS;
|
|
122
|
-
await stopServiceByPid(entry.pid, graceMs);
|
|
123
|
-
await deleteEntry(input.workspaceRoot, input.id, entry);
|
|
124
|
-
return { stopped: true };
|
|
125
|
-
});
|
|
126
|
-
const stopServiceByPid = async (pid, graceMs) => {
|
|
127
|
-
sendSignalToServiceGroup(pid, "SIGTERM");
|
|
128
|
-
const start = Date.now();
|
|
129
|
-
while (Date.now() - start < graceMs) {
|
|
130
|
-
if (!isAlive(pid)) {
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
await new Promise((resolve) => {
|
|
134
|
-
setTimeout(resolve, 100);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
if (isAlive(pid)) {
|
|
138
|
-
sendSignalToServiceGroup(pid, "SIGKILL");
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
const sendSignalToServiceGroup = (pid, signal) => {
|
|
142
|
-
try {
|
|
143
|
-
if (process.platform === "win32") {
|
|
144
|
-
process.kill(pid, signal);
|
|
145
|
-
} else {
|
|
146
|
-
process.kill(-pid, signal);
|
|
147
|
-
}
|
|
148
|
-
} catch (error) {
|
|
149
|
-
if (error.code === "ESRCH") {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
throw error;
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const splitTargetId = (raw) => {
|
|
157
|
-
const id = raw.trim();
|
|
158
|
-
const idx = id.lastIndexOf(":");
|
|
159
|
-
if (idx <= 0 || idx === id.length - 1) {
|
|
160
|
-
return void 0;
|
|
161
|
-
}
|
|
162
|
-
return { project: id.slice(0, idx), target: id.slice(idx + 1) };
|
|
163
|
-
};
|
|
164
|
-
const resolveCwd = (workspaceRoot, projectRoot, runFromWorkspaceRoot) => {
|
|
165
|
-
if (runFromWorkspaceRoot || !projectRoot) {
|
|
166
|
-
return workspaceRoot;
|
|
167
|
-
}
|
|
168
|
-
return projectRoot.startsWith("/") ? projectRoot : `${workspaceRoot}/${projectRoot}`;
|
|
169
|
-
};
|
|
170
|
-
const resolveTarget = async (workspaceRoot, visConfig, targetId) => {
|
|
171
|
-
const split = splitTargetId(targetId);
|
|
172
|
-
if (!split) {
|
|
173
|
-
pail.error(`Invalid target id "${targetId}". Expected "<project>:<target>", e.g. "@my/api:db".`);
|
|
174
|
-
return void 0;
|
|
175
|
-
}
|
|
176
|
-
const taskConfigs = await loadVisTaskConfigsForWorkspace(workspaceRoot);
|
|
177
|
-
const { projectOptions, workspace } = discoverWorkspace(workspaceRoot, visConfig, taskConfigs);
|
|
178
|
-
const project = workspace.projects[split.project];
|
|
179
|
-
const target = projectOptions.get(split.project)?.[split.target];
|
|
180
|
-
if (!project || !target) {
|
|
181
|
-
pail.error(`Target "${targetId}" not found in this workspace.`);
|
|
182
|
-
return void 0;
|
|
183
|
-
}
|
|
184
|
-
const service = target.options?.service;
|
|
185
|
-
if (!service) {
|
|
186
|
-
pail.error(`Target "${targetId}" is not a service. Add an \`options.service\` block to make it eligible for \`vis service\`.`);
|
|
187
|
-
return void 0;
|
|
188
|
-
}
|
|
189
|
-
if (!target.command) {
|
|
190
|
-
pail.error(`Target "${targetId}" has no command — services must be runnable.`);
|
|
191
|
-
return void 0;
|
|
192
|
-
}
|
|
193
|
-
const cwd = resolveCwd(workspaceRoot, project.root, target.options?.runFromWorkspaceRoot === true);
|
|
194
|
-
const envFromFiles = target.options?.envFile ? loadEnvFile(cwd, target.options.envFile) : {};
|
|
195
|
-
return {
|
|
196
|
-
command: target.command,
|
|
197
|
-
cwd,
|
|
198
|
-
env: { ...envFromFiles, ...service.env },
|
|
199
|
-
service,
|
|
200
|
-
target,
|
|
201
|
-
targetId
|
|
202
|
-
};
|
|
203
|
-
};
|
|
204
|
-
const requireWorkspace = (wsRoot) => {
|
|
205
|
-
if (!wsRoot) {
|
|
206
|
-
throw new Error("Could not determine workspace root. Run `vis service` inside a workspace.");
|
|
207
|
-
}
|
|
208
|
-
return wsRoot;
|
|
209
|
-
};
|
|
210
|
-
const serviceStartExecute = async ({ argument, options, visConfig, workspaceRoot: wsRoot }) => {
|
|
211
|
-
const workspaceRoot = requireWorkspace(wsRoot);
|
|
212
|
-
const targetId = argument[0]?.trim();
|
|
213
|
-
if (!targetId) {
|
|
214
|
-
pail.error("Missing target id. Usage: vis service start <project>:<target>");
|
|
215
|
-
process.exitCode = 1;
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
const resolved = await resolveTarget(workspaceRoot, visConfig, targetId);
|
|
219
|
-
if (!resolved) {
|
|
220
|
-
process.exitCode = 1;
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
try {
|
|
224
|
-
const { entry } = await startService({
|
|
225
|
-
command: resolved.command,
|
|
226
|
-
config: resolved.service,
|
|
227
|
-
cwd: resolved.cwd,
|
|
228
|
-
env: resolved.env,
|
|
229
|
-
id: targetId,
|
|
230
|
-
readinessTimeoutMs: options.timeout,
|
|
231
|
-
skipReadiness: options.noReadiness === true,
|
|
232
|
-
workspaceRoot
|
|
233
|
-
});
|
|
234
|
-
pail.success(`Started ${targetId} (pid ${String(entry.pid)})`);
|
|
235
|
-
pail.info(` log: ${entry.logFile}`);
|
|
236
|
-
} catch (error) {
|
|
237
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
238
|
-
if (error instanceof ServiceReadinessError) {
|
|
239
|
-
pail.error(`Readiness probe failed for ${targetId}: ${message}`);
|
|
240
|
-
} else {
|
|
241
|
-
pail.error(`Failed to start ${targetId}: ${message}`);
|
|
242
|
-
}
|
|
243
|
-
process.exitCode = 1;
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
const stopOne = async (workspaceRoot, id, graceMs) => {
|
|
247
|
-
const { stopped } = await stopService({ graceMs, id, workspaceRoot });
|
|
248
|
-
if (stopped) {
|
|
249
|
-
pail.success(`Stopped ${id}`);
|
|
250
|
-
return true;
|
|
251
|
-
}
|
|
252
|
-
pail.info(`No running service registered for ${id}`);
|
|
253
|
-
return false;
|
|
254
|
-
};
|
|
255
|
-
const serviceStopExecute = async ({ argument, options, workspaceRoot: wsRoot }) => {
|
|
256
|
-
const workspaceRoot = requireWorkspace(wsRoot);
|
|
257
|
-
const { graceMs } = options;
|
|
258
|
-
const targetId = argument[0]?.trim();
|
|
259
|
-
if (options.all === true) {
|
|
260
|
-
if (targetId) {
|
|
261
|
-
pail.error("Cannot combine --all with a target id. Use one or the other.");
|
|
262
|
-
process.exitCode = 1;
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
const entries = await readAllEntries(workspaceRoot);
|
|
266
|
-
if (entries.length === 0) {
|
|
267
|
-
pail.info("No running services registered for this workspace.");
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
for (const entry of entries) {
|
|
271
|
-
await stopOne(workspaceRoot, entry.id, graceMs);
|
|
272
|
-
}
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
if (!targetId) {
|
|
276
|
-
pail.error("Missing target id. Usage: vis service stop <project>:<target> | --all");
|
|
277
|
-
process.exitCode = 1;
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
const stopped = await stopOne(workspaceRoot, targetId, graceMs);
|
|
281
|
-
if (!stopped) {
|
|
282
|
-
process.exitCode = 1;
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
const formatPort = (entry) => {
|
|
286
|
-
const port = entry.config.readiness?.tcp.port ?? entry.config.port;
|
|
287
|
-
return port === void 0 ? "—" : String(port);
|
|
288
|
-
};
|
|
289
|
-
const VALID_LIST_FORMATS = /* @__PURE__ */ new Set(["json", "table"]);
|
|
290
|
-
const serviceListExecute = async ({ logger, options, workspaceRoot: wsRoot }) => {
|
|
291
|
-
const workspaceRoot = requireWorkspace(wsRoot);
|
|
292
|
-
const format = options.format ?? "table";
|
|
293
|
-
if (!VALID_LIST_FORMATS.has(format)) {
|
|
294
|
-
pail.error(`Invalid --format "${format}". Expected one of: ${[...VALID_LIST_FORMATS].sort().join(", ")}.`);
|
|
295
|
-
process.exitCode = 1;
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
const { surviving: entries } = await pruneDead(workspaceRoot);
|
|
299
|
-
if (format === "json") {
|
|
300
|
-
const now = Date.now();
|
|
301
|
-
process.stdout.write(
|
|
302
|
-
`${JSON.stringify(
|
|
303
|
-
entries.map((entry) => {
|
|
304
|
-
const startedMs = Date.parse(entry.startedAt);
|
|
305
|
-
return {
|
|
306
|
-
ageMs: Number.isFinite(startedMs) ? now - startedMs : null,
|
|
307
|
-
alive: isAlive(entry.pid),
|
|
308
|
-
command: entry.command,
|
|
309
|
-
cwd: entry.cwd,
|
|
310
|
-
env: entry.env,
|
|
311
|
-
id: entry.id,
|
|
312
|
-
logFile: entry.logFile,
|
|
313
|
-
pid: entry.pid,
|
|
314
|
-
port: entry.config.readiness?.tcp.port ?? entry.config.port ?? null,
|
|
315
|
-
startedAt: entry.startedAt,
|
|
316
|
-
visVersion: entry.visVersion
|
|
317
|
-
};
|
|
318
|
-
}),
|
|
319
|
-
void 0,
|
|
320
|
-
2
|
|
321
|
-
)}
|
|
322
|
-
`
|
|
323
|
-
);
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
if (entries.length === 0) {
|
|
327
|
-
pail.info("No running services registered for this workspace.");
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
const renderedAt = Date.now();
|
|
331
|
-
const rows = entries.map((entry) => {
|
|
332
|
-
const startedMs = Date.parse(entry.startedAt);
|
|
333
|
-
return {
|
|
334
|
-
age: Number.isFinite(startedMs) ? formatAge(startedMs, renderedAt) : "?",
|
|
335
|
-
id: entry.id,
|
|
336
|
-
log: entry.logFile,
|
|
337
|
-
pid: String(entry.pid),
|
|
338
|
-
port: formatPort(entry)
|
|
339
|
-
};
|
|
340
|
-
});
|
|
341
|
-
const idWidth = Math.max(2, ...rows.map((r) => r.id.length));
|
|
342
|
-
const pidWidth = Math.max(3, ...rows.map((r) => r.pid.length));
|
|
343
|
-
const portWidth = Math.max(4, ...rows.map((r) => r.port.length));
|
|
344
|
-
const ageWidth = Math.max(3, ...rows.map((r) => r.age.length));
|
|
345
|
-
logger.info(` ${"id".padEnd(idWidth)} ${"pid".padEnd(pidWidth)} ${"port".padEnd(portWidth)} ${"age".padEnd(ageWidth)} log`);
|
|
346
|
-
logger.info(` ${"-".repeat(idWidth)} ${"-".repeat(pidWidth)} ${"-".repeat(portWidth)} ${"-".repeat(ageWidth)} ---`);
|
|
347
|
-
for (const row of rows) {
|
|
348
|
-
logger.info(` ${row.id.padEnd(idWidth)} ${row.pid.padEnd(pidWidth)} ${row.port.padEnd(portWidth)} ${row.age.padEnd(ageWidth)} ${row.log}`);
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
const serviceStatusExecute = async ({ argument, options, workspaceRoot: wsRoot }) => {
|
|
352
|
-
const workspaceRoot = requireWorkspace(wsRoot);
|
|
353
|
-
const targetId = argument[0]?.trim();
|
|
354
|
-
if (!targetId) {
|
|
355
|
-
pail.error("Missing target id. Usage: vis service status <project>:<target>");
|
|
356
|
-
process.exitCode = 1;
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
const entry = await readEntry(workspaceRoot, targetId);
|
|
360
|
-
if (!entry) {
|
|
361
|
-
pail.error(`No service registered for ${targetId}.`);
|
|
362
|
-
process.exitCode = 1;
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
if (!isAlive(entry.pid)) {
|
|
366
|
-
pail.error(`Service ${targetId} is not running (pid ${String(entry.pid)} is dead). Run \`vis service start ${targetId}\` to recover.`);
|
|
367
|
-
process.exitCode = 1;
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
try {
|
|
371
|
-
await runReadiness(entry.config, { timeoutMs: options.timeout });
|
|
372
|
-
pail.success(`${targetId} healthy (pid ${String(entry.pid)})`);
|
|
373
|
-
} catch (error) {
|
|
374
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
375
|
-
pail.error(`${targetId} probe failed: ${message}`);
|
|
376
|
-
process.exitCode = 1;
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
const serviceRestartExecute = async ({
|
|
380
|
-
argument,
|
|
381
|
-
options,
|
|
382
|
-
visConfig,
|
|
383
|
-
workspaceRoot: wsRoot
|
|
384
|
-
}) => {
|
|
385
|
-
const workspaceRoot = requireWorkspace(wsRoot);
|
|
386
|
-
const targetId = argument[0]?.trim();
|
|
387
|
-
if (!targetId) {
|
|
388
|
-
pail.error("Missing target id. Usage: vis service restart <project>:<target>");
|
|
389
|
-
process.exitCode = 1;
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
await stopService({ graceMs: options.graceMs, id: targetId, workspaceRoot });
|
|
393
|
-
const resolved = await resolveTarget(workspaceRoot, visConfig, targetId);
|
|
394
|
-
if (!resolved) {
|
|
395
|
-
process.exitCode = 1;
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
try {
|
|
399
|
-
const { entry } = await startService({
|
|
400
|
-
command: resolved.command,
|
|
401
|
-
config: resolved.service,
|
|
402
|
-
cwd: resolved.cwd,
|
|
403
|
-
env: resolved.env,
|
|
404
|
-
id: targetId,
|
|
405
|
-
readinessTimeoutMs: options.timeout,
|
|
406
|
-
skipReadiness: options.noReadiness === true,
|
|
407
|
-
workspaceRoot
|
|
408
|
-
});
|
|
409
|
-
pail.success(`Restarted ${targetId} (pid ${String(entry.pid)})`);
|
|
410
|
-
} catch (error) {
|
|
411
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
412
|
-
pail.error(`Failed to restart ${targetId}: ${message}`);
|
|
413
|
-
process.exitCode = 1;
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
const TAIL_POLL_MS = 200;
|
|
417
|
-
const TAIL_MAX_CHUNK_BYTES = 1024 * 1024;
|
|
418
|
-
const tailLog = async (logFile) => {
|
|
419
|
-
let position = 0;
|
|
420
|
-
try {
|
|
421
|
-
const initial = await stat(logFile);
|
|
422
|
-
position = initial.size;
|
|
423
|
-
} catch {
|
|
424
|
-
position = 0;
|
|
425
|
-
}
|
|
426
|
-
const controller = new AbortController();
|
|
427
|
-
let sigintReceived = false;
|
|
428
|
-
const onSigint = () => {
|
|
429
|
-
sigintReceived = true;
|
|
430
|
-
controller.abort();
|
|
431
|
-
};
|
|
432
|
-
const onSigterm = () => {
|
|
433
|
-
controller.abort();
|
|
434
|
-
};
|
|
435
|
-
process.on("SIGINT", onSigint);
|
|
436
|
-
process.on("SIGTERM", onSigterm);
|
|
437
|
-
let tickInFlight = false;
|
|
438
|
-
const tickOnce = async () => {
|
|
439
|
-
if (tickInFlight) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
tickInFlight = true;
|
|
443
|
-
try {
|
|
444
|
-
const stats = await stat(logFile).catch(() => void 0);
|
|
445
|
-
if (!stats) {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
if (stats.size < position) {
|
|
449
|
-
position = 0;
|
|
450
|
-
}
|
|
451
|
-
if (stats.size === position) {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
const available = stats.size - position;
|
|
455
|
-
const chunkSize = Math.min(available, TAIL_MAX_CHUNK_BYTES);
|
|
456
|
-
const handle = await open(logFile, "r");
|
|
457
|
-
try {
|
|
458
|
-
const buffer = Buffer.alloc(chunkSize);
|
|
459
|
-
await handle.read(buffer, 0, chunkSize, position);
|
|
460
|
-
process.stdout.write(buffer);
|
|
461
|
-
position += chunkSize;
|
|
462
|
-
} finally {
|
|
463
|
-
await handle.close().catch(() => {
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
} finally {
|
|
467
|
-
tickInFlight = false;
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
try {
|
|
471
|
-
const watcher = (async () => {
|
|
472
|
-
try {
|
|
473
|
-
for await (const _event of watch(logFile, { signal: controller.signal })) {
|
|
474
|
-
await tickOnce();
|
|
475
|
-
}
|
|
476
|
-
} catch {
|
|
477
|
-
}
|
|
478
|
-
})();
|
|
479
|
-
while (!controller.signal.aborted) {
|
|
480
|
-
await tickOnce();
|
|
481
|
-
await new Promise((resolve) => {
|
|
482
|
-
const timer = setTimeout(resolve, TAIL_POLL_MS);
|
|
483
|
-
controller.signal.addEventListener(
|
|
484
|
-
"abort",
|
|
485
|
-
() => {
|
|
486
|
-
clearTimeout(timer);
|
|
487
|
-
resolve();
|
|
488
|
-
},
|
|
489
|
-
{ once: true }
|
|
490
|
-
);
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
await watcher.catch(() => {
|
|
494
|
-
});
|
|
495
|
-
await tickOnce().catch(() => {
|
|
496
|
-
});
|
|
497
|
-
} finally {
|
|
498
|
-
process.off("SIGINT", onSigint);
|
|
499
|
-
process.off("SIGTERM", onSigterm);
|
|
500
|
-
}
|
|
501
|
-
if (sigintReceived) {
|
|
502
|
-
process.exitCode = 130;
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
const serviceLogsExecute = async ({ argument, options, workspaceRoot: wsRoot }) => {
|
|
506
|
-
const workspaceRoot = requireWorkspace(wsRoot);
|
|
507
|
-
const targetId = argument[0]?.trim();
|
|
508
|
-
if (!targetId) {
|
|
509
|
-
pail.error("Missing target id. Usage: vis service logs <project>:<target>");
|
|
510
|
-
process.exitCode = 1;
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
const entry = await readEntry(workspaceRoot, targetId);
|
|
514
|
-
if (!entry) {
|
|
515
|
-
pail.error(`No service registered for ${targetId}.`);
|
|
516
|
-
process.exitCode = 1;
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
if (!await isAccessible(entry.logFile)) {
|
|
520
|
-
pail.warn(`Log file is missing for ${targetId}: ${entry.logFile}`);
|
|
521
|
-
process.exitCode = 1;
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
if (options.follow === true) {
|
|
525
|
-
await tailLog(entry.logFile);
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
const handle = await open(entry.logFile, "r");
|
|
529
|
-
try {
|
|
530
|
-
const stream = handle.createReadStream();
|
|
531
|
-
await new Promise((resolve, reject) => {
|
|
532
|
-
stream.on("end", resolve);
|
|
533
|
-
stream.on("error", reject);
|
|
534
|
-
stream.pipe(process.stdout, { end: false });
|
|
535
|
-
});
|
|
536
|
-
} finally {
|
|
537
|
-
await handle.close().catch(() => {
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
export { serviceListExecute, serviceLogsExecute, serviceRestartExecute, serviceStartExecute, serviceStatusExecute, serviceStopExecute };
|
|
1
|
+
var ae=Object.defineProperty;var j=(t,e)=>ae(t,"name",{value:e,configurable:!0});import{createRequire as ce}from"node:module";import{dim as p,bold as b,cyan as S}from"@visulima/colorize";import{join as N,dirname as Y,isAbsolute as L,relative as Q,sep as pe,resolve as ue}from"@visulima/path";import{isAccessibleSync as _,walkSync as X,ensureDirSync as Z,writeFileSync as de}from"@visulima/fs";import{f as $e}from"../packem_shared/giget-CcEy_Elm.js";import{p as l}from"./bin.js";const le=ce(import.meta.url),x=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,D=j(t=>{if(typeof x<"u"&&x.versions&&x.versions.node){const[e,r]=x.versions.node.split(".").map(Number);if(e>22||e===22&&r>=3||e===20&&r>=16)return x.getBuiltinModule(t)}return le(t)},"__cjs_getBuiltinModule"),{fileURLToPath:fe}=D("node:url"),{createInterface:me}=D("node:readline"),{mkdtempSync:he,rmSync:ge}=D("node:fs"),{tmpdir:we}=D("node:os"),{spawnSync:ye}=D("node:child_process");var ve=Object.defineProperty,y=j((t,e)=>ve(t,"name",{value:e,configurable:!0}),"i");const ee=[".ts",".mts",".cts",".js",".mjs",".cjs"],be="template.yml",Se=[".d",".test",".spec",".config",".bench",".stories"],Te=[".d.ts",".d.mts",".d.cts",".js.map",".mjs.map",".cjs.map",".ts.map"],te=y(t=>{for(const e of ee)if(t.endsWith(e))return t.slice(0,-e.length);return t},"stripExtension"),Oe=y(t=>{if(Te.some(r=>t.endsWith(r))||!ee.some(r=>t.endsWith(r)))return!1;const e=te(t);return!Se.some(r=>e.endsWith(r))},"isNativeFile"),z=y((t,e)=>{const r=[];if(!_(t))return r;for(const s of X(t,{includeDirs:!1,includeSymlinks:!1,maxDepth:1})){if(!Oe(s.name))continue;const n=te(s.name);r.push({load:y(()=>Ne(s.path),"load"),name:n,path:s.path,source:e})}return r},"scanNativeDirectory"),B=y((t,e)=>{const r=[];if(!_(t))return r;for(const s of X(t,{includeFiles:!1,includeSymlinks:!1,maxDepth:1})){if(s.path===t)continue;const n=N(s.path,be);_(n)&&r.push({load:y(()=>Re(s.path,s.name),"load"),name:s.name,path:s.path,source:e})}return r},"scanMoonDirectory"),je=y(()=>{try{const t=fe(import.meta.url),e=N(Y(t),"..","..","templates");return _(e)?e:void 0}catch{return}},"resolveBuiltinTemplatesDirectory"),q=y(t=>{const{extraDirectories:e=[],onWarning:r,workspaceRoot:s}=t,n=[...z(N(s,".vis","templates"),"native"),...B(N(s,".vis","templates"),"moon"),...B(N(s,".moon","templates"),"moon")];for(const o of e)n.push(...B(o,"config")),n.push(...z(o,"config"));const i=je();i&&n.push(...B(i,"builtin"));const a=new Map;for(const o of n){const c=a.get(o.name);if(!c){a.set(o.name,o);continue}r&&r(`Template "${o.name}" exists in multiple sources — using ${c.source} (${c.path}), ignoring ${o.source} (${o.path}).`)}return[...a.values()].sort((o,c)=>o.name.localeCompare(c.name))},"discoverTemplates"),Ne=y(async t=>{const{loadNativeTemplate:e}=await import("./loader.js");return e(t)},"loadNativeFromPath"),Re=y(async(t,e)=>{const{loadMoonTemplate:r}=await import("./index.js");return r(t,e)},"loadMoonFromPath");var ke=Object.defineProperty,v=j((t,e)=>ke(t,"name",{value:e,configurable:!0}),"c");const A=v((t,e)=>new Promise(r=>{t.question(e,s=>{r(s.trim())})}),"ask"),Ee=v(async(t,e,r)=>{const s=await A(t,` ${e} ${p(r?"[Y/n]":"[y/N]")} `);return s===""?r:s.toLowerCase()==="y"||s.toLowerCase()==="yes"},"confirm"),xe=v(async(t,e,r,s)=>{process.stderr.write(` ${e}
|
|
2
|
+
`);for(const[n,i]of r.entries()){const a=b(S(` ${String(n+1)}.`)),o=i===s?p(" (default)"):"";process.stderr.write(`${a} ${i}${o}
|
|
3
|
+
`)}for(;;){const n=await A(t,`
|
|
4
|
+
${p(`Enter choice (1-${String(r.length)}):`)} `);if(n===""&&s!==void 0)return s;const i=Number.parseInt(n,10);if(Number.isInteger(i)&&i>=1&&i<=r.length)return r[i-1];const a=r.find(o=>o===n);if(a)return a;process.stderr.write(` ${p("Invalid choice. Try again.")}
|
|
5
|
+
`)}},"selectOne"),_e=v(async(t,e,r,s)=>{process.stderr.write(` ${e} ${p("(comma-separated numbers)")}
|
|
6
|
+
`);for(const[n,i]of r.entries()){const a=b(S(` ${String(n+1)}.`)),o=s.includes(i)?p(" (default)"):"";process.stderr.write(`${a} ${i}${o}
|
|
7
|
+
`)}for(;;){const n=await A(t,`
|
|
8
|
+
${p("Enter choices:")} `);if(n===""&&s.length>0)return s;const i=n.split(",").map(a=>Number.parseInt(a.trim(),10)).filter(a=>Number.isInteger(a)&&a>=1&&a<=r.length);if(i.length>0)return i.map(a=>r[a-1]);process.stderr.write(` ${p("Invalid choice. Try again.")}
|
|
9
|
+
`)}},"selectMany"),De=v(t=>` ${p(`${t}:`)} `,"promptText"),Pe=v((t,e)=>e.prompt??t,"variableLabel"),Ie=v(t=>Object.entries(t).sort(([e,r],[s,n])=>{const i=r.order??0,a=n.order??0;return i!==a?i-a:e.localeCompare(s)}),"sortVariables"),H=v((t,e)=>{switch(t.type){case"array":return e.split(",").map(r=>r.trim()).filter(Boolean);case"boolean":return e==="true"||e==="1"||e==="yes"||e==="y";case"enum":return t.multiple?e.split(",").map(r=>r.trim()).filter(Boolean):e;case"number":{const r=Number(e);if(Number.isNaN(r))throw new TypeError(`Expected a number, got "${e}"`);return r}default:return e}},"parseValue"),C=v((t,e,r)=>{if(e.type==="enum"){const s=Array.isArray(r)?r:[r];for(const n of s)if(typeof n!="string"||!e.values.includes(n))throw new Error(`Variable "${t}" must be one of: ${e.values.join(", ")} (got "${String(n)}")`)}},"validateValue"),We=v(async t=>{const{defaults:e,interactive:r,overrides:s,variables:n}=t,i={},a=r?me({input:process.stdin,output:process.stderr}):null;try{for(const[o,c]of Ie(n)){if(Object.hasOwn(s,o)){const w=H(c,s[o]??"");C(o,c,w),i[o]=w;continue}if(e||!r||c.internal){if(c.default!==void 0){C(o,c,c.default),i[o]=c.default;continue}if(c.required)throw new Error(`Required variable "${o}" not provided. Pass --${o}=<value> or remove --defaults.`);continue}const g=Pe(o,c);let m;if(c.type==="boolean")m=await Ee(a,g,!!(c.default??!1));else if(c.type==="enum")if(c.multiple){const w=Array.isArray(c.default)?c.default:[];m=await _e(a,g,c.values,w)}else{const w=typeof c.default=="string"?c.default:void 0;m=await xe(a,g,c.values,w)}else{const w=c.default===void 0?"":` (${String(c.default)})`,T=await A(a,De(`${g}${w}`));if(T===""&&c.default!==void 0)m=c.default;else if(T===""){if(c.required)throw new Error(`Variable "${o}" is required`);continue}else m=H(c,T)}C(o,c,m),i[o]=m}return i}finally{a?.close()}},"collectOptions");var Be=Object.defineProperty,V=j((t,e)=>Be(t,"name",{value:e,configurable:!0}),"o");const qe=["git://","npm://","https://","github:","gitlab:","bitbucket:","sourcehut:"],Ae=V(t=>qe.some(e=>t.startsWith(e)),"isRemoteSource"),Me=V(async(t,e={})=>{const r=e.targetDirectory===void 0,s=e.targetDirectory??he(N(we(),"vis-generate-")),n=V(()=>{if(r)try{ge(s,{force:!0,recursive:!0})}catch{}},"cleanup");l.info(`Downloading ${t}…`);try{const i=await $e(t,{auth:e.auth||process.env.GIGET_AUTH||process.env.GITHUB_TOKEN||process.env.GH_TOKEN||void 0,dir:s,force:!0,preferOffline:e.preferOffline});return{cleanup:n,directory:i.dir}}catch(i){n();const a=i instanceof Error?i.message:String(i);throw l.warn(`Failed to download template: ${a}`),i}},"fetchRemoteTemplate");var Fe=Object.defineProperty,R=j((t,e)=>Fe(t,"name",{value:e,configurable:!0}),"a");const re=R((t,e="")=>{const r=[];for(const[s,n]of Object.entries(t)){const i=e?`${e}/${s}`:s;typeof n=="string"||Buffer.isBuffer(n)?r.push({content:n,path:i}):n&&typeof n=="object"&&r.push(...re(n,i))}return r},"flattenTree"),Ce=R(t=>t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`,"formatSize"),Le=R((t,e)=>{if(L(e))throw new Error(`Refusing to write outside destination: template produced absolute path "${e}".`);const r=N(t,e),s=Q(t,r);if(s===".."||s.startsWith(`..${pe}`)||L(s))throw new Error(`Refusing to write outside destination: "${e}" resolves to "${r}" which escapes "${t}".`);return r},"safeJoinDestination"),Ve=R((t,e)=>{Z(Y(t)),de(t,e)},"writeOne"),Ue=R((t,e,r=!1)=>{const s=typeof t=="string"?[t]:t.commands,n=typeof t=="string"?r:t.silent??r;for(const i of s){n||l.info(`$ ${i}`);const a=ye(i,{cwd:e,shell:!0,stdio:n?"ignore":"inherit"});if(a.status!==0)return l.warn(`Script failed (exit ${String(a.status)}): ${i}`),!1}return!0},"runScript"),Ge=R(t=>{const e=new Map;for(const r of t){const s=typeof r=="string"?0:r.phase??0,n=e.get(s);n?n.push(r):e.set(s,[r])}return[...e.entries()].sort(([r],[s])=>r-s)},"groupByPhase"),Je=R(async(t,e)=>{const r={builtins:{dest_dir:e.destination,dest_rel_dir:Q(e.workspaceRoot,e.destination)||".",working_dir:e.cwd,workspace_root:e.workspaceRoot},options:e.options},s=await t.produce(r),n=s.files?re(s.files):[],i=s.filesMeta??{},a=[];for(const o of n){const c=Le(e.destination,o.path);a.push({file:o,meta:i[o.path]??{},target:c})}if(e.dryRun){l.info(`${b(S("Plan"))} ${p("(dry-run, no files written)")}`);for(const o of a){const c=Buffer.isBuffer(o.file.content)?o.file.content.length:Buffer.byteLength(o.file.content,"utf8");process.stderr.write(` ${p("write")} ${o.file.path} ${p(`(${Ce(c)})`)}
|
|
10
|
+
`)}}else{Z(e.destination);let o=0,c=0;for(const g of a){const{file:m,meta:w,target:T}=g,M=_(T),O=e.force||w.force===!0;if(M&&!O){l.warn(`Skipped existing file: ${m.path} (use --force or set frontmatter force: true to overwrite)`),c+=1;continue}Ve(T,m.content),o+=1}l.success(`Wrote ${String(o)} file${o===1?"":"s"}${c>0?`, skipped ${String(c)}`:""}`)}if(!e.dryRun&&!e.skipScripts&&s.scripts&&s.scripts.length>0){const o=Ge(s.scripts);l.info(`Running ${String(s.scripts.length)} script${s.scripts.length===1?"":"s"} across ${String(o.length)} phase${o.length===1?"":"s"}…`);for(const[,c]of o)if((await Promise.all(c.map(g=>Promise.resolve(Ue(g,e.destination))))).includes(!1))throw new Error("Script failed — aborting.")}if(s.suggestions&&s.suggestions.length>0){process.stderr.write(`
|
|
11
|
+
`),l.notice("Next steps:");for(const o of s.suggestions)process.stderr.write(` ${p("•")} ${o}
|
|
12
|
+
`)}},"runTemplate");var Ke=Object.defineProperty,$=j((t,e)=>Ke(t,"name",{value:e,configurable:!0}),"u");const ze=$(async t=>{let e;try{e=(await t.load()).about?.description}catch{}return{description:e,name:t.name,path:t.path,source:t.source}},"toListEntry"),He=$((t,e)=>{const r={default:e.default,name:t,order:e.order,prompt:e.prompt,required:e.required,type:e.type};return e.type==="enum"&&(r.multiple=e.multiple,r.values=e.values),r},"summarizeVariable"),Ye=$(async t=>{const e=await t.load(),r=Object.entries(e.options??{}).sort(([s,n],[i,a])=>{const o=n.order??0,c=a.order??0;return o===c?s.localeCompare(i):o-c}).map(([s,n])=>He(s,n));return{description:e.about?.description??"",destination:e.destination,name:t.name,path:t.path,source:t.source,variables:r}},"describeTemplate"),Qe=$(t=>{if(t.length===0){l.info("No templates found."),l.notice("Create one at .vis/templates/<name>.ts (programmatic) or .vis/templates/<name>/ (moon-format with template.yml).");return}l.info("Available templates:");for(const e of t){const r=p(`(${e.source})`);process.stderr.write(` ${b(S(e.name))} ${r}
|
|
13
|
+
`)}},"printList"),Xe=$(t=>{const e={},r=[];for(const s of t){if(!s.startsWith("--")){r.push(s);continue}const n=s.indexOf("=");if(n===-1){const o=s.slice(2);o.startsWith("no-")?e[o.slice(3)]="false":e[o]="true";continue}const i=s.slice(2,n),a=s.slice(n+1);e[i]=a}return{overrides:e,remaining:r}},"parsePassthroughOverrides"),Ze=$(async t=>{const{createInterface:e}=await import("node:readline"),r=e({input:process.stdin,output:process.stderr});try{process.stderr.write(` ${b(S("vis generate"))} ${p("— pick a template")}
|
|
14
|
+
|
|
15
|
+
`);for(const[s,n]of t.entries()){const i=b(S(` ${String(s+1)}.`));process.stderr.write(`${i} ${n.name} ${p(`(${n.source})`)}
|
|
16
|
+
`)}return new Promise((s,n)=>{r.question(`
|
|
17
|
+
${p(`Enter choice (1-${String(t.length)}):`)} `,i=>{const a=Number.parseInt(i.trim(),10);Number.isInteger(a)&&a>=1&&a<=t.length?s(t[a-1].name):n(new Error("Invalid choice."))})})}finally{r.close()}},"pickInteractive"),at=$(async({argument:t,options:e,rawUnknown:r,visConfig:s,workspaceRoot:n})=>{const i=e.cwd||n||process.cwd(),a=n??i,o=s?.generator,c=Array.isArray(t)?t:t?[t]:[];if(e.list){const f=q({extraDirectories:o?.templates??[],onWarning:$(u=>{l.warn(u)},"onWarning"),workspaceRoot:a});if(e.json){const u=await Promise.all(f.map(d=>ze(d)));process.stdout.write(`${JSON.stringify(u,null,2)}
|
|
18
|
+
`);return}Qe(f);return}if(e.describe){const f=c[0];if(!f)throw new Error("`--describe` requires a template name. Run `vis generate --list` to see available templates.");const u=q({extraDirectories:o?.templates??[],onWarning:$(h=>{l.warn(h)},"onWarning"),workspaceRoot:a}).find(h=>h.name===f);if(!u)throw new Error(`Template "${f}" not found. Run \`vis generate --list\` to see available templates.`);const d=await Ye(u);if(e.json){process.stdout.write(`${JSON.stringify(d,null,2)}
|
|
19
|
+
`);return}if(l.info(`Template: ${b(S(d.name))} ${p(`(${d.source})`)}`),d.description&&l.info(d.description),d.destination&&l.info(`Destination: ${p(d.destination)}`),d.variables.length===0)l.info("No variables.");else{l.info("Variables:");for(const h of d.variables){const W=[h.type];h.required&&W.push("required"),h.default!==void 0&&W.push(`default=${JSON.stringify(h.default)}`),h.values&&W.push(`values=${h.values.join("|")}`),process.stderr.write(` ${b(S(h.name))} ${p(`(${W.join(", ")})`)}
|
|
20
|
+
`)}}return}let g=[...r??[]];if(g.length===0){const f=process.argv.slice(2),u=f.indexOf("--");u!==-1&&(g=f.slice(u+1))}const m=c.indexOf("--"),w=m===-1?[]:c.slice(m+1),T=m===-1?c:c.slice(0,m),{overrides:M}=Xe([...w,...g]);let O,P,I;const E=T[0];let F;if(E&&Ae(E)){const f=await Me(E,{auth:o?.auth,preferOffline:!!e.preferOffline||o?.preferOffline});F=f.cleanup;try{const u=q({extraDirectories:[f.directory],workspaceRoot:a}).find(d=>d.path.startsWith(f.directory));if(!u)throw new Error(`Downloaded template at ${f.directory} contains no template.yml or *.ts entrypoint.`);O=await u.load(),P=u.name,I=O.destination}catch(u){throw F(),u}}else{const f=q({extraDirectories:o?.templates??[],onWarning:$(h=>{l.warn(h)},"onWarning"),workspaceRoot:a});if(f.length===0)throw new Error("No templates found. Create one at .vis/templates/<name>.ts or .vis/templates/<name>/template.yml.");let u;if(E)u=E;else{if(e.noInteractive||!process.stdin.isTTY)throw new Error("No template specified. Pass a template name (see `vis generate --list`) or run interactively in a terminal.");u=await Ze(f)}const d=f.find(h=>h.name===u);if(!d)throw new Error(`Template "${u}" not found. Run 'vis generate --list' to see available templates.`);O=await d.load(),P=d.name,I=O.destination}const U=e.to,G=!!e.dryRun,se=!!e.force,J=!!e.defaults,oe=!!e.skipScripts,ne=!e.noInteractive&&!!process.stdin.isTTY&&!J;let k;U?k=U:I?k=I:k=".";const K=L(k)?k:ue(i,k);l.info(`Template: ${b(S(P))}`),l.info(`Target: ${p(K)}`),process.stderr.write(`
|
|
21
|
+
`);const ie=await We({defaults:J,interactive:ne,overrides:M,variables:O.options??{}});try{await Je(O,{cwd:i,destination:K,dryRun:G,force:se,options:ie,skipScripts:oe,workspaceRoot:a}),G||(process.stderr.write(`
|
|
22
|
+
`),l.success(`Template '${P}' applied.`))}finally{F?.()}},"execute");export{at as default};
|