@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,158 +0,0 @@
|
|
|
1
|
-
import { SpanStatusCode } from '@opentelemetry/api';
|
|
2
|
-
import { definePlugin } from '../packem_chunks/applyDefaults.js';
|
|
3
|
-
|
|
4
|
-
class VisConfigError extends Error {
|
|
5
|
-
chain;
|
|
6
|
-
constructor(message, chain, options) {
|
|
7
|
-
super(message, options);
|
|
8
|
-
this.name = this.constructor.name;
|
|
9
|
-
this.chain = chain;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
class VisConfigCycleError extends VisConfigError {
|
|
14
|
-
constructor(reentered, chain) {
|
|
15
|
-
const trail = [...chain, `${reentered} (re-enters)`].join(" → ");
|
|
16
|
-
super(`Config cycle: ${trail}`, chain);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
class VisConfigLoadError extends VisConfigError {
|
|
21
|
-
constructor(filePath, chain, cause) {
|
|
22
|
-
const causeMessage = cause instanceof Error ? cause.message : String(cause);
|
|
23
|
-
const trail = chain.length > 0 ? `
|
|
24
|
-
Chain: ${chain.join(" → ")}` : "";
|
|
25
|
-
super(`Failed to load ${filePath}: ${causeMessage}${trail}`, chain, { cause });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const formatChain = (chain) => chain.join(" → ");
|
|
30
|
-
class VisConfigNotFoundError extends VisConfigError {
|
|
31
|
-
constructor(specifier, chain, attempted) {
|
|
32
|
-
const head = chain.length > 0 ? chain[chain.length - 1] : "<unknown>";
|
|
33
|
-
const tried = attempted.length > 0 ? `
|
|
34
|
-
Tried:
|
|
35
|
-
${attempted.join("\n ")}` : "";
|
|
36
|
-
super(`Cannot resolve "${specifier}" extended from ${head}.${tried}
|
|
37
|
-
Chain: ${formatChain(chain)}`, chain);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const INHERIT_SENTINEL = "@inherit";
|
|
42
|
-
const ARRAY_FIELDS = ["aliases", "dependsOn", "inputs", "outputs"];
|
|
43
|
-
const mergeArrayWithInherit = (parent, child) => {
|
|
44
|
-
if (child === void 0) {
|
|
45
|
-
return parent === void 0 ? void 0 : [...parent];
|
|
46
|
-
}
|
|
47
|
-
if (!child.includes(INHERIT_SENTINEL)) {
|
|
48
|
-
return [...child];
|
|
49
|
-
}
|
|
50
|
-
const result = [];
|
|
51
|
-
for (const entry of child) {
|
|
52
|
-
if (entry === INHERIT_SENTINEL) {
|
|
53
|
-
if (parent !== void 0) {
|
|
54
|
-
result.push(...parent);
|
|
55
|
-
}
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
result.push(entry);
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
};
|
|
62
|
-
const mergeTargetWithInherit = (parent, child) => {
|
|
63
|
-
const merged = { ...parent, ...child };
|
|
64
|
-
for (const field of ARRAY_FIELDS) {
|
|
65
|
-
const parentValue = parent?.[field];
|
|
66
|
-
const childValue = child?.[field];
|
|
67
|
-
const result = mergeArrayWithInherit(parentValue, childValue);
|
|
68
|
-
if (result === void 0) {
|
|
69
|
-
delete merged[field];
|
|
70
|
-
} else {
|
|
71
|
-
merged[field] = result;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return merged;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const otelPlugin = (options) => {
|
|
78
|
-
const { renameSpan, tracer } = options;
|
|
79
|
-
let runSpan;
|
|
80
|
-
const taskSpans = /* @__PURE__ */ new Map();
|
|
81
|
-
return definePlugin({
|
|
82
|
-
hooks: {
|
|
83
|
-
"run:after": (results) => {
|
|
84
|
-
if (!runSpan) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const failed = [...results.values()].filter((r) => r.status === "failure").length;
|
|
88
|
-
runSpan.setAttribute("vis.run.tasks_total", results.size);
|
|
89
|
-
runSpan.setAttribute("vis.run.tasks_failed", failed);
|
|
90
|
-
if (failed > 0) {
|
|
91
|
-
runSpan.setStatus({ code: SpanStatusCode.ERROR, message: `${String(failed)} task(s) failed` });
|
|
92
|
-
} else {
|
|
93
|
-
runSpan.setStatus({ code: SpanStatusCode.OK });
|
|
94
|
-
}
|
|
95
|
-
runSpan.end();
|
|
96
|
-
runSpan = void 0;
|
|
97
|
-
for (const stray of taskSpans.values()) {
|
|
98
|
-
stray.end();
|
|
99
|
-
}
|
|
100
|
-
taskSpans.clear();
|
|
101
|
-
},
|
|
102
|
-
"run:before": (context) => {
|
|
103
|
-
if (runSpan) {
|
|
104
|
-
runSpan.setStatus({ code: SpanStatusCode.ERROR, message: "run:before fired while previous run was still active" });
|
|
105
|
-
runSpan.end();
|
|
106
|
-
}
|
|
107
|
-
for (const stray of taskSpans.values()) {
|
|
108
|
-
stray.end();
|
|
109
|
-
}
|
|
110
|
-
taskSpans.clear();
|
|
111
|
-
runSpan = tracer.startSpan("vis.run", {
|
|
112
|
-
attributes: {
|
|
113
|
-
"vis.run.task_count": context.tasks.length,
|
|
114
|
-
"vis.workspace_root": context.workspaceRoot
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
},
|
|
118
|
-
"task:after": (task, result) => {
|
|
119
|
-
const span = taskSpans.get(task.id);
|
|
120
|
-
if (!span) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
span.setAttribute("vis.task.exit_code", result.code ?? 0);
|
|
124
|
-
span.setAttribute("vis.task.cache_status", result.status);
|
|
125
|
-
span.end();
|
|
126
|
-
taskSpans.delete(task.id);
|
|
127
|
-
},
|
|
128
|
-
"task:before": (task) => {
|
|
129
|
-
const existing = taskSpans.get(task.id);
|
|
130
|
-
if (existing) {
|
|
131
|
-
existing.setStatus({ code: SpanStatusCode.ERROR, message: "retried — superseded by new attempt" });
|
|
132
|
-
existing.end();
|
|
133
|
-
}
|
|
134
|
-
const span = tracer.startSpan(renameSpan ? renameSpan(task) : task.id, {
|
|
135
|
-
attributes: {
|
|
136
|
-
"vis.task.id": task.id,
|
|
137
|
-
"vis.task.project": task.target.project,
|
|
138
|
-
"vis.task.target": task.target.target
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
taskSpans.set(task.id, span);
|
|
142
|
-
},
|
|
143
|
-
"task:failure": (task, result) => {
|
|
144
|
-
const span = taskSpans.get(task.id);
|
|
145
|
-
if (!span) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
span.setStatus({
|
|
149
|
-
code: SpanStatusCode.ERROR,
|
|
150
|
-
message: `Task failed with exit code ${String(result.code ?? -1)}`
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
name: "otel"
|
|
155
|
-
});
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
export { VisConfigCycleError as V, VisConfigLoadError as a, VisConfigNotFoundError as b, mergeTargetWithInherit as m, otelPlugin as o };
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
-
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
-
|
|
7
|
-
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
-
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
-
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
-
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
-
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
-
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
-
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
// Fallback to createRequire
|
|
17
|
-
return __cjs_require(module);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const {
|
|
21
|
-
Socket
|
|
22
|
-
} = __cjs_getBuiltinModule("node:net");
|
|
23
|
-
const {
|
|
24
|
-
createHash,
|
|
25
|
-
randomBytes
|
|
26
|
-
} = __cjs_getBuiltinModule("node:crypto");
|
|
27
|
-
const {
|
|
28
|
-
readdir,
|
|
29
|
-
rm,
|
|
30
|
-
unlink,
|
|
31
|
-
open,
|
|
32
|
-
readFile,
|
|
33
|
-
stat,
|
|
34
|
-
writeFile,
|
|
35
|
-
rename
|
|
36
|
-
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
37
|
-
const {
|
|
38
|
-
homedir
|
|
39
|
-
} = __cjs_getBuiltinModule("node:os");
|
|
40
|
-
import { readJson, ensureDir, isAccessible } from '@visulima/fs';
|
|
41
|
-
import { join } from '@visulima/path';
|
|
42
|
-
|
|
43
|
-
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
44
|
-
const POLL_INTERVAL_MS = 100;
|
|
45
|
-
class ServiceReadinessError extends Error {
|
|
46
|
-
constructor(message, elapsedMs) {
|
|
47
|
-
super(message);
|
|
48
|
-
this.elapsedMs = elapsedMs;
|
|
49
|
-
this.name = "ServiceReadinessError";
|
|
50
|
-
}
|
|
51
|
-
elapsedMs;
|
|
52
|
-
}
|
|
53
|
-
const waitForTcp = async (input) => {
|
|
54
|
-
const host = input.host ?? "127.0.0.1";
|
|
55
|
-
const { port } = input;
|
|
56
|
-
const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
57
|
-
const start = Date.now();
|
|
58
|
-
while (Date.now() - start < timeoutMs) {
|
|
59
|
-
const ok = await tryConnect(host, port);
|
|
60
|
-
if (ok) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
await delay(POLL_INTERVAL_MS);
|
|
64
|
-
}
|
|
65
|
-
throw new ServiceReadinessError(`Timed out waiting for ${host}:${String(port)} to accept TCP connections (${String(timeoutMs)}ms)`, Date.now() - start);
|
|
66
|
-
};
|
|
67
|
-
const tryConnect = (host, port) => new Promise((resolve) => {
|
|
68
|
-
const socket = new Socket();
|
|
69
|
-
const onDone = (ok) => {
|
|
70
|
-
socket.removeAllListeners();
|
|
71
|
-
socket.destroy();
|
|
72
|
-
resolve(ok);
|
|
73
|
-
};
|
|
74
|
-
socket.once("connect", () => onDone(true));
|
|
75
|
-
socket.once("error", () => onDone(false));
|
|
76
|
-
socket.setTimeout(POLL_INTERVAL_MS, () => onDone(false));
|
|
77
|
-
try {
|
|
78
|
-
socket.connect(port, host);
|
|
79
|
-
} catch {
|
|
80
|
-
onDone(false);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
const delay = (ms) => new Promise((resolve) => {
|
|
84
|
-
setTimeout(resolve, ms);
|
|
85
|
-
});
|
|
86
|
-
const runReadiness = async (config, override) => {
|
|
87
|
-
const probe = config.readiness ?? (config.port === void 0 ? void 0 : { tcp: { port: config.port } });
|
|
88
|
-
if (!probe) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
await waitForTcp({
|
|
92
|
-
host: probe.tcp.host,
|
|
93
|
-
port: probe.tcp.port,
|
|
94
|
-
timeoutMs: override?.timeoutMs ?? probe.tcp.timeoutMs
|
|
95
|
-
});
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const REGISTRY_ROOT_DIRNAME = ".vis-services";
|
|
99
|
-
const REGISTRY_FILE_MODE = 384;
|
|
100
|
-
const LOCK_TIMEOUT_MS = 5e3;
|
|
101
|
-
const LOCK_POLL_MS = 50;
|
|
102
|
-
const LOCK_STALE_MS = 3e4;
|
|
103
|
-
const hashWorkspace = (workspaceRoot) => createHash("sha256").update(workspaceRoot).digest("hex").slice(0, 12);
|
|
104
|
-
const getRegistryDir = async (workspaceRoot) => {
|
|
105
|
-
const directory = join(homedir(), REGISTRY_ROOT_DIRNAME, hashWorkspace(workspaceRoot));
|
|
106
|
-
try {
|
|
107
|
-
await ensureDir(directory);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
if (error.code === "ENOTDIR") {
|
|
110
|
-
throw new Error(
|
|
111
|
-
`Service registry path ${directory} exists but is not a directory. Remove it or move the conflicting file before running \`vis service\`.`
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
return directory;
|
|
117
|
-
};
|
|
118
|
-
const slugify = (id) => id.replaceAll("/", "_").replaceAll(":", "__");
|
|
119
|
-
const entryPath = (registryDirectory, id) => join(registryDirectory, `${slugify(id)}.json`);
|
|
120
|
-
const lockPath = (registryDirectory, id) => join(registryDirectory, `${slugify(id)}.lock`);
|
|
121
|
-
const readEntry = async (workspaceRoot, id) => {
|
|
122
|
-
const directory = await getRegistryDir(workspaceRoot);
|
|
123
|
-
const path = entryPath(directory, id);
|
|
124
|
-
if (!await isAccessible(path)) {
|
|
125
|
-
return void 0;
|
|
126
|
-
}
|
|
127
|
-
try {
|
|
128
|
-
return await readJson(path);
|
|
129
|
-
} catch {
|
|
130
|
-
return void 0;
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
const readAllEntries = async (workspaceRoot) => {
|
|
134
|
-
const directory = await getRegistryDir(workspaceRoot);
|
|
135
|
-
let names;
|
|
136
|
-
try {
|
|
137
|
-
names = await readdir(directory);
|
|
138
|
-
} catch {
|
|
139
|
-
return [];
|
|
140
|
-
}
|
|
141
|
-
const entries = [];
|
|
142
|
-
for (const name of names) {
|
|
143
|
-
if (!name.endsWith(".json")) {
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
try {
|
|
147
|
-
const entry = await readJson(join(directory, name));
|
|
148
|
-
entries.push(entry);
|
|
149
|
-
} catch {
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return entries;
|
|
153
|
-
};
|
|
154
|
-
const writeEntry = async (workspaceRoot, entry) => {
|
|
155
|
-
const directory = await getRegistryDir(workspaceRoot);
|
|
156
|
-
const finalPath = entryPath(directory, entry.id);
|
|
157
|
-
const stagingPath = join(directory, `.${randomBytes(8).toString("hex")}.tmp`);
|
|
158
|
-
await writeFile(stagingPath, `${JSON.stringify(entry, void 0, 2)}
|
|
159
|
-
`, { mode: REGISTRY_FILE_MODE });
|
|
160
|
-
try {
|
|
161
|
-
await rename(stagingPath, finalPath);
|
|
162
|
-
} catch (error) {
|
|
163
|
-
await rm(stagingPath, { force: true }).catch(() => {
|
|
164
|
-
});
|
|
165
|
-
throw error;
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
const deleteEntry = async (workspaceRoot, id, entry) => {
|
|
169
|
-
const directory = await getRegistryDir(workspaceRoot);
|
|
170
|
-
const resolved = entry ?? await readEntry(workspaceRoot, id);
|
|
171
|
-
await rm(entryPath(directory, id), { force: true });
|
|
172
|
-
if (resolved) {
|
|
173
|
-
await rm(resolved.logFile, { force: true }).catch(() => {
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
const isAlive = (pid) => {
|
|
178
|
-
try {
|
|
179
|
-
process.kill(pid, 0);
|
|
180
|
-
return true;
|
|
181
|
-
} catch {
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
const pruneDead = async (workspaceRoot) => {
|
|
186
|
-
const entries = await readAllEntries(workspaceRoot);
|
|
187
|
-
const pruned = [];
|
|
188
|
-
const surviving = [];
|
|
189
|
-
for (const entry of entries) {
|
|
190
|
-
if (isAlive(entry.pid)) {
|
|
191
|
-
surviving.push(entry);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const current = await readEntry(workspaceRoot, entry.id);
|
|
195
|
-
if (current && current.pid !== entry.pid) {
|
|
196
|
-
surviving.push(current);
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
await deleteEntry(workspaceRoot, entry.id, entry).catch(() => {
|
|
200
|
-
});
|
|
201
|
-
pruned.push(entry.id);
|
|
202
|
-
}
|
|
203
|
-
return { pruned, surviving };
|
|
204
|
-
};
|
|
205
|
-
const sleep = (ms) => new Promise((resolve) => {
|
|
206
|
-
setTimeout(resolve, ms);
|
|
207
|
-
});
|
|
208
|
-
const tryClaimLock = async (path) => {
|
|
209
|
-
try {
|
|
210
|
-
const handle = await open(path, "wx", REGISTRY_FILE_MODE);
|
|
211
|
-
try {
|
|
212
|
-
await handle.writeFile(String(process.pid));
|
|
213
|
-
} finally {
|
|
214
|
-
await handle.close().catch(() => {
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
return true;
|
|
218
|
-
} catch (error) {
|
|
219
|
-
if (error.code === "EEXIST") {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
throw error;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
const isLockStale = async (path) => {
|
|
226
|
-
let pid;
|
|
227
|
-
try {
|
|
228
|
-
const raw = await readFile(path, "utf8");
|
|
229
|
-
const parsed = Number.parseInt(raw.trim(), 10);
|
|
230
|
-
pid = Number.isFinite(parsed) ? parsed : void 0;
|
|
231
|
-
} catch {
|
|
232
|
-
return true;
|
|
233
|
-
}
|
|
234
|
-
if (pid !== void 0 && !isAlive(pid)) {
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
try {
|
|
238
|
-
const stats = await stat(path);
|
|
239
|
-
return Date.now() - stats.mtimeMs > LOCK_STALE_MS;
|
|
240
|
-
} catch {
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
const withServiceLock = async (workspaceRoot, id, fn) => {
|
|
245
|
-
const directory = await getRegistryDir(workspaceRoot);
|
|
246
|
-
const path = lockPath(directory, id);
|
|
247
|
-
const start = Date.now();
|
|
248
|
-
while (true) {
|
|
249
|
-
if (await tryClaimLock(path)) {
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
if (await isLockStale(path)) {
|
|
253
|
-
await unlink(path).catch(() => {
|
|
254
|
-
});
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
if (Date.now() - start >= LOCK_TIMEOUT_MS) {
|
|
258
|
-
throw new Error(
|
|
259
|
-
`Could not acquire service lock for ${id} within ${String(LOCK_TIMEOUT_MS)}ms — another \`vis service\` invocation appears to be holding it.`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
await sleep(LOCK_POLL_MS);
|
|
263
|
-
}
|
|
264
|
-
try {
|
|
265
|
-
return await fn();
|
|
266
|
-
} finally {
|
|
267
|
-
await unlink(path).catch(() => {
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
export { REGISTRY_FILE_MODE as R, ServiceReadinessError as S, writeEntry as a, runReadiness as b, readAllEntries as c, deleteEntry as d, getRegistryDir as g, isAlive as i, pruneDead as p, readEntry as r, slugify as s, withServiceLock as w };
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
-
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
-
|
|
7
|
-
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
-
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
-
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
-
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
-
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
-
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
-
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
// Fallback to createRequire
|
|
17
|
-
return __cjs_require(module);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const {
|
|
21
|
-
readdir,
|
|
22
|
-
stat,
|
|
23
|
-
readFile
|
|
24
|
-
} = __cjs_getBuiltinModule("node:fs/promises");
|
|
25
|
-
import { join } from '@visulima/path';
|
|
26
|
-
|
|
27
|
-
const findTaskInSummary = (summary, taskId) => summary.tasks.find((task) => task.taskId === taskId);
|
|
28
|
-
const diffHashBuckets = (current, previous) => {
|
|
29
|
-
const currentMap = current ?? {};
|
|
30
|
-
const previousMap = previous ?? {};
|
|
31
|
-
const added = [];
|
|
32
|
-
const removed = [];
|
|
33
|
-
const changed = [];
|
|
34
|
-
for (const key of Object.keys(currentMap)) {
|
|
35
|
-
if (!(key in previousMap)) {
|
|
36
|
-
added.push(key);
|
|
37
|
-
} else if (currentMap[key] !== previousMap[key]) {
|
|
38
|
-
changed.push(key);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
for (const key of Object.keys(previousMap)) {
|
|
42
|
-
if (!(key in currentMap)) {
|
|
43
|
-
removed.push(key);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
added.sort();
|
|
47
|
-
removed.sort();
|
|
48
|
-
changed.sort();
|
|
49
|
-
return { added, changed, removed };
|
|
50
|
-
};
|
|
51
|
-
const diffHashDetails = (current, previous) => {
|
|
52
|
-
return {
|
|
53
|
-
commandChanged: (current?.command ?? "") !== (previous?.command ?? ""),
|
|
54
|
-
implicitDeps: diffHashBuckets(current?.implicitDeps, previous?.implicitDeps),
|
|
55
|
-
nodes: diffHashBuckets(current?.nodes, previous?.nodes),
|
|
56
|
-
runtime: diffHashBuckets(current?.runtime, previous?.runtime)
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
const readRunSummaryById = async (workspaceRoot, runId) => {
|
|
60
|
-
const path = join(workspaceRoot, ".task-runner", "runs", `${runId}.json`);
|
|
61
|
-
try {
|
|
62
|
-
const content = await readFile(path, "utf8");
|
|
63
|
-
return JSON.parse(content);
|
|
64
|
-
} catch {
|
|
65
|
-
return void 0;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
const readPreviousRunSummary = async (workspaceRoot, currentId) => {
|
|
69
|
-
const runsDirectory = join(workspaceRoot, ".task-runner", "runs");
|
|
70
|
-
let dirents;
|
|
71
|
-
try {
|
|
72
|
-
dirents = await readdir(runsDirectory);
|
|
73
|
-
} catch {
|
|
74
|
-
return void 0;
|
|
75
|
-
}
|
|
76
|
-
const candidates = [];
|
|
77
|
-
for (const name of dirents) {
|
|
78
|
-
if (!name.endsWith(".json")) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (currentId !== void 0 && name === `${currentId}.json`) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
const fullPath = join(runsDirectory, name);
|
|
85
|
-
try {
|
|
86
|
-
const s = await stat(fullPath);
|
|
87
|
-
if (s.isFile()) {
|
|
88
|
-
candidates.push({ mtimeMs: s.mtimeMs, path: fullPath });
|
|
89
|
-
}
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (candidates.length === 0) {
|
|
94
|
-
return void 0;
|
|
95
|
-
}
|
|
96
|
-
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
97
|
-
try {
|
|
98
|
-
const content = await readFile(candidates[0].path, "utf8");
|
|
99
|
-
return JSON.parse(content);
|
|
100
|
-
} catch {
|
|
101
|
-
return void 0;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
const listRunSummaries = async (workspaceRoot) => {
|
|
105
|
-
const runsDirectory = join(workspaceRoot, ".task-runner", "runs");
|
|
106
|
-
let dirents;
|
|
107
|
-
try {
|
|
108
|
-
dirents = await readdir(runsDirectory);
|
|
109
|
-
} catch {
|
|
110
|
-
return [];
|
|
111
|
-
}
|
|
112
|
-
const candidates = [];
|
|
113
|
-
for (const name of dirents) {
|
|
114
|
-
if (!name.endsWith(".json")) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const fullPath = join(runsDirectory, name);
|
|
118
|
-
try {
|
|
119
|
-
const s = await stat(fullPath);
|
|
120
|
-
if (s.isFile()) {
|
|
121
|
-
candidates.push({ id: name.slice(0, -".json".length), mtimeMs: s.mtimeMs, path: fullPath });
|
|
122
|
-
}
|
|
123
|
-
} catch {
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
127
|
-
return candidates;
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
export { readPreviousRunSummary as a, diffHashDetails as d, findTaskInSummary as f, listRunSummaries as l, readRunSummaryById as r };
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { readJsonSync, isAccessibleSync, readFileSync } from '@visulima/fs';
|
|
2
|
-
import { join } from '@visulima/path';
|
|
3
|
-
|
|
4
|
-
const readNodeVersionFile = (workspaceRoot) => {
|
|
5
|
-
for (const name of [".nvmrc", ".node-version"]) {
|
|
6
|
-
const path = join(workspaceRoot, name);
|
|
7
|
-
if (!isAccessibleSync(path)) {
|
|
8
|
-
continue;
|
|
9
|
-
}
|
|
10
|
-
try {
|
|
11
|
-
const content = readFileSync(path).trim();
|
|
12
|
-
return content.replace(/^v/, "");
|
|
13
|
-
} catch {
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return void 0;
|
|
17
|
-
};
|
|
18
|
-
const compareVersions = (a, b) => {
|
|
19
|
-
const aParts = a.split(/[.\-+]/).map((p) => Number.parseInt(p, 10) || 0);
|
|
20
|
-
const bParts = b.split(/[.\-+]/).map((p) => Number.parseInt(p, 10) || 0);
|
|
21
|
-
const len = Math.max(aParts.length, bParts.length);
|
|
22
|
-
for (let i = 0; i < len; i++) {
|
|
23
|
-
const ai = aParts[i] ?? 0;
|
|
24
|
-
const bi = bParts[i] ?? 0;
|
|
25
|
-
if (ai !== bi) {
|
|
26
|
-
return ai - bi;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return 0;
|
|
30
|
-
};
|
|
31
|
-
const satisfiesRange = (actual, range) => {
|
|
32
|
-
const normalized = range.trim();
|
|
33
|
-
if (normalized === "" || normalized === "*") {
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
const clauses = normalized.split(/\s+/).filter(Boolean);
|
|
37
|
-
for (const clause of clauses) {
|
|
38
|
-
if (clause.startsWith(">=")) {
|
|
39
|
-
if (compareVersions(actual, clause.slice(2).trim()) < 0) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
} else if (clause.startsWith("<=")) {
|
|
43
|
-
if (compareVersions(actual, clause.slice(2).trim()) > 0) {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
} else if (clause.startsWith(">")) {
|
|
47
|
-
if (compareVersions(actual, clause.slice(1).trim()) <= 0) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
} else if (clause.startsWith("<")) {
|
|
51
|
-
if (compareVersions(actual, clause.slice(1).trim()) >= 0) {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
} else if (/^\d/.test(clause)) {
|
|
55
|
-
const actualParts = actual.split(".");
|
|
56
|
-
const clauseParts = clause.split(".");
|
|
57
|
-
for (const [i, clausePart] of clauseParts.entries()) {
|
|
58
|
-
if (clausePart !== actualParts[i]) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return true;
|
|
65
|
-
};
|
|
66
|
-
const checkRuntimeVersions = (workspaceRoot) => {
|
|
67
|
-
const findings = [];
|
|
68
|
-
const pkgPath = join(workspaceRoot, "package.json");
|
|
69
|
-
let rootPkg = {};
|
|
70
|
-
try {
|
|
71
|
-
rootPkg = readJsonSync(pkgPath);
|
|
72
|
-
} catch {
|
|
73
|
-
return findings;
|
|
74
|
-
}
|
|
75
|
-
const actualNode = process.versions.node;
|
|
76
|
-
if (rootPkg.engines?.node) {
|
|
77
|
-
const expected = rootPkg.engines.node;
|
|
78
|
-
if (!satisfiesRange(actualNode, expected)) {
|
|
79
|
-
findings.push({
|
|
80
|
-
actual: actualNode,
|
|
81
|
-
expected,
|
|
82
|
-
kind: "node",
|
|
83
|
-
message: `package.json engines.node requires ${expected}, but the current Node.js is ${actualNode}.`,
|
|
84
|
-
severity: "error"
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const pinnedNode = readNodeVersionFile(workspaceRoot);
|
|
89
|
-
if (pinnedNode) {
|
|
90
|
-
const [pinnedMajor, pinnedMinor] = pinnedNode.split(".");
|
|
91
|
-
const [actualMajor, actualMinor] = actualNode.split(".");
|
|
92
|
-
if (pinnedMajor !== actualMajor || pinnedMinor !== void 0 && pinnedMinor !== actualMinor) {
|
|
93
|
-
findings.push({
|
|
94
|
-
actual: actualNode,
|
|
95
|
-
expected: pinnedNode,
|
|
96
|
-
kind: "node",
|
|
97
|
-
message: `.nvmrc pins Node ${pinnedNode} but the current Node.js is ${actualNode}. Run \`nvm use\` or switch runtimes.`,
|
|
98
|
-
severity: "warning"
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (rootPkg.packageManager) {
|
|
103
|
-
const [expectedName, expectedVersion] = rootPkg.packageManager.split("@");
|
|
104
|
-
const detectedFromUserAgent = (process.env["npm_config_user_agent"] ?? "").split(" ")[0] ?? "";
|
|
105
|
-
const [detectedName, detectedVersion] = detectedFromUserAgent.split("/");
|
|
106
|
-
if (detectedName && expectedName && detectedName !== expectedName) {
|
|
107
|
-
findings.push({
|
|
108
|
-
actual: detectedName,
|
|
109
|
-
expected: expectedName,
|
|
110
|
-
kind: "packageManager",
|
|
111
|
-
message: `package.json packageManager pins ${rootPkg.packageManager} but the current invocation is ${detectedFromUserAgent}. Install the correct package manager.`,
|
|
112
|
-
severity: "error"
|
|
113
|
-
});
|
|
114
|
-
} else if (detectedVersion && expectedVersion && detectedVersion !== expectedVersion) {
|
|
115
|
-
findings.push({
|
|
116
|
-
actual: detectedVersion,
|
|
117
|
-
expected: expectedVersion,
|
|
118
|
-
kind: "packageManager",
|
|
119
|
-
message: `package.json packageManager pins ${expectedName}@${expectedVersion} but the current invocation uses ${expectedName}@${detectedVersion}.`,
|
|
120
|
-
severity: "warning"
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return findings;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
export { checkRuntimeVersions as c };
|