dflockd-client 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.cts CHANGED
@@ -6,6 +6,8 @@ declare class AcquireTimeoutError extends Error {
6
6
  declare class LockError extends Error {
7
7
  constructor(message: string);
8
8
  }
9
+ type ShardingStrategy = (key: string, numServers: number) => number;
10
+ declare function stableHashShard(key: string, numServers: number): number;
9
11
  declare function acquire(sock: net.Socket, key: string, acquireTimeoutS: number, leaseTtlS?: number): Promise<{
10
12
  token: string;
11
13
  lease: number;
@@ -21,20 +23,39 @@ declare function waitForLock(sock: net.Socket, key: string, waitTimeoutS: number
21
23
  lease: number;
22
24
  }>;
23
25
  declare function release(sock: net.Socket, key: string, token: string): Promise<void>;
26
+ declare function semAcquire(sock: net.Socket, key: string, acquireTimeoutS: number, limit: number, leaseTtlS?: number): Promise<{
27
+ token: string;
28
+ lease: number;
29
+ }>;
30
+ declare function semRenew(sock: net.Socket, key: string, token: string, leaseTtlS?: number): Promise<number>;
31
+ declare function semEnqueue(sock: net.Socket, key: string, limit: number, leaseTtlS?: number): Promise<{
32
+ status: "acquired" | "queued";
33
+ token: string | null;
34
+ lease: number | null;
35
+ }>;
36
+ declare function semWaitForLock(sock: net.Socket, key: string, waitTimeoutS: number): Promise<{
37
+ token: string;
38
+ lease: number;
39
+ }>;
40
+ declare function semRelease(sock: net.Socket, key: string, token: string): Promise<void>;
24
41
  interface DistributedLockOptions {
25
42
  key: string;
26
43
  acquireTimeoutS?: number;
27
44
  leaseTtlS?: number;
45
+ /** @deprecated Use `servers` instead. */
28
46
  host?: string;
47
+ /** @deprecated Use `servers` instead. */
29
48
  port?: number;
49
+ servers?: Array<[host: string, port: number]>;
50
+ shardingStrategy?: ShardingStrategy;
30
51
  renewRatio?: number;
31
52
  }
32
53
  declare class DistributedLock {
33
54
  readonly key: string;
34
55
  readonly acquireTimeoutS: number;
35
56
  readonly leaseTtlS: number | undefined;
36
- readonly host: string;
37
- readonly port: number;
57
+ readonly servers: Array<[string, number]>;
58
+ readonly shardingStrategy: ShardingStrategy;
38
59
  readonly renewRatio: number;
39
60
  token: string | null;
40
61
  lease: number;
@@ -42,6 +63,7 @@ declare class DistributedLock {
42
63
  private renewTimer;
43
64
  private closed;
44
65
  constructor(opts: DistributedLockOptions);
66
+ private pickServer;
45
67
  /** Acquire the lock. Returns `true` on success, `false` on timeout. */
46
68
  acquire(): Promise<boolean>;
47
69
  /** Release the lock and close the connection. */
@@ -74,5 +96,65 @@ declare class DistributedLock {
74
96
  private startRenew;
75
97
  private stopRenew;
76
98
  }
99
+ interface DistributedSemaphoreOptions {
100
+ key: string;
101
+ limit: number;
102
+ acquireTimeoutS?: number;
103
+ leaseTtlS?: number;
104
+ /** @deprecated Use `servers` instead. */
105
+ host?: string;
106
+ /** @deprecated Use `servers` instead. */
107
+ port?: number;
108
+ servers?: Array<[host: string, port: number]>;
109
+ shardingStrategy?: ShardingStrategy;
110
+ renewRatio?: number;
111
+ }
112
+ declare class DistributedSemaphore {
113
+ readonly key: string;
114
+ readonly limit: number;
115
+ readonly acquireTimeoutS: number;
116
+ readonly leaseTtlS: number | undefined;
117
+ readonly servers: Array<[string, number]>;
118
+ readonly shardingStrategy: ShardingStrategy;
119
+ readonly renewRatio: number;
120
+ token: string | null;
121
+ lease: number;
122
+ private sock;
123
+ private renewTimer;
124
+ private closed;
125
+ constructor(opts: DistributedSemaphoreOptions);
126
+ private pickServer;
127
+ /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */
128
+ acquire(): Promise<boolean>;
129
+ /** Release the semaphore slot and close the connection. */
130
+ release(): Promise<void>;
131
+ /**
132
+ * Two-phase step 1: connect and join the FIFO queue.
133
+ * Returns `"acquired"` (fast-path, slot granted immediately) or `"queued"`.
134
+ * If acquired immediately, the renew loop starts automatically.
135
+ */
136
+ enqueue(): Promise<"acquired" | "queued">;
137
+ /**
138
+ * Two-phase step 2: block until a semaphore slot is granted.
139
+ * Returns `true` if granted, `false` on timeout.
140
+ * If already acquired during `enqueue()`, returns `true` immediately.
141
+ */
142
+ wait(timeoutS?: number): Promise<boolean>;
143
+ /**
144
+ * Run `fn` while holding a semaphore slot, then release automatically.
145
+ *
146
+ * ```ts
147
+ * const sem = new DistributedSemaphore({ key: "my-resource", limit: 5 });
148
+ * await sem.withLock(async () => {
149
+ * // critical section (up to 5 concurrent holders)
150
+ * });
151
+ * ```
152
+ */
153
+ withLock<T>(fn: () => T | Promise<T>): Promise<T>;
154
+ /** Close the underlying socket (idempotent). */
155
+ close(): Promise<void>;
156
+ private startRenew;
157
+ private stopRenew;
158
+ }
77
159
 
78
- export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, LockError, acquire, enqueue, release, renew, waitForLock };
160
+ export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, waitForLock };
package/dist/client.d.ts CHANGED
@@ -6,6 +6,8 @@ declare class AcquireTimeoutError extends Error {
6
6
  declare class LockError extends Error {
7
7
  constructor(message: string);
8
8
  }
9
+ type ShardingStrategy = (key: string, numServers: number) => number;
10
+ declare function stableHashShard(key: string, numServers: number): number;
9
11
  declare function acquire(sock: net.Socket, key: string, acquireTimeoutS: number, leaseTtlS?: number): Promise<{
10
12
  token: string;
11
13
  lease: number;
@@ -21,20 +23,39 @@ declare function waitForLock(sock: net.Socket, key: string, waitTimeoutS: number
21
23
  lease: number;
22
24
  }>;
23
25
  declare function release(sock: net.Socket, key: string, token: string): Promise<void>;
26
+ declare function semAcquire(sock: net.Socket, key: string, acquireTimeoutS: number, limit: number, leaseTtlS?: number): Promise<{
27
+ token: string;
28
+ lease: number;
29
+ }>;
30
+ declare function semRenew(sock: net.Socket, key: string, token: string, leaseTtlS?: number): Promise<number>;
31
+ declare function semEnqueue(sock: net.Socket, key: string, limit: number, leaseTtlS?: number): Promise<{
32
+ status: "acquired" | "queued";
33
+ token: string | null;
34
+ lease: number | null;
35
+ }>;
36
+ declare function semWaitForLock(sock: net.Socket, key: string, waitTimeoutS: number): Promise<{
37
+ token: string;
38
+ lease: number;
39
+ }>;
40
+ declare function semRelease(sock: net.Socket, key: string, token: string): Promise<void>;
24
41
  interface DistributedLockOptions {
25
42
  key: string;
26
43
  acquireTimeoutS?: number;
27
44
  leaseTtlS?: number;
45
+ /** @deprecated Use `servers` instead. */
28
46
  host?: string;
47
+ /** @deprecated Use `servers` instead. */
29
48
  port?: number;
49
+ servers?: Array<[host: string, port: number]>;
50
+ shardingStrategy?: ShardingStrategy;
30
51
  renewRatio?: number;
31
52
  }
32
53
  declare class DistributedLock {
33
54
  readonly key: string;
34
55
  readonly acquireTimeoutS: number;
35
56
  readonly leaseTtlS: number | undefined;
36
- readonly host: string;
37
- readonly port: number;
57
+ readonly servers: Array<[string, number]>;
58
+ readonly shardingStrategy: ShardingStrategy;
38
59
  readonly renewRatio: number;
39
60
  token: string | null;
40
61
  lease: number;
@@ -42,6 +63,7 @@ declare class DistributedLock {
42
63
  private renewTimer;
43
64
  private closed;
44
65
  constructor(opts: DistributedLockOptions);
66
+ private pickServer;
45
67
  /** Acquire the lock. Returns `true` on success, `false` on timeout. */
46
68
  acquire(): Promise<boolean>;
47
69
  /** Release the lock and close the connection. */
@@ -74,5 +96,65 @@ declare class DistributedLock {
74
96
  private startRenew;
75
97
  private stopRenew;
76
98
  }
99
+ interface DistributedSemaphoreOptions {
100
+ key: string;
101
+ limit: number;
102
+ acquireTimeoutS?: number;
103
+ leaseTtlS?: number;
104
+ /** @deprecated Use `servers` instead. */
105
+ host?: string;
106
+ /** @deprecated Use `servers` instead. */
107
+ port?: number;
108
+ servers?: Array<[host: string, port: number]>;
109
+ shardingStrategy?: ShardingStrategy;
110
+ renewRatio?: number;
111
+ }
112
+ declare class DistributedSemaphore {
113
+ readonly key: string;
114
+ readonly limit: number;
115
+ readonly acquireTimeoutS: number;
116
+ readonly leaseTtlS: number | undefined;
117
+ readonly servers: Array<[string, number]>;
118
+ readonly shardingStrategy: ShardingStrategy;
119
+ readonly renewRatio: number;
120
+ token: string | null;
121
+ lease: number;
122
+ private sock;
123
+ private renewTimer;
124
+ private closed;
125
+ constructor(opts: DistributedSemaphoreOptions);
126
+ private pickServer;
127
+ /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */
128
+ acquire(): Promise<boolean>;
129
+ /** Release the semaphore slot and close the connection. */
130
+ release(): Promise<void>;
131
+ /**
132
+ * Two-phase step 1: connect and join the FIFO queue.
133
+ * Returns `"acquired"` (fast-path, slot granted immediately) or `"queued"`.
134
+ * If acquired immediately, the renew loop starts automatically.
135
+ */
136
+ enqueue(): Promise<"acquired" | "queued">;
137
+ /**
138
+ * Two-phase step 2: block until a semaphore slot is granted.
139
+ * Returns `true` if granted, `false` on timeout.
140
+ * If already acquired during `enqueue()`, returns `true` immediately.
141
+ */
142
+ wait(timeoutS?: number): Promise<boolean>;
143
+ /**
144
+ * Run `fn` while holding a semaphore slot, then release automatically.
145
+ *
146
+ * ```ts
147
+ * const sem = new DistributedSemaphore({ key: "my-resource", limit: 5 });
148
+ * await sem.withLock(async () => {
149
+ * // critical section (up to 5 concurrent holders)
150
+ * });
151
+ * ```
152
+ */
153
+ withLock<T>(fn: () => T | Promise<T>): Promise<T>;
154
+ /** Close the underlying socket (idempotent). */
155
+ close(): Promise<void>;
156
+ private startRenew;
157
+ private stopRenew;
158
+ }
77
159
 
78
- export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, LockError, acquire, enqueue, release, renew, waitForLock };
160
+ export { AcquireTimeoutError, DistributedLock, type DistributedLockOptions, DistributedSemaphore, type DistributedSemaphoreOptions, LockError, type ShardingStrategy, acquire, enqueue, release, renew, semAcquire, semEnqueue, semRelease, semRenew, semWaitForLock, stableHashShard, waitForLock };
package/dist/client.js CHANGED
@@ -52,6 +52,24 @@ function connect(host, port) {
52
52
  sock.on("error", reject);
53
53
  });
54
54
  }
55
+ var CRC32_TABLE = new Uint32Array(256);
56
+ for (let i = 0; i < 256; i++) {
57
+ let c = i;
58
+ for (let j = 0; j < 8; j++) {
59
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
60
+ }
61
+ CRC32_TABLE[i] = c;
62
+ }
63
+ function crc32(buf) {
64
+ let crc = 4294967295;
65
+ for (let i = 0; i < buf.length; i++) {
66
+ crc = CRC32_TABLE[(crc ^ buf[i]) & 255] ^ crc >>> 8;
67
+ }
68
+ return (crc ^ 4294967295) >>> 0;
69
+ }
70
+ function stableHashShard(key, numServers) {
71
+ return (crc32(Buffer.from(key, "utf-8")) >>> 0) % numServers;
72
+ }
55
73
  async function acquire(sock, key, acquireTimeoutS, leaseTtlS) {
56
74
  const arg = leaseTtlS == null ? String(acquireTimeoutS) : `${acquireTimeoutS} ${leaseTtlS}`;
57
75
  sock.write(encodeLines("l", key, arg));
@@ -119,12 +137,79 @@ async function release(sock, key, token) {
119
137
  throw new LockError(`release failed: '${resp}'`);
120
138
  }
121
139
  }
140
+ async function semAcquire(sock, key, acquireTimeoutS, limit, leaseTtlS) {
141
+ const arg = leaseTtlS == null ? `${acquireTimeoutS} ${limit}` : `${acquireTimeoutS} ${limit} ${leaseTtlS}`;
142
+ sock.write(encodeLines("sl", key, arg));
143
+ const resp = await readline(sock);
144
+ if (resp === "timeout") {
145
+ throw new AcquireTimeoutError(key);
146
+ }
147
+ if (!resp.startsWith("ok ")) {
148
+ throw new LockError(`sem_acquire failed: '${resp}'`);
149
+ }
150
+ const parts = resp.split(" ");
151
+ if (parts.length < 2) {
152
+ throw new LockError(`bad ok response: '${resp}'`);
153
+ }
154
+ const token = parts[1];
155
+ const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
156
+ return { token, lease };
157
+ }
158
+ async function semRenew(sock, key, token, leaseTtlS) {
159
+ const arg = leaseTtlS == null ? token : `${token} ${leaseTtlS}`;
160
+ sock.write(encodeLines("sn", key, arg));
161
+ const resp = await readline(sock);
162
+ if (!resp.startsWith("ok")) {
163
+ throw new LockError(`sem_renew failed: '${resp}'`);
164
+ }
165
+ const parts = resp.split(" ");
166
+ if (parts.length >= 2 && /^\d+$/.test(parts[1])) {
167
+ return parseInt(parts[1], 10);
168
+ }
169
+ return -1;
170
+ }
171
+ async function semEnqueue(sock, key, limit, leaseTtlS) {
172
+ const arg = leaseTtlS == null ? String(limit) : `${limit} ${leaseTtlS}`;
173
+ sock.write(encodeLines("se", key, arg));
174
+ const resp = await readline(sock);
175
+ if (resp.startsWith("acquired ")) {
176
+ const parts = resp.split(" ");
177
+ const token = parts[1];
178
+ const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
179
+ return { status: "acquired", token, lease };
180
+ }
181
+ if (resp === "queued") {
182
+ return { status: "queued", token: null, lease: null };
183
+ }
184
+ throw new LockError(`sem_enqueue failed: '${resp}'`);
185
+ }
186
+ async function semWaitForLock(sock, key, waitTimeoutS) {
187
+ sock.write(encodeLines("sw", key, String(waitTimeoutS)));
188
+ const resp = await readline(sock);
189
+ if (resp === "timeout") {
190
+ throw new AcquireTimeoutError(key);
191
+ }
192
+ if (!resp.startsWith("ok ")) {
193
+ throw new LockError(`sem_wait failed: '${resp}'`);
194
+ }
195
+ const parts = resp.split(" ");
196
+ const token = parts[1];
197
+ const lease = parts.length >= 3 ? parseInt(parts[2], 10) : 30;
198
+ return { token, lease };
199
+ }
200
+ async function semRelease(sock, key, token) {
201
+ sock.write(encodeLines("sr", key, token));
202
+ const resp = await readline(sock);
203
+ if (resp !== "ok") {
204
+ throw new LockError(`sem_release failed: '${resp}'`);
205
+ }
206
+ }
122
207
  var DistributedLock = class {
123
208
  key;
124
209
  acquireTimeoutS;
125
210
  leaseTtlS;
126
- host;
127
- port;
211
+ servers;
212
+ shardingStrategy;
128
213
  renewRatio;
129
214
  token = null;
130
215
  lease = 0;
@@ -135,14 +220,26 @@ var DistributedLock = class {
135
220
  this.key = opts.key;
136
221
  this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;
137
222
  this.leaseTtlS = opts.leaseTtlS;
138
- this.host = opts.host ?? DEFAULT_HOST;
139
- this.port = opts.port ?? DEFAULT_PORT;
223
+ if (opts.servers) {
224
+ if (opts.servers.length === 0) {
225
+ throw new LockError("servers list must not be empty");
226
+ }
227
+ this.servers = opts.servers;
228
+ } else {
229
+ this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];
230
+ }
231
+ this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;
140
232
  this.renewRatio = opts.renewRatio ?? 0.5;
141
233
  }
234
+ pickServer() {
235
+ const idx = this.shardingStrategy(this.key, this.servers.length);
236
+ return this.servers[idx];
237
+ }
142
238
  /** Acquire the lock. Returns `true` on success, `false` on timeout. */
143
239
  async acquire() {
144
240
  this.closed = false;
145
- this.sock = await connect(this.host, this.port);
241
+ const [host, port] = this.pickServer();
242
+ this.sock = await connect(host, port);
146
243
  try {
147
244
  const result = await acquire(
148
245
  this.sock,
@@ -178,7 +275,8 @@ var DistributedLock = class {
178
275
  */
179
276
  async enqueue() {
180
277
  this.closed = false;
181
- this.sock = await connect(this.host, this.port);
278
+ const [host, port] = this.pickServer();
279
+ this.sock = await connect(host, port);
182
280
  try {
183
281
  const result = await enqueue(this.sock, this.key, this.leaseTtlS);
184
282
  if (result.status === "acquired") {
@@ -274,14 +372,192 @@ var DistributedLock = class {
274
372
  }
275
373
  }
276
374
  };
375
+ var DistributedSemaphore = class {
376
+ key;
377
+ limit;
378
+ acquireTimeoutS;
379
+ leaseTtlS;
380
+ servers;
381
+ shardingStrategy;
382
+ renewRatio;
383
+ token = null;
384
+ lease = 0;
385
+ sock = null;
386
+ renewTimer = null;
387
+ closed = false;
388
+ constructor(opts) {
389
+ this.key = opts.key;
390
+ this.limit = opts.limit;
391
+ this.acquireTimeoutS = opts.acquireTimeoutS ?? 10;
392
+ this.leaseTtlS = opts.leaseTtlS;
393
+ if (opts.servers) {
394
+ if (opts.servers.length === 0) {
395
+ throw new LockError("servers list must not be empty");
396
+ }
397
+ this.servers = opts.servers;
398
+ } else {
399
+ this.servers = [[opts.host ?? DEFAULT_HOST, opts.port ?? DEFAULT_PORT]];
400
+ }
401
+ this.shardingStrategy = opts.shardingStrategy ?? stableHashShard;
402
+ this.renewRatio = opts.renewRatio ?? 0.5;
403
+ }
404
+ pickServer() {
405
+ const idx = this.shardingStrategy(this.key, this.servers.length);
406
+ return this.servers[idx];
407
+ }
408
+ /** Acquire a semaphore slot. Returns `true` on success, `false` on timeout. */
409
+ async acquire() {
410
+ this.closed = false;
411
+ const [host, port] = this.pickServer();
412
+ this.sock = await connect(host, port);
413
+ try {
414
+ const result = await semAcquire(
415
+ this.sock,
416
+ this.key,
417
+ this.acquireTimeoutS,
418
+ this.limit,
419
+ this.leaseTtlS
420
+ );
421
+ this.token = result.token;
422
+ this.lease = result.lease;
423
+ } catch (err) {
424
+ await this.close();
425
+ if (err instanceof AcquireTimeoutError) return false;
426
+ throw err;
427
+ }
428
+ this.startRenew();
429
+ return true;
430
+ }
431
+ /** Release the semaphore slot and close the connection. */
432
+ async release() {
433
+ try {
434
+ this.stopRenew();
435
+ if (this.sock && this.token) {
436
+ await semRelease(this.sock, this.key, this.token);
437
+ }
438
+ } finally {
439
+ await this.close();
440
+ }
441
+ }
442
+ /**
443
+ * Two-phase step 1: connect and join the FIFO queue.
444
+ * Returns `"acquired"` (fast-path, slot granted immediately) or `"queued"`.
445
+ * If acquired immediately, the renew loop starts automatically.
446
+ */
447
+ async enqueue() {
448
+ this.closed = false;
449
+ const [host, port] = this.pickServer();
450
+ this.sock = await connect(host, port);
451
+ try {
452
+ const result = await semEnqueue(this.sock, this.key, this.limit, this.leaseTtlS);
453
+ if (result.status === "acquired") {
454
+ this.token = result.token;
455
+ this.lease = result.lease ?? 0;
456
+ this.startRenew();
457
+ }
458
+ return result.status;
459
+ } catch (err) {
460
+ await this.close();
461
+ throw err;
462
+ }
463
+ }
464
+ /**
465
+ * Two-phase step 2: block until a semaphore slot is granted.
466
+ * Returns `true` if granted, `false` on timeout.
467
+ * If already acquired during `enqueue()`, returns `true` immediately.
468
+ */
469
+ async wait(timeoutS) {
470
+ if (this.token !== null) {
471
+ return true;
472
+ }
473
+ if (!this.sock) {
474
+ throw new LockError("not connected; call enqueue() first");
475
+ }
476
+ const timeout = timeoutS ?? this.acquireTimeoutS;
477
+ try {
478
+ const result = await semWaitForLock(this.sock, this.key, timeout);
479
+ this.token = result.token;
480
+ this.lease = result.lease;
481
+ } catch (err) {
482
+ await this.close();
483
+ if (err instanceof AcquireTimeoutError) return false;
484
+ throw err;
485
+ }
486
+ this.startRenew();
487
+ return true;
488
+ }
489
+ /**
490
+ * Run `fn` while holding a semaphore slot, then release automatically.
491
+ *
492
+ * ```ts
493
+ * const sem = new DistributedSemaphore({ key: "my-resource", limit: 5 });
494
+ * await sem.withLock(async () => {
495
+ * // critical section (up to 5 concurrent holders)
496
+ * });
497
+ * ```
498
+ */
499
+ async withLock(fn) {
500
+ const ok = await this.acquire();
501
+ if (!ok) {
502
+ throw new AcquireTimeoutError(this.key);
503
+ }
504
+ try {
505
+ return await fn();
506
+ } finally {
507
+ await this.release();
508
+ }
509
+ }
510
+ /** Close the underlying socket (idempotent). */
511
+ async close() {
512
+ if (this.closed) return;
513
+ this.closed = true;
514
+ this.stopRenew();
515
+ if (this.sock) {
516
+ this.sock.destroy();
517
+ this.sock = null;
518
+ }
519
+ this.token = null;
520
+ }
521
+ // -- internals --
522
+ startRenew() {
523
+ const interval = Math.max(1, this.lease * this.renewRatio) * 1e3;
524
+ const loop = async () => {
525
+ if (!this.sock || !this.token) return;
526
+ try {
527
+ await semRenew(this.sock, this.key, this.token, this.leaseTtlS);
528
+ } catch {
529
+ console.error(
530
+ `semaphore lost (renew failed): key=${this.key} token=${this.token}`
531
+ );
532
+ this.token = null;
533
+ return;
534
+ }
535
+ this.renewTimer = setTimeout(loop, interval);
536
+ };
537
+ this.renewTimer = setTimeout(loop, interval);
538
+ }
539
+ stopRenew() {
540
+ if (this.renewTimer != null) {
541
+ clearTimeout(this.renewTimer);
542
+ this.renewTimer = null;
543
+ }
544
+ }
545
+ };
277
546
  export {
278
547
  AcquireTimeoutError,
279
548
  DistributedLock,
549
+ DistributedSemaphore,
280
550
  LockError,
281
551
  acquire,
282
552
  enqueue,
283
553
  release,
284
554
  renew,
555
+ semAcquire,
556
+ semEnqueue,
557
+ semRelease,
558
+ semRenew,
559
+ semWaitForLock,
560
+ stableHashShard,
285
561
  waitForLock
286
562
  };
287
563
  //# sourceMappingURL=client.js.map