@nshiab/setup-data-project 4.0.1 → 4.0.2

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/dist/main.mjs CHANGED
@@ -227,7 +227,7 @@ async function installPackages(packages, options = {}) {
227
227
  //#endregion
228
228
  //#region package.json
229
229
  var name = "@nshiab/setup-data-project";
230
- var version = "4.0.1";
230
+ var version = "4.0.2";
231
231
  //#endregion
232
232
  //#region src/helpers/ensureTooling.ts
233
233
  const tools = ["vite-plus", "@types/node"];
package/dist/sda.mjs CHANGED
@@ -75,7 +75,8 @@ async function acquireSdaLock(command) {
75
75
  }
76
76
  writeFileSync(join(ownDirectory, "owner.tmp"), JSON.stringify({
77
77
  ticket,
78
- command
78
+ command,
79
+ projectRoot: realpathSync(process.cwd())
79
80
  }));
80
81
  renameSync(join(ownDirectory, "owner.tmp"), join(ownDirectory, "owner.json"));
81
82
  const deadline = performance.now() + 5e3;
@@ -107,10 +108,15 @@ function printSdaStatus() {
107
108
  const retry = `${run} sda:status`;
108
109
  try {
109
110
  const pids = /* @__PURE__ */ new Set();
111
+ const claims = /* @__PURE__ */ new Map();
110
112
  try {
111
113
  for (const name of readdirSync(SDA_LOCK_DIRECTORY)) {
112
114
  const match = /^(\d+)-[\da-f-]{36}$/.exec(name);
113
- if (match) pids.add(Number(match[1]));
115
+ if (match) {
116
+ const pid = Number(match[1]);
117
+ pids.add(pid);
118
+ claims.set(pid, [...claims.get(pid) ?? [], name]);
119
+ }
114
120
  }
115
121
  } catch (error) {
116
122
  if (errorCode$1(error) !== "ENOENT") throw error;
@@ -131,40 +137,67 @@ function printSdaStatus() {
131
137
  if (errorCode$1(error) === "ESRCH") continue;
132
138
  throw error;
133
139
  }
134
- if (process.platform !== "darwin" && process.platform !== "linux") throw new Error("Active SDA processes cannot be verified on this platform.");
135
- const command = execFileSync("ps", [
136
- "-p",
137
- String(pid),
138
- "-o",
139
- "command="
140
- ], {
141
- encoding: "utf8",
142
- timeout: 5e3,
143
- stdio: [
144
- "ignore",
145
- "pipe",
146
- "pipe"
147
- ]
148
- }).trim();
149
- const cwd = process.platform === "linux" ? realpathSync(`/proc/${pid}/cwd`) : execFileSync("lsof", [
150
- "-a",
151
- "-p",
152
- String(pid),
153
- "-d",
154
- "cwd",
155
- "-Fn"
156
- ], {
157
- encoding: "utf8",
158
- timeout: 5e3,
159
- stdio: [
160
- "ignore",
161
- "pipe",
162
- "pipe"
163
- ]
164
- }).split("\n").find((line) => line.startsWith("n"))?.slice(1);
140
+ let command;
141
+ if (process.platform === "win32") {
142
+ const owners = claims.get(pid) ?? [];
143
+ if (owners.length !== 1) throw new Error(`PID ${pid} has no unambiguous SDA owner record.`);
144
+ const owner = JSON.parse(readFileSync(join(SDA_LOCK_DIRECTORY, owners[0], "owner.json"), "utf8"));
145
+ if (!isObject(owner) || typeof owner.projectRoot !== "string" || realpathSync(owner.projectRoot) !== realpathSync(process.cwd())) throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);
146
+ const result = JSON.parse(execFileSync("powershell.exe", [
147
+ "-NoLogo",
148
+ "-NoProfile",
149
+ "-NonInteractive",
150
+ "-Command",
151
+ `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(\$false); (Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}' -ErrorAction Stop).CommandLine | ConvertTo-Json -Compress`
152
+ ], {
153
+ encoding: "utf8",
154
+ timeout: 5e3,
155
+ maxBuffer: 65536,
156
+ windowsHide: true,
157
+ stdio: [
158
+ "ignore",
159
+ "pipe",
160
+ "pipe"
161
+ ]
162
+ }));
163
+ if (typeof result !== "string" || !result.trim()) throw new Error(`PID ${pid} has no readable command line.`);
164
+ command = result;
165
+ } else if (process.platform === "darwin" || process.platform === "linux") {
166
+ command = execFileSync("ps", [
167
+ "-p",
168
+ String(pid),
169
+ "-o",
170
+ "command="
171
+ ], {
172
+ encoding: "utf8",
173
+ timeout: 5e3,
174
+ stdio: [
175
+ "ignore",
176
+ "pipe",
177
+ "pipe"
178
+ ]
179
+ }).trim();
180
+ const cwd = process.platform === "linux" ? realpathSync(`/proc/${pid}/cwd`) : execFileSync("lsof", [
181
+ "-a",
182
+ "-p",
183
+ String(pid),
184
+ "-d",
185
+ "cwd",
186
+ "-Fn"
187
+ ], {
188
+ encoding: "utf8",
189
+ timeout: 5e3,
190
+ stdio: [
191
+ "ignore",
192
+ "pipe",
193
+ "pipe"
194
+ ]
195
+ }).split("\n").find((line) => line.startsWith("n"))?.slice(1);
196
+ if (!cwd || realpathSync(cwd) !== realpathSync(process.cwd())) throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);
197
+ } else throw new Error("Active SDA processes cannot be verified on this platform.");
165
198
  const executable = process.argv[1];
166
- if (!cwd || realpathSync(cwd) !== realpathSync(process.cwd()) || !executable || !command.includes(executable) && !command.includes(realpathSync(executable)) && !/(?:\/@nshiab\/setup-data-project\/dist\/sda\.mjs|\/node_modules\/\.bin\/sda-run)(?:\s|$)/.test(command)) throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);
167
- messages.push(/(?:^|\s)--watch(?:\s|$)/.test(command) ? `SDA watcher is running (${run} sda:watch, PID ${pid}). It automatically fixes files and reruns the analysis. If you are an agent, ask the user to stop it with Ctrl+C, then recheck with ${retry} before doing any work.` : `SDA analysis is running (${run} sda, PID ${pid}). Wait for it to finish. Recheck with ${retry} in 30 seconds before doing any work.`);
199
+ if (!executable || !command.includes(executable) && !command.includes(realpathSync(executable)) && !/(?:[\\/]@nshiab[\\/]setup-data-project[\\/]dist[\\/]sda\.mjs|[\\/]node_modules[\\/]\.bin[\\/]sda-run)(?:["\s]|$)/.test(command)) throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);
200
+ messages.push(/(?:^|["\s])--watch(?:["\s]|$)/.test(command) ? `SDA watcher is running (${run} sda:watch, PID ${pid}). It automatically fixes files and reruns the analysis. If you are an agent, ask the user to stop it with Ctrl+C, then recheck with ${retry} before doing any work.` : `SDA analysis is running (${run} sda, PID ${pid}). Wait for it to finish. Recheck with ${retry} in 30 seconds before doing any work.`);
168
201
  }
169
202
  for (const message of messages) console.log(message);
170
203
  if (messages.length) return 1;
@@ -223,6 +256,7 @@ let contents = /* @__PURE__ */ new Map();
223
256
  const watchers = /* @__PURE__ */ new Map();
224
257
  function launch(args, command = process.execPath) {
225
258
  const child = spawn(command, args, {
259
+ argv0: windows && bun && command === process.execPath ? "bun" : void 0,
226
260
  stdio: "inherit",
227
261
  detached: !windows,
228
262
  shell: windows && command === "npm.cmd"
package/dist/sda.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sda.mjs","names":["errorCode","delay","errorCode","delay"],"sources":["../src/helpers/sdaLock.ts","../src/helpers/sdaStatus.ts","../src/sda.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { isObject } from \"./jsonObject.ts\";\n\nexport const SDA_PID_FILE = \"sda/.sda.pid\";\nexport const SDA_LOCK_DIRECTORY = \"sda/.sda-lock\";\n\ntype Claim = { id: string; pid: number; ticket: number; command?: string };\n\nfunction errorCode(error: unknown) {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\n// Bakery ordering: publish a unique claim before choosing a ticket, then wait for\n// concurrent ticket choices. Never replace/delete a shared lock during recovery.\n// https://www.microsoft.com/en-us/research/publication/new-solution-dijkstras-concurrent-programming-problem/\nexport async function acquireSdaLock(command: string): Promise<() => void> {\n const directory = resolve(SDA_LOCK_DIRECTORY);\n const pidFile = resolve(SDA_PID_FILE);\n const id = `${process.pid}-${randomUUID()}`;\n const ownDirectory = join(directory, id);\n mkdirSync(directory, { recursive: true });\n mkdirSync(ownDirectory);\n let ownsPid = false;\n const release = () => {\n if (ownsPid) {\n // Remove the informational PID before releasing ownership to the next run.\n rmSync(pidFile, { force: true });\n ownsPid = false;\n }\n rmSync(ownDirectory, { recursive: true, force: true });\n process.removeListener(\"exit\", release);\n };\n process.once(\"exit\", release);\n\n function readClaim(candidate: string): Claim | undefined {\n const match = /^(\\d+)-[\\da-f-]{36}$/.exec(candidate);\n if (!match) return;\n const pid = Number(match[1]);\n if (!Number.isInteger(pid) || pid <= 0 || pid > 2147483647) return;\n const path = join(directory, candidate);\n try {\n process.kill(pid, 0);\n } catch (error) {\n if (errorCode(error) !== \"ESRCH\") throw error;\n // Claim names are never reused, so another starter cannot acquire this path.\n rmSync(path, { recursive: true, force: true });\n return;\n }\n let record: unknown;\n try {\n record = JSON.parse(readFileSync(join(path, \"owner.json\"), \"utf8\"));\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n // A directory without a published ticket is still choosing, or has exited.\n return existsSync(path) ? { id: candidate, pid, ticket: 0 } : undefined;\n }\n if (\n !isObject(record) ||\n typeof record.ticket !== \"number\" ||\n !Number.isSafeInteger(record.ticket) ||\n record.ticket < 1 ||\n typeof record.command !== \"string\"\n )\n throw new Error(`Cannot read the SDA lock owned by process ${pid}.`);\n return { id: candidate, pid, ticket: record.ticket, command: record.command };\n }\n\n try {\n let ticket = 1;\n for (const candidate of readdirSync(directory)) {\n const claim = readClaim(candidate);\n if (claim) ticket = Math.max(ticket, claim.ticket + 1);\n }\n writeFileSync(join(ownDirectory, \"owner.tmp\"), JSON.stringify({ ticket, command }));\n renameSync(join(ownDirectory, \"owner.tmp\"), join(ownDirectory, \"owner.json\"));\n const deadline = performance.now() + 5000;\n for (const candidate of readdirSync(directory)) {\n if (candidate === id) continue;\n let claim = readClaim(candidate);\n while (claim?.ticket === 0) {\n if (performance.now() >= deadline)\n throw new Error(\n `Another SDA command is still starting (PID ${claim.pid}). Wait for it to finish starting, then try again.`,\n );\n await delay(10);\n claim = readClaim(candidate);\n }\n if (claim && (claim.ticket < ticket || (claim.ticket === ticket && claim.id < id)))\n throw new Error(\n `${claim.command} is already running${claim.command?.endsWith(\"sda:watch\") ? \" and watching this project's files\" : \" an analysis for this project\"} (PID ${claim.pid}). Wait for it to finish or stop it with Ctrl+C in its terminal, then try again.`,\n );\n }\n // Only the winner touches the shared PID file, including stale-file recovery.\n writeFileSync(pidFile, String(process.pid) + \"\\n\");\n ownsPid = true;\n return release;\n } catch (error) {\n release();\n throw error;\n }\n}\n","import { execFileSync } from \"node:child_process\";\nimport { readFileSync, readdirSync, realpathSync } from \"node:fs\";\nimport { SDA_LOCK_DIRECTORY, SDA_PID_FILE } from \"./sdaLock.ts\";\n\nfunction errorCode(error: unknown) {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\n// Status is read-only: a missing or dead owner is ignored, never removed.\nexport function printSdaStatus(): number {\n const run = `${process.versions.bun ? \"bun\" : \"npm\"} run`;\n const retry = `${run} sda:status`;\n try {\n const pids = new Set<number>();\n try {\n for (const name of readdirSync(SDA_LOCK_DIRECTORY)) {\n const match = /^(\\d+)-[\\da-f-]{36}$/.exec(name);\n if (match) pids.add(Number(match[1]));\n }\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n }\n try {\n const value = readFileSync(SDA_PID_FILE, \"utf8\").trim();\n if (!/^\\d+$/.test(value)) throw new Error(\"The SDA PID file is invalid.\");\n pids.add(Number(value));\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n }\n const messages: string[] = [];\n for (const pid of pids) {\n if (!Number.isInteger(pid) || pid <= 0 || pid > 2147483647)\n throw new Error(\"The recorded SDA PID is invalid.\");\n try {\n process.kill(pid, 0);\n } catch (error) {\n if (errorCode(error) === \"ESRCH\") continue;\n throw error;\n }\n if (process.platform !== \"darwin\" && process.platform !== \"linux\")\n throw new Error(\"Active SDA processes cannot be verified on this platform.\");\n const command = execFileSync(\"ps\", [\"-p\", String(pid), \"-o\", \"command=\"], {\n encoding: \"utf8\",\n timeout: 5000,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n }).trim();\n const cwd =\n process.platform === \"linux\"\n ? realpathSync(`/proc/${pid}/cwd`)\n : execFileSync(\"lsof\", [\"-a\", \"-p\", String(pid), \"-d\", \"cwd\", \"-Fn\"], {\n encoding: \"utf8\",\n timeout: 5000,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n })\n .split(\"\\n\")\n .find((line) => line.startsWith(\"n\"))\n ?.slice(1);\n const executable = process.argv[1];\n if (\n !cwd ||\n realpathSync(cwd) !== realpathSync(process.cwd()) ||\n !executable ||\n (!command.includes(executable) &&\n !command.includes(realpathSync(executable)) &&\n !/(?:\\/@nshiab\\/setup-data-project\\/dist\\/sda\\.mjs|\\/node_modules\\/\\.bin\\/sda-run)(?:\\s|$)/.test(\n command,\n ))\n )\n throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);\n messages.push(\n /(?:^|\\s)--watch(?:\\s|$)/.test(command)\n ? `SDA watcher is running (${run} sda:watch, PID ${pid}). It automatically fixes files and reruns the analysis. If you are an agent, ask the user to stop it with Ctrl+C, then recheck with ${retry} before doing any work.`\n : `SDA analysis is running (${run} sda, PID ${pid}). Wait for it to finish. Recheck with ${retry} in 30 seconds before doing any work.`,\n );\n }\n for (const message of messages) console.log(message);\n if (messages.length) return 1;\n console.log(\"No active SDA process found. You can proceed.\");\n return 0;\n } catch (error) {\n console.error(\n `Unable to verify SDA status: ${error instanceof Error ? error.message : String(error)} If you are an agent, ask the user to confirm that neither ${run} sda nor ${run} sda:watch is running and that you may proceed, then wait for their confirmation before doing any work.`,\n );\n return 2;\n }\n}\n","#!/usr/bin/env node\nimport { existsSync, lstatSync, type FSWatcher, readdirSync, watch } from \"node:fs\";\nimport { spawn, spawnSync, type ChildProcess } from \"node:child_process\";\nimport { dirname, join, resolve, sep } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { parseArgs } from \"node:util\";\nimport { acquireSdaLock, SDA_PID_FILE, SDA_LOCK_DIRECTORY } from \"./helpers/sdaLock.ts\";\nimport { printSdaStatus } from \"./helpers/sdaStatus.ts\";\n\nconst {\n positionals: [entry, ...paths],\n values,\n} = parseArgs({\n allowPositionals: true,\n options: {\n status: { type: \"boolean\", default: false },\n watch: { type: \"boolean\", default: false },\n ignore: { type: \"string\", multiple: true, default: [] },\n },\n});\nif (values.status) process.exit(printSdaStatus());\nif (!entry) {\n console.error(\n \"Usage: sda-run --status | [--watch] <entry-file> [watch-paths...] [--ignore path]\",\n );\n process.exit(1);\n}\nconst watching = values.watch;\nconst selected = [entry, ...paths].map((path) => resolve(path));\nconst ignored = [SDA_PID_FILE, SDA_LOCK_DIRECTORY, ...values.ignore].map((path) => resolve(path));\nconst within = (path: string, root: string) => path === root || path.startsWith(root + sep);\nconst excluded = (path: string) => ignored.some((root) => within(path, root));\nconst bun = Boolean(process.versions.bun);\nconst runCommand = (bun ? \"bun run \" : \"npm run \") + (watching ? \"sda:watch\" : \"sda\");\nconst windows = process.platform === \"win32\";\ntype Job = { child: ChildProcess; done: Promise<number | null> };\nlet releaseLock: (() => void) | undefined;\nlet fixer: Job | undefined;\nlet analysis: Job | undefined;\nlet timer: ReturnType<typeof setTimeout> | undefined;\nlet checking = false;\nlet closing = false;\nlet needsCheck = true;\nlet contents = new Map<string, string | null>();\nconst watchers = new Map<string, { watcher: FSWatcher; recursive: boolean }>();\n\nfunction launch(args: string[], command = process.execPath) {\n const child = spawn(command, args, {\n stdio: \"inherit\",\n detached: !windows,\n shell: windows && command === \"npm.cmd\",\n });\n const done = new Promise<number | null>((resolve) => {\n child.once(\"error\", (error) => {\n console.error(errorMessage(error));\n resolve(1);\n });\n child.once(\"close\", resolve);\n });\n return { child, done };\n}\n\nfunction snapshot() {\n const files = new Map<string, string | null>();\n function collect(path: string) {\n if (excluded(path) || files.has(path)) return;\n try {\n const stat = lstatSync(path, { bigint: true });\n if (stat.isDirectory()) {\n files.set(path, \"directory\");\n for (const name of readdirSync(path)) collect(join(path, name));\n } else {\n // Compare metadata without reading potentially large datasets or following symlinks.\n files.set(path, [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(\":\"));\n }\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\" && errorCode(error) !== \"ENOTDIR\") throw error;\n files.set(path, null);\n }\n }\n for (const path of selected) collect(path);\n return files;\n}\n\nfunction equal(left: Map<string, string | null>, right: Map<string, string | null>) {\n return left.size === right.size && [...left].every(([path, value]) => right.get(path) === value);\n}\n\nfunction refreshWatchers() {\n const directories = new Map<string, boolean>();\n const add = (directory: string, recursive: boolean) => {\n directories.set(directory, recursive || directories.get(directory) || false);\n };\n for (const path of selected) {\n if (excluded(path)) continue;\n if (lstatSync(path, { throwIfNoEntry: false })?.isDirectory()) add(path, true);\n // Watch the parent too, so deleted files and folders can be restored.\n let parent = dirname(path);\n while (!existsSync(parent)) parent = dirname(parent);\n add(parent, parent !== dirname(path));\n }\n for (const [directory, state] of watchers) {\n if (directories.get(directory) !== state.recursive) {\n state.watcher.close();\n watchers.delete(directory);\n }\n }\n for (const [directory, recursive] of directories) {\n if (watchers.has(directory)) continue;\n const watcher = watch(directory, { recursive }, (_event, name) => {\n if (checking || closing) return;\n const changed = name && resolve(directory, name.toString());\n if (\n !changed ||\n (!excluded(changed) &&\n selected.some((path) => within(changed, path) || within(path, changed)))\n )\n schedule();\n });\n watcher.on(\"error\", fail);\n watchers.set(directory, { watcher, recursive });\n }\n}\n\nasync function stop(job: Job | undefined) {\n if (!job?.child.pid) return;\n const pid = job.child.pid;\n if (windows) {\n if (job.child.exitCode === null && job.child.signalCode === null) {\n spawnSync(\"taskkill\", [\"/pid\", String(job.child.pid), \"/T\", \"/F\"], { stdio: \"ignore\" });\n }\n await job.done;\n return;\n }\n const signal = (name: NodeJS.Signals | 0) => {\n try {\n process.kill(-pid, name);\n return true;\n } catch (error) {\n if (errorCode(error) !== \"ESRCH\") throw error;\n return false;\n }\n };\n signal(\"SIGTERM\");\n // The direct child can exit first; retain the grace period for its entire process group.\n const deadline = performance.now() + 1000;\n while (signal(0)) {\n if (performance.now() >= deadline) {\n signal(\"SIGKILL\");\n break;\n }\n await delay(25);\n }\n await job.done;\n}\n\nasync function check() {\n if (closing || checking) return;\n if (!needsCheck && equal(contents, snapshot())) return;\n checking = true;\n needsCheck = false;\n await stop(analysis);\n analysis = undefined;\n if (closing) return;\n refreshWatchers();\n contents = snapshot();\n process.stdout.write(\"\\u001b[2J\\u001b[3J\\u001b[H\");\n console.log(\"Fixing and checking...\");\n fixer = launch([\"run\", \"fix\"], bun ? process.execPath : windows ? \"npm.cmd\" : \"npm\");\n const fixed = await fixer.done;\n if (closing) return;\n const current = snapshot();\n // Recheck edits made during fixes; ignore delayed events for unchanged file states.\n needsCheck = !equal(contents, current);\n contents = current;\n refreshWatchers();\n fixer = undefined;\n checking = false;\n if (needsCheck) {\n schedule();\n } else if (fixed === 0) {\n console.log(\"Running analysis...\");\n analysis = launch(bun ? [\"run\", entry] : [\"--env-file=.env\", entry]);\n } else {\n console.log(\"Fix failed. Watching for changes...\");\n }\n}\n\nfunction schedule() {\n clearTimeout(timer);\n timer = setTimeout(() => check().catch(fail), 0);\n}\n\nasync function shutdown(code: number) {\n if (closing) return;\n closing = true;\n clearTimeout(timer);\n for (const { watcher } of watchers.values()) watcher.close();\n await Promise.all([stop(fixer), stop(analysis)]);\n releaseLock?.();\n process.exit(code);\n}\n\nfunction errorCode(error: unknown) {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\nfunction errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction fail(error: unknown) {\n console.error(errorMessage(error));\n void shutdown(1);\n}\n\nprocess.on(\"SIGINT\", () => void shutdown(0));\nprocess.on(\"SIGTERM\", () => void shutdown(0));\n\nasync function runOnce() {\n console.log(\"Fixing and checking...\");\n fixer = launch([\"run\", \"fix\"], bun ? process.execPath : windows ? \"npm.cmd\" : \"npm\");\n const fixed = await fixer.done;\n if (closing) return;\n if (fixed !== 0) {\n await shutdown(fixed ?? 1);\n return;\n }\n await stop(fixer);\n fixer = undefined;\n if (closing) return;\n console.log(\"Running analysis...\");\n analysis = launch(bun ? [\"run\", entry] : [\"--env-file=.env\", entry]);\n const result = await analysis.done;\n await shutdown(result ?? 1);\n}\n\ntry {\n releaseLock = await acquireSdaLock(runCommand);\n if (closing) releaseLock();\n else if (watching) {\n refreshWatchers();\n void check().catch(fail);\n } else {\n void runOnce().catch(fail);\n }\n} catch (error) {\n fail(new Error(\"Cannot start \" + runCommand + \": \" + errorMessage(error)));\n}\n"],"mappings":";;;;;;;;;AAcA,MAAa,eAAe;AAC5B,MAAa,qBAAqB;AAIlC,SAASA,YAAU,OAAgB;CACjC,OAAO,iBAAiB,SAAS,UAAU,QAAQ,MAAM,OAAO,KAAA;AAClE;AAKA,eAAsB,eAAe,SAAsC;CACzE,MAAM,YAAY,QAAQ,kBAAkB;CAC5C,MAAM,UAAU,QAAQ,YAAY;CACpC,MAAM,KAAK,GAAG,QAAQ,IAAI,GAAG,WAAW;CACxC,MAAM,eAAe,KAAK,WAAW,EAAE;CACvC,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CACxC,UAAU,YAAY;CACtB,IAAI,UAAU;CACd,MAAM,gBAAgB;EACpB,IAAI,SAAS;GAEX,OAAO,SAAS,EAAE,OAAO,KAAK,CAAC;GAC/B,UAAU;EACZ;EACA,OAAO,cAAc;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EACrD,QAAQ,eAAe,QAAQ,OAAO;CACxC;CACA,QAAQ,KAAK,QAAQ,OAAO;CAE5B,SAAS,UAAU,WAAsC;EACvD,MAAM,QAAQ,uBAAuB,KAAK,SAAS;EACnD,IAAI,CAAC,OAAO;EACZ,MAAM,MAAM,OAAO,MAAM,EAAE;EAC3B,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,OAAO,KAAK,MAAM,YAAY;EAC5D,MAAM,OAAO,KAAK,WAAW,SAAS;EACtC,IAAI;GACF,QAAQ,KAAK,KAAK,CAAC;EACrB,SAAS,OAAO;GACd,IAAIA,YAAU,KAAK,MAAM,SAAS,MAAM;GAExC,OAAO,MAAM;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;GAC7C;EACF;EACA,IAAI;EACJ,IAAI;GACF,SAAS,KAAK,MAAM,aAAa,KAAK,MAAM,YAAY,GAAG,MAAM,CAAC;EACpE,SAAS,OAAO;GACd,IAAIA,YAAU,KAAK,MAAM,UAAU,MAAM;GAEzC,OAAO,WAAW,IAAI,IAAI;IAAE,IAAI;IAAW;IAAK,QAAQ;GAAE,IAAI,KAAA;EAChE;EACA,IACE,CAAC,SAAS,MAAM,KAChB,OAAO,OAAO,WAAW,YACzB,CAAC,OAAO,cAAc,OAAO,MAAM,KACnC,OAAO,SAAS,KAChB,OAAO,OAAO,YAAY,UAE1B,MAAM,IAAI,MAAM,6CAA6C,IAAI,EAAE;EACrE,OAAO;GAAE,IAAI;GAAW;GAAK,QAAQ,OAAO;GAAQ,SAAS,OAAO;EAAQ;CAC9E;CAEA,IAAI;EACF,IAAI,SAAS;EACb,KAAK,MAAM,aAAa,YAAY,SAAS,GAAG;GAC9C,MAAM,QAAQ,UAAU,SAAS;GACjC,IAAI,OAAO,SAAS,KAAK,IAAI,QAAQ,MAAM,SAAS,CAAC;EACvD;EACA,cAAc,KAAK,cAAc,WAAW,GAAG,KAAK,UAAU;GAAE;GAAQ;EAAQ,CAAC,CAAC;EAClF,WAAW,KAAK,cAAc,WAAW,GAAG,KAAK,cAAc,YAAY,CAAC;EAC5E,MAAM,WAAW,YAAY,IAAI,IAAI;EACrC,KAAK,MAAM,aAAa,YAAY,SAAS,GAAG;GAC9C,IAAI,cAAc,IAAI;GACtB,IAAI,QAAQ,UAAU,SAAS;GAC/B,OAAO,OAAO,WAAW,GAAG;IAC1B,IAAI,YAAY,IAAI,KAAK,UACvB,MAAM,IAAI,MACR,8CAA8C,MAAM,IAAI,mDAC1D;IACF,MAAMC,aAAM,EAAE;IACd,QAAQ,UAAU,SAAS;GAC7B;GACA,IAAI,UAAU,MAAM,SAAS,UAAW,MAAM,WAAW,UAAU,MAAM,KAAK,KAC5E,MAAM,IAAI,MACR,GAAG,MAAM,QAAQ,qBAAqB,MAAM,SAAS,SAAS,WAAW,IAAI,uCAAuC,gCAAgC,QAAQ,MAAM,IAAI,iFACxK;EACJ;EAEA,cAAc,SAAS,OAAO,QAAQ,GAAG,IAAI,IAAI;EACjD,UAAU;EACV,OAAO;CACT,SAAS,OAAO;EACd,QAAQ;EACR,MAAM;CACR;AACF;;;AC3GA,SAASC,YAAU,OAAgB;CACjC,OAAO,iBAAiB,SAAS,UAAU,QAAQ,MAAM,OAAO,KAAA;AAClE;AAGA,SAAgB,iBAAyB;CACvC,MAAM,MAAM,GAAG,QAAQ,SAAS,MAAM,QAAQ,MAAM;CACpD,MAAM,QAAQ,GAAG,IAAI;CACrB,IAAI;EACF,MAAM,uBAAO,IAAI,IAAY;EAC7B,IAAI;GACF,KAAK,MAAM,QAAQ,YAAY,kBAAkB,GAAG;IAClD,MAAM,QAAQ,uBAAuB,KAAK,IAAI;IAC9C,IAAI,OAAO,KAAK,IAAI,OAAO,MAAM,EAAE,CAAC;GACtC;EACF,SAAS,OAAO;GACd,IAAIA,YAAU,KAAK,MAAM,UAAU,MAAM;EAC3C;EACA,IAAI;GACF,MAAM,QAAQ,aAAa,cAAc,MAAM,CAAC,CAAC,KAAK;GACtD,IAAI,CAAC,QAAQ,KAAK,KAAK,GAAG,MAAM,IAAI,MAAM,8BAA8B;GACxE,KAAK,IAAI,OAAO,KAAK,CAAC;EACxB,SAAS,OAAO;GACd,IAAIA,YAAU,KAAK,MAAM,UAAU,MAAM;EAC3C;EACA,MAAM,WAAqB,CAAC;EAC5B,KAAK,MAAM,OAAO,MAAM;GACtB,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,OAAO,KAAK,MAAM,YAC9C,MAAM,IAAI,MAAM,kCAAkC;GACpD,IAAI;IACF,QAAQ,KAAK,KAAK,CAAC;GACrB,SAAS,OAAO;IACd,IAAIA,YAAU,KAAK,MAAM,SAAS;IAClC,MAAM;GACR;GACA,IAAI,QAAQ,aAAa,YAAY,QAAQ,aAAa,SACxD,MAAM,IAAI,MAAM,2DAA2D;GAC7E,MAAM,UAAU,aAAa,MAAM;IAAC;IAAM,OAAO,GAAG;IAAG;IAAM;GAAU,GAAG;IACxE,UAAU;IACV,SAAS;IACT,OAAO;KAAC;KAAU;KAAQ;IAAM;GAClC,CAAC,CAAC,CAAC,KAAK;GACR,MAAM,MACJ,QAAQ,aAAa,UACjB,aAAa,SAAS,IAAI,KAAK,IAC/B,aAAa,QAAQ;IAAC;IAAM;IAAM,OAAO,GAAG;IAAG;IAAM;IAAO;GAAK,GAAG;IAClE,UAAU;IACV,SAAS;IACT,OAAO;KAAC;KAAU;KAAQ;IAAM;GAClC,CAAC,CAAC,CACC,MAAM,IAAI,CAAC,CACX,MAAM,SAAS,KAAK,WAAW,GAAG,CAAC,CAAC,EACnC,MAAM,CAAC;GACjB,MAAM,aAAa,QAAQ,KAAK;GAChC,IACE,CAAC,OACD,aAAa,GAAG,MAAM,aAAa,QAAQ,IAAI,CAAC,KAChD,CAAC,cACA,CAAC,QAAQ,SAAS,UAAU,KAC3B,CAAC,QAAQ,SAAS,aAAa,UAAU,CAAC,KAC1C,CAAC,2FAA2F,KAC1F,OACF,GAEF,MAAM,IAAI,MAAM,OAAO,IAAI,sDAAsD;GACnF,SAAS,KACP,0BAA0B,KAAK,OAAO,IAClC,2BAA2B,IAAI,kBAAkB,IAAI,uIAAuI,MAAM,2BAClM,4BAA4B,IAAI,YAAY,IAAI,yCAAyC,MAAM,sCACrG;EACF;EACA,KAAK,MAAM,WAAW,UAAU,QAAQ,IAAI,OAAO;EACnD,IAAI,SAAS,QAAQ,OAAO;EAC5B,QAAQ,IAAI,+CAA+C;EAC3D,OAAO;CACT,SAAS,OAAO;EACd,QAAQ,MACN,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,6DAA6D,IAAI,WAAW,IAAI,wGACzK;EACA,OAAO;CACT;AACF;;;AC5EA,MAAM,EACJ,aAAa,CAAC,OAAO,GAAG,QACxB,WACE,UAAU;CACZ,kBAAkB;CAClB,SAAS;EACP,QAAQ;GAAE,MAAM;GAAW,SAAS;EAAM;EAC1C,OAAO;GAAE,MAAM;GAAW,SAAS;EAAM;EACzC,QAAQ;GAAE,MAAM;GAAU,UAAU;GAAM,SAAS,CAAC;EAAE;CACxD;AACF,CAAC;AACD,IAAI,OAAO,QAAQ,QAAQ,KAAK,eAAe,CAAC;AAChD,IAAI,CAAC,OAAO;CACV,QAAQ,MACN,mFACF;CACA,QAAQ,KAAK,CAAC;AAChB;AACA,MAAM,WAAW,OAAO;AACxB,MAAM,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,KAAK,SAAS,QAAQ,IAAI,CAAC;AAC9D,MAAM,UAAU;CAAC;CAAc;CAAoB,GAAG,OAAO;AAAM,CAAC,CAAC,KAAK,SAAS,QAAQ,IAAI,CAAC;AAChG,MAAM,UAAU,MAAc,SAAiB,SAAS,QAAQ,KAAK,WAAW,OAAO,GAAG;AAC1F,MAAM,YAAY,SAAiB,QAAQ,MAAM,SAAS,OAAO,MAAM,IAAI,CAAC;AAC5E,MAAM,MAAM,QAAQ,QAAQ,SAAS,GAAG;AACxC,MAAM,cAAc,MAAM,aAAa,eAAe,WAAW,cAAc;AAC/E,MAAM,UAAU,QAAQ,aAAa;AAErC,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI,WAAW;AACf,IAAI,UAAU;AACd,IAAI,aAAa;AACjB,IAAI,2BAAW,IAAI,IAA2B;AAC9C,MAAM,2BAAW,IAAI,IAAwD;AAE7E,SAAS,OAAO,MAAgB,UAAU,QAAQ,UAAU;CAC1D,MAAM,QAAQ,MAAM,SAAS,MAAM;EACjC,OAAO;EACP,UAAU,CAAC;EACX,OAAO,WAAW,YAAY;CAChC,CAAC;CAQD,OAAO;EAAE;EAAO,MAAA,IAPC,SAAwB,YAAY;GACnD,MAAM,KAAK,UAAU,UAAU;IAC7B,QAAQ,MAAM,aAAa,KAAK,CAAC;IACjC,QAAQ,CAAC;GACX,CAAC;GACD,MAAM,KAAK,SAAS,OAAO;EAC7B,CACmB;CAAE;AACvB;AAEA,SAAS,WAAW;CAClB,MAAM,wBAAQ,IAAI,IAA2B;CAC7C,SAAS,QAAQ,MAAc;EAC7B,IAAI,SAAS,IAAI,KAAK,MAAM,IAAI,IAAI,GAAG;EACvC,IAAI;GACF,MAAM,OAAO,UAAU,MAAM,EAAE,QAAQ,KAAK,CAAC;GAC7C,IAAI,KAAK,YAAY,GAAG;IACtB,MAAM,IAAI,MAAM,WAAW;IAC3B,KAAK,MAAM,QAAQ,YAAY,IAAI,GAAG,QAAQ,KAAK,MAAM,IAAI,CAAC;GAChE,OAEE,MAAM,IAAI,MAAM;IAAC,KAAK;IAAK,KAAK;IAAK,KAAK;IAAM,KAAK;IAAS,KAAK;GAAO,CAAC,CAAC,KAAK,GAAG,CAAC;EAEzF,SAAS,OAAO;GACd,IAAI,UAAU,KAAK,MAAM,YAAY,UAAU,KAAK,MAAM,WAAW,MAAM;GAC3E,MAAM,IAAI,MAAM,IAAI;EACtB;CACF;CACA,KAAK,MAAM,QAAQ,UAAU,QAAQ,IAAI;CACzC,OAAO;AACT;AAEA,SAAS,MAAM,MAAkC,OAAmC;CAClF,OAAO,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,WAAW,MAAM,IAAI,IAAI,MAAM,KAAK;AACjG;AAEA,SAAS,kBAAkB;CACzB,MAAM,8BAAc,IAAI,IAAqB;CAC7C,MAAM,OAAO,WAAmB,cAAuB;EACrD,YAAY,IAAI,WAAW,aAAa,YAAY,IAAI,SAAS,KAAK,KAAK;CAC7E;CACA,KAAK,MAAM,QAAQ,UAAU;EAC3B,IAAI,SAAS,IAAI,GAAG;EACpB,IAAI,UAAU,MAAM,EAAE,gBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI,MAAM,IAAI;EAE7E,IAAI,SAAS,QAAQ,IAAI;EACzB,OAAO,CAAC,WAAW,MAAM,GAAG,SAAS,QAAQ,MAAM;EACnD,IAAI,QAAQ,WAAW,QAAQ,IAAI,CAAC;CACtC;CACA,KAAK,MAAM,CAAC,WAAW,UAAU,UAC/B,IAAI,YAAY,IAAI,SAAS,MAAM,MAAM,WAAW;EAClD,MAAM,QAAQ,MAAM;EACpB,SAAS,OAAO,SAAS;CAC3B;CAEF,KAAK,MAAM,CAAC,WAAW,cAAc,aAAa;EAChD,IAAI,SAAS,IAAI,SAAS,GAAG;EAC7B,MAAM,UAAU,MAAM,WAAW,EAAE,UAAU,IAAI,QAAQ,SAAS;GAChE,IAAI,YAAY,SAAS;GACzB,MAAM,UAAU,QAAQ,QAAQ,WAAW,KAAK,SAAS,CAAC;GAC1D,IACE,CAAC,WACA,CAAC,SAAS,OAAO,KAChB,SAAS,MAAM,SAAS,OAAO,SAAS,IAAI,KAAK,OAAO,MAAM,OAAO,CAAC,GAExE,SAAS;EACb,CAAC;EACD,QAAQ,GAAG,SAAS,IAAI;EACxB,SAAS,IAAI,WAAW;GAAE;GAAS;EAAU,CAAC;CAChD;AACF;AAEA,eAAe,KAAK,KAAsB;CACxC,IAAI,CAAC,KAAK,MAAM,KAAK;CACrB,MAAM,MAAM,IAAI,MAAM;CACtB,IAAI,SAAS;EACX,IAAI,IAAI,MAAM,aAAa,QAAQ,IAAI,MAAM,eAAe,MAC1D,UAAU,YAAY;GAAC;GAAQ,OAAO,IAAI,MAAM,GAAG;GAAG;GAAM;EAAI,GAAG,EAAE,OAAO,SAAS,CAAC;EAExF,MAAM,IAAI;EACV;CACF;CACA,MAAM,UAAU,SAA6B;EAC3C,IAAI;GACF,QAAQ,KAAK,CAAC,KAAK,IAAI;GACvB,OAAO;EACT,SAAS,OAAO;GACd,IAAI,UAAU,KAAK,MAAM,SAAS,MAAM;GACxC,OAAO;EACT;CACF;CACA,OAAO,SAAS;CAEhB,MAAM,WAAW,YAAY,IAAI,IAAI;CACrC,OAAO,OAAO,CAAC,GAAG;EAChB,IAAI,YAAY,IAAI,KAAK,UAAU;GACjC,OAAO,SAAS;GAChB;EACF;EACA,MAAMC,aAAM,EAAE;CAChB;CACA,MAAM,IAAI;AACZ;AAEA,eAAe,QAAQ;CACrB,IAAI,WAAW,UAAU;CACzB,IAAI,CAAC,cAAc,MAAM,UAAU,SAAS,CAAC,GAAG;CAChD,WAAW;CACX,aAAa;CACb,MAAM,KAAK,QAAQ;CACnB,WAAW,KAAA;CACX,IAAI,SAAS;CACb,gBAAgB;CAChB,WAAW,SAAS;CACpB,QAAQ,OAAO,MAAM,sBAA4B;CACjD,QAAQ,IAAI,wBAAwB;CACpC,QAAQ,OAAO,CAAC,OAAO,KAAK,GAAG,MAAM,QAAQ,WAAW,UAAU,YAAY,KAAK;CACnF,MAAM,QAAQ,MAAM,MAAM;CAC1B,IAAI,SAAS;CACb,MAAM,UAAU,SAAS;CAEzB,aAAa,CAAC,MAAM,UAAU,OAAO;CACrC,WAAW;CACX,gBAAgB;CAChB,QAAQ,KAAA;CACR,WAAW;CACX,IAAI,YACF,SAAS;MACJ,IAAI,UAAU,GAAG;EACtB,QAAQ,IAAI,qBAAqB;EACjC,WAAW,OAAO,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,mBAAmB,KAAK,CAAC;CACrE,OACE,QAAQ,IAAI,qCAAqC;AAErD;AAEA,SAAS,WAAW;CAClB,aAAa,KAAK;CAClB,QAAQ,iBAAiB,MAAM,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC;AACjD;AAEA,eAAe,SAAS,MAAc;CACpC,IAAI,SAAS;CACb,UAAU;CACV,aAAa,KAAK;CAClB,KAAK,MAAM,EAAE,aAAa,SAAS,OAAO,GAAG,QAAQ,MAAM;CAC3D,MAAM,QAAQ,IAAI,CAAC,KAAK,KAAK,GAAG,KAAK,QAAQ,CAAC,CAAC;CAC/C,cAAc;CACd,QAAQ,KAAK,IAAI;AACnB;AAEA,SAAS,UAAU,OAAgB;CACjC,OAAO,iBAAiB,SAAS,UAAU,QAAQ,MAAM,OAAO,KAAA;AAClE;AAEA,SAAS,aAAa,OAAgB;CACpC,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,SAAS,KAAK,OAAgB;CAC5B,QAAQ,MAAM,aAAa,KAAK,CAAC;CACjC,SAAc,CAAC;AACjB;AAEA,QAAQ,GAAG,gBAAgB,KAAK,SAAS,CAAC,CAAC;AAC3C,QAAQ,GAAG,iBAAiB,KAAK,SAAS,CAAC,CAAC;AAE5C,eAAe,UAAU;CACvB,QAAQ,IAAI,wBAAwB;CACpC,QAAQ,OAAO,CAAC,OAAO,KAAK,GAAG,MAAM,QAAQ,WAAW,UAAU,YAAY,KAAK;CACnF,MAAM,QAAQ,MAAM,MAAM;CAC1B,IAAI,SAAS;CACb,IAAI,UAAU,GAAG;EACf,MAAM,SAAS,SAAS,CAAC;EACzB;CACF;CACA,MAAM,KAAK,KAAK;CAChB,QAAQ,KAAA;CACR,IAAI,SAAS;CACb,QAAQ,IAAI,qBAAqB;CACjC,WAAW,OAAO,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,mBAAmB,KAAK,CAAC;CAEnE,MAAM,SAAS,MADM,SAAS,QACL,CAAC;AAC5B;AAEA,IAAI;CACF,cAAc,MAAM,eAAe,UAAU;CAC7C,IAAI,SAAS,YAAY;MACpB,IAAI,UAAU;EACjB,gBAAgB;EAChB,MAAW,CAAC,CAAC,MAAM,IAAI;CACzB,OACE,QAAa,CAAC,CAAC,MAAM,IAAI;AAE7B,SAAS,OAAO;CACd,qBAAK,IAAI,MAAM,kBAAkB,aAAa,OAAO,aAAa,KAAK,CAAC,CAAC;AAC3E"}
1
+ {"version":3,"file":"sda.mjs","names":["errorCode","delay","errorCode","delay"],"sources":["../src/helpers/sdaLock.ts","../src/helpers/sdaStatus.ts","../src/sda.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n realpathSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { isObject } from \"./jsonObject.ts\";\n\nexport const SDA_PID_FILE = \"sda/.sda.pid\";\nexport const SDA_LOCK_DIRECTORY = \"sda/.sda-lock\";\n\ntype Claim = { id: string; pid: number; ticket: number; command?: string };\n\nfunction errorCode(error: unknown) {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\n// Bakery ordering: publish a unique claim before choosing a ticket, then wait for\n// concurrent ticket choices. Never replace/delete a shared lock during recovery.\n// https://www.microsoft.com/en-us/research/publication/new-solution-dijkstras-concurrent-programming-problem/\nexport async function acquireSdaLock(command: string): Promise<() => void> {\n const directory = resolve(SDA_LOCK_DIRECTORY);\n const pidFile = resolve(SDA_PID_FILE);\n const id = `${process.pid}-${randomUUID()}`;\n const ownDirectory = join(directory, id);\n mkdirSync(directory, { recursive: true });\n mkdirSync(ownDirectory);\n let ownsPid = false;\n const release = () => {\n if (ownsPid) {\n // Remove the informational PID before releasing ownership to the next run.\n rmSync(pidFile, { force: true });\n ownsPid = false;\n }\n rmSync(ownDirectory, { recursive: true, force: true });\n process.removeListener(\"exit\", release);\n };\n process.once(\"exit\", release);\n\n function readClaim(candidate: string): Claim | undefined {\n const match = /^(\\d+)-[\\da-f-]{36}$/.exec(candidate);\n if (!match) return;\n const pid = Number(match[1]);\n if (!Number.isInteger(pid) || pid <= 0 || pid > 2147483647) return;\n const path = join(directory, candidate);\n try {\n process.kill(pid, 0);\n } catch (error) {\n if (errorCode(error) !== \"ESRCH\") throw error;\n // Claim names are never reused, so another starter cannot acquire this path.\n rmSync(path, { recursive: true, force: true });\n return;\n }\n let record: unknown;\n try {\n record = JSON.parse(readFileSync(join(path, \"owner.json\"), \"utf8\"));\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n // A directory without a published ticket is still choosing, or has exited.\n return existsSync(path) ? { id: candidate, pid, ticket: 0 } : undefined;\n }\n if (\n !isObject(record) ||\n typeof record.ticket !== \"number\" ||\n !Number.isSafeInteger(record.ticket) ||\n record.ticket < 1 ||\n typeof record.command !== \"string\"\n )\n throw new Error(`Cannot read the SDA lock owned by process ${pid}.`);\n return { id: candidate, pid, ticket: record.ticket, command: record.command };\n }\n\n try {\n let ticket = 1;\n for (const candidate of readdirSync(directory)) {\n const claim = readClaim(candidate);\n if (claim) ticket = Math.max(ticket, claim.ticket + 1);\n }\n writeFileSync(\n join(ownDirectory, \"owner.tmp\"),\n JSON.stringify({ ticket, command, projectRoot: realpathSync(process.cwd()) }),\n );\n renameSync(join(ownDirectory, \"owner.tmp\"), join(ownDirectory, \"owner.json\"));\n const deadline = performance.now() + 5000;\n for (const candidate of readdirSync(directory)) {\n if (candidate === id) continue;\n let claim = readClaim(candidate);\n while (claim?.ticket === 0) {\n if (performance.now() >= deadline)\n throw new Error(\n `Another SDA command is still starting (PID ${claim.pid}). Wait for it to finish starting, then try again.`,\n );\n await delay(10);\n claim = readClaim(candidate);\n }\n if (claim && (claim.ticket < ticket || (claim.ticket === ticket && claim.id < id)))\n throw new Error(\n `${claim.command} is already running${claim.command?.endsWith(\"sda:watch\") ? \" and watching this project's files\" : \" an analysis for this project\"} (PID ${claim.pid}). Wait for it to finish or stop it with Ctrl+C in its terminal, then try again.`,\n );\n }\n // Only the winner touches the shared PID file, including stale-file recovery.\n writeFileSync(pidFile, String(process.pid) + \"\\n\");\n ownsPid = true;\n return release;\n } catch (error) {\n release();\n throw error;\n }\n}\n","import { execFileSync } from \"node:child_process\";\nimport { readFileSync, readdirSync, realpathSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { isObject } from \"./jsonObject.ts\";\nimport { SDA_LOCK_DIRECTORY, SDA_PID_FILE } from \"./sdaLock.ts\";\n\nfunction errorCode(error: unknown) {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\n// Status is read-only: a missing or dead owner is ignored, never removed.\nexport function printSdaStatus(): number {\n const run = `${process.versions.bun ? \"bun\" : \"npm\"} run`;\n const retry = `${run} sda:status`;\n try {\n const pids = new Set<number>();\n const claims = new Map<number, string[]>();\n try {\n for (const name of readdirSync(SDA_LOCK_DIRECTORY)) {\n const match = /^(\\d+)-[\\da-f-]{36}$/.exec(name);\n if (match) {\n const pid = Number(match[1]);\n pids.add(pid);\n claims.set(pid, [...(claims.get(pid) ?? []), name]);\n }\n }\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n }\n try {\n const value = readFileSync(SDA_PID_FILE, \"utf8\").trim();\n if (!/^\\d+$/.test(value)) throw new Error(\"The SDA PID file is invalid.\");\n pids.add(Number(value));\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\") throw error;\n }\n const messages: string[] = [];\n for (const pid of pids) {\n if (!Number.isInteger(pid) || pid <= 0 || pid > 2147483647)\n throw new Error(\"The recorded SDA PID is invalid.\");\n try {\n process.kill(pid, 0);\n } catch (error) {\n if (errorCode(error) === \"ESRCH\") continue;\n throw error;\n }\n let command: string;\n if (process.platform === \"win32\") {\n const owners = claims.get(pid) ?? [];\n if (owners.length !== 1) throw new Error(`PID ${pid} has no unambiguous SDA owner record.`);\n const owner: unknown = JSON.parse(\n readFileSync(join(SDA_LOCK_DIRECTORY, owners[0], \"owner.json\"), \"utf8\"),\n );\n // WMI has no readable cwd. This is the claim's recorded project association,\n // not proof of the live process's cwd or protection against PID reuse.\n if (\n !isObject(owner) ||\n typeof owner.projectRoot !== \"string\" ||\n realpathSync(owner.projectRoot) !== realpathSync(process.cwd())\n )\n throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);\n const result: unknown = JSON.parse(\n execFileSync(\n \"powershell.exe\",\n [\n \"-NoLogo\",\n \"-NoProfile\",\n \"-NonInteractive\",\n \"-Command\",\n \"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false); \" +\n `(Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}' -ErrorAction Stop).CommandLine | ConvertTo-Json -Compress`,\n ],\n {\n encoding: \"utf8\",\n timeout: 5000,\n maxBuffer: 64 * 1024,\n windowsHide: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n },\n ),\n );\n if (typeof result !== \"string\" || !result.trim())\n throw new Error(`PID ${pid} has no readable command line.`);\n command = result;\n } else if (process.platform === \"darwin\" || process.platform === \"linux\") {\n command = execFileSync(\"ps\", [\"-p\", String(pid), \"-o\", \"command=\"], {\n encoding: \"utf8\",\n timeout: 5000,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n }).trim();\n const cwd =\n process.platform === \"linux\"\n ? realpathSync(`/proc/${pid}/cwd`)\n : execFileSync(\"lsof\", [\"-a\", \"-p\", String(pid), \"-d\", \"cwd\", \"-Fn\"], {\n encoding: \"utf8\",\n timeout: 5000,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n })\n .split(\"\\n\")\n .find((line) => line.startsWith(\"n\"))\n ?.slice(1);\n if (!cwd || realpathSync(cwd) !== realpathSync(process.cwd()))\n throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);\n } else {\n throw new Error(\"Active SDA processes cannot be verified on this platform.\");\n }\n const executable = process.argv[1];\n if (\n !executable ||\n (!command.includes(executable) &&\n !command.includes(realpathSync(executable)) &&\n !/(?:[\\\\/]@nshiab[\\\\/]setup-data-project[\\\\/]dist[\\\\/]sda\\.mjs|[\\\\/]node_modules[\\\\/]\\.bin[\\\\/]sda-run)(?:[\"\\s]|$)/.test(\n command,\n ))\n )\n throw new Error(`PID ${pid} could not be verified as this project's SDA process.`);\n messages.push(\n /(?:^|[\"\\s])--watch(?:[\"\\s]|$)/.test(command)\n ? `SDA watcher is running (${run} sda:watch, PID ${pid}). It automatically fixes files and reruns the analysis. If you are an agent, ask the user to stop it with Ctrl+C, then recheck with ${retry} before doing any work.`\n : `SDA analysis is running (${run} sda, PID ${pid}). Wait for it to finish. Recheck with ${retry} in 30 seconds before doing any work.`,\n );\n }\n for (const message of messages) console.log(message);\n if (messages.length) return 1;\n console.log(\"No active SDA process found. You can proceed.\");\n return 0;\n } catch (error) {\n console.error(\n `Unable to verify SDA status: ${error instanceof Error ? error.message : String(error)} If you are an agent, ask the user to confirm that neither ${run} sda nor ${run} sda:watch is running and that you may proceed, then wait for their confirmation before doing any work.`,\n );\n return 2;\n }\n}\n","#!/usr/bin/env node\nimport { existsSync, lstatSync, type FSWatcher, readdirSync, watch } from \"node:fs\";\nimport { spawn, spawnSync, type ChildProcess } from \"node:child_process\";\nimport { dirname, join, resolve, sep } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { parseArgs } from \"node:util\";\nimport { acquireSdaLock, SDA_PID_FILE, SDA_LOCK_DIRECTORY } from \"./helpers/sdaLock.ts\";\nimport { printSdaStatus } from \"./helpers/sdaStatus.ts\";\n\nconst {\n positionals: [entry, ...paths],\n values,\n} = parseArgs({\n allowPositionals: true,\n options: {\n status: { type: \"boolean\", default: false },\n watch: { type: \"boolean\", default: false },\n ignore: { type: \"string\", multiple: true, default: [] },\n },\n});\nif (values.status) process.exit(printSdaStatus());\nif (!entry) {\n console.error(\n \"Usage: sda-run --status | [--watch] <entry-file> [watch-paths...] [--ignore path]\",\n );\n process.exit(1);\n}\nconst watching = values.watch;\nconst selected = [entry, ...paths].map((path) => resolve(path));\nconst ignored = [SDA_PID_FILE, SDA_LOCK_DIRECTORY, ...values.ignore].map((path) => resolve(path));\nconst within = (path: string, root: string) => path === root || path.startsWith(root + sep);\nconst excluded = (path: string) => ignored.some((root) => within(path, root));\nconst bun = Boolean(process.versions.bun);\nconst runCommand = (bun ? \"bun run \" : \"npm run \") + (watching ? \"sda:watch\" : \"sda\");\nconst windows = process.platform === \"win32\";\ntype Job = { child: ChildProcess; done: Promise<number | null> };\nlet releaseLock: (() => void) | undefined;\nlet fixer: Job | undefined;\nlet analysis: Job | undefined;\nlet timer: ReturnType<typeof setTimeout> | undefined;\nlet checking = false;\nlet closing = false;\nlet needsCheck = true;\nlet contents = new Map<string, string | null>();\nconst watchers = new Map<string, { watcher: FSWatcher; recursive: boolean }>();\n\nfunction launch(args: string[], command = process.execPath) {\n const child = spawn(command, args, {\n // Bun's Windows --bun shim is named node.exe; keep Bun CLI argument parsing.\n argv0: windows && bun && command === process.execPath ? \"bun\" : undefined,\n stdio: \"inherit\",\n detached: !windows,\n shell: windows && command === \"npm.cmd\",\n });\n const done = new Promise<number | null>((resolve) => {\n child.once(\"error\", (error) => {\n console.error(errorMessage(error));\n resolve(1);\n });\n child.once(\"close\", resolve);\n });\n return { child, done };\n}\n\nfunction snapshot() {\n const files = new Map<string, string | null>();\n function collect(path: string) {\n if (excluded(path) || files.has(path)) return;\n try {\n const stat = lstatSync(path, { bigint: true });\n if (stat.isDirectory()) {\n files.set(path, \"directory\");\n for (const name of readdirSync(path)) collect(join(path, name));\n } else {\n // Compare metadata without reading potentially large datasets or following symlinks.\n files.set(path, [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(\":\"));\n }\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\" && errorCode(error) !== \"ENOTDIR\") throw error;\n files.set(path, null);\n }\n }\n for (const path of selected) collect(path);\n return files;\n}\n\nfunction equal(left: Map<string, string | null>, right: Map<string, string | null>) {\n return left.size === right.size && [...left].every(([path, value]) => right.get(path) === value);\n}\n\nfunction refreshWatchers() {\n const directories = new Map<string, boolean>();\n const add = (directory: string, recursive: boolean) => {\n directories.set(directory, recursive || directories.get(directory) || false);\n };\n for (const path of selected) {\n if (excluded(path)) continue;\n if (lstatSync(path, { throwIfNoEntry: false })?.isDirectory()) add(path, true);\n // Watch the parent too, so deleted files and folders can be restored.\n let parent = dirname(path);\n while (!existsSync(parent)) parent = dirname(parent);\n add(parent, parent !== dirname(path));\n }\n for (const [directory, state] of watchers) {\n if (directories.get(directory) !== state.recursive) {\n state.watcher.close();\n watchers.delete(directory);\n }\n }\n for (const [directory, recursive] of directories) {\n if (watchers.has(directory)) continue;\n const watcher = watch(directory, { recursive }, (_event, name) => {\n if (checking || closing) return;\n const changed = name && resolve(directory, name.toString());\n if (\n !changed ||\n (!excluded(changed) &&\n selected.some((path) => within(changed, path) || within(path, changed)))\n )\n schedule();\n });\n watcher.on(\"error\", fail);\n watchers.set(directory, { watcher, recursive });\n }\n}\n\nasync function stop(job: Job | undefined) {\n if (!job?.child.pid) return;\n const pid = job.child.pid;\n if (windows) {\n if (job.child.exitCode === null && job.child.signalCode === null) {\n spawnSync(\"taskkill\", [\"/pid\", String(job.child.pid), \"/T\", \"/F\"], { stdio: \"ignore\" });\n }\n await job.done;\n return;\n }\n const signal = (name: NodeJS.Signals | 0) => {\n try {\n process.kill(-pid, name);\n return true;\n } catch (error) {\n if (errorCode(error) !== \"ESRCH\") throw error;\n return false;\n }\n };\n signal(\"SIGTERM\");\n // The direct child can exit first; retain the grace period for its entire process group.\n const deadline = performance.now() + 1000;\n while (signal(0)) {\n if (performance.now() >= deadline) {\n signal(\"SIGKILL\");\n break;\n }\n await delay(25);\n }\n await job.done;\n}\n\nasync function check() {\n if (closing || checking) return;\n if (!needsCheck && equal(contents, snapshot())) return;\n checking = true;\n needsCheck = false;\n await stop(analysis);\n analysis = undefined;\n if (closing) return;\n refreshWatchers();\n contents = snapshot();\n process.stdout.write(\"\\u001b[2J\\u001b[3J\\u001b[H\");\n console.log(\"Fixing and checking...\");\n fixer = launch([\"run\", \"fix\"], bun ? process.execPath : windows ? \"npm.cmd\" : \"npm\");\n const fixed = await fixer.done;\n if (closing) return;\n const current = snapshot();\n // Recheck edits made during fixes; ignore delayed events for unchanged file states.\n needsCheck = !equal(contents, current);\n contents = current;\n refreshWatchers();\n fixer = undefined;\n checking = false;\n if (needsCheck) {\n schedule();\n } else if (fixed === 0) {\n console.log(\"Running analysis...\");\n analysis = launch(bun ? [\"run\", entry] : [\"--env-file=.env\", entry]);\n } else {\n console.log(\"Fix failed. Watching for changes...\");\n }\n}\n\nfunction schedule() {\n clearTimeout(timer);\n timer = setTimeout(() => check().catch(fail), 0);\n}\n\nasync function shutdown(code: number) {\n if (closing) return;\n closing = true;\n clearTimeout(timer);\n for (const { watcher } of watchers.values()) watcher.close();\n await Promise.all([stop(fixer), stop(analysis)]);\n releaseLock?.();\n process.exit(code);\n}\n\nfunction errorCode(error: unknown) {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\nfunction errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction fail(error: unknown) {\n console.error(errorMessage(error));\n void shutdown(1);\n}\n\nprocess.on(\"SIGINT\", () => void shutdown(0));\nprocess.on(\"SIGTERM\", () => void shutdown(0));\n\nasync function runOnce() {\n console.log(\"Fixing and checking...\");\n fixer = launch([\"run\", \"fix\"], bun ? process.execPath : windows ? \"npm.cmd\" : \"npm\");\n const fixed = await fixer.done;\n if (closing) return;\n if (fixed !== 0) {\n await shutdown(fixed ?? 1);\n return;\n }\n await stop(fixer);\n fixer = undefined;\n if (closing) return;\n console.log(\"Running analysis...\");\n analysis = launch(bun ? [\"run\", entry] : [\"--env-file=.env\", entry]);\n const result = await analysis.done;\n await shutdown(result ?? 1);\n}\n\ntry {\n releaseLock = await acquireSdaLock(runCommand);\n if (closing) releaseLock();\n else if (watching) {\n refreshWatchers();\n void check().catch(fail);\n } else {\n void runOnce().catch(fail);\n }\n} catch (error) {\n fail(new Error(\"Cannot start \" + runCommand + \": \" + errorMessage(error)));\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nshiab/setup-data-project",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "description": "Quickly kick off a project using simple-data-analysis-core, simple-data-analysis, and journalism libraries.",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/nshiab/setup-data-project",