dflockd-client 1.5.0 → 1.7.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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  TypeScript client for the [dflockd](https://github.com/mtingers/dflockd) distributed lock daemon.
4
4
 
5
+ **[Documentation](https://mtingers.github.io/dflockd-client-ts/)**
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -109,6 +111,7 @@ without blocking. If the lock is contended, `enqueue()` returns `"queued"` and
109
111
  | `port` | `number` | `6388` | Server port *(deprecated — use `servers`)* |
110
112
  | `renewRatio` | `number` | `0.5` | Renew at `lease * ratio` seconds (e.g. 50% of TTL)|
111
113
  | `tls` | `tls.ConnectionOptions` | `undefined` | TLS options; pass `{}` for default system CA |
114
+ | `auth` | `string` | `undefined` | Auth token for servers started with `--auth-token` |
112
115
 
113
116
  ### Multi-server sharding
114
117
 
@@ -236,6 +239,7 @@ try {
236
239
  | `port` | `number` | `6388` | Server port *(deprecated — use `servers`)* |
237
240
  | `renewRatio` | `number` | `0.5` | Renew at `lease * ratio` seconds (e.g. 50% of TTL)|
238
241
  | `tls` | `tls.ConnectionOptions` | `undefined` | TLS options; pass `{}` for default system CA |
242
+ | `auth` | `string` | `undefined` | Auth token for servers started with `--auth-token` |
239
243
 
240
244
  ## Stats
241
245
 
@@ -291,16 +295,46 @@ const sem = new DistributedSemaphore({ key: "my-resource", limit: 5, tls: {} });
291
295
  const s = await stats({ host: "10.0.0.1", port: 6388, tls: {} });
292
296
  ```
293
297
 
298
+ ## Authentication
299
+
300
+ When the dflockd server is started with `--auth-token`, every connection must
301
+ authenticate before sending any other command. Pass the `auth` option to enable
302
+ token-based authentication:
303
+
304
+ ```ts
305
+ import { DistributedLock, DistributedSemaphore, stats } from "dflockd-client";
306
+
307
+ // Lock with auth
308
+ const lock = new DistributedLock({ key: "my-resource", auth: "my-secret-token" });
309
+
310
+ // Semaphore with auth
311
+ const sem = new DistributedSemaphore({ key: "my-resource", limit: 5, auth: "my-secret-token" });
312
+
313
+ // Stats with auth
314
+ const s = await stats({ host: "10.0.0.1", port: 6388, auth: "my-secret-token" });
315
+
316
+ // Combined with TLS
317
+ const secureLock = new DistributedLock({
318
+ key: "my-resource",
319
+ tls: {},
320
+ auth: "my-secret-token",
321
+ });
322
+ ```
323
+
324
+ If authentication fails, an `AuthError` (a subclass of `LockError`) is thrown.
325
+
294
326
  ### Error handling
295
327
 
296
328
  ```ts
297
- import { DistributedLock, AcquireTimeoutError, LockError } from "dflockd-client";
329
+ import { DistributedLock, AcquireTimeoutError, AuthError, LockError } from "dflockd-client";
298
330
 
299
331
  try {
300
332
  await lock.withLock(async () => { /* ... */ });
301
333
  } catch (err) {
302
334
  if (err instanceof AcquireTimeoutError) {
303
335
  // lock could not be acquired within acquireTimeoutS
336
+ } else if (err instanceof AuthError) {
337
+ // authentication failed (bad or missing token)
304
338
  } else if (err instanceof LockError) {
305
339
  // protocol-level error (bad token, server disconnect, etc.)
306
340
  }
package/dist/client.cjs CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var client_exports = {};
32
32
  __export(client_exports, {
33
33
  AcquireTimeoutError: () => AcquireTimeoutError,
34
+ AuthError: () => AuthError,
34
35
  DistributedLock: () => DistributedLock,
35
36
  DistributedSemaphore: () => DistributedSemaphore,
36
37
  LockError: () => LockError,
@@ -64,6 +65,12 @@ var LockError = class extends Error {
64
65
  this.name = "LockError";
65
66
  }
66
67
  };
68
+ var AuthError = class extends LockError {
69
+ constructor() {
70
+ super("authentication failed");
71
+ this.name = "AuthError";
72
+ }
73
+ };
67
74
  function encodeLines(...lines) {
68
75
  return Buffer.from(lines.map((l) => l + "\n").join(""), "utf-8");
69
76
  }
@@ -96,19 +103,29 @@ function readline(sock) {
96
103
  sock.on("close", onClose);
97
104
  });
98
105
  }
99
- function connect2(host, port, tlsOptions) {
100
- return new Promise((resolve, reject) => {
106
+ async function connect2(host, port, tlsOptions, auth) {
107
+ const sock = await new Promise((resolve, reject) => {
101
108
  if (tlsOptions) {
102
- const sock = tls.connect(
103
- { host, port, ...tlsOptions },
104
- () => resolve(sock)
105
- );
106
- sock.on("error", reject);
109
+ const s = tls.connect({ host, port, ...tlsOptions }, () => resolve(s));
110
+ s.on("error", reject);
107
111
  } else {
108
- const sock = net.createConnection({ host, port }, () => resolve(sock));
109
- sock.on("error", reject);
112
+ const s = net.createConnection({ host, port }, () => resolve(s));
113
+ s.on("error", reject);
110
114
  }
111
115
  });
116
+ if (auth != null) {
117
+ sock.write(encodeLines("auth", "_", auth));
118
+ const resp = await readline(sock);
119
+ if (resp === "ok") {
120
+ return sock;
121
+ }
122
+ sock.destroy();
123
+ if (resp === "error_auth") {
124
+ throw new AuthError();
125
+ }
126
+ throw new LockError(`auth failed: '${resp}'`);
127
+ }
128
+ return sock;
112
129
  }
113
130
  var CRC32_TABLE = new Uint32Array(256);
114
131
  for (let i = 0; i < 256; i++) {
@@ -274,7 +291,7 @@ async function statsProto(sock) {
274
291
  async function stats(options) {
275
292
  const host = options?.host ?? DEFAULT_HOST;
276
293
  const port = options?.port ?? DEFAULT_PORT;
277
- const sock = await connect2(host, port, options?.tls);
294
+ const sock = await connect2(host, port, options?.tls, options?.auth);
278
295
  try {
279
296
  return await statsProto(sock);
280
297
  } finally {
@@ -289,6 +306,7 @@ var DistributedLock = class {
289
306
  shardingStrategy;
290
307
  renewRatio;
291
308
  tls;
309
+ auth;
292
310
  token = null;
293
311
  lease = 0;
294
312
  sock = null;
@@ -299,6 +317,7 @@ var DistributedLock = class {
299
317
  this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;
300
318
  this.leaseTtlS = opts.leaseTtlS;
301
319
  this.tls = opts.tls;
320
+ this.auth = opts.auth;
302
321
  if (opts.servers) {
303
322
  if (opts.servers.length === 0) {
304
323
  throw new LockError("servers list must not be empty");
@@ -318,7 +337,7 @@ var DistributedLock = class {
318
337
  async acquire() {
319
338
  this.closed = false;
320
339
  const [host, port] = this.pickServer();
321
- this.sock = await connect2(host, port, this.tls);
340
+ this.sock = await connect2(host, port, this.tls, this.auth);
322
341
  try {
323
342
  const result = await acquire(
324
343
  this.sock,
@@ -355,7 +374,7 @@ var DistributedLock = class {
355
374
  async enqueue() {
356
375
  this.closed = false;
357
376
  const [host, port] = this.pickServer();
358
- this.sock = await connect2(host, port, this.tls);
377
+ this.sock = await connect2(host, port, this.tls, this.auth);
359
378
  try {
360
379
  const result = await enqueue(this.sock, this.key, this.leaseTtlS);
361
380
  if (result.status === "acquired") {
@@ -460,6 +479,7 @@ var DistributedSemaphore = class {
460
479
  shardingStrategy;
461
480
  renewRatio;
462
481
  tls;
482
+ auth;
463
483
  token = null;
464
484
  lease = 0;
465
485
  sock = null;
@@ -471,6 +491,7 @@ var DistributedSemaphore = class {
471
491
  this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;
472
492
  this.leaseTtlS = opts.leaseTtlS;
473
493
  this.tls = opts.tls;
494
+ this.auth = opts.auth;
474
495
  if (opts.servers) {
475
496
  if (opts.servers.length === 0) {
476
497
  throw new LockError("servers list must not be empty");
@@ -490,7 +511,7 @@ var DistributedSemaphore = class {
490
511
  async acquire() {
491
512
  this.closed = false;
492
513
  const [host, port] = this.pickServer();
493
- this.sock = await connect2(host, port, this.tls);
514
+ this.sock = await connect2(host, port, this.tls, this.auth);
494
515
  try {
495
516
  const result = await semAcquire(
496
517
  this.sock,
@@ -528,7 +549,7 @@ var DistributedSemaphore = class {
528
549
  async enqueue() {
529
550
  this.closed = false;
530
551
  const [host, port] = this.pickServer();
531
- this.sock = await connect2(host, port, this.tls);
552
+ this.sock = await connect2(host, port, this.tls, this.auth);
532
553
  try {
533
554
  const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);
534
555
  if (result.status === "acquired") {
@@ -627,6 +648,7 @@ var DistributedSemaphore = class {
627
648
  // Annotate the CommonJS export names for ESM import in node:
628
649
  0 && (module.exports = {
629
650
  AcquireTimeoutError,
651
+ AuthError,
630
652
  DistributedLock,
631
653
  DistributedSemaphore,
632
654
  LockError,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class AcquireTimeoutError extends Error {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = \"\";\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n resolve(buf.slice(0, idx).replace(/\\r$/, \"\"));\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nfunction connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n if (tlsOptions) {\n const sock = tls.connect({ host, port, ...tlsOptions }, () =>\n resolve(sock),\n );\n sock.on(\"error\", reject);\n } else {\n const sock = net.createConnection({ host, port }, () => resolve(sock));\n sock.on(\"error\", reject);\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire the lock. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `lock lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `semaphore lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":"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","names":["connect"]}
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class AcquireTimeoutError extends Error {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AuthError extends LockError {\n constructor() {\n super(\"authentication failed\");\n this.name = \"AuthError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = \"\";\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n resolve(buf.slice(0, idx).replace(/\\r$/, \"\"));\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n if (tlsOptions) {\n const s = tls.connect({ host, port, ...tlsOptions }, () => resolve(s));\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => resolve(s));\n s.on(\"error\", reject);\n }\n });\n\n if (auth != null) {\n sock.write(encodeLines(\"auth\", \"_\", auth));\n const resp = await readline(sock);\n if (resp === \"ok\") {\n return sock;\n }\n sock.destroy();\n if (resp === \"error_auth\") {\n throw new AuthError();\n }\n throw new LockError(`auth failed: '${resp}'`);\n }\n\n return sock;\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions; auth?: string },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls, options?.auth);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire the lock. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `lock lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `semaphore lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAAqB;AACrB,UAAqB;AAErB,IAAM,eAAe;AACrB,IAAM,eAAe;AAMd,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,KAAa;AACvB,UAAM,sBAAsB,GAAG,GAAG;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,UAAU;AAAA,EACvC,cAAc;AACZ,UAAM,uBAAuB;AAC7B,SAAK,OAAO;AAAA,EACd;AACF;AAMA,SAAS,eAAe,OAAyB;AAC/C,SAAO,OAAO,KAAK,MAAM,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO;AACjE;AAOA,SAAS,SAAS,MAAmC;AACnD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,MAAM;AAEV,UAAM,SAAS,CAAC,UAAkB;AAChC,aAAO,MAAM,SAAS,OAAO;AAC7B,YAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,UAAI,QAAQ,IAAI;AACd,gBAAQ;AACR,gBAAQ,IAAI,MAAM,GAAG,GAAG,EAAE,QAAQ,OAAO,EAAE,CAAC;AAAA,MAC9C;AAAA,IACF;AAEA,UAAM,UAAU,CAAC,QAAe;AAC9B,cAAQ;AACR,aAAO,GAAG;AAAA,IACZ;AAEA,UAAM,UAAU,MAAM;AACpB,cAAQ;AACR,aAAO,IAAI,UAAU,0BAA0B,CAAC;AAAA,IAClD;AAEA,UAAM,UAAU,MAAM;AACpB,WAAK,eAAe,QAAQ,MAAM;AAClC,WAAK,eAAe,SAAS,OAAO;AACpC,WAAK,eAAe,SAAS,OAAO;AAAA,IACtC;AAEA,SAAK,GAAG,QAAQ,MAAM;AACtB,SAAK,GAAG,SAAS,OAAO;AACxB,SAAK,GAAG,SAAS,OAAO;AAAA,EAC1B,CAAC;AACH;AAEA,eAAeA,SACb,MACA,MACA,YACA,MACqB;AACrB,QAAM,OAAO,MAAM,IAAI,QAAoB,CAAC,SAAS,WAAW;AAC9D,QAAI,YAAY;AACd,YAAM,IAAQ,YAAQ,EAAE,MAAM,MAAM,GAAG,WAAW,GAAG,MAAM,QAAQ,CAAC,CAAC;AACrE,QAAE,GAAG,SAAS,MAAM;AAAA,IACtB,OAAO;AACL,YAAM,IAAQ,qBAAiB,EAAE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,CAAC;AAC/D,QAAE,GAAG,SAAS,MAAM;AAAA,IACtB;AAAA,EACF,CAAC;AAED,MAAI,QAAQ,MAAM;AAChB,SAAK,MAAM,YAAY,QAAQ,KAAK,IAAI,CAAC;AACzC,UAAM,OAAO,MAAM,SAAS,IAAI;AAChC,QAAI,SAAS,MAAM;AACjB,aAAO;AAAA,IACT;AACA,SAAK,QAAQ;AACb,QAAI,SAAS,cAAc;AACzB,YAAM,IAAI,UAAU;AAAA,IACtB;AACA,UAAM,IAAI,UAAU,iBAAiB,IAAI,GAAG;AAAA,EAC9C;AAEA,SAAO;AACT;AAMA,IAAM,cAAc,IAAI,YAAY,GAAG;AACvC,SAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,IAAI,IAAI,aAAc,MAAM,IAAK,MAAM;AAAA,EAC7C;AACA,cAAY,CAAC,IAAI;AACnB;AAEA,SAAS,MAAM,KAAqB;AAClC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,aAAa,MAAM,IAAI,CAAC,KAAK,GAAI,IAAK,QAAQ;AAAA,EACtD;AACA,UAAQ,MAAM,gBAAgB;AAChC;AAQO,SAAS,gBAAgB,KAAa,YAA4B;AACvE,UAAQ,MAAM,OAAO,KAAK,KAAK,OAAO,CAAC,MAAM,KAAK;AACpD;AA0CA,eAAsB,QACpB,MACA,KACA,iBACA,WAC2C;AAC3C,QAAM,MACJ,aAAa,OACT,OAAO,eAAe,IACtB,GAAG,eAAe,IAAI,SAAS;AAErC,OAAK,MAAM,YAAY,KAAK,KAAK,GAAG,CAAC;AAErC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI,oBAAoB,GAAG;AAAA,EACnC;AACA,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,oBAAoB,IAAI,GAAG;AAAA,EACjD;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI,UAAU,qBAAqB,IAAI,GAAG;AAAA,EAClD;AACA,QAAM,QAAQ,MAAM,CAAC;AACrB,QAAM,QAAQ,MAAM,UAAU,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAC3D,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,eAAsB,MACpB,MACA,KACA,OACA,WACiB;AACjB,QAAM,MAAM,aAAa,OAAO,QAAQ,GAAG,KAAK,IAAI,SAAS;AAC7D,OAAK,MAAM,YAAY,KAAK,KAAK,GAAG,CAAC;AAErC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,CAAC,KAAK,WAAW,IAAI,GAAG;AAC1B,UAAM,IAAI,UAAU,kBAAkB,IAAI,GAAG;AAAA,EAC/C;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,UAAU,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,GAAG;AAC/C,WAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,EAC9B;AACA,SAAO;AACT;AAEA,eAAsB,QACpB,MACA,KACA,WACwF;AACxF,QAAM,MAAM,aAAa,OAAO,KAAK,OAAO,SAAS;AACrD,OAAK,MAAM,YAAY,KAAK,KAAK,GAAG,CAAC;AAErC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,KAAK,WAAW,WAAW,GAAG;AAChC,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,UAAM,QAAQ,MAAM,CAAC;AACrB,UAAM,QAAQ,MAAM,UAAU,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAC3D,WAAO,EAAE,QAAQ,YAAY,OAAO,MAAM;AAAA,EAC5C;AACA,MAAI,SAAS,UAAU;AACrB,WAAO,EAAE,QAAQ,UAAU,OAAO,MAAM,OAAO,KAAK;AAAA,EACtD;AACA,QAAM,IAAI,UAAU,oBAAoB,IAAI,GAAG;AACjD;AAEA,eAAsB,YACpB,MACA,KACA,cAC2C;AAC3C,OAAK,MAAM,YAAY,KAAK,KAAK,OAAO,YAAY,CAAC,CAAC;AAEtD,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI,oBAAoB,GAAG;AAAA,EACnC;AACA,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,iBAAiB,IAAI,GAAG;AAAA,EAC9C;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAM,QAAQ,MAAM,CAAC;AACrB,QAAM,QAAQ,MAAM,UAAU,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAC3D,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,eAAsB,QACpB,MACA,KACA,OACe;AACf,OAAK,MAAM,YAAY,KAAK,KAAK,KAAK,CAAC;AAEvC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,MAAM;AACjB,UAAM,IAAI,UAAU,oBAAoB,IAAI,GAAG;AAAA,EACjD;AACF;AAMA,eAAsB,WACpB,MACA,KACA,iBACA,OACA,WAC2C;AAC3C,QAAM,MACJ,aAAa,OACT,GAAG,eAAe,IAAI,KAAK,KAC3B,GAAG,eAAe,IAAI,KAAK,IAAI,SAAS;AAE9C,OAAK,MAAM,YAAY,MAAM,KAAK,GAAG,CAAC;AAEtC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI,oBAAoB,GAAG;AAAA,EACnC;AACA,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,wBAAwB,IAAI,GAAG;AAAA,EACrD;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI,UAAU,qBAAqB,IAAI,GAAG;AAAA,EAClD;AACA,QAAM,QAAQ,MAAM,CAAC;AACrB,QAAM,QAAQ,MAAM,UAAU,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAC3D,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,eAAsB,SACpB,MACA,KACA,OACA,WACiB;AACjB,QAAM,MAAM,aAAa,OAAO,QAAQ,GAAG,KAAK,IAAI,SAAS;AAC7D,OAAK,MAAM,YAAY,MAAM,KAAK,GAAG,CAAC;AAEtC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,CAAC,KAAK,WAAW,IAAI,GAAG;AAC1B,UAAM,IAAI,UAAU,sBAAsB,IAAI,GAAG;AAAA,EACnD;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,MAAI,MAAM,UAAU,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,GAAG;AAC/C,WAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,EAC9B;AACA,SAAO;AACT;AAEA,eAAsB,WACpB,MACA,KACA,OACA,WACwF;AACxF,QAAM,MAAM,aAAa,OAAO,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS;AACrE,OAAK,MAAM,YAAY,MAAM,KAAK,GAAG,CAAC;AAEtC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,KAAK,WAAW,WAAW,GAAG;AAChC,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,UAAM,QAAQ,MAAM,CAAC;AACrB,UAAM,QAAQ,MAAM,UAAU,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAC3D,WAAO,EAAE,QAAQ,YAAY,OAAO,MAAM;AAAA,EAC5C;AACA,MAAI,SAAS,UAAU;AACrB,WAAO,EAAE,QAAQ,UAAU,OAAO,MAAM,OAAO,KAAK;AAAA,EACtD;AACA,QAAM,IAAI,UAAU,wBAAwB,IAAI,GAAG;AACrD;AAEA,eAAsB,eACpB,MACA,KACA,cAC2C;AAC3C,OAAK,MAAM,YAAY,MAAM,KAAK,OAAO,YAAY,CAAC,CAAC;AAEvD,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI,oBAAoB,GAAG;AAAA,EACnC;AACA,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,qBAAqB,IAAI,GAAG;AAAA,EAClD;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAM,QAAQ,MAAM,CAAC;AACrB,QAAM,QAAQ,MAAM,UAAU,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAC3D,SAAO,EAAE,OAAO,MAAM;AACxB;AAEA,eAAsB,WACpB,MACA,KACA,OACe;AACf,OAAK,MAAM,YAAY,MAAM,KAAK,KAAK,CAAC;AAExC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,MAAM;AACjB,UAAM,IAAI,UAAU,wBAAwB,IAAI,GAAG;AAAA,EACrD;AACF;AAMA,eAAe,WAAW,MAAkC;AAC1D,OAAK,MAAM,YAAY,SAAS,KAAK,EAAE,CAAC;AAExC,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,kBAAkB,IAAI,GAAG;AAAA,EAC/C;AAEA,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,SAAO,KAAK,MAAM,IAAI;AACxB;AAaA,eAAsB,MACpB,SACgB;AAChB,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,OAAO,MAAMA,SAAQ,MAAM,MAAM,SAAS,KAAK,SAAS,IAAI;AAClE,MAAI;AACF,WAAO,MAAM,WAAW,IAAI;AAAA,EAC9B,UAAE;AACA,SAAK,QAAQ;AAAA,EACf;AACF;AAqBO,IAAM,kBAAN,MAAsB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,QAAuB;AAAA,EACvB,QAAgB;AAAA,EAER,OAA0B;AAAA,EAC1B,aAAmD;AAAA,EACnD,SAAS;AAAA,EAEjB,YAAY,MAA8B;AACxC,SAAK,MAAM,KAAK;AAChB,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,SAAK,YAAY,KAAK;AACtB,SAAK,MAAM,KAAK;AAChB,SAAK,OAAO,KAAK;AAEjB,QAAI,KAAK,SAAS;AAChB,UAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,cAAM,IAAI,UAAU,gCAAgC;AAAA,MACtD;AACA,WAAK,UAAU,KAAK;AAAA,IACtB,OAAO;AACL,WAAK,UAAU,CAAC,CAAC,KAAK,QAAQ,cAAc,KAAK,QAAQ,YAAY,CAAC;AAAA,IACxE;AAEA,SAAK,mBAAmB,KAAK,oBAAoB;AACjD,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA,EAEQ,aAA+B;AACrC,UAAM,MAAM,KAAK,iBAAiB,KAAK,KAAK,KAAK,QAAQ,MAAM;AAC/D,WAAO,KAAK,QAAQ,GAAG;AAAA,EACzB;AAAA;AAAA,EAGA,MAAM,UAA4B;AAChC,SAAK,SAAS;AACd,UAAM,CAAC,MAAM,IAAI,IAAI,KAAK,WAAW;AACrC,SAAK,OAAO,MAAMA,SAAQ,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI;AACzD,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,WAAK,QAAQ,OAAO;AACpB,WAAK,QAAQ,OAAO;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,KAAK,MAAM;AACjB,UAAI,eAAe,oBAAqB,QAAO;AAC/C,YAAM;AAAA,IACR;AACA,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,UAAyB;AAC7B,QAAI;AACF,WAAK,UAAU;AACf,UAAI,KAAK,QAAQ,KAAK,OAAO;AAC3B,cAAM,QAAQ,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK;AAAA,MAC/C;AAAA,IACF,UAAE;AACA,YAAM,KAAK,MAAM;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAA0C;AAC9C,SAAK,SAAS;AACd,UAAM,CAAC,MAAM,IAAI,IAAI,KAAK,WAAW;AACrC,SAAK,OAAO,MAAMA,SAAQ,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI;AACzD,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,KAAK,MAAM,KAAK,KAAK,KAAK,SAAS;AAChE,UAAI,OAAO,WAAW,YAAY;AAChC,aAAK,QAAQ,OAAO;AACpB,aAAK,QAAQ,OAAO,SAAS;AAC7B,aAAK,WAAW;AAAA,MAClB;AACA,aAAO,OAAO;AAAA,IAChB,SAAS,KAAK;AACZ,YAAM,KAAK,MAAM;AACjB,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,UAAqC;AAC9C,QAAI,KAAK,UAAU,MAAM;AAEvB,aAAO;AAAA,IACT;AACA,QAAI,CAAC,KAAK,MAAM;AACd,YAAM,IAAI,UAAU,qCAAqC;AAAA,IAC3D;AACA,UAAM,UAAU,YAAY,KAAK;AACjC,QAAI;AACF,YAAM,SAAS,MAAM,YAAY,KAAK,MAAM,KAAK,KAAK,OAAO;AAC7D,WAAK,QAAQ,OAAO;AACpB,WAAK,QAAQ,OAAO;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,KAAK,MAAM;AACjB,UAAI,eAAe,oBAAqB,QAAO;AAC/C,YAAM;AAAA,IACR;AACA,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,SAAY,IAAsC;AACtD,UAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,oBAAoB,KAAK,GAAG;AAAA,IACxC;AACA,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,QAAuB;AAC3B,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,SAAK,UAAU;AACf,QAAI,KAAK,MAAM;AACb,WAAK,KAAK,QAAQ;AAClB,WAAK,OAAO;AAAA,IACd;AACA,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAIQ,aAAmB;AACzB,UAAM,WAAW,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK,UAAU,IAAI;AAC7D,UAAM,OAAO,YAAY;AACvB,UAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,MAAO;AAC/B,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,KAAK,KAAK,KAAK,OAAO,KAAK,SAAS;AAAA,MAC7D,QAAQ;AACN,gBAAQ;AAAA,UACN,iCAAiC,KAAK,GAAG,UAAU,KAAK,KAAK;AAAA,QAC/D;AACA,aAAK,QAAQ;AACb;AAAA,MACF;AACA,WAAK,aAAa,WAAW,MAAM,QAAQ;AAAA,IAC7C;AACA,SAAK,aAAa,WAAW,MAAM,QAAQ;AAAA,EAC7C;AAAA,EAEQ,YAAkB;AACxB,QAAI,KAAK,cAAc,MAAM;AAC3B,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AACF;AAsBO,IAAM,uBAAN,MAA2B;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,QAAuB;AAAA,EACvB,QAAgB;AAAA,EAER,OAA0B;AAAA,EAC1B,aAAmD;AAAA,EACnD,SAAS;AAAA,EAEjB,YAAY,MAAmC;AAC7C,SAAK,MAAM,KAAK;AAChB,SAAK,QAAQ,KAAK;AAClB,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,SAAK,YAAY,KAAK;AACtB,SAAK,MAAM,KAAK;AAChB,SAAK,OAAO,KAAK;AAEjB,QAAI,KAAK,SAAS;AAChB,UAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,cAAM,IAAI,UAAU,gCAAgC;AAAA,MACtD;AACA,WAAK,UAAU,KAAK;AAAA,IACtB,OAAO;AACL,WAAK,UAAU,CAAC,CAAC,KAAK,QAAQ,cAAc,KAAK,QAAQ,YAAY,CAAC;AAAA,IACxE;AAEA,SAAK,mBAAmB,KAAK,oBAAoB;AACjD,SAAK,aAAa,KAAK,cAAc;AAAA,EACvC;AAAA,EAEQ,aAA+B;AACrC,UAAM,MAAM,KAAK,iBAAiB,KAAK,KAAK,KAAK,QAAQ,MAAM;AAC/D,WAAO,KAAK,QAAQ,GAAG;AAAA,EACzB;AAAA;AAAA,EAGA,MAAM,UAA4B;AAChC,SAAK,SAAS;AACd,UAAM,CAAC,MAAM,IAAI,IAAI,KAAK,WAAW;AACrC,SAAK,OAAO,MAAMA,SAAQ,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI;AACzD,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,WAAK,QAAQ,OAAO;AACpB,WAAK,QAAQ,OAAO;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,KAAK,MAAM;AACjB,UAAI,eAAe,oBAAqB,QAAO;AAC/C,YAAM;AAAA,IACR;AACA,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,UAAyB;AAC7B,QAAI;AACF,WAAK,UAAU;AACf,UAAI,KAAK,QAAQ,KAAK,OAAO;AAC3B,cAAM,WAAW,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK;AAAA,MAClD;AAAA,IACF,UAAE;AACA,YAAM,KAAK,MAAM;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAA0C;AAC9C,SAAK,SAAS;AACd,UAAM,CAAC,MAAM,IAAI,IAAI,KAAK,WAAW;AACrC,SAAK,OAAO,MAAMA,SAAQ,MAAM,MAAM,KAAK,KAAK,KAAK,IAAI;AACzD,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,KAAK,MAAM,KAAK,KAAK,KAAK,OAAO,KAAK,SAAS;AAC/E,UAAI,OAAO,WAAW,YAAY;AAChC,aAAK,QAAQ,OAAO;AACpB,aAAK,QAAQ,OAAO,SAAS;AAC7B,aAAK,WAAW;AAAA,MAClB;AACA,aAAO,OAAO;AAAA,IAChB,SAAS,KAAK;AACZ,YAAM,KAAK,MAAM;AACjB,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,UAAqC;AAC9C,QAAI,KAAK,UAAU,MAAM;AACvB,aAAO;AAAA,IACT;AACA,QAAI,CAAC,KAAK,MAAM;AACd,YAAM,IAAI,UAAU,qCAAqC;AAAA,IAC3D;AACA,UAAM,UAAU,YAAY,KAAK;AACjC,QAAI;AACF,YAAM,SAAS,MAAM,eAAe,KAAK,MAAM,KAAK,KAAK,OAAO;AAChE,WAAK,QAAQ,OAAO;AACpB,WAAK,QAAQ,OAAO;AAAA,IACtB,SAAS,KAAK;AACZ,YAAM,KAAK,MAAM;AACjB,UAAI,eAAe,oBAAqB,QAAO;AAC/C,YAAM;AAAA,IACR;AACA,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,SAAY,IAAsC;AACtD,UAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,oBAAoB,KAAK,GAAG;AAAA,IACxC;AACA,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,QAAuB;AAC3B,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,SAAK,UAAU;AACf,QAAI,KAAK,MAAM;AACb,WAAK,KAAK,QAAQ;AAClB,WAAK,OAAO;AAAA,IACd;AACA,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAIQ,aAAmB;AACzB,UAAM,WAAW,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK,UAAU,IAAI;AAC7D,UAAM,OAAO,YAAY;AACvB,UAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,MAAO;AAC/B,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,KAAK,KAAK,KAAK,OAAO,KAAK,SAAS;AAAA,MAChE,QAAQ;AACN,gBAAQ;AAAA,UACN,sCAAsC,KAAK,GAAG,UAAU,KAAK,KAAK;AAAA,QACpE;AACA,aAAK,QAAQ;AACb;AAAA,MACF;AACA,WAAK,aAAa,WAAW,MAAM,QAAQ;AAAA,IAC7C;AACA,SAAK,aAAa,WAAW,MAAM,QAAQ;AAAA,EAC7C;AAAA,EAEQ,YAAkB;AACxB,QAAI,KAAK,cAAc,MAAM;AAC3B,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AACF;","names":["connect"]}
package/dist/client.d.cts CHANGED
@@ -7,6 +7,9 @@ declare class AcquireTimeoutError extends Error {
7
7
  declare class LockError extends Error {
8
8
  constructor(message: string);
9
9
  }
10
+ declare class AuthError extends LockError {
11
+ constructor();
12
+ }
10
13
  type ShardingStrategy = (key: string, numServers: number) => number;
11
14
  declare function stableHashShard(key: string, numServers: number): number;
12
15
  interface StatsLock {
@@ -81,6 +84,7 @@ declare function stats(options?: {
81
84
  host?: string;
82
85
  port?: number;
83
86
  tls?: tls.ConnectionOptions;
87
+ auth?: string;
84
88
  }): Promise<Stats>;
85
89
  interface DistributedLockOptions {
86
90
  key: string;
@@ -94,6 +98,7 @@ interface DistributedLockOptions {
94
98
  shardingStrategy?: ShardingStrategy;
95
99
  renewRatio?: number;
96
100
  tls?: tls.ConnectionOptions;
101
+ auth?: string;
97
102
  }
98
103
  declare class DistributedLock {
99
104
  readonly key: string;
@@ -103,6 +108,7 @@ declare class DistributedLock {
103
108
  readonly shardingStrategy: ShardingStrategy;
104
109
  readonly renewRatio: number;
105
110
  readonly tls: tls.ConnectionOptions | undefined;
111
+ readonly auth: string | undefined;
106
112
  token: string | null;
107
113
  lease: number;
108
114
  private sock;
@@ -155,6 +161,7 @@ interface DistributedSemaphoreOptions {
155
161
  shardingStrategy?: ShardingStrategy;
156
162
  renewRatio?: number;
157
163
  tls?: tls.ConnectionOptions;
164
+ auth?: string;
158
165
  }
159
166
  declare class DistributedSemaphore {
160
167
  readonly key: string;
@@ -165,6 +172,7 @@ declare class DistributedSemaphore {
165
172
  readonly shardingStrategy: ShardingStrategy;
166
173
  readonly renewRatio: number;
167
174
  readonly tls: tls.ConnectionOptions | undefined;
175
+ readonly auth: string | undefined;
168
176
  token: string | null;
169
177
  lease: number;
170
178
  private sock;
@@ -205,4 +213,4 @@ declare class DistributedSemaphore {
205
213
  private stopRenew;
206
214
  }
207
215
 
208
- export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, type Stats, type StatsIdleLock, type StatsIdleSemaphore, type StatsLock, type StatsSemaphore, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, stats, waitForLock };
216
+ export { AcquireTimeoutError, AuthError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, type Stats, type StatsIdleLock, type StatsIdleSemaphore, type StatsLock, type StatsSemaphore, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, stats, waitForLock };
package/dist/client.d.ts CHANGED
@@ -7,6 +7,9 @@ declare class AcquireTimeoutError extends Error {
7
7
  declare class LockError extends Error {
8
8
  constructor(message: string);
9
9
  }
10
+ declare class AuthError extends LockError {
11
+ constructor();
12
+ }
10
13
  type ShardingStrategy = (key: string, numServers: number) => number;
11
14
  declare function stableHashShard(key: string, numServers: number): number;
12
15
  interface StatsLock {
@@ -81,6 +84,7 @@ declare function stats(options?: {
81
84
  host?: string;
82
85
  port?: number;
83
86
  tls?: tls.ConnectionOptions;
87
+ auth?: string;
84
88
  }): Promise<Stats>;
85
89
  interface DistributedLockOptions {
86
90
  key: string;
@@ -94,6 +98,7 @@ interface DistributedLockOptions {
94
98
  shardingStrategy?: ShardingStrategy;
95
99
  renewRatio?: number;
96
100
  tls?: tls.ConnectionOptions;
101
+ auth?: string;
97
102
  }
98
103
  declare class DistributedLock {
99
104
  readonly key: string;
@@ -103,6 +108,7 @@ declare class DistributedLock {
103
108
  readonly shardingStrategy: ShardingStrategy;
104
109
  readonly renewRatio: number;
105
110
  readonly tls: tls.ConnectionOptions | undefined;
111
+ readonly auth: string | undefined;
106
112
  token: string | null;
107
113
  lease: number;
108
114
  private sock;
@@ -155,6 +161,7 @@ interface DistributedSemaphoreOptions {
155
161
  shardingStrategy?: ShardingStrategy;
156
162
  renewRatio?: number;
157
163
  tls?: tls.ConnectionOptions;
164
+ auth?: string;
158
165
  }
159
166
  declare class DistributedSemaphore {
160
167
  readonly key: string;
@@ -165,6 +172,7 @@ declare class DistributedSemaphore {
165
172
  readonly shardingStrategy: ShardingStrategy;
166
173
  readonly renewRatio: number;
167
174
  readonly tls: tls.ConnectionOptions | undefined;
175
+ readonly auth: string | undefined;
168
176
  token: string | null;
169
177
  lease: number;
170
178
  private sock;
@@ -205,4 +213,4 @@ declare class DistributedSemaphore {
205
213
  private stopRenew;
206
214
  }
207
215
 
208
- export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, type Stats, type StatsIdleLock, type StatsIdleSemaphore, type StatsLock, type StatsSemaphore, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, stats, waitForLock };
216
+ export { AcquireTimeoutError, AuthError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, type Stats, type StatsIdleLock, type StatsIdleSemaphore, type StatsLock, type StatsSemaphore, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, stats, waitForLock };
package/dist/client.js CHANGED
@@ -15,6 +15,12 @@ var LockError = class extends Error {
15
15
  this.name = "LockError";
16
16
  }
17
17
  };
18
+ var AuthError = class extends LockError {
19
+ constructor() {
20
+ super("authentication failed");
21
+ this.name = "AuthError";
22
+ }
23
+ };
18
24
  function encodeLines(...lines) {
19
25
  return Buffer.from(lines.map((l) => l + "\n").join(""), "utf-8");
20
26
  }
@@ -47,19 +53,29 @@ function readline(sock) {
47
53
  sock.on("close", onClose);
48
54
  });
49
55
  }
50
- function connect2(host, port, tlsOptions) {
51
- return new Promise((resolve, reject) => {
56
+ async function connect2(host, port, tlsOptions, auth) {
57
+ const sock = await new Promise((resolve, reject) => {
52
58
  if (tlsOptions) {
53
- const sock = tls.connect(
54
- { host, port, ...tlsOptions },
55
- () => resolve(sock)
56
- );
57
- sock.on("error", reject);
59
+ const s = tls.connect({ host, port, ...tlsOptions }, () => resolve(s));
60
+ s.on("error", reject);
58
61
  } else {
59
- const sock = net.createConnection({ host, port }, () => resolve(sock));
60
- sock.on("error", reject);
62
+ const s = net.createConnection({ host, port }, () => resolve(s));
63
+ s.on("error", reject);
61
64
  }
62
65
  });
66
+ if (auth != null) {
67
+ sock.write(encodeLines("auth", "_", auth));
68
+ const resp = await readline(sock);
69
+ if (resp === "ok") {
70
+ return sock;
71
+ }
72
+ sock.destroy();
73
+ if (resp === "error_auth") {
74
+ throw new AuthError();
75
+ }
76
+ throw new LockError(`auth failed: '${resp}'`);
77
+ }
78
+ return sock;
63
79
  }
64
80
  var CRC32_TABLE = new Uint32Array(256);
65
81
  for (let i = 0; i < 256; i++) {
@@ -225,7 +241,7 @@ async function statsProto(sock) {
225
241
  async function stats(options) {
226
242
  const host = options?.host ?? DEFAULT_HOST;
227
243
  const port = options?.port ?? DEFAULT_PORT;
228
- const sock = await connect2(host, port, options?.tls);
244
+ const sock = await connect2(host, port, options?.tls, options?.auth);
229
245
  try {
230
246
  return await statsProto(sock);
231
247
  } finally {
@@ -240,6 +256,7 @@ var DistributedLock = class {
240
256
  shardingStrategy;
241
257
  renewRatio;
242
258
  tls;
259
+ auth;
243
260
  token = null;
244
261
  lease = 0;
245
262
  sock = null;
@@ -250,6 +267,7 @@ var DistributedLock = class {
250
267
  this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;
251
268
  this.leaseTtlS = opts.leaseTtlS;
252
269
  this.tls = opts.tls;
270
+ this.auth = opts.auth;
253
271
  if (opts.servers) {
254
272
  if (opts.servers.length === 0) {
255
273
  throw new LockError("servers list must not be empty");
@@ -269,7 +287,7 @@ var DistributedLock = class {
269
287
  async acquire() {
270
288
  this.closed = false;
271
289
  const [host, port] = this.pickServer();
272
- this.sock = await connect2(host, port, this.tls);
290
+ this.sock = await connect2(host, port, this.tls, this.auth);
273
291
  try {
274
292
  const result = await acquire(
275
293
  this.sock,
@@ -306,7 +324,7 @@ var DistributedLock = class {
306
324
  async enqueue() {
307
325
  this.closed = false;
308
326
  const [host, port] = this.pickServer();
309
- this.sock = await connect2(host, port, this.tls);
327
+ this.sock = await connect2(host, port, this.tls, this.auth);
310
328
  try {
311
329
  const result = await enqueue(this.sock, this.key, this.leaseTtlS);
312
330
  if (result.status === "acquired") {
@@ -411,6 +429,7 @@ var DistributedSemaphore = class {
411
429
  shardingStrategy;
412
430
  renewRatio;
413
431
  tls;
432
+ auth;
414
433
  token = null;
415
434
  lease = 0;
416
435
  sock = null;
@@ -422,6 +441,7 @@ var DistributedSemaphore = class {
422
441
  this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;
423
442
  this.leaseTtlS = opts.leaseTtlS;
424
443
  this.tls = opts.tls;
444
+ this.auth = opts.auth;
425
445
  if (opts.servers) {
426
446
  if (opts.servers.length === 0) {
427
447
  throw new LockError("servers list must not be empty");
@@ -441,7 +461,7 @@ var DistributedSemaphore = class {
441
461
  async acquire() {
442
462
  this.closed = false;
443
463
  const [host, port] = this.pickServer();
444
- this.sock = await connect2(host, port, this.tls);
464
+ this.sock = await connect2(host, port, this.tls, this.auth);
445
465
  try {
446
466
  const result = await semAcquire(
447
467
  this.sock,
@@ -479,7 +499,7 @@ var DistributedSemaphore = class {
479
499
  async enqueue() {
480
500
  this.closed = false;
481
501
  const [host, port] = this.pickServer();
482
- this.sock = await connect2(host, port, this.tls);
502
+ this.sock = await connect2(host, port, this.tls, this.auth);
483
503
  try {
484
504
  const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);
485
505
  if (result.status === "acquired") {
@@ -577,6 +597,7 @@ var DistributedSemaphore = class {
577
597
  };
578
598
  export {
579
599
  AcquireTimeoutError,
600
+ AuthError,
580
601
  DistributedLock,
581
602
  DistributedSemaphore,
582
603
  LockError,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class AcquireTimeoutError extends Error {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = \"\";\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n resolve(buf.slice(0, idx).replace(/\\r$/, \"\"));\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nfunction connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n): Promise<net.Socket> {\n return new Promise((resolve, reject) => {\n if (tlsOptions) {\n const sock = tls.connect({ host, port, ...tlsOptions }, () =>\n resolve(sock),\n );\n sock.on(\"error\", reject);\n } else {\n const sock = net.createConnection({ host, port }, () => resolve(sock));\n sock.on(\"error\", reject);\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire the lock. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `lock lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `semaphore lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":"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","names":["connect"]}
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import * as net from \"net\";\nimport * as tls from \"tls\";\n\nconst DEFAULT_HOST = \"127.0.0.1\";\nconst DEFAULT_PORT = 6388;\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport class AcquireTimeoutError extends Error {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\n }\n}\n\nexport class LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AuthError extends LockError {\n constructor() {\n super(\"authentication failed\");\n this.name = \"AuthError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Low-level helpers\n// ---------------------------------------------------------------------------\n\nfunction encodeLines(...lines: string[]): Buffer {\n return Buffer.from(lines.map((l) => l + \"\\n\").join(\"\"), \"utf-8\");\n}\n\n/**\n * Read one newline-terminated line from the socket.\n * Resolves with the line (without trailing \\r\\n).\n * Rejects if the connection closes before a full line arrives.\n */\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = \"\";\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n const idx = buf.indexOf(\"\\n\");\n if (idx !== -1) {\n cleanup();\n resolve(buf.slice(0, idx).replace(/\\r$/, \"\"));\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n reject(new LockError(\"server closed connection\"));\n };\n\n const cleanup = () => {\n sock.removeListener(\"data\", onData);\n sock.removeListener(\"error\", onError);\n sock.removeListener(\"close\", onClose);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n if (tlsOptions) {\n const s = tls.connect({ host, port, ...tlsOptions }, () => resolve(s));\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => resolve(s));\n s.on(\"error\", reject);\n }\n });\n\n if (auth != null) {\n sock.write(encodeLines(\"auth\", \"_\", auth));\n const resp = await readline(sock);\n if (resp === \"ok\") {\n return sock;\n }\n sock.destroy();\n if (resp === \"error_auth\") {\n throw new AuthError();\n }\n throw new LockError(`auth failed: '${resp}'`);\n }\n\n return sock;\n}\n\n// ---------------------------------------------------------------------------\n// CRC32 (same algorithm as Python's zlib.crc32)\n// ---------------------------------------------------------------------------\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let i = 0; i < 256; i++) {\n let c = i;\n for (let j = 0; j < 8; j++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n CRC32_TABLE[i] = c;\n}\n\nfunction crc32(buf: Buffer): number {\n let crc = 0xffffffff;\n for (let i = 0; i < buf.length; i++) {\n crc = CRC32_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\n// ---------------------------------------------------------------------------\n// Sharding\n// ---------------------------------------------------------------------------\n\nexport type ShardingStrategy = (key: string, numServers: number) => number;\n\nexport function stableHashShard(key: string, numServers: number): number {\n return (crc32(Buffer.from(key, \"utf-8\")) >>> 0) % numServers;\n}\n\n// ---------------------------------------------------------------------------\n// Stats types\n// ---------------------------------------------------------------------------\n\nexport interface StatsLock {\n key: string;\n owner_conn_id: number;\n lease_expires_in_s: number;\n waiters: number;\n}\n\nexport interface StatsSemaphore {\n key: string;\n limit: number;\n holders: number;\n waiters: number;\n}\n\nexport interface StatsIdleLock {\n key: string;\n idle_s: number;\n}\n\nexport interface StatsIdleSemaphore {\n key: string;\n idle_s: number;\n}\n\nexport interface Stats {\n connections: number;\n locks: StatsLock[];\n semaphores: StatsSemaphore[];\n idle_locks: StatsIdleLock[];\n idle_semaphores: StatsIdleSemaphore[];\n}\n\n// ---------------------------------------------------------------------------\n// Protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? String(acquireTimeoutS)\n : `${acquireTimeoutS} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"l\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function renew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? \"\" : String(leaseTtlS);\n sock.write(encodeLines(\"e\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`enqueue failed: '${resp}'`);\n}\n\nexport async function waitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"w\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"r\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket,\n key: string,\n acquireTimeoutS: number,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n const arg =\n leaseTtlS == null\n ? `${acquireTimeoutS} ${limit}`\n : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;\n\n sock.write(encodeLines(\"sl\", key, arg));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_acquire failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length < 2) {\n throw new LockError(`bad ok response: '${resp}'`);\n }\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRenew(\n sock: net.Socket,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n sock.write(encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok\")) {\n throw new LockError(`sem_renew failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n if (parts.length >= 2 && /^\\d+$/.test(parts[1])) {\n return parseInt(parts[1], 10);\n }\n return -1;\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;\n sock.write(encodeLines(\"se\", key, arg));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { status: \"acquired\", token, lease };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`sem_enqueue failed: '${resp}'`);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n sock.write(encodeLines(\"sw\", key, String(waitTimeoutS)));\n\n const resp = await readline(sock);\n if (resp === \"timeout\") {\n throw new AcquireTimeoutError(key);\n }\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`sem_wait failed: '${resp}'`);\n }\n\n const parts = resp.split(\" \");\n const token = parts[1];\n const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n sock.write(encodeLines(\"sr\", key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`sem_release failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stats protocol function\n// ---------------------------------------------------------------------------\n\nasync function statsProto(sock: net.Socket): Promise<Stats> {\n sock.write(encodeLines(\"stats\", \"_\", \"\"));\n\n const resp = await readline(sock);\n if (!resp.startsWith(\"ok \")) {\n throw new LockError(`stats failed: '${resp}'`);\n }\n\n const json = resp.slice(3);\n return JSON.parse(json) as Stats;\n}\n\n/**\n * Query server runtime statistics.\n *\n * Opens a short-lived connection, sends the `stats` command, and returns\n * the parsed response.\n *\n * ```ts\n * const s = await stats();\n * console.log(s.connections, s.locks.length);\n * ```\n */\nexport async function stats(\n options?: { host?: string; port?: number; tls?: tls.ConnectionOptions; auth?: string },\n): Promise<Stats> {\n const host = options?.host ?? DEFAULT_HOST;\n const port = options?.port ?? DEFAULT_PORT;\n const sock = await connect(host, port, options?.tls, options?.auth);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport interface DistributedLockOptions {\n key: string;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedLock {\n readonly key: string;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedLockOptions) {\n this.key = opts.key;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire the lock. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await acquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the lock and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await release(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, lock is already held) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await enqueue(this.sock, this.key, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until the lock is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n // Already acquired during enqueue (fast path)\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await waitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding the lock, then release automatically.\n *\n * ```ts\n * const lock = new DistributedLock({ key: \"my-resource\" });\n * await lock.withLock(async () => {\n * // critical section\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await renew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `lock lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport interface DistributedSemaphoreOptions {\n key: string;\n limit: number;\n acquireTimeoutS?: number;\n leaseTtlS?: number;\n /** @deprecated Use `servers` instead. */\n host?: string;\n /** @deprecated Use `servers` instead. */\n port?: number;\n servers?: Array<[host: string, port: number]>;\n shardingStrategy?: ShardingStrategy;\n renewRatio?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n}\n\nexport class DistributedSemaphore {\n readonly key: string;\n readonly limit: number;\n readonly acquireTimeoutS: number;\n readonly leaseTtlS: number | undefined;\n readonly servers: Array<[string, number]>;\n readonly shardingStrategy: ShardingStrategy;\n readonly renewRatio: number;\n readonly tls: tls.ConnectionOptions | undefined;\n readonly auth: string | undefined;\n\n token: string | null = null;\n lease: number = 0;\n\n private sock: net.Socket | null = null;\n private renewTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(opts: DistributedSemaphoreOptions) {\n this.key = opts.key;\n this.limit = opts.limit;\n this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;\n this.leaseTtlS = opts.leaseTtlS;\n this.tls = opts.tls;\n this.auth = opts.auth;\n\n if (opts.servers) {\n if (opts.servers.length === 0) {\n throw new LockError(\"servers list must not be empty\");\n }\n this.servers = opts.servers;\n } else {\n this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];\n }\n\n this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;\n this.renewRatio = opts.renewRatio ?? 0.5;\n }\n\n private pickServer(): [string, number] {\n const idx = this.shardingStrategy(this.key, this.servers.length);\n return this.servers[idx];\n }\n\n /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */\n async acquire(): Promise<boolean> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semAcquire(\n this.sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /** Release the semaphore slot and close the connection. */\n async release(): Promise<void> {\n try {\n this.stopRenew();\n if (this.sock && this.token) {\n await semRelease(this.sock, this.key, this.token);\n }\n } finally {\n await this.close();\n }\n }\n\n /**\n * Two-phase step 1: connect and join the FIFO queue.\n * Returns `\"acquired\"` (fast-path, slot granted immediately) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n */\n async enqueue(): Promise<\"acquired\" | \"queued\"> {\n this.closed = false;\n const [host, port] = this.pickServer();\n this.sock = await connect(host, port, this.tls, this.auth);\n try {\n const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n return result.status;\n } catch (err) {\n await this.close();\n throw err;\n }\n }\n\n /**\n * Two-phase step 2: block until a semaphore slot is granted.\n * Returns `true` if granted, `false` on timeout.\n * If already acquired during `enqueue()`, returns `true` immediately.\n */\n async wait(timeoutS?: number): Promise<boolean> {\n if (this.token !== null) {\n return true;\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n const result = await semWaitForLock(this.sock, this.key, timeout);\n this.token = result.token;\n this.lease = result.lease;\n } catch (err) {\n await this.close();\n if (err instanceof AcquireTimeoutError) return false;\n throw err;\n }\n this.startRenew();\n return true;\n }\n\n /**\n * Run `fn` while holding a semaphore slot, then release automatically.\n *\n * ```ts\n * const sem = new DistributedSemaphore({ key: \"my-resource\", limit: 5 });\n * await sem.withLock(async () => {\n * // critical section (up to 5 concurrent holders)\n * });\n * ```\n */\n async withLock<T>(fn: () => T | Promise<T>): Promise<T> {\n const ok = await this.acquire();\n if (!ok) {\n throw new AcquireTimeoutError(this.key);\n }\n try {\n return await fn();\n } finally {\n await this.release();\n }\n }\n\n /** Close the underlying socket (idempotent). */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.stopRenew();\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n }\n\n // -- internals --\n\n private startRenew(): void {\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n const loop = async () => {\n if (!this.sock || !this.token) return;\n try {\n await semRenew(this.sock, this.key, this.token, this.leaseTtlS);\n } catch {\n console.error(\n `semaphore lost (renew failed): key=${this.key} token=${this.token}`,\n );\n this.token = null;\n return;\n }\n this.renewTimer = setTimeout(loop, interval);\n };\n this.renewTimer = setTimeout(loop, interval);\n }\n\n private stopRenew(): void {\n if (this.renewTimer != null) {\n clearTimeout(this.renewTimer);\n this.renewTimer = null;\n }\n }\n}\n"],"mappings":"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","names":["connect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dflockd-client",
3
- "version": "1.5.0",
3
+ "version": "1.7.2",
4
4
  "description": "TypeScript client for the dflockd distributed lock daemon",
5
5
  "type": "module",
6
6
  "exports": {