@dbx-tools/core 0.6.223 → 0.6.225

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 CHANGED
@@ -22,8 +22,8 @@ Key features:
22
22
  Databricks bundle's App `config.env`.
23
23
  - YAML/JSON brand-context discovery and loading with shared Zod validation.
24
24
  - Idempotent executable downloads with zip/tar extraction and atomic installs.
25
- - Cross-process file locks with a Bun `flock(2)` fast path and a portable,
26
- stale-reclaiming lock-directory fallback.
25
+ - Cross-process file locks with one portable, stale-reclaiming lock-directory
26
+ protocol shared by Bun and Node.
27
27
  - Keyed mutual exclusion across the main thread and its worker threads.
28
28
 
29
29
  ## Install A Binary
@@ -285,12 +285,16 @@ await fileLock.withFileLock(["cache", name], async () => {
285
285
  ```
286
286
 
287
287
  `withFileLock()` serializes processes on the same machine or shared filesystem.
288
- Under Bun on Unix it prefers a kernel `flock(2)` lock, which the OS releases if
289
- the process dies. Plain Node, Windows, and systems without the FFI path use
290
- atomic lock-directory creation with a heartbeat and stale-lock reclamation.
291
- Callers can select the lock directory, restrict the backend cascade, observe the
292
- chosen backend, or set a wait timeout. Lock keys use the same stable structured
293
- identity as process and Postgres advisory locks.
288
+ Bun and Node both use `proper-lockfile` atomic lock-directory creation,
289
+ heartbeats, stale-lock reclamation, and ownership-safe release. Callers can
290
+ select the lock directory, explicitly request the legacy Bun `flock` backend,
291
+ observe the chosen backend, or set a wait timeout. Lock keys use the same stable
292
+ structured identity as process and Postgres advisory locks.
293
+
294
+ Drain long-running processes that still use the legacy default before starting
295
+ an upgraded process against the same shared state. The explicit `flock` backend
296
+ remains available for compatibility, but it does not coordinate with the
297
+ portable directory protocol.
294
298
 
295
299
  Use `processLock.withProcessLock()` when only threads in one process compete,
296
300
  `fileLock.withFileLock()` when local processes compete, and Postgres advisory
@@ -328,8 +332,8 @@ cached too.
328
332
  - `config` - scoped environment, dotenv, and validated Databricks bundle lookup,
329
333
  including runtime detection and typed coercion helpers.
330
334
  - `file` - best-effort filesystem stat.
331
- - `fileLock` - cascading cross-process locks using `flock` or portable lock
332
- directories.
335
+ - `fileLock` - portable cross-process lock directories shared by Bun and Node,
336
+ with an explicit legacy `flock` option.
333
337
  - `processLock` - keyed mutual exclusion across the main thread and its workers,
334
338
  with worker wiring (`processLockWorkerOptions`, `attachProcessLock`,
335
339
  `processLockAttached`).
package/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/core";
6
- export const PACKAGE_VERSION = "0.6.223";
6
+ export const PACKAGE_VERSION = "0.6.225";
7
7
  export * as bin from "./src/bin.ts";
8
8
  export * as brand from "./src/brand.ts";
9
9
  export * as bundle from "./src/bundle.ts";
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export declare const PACKAGE_IDENTIFIER = "@dbx-tools/core";
2
- export declare const PACKAGE_VERSION = "0.6.223";
2
+ export declare const PACKAGE_VERSION = "0.6.225";
3
3
  export * as bin from "./src/bin.ts";
4
4
  export * as brand from "./src/brand.ts";
5
5
  export * as bundle from "./src/bundle.ts";
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // Regenerated from the exporting modules in ./src.
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
  export const PACKAGE_IDENTIFIER = "@dbx-tools/core";
5
- export const PACKAGE_VERSION = "0.6.223";
5
+ export const PACKAGE_VERSION = "0.6.225";
6
6
  export * as bin from "./src/bin.js";
7
7
  export * as brand from "./src/brand.js";
8
8
  export * as bundle from "./src/bundle.js";
@@ -15,4 +15,4 @@ export * as project from "./src/project.js";
15
15
  export { BrandContextSchema, defaultBrandContext, parseBrandContext, brandContextJsonSchema, brandContextPrompt } from "./src/brand.js";
16
16
  export { MAX_TCP_PORT, ENV_ONLY, valueSchema, bundleResourceSchema, bundleEnvEntrySchema, bundleAppSchema, appEnvEntrySchema, appSchema } from "./src/config.js";
17
17
  export { COMMAND_NOT_FOUND_EXIT_CODE } from "./src/exec.js";
18
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxpQkFBaUIsQ0FBQztBQUNwRCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxHQUFHLE1BQU0sY0FBYyxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxLQUFLLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxpQkFBaUIsQ0FBQztBQUMxQyxPQUFPLEtBQUssTUFBTSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sS0FBSyxJQUFJLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sS0FBSyxJQUFJLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sS0FBSyxRQUFRLE1BQU0sb0JBQW9CLENBQUM7QUFDL0MsT0FBTyxLQUFLLFdBQVcsTUFBTSx1QkFBdUIsQ0FBQztBQUNyRCxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxtQkFBbUIsRUFBRSxpQkFBaUIsRUFBRSxzQkFBc0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXhJLE9BQU8sRUFBRSxZQUFZLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxvQkFBb0IsRUFBRSxvQkFBb0IsRUFBRSxlQUFlLEVBQUUsaUJBQWlCLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFakssT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sZUFBZSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfSURFTlRJRklFUiA9IFwiQGRieC10b29scy9jb3JlXCI7XG5leHBvcnQgY29uc3QgUEFDS0FHRV9WRVJTSU9OID0gXCIwLjYuMjIzXCI7XG5leHBvcnQgKiBhcyBiaW4gZnJvbSBcIi4vc3JjL2Jpbi50c1wiO1xuZXhwb3J0ICogYXMgYnJhbmQgZnJvbSBcIi4vc3JjL2JyYW5kLnRzXCI7XG5leHBvcnQgKiBhcyBidW5kbGUgZnJvbSBcIi4vc3JjL2J1bmRsZS50c1wiO1xuZXhwb3J0ICogYXMgY29uZmlnIGZyb20gXCIuL3NyYy9jb25maWcudHNcIjtcbmV4cG9ydCAqIGFzIGV4ZWMgZnJvbSBcIi4vc3JjL2V4ZWMudHNcIjtcbmV4cG9ydCAqIGFzIGZpbGUgZnJvbSBcIi4vc3JjL2ZpbGUudHNcIjtcbmV4cG9ydCAqIGFzIGZpbGVMb2NrIGZyb20gXCIuL3NyYy9maWxlLWxvY2sudHNcIjtcbmV4cG9ydCAqIGFzIHByb2Nlc3NMb2NrIGZyb20gXCIuL3NyYy9wcm9jZXNzLWxvY2sudHNcIjtcbmV4cG9ydCAqIGFzIHByb2plY3QgZnJvbSBcIi4vc3JjL3Byb2plY3QudHNcIjtcbmV4cG9ydCB0eXBlIHsgQmluQ29udGV4dCwgQmluU2VsZWN0aW9uQ29udGV4dCwgQmluU2VsZWN0b3IsIEJpblZlcnNpb25PdXRwdXQsIEJpblZlcnNpb25QYXJzZXIsIEJpbk9wdGlvbnMsIEJpblVybCB9IGZyb20gXCIuL3NyYy9iaW4udHNcIjtcbmV4cG9ydCB7IEJyYW5kQ29udGV4dFNjaGVtYSwgZGVmYXVsdEJyYW5kQ29udGV4dCwgcGFyc2VCcmFuZENvbnRleHQsIGJyYW5kQ29udGV4dEpzb25TY2hlbWEsIGJyYW5kQ29udGV4dFByb21wdCB9IGZyb20gXCIuL3NyYy9icmFuZC50c1wiO1xuZXhwb3J0IHR5cGUgeyBCcmFuZENvbnRleHQsIEJyYW5kQ29udGV4dElucHV0IH0gZnJvbSBcIi4vc3JjL2JyYW5kLnRzXCI7XG5leHBvcnQgeyBNQVhfVENQX1BPUlQsIEVOVl9PTkxZLCB2YWx1ZVNjaGVtYSwgYnVuZGxlUmVzb3VyY2VTY2hlbWEsIGJ1bmRsZUVudkVudHJ5U2NoZW1hLCBidW5kbGVBcHBTY2hlbWEsIGFwcEVudkVudHJ5U2NoZW1hLCBhcHBTY2hlbWEgfSBmcm9tIFwiLi9zcmMvY29uZmlnLnRzXCI7XG5leHBvcnQgdHlwZSB7IENvbmZpZ01hcFZhbHVlLCBDb25maWdEYXRhLCBDb25maWdTb3VyY2UsIENvbmZpZ09wdGlvbnMsIENvbmZpZ0ZpbGUgfSBmcm9tIFwiLi9zcmMvY29uZmlnLnRzXCI7XG5leHBvcnQgeyBDT01NQU5EX05PVF9GT1VORF9FWElUX0NPREUgfSBmcm9tIFwiLi9zcmMvZXhlYy50c1wiO1xuZXhwb3J0IHR5cGUgeyBFeGVjU3RkaW8sIExpbmVIYW5kbGVyLCBTdGRpb09wdGlvbiwgRXhlY1Jlc3VsdCwgQ2hpbGRQcm9jZXNzUmVzdWx0LCBFeGVjT3B0aW9ucywgU3luY0V4ZWNTdGRpbywgU3luY0V4ZWNPcHRpb25zLCBTcGF3bkFyZ3MgfSBmcm9tIFwiLi9zcmMvZXhlYy50c1wiO1xuZXhwb3J0IHR5cGUgeyBGaWxlTG9ja0JhY2tlbmQsIEZpbGVMb2NrQWNxdWlzaXRpb24sIEZpbGVMb2NrT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9maWxlLWxvY2sudHNcIjtcbmV4cG9ydCB0eXBlIHsgUHJvamVjdENvbnRleHQgfSBmcm9tIFwiLi9zcmMvcHJvamVjdC50c1wiO1xuIl19
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxpQkFBaUIsQ0FBQztBQUNwRCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsU0FBUyxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxHQUFHLE1BQU0sY0FBYyxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxLQUFLLE1BQU0sZ0JBQWdCLENBQUM7QUFDeEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxpQkFBaUIsQ0FBQztBQUMxQyxPQUFPLEtBQUssTUFBTSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sS0FBSyxJQUFJLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sS0FBSyxJQUFJLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sS0FBSyxRQUFRLE1BQU0sb0JBQW9CLENBQUM7QUFDL0MsT0FBTyxLQUFLLFdBQVcsTUFBTSx1QkFBdUIsQ0FBQztBQUNyRCxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxtQkFBbUIsRUFBRSxpQkFBaUIsRUFBRSxzQkFBc0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXhJLE9BQU8sRUFBRSxZQUFZLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxvQkFBb0IsRUFBRSxvQkFBb0IsRUFBRSxlQUFlLEVBQUUsaUJBQWlCLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFakssT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sZUFBZSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gR0VORVJBVEVEIGJ5IHByb2plbiB3YXRjaCAtIERPIE5PVCBFRElULlxuLy8gUmVnZW5lcmF0ZWQgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZXMgaW4gLi9zcmMuXG4vLyBIYW5kIGVkaXRzIGFyZSBvdmVyd3JpdHRlbiBvbiB0aGUgbmV4dCB3YXRjaDsgdGhpcyBmaWxlIGlzIHJlYWQtb25seS5cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfSURFTlRJRklFUiA9IFwiQGRieC10b29scy9jb3JlXCI7XG5leHBvcnQgY29uc3QgUEFDS0FHRV9WRVJTSU9OID0gXCIwLjYuMjI1XCI7XG5leHBvcnQgKiBhcyBiaW4gZnJvbSBcIi4vc3JjL2Jpbi50c1wiO1xuZXhwb3J0ICogYXMgYnJhbmQgZnJvbSBcIi4vc3JjL2JyYW5kLnRzXCI7XG5leHBvcnQgKiBhcyBidW5kbGUgZnJvbSBcIi4vc3JjL2J1bmRsZS50c1wiO1xuZXhwb3J0ICogYXMgY29uZmlnIGZyb20gXCIuL3NyYy9jb25maWcudHNcIjtcbmV4cG9ydCAqIGFzIGV4ZWMgZnJvbSBcIi4vc3JjL2V4ZWMudHNcIjtcbmV4cG9ydCAqIGFzIGZpbGUgZnJvbSBcIi4vc3JjL2ZpbGUudHNcIjtcbmV4cG9ydCAqIGFzIGZpbGVMb2NrIGZyb20gXCIuL3NyYy9maWxlLWxvY2sudHNcIjtcbmV4cG9ydCAqIGFzIHByb2Nlc3NMb2NrIGZyb20gXCIuL3NyYy9wcm9jZXNzLWxvY2sudHNcIjtcbmV4cG9ydCAqIGFzIHByb2plY3QgZnJvbSBcIi4vc3JjL3Byb2plY3QudHNcIjtcbmV4cG9ydCB0eXBlIHsgQmluQ29udGV4dCwgQmluU2VsZWN0aW9uQ29udGV4dCwgQmluU2VsZWN0b3IsIEJpblZlcnNpb25PdXRwdXQsIEJpblZlcnNpb25QYXJzZXIsIEJpbk9wdGlvbnMsIEJpblVybCB9IGZyb20gXCIuL3NyYy9iaW4udHNcIjtcbmV4cG9ydCB7IEJyYW5kQ29udGV4dFNjaGVtYSwgZGVmYXVsdEJyYW5kQ29udGV4dCwgcGFyc2VCcmFuZENvbnRleHQsIGJyYW5kQ29udGV4dEpzb25TY2hlbWEsIGJyYW5kQ29udGV4dFByb21wdCB9IGZyb20gXCIuL3NyYy9icmFuZC50c1wiO1xuZXhwb3J0IHR5cGUgeyBCcmFuZENvbnRleHQsIEJyYW5kQ29udGV4dElucHV0IH0gZnJvbSBcIi4vc3JjL2JyYW5kLnRzXCI7XG5leHBvcnQgeyBNQVhfVENQX1BPUlQsIEVOVl9PTkxZLCB2YWx1ZVNjaGVtYSwgYnVuZGxlUmVzb3VyY2VTY2hlbWEsIGJ1bmRsZUVudkVudHJ5U2NoZW1hLCBidW5kbGVBcHBTY2hlbWEsIGFwcEVudkVudHJ5U2NoZW1hLCBhcHBTY2hlbWEgfSBmcm9tIFwiLi9zcmMvY29uZmlnLnRzXCI7XG5leHBvcnQgdHlwZSB7IENvbmZpZ01hcFZhbHVlLCBDb25maWdEYXRhLCBDb25maWdTb3VyY2UsIENvbmZpZ09wdGlvbnMsIENvbmZpZ0ZpbGUgfSBmcm9tIFwiLi9zcmMvY29uZmlnLnRzXCI7XG5leHBvcnQgeyBDT01NQU5EX05PVF9GT1VORF9FWElUX0NPREUgfSBmcm9tIFwiLi9zcmMvZXhlYy50c1wiO1xuZXhwb3J0IHR5cGUgeyBFeGVjU3RkaW8sIExpbmVIYW5kbGVyLCBTdGRpb09wdGlvbiwgRXhlY1Jlc3VsdCwgQ2hpbGRQcm9jZXNzUmVzdWx0LCBFeGVjT3B0aW9ucywgU3luY0V4ZWNTdGRpbywgU3luY0V4ZWNPcHRpb25zLCBTcGF3bkFyZ3MgfSBmcm9tIFwiLi9zcmMvZXhlYy50c1wiO1xuZXhwb3J0IHR5cGUgeyBGaWxlTG9ja0JhY2tlbmQsIEZpbGVMb2NrQWNxdWlzaXRpb24sIEZpbGVMb2NrT3B0aW9ucyB9IGZyb20gXCIuL3NyYy9maWxlLWxvY2sudHNcIjtcbmV4cG9ydCB0eXBlIHsgUHJvamVjdENvbnRleHQgfSBmcm9tIFwiLi9zcmMvcHJvamVjdC50c1wiO1xuIl19
@@ -5,16 +5,14 @@
5
5
  * server) on the same key. For in-process / worker-thread exclusion use
6
6
  * {@link withProcessLock} from `./process-lock.ts` instead.
7
7
  *
8
- * Backends, in order:
8
+ * Backends:
9
9
  *
10
- * 1. **flock** — `flock(2)` via Bun FFI on Unix when `bun:ffi` can load libc.
11
- * Not available on Windows, and not available under plain Node (no FFI).
12
- * Kernel releases the lock when the fd closes (including process death).
13
- * 2. **file** — atomic lock-directory creation. This is the strategy used by
14
- * `proper-lockfile`: `mkdir` is atomic on Windows, Unix, and network file
15
- * systems where `open(..., "wx")` may not be reliable. Stale detection is
16
- * always on (fixed {@link STALE_MS} + heartbeat) so a crashed holder on this
17
- * backend can be reclaimed.
10
+ * 1. **file** (default) — `proper-lockfile` atomic lock-directory creation,
11
+ * heartbeat, stale recovery, and ownership-safe release. Bun and Node use
12
+ * this same protocol.
13
+ * 2. **flock** (explicit compatibility option) — `flock(2)` via Bun FFI on
14
+ * Unix. It remains available to callers that selected it explicitly, but a
15
+ * runtime-dependent cascade cannot coordinate Bun and Node.
18
16
  *
19
17
  * The first backend that can be *initialized* is used for the whole call. A busy
20
18
  * lock waits; an unavailable backend falls through to the next. Callers may only
@@ -34,7 +32,7 @@ export interface FileLockOptions {
34
32
  * Directory for lockfiles. Defaults to `$TMPDIR/dbx-tools-locks`.
35
33
  */
36
34
  dir?: string;
37
- /** Override the cascade. Defaults to `flock` → `file`. */
35
+ /** Override the backend list. Defaults to the portable `file` protocol. */
38
36
  backends?: readonly FileLockBackend[];
39
37
  /**
40
38
  * Stop waiting and throw after this many milliseconds. Omit to poll forever.
@@ -5,16 +5,14 @@
5
5
  * server) on the same key. For in-process / worker-thread exclusion use
6
6
  * {@link withProcessLock} from `./process-lock.ts` instead.
7
7
  *
8
- * Backends, in order:
8
+ * Backends:
9
9
  *
10
- * 1. **flock** — `flock(2)` via Bun FFI on Unix when `bun:ffi` can load libc.
11
- * Not available on Windows, and not available under plain Node (no FFI).
12
- * Kernel releases the lock when the fd closes (including process death).
13
- * 2. **file** — atomic lock-directory creation. This is the strategy used by
14
- * `proper-lockfile`: `mkdir` is atomic on Windows, Unix, and network file
15
- * systems where `open(..., "wx")` may not be reliable. Stale detection is
16
- * always on (fixed {@link STALE_MS} + heartbeat) so a crashed holder on this
17
- * backend can be reclaimed.
10
+ * 1. **file** (default) — `proper-lockfile` atomic lock-directory creation,
11
+ * heartbeat, stale recovery, and ownership-safe release. Bun and Node use
12
+ * this same protocol.
13
+ * 2. **flock** (explicit compatibility option) — `flock(2)` via Bun FFI on
14
+ * Unix. It remains available to callers that selected it explicitly, but a
15
+ * runtime-dependent cascade cannot coordinate Bun and Node.
18
16
  *
19
17
  * The first backend that can be *initialized* is used for the whole call. A busy
20
18
  * lock waits; an unavailable backend falls through to the next. Callers may only
@@ -31,12 +29,13 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
31
29
  }
32
30
  return path;
33
31
  };
34
- import { mkdir, open, rm, stat, utimes } from "node:fs/promises";
32
+ import { mkdir, open } from "node:fs/promises";
35
33
  import { tmpdir } from "node:os";
36
34
  import { dirname, join } from "node:path";
37
35
  import { async, error, functionModule, hash, log, object } from "@dbx-tools/shared-core";
36
+ import lockfile from "proper-lockfile";
38
37
  const logger = log.logger("core:file-lock");
39
- const DEFAULT_BACKENDS = ["flock", "file"];
38
+ const DEFAULT_BACKENDS = ["file"];
40
39
  /** Poll interval while waiting for a contended OS lock. */
41
40
  const POLL_MS = 50;
42
41
  /**
@@ -84,7 +83,7 @@ export async function withFileLock(key, fn, options = {}) {
84
83
  return holdFlock(lockPath, flock, deadline, fn);
85
84
  }
86
85
  case "file": {
87
- const lockPath = join(dir, `${id}.lock`);
86
+ const lockPath = join(dir, id);
88
87
  logger.debug("acquiring lock", { backend, key: id, path: lockPath });
89
88
  options.onAcquire?.({ backend });
90
89
  return holdLockDirectory(lockPath, deadline, fn);
@@ -162,69 +161,37 @@ async function waitForFlock(fd, flock, lockPath, deadline) {
162
161
  }
163
162
  }
164
163
  /**
165
- * Atomic lock-directory creation — the portable / Windows path.
166
- *
167
- * `mkdir` is the same primitive used by `proper-lockfile`: it is atomic across
168
- * supported local and network filesystems. The holder refreshes mtime on a fixed
169
- * heartbeat so a live long-running lock is never reclaimed; a crashed holder's
170
- * directory becomes reclaimable after {@link STALE_MS}.
164
+ * Portable lock-directory ownership through `proper-lockfile`.
171
165
  */
172
166
  async function holdLockDirectory(lockPath, deadline, fn) {
173
167
  await ensureParentDir(lockPath);
174
- await acquireLockDirectory(lockPath, deadline);
175
- const stopHeartbeat = startHeartbeat(lockPath);
168
+ const release = await acquireLockDirectory(lockPath, deadline);
176
169
  try {
177
170
  return await fn();
178
171
  }
179
172
  finally {
180
- stopHeartbeat();
181
- await rm(lockPath, { recursive: true, force: true }).catch(() => { });
173
+ await release();
182
174
  }
183
175
  }
184
176
  async function acquireLockDirectory(lockPath, deadline) {
185
177
  for (;;) {
186
178
  try {
187
- await mkdir(lockPath);
188
- return;
179
+ return await lockfile.lock(lockPath, {
180
+ realpath: false,
181
+ stale: STALE_MS,
182
+ update: UPDATE_MS,
183
+ retries: 0,
184
+ });
189
185
  }
190
186
  catch (cause) {
191
187
  const err = cause;
192
- if (err.code !== "EEXIST")
188
+ if (err.code !== "ELOCKED")
193
189
  throw error.toError(cause);
194
- await maybeReclaimStale(lockPath);
195
190
  assertBeforeDeadline(lockPath, deadline);
196
191
  await async.sleep(POLL_MS);
197
192
  }
198
193
  }
199
194
  }
200
- async function maybeReclaimStale(lockPath) {
201
- try {
202
- const { mtimeMs } = await stat(lockPath);
203
- const age = Date.now() - mtimeMs;
204
- if (age < STALE_MS)
205
- return;
206
- logger.debug("reclaiming stale lock directory", { path: lockPath, ageMs: age });
207
- await rm(lockPath, { recursive: true, force: true });
208
- }
209
- catch (cause) {
210
- const err = cause;
211
- if (err.code !== "ENOENT")
212
- throw error.toError(cause);
213
- }
214
- }
215
- function startHeartbeat(lockPath) {
216
- const timer = setInterval(() => {
217
- const now = new Date();
218
- void utimes(lockPath, now, now).catch((cause) => {
219
- logger.warn("file lock heartbeat failed", {
220
- path: lockPath,
221
- error: error.errorMessage(cause),
222
- });
223
- });
224
- }, UPDATE_MS);
225
- timer.unref();
226
- return () => clearInterval(timer);
227
- }
228
195
  function assertBeforeDeadline(lockPath, deadline) {
229
196
  if (deadline !== undefined && Date.now() >= deadline) {
230
197
  throw new Error(`Timed out waiting for file lock: ${lockPath}`);
@@ -233,4 +200,4 @@ function assertBeforeDeadline(lockPath, deadline) {
233
200
  async function ensureParentDir(lockPath) {
234
201
  await mkdir(dirname(lockPath), { recursive: true });
235
202
  }
236
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS1sb2NrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ZpbGUtbG9jay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHOzs7Ozs7Ozs7QUFFSCxPQUFPLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDakMsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFFMUMsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsY0FBYyxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFekYsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0FBSzVDLE1BQU0sZ0JBQWdCLEdBQStCLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBRXZFLDJEQUEyRDtBQUMzRCxNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUM7QUFFbkI7Ozs7R0FJRztBQUNILE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLE1BQU0sQ0FBQztBQUUzRSwrRUFBK0U7QUFDL0UsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixDQUFDLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztBQUVuRix1REFBdUQ7QUFDdkQsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDO0FBQ2xCLE1BQU0sT0FBTyxHQUFHLENBQUMsQ0FBQztBQUNsQixNQUFNLE9BQU8sR0FBRyxDQUFDLENBQUM7QUFpQ2xC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b0JBQW9CLENBQUMsUUFBZ0IsRUFBRSxRQUE0QjtJQUMxRSxJQUFJLFFBQVEsS0FBSyxTQUFTLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQ3JELE1BQU0sSUFBSSxLQUFLLENBQUMsb0NBQW9DLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDbEUsQ0FBQztBQUNILENBQUM7QUFFRCxLQUFLLFVBQVUsZUFBZSxDQUFDLFFBQWdCO0lBQzdDLE1BQU0sS0FBSyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0FBQ3RELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENhc2NhZGluZyBjcm9zcy1wcm9jZXNzIG11dHVhbCBleGNsdXNpb24gdmlhIGEgbG9ja2ZpbGUuXG4gKlxuICogU2VyaWFsaXplcyBjb25jdXJyZW50ICpwcm9jZXNzZXMqICh0d28gYGJ1biBydW4gZGVtb2Agc2hlbGxzLCBhIENMSSBiZXNpZGUgYVxuICogc2VydmVyKSBvbiB0aGUgc2FtZSBrZXkuIEZvciBpbi1wcm9jZXNzIC8gd29ya2VyLXRocmVhZCBleGNsdXNpb24gdXNlXG4gKiB7QGxpbmsgd2l0aFByb2Nlc3NMb2NrfSBmcm9tIGAuL3Byb2Nlc3MtbG9jay50c2AgaW5zdGVhZC5cbiAqXG4gKiBCYWNrZW5kcywgaW4gb3JkZXI6XG4gKlxuICogMS4gKipmbG9jayoqIOKAlCBgZmxvY2soMilgIHZpYSBCdW4gRkZJIG9uIFVuaXggd2hlbiBgYnVuOmZmaWAgY2FuIGxvYWQgbGliYy5cbiAqICAgIE5vdCBhdmFpbGFibGUgb24gV2luZG93cywgYW5kIG5vdCBhdmFpbGFibGUgdW5kZXIgcGxhaW4gTm9kZSAobm8gRkZJKS5cbiAqICAgIEtlcm5lbCByZWxlYXNlcyB0aGUgbG9jayB3aGVuIHRoZSBmZCBjbG9zZXMgKGluY2x1ZGluZyBwcm9jZXNzIGRlYXRoKS5cbiAqIDIuICoqZmlsZSoqIOKAlCBhdG9taWMgbG9jay1kaXJlY3RvcnkgY3JlYXRpb24uIFRoaXMgaXMgdGhlIHN0cmF0ZWd5IHVzZWQgYnlcbiAqICAgIGBwcm9wZXItbG9ja2ZpbGVgOiBgbWtkaXJgIGlzIGF0b21pYyBvbiBXaW5kb3dzLCBVbml4LCBhbmQgbmV0d29yayBmaWxlXG4gKiAgICBzeXN0ZW1zIHdoZXJlIGBvcGVuKC4uLiwgXCJ3eFwiKWAgbWF5IG5vdCBiZSByZWxpYWJsZS4gU3RhbGUgZGV0ZWN0aW9uIGlzXG4gKiAgICBhbHdheXMgb24gKGZpeGVkIHtAbGluayBTVEFMRV9NU30gKyBoZWFydGJlYXQpIHNvIGEgY3Jhc2hlZCBob2xkZXIgb24gdGhpc1xuICogICAgYmFja2VuZCBjYW4gYmUgcmVjbGFpbWVkLlxuICpcbiAqIFRoZSBmaXJzdCBiYWNrZW5kIHRoYXQgY2FuIGJlICppbml0aWFsaXplZCogaXMgdXNlZCBmb3IgdGhlIHdob2xlIGNhbGwuIEEgYnVzeVxuICogbG9jayB3YWl0czsgYW4gdW5hdmFpbGFibGUgYmFja2VuZCBmYWxscyB0aHJvdWdoIHRvIHRoZSBuZXh0LiBDYWxsZXJzIG1heSBvbmx5XG4gKiBib3VuZCBob3cgbG9uZyB0byB3YWl0ICh7QGxpbmsgRmlsZUxvY2tPcHRpb25zLnRpbWVvdXRNc30pOyBzdGFsZSB0aW1pbmcgaXNcbiAqIG5vdCBjb25maWd1cmFibGUgc28gZXZlcnkgaG9sZGVyIGFuZCB3YWl0ZXIgYWdyZWVzLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBta2Rpciwgb3Blbiwgcm0sIHN0YXQsIHV0aW1lcyB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgeyB0bXBkaXIgfSBmcm9tIFwibm9kZTpvc1wiO1xuaW1wb3J0IHsgZGlybmFtZSwgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcblxuaW1wb3J0IHsgYXN5bmMsIGVycm9yLCBmdW5jdGlvbk1vZHVsZSwgaGFzaCwgbG9nLCBvYmplY3QgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwiY29yZTpmaWxlLWxvY2tcIik7XG5cbi8qKiBCYWNrZW5kcyB7QGxpbmsgd2l0aEZpbGVMb2NrfSBjYW4gYXR0ZW1wdCwgaW4gY2FzY2FkZSBvcmRlci4gKi9cbmV4cG9ydCB0eXBlIEZpbGVMb2NrQmFja2VuZCA9IFwiZmxvY2tcIiB8IFwiZmlsZVwiO1xuXG5jb25zdCBERUZBVUxUX0JBQ0tFTkRTOiByZWFkb25seSBGaWxlTG9ja0JhY2tlbmRbXSA9IFtcImZsb2NrXCIsIFwiZmlsZVwiXTtcblxuLyoqIFBvbGwgaW50ZXJ2YWwgd2hpbGUgd2FpdGluZyBmb3IgYSBjb250ZW5kZWQgT1MgbG9jay4gKi9cbmNvbnN0IFBPTExfTVMgPSA1MDtcblxuLyoqXG4gKiBBZ2UgYWZ0ZXIgd2hpY2ggYW4gdW5yZWZyZXNoZWQgbG9jayBkaXJlY3RvcnkgbWF5IGJlIHJlbW92ZWQuXG4gKiBNYXRjaGVzIGBwcm9wZXItbG9ja2ZpbGVgJ3MgZGVmYXVsdC4gTm90IGNhbGxlci1jb25maWd1cmFibGU6IGV2ZXJ5IHByb2Nlc3NcbiAqIG11c3Qgc2hhcmUgdGhlIHNhbWUgdGhyZXNob2xkIG9yIGEgbGl2ZSBob2xkZXIgY2FuIGJlIG1pc3Rha2VuIGZvciBzdGFsZS5cbiAqL1xuY29uc3QgU1RBTEVfTVMgPSBvYmplY3QudG9OdW1iZXIocHJvY2Vzcy5lbnYuRklMRV9MT0NLX1NUQUxFX01TKSA/PyAxMF8wMDA7XG5cbi8qKiBIZWFydGJlYXQgaW50ZXJ2YWwgZm9yIHJlZnJlc2hpbmcgbG9jay1kaXJlY3RvcnkgbXRpbWUgKGBTVEFMRV9NUyAvIDJgKS4gKi9cbmNvbnN0IFVQREFURV9NUyA9IG9iamVjdC50b051bWJlcihwcm9jZXNzLmVudi5GSUxFX0xPQ0tfVVBEQVRFX01TKSA/PyBTVEFMRV9NUyAvIDI7XG5cbi8qKiBgZmxvY2soMilgIG9wZXJhdGlvbiBiaXRzIChMaW51eCAvIG1hY09TIC8gQlNEKS4gKi9cbmNvbnN0IExPQ0tfRVggPSAyO1xuY29uc3QgTE9DS19OQiA9IDQ7XG5jb25zdCBMT0NLX1VOID0gODtcblxuLyoqIFJlc3VsdCBtZXRhZGF0YSB3aGVuIGEgY2FsbGVyIHdhbnRzIHRvIGtub3cgd2hpY2ggYmFja2VuZCByYW4uICovXG5leHBvcnQgaW50ZXJmYWNlIEZpbGVMb2NrQWNxdWlzaXRpb24ge1xuICBiYWNrZW5kOiBGaWxlTG9ja0JhY2tlbmQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRmlsZUxvY2tPcHRpb25zIHtcbiAgLyoqXG4gICAqIERpcmVjdG9yeSBmb3IgbG9ja2ZpbGVzLiBEZWZhdWx0cyB0byBgJFRNUERJUi9kYngtdG9vbHMtbG9ja3NgLlxuICAgKi9cbiAgZGlyPzogc3RyaW5nO1xuICAvKiogT3ZlcnJpZGUgdGhlIGNhc2NhZGUuIERlZmF1bHRzIHRvIGBmbG9ja2Ag4oaSIGBmaWxlYC4gKi9cbiAgYmFja2VuZHM/OiByZWFkb25seSBGaWxlTG9ja0JhY2tlbmRbXTtcbiAgLyoqXG4gICAqIFN0b3Agd2FpdGluZyBhbmQgdGhyb3cgYWZ0ZXIgdGhpcyBtYW55IG1pbGxpc2Vjb25kcy4gT21pdCB0byBwb2xsIGZvcmV2ZXIuXG4gICAqIFRoaXMgaXMgdGhlIG9ubHkgd2FpdC1sb29wIGtub2I7IHN0YWxlIHJlY2xhaW0gdGltaW5nIGlzIGZpeGVkLlxuICAgKi9cbiAgdGltZW91dE1zPzogbnVtYmVyO1xuICAvKiogSW52b2tlZCBvbmNlIHRoZSBiYWNrZW5kIGZvciB0aGlzIGNhbGwgaGFzIGJlZW4gY2hvc2VuLiAqL1xuICBvbkFjcXVpcmU/OiAoYWNxdWlzaXRpb246IEZpbGVMb2NrQWNxdWlzaXRpb24pID0+IHZvaWQ7XG59XG5cbnR5cGUgRmxvY2tGbiA9IChmZDogbnVtYmVyLCBvcGVyYXRpb246IG51bWJlcikgPT4gbnVtYmVyO1xuXG5pbnRlcmZhY2UgQnVuRmZpTW9kdWxlIHtcbiAgZGxvcGVuOiAoXG4gICAgcGF0aDogc3RyaW5nLFxuICAgIHN5bWJvbHM6IFJlY29yZDxzdHJpbmcsIHsgYXJnczogc3RyaW5nW107IHJldHVybnM6IHN0cmluZyB9PixcbiAgKSA9PiB7IHN5bWJvbHM6IHsgZmxvY2s6IEZsb2NrRm4gfSB9O1xuICBzdWZmaXg6IHN0cmluZztcbn1cblxuLyoqXG4gKiBSdW4gYGZuYCB3aGlsZSBob2xkaW5nIGEgY3Jvc3MtcHJvY2VzcyBsb2NrIG5hbWVkIGJ5IGBrZXlgLlxuICpcbiAqIGBrZXlgIGlzIGNhbm9uaWNhbGl6ZWQgd2l0aCBgb2JqZWN0LnRvU3RhYmxlS2V5YCwgdGhlIHNhbWUgaWRlbnRpdHkgcnVsZSBhc1xuICogcHJvY2VzcyBhbmQgUG9zdGdyZXMgYWR2aXNvcnkgbG9ja3MuXG4gKlxuICogQGV4YW1wbGVcbiAqIGF3YWl0IHdpdGhGaWxlTG9jayhbXCJjYWNoZVwiLCBcImFwcGtpdFwiXSwgYXN5bmMgKCkgPT4ge1xuICogICBhd2FpdCBtaWdyYXRlKCk7XG4gKiB9KTtcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdpdGhGaWxlTG9jazxUPihcbiAga2V5OiB1bmtub3duLFxuICBmbjogKCkgPT4gVCB8IFByb21pc2U8VD4sXG4gIG9wdGlvbnM6IEZpbGVMb2NrT3B0aW9ucyA9IHt9LFxuKTogUHJvbWlzZTxUPiB7XG4gIGlmIChvcHRpb25zLnRpbWVvdXRNcyAhPT0gdW5kZWZpbmVkICYmIG9wdGlvbnMudGltZW91dE1zIDwgMCkge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJ0aW1lb3V0TXMgbXVzdCBiZSBub24tbmVnYXRpdmVcIik7XG4gIH1cbiAgY29uc3QgaWQgPSBsb2NrSWQoa2V5KTtcbiAgY29uc3QgYmFja2VuZHMgPSBvcHRpb25zLmJhY2tlbmRzID8/IERFRkFVTFRfQkFDS0VORFM7XG4gIGNvbnN0IGRpciA9IG9wdGlvbnMuZGlyID8/IGpvaW4odG1wZGlyKCksIFwiZGJ4LXRvb2xzLWxvY2tzXCIpO1xuICBjb25zdCBkZWFkbGluZSA9IG9wdGlvbnMudGltZW91dE1zID09PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiBEYXRlLm5vdygpICsgb3B0aW9ucy50aW1lb3V0TXM7XG5cbiAgZm9yIChjb25zdCBiYWNrZW5kIG9mIGJhY2tlbmRzKSB7XG4gICAgc3dpdGNoIChiYWNrZW5kKSB7XG4gICAgICBjYXNlIFwiZmxvY2tcIjoge1xuICAgICAgICBjb25zdCBsb2NrUGF0aCA9IGpvaW4oZGlyLCBgJHtpZH0uZmxvY2tgKTtcbiAgICAgICAgY29uc3QgZmxvY2sgPSBhd2FpdCByZXNvbHZlRmxvY2soKTtcbiAgICAgICAgaWYgKCFmbG9jaykge1xuICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhcImxvY2sgYmFja2VuZCB1bmF2YWlsYWJsZVwiLCB7IGJhY2tlbmQsIGtleTogaWQgfSk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgbG9nZ2VyLmRlYnVnKFwiYWNxdWlyaW5nIGxvY2tcIiwgeyBiYWNrZW5kLCBrZXk6IGlkLCBwYXRoOiBsb2NrUGF0aCB9KTtcbiAgICAgICAgb3B0aW9ucy5vbkFjcXVpcmU/Lih7IGJhY2tlbmQgfSk7XG4gICAgICAgIHJldHVybiBob2xkRmxvY2sobG9ja1BhdGgsIGZsb2NrLCBkZWFkbGluZSwgZm4pO1xuICAgICAgfVxuICAgICAgY2FzZSBcImZpbGVcIjoge1xuICAgICAgICBjb25zdCBsb2NrUGF0aCA9IGpvaW4oZGlyLCBgJHtpZH0ubG9ja2ApO1xuICAgICAgICBsb2dnZXIuZGVidWcoXCJhY3F1aXJpbmcgbG9ja1wiLCB7IGJhY2tlbmQsIGtleTogaWQsIHBhdGg6IGxvY2tQYXRoIH0pO1xuICAgICAgICBvcHRpb25zLm9uQWNxdWlyZT8uKHsgYmFja2VuZCB9KTtcbiAgICAgICAgcmV0dXJuIGhvbGRMb2NrRGlyZWN0b3J5KGxvY2tQYXRoLCBkZWFkbGluZSwgZm4pO1xuICAgICAgfVxuICAgICAgZGVmYXVsdDoge1xuICAgICAgICBjb25zdCBfZXhoYXVzdGl2ZTogbmV2ZXIgPSBiYWNrZW5kO1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYHVua25vd24gZmlsZS1sb2NrIGJhY2tlbmQ6ICR7U3RyaW5nKF9leGhhdXN0aXZlKX1gKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoXCJ3aXRoRmlsZUxvY2s6IG5vIGxvY2sgYmFja2VuZCBhdmFpbGFibGVcIik7XG59XG5cbi8qKiBDYW5vbmljYWwgZmlsZXN5c3RlbS1zYWZlIGlkIGZvciBhIGxvY2sga2V5LiAqL1xuZnVuY3Rpb24gbG9ja0lkKGtleTogdW5rbm93bik6IHN0cmluZyB7XG4gIGNvbnN0IHN0YWJsZSA9IG9iamVjdFxuICAgIC50b09uZU9yTWFueShrZXkpXG4gICAgLm1hcCgocGFydCkgPT4gb2JqZWN0LnRvU3RhYmxlS2V5KHBhcnQpKVxuICAgIC5qb2luKFwiXFx1MDAwMFwiKTtcbiAgLy8gU2hvcnQgZGlnZXN0IHNvIHBhdGhzIHN0YXkgd2VsbCB1bmRlciBPUyBsaW1pdHM7IGNvbGxpc2lvbnMgYXJlIGZpbmUg4oCUIHRoZXlcbiAgLy8gb25seSBtZXJnZSBjcml0aWNhbCBzZWN0aW9ucyB0aGF0IGFscmVhZHkgc2hhcmVkIGEga2V5IHN0cmluZy5cbiAgcmV0dXJuIGhhc2guZm52SGFzaChzdGFibGUpO1xufVxuXG4vKipcbiAqIFJlc29sdmUgYGZsb2NrKDIpYCB0aHJvdWdoIEJ1biBGRkkgd2hlbiBwb3NzaWJsZS5cbiAqXG4gKiBNZW1vaXplZDogdGhlIEZGSSBpbXBvcnQgKyBsaWJjIGBkbG9wZW5gIHJ1biBvbmNlIHBlciBwcm9jZXNzLiBSZXR1cm5zXG4gKiBgdW5kZWZpbmVkYCBvbiBXaW5kb3dzLCB1bmRlciBwbGFpbiBOb2RlLCBvciB3aGVuIGxpYmMgY2Fubm90IGJlIGxvYWRlZCDigJRcbiAqIGNhbGxlcnMgZmFsbCB0aHJvdWdoIHRvIHRoZSBuZXh0IGJhY2tlbmQuIEEgbWlzcyBpcyBjYWNoZWQgdG9vLCBzbyBhIHByb2Nlc3NcbiAqIHRoYXQgY2Fubm90IGZsb2NrIG5ldmVyIHJldHJpZXMgdGhlIGxvYWQuXG4gKi9cbmNvbnN0IHJlc29sdmVGbG9jayA9IGZ1bmN0aW9uTW9kdWxlLm1lbW9pemUoYXN5bmMgKCk6IFByb21pc2U8RmxvY2tGbiB8IHVuZGVmaW5lZD4gPT4ge1xuICBpZiAocHJvY2Vzcy5wbGF0Zm9ybSA9PT0gXCJ3aW4zMlwiKSByZXR1cm4gdW5kZWZpbmVkO1xuICBpZiAoIXByb2Nlc3MudmVyc2lvbnMuYnVuKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gIHRyeSB7XG4gICAgLy8gRHluYW1pYyBzcGVjaWZpZXIgc28gYHRzY2AgKG5vIGBAdHlwZXNgIGZvciBgYnVuOmZmaWApIGRvZXMgbm90IHJlc29sdmUgaXQ7XG4gICAgLy8gdGhlIGltcG9ydCBvbmx5IHN1Y2NlZWRzIHVuZGVyIEJ1biBhdCBydW50aW1lLlxuICAgIGNvbnN0IHNwZWNpZmllciA9IFwiYnVuOmZmaVwiO1xuICAgIGNvbnN0IGZmaSA9IChhd2FpdCBpbXBvcnQoc3BlY2lmaWVyKSkgYXMgdW5rbm93biBhcyBCdW5GZmlNb2R1bGU7XG4gICAgY29uc3QgbGliUGF0aCA9IHByb2Nlc3MucGxhdGZvcm0gPT09IFwiZGFyd2luXCIgPyBcImxpYlN5c3RlbS5CLmR5bGliXCIgOiBgbGliYy4ke2ZmaS5zdWZmaXh9YDtcbiAgICBjb25zdCBsaWIgPSBmZmkuZGxvcGVuKGxpYlBhdGgsIHtcbiAgICAgIGZsb2NrOiB7IGFyZ3M6IFtcImkzMlwiLCBcImkzMlwiXSwgcmV0dXJuczogXCJpMzJcIiB9LFxuICAgIH0pO1xuICAgIHJldHVybiBsaWIuc3ltYm9scy5mbG9jaztcbiAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICBsb2dnZXIuZGVidWcoXCJmbG9jayBGRkkgdW5hdmFpbGFibGVcIiwgeyBlcnJvcjogZXJyb3IuZXJyb3JNZXNzYWdlKGNhdXNlKSB9KTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59KTtcblxuYXN5bmMgZnVuY3Rpb24gaG9sZEZsb2NrPFQ+KFxuICBsb2NrUGF0aDogc3RyaW5nLFxuICBmbG9jazogRmxvY2tGbixcbiAgZGVhZGxpbmU6IG51bWJlciB8IHVuZGVmaW5lZCxcbiAgZm46ICgpID0+IFQgfCBQcm9taXNlPFQ+LFxuKTogUHJvbWlzZTxUPiB7XG4gIGF3YWl0IGVuc3VyZVBhcmVudERpcihsb2NrUGF0aCk7XG4gIGNvbnN0IGhhbmRsZSA9IGF3YWl0IG9wZW4obG9ja1BhdGgsIFwiYStcIik7XG4gIHRyeSB7XG4gICAgYXdhaXQgd2FpdEZvckZsb2NrKGhhbmRsZS5mZCwgZmxvY2ssIGxvY2tQYXRoLCBkZWFkbGluZSk7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBmbigpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBmbG9jayhoYW5kbGUuZmQsIExPQ0tfVU4pO1xuICAgIH1cbiAgfSBmaW5hbGx5IHtcbiAgICBhd2FpdCBoYW5kbGUuY2xvc2UoKS5jYXRjaCgoKSA9PiB7fSk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gd2FpdEZvckZsb2NrKFxuICBmZDogbnVtYmVyLFxuICBmbG9jazogRmxvY2tGbixcbiAgbG9ja1BhdGg6IHN0cmluZyxcbiAgZGVhZGxpbmU6IG51bWJlciB8IHVuZGVmaW5lZCxcbik6IFByb21pc2U8dm9pZD4ge1xuICBmb3IgKDs7KSB7XG4gICAgY29uc3QgcmMgPSBmbG9jayhmZCwgTE9DS19FWCB8IExPQ0tfTkIpO1xuICAgIGlmIChyYyA9PT0gMCkgcmV0dXJuO1xuICAgIGFzc2VydEJlZm9yZURlYWRsaW5lKGxvY2tQYXRoLCBkZWFkbGluZSk7XG4gICAgYXdhaXQgYXN5bmMuc2xlZXAoUE9MTF9NUyk7XG4gIH1cbn1cblxuLyoqXG4gKiBBdG9taWMgbG9jay1kaXJlY3RvcnkgY3JlYXRpb24g4oCUIHRoZSBwb3J0YWJsZSAvIFdpbmRvd3MgcGF0aC5cbiAqXG4gKiBgbWtkaXJgIGlzIHRoZSBzYW1lIHByaW1pdGl2ZSB1c2VkIGJ5IGBwcm9wZXItbG9ja2ZpbGVgOiBpdCBpcyBhdG9taWMgYWNyb3NzXG4gKiBzdXBwb3J0ZWQgbG9jYWwgYW5kIG5ldHdvcmsgZmlsZXN5c3RlbXMuIFRoZSBob2xkZXIgcmVmcmVzaGVzIG10aW1lIG9uIGEgZml4ZWRcbiAqIGhlYXJ0YmVhdCBzbyBhIGxpdmUgbG9uZy1ydW5uaW5nIGxvY2sgaXMgbmV2ZXIgcmVjbGFpbWVkOyBhIGNyYXNoZWQgaG9sZGVyJ3NcbiAqIGRpcmVjdG9yeSBiZWNvbWVzIHJlY2xhaW1hYmxlIGFmdGVyIHtAbGluayBTVEFMRV9NU30uXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGhvbGRMb2NrRGlyZWN0b3J5PFQ+KFxuICBsb2NrUGF0aDogc3RyaW5nLFxuICBkZWFkbGluZTogbnVtYmVyIHwgdW5kZWZpbmVkLFxuICBmbjogKCkgPT4gVCB8IFByb21pc2U8VD4sXG4pOiBQcm9taXNlPFQ+IHtcbiAgYXdhaXQgZW5zdXJlUGFyZW50RGlyKGxvY2tQYXRoKTtcbiAgYXdhaXQgYWNxdWlyZUxvY2tEaXJlY3RvcnkobG9ja1BhdGgsIGRlYWRsaW5lKTtcbiAgY29uc3Qgc3RvcEhlYXJ0YmVhdCA9IHN0YXJ0SGVhcnRiZWF0KGxvY2tQYXRoKTtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgZm4oKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBzdG9wSGVhcnRiZWF0KCk7XG4gICAgYXdhaXQgcm0obG9ja1BhdGgsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KS5jYXRjaCgoKSA9PiB7fSk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gYWNxdWlyZUxvY2tEaXJlY3RvcnkobG9ja1BhdGg6IHN0cmluZywgZGVhZGxpbmU6IG51bWJlciB8IHVuZGVmaW5lZCk6IFByb21pc2U8dm9pZD4ge1xuICBmb3IgKDs7KSB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IG1rZGlyKGxvY2tQYXRoKTtcbiAgICAgIHJldHVybjtcbiAgICB9IGNhdGNoIChjYXVzZSkge1xuICAgICAgY29uc3QgZXJyID0gY2F1c2UgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uO1xuICAgICAgaWYgKGVyci5jb2RlICE9PSBcIkVFWElTVFwiKSB0aHJvdyBlcnJvci50b0Vycm9yKGNhdXNlKTtcbiAgICAgIGF3YWl0IG1heWJlUmVjbGFpbVN0YWxlKGxvY2tQYXRoKTtcbiAgICAgIGFzc2VydEJlZm9yZURlYWRsaW5lKGxvY2tQYXRoLCBkZWFkbGluZSk7XG4gICAgICBhd2FpdCBhc3luYy5zbGVlcChQT0xMX01TKTtcbiAgICB9XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gbWF5YmVSZWNsYWltU3RhbGUobG9ja1BhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IHsgbXRpbWVNcyB9ID0gYXdhaXQgc3RhdChsb2NrUGF0aCk7XG4gICAgY29uc3QgYWdlID0gRGF0ZS5ub3coKSAtIG10aW1lTXM7XG4gICAgaWYgKGFnZSA8IFNUQUxFX01TKSByZXR1cm47XG4gICAgbG9nZ2VyLmRlYnVnKFwicmVjbGFpbWluZyBzdGFsZSBsb2NrIGRpcmVjdG9yeVwiLCB7IHBhdGg6IGxvY2tQYXRoLCBhZ2VNczogYWdlIH0pO1xuICAgIGF3YWl0IHJtKGxvY2tQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IHRydWUgfSk7XG4gIH0gY2F0Y2ggKGNhdXNlKSB7XG4gICAgY29uc3QgZXJyID0gY2F1c2UgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uO1xuICAgIGlmIChlcnIuY29kZSAhPT0gXCJFTk9FTlRcIikgdGhyb3cgZXJyb3IudG9FcnJvcihjYXVzZSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gc3RhcnRIZWFydGJlYXQobG9ja1BhdGg6IHN0cmluZyk6ICgpID0+IHZvaWQge1xuICBjb25zdCB0aW1lciA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuICAgIHZvaWQgdXRpbWVzKGxvY2tQYXRoLCBub3csIG5vdykuY2F0Y2goKGNhdXNlKSA9PiB7XG4gICAgICBsb2dnZXIud2FybihcImZpbGUgbG9jayBoZWFydGJlYXQgZmFpbGVkXCIsIHtcbiAgICAgICAgcGF0aDogbG9ja1BhdGgsXG4gICAgICAgIGVycm9yOiBlcnJvci5lcnJvck1lc3NhZ2UoY2F1c2UpLFxuICAgICAgfSk7XG4gICAgfSk7XG4gIH0sIFVQREFURV9NUyk7XG4gIHRpbWVyLnVucmVmKCk7XG4gIHJldHVybiAoKSA9PiBjbGVhckludGVydmFsKHRpbWVyKTtcbn1cblxuZnVuY3Rpb24gYXNzZXJ0QmVmb3JlRGVhZGxpbmUobG9ja1BhdGg6IHN0cmluZywgZGVhZGxpbmU6IG51bWJlciB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICBpZiAoZGVhZGxpbmUgIT09IHVuZGVmaW5lZCAmJiBEYXRlLm5vdygpID49IGRlYWRsaW5lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBUaW1lZCBvdXQgd2FpdGluZyBmb3IgZmlsZSBsb2NrOiAke2xvY2tQYXRofWApO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGVuc3VyZVBhcmVudERpcihsb2NrUGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IG1rZGlyKGRpcm5hbWUobG9ja1BhdGgpLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbn1cbiJdfQ==
203
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS1sb2NrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ZpbGUtbG9jay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRzs7Ozs7Ozs7O0FBRUgsT0FBTyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMvQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ2pDLE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRTFDLE9BQU8sRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLGNBQWMsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3pGLE9BQU8sUUFBUSxNQUFNLGlCQUFpQixDQUFDO0FBRXZDLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztBQUs1QyxNQUFNLGdCQUFnQixHQUErQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0FBRTlELDJEQUEyRDtBQUMzRCxNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUM7QUFFbkI7Ozs7R0FJRztBQUNILE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLE1BQU0sQ0FBQztBQUUzRSwrRUFBK0U7QUFDL0UsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixDQUFDLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztBQUVuRix1REFBdUQ7QUFDdkQsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDO0FBQ2xCLE1BQU0sT0FBTyxHQUFHLENBQUMsQ0FBQztBQUNsQixNQUFNLE9BQU8sR0FBRyxDQUFDLENBQUM7QUFpQ2xCOzs7Ozs7Ozs7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QUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUN0RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDYXNjYWRpbmcgY3Jvc3MtcHJvY2VzcyBtdXR1YWwgZXhjbHVzaW9uIHZpYSBhIGxvY2tmaWxlLlxuICpcbiAqIFNlcmlhbGl6ZXMgY29uY3VycmVudCAqcHJvY2Vzc2VzKiAodHdvIGBidW4gcnVuIGRlbW9gIHNoZWxscywgYSBDTEkgYmVzaWRlIGFcbiAqIHNlcnZlcikgb24gdGhlIHNhbWUga2V5LiBGb3IgaW4tcHJvY2VzcyAvIHdvcmtlci10aHJlYWQgZXhjbHVzaW9uIHVzZVxuICoge0BsaW5rIHdpdGhQcm9jZXNzTG9ja30gZnJvbSBgLi9wcm9jZXNzLWxvY2sudHNgIGluc3RlYWQuXG4gKlxuICogQmFja2VuZHM6XG4gKlxuICogMS4gKipmaWxlKiogKGRlZmF1bHQpIOKAlCBgcHJvcGVyLWxvY2tmaWxlYCBhdG9taWMgbG9jay1kaXJlY3RvcnkgY3JlYXRpb24sXG4gKiAgICBoZWFydGJlYXQsIHN0YWxlIHJlY292ZXJ5LCBhbmQgb3duZXJzaGlwLXNhZmUgcmVsZWFzZS4gQnVuIGFuZCBOb2RlIHVzZVxuICogICAgdGhpcyBzYW1lIHByb3RvY29sLlxuICogMi4gKipmbG9jayoqIChleHBsaWNpdCBjb21wYXRpYmlsaXR5IG9wdGlvbikg4oCUIGBmbG9jaygyKWAgdmlhIEJ1biBGRkkgb25cbiAqICAgIFVuaXguIEl0IHJlbWFpbnMgYXZhaWxhYmxlIHRvIGNhbGxlcnMgdGhhdCBzZWxlY3RlZCBpdCBleHBsaWNpdGx5LCBidXQgYVxuICogICAgcnVudGltZS1kZXBlbmRlbnQgY2FzY2FkZSBjYW5ub3QgY29vcmRpbmF0ZSBCdW4gYW5kIE5vZGUuXG4gKlxuICogVGhlIGZpcnN0IGJhY2tlbmQgdGhhdCBjYW4gYmUgKmluaXRpYWxpemVkKiBpcyB1c2VkIGZvciB0aGUgd2hvbGUgY2FsbC4gQSBidXN5XG4gKiBsb2NrIHdhaXRzOyBhbiB1bmF2YWlsYWJsZSBiYWNrZW5kIGZhbGxzIHRocm91Z2ggdG8gdGhlIG5leHQuIENhbGxlcnMgbWF5IG9ubHlcbiAqIGJvdW5kIGhvdyBsb25nIHRvIHdhaXQgKHtAbGluayBGaWxlTG9ja09wdGlvbnMudGltZW91dE1zfSk7IHN0YWxlIHRpbWluZyBpc1xuICogbm90IGNvbmZpZ3VyYWJsZSBzbyBldmVyeSBob2xkZXIgYW5kIHdhaXRlciBhZ3JlZXMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IG1rZGlyLCBvcGVuIH0gZnJvbSBcIm5vZGU6ZnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IHRtcGRpciB9IGZyb20gXCJub2RlOm9zXCI7XG5pbXBvcnQgeyBkaXJuYW1lLCBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuXG5pbXBvcnQgeyBhc3luYywgZXJyb3IsIGZ1bmN0aW9uTW9kdWxlLCBoYXNoLCBsb2csIG9iamVjdCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgbG9ja2ZpbGUgZnJvbSBcInByb3Blci1sb2NrZmlsZVwiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwiY29yZTpmaWxlLWxvY2tcIik7XG5cbi8qKiBCYWNrZW5kcyB7QGxpbmsgd2l0aEZpbGVMb2NrfSBjYW4gYXR0ZW1wdCwgaW4gY2FzY2FkZSBvcmRlci4gKi9cbmV4cG9ydCB0eXBlIEZpbGVMb2NrQmFja2VuZCA9IFwiZmxvY2tcIiB8IFwiZmlsZVwiO1xuXG5jb25zdCBERUZBVUxUX0JBQ0tFTkRTOiByZWFkb25seSBGaWxlTG9ja0JhY2tlbmRbXSA9IFtcImZpbGVcIl07XG5cbi8qKiBQb2xsIGludGVydmFsIHdoaWxlIHdhaXRpbmcgZm9yIGEgY29udGVuZGVkIE9TIGxvY2suICovXG5jb25zdCBQT0xMX01TID0gNTA7XG5cbi8qKlxuICogQWdlIGFmdGVyIHdoaWNoIGFuIHVucmVmcmVzaGVkIGxvY2sgZGlyZWN0b3J5IG1heSBiZSByZW1vdmVkLlxuICogTWF0Y2hlcyBgcHJvcGVyLWxvY2tmaWxlYCdzIGRlZmF1bHQuIE5vdCBjYWxsZXItY29uZmlndXJhYmxlOiBldmVyeSBwcm9jZXNzXG4gKiBtdXN0IHNoYXJlIHRoZSBzYW1lIHRocmVzaG9sZCBvciBhIGxpdmUgaG9sZGVyIGNhbiBiZSBtaXN0YWtlbiBmb3Igc3RhbGUuXG4gKi9cbmNvbnN0IFNUQUxFX01TID0gb2JqZWN0LnRvTnVtYmVyKHByb2Nlc3MuZW52LkZJTEVfTE9DS19TVEFMRV9NUykgPz8gMTBfMDAwO1xuXG4vKiogSGVhcnRiZWF0IGludGVydmFsIGZvciByZWZyZXNoaW5nIGxvY2stZGlyZWN0b3J5IG10aW1lIChgU1RBTEVfTVMgLyAyYCkuICovXG5jb25zdCBVUERBVEVfTVMgPSBvYmplY3QudG9OdW1iZXIocHJvY2Vzcy5lbnYuRklMRV9MT0NLX1VQREFURV9NUykgPz8gU1RBTEVfTVMgLyAyO1xuXG4vKiogYGZsb2NrKDIpYCBvcGVyYXRpb24gYml0cyAoTGludXggLyBtYWNPUyAvIEJTRCkuICovXG5jb25zdCBMT0NLX0VYID0gMjtcbmNvbnN0IExPQ0tfTkIgPSA0O1xuY29uc3QgTE9DS19VTiA9IDg7XG5cbi8qKiBSZXN1bHQgbWV0YWRhdGEgd2hlbiBhIGNhbGxlciB3YW50cyB0byBrbm93IHdoaWNoIGJhY2tlbmQgcmFuLiAqL1xuZXhwb3J0IGludGVyZmFjZSBGaWxlTG9ja0FjcXVpc2l0aW9uIHtcbiAgYmFja2VuZDogRmlsZUxvY2tCYWNrZW5kO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEZpbGVMb2NrT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBEaXJlY3RvcnkgZm9yIGxvY2tmaWxlcy4gRGVmYXVsdHMgdG8gYCRUTVBESVIvZGJ4LXRvb2xzLWxvY2tzYC5cbiAgICovXG4gIGRpcj86IHN0cmluZztcbiAgLyoqIE92ZXJyaWRlIHRoZSBiYWNrZW5kIGxpc3QuIERlZmF1bHRzIHRvIHRoZSBwb3J0YWJsZSBgZmlsZWAgcHJvdG9jb2wuICovXG4gIGJhY2tlbmRzPzogcmVhZG9ubHkgRmlsZUxvY2tCYWNrZW5kW107XG4gIC8qKlxuICAgKiBTdG9wIHdhaXRpbmcgYW5kIHRocm93IGFmdGVyIHRoaXMgbWFueSBtaWxsaXNlY29uZHMuIE9taXQgdG8gcG9sbCBmb3JldmVyLlxuICAgKiBUaGlzIGlzIHRoZSBvbmx5IHdhaXQtbG9vcCBrbm9iOyBzdGFsZSByZWNsYWltIHRpbWluZyBpcyBmaXhlZC5cbiAgICovXG4gIHRpbWVvdXRNcz86IG51bWJlcjtcbiAgLyoqIEludm9rZWQgb25jZSB0aGUgYmFja2VuZCBmb3IgdGhpcyBjYWxsIGhhcyBiZWVuIGNob3Nlbi4gKi9cbiAgb25BY3F1aXJlPzogKGFjcXVpc2l0aW9uOiBGaWxlTG9ja0FjcXVpc2l0aW9uKSA9PiB2b2lkO1xufVxuXG50eXBlIEZsb2NrRm4gPSAoZmQ6IG51bWJlciwgb3BlcmF0aW9uOiBudW1iZXIpID0+IG51bWJlcjtcblxuaW50ZXJmYWNlIEJ1bkZmaU1vZHVsZSB7XG4gIGRsb3BlbjogKFxuICAgIHBhdGg6IHN0cmluZyxcbiAgICBzeW1ib2xzOiBSZWNvcmQ8c3RyaW5nLCB7IGFyZ3M6IHN0cmluZ1tdOyByZXR1cm5zOiBzdHJpbmcgfT4sXG4gICkgPT4geyBzeW1ib2xzOiB7IGZsb2NrOiBGbG9ja0ZuIH0gfTtcbiAgc3VmZml4OiBzdHJpbmc7XG59XG5cbi8qKlxuICogUnVuIGBmbmAgd2hpbGUgaG9sZGluZyBhIGNyb3NzLXByb2Nlc3MgbG9jayBuYW1lZCBieSBga2V5YC5cbiAqXG4gKiBga2V5YCBpcyBjYW5vbmljYWxpemVkIHdpdGggYG9iamVjdC50b1N0YWJsZUtleWAsIHRoZSBzYW1lIGlkZW50aXR5IHJ1bGUgYXNcbiAqIHByb2Nlc3MgYW5kIFBvc3RncmVzIGFkdmlzb3J5IGxvY2tzLlxuICpcbiAqIEBleGFtcGxlXG4gKiBhd2FpdCB3aXRoRmlsZUxvY2soW1wiY2FjaGVcIiwgXCJhcHBraXRcIl0sIGFzeW5jICgpID0+IHtcbiAqICAgYXdhaXQgbWlncmF0ZSgpO1xuICogfSk7XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB3aXRoRmlsZUxvY2s8VD4oXG4gIGtleTogdW5rbm93bixcbiAgZm46ICgpID0+IFQgfCBQcm9taXNlPFQ+LFxuICBvcHRpb25zOiBGaWxlTG9ja09wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8VD4ge1xuICBpZiAob3B0aW9ucy50aW1lb3V0TXMgIT09IHVuZGVmaW5lZCAmJiBvcHRpb25zLnRpbWVvdXRNcyA8IDApIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwidGltZW91dE1zIG11c3QgYmUgbm9uLW5lZ2F0aXZlXCIpO1xuICB9XG4gIGNvbnN0IGlkID0gbG9ja0lkKGtleSk7XG4gIGNvbnN0IGJhY2tlbmRzID0gb3B0aW9ucy5iYWNrZW5kcyA/PyBERUZBVUxUX0JBQ0tFTkRTO1xuICBjb25zdCBkaXIgPSBvcHRpb25zLmRpciA/PyBqb2luKHRtcGRpcigpLCBcImRieC10b29scy1sb2Nrc1wiKTtcbiAgY29uc3QgZGVhZGxpbmUgPSBvcHRpb25zLnRpbWVvdXRNcyA9PT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogRGF0ZS5ub3coKSArIG9wdGlvbnMudGltZW91dE1zO1xuXG4gIGZvciAoY29uc3QgYmFja2VuZCBvZiBiYWNrZW5kcykge1xuICAgIHN3aXRjaCAoYmFja2VuZCkge1xuICAgICAgY2FzZSBcImZsb2NrXCI6IHtcbiAgICAgICAgY29uc3QgbG9ja1BhdGggPSBqb2luKGRpciwgYCR7aWR9LmZsb2NrYCk7XG4gICAgICAgIGNvbnN0IGZsb2NrID0gYXdhaXQgcmVzb2x2ZUZsb2NrKCk7XG4gICAgICAgIGlmICghZmxvY2spIHtcbiAgICAgICAgICBsb2dnZXIuZGVidWcoXCJsb2NrIGJhY2tlbmQgdW5hdmFpbGFibGVcIiwgeyBiYWNrZW5kLCBrZXk6IGlkIH0pO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhcImFjcXVpcmluZyBsb2NrXCIsIHsgYmFja2VuZCwga2V5OiBpZCwgcGF0aDogbG9ja1BhdGggfSk7XG4gICAgICAgIG9wdGlvbnMub25BY3F1aXJlPy4oeyBiYWNrZW5kIH0pO1xuICAgICAgICByZXR1cm4gaG9sZEZsb2NrKGxvY2tQYXRoLCBmbG9jaywgZGVhZGxpbmUsIGZuKTtcbiAgICAgIH1cbiAgICAgIGNhc2UgXCJmaWxlXCI6IHtcbiAgICAgICAgY29uc3QgbG9ja1BhdGggPSBqb2luKGRpciwgaWQpO1xuICAgICAgICBsb2dnZXIuZGVidWcoXCJhY3F1aXJpbmcgbG9ja1wiLCB7IGJhY2tlbmQsIGtleTogaWQsIHBhdGg6IGxvY2tQYXRoIH0pO1xuICAgICAgICBvcHRpb25zLm9uQWNxdWlyZT8uKHsgYmFja2VuZCB9KTtcbiAgICAgICAgcmV0dXJuIGhvbGRMb2NrRGlyZWN0b3J5KGxvY2tQYXRoLCBkZWFkbGluZSwgZm4pO1xuICAgICAgfVxuICAgICAgZGVmYXVsdDoge1xuICAgICAgICBjb25zdCBfZXhoYXVzdGl2ZTogbmV2ZXIgPSBiYWNrZW5kO1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYHVua25vd24gZmlsZS1sb2NrIGJhY2tlbmQ6ICR7U3RyaW5nKF9leGhhdXN0aXZlKX1gKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoXCJ3aXRoRmlsZUxvY2s6IG5vIGxvY2sgYmFja2VuZCBhdmFpbGFibGVcIik7XG59XG5cbi8qKiBDYW5vbmljYWwgZmlsZXN5c3RlbS1zYWZlIGlkIGZvciBhIGxvY2sga2V5LiAqL1xuZnVuY3Rpb24gbG9ja0lkKGtleTogdW5rbm93bik6IHN0cmluZyB7XG4gIGNvbnN0IHN0YWJsZSA9IG9iamVjdFxuICAgIC50b09uZU9yTWFueShrZXkpXG4gICAgLm1hcCgocGFydCkgPT4gb2JqZWN0LnRvU3RhYmxlS2V5KHBhcnQpKVxuICAgIC5qb2luKFwiXFx1MDAwMFwiKTtcbiAgLy8gU2hvcnQgZGlnZXN0IHNvIHBhdGhzIHN0YXkgd2VsbCB1bmRlciBPUyBsaW1pdHM7IGNvbGxpc2lvbnMgYXJlIGZpbmUg4oCUIHRoZXlcbiAgLy8gb25seSBtZXJnZSBjcml0aWNhbCBzZWN0aW9ucyB0aGF0IGFscmVhZHkgc2hhcmVkIGEga2V5IHN0cmluZy5cbiAgcmV0dXJuIGhhc2guZm52SGFzaChzdGFibGUpO1xufVxuXG4vKipcbiAqIFJlc29sdmUgYGZsb2NrKDIpYCB0aHJvdWdoIEJ1biBGRkkgd2hlbiBwb3NzaWJsZS5cbiAqXG4gKiBNZW1vaXplZDogdGhlIEZGSSBpbXBvcnQgKyBsaWJjIGBkbG9wZW5gIHJ1biBvbmNlIHBlciBwcm9jZXNzLiBSZXR1cm5zXG4gKiBgdW5kZWZpbmVkYCBvbiBXaW5kb3dzLCB1bmRlciBwbGFpbiBOb2RlLCBvciB3aGVuIGxpYmMgY2Fubm90IGJlIGxvYWRlZCDigJRcbiAqIGNhbGxlcnMgZmFsbCB0aHJvdWdoIHRvIHRoZSBuZXh0IGJhY2tlbmQuIEEgbWlzcyBpcyBjYWNoZWQgdG9vLCBzbyBhIHByb2Nlc3NcbiAqIHRoYXQgY2Fubm90IGZsb2NrIG5ldmVyIHJldHJpZXMgdGhlIGxvYWQuXG4gKi9cbmNvbnN0IHJlc29sdmVGbG9jayA9IGZ1bmN0aW9uTW9kdWxlLm1lbW9pemUoYXN5bmMgKCk6IFByb21pc2U8RmxvY2tGbiB8IHVuZGVmaW5lZD4gPT4ge1xuICBpZiAocHJvY2Vzcy5wbGF0Zm9ybSA9PT0gXCJ3aW4zMlwiKSByZXR1cm4gdW5kZWZpbmVkO1xuICBpZiAoIXByb2Nlc3MudmVyc2lvbnMuYnVuKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gIHRyeSB7XG4gICAgLy8gRHluYW1pYyBzcGVjaWZpZXIgc28gYHRzY2AgKG5vIGBAdHlwZXNgIGZvciBgYnVuOmZmaWApIGRvZXMgbm90IHJlc29sdmUgaXQ7XG4gICAgLy8gdGhlIGltcG9ydCBvbmx5IHN1Y2NlZWRzIHVuZGVyIEJ1biBhdCBydW50aW1lLlxuICAgIGNvbnN0IHNwZWNpZmllciA9IFwiYnVuOmZmaVwiO1xuICAgIGNvbnN0IGZmaSA9IChhd2FpdCBpbXBvcnQoc3BlY2lmaWVyKSkgYXMgdW5rbm93biBhcyBCdW5GZmlNb2R1bGU7XG4gICAgY29uc3QgbGliUGF0aCA9IHByb2Nlc3MucGxhdGZvcm0gPT09IFwiZGFyd2luXCIgPyBcImxpYlN5c3RlbS5CLmR5bGliXCIgOiBgbGliYy4ke2ZmaS5zdWZmaXh9YDtcbiAgICBjb25zdCBsaWIgPSBmZmkuZGxvcGVuKGxpYlBhdGgsIHtcbiAgICAgIGZsb2NrOiB7IGFyZ3M6IFtcImkzMlwiLCBcImkzMlwiXSwgcmV0dXJuczogXCJpMzJcIiB9LFxuICAgIH0pO1xuICAgIHJldHVybiBsaWIuc3ltYm9scy5mbG9jaztcbiAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICBsb2dnZXIuZGVidWcoXCJmbG9jayBGRkkgdW5hdmFpbGFibGVcIiwgeyBlcnJvcjogZXJyb3IuZXJyb3JNZXNzYWdlKGNhdXNlKSB9KTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59KTtcblxuYXN5bmMgZnVuY3Rpb24gaG9sZEZsb2NrPFQ+KFxuICBsb2NrUGF0aDogc3RyaW5nLFxuICBmbG9jazogRmxvY2tGbixcbiAgZGVhZGxpbmU6IG51bWJlciB8IHVuZGVmaW5lZCxcbiAgZm46ICgpID0+IFQgfCBQcm9taXNlPFQ+LFxuKTogUHJvbWlzZTxUPiB7XG4gIGF3YWl0IGVuc3VyZVBhcmVudERpcihsb2NrUGF0aCk7XG4gIGNvbnN0IGhhbmRsZSA9IGF3YWl0IG9wZW4obG9ja1BhdGgsIFwiYStcIik7XG4gIHRyeSB7XG4gICAgYXdhaXQgd2FpdEZvckZsb2NrKGhhbmRsZS5mZCwgZmxvY2ssIGxvY2tQYXRoLCBkZWFkbGluZSk7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCBmbigpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBmbG9jayhoYW5kbGUuZmQsIExPQ0tfVU4pO1xuICAgIH1cbiAgfSBmaW5hbGx5IHtcbiAgICBhd2FpdCBoYW5kbGUuY2xvc2UoKS5jYXRjaCgoKSA9PiB7fSk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gd2FpdEZvckZsb2NrKFxuICBmZDogbnVtYmVyLFxuICBmbG9jazogRmxvY2tGbixcbiAgbG9ja1BhdGg6IHN0cmluZyxcbiAgZGVhZGxpbmU6IG51bWJlciB8IHVuZGVmaW5lZCxcbik6IFByb21pc2U8dm9pZD4ge1xuICBmb3IgKDs7KSB7XG4gICAgY29uc3QgcmMgPSBmbG9jayhmZCwgTE9DS19FWCB8IExPQ0tfTkIpO1xuICAgIGlmIChyYyA9PT0gMCkgcmV0dXJuO1xuICAgIGFzc2VydEJlZm9yZURlYWRsaW5lKGxvY2tQYXRoLCBkZWFkbGluZSk7XG4gICAgYXdhaXQgYXN5bmMuc2xlZXAoUE9MTF9NUyk7XG4gIH1cbn1cblxuLyoqXG4gKiBQb3J0YWJsZSBsb2NrLWRpcmVjdG9yeSBvd25lcnNoaXAgdGhyb3VnaCBgcHJvcGVyLWxvY2tmaWxlYC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gaG9sZExvY2tEaXJlY3Rvcnk8VD4oXG4gIGxvY2tQYXRoOiBzdHJpbmcsXG4gIGRlYWRsaW5lOiBudW1iZXIgfCB1bmRlZmluZWQsXG4gIGZuOiAoKSA9PiBUIHwgUHJvbWlzZTxUPixcbik6IFByb21pc2U8VD4ge1xuICBhd2FpdCBlbnN1cmVQYXJlbnREaXIobG9ja1BhdGgpO1xuICBjb25zdCByZWxlYXNlID0gYXdhaXQgYWNxdWlyZUxvY2tEaXJlY3RvcnkobG9ja1BhdGgsIGRlYWRsaW5lKTtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgZm4oKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBhd2FpdCByZWxlYXNlKCk7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gYWNxdWlyZUxvY2tEaXJlY3RvcnkoXG4gIGxvY2tQYXRoOiBzdHJpbmcsXG4gIGRlYWRsaW5lOiBudW1iZXIgfCB1bmRlZmluZWQsXG4pOiBQcm9taXNlPCgpID0+IFByb21pc2U8dm9pZD4+IHtcbiAgZm9yICg7Oykge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgbG9ja2ZpbGUubG9jayhsb2NrUGF0aCwge1xuICAgICAgICByZWFscGF0aDogZmFsc2UsXG4gICAgICAgIHN0YWxlOiBTVEFMRV9NUyxcbiAgICAgICAgdXBkYXRlOiBVUERBVEVfTVMsXG4gICAgICAgIHJldHJpZXM6IDAsXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChjYXVzZSkge1xuICAgICAgY29uc3QgZXJyID0gY2F1c2UgYXMgTm9kZUpTLkVycm5vRXhjZXB0aW9uO1xuICAgICAgaWYgKGVyci5jb2RlICE9PSBcIkVMT0NLRURcIikgdGhyb3cgZXJyb3IudG9FcnJvcihjYXVzZSk7XG4gICAgICBhc3NlcnRCZWZvcmVEZWFkbGluZShsb2NrUGF0aCwgZGVhZGxpbmUpO1xuICAgICAgYXdhaXQgYXN5bmMuc2xlZXAoUE9MTF9NUyk7XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIGFzc2VydEJlZm9yZURlYWRsaW5lKGxvY2tQYXRoOiBzdHJpbmcsIGRlYWRsaW5lOiBudW1iZXIgfCB1bmRlZmluZWQpOiB2b2lkIHtcbiAgaWYgKGRlYWRsaW5lICE9PSB1bmRlZmluZWQgJiYgRGF0ZS5ub3coKSA+PSBkZWFkbGluZSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgVGltZWQgb3V0IHdhaXRpbmcgZm9yIGZpbGUgbG9jazogJHtsb2NrUGF0aH1gKTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBlbnN1cmVQYXJlbnREaXIobG9ja1BhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBta2RpcihkaXJuYW1lKGxvY2tQYXRoKSwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG59XG4iXX0=
package/package.json CHANGED
@@ -21,11 +21,13 @@
21
21
  "devDependencies": {
22
22
  "@types/bun": "1.3.14",
23
23
  "@types/node": "^24.6.0",
24
+ "@types/proper-lockfile": "^4.1.4",
24
25
  "typescript": "^5.9.3"
25
26
  },
26
27
  "dependencies": {
27
- "@dbx-tools/shared-core": "0.6.223",
28
+ "@dbx-tools/shared-core": "0.6.225",
28
29
  "extract-zip": "^2.0.1",
30
+ "proper-lockfile": "^4.1.2",
29
31
  "tar": "^7.5.22",
30
32
  "yaml": "^2.9.0",
31
33
  "zod": "4.3.6"
@@ -44,7 +46,7 @@
44
46
  "./package.json": "./package.json"
45
47
  }
46
48
  },
47
- "version": "0.6.223",
49
+ "version": "0.6.225",
48
50
  "types": "./lib/index.d.ts",
49
51
  "type": "module",
50
52
  "exports": {
package/src/file-lock.ts CHANGED
@@ -5,16 +5,14 @@
5
5
  * server) on the same key. For in-process / worker-thread exclusion use
6
6
  * {@link withProcessLock} from `./process-lock.ts` instead.
7
7
  *
8
- * Backends, in order:
8
+ * Backends:
9
9
  *
10
- * 1. **flock** — `flock(2)` via Bun FFI on Unix when `bun:ffi` can load libc.
11
- * Not available on Windows, and not available under plain Node (no FFI).
12
- * Kernel releases the lock when the fd closes (including process death).
13
- * 2. **file** — atomic lock-directory creation. This is the strategy used by
14
- * `proper-lockfile`: `mkdir` is atomic on Windows, Unix, and network file
15
- * systems where `open(..., "wx")` may not be reliable. Stale detection is
16
- * always on (fixed {@link STALE_MS} + heartbeat) so a crashed holder on this
17
- * backend can be reclaimed.
10
+ * 1. **file** (default) — `proper-lockfile` atomic lock-directory creation,
11
+ * heartbeat, stale recovery, and ownership-safe release. Bun and Node use
12
+ * this same protocol.
13
+ * 2. **flock** (explicit compatibility option) — `flock(2)` via Bun FFI on
14
+ * Unix. It remains available to callers that selected it explicitly, but a
15
+ * runtime-dependent cascade cannot coordinate Bun and Node.
18
16
  *
19
17
  * The first backend that can be *initialized* is used for the whole call. A busy
20
18
  * lock waits; an unavailable backend falls through to the next. Callers may only
@@ -24,18 +22,19 @@
24
22
  * @module
25
23
  */
26
24
 
27
- import { mkdir, open, rm, stat, utimes } from "node:fs/promises";
25
+ import { mkdir, open } from "node:fs/promises";
28
26
  import { tmpdir } from "node:os";
29
27
  import { dirname, join } from "node:path";
30
28
 
31
29
  import { async, error, functionModule, hash, log, object } from "@dbx-tools/shared-core";
30
+ import lockfile from "proper-lockfile";
32
31
 
33
32
  const logger = log.logger("core:file-lock");
34
33
 
35
34
  /** Backends {@link withFileLock} can attempt, in cascade order. */
36
35
  export type FileLockBackend = "flock" | "file";
37
36
 
38
- const DEFAULT_BACKENDS: readonly FileLockBackend[] = ["flock", "file"];
37
+ const DEFAULT_BACKENDS: readonly FileLockBackend[] = ["file"];
39
38
 
40
39
  /** Poll interval while waiting for a contended OS lock. */
41
40
  const POLL_MS = 50;
@@ -65,7 +64,7 @@ export interface FileLockOptions {
65
64
  * Directory for lockfiles. Defaults to `$TMPDIR/dbx-tools-locks`.
66
65
  */
67
66
  dir?: string;
68
- /** Override the cascade. Defaults to `flock` → `file`. */
67
+ /** Override the backend list. Defaults to the portable `file` protocol. */
69
68
  backends?: readonly FileLockBackend[];
70
69
  /**
71
70
  * Stop waiting and throw after this many milliseconds. Omit to poll forever.
@@ -124,7 +123,7 @@ export async function withFileLock<T>(
124
123
  return holdFlock(lockPath, flock, deadline, fn);
125
124
  }
126
125
  case "file": {
127
- const lockPath = join(dir, `${id}.lock`);
126
+ const lockPath = join(dir, id);
128
127
  logger.debug("acquiring lock", { backend, key: id, path: lockPath });
129
128
  options.onAcquire?.({ backend });
130
129
  return holdLockDirectory(lockPath, deadline, fn);
@@ -213,12 +212,7 @@ async function waitForFlock(
213
212
  }
214
213
 
215
214
  /**
216
- * Atomic lock-directory creation — the portable / Windows path.
217
- *
218
- * `mkdir` is the same primitive used by `proper-lockfile`: it is atomic across
219
- * supported local and network filesystems. The holder refreshes mtime on a fixed
220
- * heartbeat so a live long-running lock is never reclaimed; a crashed holder's
221
- * directory becomes reclaimable after {@link STALE_MS}.
215
+ * Portable lock-directory ownership through `proper-lockfile`.
222
216
  */
223
217
  async function holdLockDirectory<T>(
224
218
  lockPath: string,
@@ -226,58 +220,35 @@ async function holdLockDirectory<T>(
226
220
  fn: () => T | Promise<T>,
227
221
  ): Promise<T> {
228
222
  await ensureParentDir(lockPath);
229
- await acquireLockDirectory(lockPath, deadline);
230
- const stopHeartbeat = startHeartbeat(lockPath);
223
+ const release = await acquireLockDirectory(lockPath, deadline);
231
224
  try {
232
225
  return await fn();
233
226
  } finally {
234
- stopHeartbeat();
235
- await rm(lockPath, { recursive: true, force: true }).catch(() => {});
227
+ await release();
236
228
  }
237
229
  }
238
230
 
239
- async function acquireLockDirectory(lockPath: string, deadline: number | undefined): Promise<void> {
231
+ async function acquireLockDirectory(
232
+ lockPath: string,
233
+ deadline: number | undefined,
234
+ ): Promise<() => Promise<void>> {
240
235
  for (;;) {
241
236
  try {
242
- await mkdir(lockPath);
243
- return;
237
+ return await lockfile.lock(lockPath, {
238
+ realpath: false,
239
+ stale: STALE_MS,
240
+ update: UPDATE_MS,
241
+ retries: 0,
242
+ });
244
243
  } catch (cause) {
245
244
  const err = cause as NodeJS.ErrnoException;
246
- if (err.code !== "EEXIST") throw error.toError(cause);
247
- await maybeReclaimStale(lockPath);
245
+ if (err.code !== "ELOCKED") throw error.toError(cause);
248
246
  assertBeforeDeadline(lockPath, deadline);
249
247
  await async.sleep(POLL_MS);
250
248
  }
251
249
  }
252
250
  }
253
251
 
254
- async function maybeReclaimStale(lockPath: string): Promise<void> {
255
- try {
256
- const { mtimeMs } = await stat(lockPath);
257
- const age = Date.now() - mtimeMs;
258
- if (age < STALE_MS) return;
259
- logger.debug("reclaiming stale lock directory", { path: lockPath, ageMs: age });
260
- await rm(lockPath, { recursive: true, force: true });
261
- } catch (cause) {
262
- const err = cause as NodeJS.ErrnoException;
263
- if (err.code !== "ENOENT") throw error.toError(cause);
264
- }
265
- }
266
-
267
- function startHeartbeat(lockPath: string): () => void {
268
- const timer = setInterval(() => {
269
- const now = new Date();
270
- void utimes(lockPath, now, now).catch((cause) => {
271
- logger.warn("file lock heartbeat failed", {
272
- path: lockPath,
273
- error: error.errorMessage(cause),
274
- });
275
- });
276
- }, UPDATE_MS);
277
- timer.unref();
278
- return () => clearInterval(timer);
279
- }
280
-
281
252
  function assertBeforeDeadline(lockPath: string, deadline: number | undefined): void {
282
253
  if (deadline !== undefined && Date.now() >= deadline) {
283
254
  throw new Error(`Timed out waiting for file lock: ${lockPath}`);