dflockd-client 1.9.0 → 1.9.1
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 +74 -128
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +74 -128
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
package/dist/client.cjs
CHANGED
|
@@ -161,6 +161,11 @@ function readline(sock) {
|
|
|
161
161
|
sock.removeListener("close", onClose);
|
|
162
162
|
sock.removeListener("end", onEnd);
|
|
163
163
|
};
|
|
164
|
+
if (sock.readableEnded || sock.destroyed) {
|
|
165
|
+
_readlineBuf.delete(sock);
|
|
166
|
+
reject(new LockError("server closed connection"));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
164
169
|
sock.on("data", onData);
|
|
165
170
|
sock.on("error", onError);
|
|
166
171
|
sock.on("close", onClose);
|
|
@@ -254,199 +259,134 @@ function stableHashShard(key, numServers) {
|
|
|
254
259
|
}
|
|
255
260
|
return crc32(Buffer.from(key, "utf-8")) % numServers;
|
|
256
261
|
}
|
|
257
|
-
async function
|
|
262
|
+
async function protoAcquire(sock, cmd, label, key, acquireTimeoutS, leaseTtlS, limit) {
|
|
258
263
|
validateKey(key);
|
|
259
264
|
if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {
|
|
260
265
|
throw new LockError("acquireTimeoutS must be a finite number >= 0");
|
|
261
266
|
}
|
|
267
|
+
if (limit != null && (!Number.isInteger(limit) || limit < 1)) {
|
|
268
|
+
throw new LockError("limit must be an integer >= 1");
|
|
269
|
+
}
|
|
262
270
|
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
263
271
|
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
264
272
|
}
|
|
265
|
-
const
|
|
266
|
-
|
|
273
|
+
const parts = [acquireTimeoutS];
|
|
274
|
+
if (limit != null) parts.push(limit);
|
|
275
|
+
if (leaseTtlS != null) parts.push(leaseTtlS);
|
|
276
|
+
await writeAll(sock, encodeLines(cmd, key, parts.join(" ")));
|
|
267
277
|
const resp = await readline(sock);
|
|
268
278
|
if (resp === "timeout") {
|
|
269
279
|
throw new AcquireTimeoutError(key);
|
|
270
280
|
}
|
|
271
281
|
if (!resp.startsWith("ok ")) {
|
|
272
|
-
throw new LockError(
|
|
282
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
273
283
|
}
|
|
274
|
-
const
|
|
275
|
-
const token =
|
|
284
|
+
const respParts = resp.split(" ");
|
|
285
|
+
const token = respParts[1];
|
|
276
286
|
if (!token) {
|
|
277
|
-
throw new LockError(
|
|
287
|
+
throw new LockError(`${label}: server returned no token: '${resp}'`);
|
|
278
288
|
}
|
|
279
|
-
|
|
280
|
-
return { token, lease };
|
|
289
|
+
return { token, lease: parseLease(respParts[2]) };
|
|
281
290
|
}
|
|
282
|
-
async function
|
|
291
|
+
async function protoRenew(sock, cmd, label, key, token, leaseTtlS) {
|
|
283
292
|
validateKey(key);
|
|
284
293
|
validateToken(token);
|
|
285
294
|
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
286
295
|
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
287
296
|
}
|
|
288
297
|
const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
|
|
289
|
-
await writeAll(sock, encodeLines(
|
|
298
|
+
await writeAll(sock, encodeLines(cmd, key, arg));
|
|
290
299
|
const resp = await readline(sock);
|
|
291
300
|
if (resp !== "ok" && !resp.startsWith("ok ")) {
|
|
292
|
-
throw new LockError(
|
|
293
|
-
}
|
|
294
|
-
if (resp === "ok") {
|
|
295
|
-
return leaseTtlS ?? 30;
|
|
301
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
296
302
|
}
|
|
303
|
+
if (resp === "ok") return leaseTtlS ?? 30;
|
|
297
304
|
return parseLease(resp.split(" ")[1]);
|
|
298
305
|
}
|
|
299
|
-
async function
|
|
306
|
+
async function protoEnqueue(sock, cmd, label, key, leaseTtlS, limit) {
|
|
300
307
|
validateKey(key);
|
|
308
|
+
if (limit != null && (!Number.isInteger(limit) || limit < 1)) {
|
|
309
|
+
throw new LockError("limit must be an integer >= 1");
|
|
310
|
+
}
|
|
301
311
|
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
302
312
|
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
303
313
|
}
|
|
304
|
-
const
|
|
305
|
-
|
|
314
|
+
const parts = [];
|
|
315
|
+
if (limit != null) parts.push(limit);
|
|
316
|
+
if (leaseTtlS != null) parts.push(leaseTtlS);
|
|
317
|
+
await writeAll(sock, encodeLines(cmd, key, parts.join(" ")));
|
|
306
318
|
const resp = await readline(sock);
|
|
307
319
|
if (resp.startsWith("acquired ")) {
|
|
308
|
-
const
|
|
309
|
-
const token =
|
|
320
|
+
const respParts = resp.split(" ");
|
|
321
|
+
const token = respParts[1];
|
|
310
322
|
if (!token) {
|
|
311
|
-
throw new LockError(
|
|
323
|
+
throw new LockError(`${label}: server returned no token: '${resp}'`);
|
|
312
324
|
}
|
|
313
|
-
|
|
314
|
-
return { status: "acquired", token, lease };
|
|
325
|
+
return { status: "acquired", token, lease: parseLease(respParts[2]) };
|
|
315
326
|
}
|
|
316
327
|
if (resp === "queued") {
|
|
317
328
|
return { status: "queued", token: null, lease: null };
|
|
318
329
|
}
|
|
319
|
-
throw new LockError(
|
|
330
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
320
331
|
}
|
|
321
|
-
async function
|
|
332
|
+
async function protoWait(sock, cmd, label, key, waitTimeoutS) {
|
|
322
333
|
validateKey(key);
|
|
323
334
|
if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {
|
|
324
335
|
throw new LockError("waitTimeoutS must be a finite number >= 0");
|
|
325
336
|
}
|
|
326
|
-
await writeAll(sock, encodeLines(
|
|
337
|
+
await writeAll(sock, encodeLines(cmd, key, String(waitTimeoutS)));
|
|
327
338
|
const resp = await readline(sock);
|
|
328
339
|
if (resp === "timeout") {
|
|
329
340
|
throw new AcquireTimeoutError(key);
|
|
330
341
|
}
|
|
331
342
|
if (!resp.startsWith("ok ")) {
|
|
332
|
-
throw new LockError(
|
|
343
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
333
344
|
}
|
|
334
|
-
const
|
|
335
|
-
const token =
|
|
345
|
+
const respParts = resp.split(" ");
|
|
346
|
+
const token = respParts[1];
|
|
336
347
|
if (!token) {
|
|
337
|
-
throw new LockError(
|
|
348
|
+
throw new LockError(`${label}: server returned no token: '${resp}'`);
|
|
338
349
|
}
|
|
339
|
-
|
|
340
|
-
return { token, lease };
|
|
350
|
+
return { token, lease: parseLease(respParts[2]) };
|
|
341
351
|
}
|
|
342
|
-
async function
|
|
352
|
+
async function protoRelease(sock, cmd, label, key, token) {
|
|
343
353
|
validateKey(key);
|
|
344
354
|
validateToken(token);
|
|
345
|
-
await writeAll(sock, encodeLines(
|
|
355
|
+
await writeAll(sock, encodeLines(cmd, key, token));
|
|
346
356
|
const resp = await readline(sock);
|
|
347
357
|
if (resp !== "ok") {
|
|
348
|
-
throw new LockError(
|
|
358
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
349
359
|
}
|
|
350
360
|
}
|
|
361
|
+
async function acquire(sock, key, acquireTimeoutS, leaseTtlS) {
|
|
362
|
+
return protoAcquire(sock, "l", "acquire", key, acquireTimeoutS, leaseTtlS);
|
|
363
|
+
}
|
|
364
|
+
async function renew(sock, key, token, leaseTtlS) {
|
|
365
|
+
return protoRenew(sock, "n", "renew", key, token, leaseTtlS);
|
|
366
|
+
}
|
|
367
|
+
async function enqueue(sock, key, leaseTtlS) {
|
|
368
|
+
return protoEnqueue(sock, "e", "enqueue", key, leaseTtlS);
|
|
369
|
+
}
|
|
370
|
+
async function waitForLock(sock, key, waitTimeoutS) {
|
|
371
|
+
return protoWait(sock, "w", "wait", key, waitTimeoutS);
|
|
372
|
+
}
|
|
373
|
+
async function release(sock, key, token) {
|
|
374
|
+
return protoRelease(sock, "r", "release", key, token);
|
|
375
|
+
}
|
|
351
376
|
async function semAcquire(sock, key, acquireTimeoutS, limit, leaseTtlS) {
|
|
352
|
-
|
|
353
|
-
if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {
|
|
354
|
-
throw new LockError("acquireTimeoutS must be a finite number >= 0");
|
|
355
|
-
}
|
|
356
|
-
if (!Number.isInteger(limit) || limit < 1) {
|
|
357
|
-
throw new LockError("limit must be an integer >= 1");
|
|
358
|
-
}
|
|
359
|
-
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
360
|
-
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
361
|
-
}
|
|
362
|
-
const arg = leaseTtlS == null ? `${acquireTimeoutS} ${limit}` : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;
|
|
363
|
-
await writeAll(sock, encodeLines("sl", key, arg));
|
|
364
|
-
const resp = await readline(sock);
|
|
365
|
-
if (resp === "timeout") {
|
|
366
|
-
throw new AcquireTimeoutError(key);
|
|
367
|
-
}
|
|
368
|
-
if (!resp.startsWith("ok ")) {
|
|
369
|
-
throw new LockError(`sem_acquire failed: '${resp}'`);
|
|
370
|
-
}
|
|
371
|
-
const parts = resp.split(" ");
|
|
372
|
-
const token = parts[1];
|
|
373
|
-
if (!token) {
|
|
374
|
-
throw new LockError(`sem_acquire: server returned no token: '${resp}'`);
|
|
375
|
-
}
|
|
376
|
-
const lease = parseLease(parts[2]);
|
|
377
|
-
return { token, lease };
|
|
377
|
+
return protoAcquire(sock, "sl", "sem_acquire", key, acquireTimeoutS, leaseTtlS, limit);
|
|
378
378
|
}
|
|
379
379
|
async function semRenew(sock, key, token, leaseTtlS) {
|
|
380
|
-
|
|
381
|
-
validateToken(token);
|
|
382
|
-
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
383
|
-
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
384
|
-
}
|
|
385
|
-
const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
|
|
386
|
-
await writeAll(sock, encodeLines("sn", key, arg));
|
|
387
|
-
const resp = await readline(sock);
|
|
388
|
-
if (resp !== "ok" && !resp.startsWith("ok ")) {
|
|
389
|
-
throw new LockError(`sem_renew failed: '${resp}'`);
|
|
390
|
-
}
|
|
391
|
-
if (resp === "ok") {
|
|
392
|
-
return leaseTtlS ?? 30;
|
|
393
|
-
}
|
|
394
|
-
return parseLease(resp.split(" ")[1]);
|
|
380
|
+
return protoRenew(sock, "sn", "sem_renew", key, token, leaseTtlS);
|
|
395
381
|
}
|
|
396
382
|
async function semEnqueue(sock, key, limit, leaseTtlS) {
|
|
397
|
-
|
|
398
|
-
if (!Number.isInteger(limit) || limit < 1) {
|
|
399
|
-
throw new LockError("limit must be an integer >= 1");
|
|
400
|
-
}
|
|
401
|
-
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
402
|
-
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
403
|
-
}
|
|
404
|
-
const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;
|
|
405
|
-
await writeAll(sock, encodeLines("se", key, arg));
|
|
406
|
-
const resp = await readline(sock);
|
|
407
|
-
if (resp.startsWith("acquired ")) {
|
|
408
|
-
const parts = resp.split(" ");
|
|
409
|
-
const token = parts[1];
|
|
410
|
-
if (!token) {
|
|
411
|
-
throw new LockError(`sem_enqueue: server returned no token: '${resp}'`);
|
|
412
|
-
}
|
|
413
|
-
const lease = parseLease(parts[2]);
|
|
414
|
-
return { status: "acquired", token, lease };
|
|
415
|
-
}
|
|
416
|
-
if (resp === "queued") {
|
|
417
|
-
return { status: "queued", token: null, lease: null };
|
|
418
|
-
}
|
|
419
|
-
throw new LockError(`sem_enqueue failed: '${resp}'`);
|
|
383
|
+
return protoEnqueue(sock, "se", "sem_enqueue", key, leaseTtlS, limit);
|
|
420
384
|
}
|
|
421
385
|
async function semWaitForLock(sock, key, waitTimeoutS) {
|
|
422
|
-
|
|
423
|
-
if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {
|
|
424
|
-
throw new LockError("waitTimeoutS must be a finite number >= 0");
|
|
425
|
-
}
|
|
426
|
-
await writeAll(sock, encodeLines("sw", key, String(waitTimeoutS)));
|
|
427
|
-
const resp = await readline(sock);
|
|
428
|
-
if (resp === "timeout") {
|
|
429
|
-
throw new AcquireTimeoutError(key);
|
|
430
|
-
}
|
|
431
|
-
if (!resp.startsWith("ok ")) {
|
|
432
|
-
throw new LockError(`sem_wait failed: '${resp}'`);
|
|
433
|
-
}
|
|
434
|
-
const parts = resp.split(" ");
|
|
435
|
-
const token = parts[1];
|
|
436
|
-
if (!token) {
|
|
437
|
-
throw new LockError(`sem_wait: server returned no token: '${resp}'`);
|
|
438
|
-
}
|
|
439
|
-
const lease = parseLease(parts[2]);
|
|
440
|
-
return { token, lease };
|
|
386
|
+
return protoWait(sock, "sw", "sem_wait", key, waitTimeoutS);
|
|
441
387
|
}
|
|
442
388
|
async function semRelease(sock, key, token) {
|
|
443
|
-
|
|
444
|
-
validateToken(token);
|
|
445
|
-
await writeAll(sock, encodeLines("sr", key, token));
|
|
446
|
-
const resp = await readline(sock);
|
|
447
|
-
if (resp !== "ok") {
|
|
448
|
-
throw new LockError(`sem_release failed: '${resp}'`);
|
|
449
|
-
}
|
|
389
|
+
return protoRelease(sock, "sr", "sem_release", key, token);
|
|
450
390
|
}
|
|
451
391
|
async function statsProto(sock) {
|
|
452
392
|
await writeAll(sock, encodeLines("stats", "_", ""));
|
|
@@ -604,7 +544,10 @@ var DistributedPrimitive = class {
|
|
|
604
544
|
try {
|
|
605
545
|
this.stopRenew();
|
|
606
546
|
if (this.renewInFlight) {
|
|
607
|
-
await
|
|
547
|
+
await Promise.race([
|
|
548
|
+
this.renewInFlight,
|
|
549
|
+
new Promise((r) => setTimeout(r, 5e3))
|
|
550
|
+
]);
|
|
608
551
|
this.stopRenew();
|
|
609
552
|
}
|
|
610
553
|
if (sockToRelease != null && tokenToRelease != null) {
|
|
@@ -639,11 +582,11 @@ var DistributedPrimitive = class {
|
|
|
639
582
|
this.suspendSocketTimeout(this.sock);
|
|
640
583
|
const result = await this.doEnqueue(this.sock);
|
|
641
584
|
if (result.status === "acquired") {
|
|
642
|
-
this.restoreSocketTimeout(this.sock);
|
|
643
585
|
this.token = result.token;
|
|
644
586
|
this.lease = result.lease ?? 0;
|
|
645
587
|
this.startRenew();
|
|
646
588
|
}
|
|
589
|
+
this.restoreSocketTimeout(this.sock);
|
|
647
590
|
return result.status;
|
|
648
591
|
} catch (err) {
|
|
649
592
|
this.close();
|
|
@@ -728,7 +671,10 @@ var DistributedPrimitive = class {
|
|
|
728
671
|
const start = Date.now();
|
|
729
672
|
const p = (async () => {
|
|
730
673
|
try {
|
|
731
|
-
|
|
674
|
+
const newLease = await this.doRenew(sock, savedToken);
|
|
675
|
+
if (this.token === savedToken && !this.closed) {
|
|
676
|
+
this.lease = newLease;
|
|
677
|
+
}
|
|
732
678
|
} catch {
|
|
733
679
|
if (this.token === savedToken) {
|
|
734
680
|
this.token = null;
|
package/dist/client.cjs.map
CHANGED
|
@@ -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// Validation helpers\n// ---------------------------------------------------------------------------\n\nfunction validateKey(key: string): void {\n if (key === \"\") {\n throw new LockError(\"key must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(key)) {\n throw new LockError(\n \"key must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateAuth(auth: string): void {\n if (/[\\0\\n\\r]/.test(auth)) {\n throw new LockError(\n \"auth token must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateToken(token: string): void {\n if (token === \"\") {\n throw new LockError(\"token must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(token)) {\n throw new LockError(\n \"token must not contain NUL, newline, or carriage return characters\",\n );\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 * Parse a lease value from a server response part.\n * Returns `fallback` when the value is missing, empty, or non-numeric.\n */\nfunction parseLease(value: string | undefined, fallback: number = 30): number {\n if (value == null || value === \"\") return fallback;\n const n = Number(value);\n return Number.isFinite(n) && n >= 0 ? n : fallback;\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 *\n * NOTE: Concurrent calls to readline() on the same socket are unsafe —\n * callers must serialize reads (the request-response protocol naturally\n * enforces this).\n */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\nconst MAX_LINE_LENGTH = 1024 * 1024; // 1 MB\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 } else if (buf.length > MAX_LINE_LENGTH) {\n cleanup();\n _readlineBuf.delete(sock);\n reject(new LockError(\"server response exceeded maximum line length\"));\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 onEnd = () => {\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 sock.removeListener(\"end\", onEnd);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n sock.on(\"end\", onEnd);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n connectTimeoutMs?: number,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let settled = false;\n\n // tls.connect() registers the callback on \"secureConnect\", not \"connect\".\n const connectEvent = tlsOptions ? \"secureConnect\" : \"connect\";\n\n const onConnect = () => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(\"error\", onError);\n resolve(s);\n };\n\n const onError = (err: Error) => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(err);\n };\n\n let s: net.Socket;\n if (tlsOptions) {\n s = tls.connect({ ...tlsOptions, host, port }, onConnect);\n } else {\n s = net.createConnection({ host, port }, onConnect);\n }\n s.on(\"error\", onError);\n\n if (connectTimeoutMs != null && connectTimeoutMs > 0) {\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n s.removeListener(\"error\", onError);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(\n new LockError(\n `connect timed out after ${connectTimeoutMs}ms to ${host}:${port}`,\n ),\n );\n }, connectTimeoutMs);\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 validateAuth(auth);\n let resp: string;\n try {\n await writeAll(sock, encodeLines(\"auth\", \"_\", auth));\n resp = await readline(sock);\n } catch (err) {\n sock.destroy();\n throw err;\n }\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 if (numServers <= 0) {\n throw new LockError(\"numServers must be greater than 0\");\n }\n return crc32(Buffer.from(key, \"utf-8\")) % 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 validateKey(key);\n if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 if (!token) {\n throw new LockError(`acquire: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n validateToken(token);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 // Bare \"ok\" — server confirmed renewal but didn't echo the lease.\n if (resp === \"ok\") {\n return leaseTtlS ?? 30;\n }\n\n return parseLease(resp.split(\" \")[1]);\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n validateKey(key);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n // Lease TTL is optional for enqueue; empty arg means \"use server default\".\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 if (!token) {\n throw new LockError(`enqueue: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {\n throw new LockError(\"waitTimeoutS must be a finite number >= 0\");\n }\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 if (!token) {\n throw new LockError(`wait: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n validateKey(key);\n validateToken(token);\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 validateKey(key);\n if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (!Number.isInteger(limit) || limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 if (!token) {\n throw new LockError(`sem_acquire: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n validateToken(token);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 // Bare \"ok\" — server confirmed renewal but didn't echo the lease.\n if (resp === \"ok\") {\n return leaseTtlS ?? 30;\n }\n\n return parseLease(resp.split(\" \")[1]);\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n validateKey(key);\n if (!Number.isInteger(limit) || limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 if (!token) {\n throw new LockError(`sem_enqueue: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {\n throw new LockError(\"waitTimeoutS must be a finite number >= 0\");\n }\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 if (!token) {\n throw new LockError(`sem_wait: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n validateKey(key);\n validateToken(token);\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?: {\n host?: string;\n port?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n connectTimeoutMs?: number;\n },\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, options?.connectTimeoutMs);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Option interfaces\n// ---------------------------------------------------------------------------\n\ninterface BaseOptions {\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 /** TCP connect timeout in milliseconds. Undefined means no timeout. */\n connectTimeoutMs?: number;\n /**\n * Socket idle timeout in milliseconds. If no data is received within this\n * period, the socket emits a 'timeout' event and is destroyed, causing any\n * pending `readline` to reject. Undefined means no timeout.\n */\n socketTimeoutMs?: number;\n}\n\nexport interface DistributedLockOptions extends BaseOptions {}\n\nexport interface DistributedSemaphoreOptions extends BaseOptions {\n limit: number;\n}\n\n// ---------------------------------------------------------------------------\n// Shared base class\n// ---------------------------------------------------------------------------\n\nabstract class DistributedPrimitive {\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 readonly connectTimeoutMs: number | undefined;\n readonly socketTimeoutMs: number | 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 renewInFlight: Promise<void> | null = null;\n private closed = false;\n\n constructor(opts: BaseOptions) {\n validateKey(opts.key);\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 this.connectTimeoutMs = opts.connectTimeoutMs;\n this.socketTimeoutMs = opts.socketTimeoutMs;\n\n if (!Number.isFinite(this.acquireTimeoutS) || this.acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (this.leaseTtlS != null && (!Number.isFinite(this.leaseTtlS) || this.leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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\n const renewRatio = opts.renewRatio ?? 0.5;\n if (!Number.isFinite(renewRatio) || renewRatio <= 0 || renewRatio >= 1) {\n throw new LockError(\"renewRatio must be a finite number between 0 and 1 (exclusive)\");\n }\n this.renewRatio = renewRatio;\n }\n\n // -- abstract protocol hooks (implemented by subclasses) --\n\n protected abstract doAcquire(\n sock: net.Socket,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doEnqueue(\n sock: net.Socket,\n ): Promise<{\n status: \"acquired\" | \"queued\";\n token: string | null;\n lease: number | null;\n }>;\n\n protected abstract doWait(\n sock: net.Socket,\n timeoutS: number,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doRelease(\n sock: net.Socket,\n token: string,\n ): Promise<void>;\n\n protected abstract doRenew(\n sock: net.Socket,\n token: string,\n ): Promise<number>;\n\n // -- public API --\n\n private async openConnection(): Promise<net.Socket> {\n const [host, port] = this.pickServer();\n const sock = await connect(host, port, this.tls, this.auth, this.connectTimeoutMs);\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n // Register the listener once; use setTimeout(ms)/setTimeout(0) to\n // toggle without accumulating duplicate listeners.\n sock.on(\"timeout\", () => {\n sock.destroy(new LockError(\"socket idle timeout\"));\n });\n sock.setTimeout(this.socketTimeoutMs);\n }\n return sock;\n }\n\n /**\n * Suspend or restore the socket idle timeout. Only has effect when\n * `socketTimeoutMs` was set at construction time and a listener was\n * registered in `openConnection`.\n */\n private suspendSocketTimeout(sock: net.Socket): void {\n sock.setTimeout(0);\n }\n\n private restoreSocketTimeout(sock: net.Socket): void {\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n sock.setTimeout(this.socketTimeoutMs);\n }\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 / semaphore slot.\n * Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n // Suspend socket idle timeout during the blocking acquire — the server\n // won't send data until the lock is granted or the acquire times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doAcquire(this.sock);\n this.restoreSocketTimeout(this.sock);\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 * Release the lock / semaphore slot and close the connection.\n *\n * Throws `LockError` if the instance is already closed (e.g. after a\n * previous `release()` or `close()` call).\n *\n * The server-side release itself is best-effort: if the underlying\n * connection is already dead the protocol-level release error is silently\n * ignored so that the method doesn't throw on transient network failures.\n */\n async release(): Promise<void> {\n if (this.closed) {\n throw new LockError(\"not connected; nothing to release\");\n }\n // Capture the token and socket before awaiting anything — a concurrent\n // renew failure can set this.token/this.sock to null (via onLockLost →\n // close()), which would cause us to skip the server-side release and\n // leave the lock held until lease expiry.\n const tokenToRelease = this.token;\n const sockToRelease = this.sock;\n try {\n this.stopRenew();\n // Wait for any in-flight renew to finish before sending the release\n // command — concurrent reads/writes on the same socket are unsafe.\n if (this.renewInFlight) {\n await this.renewInFlight;\n // The loop resumes before us (it registered its .then first) and may\n // have scheduled a new timer. Clear it so it cannot fire during\n // doRelease below.\n this.stopRenew();\n }\n if (sockToRelease != null && tokenToRelease != null) {\n try {\n await this.doRelease(sockToRelease, tokenToRelease);\n } catch {\n // Best-effort: connection may already be dead.\n }\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) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n this.suspendSocketTimeout(this.sock);\n const result = await this.doEnqueue(this.sock);\n if (result.status === \"acquired\") {\n this.restoreSocketTimeout(this.sock);\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 / 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 // Already acquired during enqueue (fast path)\n return true;\n }\n if (this.closed) {\n throw new LockError(\"connection closed; call enqueue() again\");\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n // Suspend socket idle timeout during the blocking wait — the server\n // won't send data until the lock/slot is granted or the wait times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doWait(this.sock, timeout);\n this.restoreSocketTimeout(this.sock);\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 / slot, then release automatically.\n *\n * If `fn()` throws, its error is always preserved — a concurrent\n * release failure will not mask it.\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 let threw = false;\n try {\n return await fn();\n } catch (err) {\n threw = true;\n throw err;\n } finally {\n try {\n await this.release();\n } catch (releaseErr) {\n // If fn() already threw, swallow the release error so it\n // doesn't mask the original error.\n if (!threw) throw releaseErr;\n }\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 this.renewInFlight = null;\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n this.lease = 0;\n }\n\n // -- internals --\n\n private startRenew(): void {\n this.stopRenew();\n const loop = async () => {\n const savedToken = this.token;\n const sock = this.sock;\n if (!sock || !savedToken) return;\n const start = Date.now();\n const p = (async () => {\n try {\n this.lease = await this.doRenew(sock, savedToken);\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 try {\n // Cast to unknown: the declared return type is void, but an\n // async callback will return a Promise at runtime.\n const result: unknown = this.onLockLost(this.key, savedToken);\n // Guard against async callbacks returning a rejected Promise.\n if (result instanceof Promise) {\n result.catch(() => {});\n }\n } catch {\n // Never let a user callback crash the process.\n }\n }\n // Tear down the connection so the instance is in a clean state\n // for re-acquisition. Without this the socket leaks and a\n // subsequent acquire() without { force: true } would throw\n // \"already connected\".\n this.close();\n }\n return;\n }\n })();\n this.renewInFlight = p;\n await p;\n this.renewInFlight = null;\n // Guard: if close() was called while the renew was in-flight, don't\n // schedule another iteration (avoids clobbering a new acquire's timer).\n if (this.closed || this.token !== savedToken) return;\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 this.renewTimer.unref();\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, interval);\n this.renewTimer.unref();\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// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport class DistributedLock extends DistributedPrimitive {\n constructor(opts: DistributedLockOptions) {\n super(opts);\n }\n\n protected doAcquire(sock: net.Socket) {\n return acquire(sock, this.key, this.acquireTimeoutS, this.leaseTtlS);\n }\n\n protected doEnqueue(sock: net.Socket) {\n return enqueue(sock, this.key, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return waitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return release(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return renew(sock, this.key, token, this.leaseTtlS);\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport class DistributedSemaphore extends DistributedPrimitive {\n readonly limit: number;\n\n constructor(opts: DistributedSemaphoreOptions) {\n super(opts);\n if (!Number.isInteger(opts.limit) || opts.limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n this.limit = opts.limit;\n }\n\n protected doAcquire(sock: net.Socket) {\n return semAcquire(\n sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n }\n\n protected doEnqueue(sock: net.Socket) {\n return semEnqueue(sock, this.key, this.limit, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return semWaitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return semRelease(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return semRenew(sock, this.key, token, this.leaseTtlS);\n }\n}\n"],"mappings":"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","names":["connect","interval"]}
|
|
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// Validation helpers\n// ---------------------------------------------------------------------------\n\nfunction validateKey(key: string): void {\n if (key === \"\") {\n throw new LockError(\"key must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(key)) {\n throw new LockError(\n \"key must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateAuth(auth: string): void {\n if (/[\\0\\n\\r]/.test(auth)) {\n throw new LockError(\n \"auth token must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateToken(token: string): void {\n if (token === \"\") {\n throw new LockError(\"token must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(token)) {\n throw new LockError(\n \"token must not contain NUL, newline, or carriage return characters\",\n );\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 * Parse a lease value from a server response part.\n * Returns `fallback` when the value is missing, empty, or non-numeric.\n */\nfunction parseLease(value: string | undefined, fallback: number = 30): number {\n if (value == null || value === \"\") return fallback;\n const n = Number(value);\n return Number.isFinite(n) && n >= 0 ? n : fallback;\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 *\n * NOTE: Concurrent calls to readline() on the same socket are unsafe —\n * callers must serialize reads (the request-response protocol naturally\n * enforces this).\n */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\nconst MAX_LINE_LENGTH = 1024 * 1024; // 1 MB\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 } else if (buf.length > MAX_LINE_LENGTH) {\n cleanup();\n _readlineBuf.delete(sock);\n reject(new LockError(\"server response exceeded maximum line length\"));\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 onEnd = () => {\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 sock.removeListener(\"end\", onEnd);\n };\n\n // If the readable side already ended (e.g. the server closed before we\n // started reading), the 'end'/'close' events won't fire again.\n if (sock.readableEnded || sock.destroyed) {\n _readlineBuf.delete(sock);\n reject(new LockError(\"server closed connection\"));\n return;\n }\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n sock.on(\"end\", onEnd);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n connectTimeoutMs?: number,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let settled = false;\n\n // tls.connect() registers the callback on \"secureConnect\", not \"connect\".\n const connectEvent = tlsOptions ? \"secureConnect\" : \"connect\";\n\n const onConnect = () => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(\"error\", onError);\n resolve(s);\n };\n\n const onError = (err: Error) => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(err);\n };\n\n let s: net.Socket;\n if (tlsOptions) {\n s = tls.connect({ ...tlsOptions, host, port }, onConnect);\n } else {\n s = net.createConnection({ host, port }, onConnect);\n }\n s.on(\"error\", onError);\n\n if (connectTimeoutMs != null && connectTimeoutMs > 0) {\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n s.removeListener(\"error\", onError);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(\n new LockError(\n `connect timed out after ${connectTimeoutMs}ms to ${host}:${port}`,\n ),\n );\n }, connectTimeoutMs);\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 validateAuth(auth);\n let resp: string;\n try {\n await writeAll(sock, encodeLines(\"auth\", \"_\", auth));\n resp = await readline(sock);\n } catch (err) {\n sock.destroy();\n throw err;\n }\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 if (numServers <= 0) {\n throw new LockError(\"numServers must be greater than 0\");\n }\n return crc32(Buffer.from(key, \"utf-8\")) % 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 helpers (shared by lock and semaphore functions)\n// ---------------------------------------------------------------------------\n\nasync function protoAcquire(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n limit?: number,\n): Promise<{ token: string; lease: number }> {\n validateKey(key);\n if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (limit != null && (!Number.isInteger(limit) || limit < 1)) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n\n const parts: (string | number)[] = [acquireTimeoutS];\n if (limit != null) parts.push(limit);\n if (leaseTtlS != null) parts.push(leaseTtlS);\n\n await writeAll(sock, encodeLines(cmd, key, parts.join(\" \")));\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(`${label} failed: '${resp}'`);\n }\n\n const respParts = resp.split(\" \");\n const token = respParts[1];\n if (!token) {\n throw new LockError(`${label}: server returned no token: '${resp}'`);\n }\n return { token, lease: parseLease(respParts[2]) };\n}\n\nasync function protoRenew(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n validateKey(key);\n validateToken(token);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n await writeAll(sock, encodeLines(cmd, key, arg));\n\n const resp = await readline(sock);\n if (resp !== \"ok\" && !resp.startsWith(\"ok \")) {\n throw new LockError(`${label} failed: '${resp}'`);\n }\n\n // Bare \"ok\" — server confirmed renewal but didn't echo the lease.\n if (resp === \"ok\") return leaseTtlS ?? 30;\n return parseLease(resp.split(\" \")[1]);\n}\n\nasync function protoEnqueue(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n leaseTtlS?: number,\n limit?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n validateKey(key);\n if (limit != null && (!Number.isInteger(limit) || limit < 1)) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n\n const parts: (string | number)[] = [];\n if (limit != null) parts.push(limit);\n if (leaseTtlS != null) parts.push(leaseTtlS);\n\n await writeAll(sock, encodeLines(cmd, key, parts.join(\" \")));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const respParts = resp.split(\" \");\n const token = respParts[1];\n if (!token) {\n throw new LockError(`${label}: server returned no token: '${resp}'`);\n }\n return { status: \"acquired\", token, lease: parseLease(respParts[2]) };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`${label} failed: '${resp}'`);\n}\n\nasync function protoWait(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n validateKey(key);\n if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {\n throw new LockError(\"waitTimeoutS must be a finite number >= 0\");\n }\n\n await writeAll(sock, encodeLines(cmd, 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(`${label} failed: '${resp}'`);\n }\n\n const respParts = resp.split(\" \");\n const token = respParts[1];\n if (!token) {\n throw new LockError(`${label}: server returned no token: '${resp}'`);\n }\n return { token, lease: parseLease(respParts[2]) };\n}\n\nasync function protoRelease(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n token: string,\n): Promise<void> {\n validateKey(key);\n validateToken(token);\n await writeAll(sock, encodeLines(cmd, key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`${label} failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lock protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket, key: string, acquireTimeoutS: number, leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n return protoAcquire(sock, \"l\", \"acquire\", key, acquireTimeoutS, leaseTtlS);\n}\n\nexport async function renew(\n sock: net.Socket, key: string, token: string, leaseTtlS?: number,\n): Promise<number> {\n return protoRenew(sock, \"n\", \"renew\", key, token, leaseTtlS);\n}\n\nexport async function enqueue(\n sock: net.Socket, key: string, leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n return protoEnqueue(sock, \"e\", \"enqueue\", key, leaseTtlS);\n}\n\nexport async function waitForLock(\n sock: net.Socket, key: string, waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n return protoWait(sock, \"w\", \"wait\", key, waitTimeoutS);\n}\n\nexport async function release(\n sock: net.Socket, key: string, token: string,\n): Promise<void> {\n return protoRelease(sock, \"r\", \"release\", key, token);\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket, key: string, acquireTimeoutS: number, limit: number, leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n return protoAcquire(sock, \"sl\", \"sem_acquire\", key, acquireTimeoutS, leaseTtlS, limit);\n}\n\nexport async function semRenew(\n sock: net.Socket, key: string, token: string, leaseTtlS?: number,\n): Promise<number> {\n return protoRenew(sock, \"sn\", \"sem_renew\", key, token, leaseTtlS);\n}\n\nexport async function semEnqueue(\n sock: net.Socket, key: string, limit: number, leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n return protoEnqueue(sock, \"se\", \"sem_enqueue\", key, leaseTtlS, limit);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket, key: string, waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n return protoWait(sock, \"sw\", \"sem_wait\", key, waitTimeoutS);\n}\n\nexport async function semRelease(\n sock: net.Socket, key: string, token: string,\n): Promise<void> {\n return protoRelease(sock, \"sr\", \"sem_release\", key, token);\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?: {\n host?: string;\n port?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n connectTimeoutMs?: number;\n },\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, options?.connectTimeoutMs);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Option interfaces\n// ---------------------------------------------------------------------------\n\ninterface BaseOptions {\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 /** TCP connect timeout in milliseconds. Undefined means no timeout. */\n connectTimeoutMs?: number;\n /**\n * Socket idle timeout in milliseconds. If no data is received within this\n * period, the socket emits a 'timeout' event and is destroyed, causing any\n * pending `readline` to reject. Undefined means no timeout.\n */\n socketTimeoutMs?: number;\n}\n\nexport interface DistributedLockOptions extends BaseOptions {}\n\nexport interface DistributedSemaphoreOptions extends BaseOptions {\n limit: number;\n}\n\n// ---------------------------------------------------------------------------\n// Shared base class\n// ---------------------------------------------------------------------------\n\nabstract class DistributedPrimitive {\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 readonly connectTimeoutMs: number | undefined;\n readonly socketTimeoutMs: number | 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 renewInFlight: Promise<void> | null = null;\n private closed = false;\n\n constructor(opts: BaseOptions) {\n validateKey(opts.key);\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 this.connectTimeoutMs = opts.connectTimeoutMs;\n this.socketTimeoutMs = opts.socketTimeoutMs;\n\n if (!Number.isFinite(this.acquireTimeoutS) || this.acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (this.leaseTtlS != null && (!Number.isFinite(this.leaseTtlS) || this.leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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\n const renewRatio = opts.renewRatio ?? 0.5;\n if (!Number.isFinite(renewRatio) || renewRatio <= 0 || renewRatio >= 1) {\n throw new LockError(\"renewRatio must be a finite number between 0 and 1 (exclusive)\");\n }\n this.renewRatio = renewRatio;\n }\n\n // -- abstract protocol hooks (implemented by subclasses) --\n\n protected abstract doAcquire(\n sock: net.Socket,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doEnqueue(\n sock: net.Socket,\n ): Promise<{\n status: \"acquired\" | \"queued\";\n token: string | null;\n lease: number | null;\n }>;\n\n protected abstract doWait(\n sock: net.Socket,\n timeoutS: number,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doRelease(\n sock: net.Socket,\n token: string,\n ): Promise<void>;\n\n protected abstract doRenew(\n sock: net.Socket,\n token: string,\n ): Promise<number>;\n\n // -- public API --\n\n private async openConnection(): Promise<net.Socket> {\n const [host, port] = this.pickServer();\n const sock = await connect(host, port, this.tls, this.auth, this.connectTimeoutMs);\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n // Register the listener once; use setTimeout(ms)/setTimeout(0) to\n // toggle without accumulating duplicate listeners.\n sock.on(\"timeout\", () => {\n sock.destroy(new LockError(\"socket idle timeout\"));\n });\n sock.setTimeout(this.socketTimeoutMs);\n }\n return sock;\n }\n\n /**\n * Suspend or restore the socket idle timeout. Only has effect when\n * `socketTimeoutMs` was set at construction time and a listener was\n * registered in `openConnection`.\n */\n private suspendSocketTimeout(sock: net.Socket): void {\n sock.setTimeout(0);\n }\n\n private restoreSocketTimeout(sock: net.Socket): void {\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n sock.setTimeout(this.socketTimeoutMs);\n }\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 / semaphore slot.\n * Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n // Suspend socket idle timeout during the blocking acquire — the server\n // won't send data until the lock is granted or the acquire times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doAcquire(this.sock);\n this.restoreSocketTimeout(this.sock);\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 * Release the lock / semaphore slot and close the connection.\n *\n * Throws `LockError` if the instance is already closed (e.g. after a\n * previous `release()` or `close()` call).\n *\n * The server-side release itself is best-effort: if the underlying\n * connection is already dead the protocol-level release error is silently\n * ignored so that the method doesn't throw on transient network failures.\n */\n async release(): Promise<void> {\n if (this.closed) {\n throw new LockError(\"not connected; nothing to release\");\n }\n // Capture the token and socket before awaiting anything — a concurrent\n // renew failure can set this.token/this.sock to null (via onLockLost →\n // close()), which would cause us to skip the server-side release and\n // leave the lock held until lease expiry.\n const tokenToRelease = this.token;\n const sockToRelease = this.sock;\n try {\n this.stopRenew();\n // Wait for any in-flight renew to finish before sending the release\n // command — concurrent reads/writes on the same socket are unsafe.\n // Bound the wait to 5s so release() doesn't hang forever when the\n // network is unresponsive and no socketTimeoutMs is configured.\n if (this.renewInFlight) {\n await Promise.race([\n this.renewInFlight,\n new Promise<void>((r) => setTimeout(r, 5000)),\n ]);\n // The loop resumes before us (it registered its .then first) and may\n // have scheduled a new timer. Clear it so it cannot fire during\n // doRelease below.\n this.stopRenew();\n }\n if (sockToRelease != null && tokenToRelease != null) {\n try {\n await this.doRelease(sockToRelease, tokenToRelease);\n } catch {\n // Best-effort: connection may already be dead.\n }\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) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n this.suspendSocketTimeout(this.sock);\n const result = await this.doEnqueue(this.sock);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n this.restoreSocketTimeout(this.sock);\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 / 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 // Already acquired during enqueue (fast path)\n return true;\n }\n if (this.closed) {\n throw new LockError(\"connection closed; call enqueue() again\");\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n // Suspend socket idle timeout during the blocking wait — the server\n // won't send data until the lock/slot is granted or the wait times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doWait(this.sock, timeout);\n this.restoreSocketTimeout(this.sock);\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 / slot, then release automatically.\n *\n * If `fn()` throws, its error is always preserved — a concurrent\n * release failure will not mask it.\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 let threw = false;\n try {\n return await fn();\n } catch (err) {\n threw = true;\n throw err;\n } finally {\n try {\n await this.release();\n } catch (releaseErr) {\n // If fn() already threw, swallow the release error so it\n // doesn't mask the original error.\n if (!threw) throw releaseErr;\n }\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 this.renewInFlight = null;\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n this.lease = 0;\n }\n\n // -- internals --\n\n private startRenew(): void {\n this.stopRenew();\n const loop = async () => {\n const savedToken = this.token;\n const sock = this.sock;\n if (!sock || !savedToken) return;\n const start = Date.now();\n const p = (async () => {\n try {\n const newLease = await this.doRenew(sock, savedToken);\n // Guard: if close() ran while the renew was in-flight, don't\n // clobber the reset state (lease=0, token=null).\n if (this.token === savedToken && !this.closed) {\n this.lease = newLease;\n }\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 try {\n // Cast to unknown: the declared return type is void, but an\n // async callback will return a Promise at runtime.\n const result: unknown = this.onLockLost(this.key, savedToken);\n // Guard against async callbacks returning a rejected Promise.\n if (result instanceof Promise) {\n result.catch(() => {});\n }\n } catch {\n // Never let a user callback crash the process.\n }\n }\n // Tear down the connection so the instance is in a clean state\n // for re-acquisition. Without this the socket leaks and a\n // subsequent acquire() without { force: true } would throw\n // \"already connected\".\n this.close();\n }\n return;\n }\n })();\n this.renewInFlight = p;\n await p;\n this.renewInFlight = null;\n // Guard: if close() was called while the renew was in-flight, don't\n // schedule another iteration (avoids clobbering a new acquire's timer).\n if (this.closed || this.token !== savedToken) return;\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 this.renewTimer.unref();\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, interval);\n this.renewTimer.unref();\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// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport class DistributedLock extends DistributedPrimitive {\n constructor(opts: DistributedLockOptions) {\n super(opts);\n }\n\n protected doAcquire(sock: net.Socket) {\n return acquire(sock, this.key, this.acquireTimeoutS, this.leaseTtlS);\n }\n\n protected doEnqueue(sock: net.Socket) {\n return enqueue(sock, this.key, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return waitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return release(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return renew(sock, this.key, token, this.leaseTtlS);\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport class DistributedSemaphore extends DistributedPrimitive {\n readonly limit: number;\n\n constructor(opts: DistributedSemaphoreOptions) {\n super(opts);\n if (!Number.isInteger(opts.limit) || opts.limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n this.limit = opts.limit;\n }\n\n protected doAcquire(sock: net.Socket) {\n return semAcquire(\n sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n }\n\n protected doEnqueue(sock: net.Socket) {\n return semEnqueue(sock, this.key, this.limit, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return semWaitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return semRelease(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return semRenew(sock, this.key, token, this.leaseTtlS);\n }\n}\n"],"mappings":"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","names":["connect","interval"]}
|
package/dist/client.js
CHANGED
|
@@ -111,6 +111,11 @@ function readline(sock) {
|
|
|
111
111
|
sock.removeListener("close", onClose);
|
|
112
112
|
sock.removeListener("end", onEnd);
|
|
113
113
|
};
|
|
114
|
+
if (sock.readableEnded || sock.destroyed) {
|
|
115
|
+
_readlineBuf.delete(sock);
|
|
116
|
+
reject(new LockError("server closed connection"));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
114
119
|
sock.on("data", onData);
|
|
115
120
|
sock.on("error", onError);
|
|
116
121
|
sock.on("close", onClose);
|
|
@@ -204,199 +209,134 @@ function stableHashShard(key, numServers) {
|
|
|
204
209
|
}
|
|
205
210
|
return crc32(Buffer.from(key, "utf-8")) % numServers;
|
|
206
211
|
}
|
|
207
|
-
async function
|
|
212
|
+
async function protoAcquire(sock, cmd, label, key, acquireTimeoutS, leaseTtlS, limit) {
|
|
208
213
|
validateKey(key);
|
|
209
214
|
if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {
|
|
210
215
|
throw new LockError("acquireTimeoutS must be a finite number >= 0");
|
|
211
216
|
}
|
|
217
|
+
if (limit != null && (!Number.isInteger(limit) || limit < 1)) {
|
|
218
|
+
throw new LockError("limit must be an integer >= 1");
|
|
219
|
+
}
|
|
212
220
|
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
213
221
|
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
214
222
|
}
|
|
215
|
-
const
|
|
216
|
-
|
|
223
|
+
const parts = [acquireTimeoutS];
|
|
224
|
+
if (limit != null) parts.push(limit);
|
|
225
|
+
if (leaseTtlS != null) parts.push(leaseTtlS);
|
|
226
|
+
await writeAll(sock, encodeLines(cmd, key, parts.join(" ")));
|
|
217
227
|
const resp = await readline(sock);
|
|
218
228
|
if (resp === "timeout") {
|
|
219
229
|
throw new AcquireTimeoutError(key);
|
|
220
230
|
}
|
|
221
231
|
if (!resp.startsWith("ok ")) {
|
|
222
|
-
throw new LockError(
|
|
232
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
223
233
|
}
|
|
224
|
-
const
|
|
225
|
-
const token =
|
|
234
|
+
const respParts = resp.split(" ");
|
|
235
|
+
const token = respParts[1];
|
|
226
236
|
if (!token) {
|
|
227
|
-
throw new LockError(
|
|
237
|
+
throw new LockError(`${label}: server returned no token: '${resp}'`);
|
|
228
238
|
}
|
|
229
|
-
|
|
230
|
-
return { token, lease };
|
|
239
|
+
return { token, lease: parseLease(respParts[2]) };
|
|
231
240
|
}
|
|
232
|
-
async function
|
|
241
|
+
async function protoRenew(sock, cmd, label, key, token, leaseTtlS) {
|
|
233
242
|
validateKey(key);
|
|
234
243
|
validateToken(token);
|
|
235
244
|
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
236
245
|
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
237
246
|
}
|
|
238
247
|
const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
|
|
239
|
-
await writeAll(sock, encodeLines(
|
|
248
|
+
await writeAll(sock, encodeLines(cmd, key, arg));
|
|
240
249
|
const resp = await readline(sock);
|
|
241
250
|
if (resp !== "ok" && !resp.startsWith("ok ")) {
|
|
242
|
-
throw new LockError(
|
|
243
|
-
}
|
|
244
|
-
if (resp === "ok") {
|
|
245
|
-
return leaseTtlS ?? 30;
|
|
251
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
246
252
|
}
|
|
253
|
+
if (resp === "ok") return leaseTtlS ?? 30;
|
|
247
254
|
return parseLease(resp.split(" ")[1]);
|
|
248
255
|
}
|
|
249
|
-
async function
|
|
256
|
+
async function protoEnqueue(sock, cmd, label, key, leaseTtlS, limit) {
|
|
250
257
|
validateKey(key);
|
|
258
|
+
if (limit != null && (!Number.isInteger(limit) || limit < 1)) {
|
|
259
|
+
throw new LockError("limit must be an integer >= 1");
|
|
260
|
+
}
|
|
251
261
|
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
252
262
|
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
253
263
|
}
|
|
254
|
-
const
|
|
255
|
-
|
|
264
|
+
const parts = [];
|
|
265
|
+
if (limit != null) parts.push(limit);
|
|
266
|
+
if (leaseTtlS != null) parts.push(leaseTtlS);
|
|
267
|
+
await writeAll(sock, encodeLines(cmd, key, parts.join(" ")));
|
|
256
268
|
const resp = await readline(sock);
|
|
257
269
|
if (resp.startsWith("acquired ")) {
|
|
258
|
-
const
|
|
259
|
-
const token =
|
|
270
|
+
const respParts = resp.split(" ");
|
|
271
|
+
const token = respParts[1];
|
|
260
272
|
if (!token) {
|
|
261
|
-
throw new LockError(
|
|
273
|
+
throw new LockError(`${label}: server returned no token: '${resp}'`);
|
|
262
274
|
}
|
|
263
|
-
|
|
264
|
-
return { status: "acquired", token, lease };
|
|
275
|
+
return { status: "acquired", token, lease: parseLease(respParts[2]) };
|
|
265
276
|
}
|
|
266
277
|
if (resp === "queued") {
|
|
267
278
|
return { status: "queued", token: null, lease: null };
|
|
268
279
|
}
|
|
269
|
-
throw new LockError(
|
|
280
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
270
281
|
}
|
|
271
|
-
async function
|
|
282
|
+
async function protoWait(sock, cmd, label, key, waitTimeoutS) {
|
|
272
283
|
validateKey(key);
|
|
273
284
|
if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {
|
|
274
285
|
throw new LockError("waitTimeoutS must be a finite number >= 0");
|
|
275
286
|
}
|
|
276
|
-
await writeAll(sock, encodeLines(
|
|
287
|
+
await writeAll(sock, encodeLines(cmd, key, String(waitTimeoutS)));
|
|
277
288
|
const resp = await readline(sock);
|
|
278
289
|
if (resp === "timeout") {
|
|
279
290
|
throw new AcquireTimeoutError(key);
|
|
280
291
|
}
|
|
281
292
|
if (!resp.startsWith("ok ")) {
|
|
282
|
-
throw new LockError(
|
|
293
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
283
294
|
}
|
|
284
|
-
const
|
|
285
|
-
const token =
|
|
295
|
+
const respParts = resp.split(" ");
|
|
296
|
+
const token = respParts[1];
|
|
286
297
|
if (!token) {
|
|
287
|
-
throw new LockError(
|
|
298
|
+
throw new LockError(`${label}: server returned no token: '${resp}'`);
|
|
288
299
|
}
|
|
289
|
-
|
|
290
|
-
return { token, lease };
|
|
300
|
+
return { token, lease: parseLease(respParts[2]) };
|
|
291
301
|
}
|
|
292
|
-
async function
|
|
302
|
+
async function protoRelease(sock, cmd, label, key, token) {
|
|
293
303
|
validateKey(key);
|
|
294
304
|
validateToken(token);
|
|
295
|
-
await writeAll(sock, encodeLines(
|
|
305
|
+
await writeAll(sock, encodeLines(cmd, key, token));
|
|
296
306
|
const resp = await readline(sock);
|
|
297
307
|
if (resp !== "ok") {
|
|
298
|
-
throw new LockError(
|
|
308
|
+
throw new LockError(`${label} failed: '${resp}'`);
|
|
299
309
|
}
|
|
300
310
|
}
|
|
311
|
+
async function acquire(sock, key, acquireTimeoutS, leaseTtlS) {
|
|
312
|
+
return protoAcquire(sock, "l", "acquire", key, acquireTimeoutS, leaseTtlS);
|
|
313
|
+
}
|
|
314
|
+
async function renew(sock, key, token, leaseTtlS) {
|
|
315
|
+
return protoRenew(sock, "n", "renew", key, token, leaseTtlS);
|
|
316
|
+
}
|
|
317
|
+
async function enqueue(sock, key, leaseTtlS) {
|
|
318
|
+
return protoEnqueue(sock, "e", "enqueue", key, leaseTtlS);
|
|
319
|
+
}
|
|
320
|
+
async function waitForLock(sock, key, waitTimeoutS) {
|
|
321
|
+
return protoWait(sock, "w", "wait", key, waitTimeoutS);
|
|
322
|
+
}
|
|
323
|
+
async function release(sock, key, token) {
|
|
324
|
+
return protoRelease(sock, "r", "release", key, token);
|
|
325
|
+
}
|
|
301
326
|
async function semAcquire(sock, key, acquireTimeoutS, limit, leaseTtlS) {
|
|
302
|
-
|
|
303
|
-
if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {
|
|
304
|
-
throw new LockError("acquireTimeoutS must be a finite number >= 0");
|
|
305
|
-
}
|
|
306
|
-
if (!Number.isInteger(limit) || limit < 1) {
|
|
307
|
-
throw new LockError("limit must be an integer >= 1");
|
|
308
|
-
}
|
|
309
|
-
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
310
|
-
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
311
|
-
}
|
|
312
|
-
const arg = leaseTtlS == null ? `${acquireTimeoutS} ${limit}` : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;
|
|
313
|
-
await writeAll(sock, encodeLines("sl", key, arg));
|
|
314
|
-
const resp = await readline(sock);
|
|
315
|
-
if (resp === "timeout") {
|
|
316
|
-
throw new AcquireTimeoutError(key);
|
|
317
|
-
}
|
|
318
|
-
if (!resp.startsWith("ok ")) {
|
|
319
|
-
throw new LockError(`sem_acquire failed: '${resp}'`);
|
|
320
|
-
}
|
|
321
|
-
const parts = resp.split(" ");
|
|
322
|
-
const token = parts[1];
|
|
323
|
-
if (!token) {
|
|
324
|
-
throw new LockError(`sem_acquire: server returned no token: '${resp}'`);
|
|
325
|
-
}
|
|
326
|
-
const lease = parseLease(parts[2]);
|
|
327
|
-
return { token, lease };
|
|
327
|
+
return protoAcquire(sock, "sl", "sem_acquire", key, acquireTimeoutS, leaseTtlS, limit);
|
|
328
328
|
}
|
|
329
329
|
async function semRenew(sock, key, token, leaseTtlS) {
|
|
330
|
-
|
|
331
|
-
validateToken(token);
|
|
332
|
-
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
333
|
-
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
334
|
-
}
|
|
335
|
-
const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
|
|
336
|
-
await writeAll(sock, encodeLines("sn", key, arg));
|
|
337
|
-
const resp = await readline(sock);
|
|
338
|
-
if (resp !== "ok" && !resp.startsWith("ok ")) {
|
|
339
|
-
throw new LockError(`sem_renew failed: '${resp}'`);
|
|
340
|
-
}
|
|
341
|
-
if (resp === "ok") {
|
|
342
|
-
return leaseTtlS ?? 30;
|
|
343
|
-
}
|
|
344
|
-
return parseLease(resp.split(" ")[1]);
|
|
330
|
+
return protoRenew(sock, "sn", "sem_renew", key, token, leaseTtlS);
|
|
345
331
|
}
|
|
346
332
|
async function semEnqueue(sock, key, limit, leaseTtlS) {
|
|
347
|
-
|
|
348
|
-
if (!Number.isInteger(limit) || limit < 1) {
|
|
349
|
-
throw new LockError("limit must be an integer >= 1");
|
|
350
|
-
}
|
|
351
|
-
if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {
|
|
352
|
-
throw new LockError("leaseTtlS must be a finite number > 0");
|
|
353
|
-
}
|
|
354
|
-
const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;
|
|
355
|
-
await writeAll(sock, encodeLines("se", key, arg));
|
|
356
|
-
const resp = await readline(sock);
|
|
357
|
-
if (resp.startsWith("acquired ")) {
|
|
358
|
-
const parts = resp.split(" ");
|
|
359
|
-
const token = parts[1];
|
|
360
|
-
if (!token) {
|
|
361
|
-
throw new LockError(`sem_enqueue: server returned no token: '${resp}'`);
|
|
362
|
-
}
|
|
363
|
-
const lease = parseLease(parts[2]);
|
|
364
|
-
return { status: "acquired", token, lease };
|
|
365
|
-
}
|
|
366
|
-
if (resp === "queued") {
|
|
367
|
-
return { status: "queued", token: null, lease: null };
|
|
368
|
-
}
|
|
369
|
-
throw new LockError(`sem_enqueue failed: '${resp}'`);
|
|
333
|
+
return protoEnqueue(sock, "se", "sem_enqueue", key, leaseTtlS, limit);
|
|
370
334
|
}
|
|
371
335
|
async function semWaitForLock(sock, key, waitTimeoutS) {
|
|
372
|
-
|
|
373
|
-
if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {
|
|
374
|
-
throw new LockError("waitTimeoutS must be a finite number >= 0");
|
|
375
|
-
}
|
|
376
|
-
await writeAll(sock, encodeLines("sw", key, String(waitTimeoutS)));
|
|
377
|
-
const resp = await readline(sock);
|
|
378
|
-
if (resp === "timeout") {
|
|
379
|
-
throw new AcquireTimeoutError(key);
|
|
380
|
-
}
|
|
381
|
-
if (!resp.startsWith("ok ")) {
|
|
382
|
-
throw new LockError(`sem_wait failed: '${resp}'`);
|
|
383
|
-
}
|
|
384
|
-
const parts = resp.split(" ");
|
|
385
|
-
const token = parts[1];
|
|
386
|
-
if (!token) {
|
|
387
|
-
throw new LockError(`sem_wait: server returned no token: '${resp}'`);
|
|
388
|
-
}
|
|
389
|
-
const lease = parseLease(parts[2]);
|
|
390
|
-
return { token, lease };
|
|
336
|
+
return protoWait(sock, "sw", "sem_wait", key, waitTimeoutS);
|
|
391
337
|
}
|
|
392
338
|
async function semRelease(sock, key, token) {
|
|
393
|
-
|
|
394
|
-
validateToken(token);
|
|
395
|
-
await writeAll(sock, encodeLines("sr", key, token));
|
|
396
|
-
const resp = await readline(sock);
|
|
397
|
-
if (resp !== "ok") {
|
|
398
|
-
throw new LockError(`sem_release failed: '${resp}'`);
|
|
399
|
-
}
|
|
339
|
+
return protoRelease(sock, "sr", "sem_release", key, token);
|
|
400
340
|
}
|
|
401
341
|
async function statsProto(sock) {
|
|
402
342
|
await writeAll(sock, encodeLines("stats", "_", ""));
|
|
@@ -554,7 +494,10 @@ var DistributedPrimitive = class {
|
|
|
554
494
|
try {
|
|
555
495
|
this.stopRenew();
|
|
556
496
|
if (this.renewInFlight) {
|
|
557
|
-
await
|
|
497
|
+
await Promise.race([
|
|
498
|
+
this.renewInFlight,
|
|
499
|
+
new Promise((r) => setTimeout(r, 5e3))
|
|
500
|
+
]);
|
|
558
501
|
this.stopRenew();
|
|
559
502
|
}
|
|
560
503
|
if (sockToRelease != null && tokenToRelease != null) {
|
|
@@ -589,11 +532,11 @@ var DistributedPrimitive = class {
|
|
|
589
532
|
this.suspendSocketTimeout(this.sock);
|
|
590
533
|
const result = await this.doEnqueue(this.sock);
|
|
591
534
|
if (result.status === "acquired") {
|
|
592
|
-
this.restoreSocketTimeout(this.sock);
|
|
593
535
|
this.token = result.token;
|
|
594
536
|
this.lease = result.lease ?? 0;
|
|
595
537
|
this.startRenew();
|
|
596
538
|
}
|
|
539
|
+
this.restoreSocketTimeout(this.sock);
|
|
597
540
|
return result.status;
|
|
598
541
|
} catch (err) {
|
|
599
542
|
this.close();
|
|
@@ -678,7 +621,10 @@ var DistributedPrimitive = class {
|
|
|
678
621
|
const start = Date.now();
|
|
679
622
|
const p = (async () => {
|
|
680
623
|
try {
|
|
681
|
-
|
|
624
|
+
const newLease = await this.doRenew(sock, savedToken);
|
|
625
|
+
if (this.token === savedToken && !this.closed) {
|
|
626
|
+
this.lease = newLease;
|
|
627
|
+
}
|
|
682
628
|
} catch {
|
|
683
629
|
if (this.token === savedToken) {
|
|
684
630
|
this.token = null;
|
package/dist/client.js.map
CHANGED
|
@@ -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// Validation helpers\n// ---------------------------------------------------------------------------\n\nfunction validateKey(key: string): void {\n if (key === \"\") {\n throw new LockError(\"key must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(key)) {\n throw new LockError(\n \"key must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateAuth(auth: string): void {\n if (/[\\0\\n\\r]/.test(auth)) {\n throw new LockError(\n \"auth token must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateToken(token: string): void {\n if (token === \"\") {\n throw new LockError(\"token must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(token)) {\n throw new LockError(\n \"token must not contain NUL, newline, or carriage return characters\",\n );\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 * Parse a lease value from a server response part.\n * Returns `fallback` when the value is missing, empty, or non-numeric.\n */\nfunction parseLease(value: string | undefined, fallback: number = 30): number {\n if (value == null || value === \"\") return fallback;\n const n = Number(value);\n return Number.isFinite(n) && n >= 0 ? n : fallback;\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 *\n * NOTE: Concurrent calls to readline() on the same socket are unsafe —\n * callers must serialize reads (the request-response protocol naturally\n * enforces this).\n */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\nconst MAX_LINE_LENGTH = 1024 * 1024; // 1 MB\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 } else if (buf.length > MAX_LINE_LENGTH) {\n cleanup();\n _readlineBuf.delete(sock);\n reject(new LockError(\"server response exceeded maximum line length\"));\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 onEnd = () => {\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 sock.removeListener(\"end\", onEnd);\n };\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n sock.on(\"end\", onEnd);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n connectTimeoutMs?: number,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let settled = false;\n\n // tls.connect() registers the callback on \"secureConnect\", not \"connect\".\n const connectEvent = tlsOptions ? \"secureConnect\" : \"connect\";\n\n const onConnect = () => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(\"error\", onError);\n resolve(s);\n };\n\n const onError = (err: Error) => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(err);\n };\n\n let s: net.Socket;\n if (tlsOptions) {\n s = tls.connect({ ...tlsOptions, host, port }, onConnect);\n } else {\n s = net.createConnection({ host, port }, onConnect);\n }\n s.on(\"error\", onError);\n\n if (connectTimeoutMs != null && connectTimeoutMs > 0) {\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n s.removeListener(\"error\", onError);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(\n new LockError(\n `connect timed out after ${connectTimeoutMs}ms to ${host}:${port}`,\n ),\n );\n }, connectTimeoutMs);\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 validateAuth(auth);\n let resp: string;\n try {\n await writeAll(sock, encodeLines(\"auth\", \"_\", auth));\n resp = await readline(sock);\n } catch (err) {\n sock.destroy();\n throw err;\n }\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 if (numServers <= 0) {\n throw new LockError(\"numServers must be greater than 0\");\n }\n return crc32(Buffer.from(key, \"utf-8\")) % 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 validateKey(key);\n if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 if (!token) {\n throw new LockError(`acquire: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n validateToken(token);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 // Bare \"ok\" — server confirmed renewal but didn't echo the lease.\n if (resp === \"ok\") {\n return leaseTtlS ?? 30;\n }\n\n return parseLease(resp.split(\" \")[1]);\n}\n\nexport async function enqueue(\n sock: net.Socket,\n key: string,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n validateKey(key);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n // Lease TTL is optional for enqueue; empty arg means \"use server default\".\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 if (!token) {\n throw new LockError(`enqueue: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {\n throw new LockError(\"waitTimeoutS must be a finite number >= 0\");\n }\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 if (!token) {\n throw new LockError(`wait: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\n return { token, lease };\n}\n\nexport async function release(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n validateKey(key);\n validateToken(token);\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 validateKey(key);\n if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (!Number.isInteger(limit) || limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 if (!token) {\n throw new LockError(`sem_acquire: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n validateToken(token);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 // Bare \"ok\" — server confirmed renewal but didn't echo the lease.\n if (resp === \"ok\") {\n return leaseTtlS ?? 30;\n }\n\n return parseLease(resp.split(\" \")[1]);\n}\n\nexport async function semEnqueue(\n sock: net.Socket,\n key: string,\n limit: number,\n leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n validateKey(key);\n if (!Number.isInteger(limit) || limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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 if (!token) {\n throw new LockError(`sem_enqueue: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\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 validateKey(key);\n if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {\n throw new LockError(\"waitTimeoutS must be a finite number >= 0\");\n }\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 if (!token) {\n throw new LockError(`sem_wait: server returned no token: '${resp}'`);\n }\n const lease = parseLease(parts[2]);\n return { token, lease };\n}\n\nexport async function semRelease(\n sock: net.Socket,\n key: string,\n token: string,\n): Promise<void> {\n validateKey(key);\n validateToken(token);\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?: {\n host?: string;\n port?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n connectTimeoutMs?: number;\n },\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, options?.connectTimeoutMs);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Option interfaces\n// ---------------------------------------------------------------------------\n\ninterface BaseOptions {\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 /** TCP connect timeout in milliseconds. Undefined means no timeout. */\n connectTimeoutMs?: number;\n /**\n * Socket idle timeout in milliseconds. If no data is received within this\n * period, the socket emits a 'timeout' event and is destroyed, causing any\n * pending `readline` to reject. Undefined means no timeout.\n */\n socketTimeoutMs?: number;\n}\n\nexport interface DistributedLockOptions extends BaseOptions {}\n\nexport interface DistributedSemaphoreOptions extends BaseOptions {\n limit: number;\n}\n\n// ---------------------------------------------------------------------------\n// Shared base class\n// ---------------------------------------------------------------------------\n\nabstract class DistributedPrimitive {\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 readonly connectTimeoutMs: number | undefined;\n readonly socketTimeoutMs: number | 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 renewInFlight: Promise<void> | null = null;\n private closed = false;\n\n constructor(opts: BaseOptions) {\n validateKey(opts.key);\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 this.connectTimeoutMs = opts.connectTimeoutMs;\n this.socketTimeoutMs = opts.socketTimeoutMs;\n\n if (!Number.isFinite(this.acquireTimeoutS) || this.acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (this.leaseTtlS != null && (!Number.isFinite(this.leaseTtlS) || this.leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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\n const renewRatio = opts.renewRatio ?? 0.5;\n if (!Number.isFinite(renewRatio) || renewRatio <= 0 || renewRatio >= 1) {\n throw new LockError(\"renewRatio must be a finite number between 0 and 1 (exclusive)\");\n }\n this.renewRatio = renewRatio;\n }\n\n // -- abstract protocol hooks (implemented by subclasses) --\n\n protected abstract doAcquire(\n sock: net.Socket,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doEnqueue(\n sock: net.Socket,\n ): Promise<{\n status: \"acquired\" | \"queued\";\n token: string | null;\n lease: number | null;\n }>;\n\n protected abstract doWait(\n sock: net.Socket,\n timeoutS: number,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doRelease(\n sock: net.Socket,\n token: string,\n ): Promise<void>;\n\n protected abstract doRenew(\n sock: net.Socket,\n token: string,\n ): Promise<number>;\n\n // -- public API --\n\n private async openConnection(): Promise<net.Socket> {\n const [host, port] = this.pickServer();\n const sock = await connect(host, port, this.tls, this.auth, this.connectTimeoutMs);\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n // Register the listener once; use setTimeout(ms)/setTimeout(0) to\n // toggle without accumulating duplicate listeners.\n sock.on(\"timeout\", () => {\n sock.destroy(new LockError(\"socket idle timeout\"));\n });\n sock.setTimeout(this.socketTimeoutMs);\n }\n return sock;\n }\n\n /**\n * Suspend or restore the socket idle timeout. Only has effect when\n * `socketTimeoutMs` was set at construction time and a listener was\n * registered in `openConnection`.\n */\n private suspendSocketTimeout(sock: net.Socket): void {\n sock.setTimeout(0);\n }\n\n private restoreSocketTimeout(sock: net.Socket): void {\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n sock.setTimeout(this.socketTimeoutMs);\n }\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 / semaphore slot.\n * Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n // Suspend socket idle timeout during the blocking acquire — the server\n // won't send data until the lock is granted or the acquire times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doAcquire(this.sock);\n this.restoreSocketTimeout(this.sock);\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 * Release the lock / semaphore slot and close the connection.\n *\n * Throws `LockError` if the instance is already closed (e.g. after a\n * previous `release()` or `close()` call).\n *\n * The server-side release itself is best-effort: if the underlying\n * connection is already dead the protocol-level release error is silently\n * ignored so that the method doesn't throw on transient network failures.\n */\n async release(): Promise<void> {\n if (this.closed) {\n throw new LockError(\"not connected; nothing to release\");\n }\n // Capture the token and socket before awaiting anything — a concurrent\n // renew failure can set this.token/this.sock to null (via onLockLost →\n // close()), which would cause us to skip the server-side release and\n // leave the lock held until lease expiry.\n const tokenToRelease = this.token;\n const sockToRelease = this.sock;\n try {\n this.stopRenew();\n // Wait for any in-flight renew to finish before sending the release\n // command — concurrent reads/writes on the same socket are unsafe.\n if (this.renewInFlight) {\n await this.renewInFlight;\n // The loop resumes before us (it registered its .then first) and may\n // have scheduled a new timer. Clear it so it cannot fire during\n // doRelease below.\n this.stopRenew();\n }\n if (sockToRelease != null && tokenToRelease != null) {\n try {\n await this.doRelease(sockToRelease, tokenToRelease);\n } catch {\n // Best-effort: connection may already be dead.\n }\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) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n this.suspendSocketTimeout(this.sock);\n const result = await this.doEnqueue(this.sock);\n if (result.status === \"acquired\") {\n this.restoreSocketTimeout(this.sock);\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 / 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 // Already acquired during enqueue (fast path)\n return true;\n }\n if (this.closed) {\n throw new LockError(\"connection closed; call enqueue() again\");\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n // Suspend socket idle timeout during the blocking wait — the server\n // won't send data until the lock/slot is granted or the wait times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doWait(this.sock, timeout);\n this.restoreSocketTimeout(this.sock);\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 / slot, then release automatically.\n *\n * If `fn()` throws, its error is always preserved — a concurrent\n * release failure will not mask it.\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 let threw = false;\n try {\n return await fn();\n } catch (err) {\n threw = true;\n throw err;\n } finally {\n try {\n await this.release();\n } catch (releaseErr) {\n // If fn() already threw, swallow the release error so it\n // doesn't mask the original error.\n if (!threw) throw releaseErr;\n }\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 this.renewInFlight = null;\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n this.lease = 0;\n }\n\n // -- internals --\n\n private startRenew(): void {\n this.stopRenew();\n const loop = async () => {\n const savedToken = this.token;\n const sock = this.sock;\n if (!sock || !savedToken) return;\n const start = Date.now();\n const p = (async () => {\n try {\n this.lease = await this.doRenew(sock, savedToken);\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 try {\n // Cast to unknown: the declared return type is void, but an\n // async callback will return a Promise at runtime.\n const result: unknown = this.onLockLost(this.key, savedToken);\n // Guard against async callbacks returning a rejected Promise.\n if (result instanceof Promise) {\n result.catch(() => {});\n }\n } catch {\n // Never let a user callback crash the process.\n }\n }\n // Tear down the connection so the instance is in a clean state\n // for re-acquisition. Without this the socket leaks and a\n // subsequent acquire() without { force: true } would throw\n // \"already connected\".\n this.close();\n }\n return;\n }\n })();\n this.renewInFlight = p;\n await p;\n this.renewInFlight = null;\n // Guard: if close() was called while the renew was in-flight, don't\n // schedule another iteration (avoids clobbering a new acquire's timer).\n if (this.closed || this.token !== savedToken) return;\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 this.renewTimer.unref();\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, interval);\n this.renewTimer.unref();\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// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport class DistributedLock extends DistributedPrimitive {\n constructor(opts: DistributedLockOptions) {\n super(opts);\n }\n\n protected doAcquire(sock: net.Socket) {\n return acquire(sock, this.key, this.acquireTimeoutS, this.leaseTtlS);\n }\n\n protected doEnqueue(sock: net.Socket) {\n return enqueue(sock, this.key, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return waitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return release(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return renew(sock, this.key, token, this.leaseTtlS);\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport class DistributedSemaphore extends DistributedPrimitive {\n readonly limit: number;\n\n constructor(opts: DistributedSemaphoreOptions) {\n super(opts);\n if (!Number.isInteger(opts.limit) || opts.limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n this.limit = opts.limit;\n }\n\n protected doAcquire(sock: net.Socket) {\n return semAcquire(\n sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n }\n\n protected doEnqueue(sock: net.Socket) {\n return semEnqueue(sock, this.key, this.limit, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return semWaitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return semRelease(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return semRenew(sock, this.key, token, this.leaseTtlS);\n }\n}\n"],"mappings":"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","names":["connect","interval"]}
|
|
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// Validation helpers\n// ---------------------------------------------------------------------------\n\nfunction validateKey(key: string): void {\n if (key === \"\") {\n throw new LockError(\"key must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(key)) {\n throw new LockError(\n \"key must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateAuth(auth: string): void {\n if (/[\\0\\n\\r]/.test(auth)) {\n throw new LockError(\n \"auth token must not contain NUL, newline, or carriage return characters\",\n );\n }\n}\n\nfunction validateToken(token: string): void {\n if (token === \"\") {\n throw new LockError(\"token must not be empty\");\n }\n if (/[\\0\\n\\r]/.test(token)) {\n throw new LockError(\n \"token must not contain NUL, newline, or carriage return characters\",\n );\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 * Parse a lease value from a server response part.\n * Returns `fallback` when the value is missing, empty, or non-numeric.\n */\nfunction parseLease(value: string | undefined, fallback: number = 30): number {\n if (value == null || value === \"\") return fallback;\n const n = Number(value);\n return Number.isFinite(n) && n >= 0 ? n : fallback;\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 *\n * NOTE: Concurrent calls to readline() on the same socket are unsafe —\n * callers must serialize reads (the request-response protocol naturally\n * enforces this).\n */\nconst _readlineBuf = new WeakMap<net.Socket, string>();\nconst MAX_LINE_LENGTH = 1024 * 1024; // 1 MB\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 } else if (buf.length > MAX_LINE_LENGTH) {\n cleanup();\n _readlineBuf.delete(sock);\n reject(new LockError(\"server response exceeded maximum line length\"));\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 onEnd = () => {\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 sock.removeListener(\"end\", onEnd);\n };\n\n // If the readable side already ended (e.g. the server closed before we\n // started reading), the 'end'/'close' events won't fire again.\n if (sock.readableEnded || sock.destroyed) {\n _readlineBuf.delete(sock);\n reject(new LockError(\"server closed connection\"));\n return;\n }\n\n sock.on(\"data\", onData);\n sock.on(\"error\", onError);\n sock.on(\"close\", onClose);\n sock.on(\"end\", onEnd);\n });\n}\n\nasync function connect(\n host: string,\n port: number,\n tlsOptions?: tls.ConnectionOptions,\n auth?: string,\n connectTimeoutMs?: number,\n): Promise<net.Socket> {\n const sock = await new Promise<net.Socket>((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let settled = false;\n\n // tls.connect() registers the callback on \"secureConnect\", not \"connect\".\n const connectEvent = tlsOptions ? \"secureConnect\" : \"connect\";\n\n const onConnect = () => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(\"error\", onError);\n resolve(s);\n };\n\n const onError = (err: Error) => {\n if (settled) return;\n settled = true;\n if (timer) clearTimeout(timer);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(err);\n };\n\n let s: net.Socket;\n if (tlsOptions) {\n s = tls.connect({ ...tlsOptions, host, port }, onConnect);\n } else {\n s = net.createConnection({ host, port }, onConnect);\n }\n s.on(\"error\", onError);\n\n if (connectTimeoutMs != null && connectTimeoutMs > 0) {\n timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n s.removeListener(\"error\", onError);\n s.removeListener(connectEvent, onConnect);\n s.destroy();\n reject(\n new LockError(\n `connect timed out after ${connectTimeoutMs}ms to ${host}:${port}`,\n ),\n );\n }, connectTimeoutMs);\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 validateAuth(auth);\n let resp: string;\n try {\n await writeAll(sock, encodeLines(\"auth\", \"_\", auth));\n resp = await readline(sock);\n } catch (err) {\n sock.destroy();\n throw err;\n }\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 if (numServers <= 0) {\n throw new LockError(\"numServers must be greater than 0\");\n }\n return crc32(Buffer.from(key, \"utf-8\")) % 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 helpers (shared by lock and semaphore functions)\n// ---------------------------------------------------------------------------\n\nasync function protoAcquire(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n acquireTimeoutS: number,\n leaseTtlS?: number,\n limit?: number,\n): Promise<{ token: string; lease: number }> {\n validateKey(key);\n if (!Number.isFinite(acquireTimeoutS) || acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (limit != null && (!Number.isInteger(limit) || limit < 1)) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n\n const parts: (string | number)[] = [acquireTimeoutS];\n if (limit != null) parts.push(limit);\n if (leaseTtlS != null) parts.push(leaseTtlS);\n\n await writeAll(sock, encodeLines(cmd, key, parts.join(\" \")));\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(`${label} failed: '${resp}'`);\n }\n\n const respParts = resp.split(\" \");\n const token = respParts[1];\n if (!token) {\n throw new LockError(`${label}: server returned no token: '${resp}'`);\n }\n return { token, lease: parseLease(respParts[2]) };\n}\n\nasync function protoRenew(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n token: string,\n leaseTtlS?: number,\n): Promise<number> {\n validateKey(key);\n validateToken(token);\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n\n const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;\n await writeAll(sock, encodeLines(cmd, key, arg));\n\n const resp = await readline(sock);\n if (resp !== \"ok\" && !resp.startsWith(\"ok \")) {\n throw new LockError(`${label} failed: '${resp}'`);\n }\n\n // Bare \"ok\" — server confirmed renewal but didn't echo the lease.\n if (resp === \"ok\") return leaseTtlS ?? 30;\n return parseLease(resp.split(\" \")[1]);\n}\n\nasync function protoEnqueue(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n leaseTtlS?: number,\n limit?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n validateKey(key);\n if (limit != null && (!Number.isInteger(limit) || limit < 1)) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n if (leaseTtlS != null && (!Number.isFinite(leaseTtlS) || leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\n\n const parts: (string | number)[] = [];\n if (limit != null) parts.push(limit);\n if (leaseTtlS != null) parts.push(leaseTtlS);\n\n await writeAll(sock, encodeLines(cmd, key, parts.join(\" \")));\n\n const resp = await readline(sock);\n if (resp.startsWith(\"acquired \")) {\n const respParts = resp.split(\" \");\n const token = respParts[1];\n if (!token) {\n throw new LockError(`${label}: server returned no token: '${resp}'`);\n }\n return { status: \"acquired\", token, lease: parseLease(respParts[2]) };\n }\n if (resp === \"queued\") {\n return { status: \"queued\", token: null, lease: null };\n }\n throw new LockError(`${label} failed: '${resp}'`);\n}\n\nasync function protoWait(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n validateKey(key);\n if (!Number.isFinite(waitTimeoutS) || waitTimeoutS < 0) {\n throw new LockError(\"waitTimeoutS must be a finite number >= 0\");\n }\n\n await writeAll(sock, encodeLines(cmd, 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(`${label} failed: '${resp}'`);\n }\n\n const respParts = resp.split(\" \");\n const token = respParts[1];\n if (!token) {\n throw new LockError(`${label}: server returned no token: '${resp}'`);\n }\n return { token, lease: parseLease(respParts[2]) };\n}\n\nasync function protoRelease(\n sock: net.Socket,\n cmd: string,\n label: string,\n key: string,\n token: string,\n): Promise<void> {\n validateKey(key);\n validateToken(token);\n await writeAll(sock, encodeLines(cmd, key, token));\n\n const resp = await readline(sock);\n if (resp !== \"ok\") {\n throw new LockError(`${label} failed: '${resp}'`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Lock protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function acquire(\n sock: net.Socket, key: string, acquireTimeoutS: number, leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n return protoAcquire(sock, \"l\", \"acquire\", key, acquireTimeoutS, leaseTtlS);\n}\n\nexport async function renew(\n sock: net.Socket, key: string, token: string, leaseTtlS?: number,\n): Promise<number> {\n return protoRenew(sock, \"n\", \"renew\", key, token, leaseTtlS);\n}\n\nexport async function enqueue(\n sock: net.Socket, key: string, leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n return protoEnqueue(sock, \"e\", \"enqueue\", key, leaseTtlS);\n}\n\nexport async function waitForLock(\n sock: net.Socket, key: string, waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n return protoWait(sock, \"w\", \"wait\", key, waitTimeoutS);\n}\n\nexport async function release(\n sock: net.Socket, key: string, token: string,\n): Promise<void> {\n return protoRelease(sock, \"r\", \"release\", key, token);\n}\n\n// ---------------------------------------------------------------------------\n// Semaphore protocol functions\n// ---------------------------------------------------------------------------\n\nexport async function semAcquire(\n sock: net.Socket, key: string, acquireTimeoutS: number, limit: number, leaseTtlS?: number,\n): Promise<{ token: string; lease: number }> {\n return protoAcquire(sock, \"sl\", \"sem_acquire\", key, acquireTimeoutS, leaseTtlS, limit);\n}\n\nexport async function semRenew(\n sock: net.Socket, key: string, token: string, leaseTtlS?: number,\n): Promise<number> {\n return protoRenew(sock, \"sn\", \"sem_renew\", key, token, leaseTtlS);\n}\n\nexport async function semEnqueue(\n sock: net.Socket, key: string, limit: number, leaseTtlS?: number,\n): Promise<{ status: \"acquired\" | \"queued\"; token: string | null; lease: number | null }> {\n return protoEnqueue(sock, \"se\", \"sem_enqueue\", key, leaseTtlS, limit);\n}\n\nexport async function semWaitForLock(\n sock: net.Socket, key: string, waitTimeoutS: number,\n): Promise<{ token: string; lease: number }> {\n return protoWait(sock, \"sw\", \"sem_wait\", key, waitTimeoutS);\n}\n\nexport async function semRelease(\n sock: net.Socket, key: string, token: string,\n): Promise<void> {\n return protoRelease(sock, \"sr\", \"sem_release\", key, token);\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?: {\n host?: string;\n port?: number;\n tls?: tls.ConnectionOptions;\n auth?: string;\n connectTimeoutMs?: number;\n },\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, options?.connectTimeoutMs);\n try {\n return await statsProto(sock);\n } finally {\n sock.destroy();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Option interfaces\n// ---------------------------------------------------------------------------\n\ninterface BaseOptions {\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 /** TCP connect timeout in milliseconds. Undefined means no timeout. */\n connectTimeoutMs?: number;\n /**\n * Socket idle timeout in milliseconds. If no data is received within this\n * period, the socket emits a 'timeout' event and is destroyed, causing any\n * pending `readline` to reject. Undefined means no timeout.\n */\n socketTimeoutMs?: number;\n}\n\nexport interface DistributedLockOptions extends BaseOptions {}\n\nexport interface DistributedSemaphoreOptions extends BaseOptions {\n limit: number;\n}\n\n// ---------------------------------------------------------------------------\n// Shared base class\n// ---------------------------------------------------------------------------\n\nabstract class DistributedPrimitive {\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 readonly connectTimeoutMs: number | undefined;\n readonly socketTimeoutMs: number | 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 renewInFlight: Promise<void> | null = null;\n private closed = false;\n\n constructor(opts: BaseOptions) {\n validateKey(opts.key);\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 this.connectTimeoutMs = opts.connectTimeoutMs;\n this.socketTimeoutMs = opts.socketTimeoutMs;\n\n if (!Number.isFinite(this.acquireTimeoutS) || this.acquireTimeoutS < 0) {\n throw new LockError(\"acquireTimeoutS must be a finite number >= 0\");\n }\n if (this.leaseTtlS != null && (!Number.isFinite(this.leaseTtlS) || this.leaseTtlS <= 0)) {\n throw new LockError(\"leaseTtlS must be a finite number > 0\");\n }\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\n const renewRatio = opts.renewRatio ?? 0.5;\n if (!Number.isFinite(renewRatio) || renewRatio <= 0 || renewRatio >= 1) {\n throw new LockError(\"renewRatio must be a finite number between 0 and 1 (exclusive)\");\n }\n this.renewRatio = renewRatio;\n }\n\n // -- abstract protocol hooks (implemented by subclasses) --\n\n protected abstract doAcquire(\n sock: net.Socket,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doEnqueue(\n sock: net.Socket,\n ): Promise<{\n status: \"acquired\" | \"queued\";\n token: string | null;\n lease: number | null;\n }>;\n\n protected abstract doWait(\n sock: net.Socket,\n timeoutS: number,\n ): Promise<{ token: string; lease: number }>;\n\n protected abstract doRelease(\n sock: net.Socket,\n token: string,\n ): Promise<void>;\n\n protected abstract doRenew(\n sock: net.Socket,\n token: string,\n ): Promise<number>;\n\n // -- public API --\n\n private async openConnection(): Promise<net.Socket> {\n const [host, port] = this.pickServer();\n const sock = await connect(host, port, this.tls, this.auth, this.connectTimeoutMs);\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n // Register the listener once; use setTimeout(ms)/setTimeout(0) to\n // toggle without accumulating duplicate listeners.\n sock.on(\"timeout\", () => {\n sock.destroy(new LockError(\"socket idle timeout\"));\n });\n sock.setTimeout(this.socketTimeoutMs);\n }\n return sock;\n }\n\n /**\n * Suspend or restore the socket idle timeout. Only has effect when\n * `socketTimeoutMs` was set at construction time and a listener was\n * registered in `openConnection`.\n */\n private suspendSocketTimeout(sock: net.Socket): void {\n sock.setTimeout(0);\n }\n\n private restoreSocketTimeout(sock: net.Socket): void {\n if (this.socketTimeoutMs != null && this.socketTimeoutMs > 0) {\n sock.setTimeout(this.socketTimeoutMs);\n }\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 / semaphore slot.\n * Returns `true` on success, `false` on timeout.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n // Suspend socket idle timeout during the blocking acquire — the server\n // won't send data until the lock is granted or the acquire times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doAcquire(this.sock);\n this.restoreSocketTimeout(this.sock);\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 * Release the lock / semaphore slot and close the connection.\n *\n * Throws `LockError` if the instance is already closed (e.g. after a\n * previous `release()` or `close()` call).\n *\n * The server-side release itself is best-effort: if the underlying\n * connection is already dead the protocol-level release error is silently\n * ignored so that the method doesn't throw on transient network failures.\n */\n async release(): Promise<void> {\n if (this.closed) {\n throw new LockError(\"not connected; nothing to release\");\n }\n // Capture the token and socket before awaiting anything — a concurrent\n // renew failure can set this.token/this.sock to null (via onLockLost →\n // close()), which would cause us to skip the server-side release and\n // leave the lock held until lease expiry.\n const tokenToRelease = this.token;\n const sockToRelease = this.sock;\n try {\n this.stopRenew();\n // Wait for any in-flight renew to finish before sending the release\n // command — concurrent reads/writes on the same socket are unsafe.\n // Bound the wait to 5s so release() doesn't hang forever when the\n // network is unresponsive and no socketTimeoutMs is configured.\n if (this.renewInFlight) {\n await Promise.race([\n this.renewInFlight,\n new Promise<void>((r) => setTimeout(r, 5000)),\n ]);\n // The loop resumes before us (it registered its .then first) and may\n // have scheduled a new timer. Clear it so it cannot fire during\n // doRelease below.\n this.stopRenew();\n }\n if (sockToRelease != null && tokenToRelease != null) {\n try {\n await this.doRelease(sockToRelease, tokenToRelease);\n } catch {\n // Best-effort: connection may already be dead.\n }\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) or `\"queued\"`.\n * If acquired immediately, the renew loop starts automatically.\n * @param opts.force - If `true`, silently close any existing connection\n * 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(\n \"already connected; call release() or close() first, or pass { force: true }\",\n );\n }\n this.close();\n }\n this.closed = false;\n this.sock = await this.openConnection();\n try {\n this.suspendSocketTimeout(this.sock);\n const result = await this.doEnqueue(this.sock);\n if (result.status === \"acquired\") {\n this.token = result.token;\n this.lease = result.lease ?? 0;\n this.startRenew();\n }\n this.restoreSocketTimeout(this.sock);\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 / 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 // Already acquired during enqueue (fast path)\n return true;\n }\n if (this.closed) {\n throw new LockError(\"connection closed; call enqueue() again\");\n }\n if (!this.sock) {\n throw new LockError(\"not connected; call enqueue() first\");\n }\n const timeout = timeoutS ?? this.acquireTimeoutS;\n try {\n // Suspend socket idle timeout during the blocking wait — the server\n // won't send data until the lock/slot is granted or the wait times out.\n this.suspendSocketTimeout(this.sock);\n const result = await this.doWait(this.sock, timeout);\n this.restoreSocketTimeout(this.sock);\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 / slot, then release automatically.\n *\n * If `fn()` throws, its error is always preserved — a concurrent\n * release failure will not mask it.\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 let threw = false;\n try {\n return await fn();\n } catch (err) {\n threw = true;\n throw err;\n } finally {\n try {\n await this.release();\n } catch (releaseErr) {\n // If fn() already threw, swallow the release error so it\n // doesn't mask the original error.\n if (!threw) throw releaseErr;\n }\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 this.renewInFlight = null;\n if (this.sock) {\n this.sock.destroy();\n this.sock = null;\n }\n this.token = null;\n this.lease = 0;\n }\n\n // -- internals --\n\n private startRenew(): void {\n this.stopRenew();\n const loop = async () => {\n const savedToken = this.token;\n const sock = this.sock;\n if (!sock || !savedToken) return;\n const start = Date.now();\n const p = (async () => {\n try {\n const newLease = await this.doRenew(sock, savedToken);\n // Guard: if close() ran while the renew was in-flight, don't\n // clobber the reset state (lease=0, token=null).\n if (this.token === savedToken && !this.closed) {\n this.lease = newLease;\n }\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 try {\n // Cast to unknown: the declared return type is void, but an\n // async callback will return a Promise at runtime.\n const result: unknown = this.onLockLost(this.key, savedToken);\n // Guard against async callbacks returning a rejected Promise.\n if (result instanceof Promise) {\n result.catch(() => {});\n }\n } catch {\n // Never let a user callback crash the process.\n }\n }\n // Tear down the connection so the instance is in a clean state\n // for re-acquisition. Without this the socket leaks and a\n // subsequent acquire() without { force: true } would throw\n // \"already connected\".\n this.close();\n }\n return;\n }\n })();\n this.renewInFlight = p;\n await p;\n this.renewInFlight = null;\n // Guard: if close() was called while the renew was in-flight, don't\n // schedule another iteration (avoids clobbering a new acquire's timer).\n if (this.closed || this.token !== savedToken) return;\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 this.renewTimer.unref();\n };\n const interval = Math.max(1, this.lease * this.renewRatio) * 1000;\n this.renewTimer = setTimeout(loop, interval);\n this.renewTimer.unref();\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// DistributedLock\n// ---------------------------------------------------------------------------\n\nexport class DistributedLock extends DistributedPrimitive {\n constructor(opts: DistributedLockOptions) {\n super(opts);\n }\n\n protected doAcquire(sock: net.Socket) {\n return acquire(sock, this.key, this.acquireTimeoutS, this.leaseTtlS);\n }\n\n protected doEnqueue(sock: net.Socket) {\n return enqueue(sock, this.key, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return waitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return release(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return renew(sock, this.key, token, this.leaseTtlS);\n }\n}\n\n// ---------------------------------------------------------------------------\n// DistributedSemaphore\n// ---------------------------------------------------------------------------\n\nexport class DistributedSemaphore extends DistributedPrimitive {\n readonly limit: number;\n\n constructor(opts: DistributedSemaphoreOptions) {\n super(opts);\n if (!Number.isInteger(opts.limit) || opts.limit < 1) {\n throw new LockError(\"limit must be an integer >= 1\");\n }\n this.limit = opts.limit;\n }\n\n protected doAcquire(sock: net.Socket) {\n return semAcquire(\n sock,\n this.key,\n this.acquireTimeoutS,\n this.limit,\n this.leaseTtlS,\n );\n }\n\n protected doEnqueue(sock: net.Socket) {\n return semEnqueue(sock, this.key, this.limit, this.leaseTtlS);\n }\n\n protected doWait(sock: net.Socket, timeoutS: number) {\n return semWaitForLock(sock, this.key, timeoutS);\n }\n\n protected doRelease(sock: net.Socket, token: string) {\n return semRelease(sock, this.key, token);\n }\n\n protected doRenew(sock: net.Socket, token: string) {\n return semRenew(sock, this.key, token, this.leaseTtlS);\n }\n}\n"],"mappings":";AAAA,YAAY,SAAS;AACrB,YAAY,SAAS;AAErB,IAAM,eAAe;AACrB,IAAM,eAAe;AAMd,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,UAAU;AAAA,EACjD,YAAY,KAAa;AACvB,UAAM,sBAAsB,GAAG,GAAG;AAClC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,UAAU;AAAA,EACvC,cAAc;AACZ,UAAM,uBAAuB;AAC7B,SAAK,OAAO;AAAA,EACd;AACF;AAMA,SAAS,YAAY,KAAmB;AACtC,MAAI,QAAQ,IAAI;AACd,UAAM,IAAI,UAAU,uBAAuB;AAAA,EAC7C;AACA,MAAI,WAAW,KAAK,GAAG,GAAG;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,aAAa,MAAoB;AACxC,MAAI,WAAW,KAAK,IAAI,GAAG;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,cAAc,OAAqB;AAC1C,MAAI,UAAU,IAAI;AAChB,UAAM,IAAI,UAAU,yBAAyB;AAAA,EAC/C;AACA,MAAI,WAAW,KAAK,KAAK,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,eAAe,OAAyB;AAC/C,SAAO,OAAO,KAAK,MAAM,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO;AACjE;AAEA,SAAS,SAAS,MAAkB,MAA6B;AAC/D,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,SAAK,MAAM,MAAM,CAAC,QAAQ;AACxB,UAAI,IAAK,QAAO,GAAG;AAAA,UACd,SAAQ;AAAA,IACf,CAAC;AAAA,EACH,CAAC;AACH;AAMA,SAAS,WAAW,OAA2B,WAAmB,IAAY;AAC5E,MAAI,SAAS,QAAQ,UAAU,GAAI,QAAO;AAC1C,QAAM,IAAI,OAAO,KAAK;AACtB,SAAO,OAAO,SAAS,CAAC,KAAK,KAAK,IAAI,IAAI;AAC5C;AAWA,IAAM,eAAe,oBAAI,QAA4B;AACrD,IAAM,kBAAkB,OAAO;AAE/B,SAAS,SAAS,MAAmC;AACnD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,MAAM,aAAa,IAAI,IAAI,KAAK;AAGpC,UAAM,WAAW,IAAI,QAAQ,IAAI;AACjC,QAAI,aAAa,IAAI;AACnB,YAAM,OAAO,IAAI,MAAM,GAAG,QAAQ,EAAE,QAAQ,OAAO,EAAE;AACrD,mBAAa,IAAI,MAAM,IAAI,MAAM,WAAW,CAAC,CAAC;AAC9C,cAAQ,IAAI;AACZ;AAAA,IACF;AAEA,UAAM,SAAS,CAAC,UAAkB;AAChC,aAAO,MAAM,SAAS,OAAO;AAC7B,YAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,UAAI,QAAQ,IAAI;AACd,gBAAQ;AACR,cAAM,OAAO,IAAI,MAAM,GAAG,GAAG,EAAE,QAAQ,OAAO,EAAE;AAChD,qBAAa,IAAI,MAAM,IAAI,MAAM,MAAM,CAAC,CAAC;AACzC,gBAAQ,IAAI;AAAA,MACd,WAAW,IAAI,SAAS,iBAAiB;AACvC,gBAAQ;AACR,qBAAa,OAAO,IAAI;AACxB,eAAO,IAAI,UAAU,8CAA8C,CAAC;AAAA,MACtE;AAAA,IACF;AAEA,UAAM,UAAU,CAAC,QAAe;AAC9B,cAAQ;AACR,mBAAa,OAAO,IAAI;AACxB,aAAO,GAAG;AAAA,IACZ;AAEA,UAAM,UAAU,MAAM;AACpB,cAAQ;AACR,mBAAa,OAAO,IAAI;AACxB,aAAO,IAAI,UAAU,0BAA0B,CAAC;AAAA,IAClD;AAEA,UAAM,QAAQ,MAAM;AAClB,cAAQ;AACR,mBAAa,OAAO,IAAI;AACxB,aAAO,IAAI,UAAU,0BAA0B,CAAC;AAAA,IAClD;AAEA,UAAM,UAAU,MAAM;AACpB,WAAK,eAAe,QAAQ,MAAM;AAClC,WAAK,eAAe,SAAS,OAAO;AACpC,WAAK,eAAe,SAAS,OAAO;AACpC,WAAK,eAAe,OAAO,KAAK;AAAA,IAClC;AAIA,QAAI,KAAK,iBAAiB,KAAK,WAAW;AACxC,mBAAa,OAAO,IAAI;AACxB,aAAO,IAAI,UAAU,0BAA0B,CAAC;AAChD;AAAA,IACF;AAEA,SAAK,GAAG,QAAQ,MAAM;AACtB,SAAK,GAAG,SAAS,OAAO;AACxB,SAAK,GAAG,SAAS,OAAO;AACxB,SAAK,GAAG,OAAO,KAAK;AAAA,EACtB,CAAC;AACH;AAEA,eAAeA,SACb,MACA,MACA,YACA,MACA,kBACqB;AACrB,QAAM,OAAO,MAAM,IAAI,QAAoB,CAAC,SAAS,WAAW;AAC9D,QAAI,QAA8C;AAClD,QAAI,UAAU;AAGd,UAAM,eAAe,aAAa,kBAAkB;AAEpD,UAAM,YAAY,MAAM;AACtB,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAC7B,QAAE,eAAe,SAAS,OAAO;AACjC,cAAQ,CAAC;AAAA,IACX;AAEA,UAAM,UAAU,CAAC,QAAe;AAC9B,UAAI,QAAS;AACb,gBAAU;AACV,UAAI,MAAO,cAAa,KAAK;AAC7B,QAAE,eAAe,cAAc,SAAS;AACxC,QAAE,QAAQ;AACV,aAAO,GAAG;AAAA,IACZ;AAEA,QAAI;AACJ,QAAI,YAAY;AACd,UAAQ,YAAQ,EAAE,GAAG,YAAY,MAAM,KAAK,GAAG,SAAS;AAAA,IAC1D,OAAO;AACL,UAAQ,qBAAiB,EAAE,MAAM,KAAK,GAAG,SAAS;AAAA,IACpD;AACA,MAAE,GAAG,SAAS,OAAO;AAErB,QAAI,oBAAoB,QAAQ,mBAAmB,GAAG;AACpD,cAAQ,WAAW,MAAM;AACvB,YAAI,QAAS;AACb,kBAAU;AACV,UAAE,eAAe,SAAS,OAAO;AACjC,UAAE,eAAe,cAAc,SAAS;AACxC,UAAE,QAAQ;AACV;AAAA,UACE,IAAI;AAAA,YACF,2BAA2B,gBAAgB,SAAS,IAAI,IAAI,IAAI;AAAA,UAClE;AAAA,QACF;AAAA,MACF,GAAG,gBAAgB;AAAA,IACrB;AAAA,EACF,CAAC;AAGD,OAAK,WAAW,IAAI;AAIpB,OAAK,GAAG,SAAS,MAAM;AAAA,EAAC,CAAC;AAEzB,MAAI,QAAQ,QAAQ,SAAS,IAAI;AAC/B,iBAAa,IAAI;AACjB,QAAI;AACJ,QAAI;AACF,YAAM,SAAS,MAAM,YAAY,QAAQ,KAAK,IAAI,CAAC;AACnD,aAAO,MAAM,SAAS,IAAI;AAAA,IAC5B,SAAS,KAAK;AACZ,WAAK,QAAQ;AACb,YAAM;AAAA,IACR;AACA,QAAI,SAAS,MAAM;AACjB,aAAO;AAAA,IACT;AACA,SAAK,QAAQ;AACb,QAAI,SAAS,cAAc;AACzB,YAAM,IAAI,UAAU;AAAA,IACtB;AACA,UAAM,IAAI,UAAU,iBAAiB,IAAI,GAAG;AAAA,EAC9C;AAEA,SAAO;AACT;AAMA,IAAM,cAAc,IAAI,YAAY,GAAG;AACvC,SAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,IAAI,IAAI,aAAc,MAAM,IAAK,MAAM;AAAA,EAC7C;AACA,cAAY,CAAC,IAAI;AACnB;AAEA,SAAS,MAAM,KAAqB;AAClC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAM,aAAa,MAAM,IAAI,CAAC,KAAK,GAAI,IAAK,QAAQ;AAAA,EACtD;AACA,UAAQ,MAAM,gBAAgB;AAChC;AAQO,SAAS,gBAAgB,KAAa,YAA4B;AACvE,MAAI,cAAc,GAAG;AACnB,UAAM,IAAI,UAAU,mCAAmC;AAAA,EACzD;AACA,SAAO,MAAM,OAAO,KAAK,KAAK,OAAO,CAAC,IAAI;AAC5C;AA0CA,eAAe,aACb,MACA,KACA,OACA,KACA,iBACA,WACA,OAC2C;AAC3C,cAAY,GAAG;AACf,MAAI,CAAC,OAAO,SAAS,eAAe,KAAK,kBAAkB,GAAG;AAC5D,UAAM,IAAI,UAAU,8CAA8C;AAAA,EACpE;AACA,MAAI,SAAS,SAAS,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,IAAI;AAC5D,UAAM,IAAI,UAAU,+BAA+B;AAAA,EACrD;AACA,MAAI,aAAa,SAAS,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,IAAI;AACxE,UAAM,IAAI,UAAU,uCAAuC;AAAA,EAC7D;AAEA,QAAM,QAA6B,CAAC,eAAe;AACnD,MAAI,SAAS,KAAM,OAAM,KAAK,KAAK;AACnC,MAAI,aAAa,KAAM,OAAM,KAAK,SAAS;AAE3C,QAAM,SAAS,MAAM,YAAY,KAAK,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC;AAE3D,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI,oBAAoB,GAAG;AAAA,EACnC;AACA,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,GAAG,KAAK,aAAa,IAAI,GAAG;AAAA,EAClD;AAEA,QAAM,YAAY,KAAK,MAAM,GAAG;AAChC,QAAM,QAAQ,UAAU,CAAC;AACzB,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,UAAU,GAAG,KAAK,gCAAgC,IAAI,GAAG;AAAA,EACrE;AACA,SAAO,EAAE,OAAO,OAAO,WAAW,UAAU,CAAC,CAAC,EAAE;AAClD;AAEA,eAAe,WACb,MACA,KACA,OACA,KACA,OACA,WACiB;AACjB,cAAY,GAAG;AACf,gBAAc,KAAK;AACnB,MAAI,aAAa,SAAS,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,IAAI;AACxE,UAAM,IAAI,UAAU,uCAAuC;AAAA,EAC7D;AAEA,QAAM,MAAM,aAAa,OAAO,QAAQ,GAAG,KAAK,IAAI,SAAS;AAC7D,QAAM,SAAS,MAAM,YAAY,KAAK,KAAK,GAAG,CAAC;AAE/C,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,QAAQ,CAAC,KAAK,WAAW,KAAK,GAAG;AAC5C,UAAM,IAAI,UAAU,GAAG,KAAK,aAAa,IAAI,GAAG;AAAA,EAClD;AAGA,MAAI,SAAS,KAAM,QAAO,aAAa;AACvC,SAAO,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC;AACtC;AAEA,eAAe,aACb,MACA,KACA,OACA,KACA,WACA,OACwF;AACxF,cAAY,GAAG;AACf,MAAI,SAAS,SAAS,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,IAAI;AAC5D,UAAM,IAAI,UAAU,+BAA+B;AAAA,EACrD;AACA,MAAI,aAAa,SAAS,CAAC,OAAO,SAAS,SAAS,KAAK,aAAa,IAAI;AACxE,UAAM,IAAI,UAAU,uCAAuC;AAAA,EAC7D;AAEA,QAAM,QAA6B,CAAC;AACpC,MAAI,SAAS,KAAM,OAAM,KAAK,KAAK;AACnC,MAAI,aAAa,KAAM,OAAM,KAAK,SAAS;AAE3C,QAAM,SAAS,MAAM,YAAY,KAAK,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC;AAE3D,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,KAAK,WAAW,WAAW,GAAG;AAChC,UAAM,YAAY,KAAK,MAAM,GAAG;AAChC,UAAM,QAAQ,UAAU,CAAC;AACzB,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,UAAU,GAAG,KAAK,gCAAgC,IAAI,GAAG;AAAA,IACrE;AACA,WAAO,EAAE,QAAQ,YAAY,OAAO,OAAO,WAAW,UAAU,CAAC,CAAC,EAAE;AAAA,EACtE;AACA,MAAI,SAAS,UAAU;AACrB,WAAO,EAAE,QAAQ,UAAU,OAAO,MAAM,OAAO,KAAK;AAAA,EACtD;AACA,QAAM,IAAI,UAAU,GAAG,KAAK,aAAa,IAAI,GAAG;AAClD;AAEA,eAAe,UACb,MACA,KACA,OACA,KACA,cAC2C;AAC3C,cAAY,GAAG;AACf,MAAI,CAAC,OAAO,SAAS,YAAY,KAAK,eAAe,GAAG;AACtD,UAAM,IAAI,UAAU,2CAA2C;AAAA,EACjE;AAEA,QAAM,SAAS,MAAM,YAAY,KAAK,KAAK,OAAO,YAAY,CAAC,CAAC;AAEhE,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI,oBAAoB,GAAG;AAAA,EACnC;AACA,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,GAAG,KAAK,aAAa,IAAI,GAAG;AAAA,EAClD;AAEA,QAAM,YAAY,KAAK,MAAM,GAAG;AAChC,QAAM,QAAQ,UAAU,CAAC;AACzB,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,UAAU,GAAG,KAAK,gCAAgC,IAAI,GAAG;AAAA,EACrE;AACA,SAAO,EAAE,OAAO,OAAO,WAAW,UAAU,CAAC,CAAC,EAAE;AAClD;AAEA,eAAe,aACb,MACA,KACA,OACA,KACA,OACe;AACf,cAAY,GAAG;AACf,gBAAc,KAAK;AACnB,QAAM,SAAS,MAAM,YAAY,KAAK,KAAK,KAAK,CAAC;AAEjD,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,SAAS,MAAM;AACjB,UAAM,IAAI,UAAU,GAAG,KAAK,aAAa,IAAI,GAAG;AAAA,EAClD;AACF;AAMA,eAAsB,QACpB,MAAkB,KAAa,iBAAyB,WACb;AAC3C,SAAO,aAAa,MAAM,KAAK,WAAW,KAAK,iBAAiB,SAAS;AAC3E;AAEA,eAAsB,MACpB,MAAkB,KAAa,OAAe,WAC7B;AACjB,SAAO,WAAW,MAAM,KAAK,SAAS,KAAK,OAAO,SAAS;AAC7D;AAEA,eAAsB,QACpB,MAAkB,KAAa,WACyD;AACxF,SAAO,aAAa,MAAM,KAAK,WAAW,KAAK,SAAS;AAC1D;AAEA,eAAsB,YACpB,MAAkB,KAAa,cACY;AAC3C,SAAO,UAAU,MAAM,KAAK,QAAQ,KAAK,YAAY;AACvD;AAEA,eAAsB,QACpB,MAAkB,KAAa,OAChB;AACf,SAAO,aAAa,MAAM,KAAK,WAAW,KAAK,KAAK;AACtD;AAMA,eAAsB,WACpB,MAAkB,KAAa,iBAAyB,OAAe,WAC5B;AAC3C,SAAO,aAAa,MAAM,MAAM,eAAe,KAAK,iBAAiB,WAAW,KAAK;AACvF;AAEA,eAAsB,SACpB,MAAkB,KAAa,OAAe,WAC7B;AACjB,SAAO,WAAW,MAAM,MAAM,aAAa,KAAK,OAAO,SAAS;AAClE;AAEA,eAAsB,WACpB,MAAkB,KAAa,OAAe,WAC0C;AACxF,SAAO,aAAa,MAAM,MAAM,eAAe,KAAK,WAAW,KAAK;AACtE;AAEA,eAAsB,eACpB,MAAkB,KAAa,cACY;AAC3C,SAAO,UAAU,MAAM,MAAM,YAAY,KAAK,YAAY;AAC5D;AAEA,eAAsB,WACpB,MAAkB,KAAa,OAChB;AACf,SAAO,aAAa,MAAM,MAAM,eAAe,KAAK,KAAK;AAC3D;AAMA,eAAe,WAAW,MAAkC;AAC1D,QAAM,SAAS,MAAM,YAAY,SAAS,KAAK,EAAE,CAAC;AAElD,QAAM,OAAO,MAAM,SAAS,IAAI;AAChC,MAAI,CAAC,KAAK,WAAW,KAAK,GAAG;AAC3B,UAAM,IAAI,UAAU,kBAAkB,IAAI,GAAG;AAAA,EAC/C;AAEA,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,UAAM,IAAI,UAAU,oCAAoC,IAAI,GAAG;AAAA,EACjE;AACF;AAaA,eAAsB,MACpB,SAOgB;AAChB,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,OAAO,MAAMA,SAAQ,MAAM,MAAM,SAAS,KAAK,SAAS,MAAM,SAAS,gBAAgB;AAC7F,MAAI;AACF,WAAO,MAAM,WAAW,IAAI;AAAA,EAC9B,UAAE;AACA,SAAK,QAAQ;AAAA,EACf;AACF;AAwCA,IAAe,uBAAf,MAAoC;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,QAAuB;AAAA,EACvB,QAAgB;AAAA,EAER,OAA0B;AAAA,EAC1B,aAAmD;AAAA,EACnD,gBAAsC;AAAA,EACtC,SAAS;AAAA,EAEjB,YAAY,MAAmB;AAC7B,gBAAY,KAAK,GAAG;AACpB,SAAK,MAAM,KAAK;AAChB,SAAK,kBAAkB,KAAK,mBAAmB;AAC/C,SAAK,YAAY,KAAK;AACtB,SAAK,MAAM,KAAK;AAChB,SAAK,OAAO,KAAK;AACjB,SAAK,aAAa,KAAK;AACvB,SAAK,mBAAmB,KAAK;AAC7B,SAAK,kBAAkB,KAAK;AAE5B,QAAI,CAAC,OAAO,SAAS,KAAK,eAAe,KAAK,KAAK,kBAAkB,GAAG;AACtE,YAAM,IAAI,UAAU,8CAA8C;AAAA,IACpE;AACA,QAAI,KAAK,aAAa,SAAS,CAAC,OAAO,SAAS,KAAK,SAAS,KAAK,KAAK,aAAa,IAAI;AACvF,YAAM,IAAI,UAAU,uCAAuC;AAAA,IAC7D;AAEA,QAAI,KAAK,SAAS;AAChB,UAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,cAAM,IAAI,UAAU,gCAAgC;AAAA,MACtD;AACA,WAAK,UAAU,CAAC,GAAG,KAAK,OAAO;AAAA,IACjC,OAAO;AACL,WAAK,UAAU,CAAC,CAAC,KAAK,QAAQ,cAAc,KAAK,QAAQ,YAAY,CAAC;AAAA,IACxE;AAEA,SAAK,mBAAmB,KAAK,oBAAoB;AAEjD,UAAM,aAAa,KAAK,cAAc;AACtC,QAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,KAAK,cAAc,GAAG;AACtE,YAAM,IAAI,UAAU,gEAAgE;AAAA,IACtF;AACA,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAiCA,MAAc,iBAAsC;AAClD,UAAM,CAAC,MAAM,IAAI,IAAI,KAAK,WAAW;AACrC,UAAM,OAAO,MAAMA,SAAQ,MAAM,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,gBAAgB;AACjF,QAAI,KAAK,mBAAmB,QAAQ,KAAK,kBAAkB,GAAG;AAG5D,WAAK,GAAG,WAAW,MAAM;AACvB,aAAK,QAAQ,IAAI,UAAU,qBAAqB,CAAC;AAAA,MACnD,CAAC;AACD,WAAK,WAAW,KAAK,eAAe;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,qBAAqB,MAAwB;AACnD,SAAK,WAAW,CAAC;AAAA,EACnB;AAAA,EAEQ,qBAAqB,MAAwB;AACnD,QAAI,KAAK,mBAAmB,QAAQ,KAAK,kBAAkB,GAAG;AAC5D,WAAK,WAAW,KAAK,eAAe;AAAA,IACtC;AAAA,EACF;AAAA,EAEQ,aAA+B;AACrC,UAAM,MAAM,KAAK,iBAAiB,KAAK,KAAK,KAAK,QAAQ,MAAM;AAC/D,QAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,QAAQ;AACnE,YAAM,IAAI;AAAA,QACR,2CAA2C,GAAG,QAAQ,KAAK,QAAQ,MAAM;AAAA,MAC3E;AAAA,IACF;AACA,WAAO,KAAK,QAAQ,GAAG;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,QAAQ,MAA8C;AAC1D,QAAI,KAAK,QAAQ,CAAC,KAAK,QAAQ;AAC7B,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,WAAK,MAAM;AAAA,IACb;AACA,SAAK,SAAS;AACd,SAAK,OAAO,MAAM,KAAK,eAAe;AACtC,QAAI;AAGF,WAAK,qBAAqB,KAAK,IAAI;AACnC,YAAM,SAAS,MAAM,KAAK,UAAU,KAAK,IAAI;AAC7C,WAAK,qBAAqB,KAAK,IAAI;AACnC,WAAK,QAAQ,OAAO;AACpB,WAAK,QAAQ,OAAO;AAAA,IACtB,SAAS,KAAK;AACZ,WAAK,MAAM;AACX,UAAI,eAAe,oBAAqB,QAAO;AAC/C,YAAM;AAAA,IACR;AACA,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,UAAyB;AAC7B,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,UAAU,mCAAmC;AAAA,IACzD;AAKA,UAAM,iBAAiB,KAAK;AAC5B,UAAM,gBAAgB,KAAK;AAC3B,QAAI;AACF,WAAK,UAAU;AAKf,UAAI,KAAK,eAAe;AACtB,cAAM,QAAQ,KAAK;AAAA,UACjB,KAAK;AAAA,UACL,IAAI,QAAc,CAAC,MAAM,WAAW,GAAG,GAAI,CAAC;AAAA,QAC9C,CAAC;AAID,aAAK,UAAU;AAAA,MACjB;AACA,UAAI,iBAAiB,QAAQ,kBAAkB,MAAM;AACnD,YAAI;AACF,gBAAM,KAAK,UAAU,eAAe,cAAc;AAAA,QACpD,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,UAAE;AACA,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QAAQ,MAA4D;AACxE,QAAI,KAAK,QAAQ,CAAC,KAAK,QAAQ;AAC7B,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,WAAK,MAAM;AAAA,IACb;AACA,SAAK,SAAS;AACd,SAAK,OAAO,MAAM,KAAK,eAAe;AACtC,QAAI;AACF,WAAK,qBAAqB,KAAK,IAAI;AACnC,YAAM,SAAS,MAAM,KAAK,UAAU,KAAK,IAAI;AAC7C,UAAI,OAAO,WAAW,YAAY;AAChC,aAAK,QAAQ,OAAO;AACpB,aAAK,QAAQ,OAAO,SAAS;AAC7B,aAAK,WAAW;AAAA,MAClB;AACA,WAAK,qBAAqB,KAAK,IAAI;AACnC,aAAO,OAAO;AAAA,IAChB,SAAS,KAAK;AACZ,WAAK,MAAM;AACX,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,KAAK,UAAqC;AAC9C,QAAI,KAAK,UAAU,MAAM;AAEvB,aAAO;AAAA,IACT;AACA,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,UAAU,yCAAyC;AAAA,IAC/D;AACA,QAAI,CAAC,KAAK,MAAM;AACd,YAAM,IAAI,UAAU,qCAAqC;AAAA,IAC3D;AACA,UAAM,UAAU,YAAY,KAAK;AACjC,QAAI;AAGF,WAAK,qBAAqB,KAAK,IAAI;AACnC,YAAM,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM,OAAO;AACnD,WAAK,qBAAqB,KAAK,IAAI;AACnC,WAAK,QAAQ,OAAO;AACpB,WAAK,QAAQ,OAAO;AAAA,IACtB,SAAS,KAAK;AACZ,WAAK,MAAM;AACX,UAAI,eAAe,oBAAqB,QAAO;AAC/C,YAAM;AAAA,IACR;AACA,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAAY,IAAsC;AACtD,UAAM,KAAK,MAAM,KAAK,QAAQ;AAC9B,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,oBAAoB,KAAK,GAAG;AAAA,IACxC;AACA,QAAI,QAAQ;AACZ,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,KAAK;AACZ,cAAQ;AACR,YAAM;AAAA,IACR,UAAE;AACA,UAAI;AACF,cAAM,KAAK,QAAQ;AAAA,MACrB,SAAS,YAAY;AAGnB,YAAI,CAAC,MAAO,OAAM;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,QAAc;AACZ,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,SAAK,UAAU;AACf,SAAK,gBAAgB;AACrB,QAAI,KAAK,MAAM;AACb,WAAK,KAAK,QAAQ;AAClB,WAAK,OAAO;AAAA,IACd;AACA,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAIQ,aAAmB;AACzB,SAAK,UAAU;AACf,UAAM,OAAO,YAAY;AACvB,YAAM,aAAa,KAAK;AACxB,YAAM,OAAO,KAAK;AAClB,UAAI,CAAC,QAAQ,CAAC,WAAY;AAC1B,YAAM,QAAQ,KAAK,IAAI;AACvB,YAAM,KAAK,YAAY;AACrB,YAAI;AACF,gBAAM,WAAW,MAAM,KAAK,QAAQ,MAAM,UAAU;AAGpD,cAAI,KAAK,UAAU,cAAc,CAAC,KAAK,QAAQ;AAC7C,iBAAK,QAAQ;AAAA,UACf;AAAA,QACF,QAAQ;AAEN,cAAI,KAAK,UAAU,YAAY;AAC7B,iBAAK,QAAQ;AACb,gBAAI,KAAK,YAAY;AACnB,kBAAI;AAGF,sBAAM,SAAkB,KAAK,WAAW,KAAK,KAAK,UAAU;AAE5D,oBAAI,kBAAkB,SAAS;AAC7B,yBAAO,MAAM,MAAM;AAAA,kBAAC,CAAC;AAAA,gBACvB;AAAA,cACF,QAAQ;AAAA,cAER;AAAA,YACF;AAKA,iBAAK,MAAM;AAAA,UACb;AACA;AAAA,QACF;AAAA,MACF,GAAG;AACH,WAAK,gBAAgB;AACrB,YAAM;AACN,WAAK,gBAAgB;AAGrB,UAAI,KAAK,UAAU,KAAK,UAAU,WAAY;AAC9C,YAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,YAAMC,YAAW,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK,UAAU,IAAI;AAC7D,WAAK,aAAa,WAAW,MAAM,KAAK,IAAI,GAAGA,YAAW,OAAO,CAAC;AAClE,WAAK,WAAW,MAAM;AAAA,IACxB;AACA,UAAM,WAAW,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK,UAAU,IAAI;AAC7D,SAAK,aAAa,WAAW,MAAM,QAAQ;AAC3C,SAAK,WAAW,MAAM;AAAA,EACxB;AAAA,EAEQ,YAAkB;AACxB,QAAI,KAAK,cAAc,MAAM;AAC3B,mBAAa,KAAK,UAAU;AAC5B,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AACF;AAMO,IAAM,kBAAN,cAA8B,qBAAqB;AAAA,EACxD,YAAY,MAA8B;AACxC,UAAM,IAAI;AAAA,EACZ;AAAA,EAEU,UAAU,MAAkB;AACpC,WAAO,QAAQ,MAAM,KAAK,KAAK,KAAK,iBAAiB,KAAK,SAAS;AAAA,EACrE;AAAA,EAEU,UAAU,MAAkB;AACpC,WAAO,QAAQ,MAAM,KAAK,KAAK,KAAK,SAAS;AAAA,EAC/C;AAAA,EAEU,OAAO,MAAkB,UAAkB;AACnD,WAAO,YAAY,MAAM,KAAK,KAAK,QAAQ;AAAA,EAC7C;AAAA,EAEU,UAAU,MAAkB,OAAe;AACnD,WAAO,QAAQ,MAAM,KAAK,KAAK,KAAK;AAAA,EACtC;AAAA,EAEU,QAAQ,MAAkB,OAAe;AACjD,WAAO,MAAM,MAAM,KAAK,KAAK,OAAO,KAAK,SAAS;AAAA,EACpD;AACF;AAMO,IAAM,uBAAN,cAAmC,qBAAqB;AAAA,EACpD;AAAA,EAET,YAAY,MAAmC;AAC7C,UAAM,IAAI;AACV,QAAI,CAAC,OAAO,UAAU,KAAK,KAAK,KAAK,KAAK,QAAQ,GAAG;AACnD,YAAM,IAAI,UAAU,+BAA+B;AAAA,IACrD;AACA,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEU,UAAU,MAAkB;AACpC,WAAO;AAAA,MACL;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEU,UAAU,MAAkB;AACpC,WAAO,WAAW,MAAM,KAAK,KAAK,KAAK,OAAO,KAAK,SAAS;AAAA,EAC9D;AAAA,EAEU,OAAO,MAAkB,UAAkB;AACnD,WAAO,eAAe,MAAM,KAAK,KAAK,QAAQ;AAAA,EAChD;AAAA,EAEU,UAAU,MAAkB,OAAe;AACnD,WAAO,WAAW,MAAM,KAAK,KAAK,KAAK;AAAA,EACzC;AAAA,EAEU,QAAQ,MAAkB,OAAe;AACjD,WAAO,SAAS,MAAM,KAAK,KAAK,OAAO,KAAK,SAAS;AAAA,EACvD;AACF;","names":["connect","interval"]}
|