sdd-mcp-server 3.5.1 → 5.0.0
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/README.md +92 -683
- package/agents/architect.md +15 -93
- package/agents/implementer.md +16 -141
- package/agents/planner.md +16 -84
- package/agents/reviewer.md +16 -239
- package/agents/security-auditor.md +16 -114
- package/agents/tdd-guide.md +17 -228
- package/dist/adapters/cli/SDDToolAdapter.d.ts +14 -8
- package/dist/adapters/cli/SDDToolAdapter.js +188 -405
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
- package/dist/application/services/ContextCompactionService.d.ts +88 -16
- package/dist/application/services/ContextCompactionService.js +474 -187
- package/dist/application/services/ContextCompactionService.js.map +1 -1
- package/dist/application/services/ProjectService.js +3 -3
- package/dist/application/services/ProjectService.js.map +1 -1
- package/dist/application/services/SpecPathResolver.d.ts +24 -0
- package/dist/application/services/SpecPathResolver.js +70 -0
- package/dist/application/services/SpecPathResolver.js.map +1 -0
- package/dist/application/services/WorkflowEngineService.d.ts +214 -50
- package/dist/application/services/WorkflowEngineService.js +1447 -292
- package/dist/application/services/WorkflowEngineService.js.map +1 -1
- package/dist/application/services/WorkflowErrors.d.ts +16 -0
- package/dist/application/services/WorkflowErrors.js +53 -0
- package/dist/application/services/WorkflowErrors.js.map +1 -0
- package/dist/application/services/WorkflowValidationService.d.ts +25 -46
- package/dist/application/services/WorkflowValidationService.js +284 -627
- package/dist/application/services/WorkflowValidationService.js.map +1 -1
- package/dist/cli/install-skills.d.ts +3 -9
- package/dist/cli/install-skills.js +129 -174
- package/dist/cli/install-skills.js.map +1 -1
- package/dist/cli/install-target.d.ts +42 -8
- package/dist/cli/install-target.js +27 -9
- package/dist/cli/install-target.js.map +1 -1
- package/dist/cli/sdd-mcp-cli.d.ts +1 -1
- package/dist/cli/sdd-mcp-cli.js +7 -6
- package/dist/cli/sdd-mcp-cli.js.map +1 -1
- package/dist/cli/tool-support/claude-code.js +17 -34
- package/dist/cli/tool-support/claude-code.js.map +1 -1
- package/dist/cli/tool-support/codex.d.ts +0 -53
- package/dist/cli/tool-support/codex.js +10 -94
- package/dist/cli/tool-support/codex.js.map +1 -1
- package/dist/cli/tool-support/index.d.ts +3 -2
- package/dist/cli/tool-support/index.js +3 -1
- package/dist/cli/tool-support/index.js.map +1 -1
- package/dist/cli/tool-support/mcp-registration.d.ts +22 -0
- package/dist/cli/tool-support/mcp-registration.js +275 -0
- package/dist/cli/tool-support/mcp-registration.js.map +1 -0
- package/dist/cli/tool-support/omp.d.ts +5 -0
- package/dist/cli/tool-support/omp.js +47 -0
- package/dist/cli/tool-support/omp.js.map +1 -0
- package/dist/cli/tool-support/root-guidance.d.ts +2 -9
- package/dist/cli/tool-support/root-guidance.js +44 -37
- package/dist/cli/tool-support/root-guidance.js.map +1 -1
- package/dist/cli/tool-support/target-agent-renderer.d.ts +1 -0
- package/dist/cli/tool-support/target-agent-renderer.js +37 -4
- package/dist/cli/tool-support/target-agent-renderer.js.map +1 -1
- package/dist/cli/tool-support/target-installer.d.ts +9 -3
- package/dist/cli/tool-support/target-installer.js +100 -26
- package/dist/cli/tool-support/target-installer.js.map +1 -1
- package/dist/cli/utils/preserving-writer.d.ts +56 -0
- package/dist/cli/utils/preserving-writer.js +603 -10
- package/dist/cli/utils/preserving-writer.js.map +1 -1
- package/dist/domain/ports.d.ts +4 -0
- package/dist/domain/types.d.ts +52 -7
- package/dist/domain/types.js +5 -4
- package/dist/domain/types.js.map +1 -1
- package/dist/index.d.ts +13 -10
- package/dist/index.js +16 -1199
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.d.ts +3 -0
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.js +10 -0
- package/dist/infrastructure/adapters/NodeFileSystemAdapter.js.map +1 -1
- package/dist/infrastructure/mcp/CapabilityNegotiator.js +3 -3
- package/dist/infrastructure/mcp/CapabilityNegotiator.js.map +1 -1
- package/dist/infrastructure/mcp/MCPServer.js +13 -13
- package/dist/infrastructure/mcp/MCPServer.js.map +1 -1
- package/dist/infrastructure/mcp/ToolRegistry.d.ts +5 -1
- package/dist/infrastructure/mcp/ToolRegistry.js +11 -4
- package/dist/infrastructure/mcp/ToolRegistry.js.map +1 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.d.ts +6 -0
- package/dist/infrastructure/mcp/sddToolDefinitions.js +110 -0
- package/dist/infrastructure/mcp/sddToolDefinitions.js.map +1 -0
- package/dist/infrastructure/schemas/project.schema.d.ts +2 -2
- package/dist/infrastructure/schemas/project.schema.js +2 -2
- package/dist/infrastructure/schemas/project.schema.js.map +1 -1
- package/dist/shared/version.d.ts +3 -0
- package/dist/shared/version.js +4 -0
- package/dist/shared/version.js.map +1 -0
- package/dist/utils/atomicWrite.d.ts +8 -35
- package/dist/utils/atomicWrite.js +24 -57
- package/dist/utils/atomicWrite.js.map +1 -1
- package/dist/utils/withFilesystemLock.d.ts +22 -0
- package/dist/utils/withFilesystemLock.js +219 -0
- package/dist/utils/withFilesystemLock.js.map +1 -0
- package/mcp-server.js +5 -2883
- package/package.json +8 -3
- package/scripts/context-usage-report.mjs +602 -0
- package/sdd-entry.js +17 -6
- package/skills/sdd-commit/REFERENCE.md +31 -0
- package/skills/sdd-commit/SKILL.md +17 -273
- package/skills/sdd-design/REFERENCE.md +51 -0
- package/skills/sdd-design/SKILL.md +25 -262
- package/skills/sdd-implement/REFERENCE.md +30 -0
- package/skills/sdd-implement/SKILL.md +27 -284
- package/skills/sdd-requirements/REFERENCE.md +39 -0
- package/skills/sdd-requirements/SKILL.md +28 -132
- package/skills/sdd-review/REFERENCE.md +26 -0
- package/skills/sdd-review/SKILL.md +17 -181
- package/skills/sdd-security-check/REFERENCE.md +19 -0
- package/skills/sdd-security-check/SKILL.md +18 -184
- package/skills/sdd-steering/REFERENCE.md +25 -0
- package/skills/sdd-steering/SKILL.md +18 -216
- package/skills/sdd-steering-custom/REFERENCE.md +27 -0
- package/skills/sdd-steering-custom/SKILL.md +19 -203
- package/skills/sdd-tasks/REFERENCE.md +25 -0
- package/skills/sdd-tasks/SKILL.md +27 -244
- package/skills/sdd-test-gen/REFERENCE.md +15 -0
- package/skills/sdd-test-gen/SKILL.md +17 -287
- package/skills/simple-task/REFERENCE.md +22 -0
- package/skills/simple-task/SKILL.md +17 -138
- package/templates/CLAUDE.md +13 -31
- package/templates/codex-AGENTS.md +7 -9
- package/rules/git-workflow.md +0 -92
- package/rules/sdd-workflow.md +0 -116
|
@@ -1,64 +1,31 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import writeFileAtomic from "write-file-atomic";
|
|
4
|
+
const pendingWrites = new Map();
|
|
1
5
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Prevents file corruption when write operations are interrupted (crash, kill signal).
|
|
6
|
-
* Uses the temp-file + rename pattern which is atomic on POSIX systems.
|
|
7
|
-
* 防止寫入操作被中斷時(crash、kill signal)造成檔案損壞。
|
|
8
|
-
* 使用 temp-file + rename 模式,在 POSIX 系統上是原子操作。
|
|
9
|
-
*
|
|
10
|
-
* @note Windows Limitation / Windows 限制:
|
|
11
|
-
* On Windows, fs.rename() may fail with EEXIST if target exists.
|
|
12
|
-
* For Windows production use, consider using 'write-file-atomic' package.
|
|
13
|
-
* 在 Windows 上,若目標檔案存在,fs.rename() 可能會失敗。
|
|
14
|
-
* 若需在 Windows 生產環境使用,建議改用 'write-file-atomic' 套件。
|
|
15
|
-
*
|
|
16
|
-
* @module utils/atomicWrite
|
|
17
|
-
*/
|
|
18
|
-
import * as fs from "fs/promises";
|
|
19
|
-
import * as path from "path";
|
|
20
|
-
/**
|
|
21
|
-
* Write content to a file atomically.
|
|
22
|
-
*
|
|
23
|
-
* This function writes to a temporary file first, then renames it to the target path.
|
|
24
|
-
* The rename operation is atomic on POSIX systems, ensuring the file is never left
|
|
25
|
-
* in a corrupted state.
|
|
26
|
-
*
|
|
27
|
-
* @param filePath - The target file path
|
|
28
|
-
* @param content - The content to write
|
|
29
|
-
* @param options - Optional write options
|
|
30
|
-
* @returns Promise that resolves when write is complete
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```typescript
|
|
34
|
-
* await atomicWriteFile('/path/to/spec.json', JSON.stringify(data, null, 2));
|
|
35
|
-
* ```
|
|
6
|
+
* Atomically replaces a file. `write-file-atomic` queues concurrent writes to
|
|
7
|
+
* the same destination, preserves an existing mode by default, cleans its
|
|
8
|
+
* temporary file on failure, and implements replacement on Windows.
|
|
36
9
|
*/
|
|
37
|
-
export async function atomicWriteFile(filePath, content, options) {
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
10
|
+
export async function atomicWriteFile(filePath, content, options = {}) {
|
|
11
|
+
const key = resolve(filePath);
|
|
12
|
+
const prior = pendingWrites.get(key) ?? Promise.resolve();
|
|
13
|
+
const operation = prior
|
|
14
|
+
.catch(() => undefined)
|
|
15
|
+
.then(async () => {
|
|
16
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
17
|
+
await writeFileAtomic(filePath, content, {
|
|
18
|
+
encoding: options.encoding ?? "utf8",
|
|
19
|
+
mode: options.mode,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
pendingWrites.set(key, operation);
|
|
45
23
|
try {
|
|
46
|
-
|
|
47
|
-
await fs.mkdir(dir, { recursive: true });
|
|
48
|
-
// Write to temp file
|
|
49
|
-
await fs.writeFile(tempPath, content, encoding);
|
|
50
|
-
// Atomic rename (on POSIX systems)
|
|
51
|
-
await fs.rename(tempPath, filePath);
|
|
24
|
+
await operation;
|
|
52
25
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
await fs.unlink(tempPath);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
// Ignore cleanup errors
|
|
60
|
-
}
|
|
61
|
-
throw error;
|
|
26
|
+
finally {
|
|
27
|
+
if (pendingWrites.get(key) === operation)
|
|
28
|
+
pendingWrites.delete(key);
|
|
62
29
|
}
|
|
63
30
|
}
|
|
64
31
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomicWrite.js","sourceRoot":"","sources":["../../src/utils/atomicWrite.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"atomicWrite.js","sourceRoot":"","sources":["../../src/utils/atomicWrite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAEhD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAyB,CAAC;AAOvD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,OAAe,EACf,UAA8B,EAAE;IAEhC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1D,MAAM,SAAS,GAAG,KAAK;SACpB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACtB,IAAI,CAAC,KAAK,IAAI,EAAE;QACf,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE;YACvC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,MAAM;YACpC,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACL,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,SAAS,CAAC;IAClB,CAAC;YAAS,CAAC;QACT,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,IAAa,EACb,OAA6B;IAE7B,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface FilesystemLockLease {
|
|
2
|
+
assertHeld(): Promise<void>;
|
|
3
|
+
}
|
|
4
|
+
export interface FilesystemLockOptions {
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
retryDelayMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class FilesystemLockTimeoutError extends Error {
|
|
9
|
+
readonly lockPath: string;
|
|
10
|
+
readonly code = "LockTimeout";
|
|
11
|
+
constructor(lockPath: string);
|
|
12
|
+
}
|
|
13
|
+
export declare class FilesystemLockCompromisedError extends Error {
|
|
14
|
+
readonly lockPath: string;
|
|
15
|
+
readonly code = "LockCompromised";
|
|
16
|
+
constructor(lockPath: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Runs an action while holding a token-fenced, cross-process filesystem lease.
|
|
20
|
+
* Call assertHeld immediately after every durable write made by the action.
|
|
21
|
+
*/
|
|
22
|
+
export declare function withFilesystemLock<T>(lockPath: string, action: (lease: FilesystemLockLease) => Promise<T>, options?: FilesystemLockOptions): Promise<T>;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { link, lstat, mkdir, open, rename, unlink } from 'node:fs/promises';
|
|
3
|
+
import { hostname as localHostname } from 'node:os';
|
|
4
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
|
+
export class FilesystemLockTimeoutError extends Error {
|
|
6
|
+
lockPath;
|
|
7
|
+
code = 'LockTimeout';
|
|
8
|
+
constructor(lockPath) {
|
|
9
|
+
super(`Timed out acquiring filesystem lock: ${lockPath}`);
|
|
10
|
+
this.lockPath = lockPath;
|
|
11
|
+
this.name = 'FilesystemLockTimeoutError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class FilesystemLockCompromisedError extends Error {
|
|
15
|
+
lockPath;
|
|
16
|
+
code = 'LockCompromised';
|
|
17
|
+
constructor(lockPath) {
|
|
18
|
+
super(`Filesystem lock ownership was lost: ${lockPath}`);
|
|
19
|
+
this.lockPath = lockPath;
|
|
20
|
+
this.name = 'FilesystemLockCompromisedError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function parseOwner(bytes) {
|
|
24
|
+
try {
|
|
25
|
+
const value = JSON.parse(bytes);
|
|
26
|
+
if (typeof value.token !== 'string' || value.token.length === 0
|
|
27
|
+
|| !Number.isSafeInteger(value.pid) || value.pid <= 0
|
|
28
|
+
|| typeof value.hostname !== 'string' || value.hostname.length === 0)
|
|
29
|
+
return undefined;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function readSnapshot(lockPath) {
|
|
37
|
+
let handle;
|
|
38
|
+
try {
|
|
39
|
+
handle = await open(lockPath, 'r');
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error.code === 'ENOENT')
|
|
43
|
+
return undefined;
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const stat = await handle.stat({ bigint: true });
|
|
48
|
+
const bytes = stat.size <= 4096n ? await handle.readFile('utf8') : undefined;
|
|
49
|
+
const canonical = await lstat(lockPath, { bigint: true }).catch((error) => {
|
|
50
|
+
if (error.code === 'ENOENT')
|
|
51
|
+
return undefined;
|
|
52
|
+
throw error;
|
|
53
|
+
});
|
|
54
|
+
if (!canonical || canonical.dev !== stat.dev || canonical.ino !== stat.ino)
|
|
55
|
+
return undefined;
|
|
56
|
+
return {
|
|
57
|
+
owner: bytes === undefined ? undefined : parseOwner(bytes),
|
|
58
|
+
identity: { dev: stat.dev, ino: stat.ino },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await handle.close();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function sameIdentity(left, right) {
|
|
66
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
67
|
+
}
|
|
68
|
+
function ownerIsProvenDead(owner, hostname) {
|
|
69
|
+
if (owner.hostname !== hostname)
|
|
70
|
+
return false;
|
|
71
|
+
try {
|
|
72
|
+
process.kill(owner.pid, 0);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return error.code === 'ESRCH';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function removeIfPresent(path) {
|
|
80
|
+
try {
|
|
81
|
+
await unlink(path);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error.code !== 'ENOENT')
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function writeDurableOwner(path, owner) {
|
|
89
|
+
const handle = await open(path, 'wx', 0o600);
|
|
90
|
+
try {
|
|
91
|
+
await handle.writeFile(JSON.stringify(owner), 'utf8');
|
|
92
|
+
await handle.sync();
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await handle.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function tryEvictDeadOwner(lockPath, observed, hostname) {
|
|
99
|
+
if (!observed.owner || !ownerIsProvenDead(observed.owner, hostname))
|
|
100
|
+
return false;
|
|
101
|
+
const current = await readSnapshot(lockPath);
|
|
102
|
+
if (!current?.owner
|
|
103
|
+
|| current.owner.token !== observed.owner.token
|
|
104
|
+
|| !sameIdentity(current.identity, observed.identity)
|
|
105
|
+
|| !ownerIsProvenDead(current.owner, hostname))
|
|
106
|
+
return false;
|
|
107
|
+
const quarantine = join(dirname(lockPath), `.${basename(lockPath)}.${randomUUID()}.quarantine`);
|
|
108
|
+
try {
|
|
109
|
+
await rename(lockPath, quarantine);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error.code === 'ENOENT')
|
|
113
|
+
return true;
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
const moved = await readSnapshot(quarantine);
|
|
117
|
+
if (!moved?.owner
|
|
118
|
+
|| moved.owner.token !== observed.owner.token
|
|
119
|
+
|| moved.owner.pid !== observed.owner.pid
|
|
120
|
+
|| moved.owner.hostname !== observed.owner.hostname
|
|
121
|
+
|| !sameIdentity(moved.identity, observed.identity)) {
|
|
122
|
+
try {
|
|
123
|
+
await link(quarantine, lockPath);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Preserve whichever owner currently occupies the canonical path.
|
|
127
|
+
}
|
|
128
|
+
await removeIfPresent(quarantine);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
await removeIfPresent(quarantine);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
async function delay(milliseconds) {
|
|
135
|
+
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Runs an action while holding a token-fenced, cross-process filesystem lease.
|
|
139
|
+
* Call assertHeld immediately after every durable write made by the action.
|
|
140
|
+
*/
|
|
141
|
+
export async function withFilesystemLock(lockPath, action, options = {}) {
|
|
142
|
+
const timeoutMs = options.timeoutMs ?? 2_000;
|
|
143
|
+
const retryDelayMs = options.retryDelayMs ?? 25;
|
|
144
|
+
const deadline = Date.now() + timeoutMs;
|
|
145
|
+
await mkdir(dirname(lockPath), { recursive: true });
|
|
146
|
+
const owner = { token: randomUUID(), pid: process.pid, hostname: localHostname() };
|
|
147
|
+
const tempPath = join(dirname(lockPath), `.${basename(lockPath)}.${owner.token}.tmp`);
|
|
148
|
+
await writeDurableOwner(tempPath, owner);
|
|
149
|
+
let acquired = false;
|
|
150
|
+
try {
|
|
151
|
+
while (!acquired) {
|
|
152
|
+
try {
|
|
153
|
+
await link(tempPath, lockPath);
|
|
154
|
+
acquired = true;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const code = error.code;
|
|
158
|
+
if (code !== 'EEXIST')
|
|
159
|
+
throw error;
|
|
160
|
+
const observed = await readSnapshot(lockPath);
|
|
161
|
+
if (observed)
|
|
162
|
+
await tryEvictDeadOwner(lockPath, observed, owner.hostname);
|
|
163
|
+
if (Date.now() >= deadline)
|
|
164
|
+
throw new FilesystemLockTimeoutError(lockPath);
|
|
165
|
+
await delay(retryDelayMs);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
await removeIfPresent(tempPath);
|
|
171
|
+
}
|
|
172
|
+
const acquiredSnapshot = await readSnapshot(lockPath);
|
|
173
|
+
if (!acquiredSnapshot?.owner || acquiredSnapshot.owner.token !== owner.token) {
|
|
174
|
+
throw new FilesystemLockCompromisedError(lockPath);
|
|
175
|
+
}
|
|
176
|
+
const assertHeld = async () => {
|
|
177
|
+
const current = await readSnapshot(lockPath);
|
|
178
|
+
if (!current?.owner
|
|
179
|
+
|| current.owner.token !== owner.token
|
|
180
|
+
|| !sameIdentity(current.identity, acquiredSnapshot.identity))
|
|
181
|
+
throw new FilesystemLockCompromisedError(lockPath);
|
|
182
|
+
};
|
|
183
|
+
let actionError;
|
|
184
|
+
let value;
|
|
185
|
+
try {
|
|
186
|
+
await assertHeld();
|
|
187
|
+
value = await action({ assertHeld });
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
actionError = error;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
await assertHeld();
|
|
194
|
+
const releasePath = join(dirname(lockPath), `.${basename(lockPath)}.${owner.token}.release`);
|
|
195
|
+
await rename(lockPath, releasePath);
|
|
196
|
+
const released = await readSnapshot(releasePath);
|
|
197
|
+
if (!released?.owner
|
|
198
|
+
|| released.owner.token !== owner.token
|
|
199
|
+
|| !sameIdentity(released.identity, acquiredSnapshot.identity)) {
|
|
200
|
+
try {
|
|
201
|
+
await link(releasePath, lockPath);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Preserve whichever owner currently occupies the canonical path.
|
|
205
|
+
}
|
|
206
|
+
throw new FilesystemLockCompromisedError(lockPath);
|
|
207
|
+
}
|
|
208
|
+
await removeIfPresent(releasePath);
|
|
209
|
+
}
|
|
210
|
+
catch (releaseError) {
|
|
211
|
+
if (actionError === undefined
|
|
212
|
+
|| releaseError instanceof FilesystemLockCompromisedError)
|
|
213
|
+
actionError = releaseError;
|
|
214
|
+
}
|
|
215
|
+
if (actionError !== undefined)
|
|
216
|
+
throw actionError;
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=withFilesystemLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withFilesystemLock.js","sourceRoot":"","sources":["../../src/utils/withFilesystemLock.ts"],"names":[],"mappings":"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"}
|