dflockd-client 1.8.1 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.cjs CHANGED
@@ -74,6 +74,14 @@ var AuthError = class extends LockError {
74
74
  function encodeLines(...lines) {
75
75
  return Buffer.from(lines.map((l) => l + "\n").join(""), "utf-8");
76
76
  }
77
+ function writeAll(sock, data) {
78
+ return new Promise((resolve, reject) => {
79
+ sock.write(data, (err) => {
80
+ if (err) reject(err);
81
+ else resolve();
82
+ });
83
+ });
84
+ }
77
85
  var _readlineBuf = /* @__PURE__ */ new WeakMap();
78
86
  function readline(sock) {
79
87
  return new Promise((resolve, reject) => {
@@ -97,10 +105,12 @@ function readline(sock) {
97
105
  };
98
106
  const onError = (err) => {
99
107
  cleanup();
108
+ _readlineBuf.delete(sock);
100
109
  reject(err);
101
110
  };
102
111
  const onClose = () => {
103
112
  cleanup();
113
+ _readlineBuf.delete(sock);
104
114
  reject(new LockError("server closed connection"));
105
115
  };
106
116
  const cleanup = () => {
@@ -129,10 +139,11 @@ async function connect2(host, port, tlsOptions, auth) {
129
139
  s.on("error", reject);
130
140
  }
131
141
  });
142
+ sock.setNoDelay(true);
132
143
  sock.on("error", () => {
133
144
  });
134
145
  if (auth != null && auth !== "") {
135
- sock.write(encodeLines("auth", "_", auth));
146
+ await writeAll(sock, encodeLines("auth", "_", auth));
136
147
  const resp = await readline(sock);
137
148
  if (resp === "ok") {
138
149
  return sock;
@@ -165,7 +176,7 @@ function stableHashShard(key, numServers) {
165
176
  }
166
177
  async function acquire(sock, key, acquireTimeoutS, leaseTtlS) {
167
178
  const arg = leaseTtlS == null ? String(acquireTimeoutS) : `${acquireTimeoutS} ${leaseTtlS}`;
168
- sock.write(encodeLines("l", key, arg));
179
+ await writeAll(sock, encodeLines("l", key, arg));
169
180
  const resp = await readline(sock);
170
181
  if (resp === "timeout") {
171
182
  throw new AcquireTimeoutError(key);
@@ -174,18 +185,15 @@ async function acquire(sock, key, acquireTimeoutS, leaseTtlS) {
174
185
  throw new LockError(`acquire failed: '${resp}'`);
175
186
  }
176
187
  const parts = resp.split(" ");
177
- if (parts.length < 2) {
178
- throw new LockError(`bad ok response: '${resp}'`);
179
- }
180
188
  const token = parts[1];
181
189
  const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
182
190
  return { token, lease };
183
191
  }
184
192
  async function renew(sock, key, token, leaseTtlS) {
185
193
  const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
186
- sock.write(encodeLines("n", key, arg));
194
+ await writeAll(sock, encodeLines("n", key, arg));
187
195
  const resp = await readline(sock);
188
- if (!resp.startsWith("ok")) {
196
+ if (resp !== "ok" && !resp.startsWith("ok ")) {
189
197
  throw new LockError(`renew failed: '${resp}'`);
190
198
  }
191
199
  const parts = resp.split(" ");
@@ -196,12 +204,12 @@ async function renew(sock, key, token, leaseTtlS) {
196
204
  }
197
205
  async function enqueue(sock, key, leaseTtlS) {
198
206
  const arg = leaseTtlS == null ? "" : String(leaseTtlS);
199
- sock.write(encodeLines("e", key, arg));
207
+ await writeAll(sock, encodeLines("e", key, arg));
200
208
  const resp = await readline(sock);
201
209
  if (resp.startsWith("acquired ")) {
202
210
  const parts = resp.split(" ");
203
211
  const token = parts[1];
204
- const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;
212
+ const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
205
213
  return { status: "acquired", token, lease };
206
214
  }
207
215
  if (resp === "queued") {
@@ -210,7 +218,7 @@ async function enqueue(sock, key, leaseTtlS) {
210
218
  throw new LockError(`enqueue failed: '${resp}'`);
211
219
  }
212
220
  async function waitForLock(sock, key, waitTimeoutS) {
213
- sock.write(encodeLines("w", key, String(waitTimeoutS)));
221
+ await writeAll(sock, encodeLines("w", key, String(waitTimeoutS)));
214
222
  const resp = await readline(sock);
215
223
  if (resp === "timeout") {
216
224
  throw new AcquireTimeoutError(key);
@@ -220,11 +228,11 @@ async function waitForLock(sock, key, waitTimeoutS) {
220
228
  }
221
229
  const parts = resp.split(" ");
222
230
  const token = parts[1];
223
- const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;
231
+ const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
224
232
  return { token, lease };
225
233
  }
226
234
  async function release(sock, key, token) {
227
- sock.write(encodeLines("r", key, token));
235
+ await writeAll(sock, encodeLines("r", key, token));
228
236
  const resp = await readline(sock);
229
237
  if (resp !== "ok") {
230
238
  throw new LockError(`release failed: '${resp}'`);
@@ -232,7 +240,7 @@ async function release(sock, key, token) {
232
240
  }
233
241
  async function semAcquire(sock, key, acquireTimeoutS, limit, leaseTtlS) {
234
242
  const arg = leaseTtlS == null ? `${acquireTimeoutS} ${limit}` : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;
235
- sock.write(encodeLines("sl", key, arg));
243
+ await writeAll(sock, encodeLines("sl", key, arg));
236
244
  const resp = await readline(sock);
237
245
  if (resp === "timeout") {
238
246
  throw new AcquireTimeoutError(key);
@@ -241,18 +249,15 @@ async function semAcquire(sock, key, acquireTimeoutS, limit, leaseTtlS) {
241
249
  throw new LockError(`sem_acquire failed: '${resp}'`);
242
250
  }
243
251
  const parts = resp.split(" ");
244
- if (parts.length < 2) {
245
- throw new LockError(`bad ok response: '${resp}'`);
246
- }
247
252
  const token = parts[1];
248
253
  const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
249
254
  return { token, lease };
250
255
  }
251
256
  async function semRenew(sock, key, token, leaseTtlS) {
252
257
  const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
253
- sock.write(encodeLines("sn", key, arg));
258
+ await writeAll(sock, encodeLines("sn", key, arg));
254
259
  const resp = await readline(sock);
255
- if (!resp.startsWith("ok")) {
260
+ if (resp !== "ok" && !resp.startsWith("ok ")) {
256
261
  throw new LockError(`sem_renew failed: '${resp}'`);
257
262
  }
258
263
  const parts = resp.split(" ");
@@ -263,7 +268,7 @@ async function semRenew(sock, key, token, leaseTtlS) {
263
268
  }
264
269
  async function semEnqueue(sock, key, limit, leaseTtlS) {
265
270
  const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;
266
- sock.write(encodeLines("se", key, arg));
271
+ await writeAll(sock, encodeLines("se", key, arg));
267
272
  const resp = await readline(sock);
268
273
  if (resp.startsWith("acquired ")) {
269
274
  const parts = resp.split(" ");
@@ -277,7 +282,7 @@ async function semEnqueue(sock, key, limit, leaseTtlS) {
277
282
  throw new LockError(`sem_enqueue failed: '${resp}'`);
278
283
  }
279
284
  async function semWaitForLock(sock, key, waitTimeoutS) {
280
- sock.write(encodeLines("sw", key, String(waitTimeoutS)));
285
+ await writeAll(sock, encodeLines("sw", key, String(waitTimeoutS)));
281
286
  const resp = await readline(sock);
282
287
  if (resp === "timeout") {
283
288
  throw new AcquireTimeoutError(key);
@@ -291,20 +296,24 @@ async function semWaitForLock(sock, key, waitTimeoutS) {
291
296
  return { token, lease };
292
297
  }
293
298
  async function semRelease(sock, key, token) {
294
- sock.write(encodeLines("sr", key, token));
299
+ await writeAll(sock, encodeLines("sr", key, token));
295
300
  const resp = await readline(sock);
296
301
  if (resp !== "ok") {
297
302
  throw new LockError(`sem_release failed: '${resp}'`);
298
303
  }
299
304
  }
300
305
  async function statsProto(sock) {
301
- sock.write(encodeLines("stats", "_", ""));
306
+ await writeAll(sock, encodeLines("stats", "_", ""));
302
307
  const resp = await readline(sock);
303
308
  if (!resp.startsWith("ok ")) {
304
309
  throw new LockError(`stats failed: '${resp}'`);
305
310
  }
306
311
  const json = resp.slice(3);
307
- return JSON.parse(json);
312
+ try {
313
+ return JSON.parse(json);
314
+ } catch {
315
+ throw new LockError(`stats: malformed JSON response: '${json}'`);
316
+ }
308
317
  }
309
318
  async function stats(options) {
310
319
  const host = options?.host ?? DEFAULT_HOST;
@@ -342,7 +351,7 @@ var DistributedLock = class {
342
351
  if (opts.servers.length === 0) {
343
352
  throw new LockError("servers list must not be empty");
344
353
  }
345
- this.servers = opts.servers;
354
+ this.servers = [...opts.servers];
346
355
  } else {
347
356
  this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];
348
357
  }
@@ -367,7 +376,7 @@ var DistributedLock = class {
367
376
  if (!opts?.force) {
368
377
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
369
378
  }
370
- await this.close();
379
+ this.close();
371
380
  }
372
381
  this.closed = false;
373
382
  const [host, port] = this.pickServer();
@@ -382,7 +391,7 @@ var DistributedLock = class {
382
391
  this.token = result.token;
383
392
  this.lease = result.lease;
384
393
  } catch (err) {
385
- await this.close();
394
+ this.close();
386
395
  if (err instanceof AcquireTimeoutError) return false;
387
396
  throw err;
388
397
  }
@@ -397,7 +406,7 @@ var DistributedLock = class {
397
406
  await release(this.sock, this.key, this.token);
398
407
  }
399
408
  } finally {
400
- await this.close();
409
+ this.close();
401
410
  }
402
411
  }
403
412
  /**
@@ -411,7 +420,7 @@ var DistributedLock = class {
411
420
  if (!opts?.force) {
412
421
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
413
422
  }
414
- await this.close();
423
+ this.close();
415
424
  }
416
425
  this.closed = false;
417
426
  const [host, port] = this.pickServer();
@@ -425,7 +434,7 @@ var DistributedLock = class {
425
434
  }
426
435
  return result.status;
427
436
  } catch (err) {
428
- await this.close();
437
+ this.close();
429
438
  throw err;
430
439
  }
431
440
  }
@@ -447,7 +456,7 @@ var DistributedLock = class {
447
456
  this.token = result.token;
448
457
  this.lease = result.lease;
449
458
  } catch (err) {
450
- await this.close();
459
+ this.close();
451
460
  if (err instanceof AcquireTimeoutError) return false;
452
461
  throw err;
453
462
  }
@@ -476,7 +485,7 @@ var DistributedLock = class {
476
485
  }
477
486
  }
478
487
  /** Close the underlying socket (idempotent). */
479
- async close() {
488
+ close() {
480
489
  if (this.closed) return;
481
490
  this.closed = true;
482
491
  this.stopRenew();
@@ -488,21 +497,26 @@ var DistributedLock = class {
488
497
  }
489
498
  // -- internals --
490
499
  startRenew() {
491
- const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
492
500
  const loop = async () => {
493
- if (!this.sock || !this.token) return;
501
+ const savedToken = this.token;
502
+ if (!this.sock || !savedToken) return;
503
+ const start = Date.now();
494
504
  try {
495
- await renew(this.sock, this.key, this.token, this.leaseTtlS);
505
+ this.lease = await renew(this.sock, this.key, savedToken, this.leaseTtlS);
496
506
  } catch {
497
- const lostToken = this.token;
498
- this.token = null;
499
- if (this.onLockLost) {
500
- this.onLockLost(this.key, lostToken);
507
+ if (this.token === savedToken) {
508
+ this.token = null;
509
+ if (this.onLockLost) {
510
+ this.onLockLost(this.key, savedToken);
511
+ }
501
512
  }
502
513
  return;
503
514
  }
504
- this.renewTimer = setTimeout(loop, interval);
515
+ const elapsed = Date.now() - start;
516
+ const interval2 = Math.max(1, this.lease * this.renewRatio) * 1e3;
517
+ this.renewTimer = setTimeout(loop, Math.max(0, interval2 - elapsed));
505
518
  };
519
+ const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
506
520
  this.renewTimer = setTimeout(loop, interval);
507
521
  }
508
522
  stopRenew() {
@@ -540,7 +554,7 @@ var DistributedSemaphore = class {
540
554
  if (opts.servers.length === 0) {
541
555
  throw new LockError("servers list must not be empty");
542
556
  }
543
- this.servers = opts.servers;
557
+ this.servers = [...opts.servers];
544
558
  } else {
545
559
  this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];
546
560
  }
@@ -565,7 +579,7 @@ var DistributedSemaphore = class {
565
579
  if (!opts?.force) {
566
580
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
567
581
  }
568
- await this.close();
582
+ this.close();
569
583
  }
570
584
  this.closed = false;
571
585
  const [host, port] = this.pickServer();
@@ -581,7 +595,7 @@ var DistributedSemaphore = class {
581
595
  this.token = result.token;
582
596
  this.lease = result.lease;
583
597
  } catch (err) {
584
- await this.close();
598
+ this.close();
585
599
  if (err instanceof AcquireTimeoutError) return false;
586
600
  throw err;
587
601
  }
@@ -596,7 +610,7 @@ var DistributedSemaphore = class {
596
610
  await semRelease(this.sock, this.key, this.token);
597
611
  }
598
612
  } finally {
599
- await this.close();
613
+ this.close();
600
614
  }
601
615
  }
602
616
  /**
@@ -610,7 +624,7 @@ var DistributedSemaphore = class {
610
624
  if (!opts?.force) {
611
625
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
612
626
  }
613
- await this.close();
627
+ this.close();
614
628
  }
615
629
  this.closed = false;
616
630
  const [host, port] = this.pickServer();
@@ -624,7 +638,7 @@ var DistributedSemaphore = class {
624
638
  }
625
639
  return result.status;
626
640
  } catch (err) {
627
- await this.close();
641
+ this.close();
628
642
  throw err;
629
643
  }
630
644
  }
@@ -646,7 +660,7 @@ var DistributedSemaphore = class {
646
660
  this.token = result.token;
647
661
  this.lease = result.lease;
648
662
  } catch (err) {
649
- await this.close();
663
+ this.close();
650
664
  if (err instanceof AcquireTimeoutError) return false;
651
665
  throw err;
652
666
  }
@@ -675,7 +689,7 @@ var DistributedSemaphore = class {
675
689
  }
676
690
  }
677
691
  /** Close the underlying socket (idempotent). */
678
- async close() {
692
+ close() {
679
693
  if (this.closed) return;
680
694
  this.closed = true;
681
695
  this.stopRenew();
@@ -687,21 +701,26 @@ var DistributedSemaphore = class {
687
701
  }
688
702
  // -- internals --
689
703
  startRenew() {
690
- const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
691
704
  const loop = async () => {
692
- if (!this.sock || !this.token) return;
705
+ const savedToken = this.token;
706
+ if (!this.sock || !savedToken) return;
707
+ const start = Date.now();
693
708
  try {
694
- await semRenew(this.sock, this.key, this.token, this.leaseTtlS);
709
+ this.lease = await semRenew(this.sock, this.key, savedToken, this.leaseTtlS);
695
710
  } catch {
696
- const lostToken = this.token;
697
- this.token = null;
698
- if (this.onLockLost) {
699
- this.onLockLost(this.key, lostToken);
711
+ if (this.token === savedToken) {
712
+ this.token = null;
713
+ if (this.onLockLost) {
714
+ this.onLockLost(this.key, savedToken);
715
+ }
700
716
  }
701
717
  return;
702
718
  }
703
- this.renewTimer = setTimeout(loop, interval);
719
+ const elapsed = Date.now() - start;
720
+ const interval2 = Math.max(1, this.lease * this.renewRatio) * 1e3;
721
+ this.renewTimer = setTimeout(loop, Math.max(0, interval2 - elapsed));
704
722
  };
723
+ const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
705
724
  this.renewTimer = setTimeout(loop, interval);
706
725
  }
707
726
  stopRenew() {
@@ -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 LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AcquireTimeoutError extends LockError {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\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 */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\n\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = _readlineBuf.get(sock) ?? \"\";\n\n // Check if a complete line is already buffered from a previous read.\n const existing = buf.indexOf(\"\\n\");\n if (existing !== -1) {\n const line = buf.slice(0, existing).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(existing + 1));\n resolve(line);\n return;\n }\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 const line = buf.slice(0, idx).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(idx + 1));\n resolve(line);\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 }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n }\n });\n\n // Prevent unhandled 'error' events from crashing the process.\n // Errors are detected through readline's close handler.\n sock.on(\"error\", () => {});\n\n if (auth != null && auth !== \"\") {\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 throw new LockError(`renew: malformed response: '${resp}'`);\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 throw new LockError(`sem_renew: malformed response: '${resp}'`);\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire the lock. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire a semaphore slot. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\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 LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AcquireTimeoutError extends LockError {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\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\nfunction writeAll(sock: net.Socket, data: Buffer): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n sock.write(data, (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\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 */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\n\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = _readlineBuf.get(sock) ?? \"\";\n\n // Check if a complete line is already buffered from a previous read.\n const existing = buf.indexOf(\"\\n\");\n if (existing !== -1) {\n const line = buf.slice(0, existing).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(existing + 1));\n resolve(line);\n return;\n }\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 const line = buf.slice(0, idx).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(idx + 1));\n resolve(line);\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n _readlineBuf.delete(sock);\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n _readlineBuf.delete(sock);\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 }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n }\n });\n\n // Disable Nagle's algorithm for lower latency on small lock commands.\n sock.setNoDelay(true);\n\n // Prevent unhandled 'error' events from crashing the process.\n // Errors are detected through readline's close handler.\n sock.on(\"error\", () => {});\n\n if (auth != null && auth !== \"\") {\n await writeAll(sock, 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 await writeAll(sock, 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 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 await writeAll(sock, encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (resp !== \"ok\" && !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 throw new LockError(`renew: malformed response: '${resp}'`);\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 await writeAll(sock, 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) : 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(`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 await writeAll(sock, 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) : 30;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n await writeAll(sock, 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 await writeAll(sock, 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 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 await writeAll(sock, encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (resp !== \"ok\" && !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 throw new LockError(`sem_renew: malformed response: '${resp}'`);\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 await writeAll(sock, 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 await writeAll(sock, 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 await writeAll(sock, 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 await writeAll(sock, 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 try {\n return JSON.parse(json) as Stats;\n } catch {\n throw new LockError(`stats: malformed JSON response: '${json}'`);\n }\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire the lock. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 close(): 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 loop = async () => {\n const savedToken = this.token;\n if (!this.sock || !savedToken) return;\n const start = Date.now();\n try {\n this.lease = await renew(this.sock, this.key, savedToken, this.leaseTtlS);\n } catch {\n // Only signal lock-lost if we still own this token (close() may have cleared it)\n if (this.token === savedToken) {\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, savedToken);\n }\n }\n return;\n }\n const elapsed = Date.now() - start;\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, Math.max(0, interval - elapsed));\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire a semaphore slot. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 close(): 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 loop = async () => {\n const savedToken = this.token;\n if (!this.sock || !savedToken) return;\n const start = Date.now();\n try {\n this.lease = await semRenew(this.sock, this.key, savedToken, this.leaseTtlS);\n } catch {\n if (this.token === savedToken) {\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, savedToken);\n }\n }\n return;\n }\n const elapsed = Date.now() - start;\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, Math.max(0, interval - elapsed));\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\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","interval"]}
package/dist/client.d.cts CHANGED
@@ -154,7 +154,7 @@ declare class DistributedLock {
154
154
  */
155
155
  withLock<T>(fn: () => T | Promise<T>): Promise<T>;
156
156
  /** Close the underlying socket (idempotent). */
157
- close(): Promise<void>;
157
+ close(): void;
158
158
  private startRenew;
159
159
  private stopRenew;
160
160
  }
@@ -228,7 +228,7 @@ declare class DistributedSemaphore {
228
228
  */
229
229
  withLock<T>(fn: () => T | Promise<T>): Promise<T>;
230
230
  /** Close the underlying socket (idempotent). */
231
- close(): Promise<void>;
231
+ close(): void;
232
232
  private startRenew;
233
233
  private stopRenew;
234
234
  }
package/dist/client.d.ts CHANGED
@@ -154,7 +154,7 @@ declare class DistributedLock {
154
154
  */
155
155
  withLock<T>(fn: () => T | Promise<T>): Promise<T>;
156
156
  /** Close the underlying socket (idempotent). */
157
- close(): Promise<void>;
157
+ close(): void;
158
158
  private startRenew;
159
159
  private stopRenew;
160
160
  }
@@ -228,7 +228,7 @@ declare class DistributedSemaphore {
228
228
  */
229
229
  withLock<T>(fn: () => T | Promise<T>): Promise<T>;
230
230
  /** Close the underlying socket (idempotent). */
231
- close(): Promise<void>;
231
+ close(): void;
232
232
  private startRenew;
233
233
  private stopRenew;
234
234
  }
package/dist/client.js CHANGED
@@ -24,6 +24,14 @@ var AuthError = class extends LockError {
24
24
  function encodeLines(...lines) {
25
25
  return Buffer.from(lines.map((l) => l + "\n").join(""), "utf-8");
26
26
  }
27
+ function writeAll(sock, data) {
28
+ return new Promise((resolve, reject) => {
29
+ sock.write(data, (err) => {
30
+ if (err) reject(err);
31
+ else resolve();
32
+ });
33
+ });
34
+ }
27
35
  var _readlineBuf = /* @__PURE__ */ new WeakMap();
28
36
  function readline(sock) {
29
37
  return new Promise((resolve, reject) => {
@@ -47,10 +55,12 @@ function readline(sock) {
47
55
  };
48
56
  const onError = (err) => {
49
57
  cleanup();
58
+ _readlineBuf.delete(sock);
50
59
  reject(err);
51
60
  };
52
61
  const onClose = () => {
53
62
  cleanup();
63
+ _readlineBuf.delete(sock);
54
64
  reject(new LockError("server closed connection"));
55
65
  };
56
66
  const cleanup = () => {
@@ -79,10 +89,11 @@ async function connect2(host, port, tlsOptions, auth) {
79
89
  s.on("error", reject);
80
90
  }
81
91
  });
92
+ sock.setNoDelay(true);
82
93
  sock.on("error", () => {
83
94
  });
84
95
  if (auth != null && auth !== "") {
85
- sock.write(encodeLines("auth", "_", auth));
96
+ await writeAll(sock, encodeLines("auth", "_", auth));
86
97
  const resp = await readline(sock);
87
98
  if (resp === "ok") {
88
99
  return sock;
@@ -115,7 +126,7 @@ function stableHashShard(key, numServers) {
115
126
  }
116
127
  async function acquire(sock, key, acquireTimeoutS, leaseTtlS) {
117
128
  const arg = leaseTtlS == null ? String(acquireTimeoutS) : `${acquireTimeoutS} ${leaseTtlS}`;
118
- sock.write(encodeLines("l", key, arg));
129
+ await writeAll(sock, encodeLines("l", key, arg));
119
130
  const resp = await readline(sock);
120
131
  if (resp === "timeout") {
121
132
  throw new AcquireTimeoutError(key);
@@ -124,18 +135,15 @@ async function acquire(sock, key, acquireTimeoutS, leaseTtlS) {
124
135
  throw new LockError(`acquire failed: '${resp}'`);
125
136
  }
126
137
  const parts = resp.split(" ");
127
- if (parts.length < 2) {
128
- throw new LockError(`bad ok response: '${resp}'`);
129
- }
130
138
  const token = parts[1];
131
139
  const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
132
140
  return { token, lease };
133
141
  }
134
142
  async function renew(sock, key, token, leaseTtlS) {
135
143
  const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
136
- sock.write(encodeLines("n", key, arg));
144
+ await writeAll(sock, encodeLines("n", key, arg));
137
145
  const resp = await readline(sock);
138
- if (!resp.startsWith("ok")) {
146
+ if (resp !== "ok" && !resp.startsWith("ok ")) {
139
147
  throw new LockError(`renew failed: '${resp}'`);
140
148
  }
141
149
  const parts = resp.split(" ");
@@ -146,12 +154,12 @@ async function renew(sock, key, token, leaseTtlS) {
146
154
  }
147
155
  async function enqueue(sock, key, leaseTtlS) {
148
156
  const arg = leaseTtlS == null ? "" : String(leaseTtlS);
149
- sock.write(encodeLines("e", key, arg));
157
+ await writeAll(sock, encodeLines("e", key, arg));
150
158
  const resp = await readline(sock);
151
159
  if (resp.startsWith("acquired ")) {
152
160
  const parts = resp.split(" ");
153
161
  const token = parts[1];
154
- const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;
162
+ const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
155
163
  return { status: "acquired", token, lease };
156
164
  }
157
165
  if (resp === "queued") {
@@ -160,7 +168,7 @@ async function enqueue(sock, key, leaseTtlS) {
160
168
  throw new LockError(`enqueue failed: '${resp}'`);
161
169
  }
162
170
  async function waitForLock(sock, key, waitTimeoutS) {
163
- sock.write(encodeLines("w", key, String(waitTimeoutS)));
171
+ await writeAll(sock, encodeLines("w", key, String(waitTimeoutS)));
164
172
  const resp = await readline(sock);
165
173
  if (resp === "timeout") {
166
174
  throw new AcquireTimeoutError(key);
@@ -170,11 +178,11 @@ async function waitForLock(sock, key, waitTimeoutS) {
170
178
  }
171
179
  const parts = resp.split(" ");
172
180
  const token = parts[1];
173
- const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 33;
181
+ const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
174
182
  return { token, lease };
175
183
  }
176
184
  async function release(sock, key, token) {
177
- sock.write(encodeLines("r", key, token));
185
+ await writeAll(sock, encodeLines("r", key, token));
178
186
  const resp = await readline(sock);
179
187
  if (resp !== "ok") {
180
188
  throw new LockError(`release failed: '${resp}'`);
@@ -182,7 +190,7 @@ async function release(sock, key, token) {
182
190
  }
183
191
  async function semAcquire(sock, key, acquireTimeoutS, limit, leaseTtlS) {
184
192
  const arg = leaseTtlS == null ? `${acquireTimeoutS} ${limit}` : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;
185
- sock.write(encodeLines("sl", key, arg));
193
+ await writeAll(sock, encodeLines("sl", key, arg));
186
194
  const resp = await readline(sock);
187
195
  if (resp === "timeout") {
188
196
  throw new AcquireTimeoutError(key);
@@ -191,18 +199,15 @@ async function semAcquire(sock, key, acquireTimeoutS, limit, leaseTtlS) {
191
199
  throw new LockError(`sem_acquire failed: '${resp}'`);
192
200
  }
193
201
  const parts = resp.split(" ");
194
- if (parts.length < 2) {
195
- throw new LockError(`bad ok response: '${resp}'`);
196
- }
197
202
  const token = parts[1];
198
203
  const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
199
204
  return { token, lease };
200
205
  }
201
206
  async function semRenew(sock, key, token, leaseTtlS) {
202
207
  const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
203
- sock.write(encodeLines("sn", key, arg));
208
+ await writeAll(sock, encodeLines("sn", key, arg));
204
209
  const resp = await readline(sock);
205
- if (!resp.startsWith("ok")) {
210
+ if (resp !== "ok" && !resp.startsWith("ok ")) {
206
211
  throw new LockError(`sem_renew failed: '${resp}'`);
207
212
  }
208
213
  const parts = resp.split(" ");
@@ -213,7 +218,7 @@ async function semRenew(sock, key, token, leaseTtlS) {
213
218
  }
214
219
  async function semEnqueue(sock, key, limit, leaseTtlS) {
215
220
  const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;
216
- sock.write(encodeLines("se", key, arg));
221
+ await writeAll(sock, encodeLines("se", key, arg));
217
222
  const resp = await readline(sock);
218
223
  if (resp.startsWith("acquired ")) {
219
224
  const parts = resp.split(" ");
@@ -227,7 +232,7 @@ async function semEnqueue(sock, key, limit, leaseTtlS) {
227
232
  throw new LockError(`sem_enqueue failed: '${resp}'`);
228
233
  }
229
234
  async function semWaitForLock(sock, key, waitTimeoutS) {
230
- sock.write(encodeLines("sw", key, String(waitTimeoutS)));
235
+ await writeAll(sock, encodeLines("sw", key, String(waitTimeoutS)));
231
236
  const resp = await readline(sock);
232
237
  if (resp === "timeout") {
233
238
  throw new AcquireTimeoutError(key);
@@ -241,20 +246,24 @@ async function semWaitForLock(sock, key, waitTimeoutS) {
241
246
  return { token, lease };
242
247
  }
243
248
  async function semRelease(sock, key, token) {
244
- sock.write(encodeLines("sr", key, token));
249
+ await writeAll(sock, encodeLines("sr", key, token));
245
250
  const resp = await readline(sock);
246
251
  if (resp !== "ok") {
247
252
  throw new LockError(`sem_release failed: '${resp}'`);
248
253
  }
249
254
  }
250
255
  async function statsProto(sock) {
251
- sock.write(encodeLines("stats", "_", ""));
256
+ await writeAll(sock, encodeLines("stats", "_", ""));
252
257
  const resp = await readline(sock);
253
258
  if (!resp.startsWith("ok ")) {
254
259
  throw new LockError(`stats failed: '${resp}'`);
255
260
  }
256
261
  const json = resp.slice(3);
257
- return JSON.parse(json);
262
+ try {
263
+ return JSON.parse(json);
264
+ } catch {
265
+ throw new LockError(`stats: malformed JSON response: '${json}'`);
266
+ }
258
267
  }
259
268
  async function stats(options) {
260
269
  const host = options?.host ?? DEFAULT_HOST;
@@ -292,7 +301,7 @@ var DistributedLock = class {
292
301
  if (opts.servers.length === 0) {
293
302
  throw new LockError("servers list must not be empty");
294
303
  }
295
- this.servers = opts.servers;
304
+ this.servers = [...opts.servers];
296
305
  } else {
297
306
  this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];
298
307
  }
@@ -317,7 +326,7 @@ var DistributedLock = class {
317
326
  if (!opts?.force) {
318
327
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
319
328
  }
320
- await this.close();
329
+ this.close();
321
330
  }
322
331
  this.closed = false;
323
332
  const [host, port] = this.pickServer();
@@ -332,7 +341,7 @@ var DistributedLock = class {
332
341
  this.token = result.token;
333
342
  this.lease = result.lease;
334
343
  } catch (err) {
335
- await this.close();
344
+ this.close();
336
345
  if (err instanceof AcquireTimeoutError) return false;
337
346
  throw err;
338
347
  }
@@ -347,7 +356,7 @@ var DistributedLock = class {
347
356
  await release(this.sock, this.key, this.token);
348
357
  }
349
358
  } finally {
350
- await this.close();
359
+ this.close();
351
360
  }
352
361
  }
353
362
  /**
@@ -361,7 +370,7 @@ var DistributedLock = class {
361
370
  if (!opts?.force) {
362
371
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
363
372
  }
364
- await this.close();
373
+ this.close();
365
374
  }
366
375
  this.closed = false;
367
376
  const [host, port] = this.pickServer();
@@ -375,7 +384,7 @@ var DistributedLock = class {
375
384
  }
376
385
  return result.status;
377
386
  } catch (err) {
378
- await this.close();
387
+ this.close();
379
388
  throw err;
380
389
  }
381
390
  }
@@ -397,7 +406,7 @@ var DistributedLock = class {
397
406
  this.token = result.token;
398
407
  this.lease = result.lease;
399
408
  } catch (err) {
400
- await this.close();
409
+ this.close();
401
410
  if (err instanceof AcquireTimeoutError) return false;
402
411
  throw err;
403
412
  }
@@ -426,7 +435,7 @@ var DistributedLock = class {
426
435
  }
427
436
  }
428
437
  /** Close the underlying socket (idempotent). */
429
- async close() {
438
+ close() {
430
439
  if (this.closed) return;
431
440
  this.closed = true;
432
441
  this.stopRenew();
@@ -438,21 +447,26 @@ var DistributedLock = class {
438
447
  }
439
448
  // -- internals --
440
449
  startRenew() {
441
- const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
442
450
  const loop = async () => {
443
- if (!this.sock || !this.token) return;
451
+ const savedToken = this.token;
452
+ if (!this.sock || !savedToken) return;
453
+ const start = Date.now();
444
454
  try {
445
- await renew(this.sock, this.key, this.token, this.leaseTtlS);
455
+ this.lease = await renew(this.sock, this.key, savedToken, this.leaseTtlS);
446
456
  } catch {
447
- const lostToken = this.token;
448
- this.token = null;
449
- if (this.onLockLost) {
450
- this.onLockLost(this.key, lostToken);
457
+ if (this.token === savedToken) {
458
+ this.token = null;
459
+ if (this.onLockLost) {
460
+ this.onLockLost(this.key, savedToken);
461
+ }
451
462
  }
452
463
  return;
453
464
  }
454
- this.renewTimer = setTimeout(loop, interval);
465
+ const elapsed = Date.now() - start;
466
+ const interval2 = Math.max(1, this.lease * this.renewRatio) * 1e3;
467
+ this.renewTimer = setTimeout(loop, Math.max(0, interval2 - elapsed));
455
468
  };
469
+ const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
456
470
  this.renewTimer = setTimeout(loop, interval);
457
471
  }
458
472
  stopRenew() {
@@ -490,7 +504,7 @@ var DistributedSemaphore = class {
490
504
  if (opts.servers.length === 0) {
491
505
  throw new LockError("servers list must not be empty");
492
506
  }
493
- this.servers = opts.servers;
507
+ this.servers = [...opts.servers];
494
508
  } else {
495
509
  this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];
496
510
  }
@@ -515,7 +529,7 @@ var DistributedSemaphore = class {
515
529
  if (!opts?.force) {
516
530
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
517
531
  }
518
- await this.close();
532
+ this.close();
519
533
  }
520
534
  this.closed = false;
521
535
  const [host, port] = this.pickServer();
@@ -531,7 +545,7 @@ var DistributedSemaphore = class {
531
545
  this.token = result.token;
532
546
  this.lease = result.lease;
533
547
  } catch (err) {
534
- await this.close();
548
+ this.close();
535
549
  if (err instanceof AcquireTimeoutError) return false;
536
550
  throw err;
537
551
  }
@@ -546,7 +560,7 @@ var DistributedSemaphore = class {
546
560
  await semRelease(this.sock, this.key, this.token);
547
561
  }
548
562
  } finally {
549
- await this.close();
563
+ this.close();
550
564
  }
551
565
  }
552
566
  /**
@@ -560,7 +574,7 @@ var DistributedSemaphore = class {
560
574
  if (!opts?.force) {
561
575
  throw new LockError("already connected; call release() or close() first, or pass { force: true }");
562
576
  }
563
- await this.close();
577
+ this.close();
564
578
  }
565
579
  this.closed = false;
566
580
  const [host, port] = this.pickServer();
@@ -574,7 +588,7 @@ var DistributedSemaphore = class {
574
588
  }
575
589
  return result.status;
576
590
  } catch (err) {
577
- await this.close();
591
+ this.close();
578
592
  throw err;
579
593
  }
580
594
  }
@@ -596,7 +610,7 @@ var DistributedSemaphore = class {
596
610
  this.token = result.token;
597
611
  this.lease = result.lease;
598
612
  } catch (err) {
599
- await this.close();
613
+ this.close();
600
614
  if (err instanceof AcquireTimeoutError) return false;
601
615
  throw err;
602
616
  }
@@ -625,7 +639,7 @@ var DistributedSemaphore = class {
625
639
  }
626
640
  }
627
641
  /** Close the underlying socket (idempotent). */
628
- async close() {
642
+ close() {
629
643
  if (this.closed) return;
630
644
  this.closed = true;
631
645
  this.stopRenew();
@@ -637,21 +651,26 @@ var DistributedSemaphore = class {
637
651
  }
638
652
  // -- internals --
639
653
  startRenew() {
640
- const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
641
654
  const loop = async () => {
642
- if (!this.sock || !this.token) return;
655
+ const savedToken = this.token;
656
+ if (!this.sock || !savedToken) return;
657
+ const start = Date.now();
643
658
  try {
644
- await semRenew(this.sock, this.key, this.token, this.leaseTtlS);
659
+ this.lease = await semRenew(this.sock, this.key, savedToken, this.leaseTtlS);
645
660
  } catch {
646
- const lostToken = this.token;
647
- this.token = null;
648
- if (this.onLockLost) {
649
- this.onLockLost(this.key, lostToken);
661
+ if (this.token === savedToken) {
662
+ this.token = null;
663
+ if (this.onLockLost) {
664
+ this.onLockLost(this.key, savedToken);
665
+ }
650
666
  }
651
667
  return;
652
668
  }
653
- this.renewTimer = setTimeout(loop, interval);
669
+ const elapsed = Date.now() - start;
670
+ const interval2 = Math.max(1, this.lease * this.renewRatio) * 1e3;
671
+ this.renewTimer = setTimeout(loop, Math.max(0, interval2 - elapsed));
654
672
  };
673
+ const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
655
674
  this.renewTimer = setTimeout(loop, interval);
656
675
  }
657
676
  stopRenew() {
@@ -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 LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AcquireTimeoutError extends LockError {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\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 */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\n\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = _readlineBuf.get(sock) ?? \"\";\n\n // Check if a complete line is already buffered from a previous read.\n const existing = buf.indexOf(\"\\n\");\n if (existing !== -1) {\n const line = buf.slice(0, existing).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(existing + 1));\n resolve(line);\n return;\n }\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 const line = buf.slice(0, idx).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(idx + 1));\n resolve(line);\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 }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n }\n });\n\n // Prevent unhandled 'error' events from crashing the process.\n // Errors are detected through readline's close handler.\n sock.on(\"error\", () => {});\n\n if (auth != null && auth !== \"\") {\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 throw new LockError(`renew: malformed response: '${resp}'`);\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 throw new LockError(`sem_renew: malformed response: '${resp}'`);\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire the lock. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire a semaphore slot. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n await this.close();\n }\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 const lostToken = this.token!;\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, lostToken);\n }\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 LockError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LockError\";\n }\n}\n\nexport class AcquireTimeoutError extends LockError {\n constructor(key: string) {\n super(`timeout acquiring '${key}'`);\n this.name = \"AcquireTimeoutError\";\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\nfunction writeAll(sock: net.Socket, data: Buffer): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n sock.write(data, (err) => {\n if (err) reject(err);\n else resolve();\n });\n });\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 */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\n\nfunction readline(sock: net.Socket): Promise<string> {\n return new Promise((resolve, reject) => {\n let buf = _readlineBuf.get(sock) ?? \"\";\n\n // Check if a complete line is already buffered from a previous read.\n const existing = buf.indexOf(\"\\n\");\n if (existing !== -1) {\n const line = buf.slice(0, existing).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(existing + 1));\n resolve(line);\n return;\n }\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 const line = buf.slice(0, idx).replace(/\\r$/, \"\");\n _readlineBuf.set(sock, buf.slice(idx + 1));\n resolve(line);\n }\n };\n\n const onError = (err: Error) => {\n cleanup();\n _readlineBuf.delete(sock);\n reject(err);\n };\n\n const onClose = () => {\n cleanup();\n _readlineBuf.delete(sock);\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 }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n } else {\n const s = net.createConnection({ host, port }, () => {\n s.removeListener(\"error\", reject);\n resolve(s);\n });\n s.on(\"error\", reject);\n }\n });\n\n // Disable Nagle's algorithm for lower latency on small lock commands.\n sock.setNoDelay(true);\n\n // Prevent unhandled 'error' events from crashing the process.\n // Errors are detected through readline's close handler.\n sock.on(\"error\", () => {});\n\n if (auth != null && auth !== \"\") {\n await writeAll(sock, 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 await writeAll(sock, 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 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 await writeAll(sock, encodeLines(\"n\", key, arg));\n\n const resp = await readline(sock);\n if (resp !== \"ok\" && !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 throw new LockError(`renew: malformed response: '${resp}'`);\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 await writeAll(sock, 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) : 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(`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 await writeAll(sock, 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) : 30;\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n await writeAll(sock, 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 await writeAll(sock, 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 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 await writeAll(sock, encodeLines(\"sn\", key, arg));\n\n const resp = await readline(sock);\n if (resp !== \"ok\" && !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 throw new LockError(`sem_renew: malformed response: '${resp}'`);\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 await writeAll(sock, 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 await writeAll(sock, 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 await writeAll(sock, 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 await writeAll(sock, 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 try {\n return JSON.parse(json) as Stats;\n } catch {\n throw new LockError(`stats: malformed JSON response: '${json}'`);\n }\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire the lock. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 close(): 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 loop = async () => {\n const savedToken = this.token;\n if (!this.sock || !savedToken) return;\n const start = Date.now();\n try {\n this.lease = await renew(this.sock, this.key, savedToken, this.leaseTtlS);\n } catch {\n // Only signal lock-lost if we still own this token (close() may have cleared it)\n if (this.token === savedToken) {\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, savedToken);\n }\n }\n return;\n }\n const elapsed = Date.now() - start;\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, Math.max(0, interval - elapsed));\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\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 onLockLost?: (key: string, token: string) => void;\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 readonly onLockLost: ((key: string, token: string) => void) | 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 this.onLockLost = opts.onLockLost;\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 if (!Number.isInteger(idx) || idx < 0 || idx >= this.servers.length) {\n throw new LockError(\n `shardingStrategy returned invalid index ${idx} for ${this.servers.length} server(s)`,\n );\n }\n return this.servers[idx];\n }\n\n /**\n * Acquire a semaphore slot. Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection before acquiring. Defaults to `false`, which throws if already connected.\n */\n async acquire(opts?: { force?: boolean }): Promise<boolean> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 * @param opts.force - If `true`, silently close any existing connection before enqueuing. Defaults to `false`, which throws if already connected.\n */\n async enqueue(opts?: { force?: boolean }): Promise<\"acquired\" | \"queued\"> {\n if (this.sock && !this.closed) {\n if (!opts?.force) {\n throw new LockError(\"already connected; call release() or close() first, or pass { force: true }\");\n }\n this.close();\n }\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 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 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 close(): 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 loop = async () => {\n const savedToken = this.token;\n if (!this.sock || !savedToken) return;\n const start = Date.now();\n try {\n this.lease = await semRenew(this.sock, this.key, savedToken, this.leaseTtlS);\n } catch {\n if (this.token === savedToken) {\n this.token = null;\n if (this.onLockLost) {\n this.onLockLost(this.key, savedToken);\n }\n }\n return;\n }\n const elapsed = Date.now() - start;\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, Math.max(0, interval - elapsed));\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\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","interval"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dflockd-client",
3
- "version": "1.8.1",
3
+ "version": "1.8.2",
4
4
  "description": "TypeScript client for the dflockd distributed lock daemon",
5
5
  "type": "module",
6
6
  "exports": {