@zukhruf/mutex 0.3.38 → 0.3.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -13
- package/dist/index.d.ts +0 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -15
- package/dist/index.js.map +1 -1
- package/dist/lock-stores/file-system/index.d.ts +5 -0
- package/dist/lock-stores/file-system/index.d.ts.map +1 -0
- package/dist/lock-stores/file-system/index.js +5 -0
- package/dist/lock-stores/file-system/index.js.map +1 -0
- package/dist/lock-stores/ipc/index.d.ts +5 -0
- package/dist/lock-stores/ipc/index.d.ts.map +1 -0
- package/dist/lock-stores/ipc/index.js +5 -0
- package/dist/lock-stores/ipc/index.js.map +1 -0
- package/dist/lock-stores/memory/index.d.ts +2 -0
- package/dist/lock-stores/memory/index.d.ts.map +1 -0
- package/dist/lock-stores/memory/index.js +2 -0
- package/dist/lock-stores/memory/index.js.map +1 -0
- package/dist/lock-stores/postgres/index.d.ts +2 -0
- package/dist/lock-stores/postgres/index.d.ts.map +1 -0
- package/dist/lock-stores/postgres/index.js +2 -0
- package/dist/lock-stores/postgres/index.js.map +1 -0
- package/dist/lock-stores/postgres/postgres-store.d.ts +47 -0
- package/dist/lock-stores/postgres/postgres-store.d.ts.map +1 -0
- package/dist/lock-stores/postgres/postgres-store.js +254 -0
- package/dist/lock-stores/postgres/postgres-store.js.map +1 -0
- package/dist/lock-stores/socket/index.d.ts +7 -0
- package/dist/lock-stores/socket/index.d.ts.map +1 -0
- package/dist/lock-stores/socket/index.js +7 -0
- package/dist/lock-stores/socket/index.js.map +1 -0
- package/dist/lock-stores/sqlite/index.d.ts +4 -0
- package/dist/lock-stores/sqlite/index.d.ts.map +1 -0
- package/dist/lock-stores/sqlite/index.js +3 -0
- package/dist/lock-stores/sqlite/index.js.map +1 -0
- package/dist/lock-stores/thread/index.d.ts +5 -0
- package/dist/lock-stores/thread/index.d.ts.map +1 -0
- package/dist/lock-stores/thread/index.js +5 -0
- package/dist/lock-stores/thread/index.js.map +1 -0
- package/package.json +17 -8
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @zukhruf/mutex
|
|
2
2
|
|
|
3
|
-
A mutex for Node.js with interchangeable lock stores. You select who shares the locks: one object, the threads of one process, a parent process and its children,
|
|
3
|
+
A mutex for Node.js with interchangeable lock stores. You select who shares the locks: one object, the threads of one process, a parent process and its children, all processes on one host, or all machines that reach one Postgres database. The code that uses the mutex stays the same. Each lease has a fencing token, so a resource can refuse the late writes of a holder that lost its key.
|
|
4
4
|
|
|
5
5
|
The words in these documents have one meaning each. See the glossary in [CONTEXT.md](./CONTEXT.md).
|
|
6
6
|
|
|
@@ -19,7 +19,8 @@ if (stock > 0) {
|
|
|
19
19
|
A mutex lets one holder at a time run this code for a key:
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
|
-
import {
|
|
22
|
+
import { Mutex } from '@zukhruf/mutex';
|
|
23
|
+
import { MemoryStore } from '@zukhruf/mutex/memory';
|
|
23
24
|
|
|
24
25
|
const mutex = new Mutex(new MemoryStore());
|
|
25
26
|
|
|
@@ -35,22 +36,24 @@ The full program is in the recipe [Stop two requests from selling the last item]
|
|
|
35
36
|
|
|
36
37
|
## Select a lock store
|
|
37
38
|
|
|
38
|
-
First find who writes to the resource. Then select the lock store with that [reach](./docs/concepts/reach.md).
|
|
39
|
+
First find who writes to the resource. Then select the lock store with that [reach](./docs/concepts/reach.md). Each family of lock stores has its own import path. The root `@zukhruf/mutex` gives only `Mutex` and the acquire modes ([ADR 0020](./docs/adr/0020-each-store-family-has-its-own-import-path.md)).
|
|
39
40
|
|
|
40
|
-
| Lock store | Reach
|
|
41
|
-
| ---------------------------------------------------------------- |
|
|
42
|
-
| [MemoryStore](./docs/stores/memory-store.md) | One object
|
|
43
|
-
| [ThreadStore](./docs/stores/thread-store.md) | One process (threads)
|
|
44
|
-
| [IpcStore](./docs/stores/ipc-store.md) | Parent and its children
|
|
45
|
-
| [TicketQueueFileStore](./docs/stores/ticket-queue-file-store.md) | One host
|
|
46
|
-
| [LockFileStore](./docs/stores/lock-file-store.md) | One host
|
|
47
|
-
| [SqliteStore](./docs/stores/sqlite-store.md) | One host
|
|
48
|
-
| [SocketStore](./docs/stores/socket-store.md) | One host
|
|
41
|
+
| Lock store | Import | Reach | Order | A holder stops | Needs |
|
|
42
|
+
| ---------------------------------------------------------------- | ---------------------------- | --------------------------------------- | --------------------------------------- | --------------------------------------------- | -------------------------------------------- |
|
|
43
|
+
| [MemoryStore](./docs/stores/memory-store.md) | `@zukhruf/mutex/memory` | One object | First come, first served | The locks stop with the process | Nothing |
|
|
44
|
+
| [ThreadStore](./docs/stores/thread-store.md) | `@zukhruf/mutex/thread` | One process (threads) | First come, first served | Released when the worker exits | `adopt(worker)` |
|
|
45
|
+
| [IpcStore](./docs/stores/ipc-store.md) | `@zukhruf/mutex/ipc` | Parent and its children | First come, first served | Released in approximately 2 ms | `fork()` and `adopt(child)` |
|
|
46
|
+
| [TicketQueueFileStore](./docs/stores/ticket-queue-file-store.md) | `@zukhruf/mutex/file-system` | One host | First come, first served | Released after a waiter finds that it stopped | A shared directory |
|
|
47
|
+
| [LockFileStore](./docs/stores/lock-file-store.md) | `@zukhruf/mutex/file-system` | One host | No order | Released after a waiter finds that it stopped | A shared directory |
|
|
48
|
+
| [SqliteStore](./docs/stores/sqlite-store.md) | `@zukhruf/mutex/sqlite` | One host | First come, first served in one process | Released by the kernel | A shared directory |
|
|
49
|
+
| [SocketStore](./docs/stores/socket-store.md) | `@zukhruf/mutex/socket` | One host | First come, first served | Released in approximately 2 ms | A shared directory |
|
|
50
|
+
| [PostgresStore](./docs/stores/postgres-store.md) | `@zukhruf/mutex/postgres` | Every machine that reaches the database | Postgres' lock queue | Released when its session ends | A Postgres database and a `connect` function |
|
|
49
51
|
|
|
50
52
|
If you are not sure:
|
|
51
53
|
|
|
52
54
|
- **One process:** use `MemoryStore`.
|
|
53
55
|
- **Several processes on one host:** use `SqliteStore`.
|
|
56
|
+
- **Many machines that already use Postgres:** use `PostgresStore`.
|
|
54
57
|
|
|
55
58
|
## Acquire modes
|
|
56
59
|
|
|
@@ -151,7 +154,8 @@ npm install @zukhruf/mutex
|
|
|
151
154
|
```
|
|
152
155
|
|
|
153
156
|
```ts
|
|
154
|
-
import { Mutex
|
|
157
|
+
import { Mutex } from '@zukhruf/mutex';
|
|
158
|
+
import { SqliteStore } from '@zukhruf/mutex/sqlite';
|
|
155
159
|
```
|
|
156
160
|
|
|
157
161
|
## Development
|
package/dist/index.d.ts
CHANGED
|
@@ -7,19 +7,4 @@ export type { Acquired, AcquireMode, ModeResult, NotAcquired, Outcome, OutcomeRe
|
|
|
7
7
|
export { Modes } from './mutex/acquire-modes/modes.ts';
|
|
8
8
|
export { WaitMode } from './mutex/acquire-modes/wait-mode.ts';
|
|
9
9
|
export { SkipIfBusyMode, type SkipIfBusyOptions, } from './mutex/acquire-modes/skip-if-busy-mode.ts';
|
|
10
|
-
export { MemoryStore, type MemoryStoreOptions, } from './lock-stores/memory/memory-store.ts';
|
|
11
|
-
export { ThreadLockCoordinator, type ThreadLockCoordinatorOptions, } from './lock-stores/thread/thread-lock-coordinator.ts';
|
|
12
|
-
export { ThreadStore } from './lock-stores/thread/thread-store.ts';
|
|
13
|
-
export { IpcLockCoordinator, type IpcLockCoordinatorOptions, } from './lock-stores/ipc/ipc-lock-coordinator.ts';
|
|
14
|
-
export { IpcStore } from './lock-stores/ipc/ipc-store.ts';
|
|
15
|
-
export { CoordinatorUnavailableError } from './lock-stores/remote/coordinator-unavailable-error.ts';
|
|
16
|
-
export { GrantFailedError } from './lock-stores/remote/grant-failed-error.ts';
|
|
17
|
-
export { UnsupportedRequestError } from './lock-stores/remote/unsupported-request-error.ts';
|
|
18
|
-
export { NetworkDirectoryError } from '@zukhruf/fs';
|
|
19
|
-
export { FileLockStore, type FileLockStoreOptions, } from './lock-stores/file-system/file-lock-store.ts';
|
|
20
|
-
export { TicketQueueFileStore } from './lock-stores/file-system/ticket-queue-file-store.ts';
|
|
21
|
-
export { LockFileStore } from './lock-stores/file-system/lock-file-store.ts';
|
|
22
|
-
export { SqliteStore } from './lock-stores/sqlite/sqlite-store.ts';
|
|
23
|
-
export { ProtocolVersionError } from './lock-stores/socket/protocol-version-error.ts';
|
|
24
|
-
export { SocketStore, type SocketRole, type SocketStoreOptions, } from './lock-stores/socket/socket-store.ts';
|
|
25
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EACV,QAAQ,EACR,WAAW,EACX,UAAU,EACV,WAAW,EACX,OAAO,EACP,cAAc,GACf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,KAAK,iBAAiB,GACvB,MAAM,4CAA4C,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EACV,QAAQ,EACR,WAAW,EACX,UAAU,EACV,WAAW,EACX,OAAO,EACP,cAAc,GACf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,EACd,KAAK,iBAAiB,GACvB,MAAM,4CAA4C,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,19 +5,4 @@ export { LeaseLostError } from '@zukhruf/lease';
|
|
|
5
5
|
export { Modes } from "./mutex/acquire-modes/modes.js";
|
|
6
6
|
export { WaitMode } from "./mutex/acquire-modes/wait-mode.js";
|
|
7
7
|
export { SkipIfBusyMode, } from "./mutex/acquire-modes/skip-if-busy-mode.js";
|
|
8
|
-
export { MemoryStore, } from "./lock-stores/memory/memory-store.js";
|
|
9
|
-
export { ThreadLockCoordinator, } from "./lock-stores/thread/thread-lock-coordinator.js";
|
|
10
|
-
export { ThreadStore } from "./lock-stores/thread/thread-store.js";
|
|
11
|
-
export { IpcLockCoordinator, } from "./lock-stores/ipc/ipc-lock-coordinator.js";
|
|
12
|
-
export { IpcStore } from "./lock-stores/ipc/ipc-store.js";
|
|
13
|
-
export { CoordinatorUnavailableError } from "./lock-stores/remote/coordinator-unavailable-error.js";
|
|
14
|
-
export { GrantFailedError } from "./lock-stores/remote/grant-failed-error.js";
|
|
15
|
-
export { UnsupportedRequestError } from "./lock-stores/remote/unsupported-request-error.js";
|
|
16
|
-
export { NetworkDirectoryError } from '@zukhruf/fs';
|
|
17
|
-
export { FileLockStore, } from "./lock-stores/file-system/file-lock-store.js";
|
|
18
|
-
export { TicketQueueFileStore } from "./lock-stores/file-system/ticket-queue-file-store.js";
|
|
19
|
-
export { LockFileStore } from "./lock-stores/file-system/lock-file-store.js";
|
|
20
|
-
export { SqliteStore } from "./lock-stores/sqlite/sqlite-store.js";
|
|
21
|
-
export { ProtocolVersionError } from "./lock-stores/socket/protocol-version-error.js";
|
|
22
|
-
export { SocketStore, } from "./lock-stores/socket/socket-store.js";
|
|
23
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAShD,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,GAEf,MAAM,4CAA4C,CAAC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAShD,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,GAEf,MAAM,4CAA4C,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { FileLockStore, type FileLockStoreOptions } from './file-lock-store.ts';
|
|
2
|
+
export { LockFileStore } from './lock-file-store.ts';
|
|
3
|
+
export { TicketQueueFileStore } from './ticket-queue-file-store.ts';
|
|
4
|
+
export { NetworkDirectoryError } from '@zukhruf/fs';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { IpcLockCoordinator, type IpcLockCoordinatorOptions, } from './ipc-lock-coordinator.ts';
|
|
2
|
+
export { IpcStore } from './ipc-store.ts';
|
|
3
|
+
export { CoordinatorUnavailableError } from '../remote/coordinator-unavailable-error.ts';
|
|
4
|
+
export { GrantFailedError } from '../remote/grant-failed-error.ts';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/ipc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { IpcLockCoordinator, } from "./ipc-lock-coordinator.js";
|
|
2
|
+
export { IpcStore } from "./ipc-store.js";
|
|
3
|
+
export { CoordinatorUnavailableError } from "../remote/coordinator-unavailable-error.js";
|
|
4
|
+
export { GrantFailedError } from "../remote/grant-failed-error.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lock-stores/ipc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lock-stores/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA2B,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lock-stores/postgres/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,GAGd,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { LockHandle } from '../../mutex/lease.ts';
|
|
2
|
+
import type { AcquireOptions, LockStore } from '../../mutex/lock-store.ts';
|
|
3
|
+
/**
|
|
4
|
+
* One connection to the Postgres server: only the methods that the store
|
|
5
|
+
* calls. A `PoolClient` of `pg` (node-postgres) fits it.
|
|
6
|
+
*/
|
|
7
|
+
export interface PostgresSession {
|
|
8
|
+
query(text: string, values: unknown[]): Promise<{
|
|
9
|
+
rows: Record<string, unknown>[];
|
|
10
|
+
}>;
|
|
11
|
+
on(event: 'error', listener: (error: Error) => void): unknown;
|
|
12
|
+
on(event: 'end', listener: () => void): unknown;
|
|
13
|
+
off(event: 'error' | 'end', listener: (error: Error) => void): unknown;
|
|
14
|
+
/** `true` destroys the session; `false` gives it back to its pool. */
|
|
15
|
+
release(destroy: boolean): void;
|
|
16
|
+
}
|
|
17
|
+
export interface PostgresStoreOptions {
|
|
18
|
+
/** Gives a new session. The store owns each session that it gets. */
|
|
19
|
+
connect(): Promise<PostgresSession>;
|
|
20
|
+
/** The name of an existing sequence for the fencing tokens. The store runs no DDL. */
|
|
21
|
+
sequence: string;
|
|
22
|
+
/**
|
|
23
|
+
* Milliseconds between two heartbeats of a holder, and the time limit of
|
|
24
|
+
* each heartbeat: the longest time that a holder can work after its session
|
|
25
|
+
* ended, before its signal aborts.
|
|
26
|
+
*/
|
|
27
|
+
heartbeatInterval: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Holds a session-level advisory lock of Postgres for each key, on a session
|
|
31
|
+
* of its own. The server releases the lock when the session ends, so a
|
|
32
|
+
* holder that stops never leaves a key locked. Every process on every machine
|
|
33
|
+
* that reaches one database shares the locks.
|
|
34
|
+
*
|
|
35
|
+
* Callers in this process first line up in a queue in memory, so only the
|
|
36
|
+
* first one gets a session: one session per key per process, however many
|
|
37
|
+
* callers wait, and first-come order within the process.
|
|
38
|
+
*/
|
|
39
|
+
export declare class PostgresStore implements LockStore {
|
|
40
|
+
#private;
|
|
41
|
+
constructor(options: PostgresStoreOptions);
|
|
42
|
+
acquire(key: string, options?: AcquireOptions): Promise<LockHandle>;
|
|
43
|
+
tryAcquire(key: string): Promise<LockHandle | undefined>;
|
|
44
|
+
/** Reads `pg_locks` on a short session, and never runs an advisory lock function. */
|
|
45
|
+
isHeld(key: string): Promise<boolean>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=postgres-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/postgres/postgres-store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAG3E;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EAAE,GAChB,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;IAChD,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;IAC9D,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;IAChD,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;IACvE,sEAAsE;IACtE,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IACpC,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAWD;;;;;;;;;GASG;AACH,qBAAa,aAAc,YAAW,SAAS;;gBAIjC,OAAO,EAAE,oBAAoB;IAInC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;IAUnE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAU9D,qFAAqF;IAC/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAkC5C"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { untilAborted } from '@zukhruf/async';
|
|
4
|
+
import { FencingToken } from '@zukhruf/fencing';
|
|
5
|
+
import { LeaseController } from '@zukhruf/lease';
|
|
6
|
+
import { MemoryStore } from "../memory/memory-store.js";
|
|
7
|
+
const heldQuery = `SELECT EXISTS (
|
|
8
|
+
SELECT 1 FROM pg_catalog.pg_locks
|
|
9
|
+
WHERE locktype = 'advisory'
|
|
10
|
+
AND database = (SELECT oid FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database())
|
|
11
|
+
AND objsubid = 1
|
|
12
|
+
AND ((classid::bigint << 32) | objid::bigint) = $1::bigint
|
|
13
|
+
AND granted
|
|
14
|
+
) AS held`;
|
|
15
|
+
/**
|
|
16
|
+
* Holds a session-level advisory lock of Postgres for each key, on a session
|
|
17
|
+
* of its own. The server releases the lock when the session ends, so a
|
|
18
|
+
* holder that stops never leaves a key locked. Every process on every machine
|
|
19
|
+
* that reaches one database shares the locks.
|
|
20
|
+
*
|
|
21
|
+
* Callers in this process first line up in a queue in memory, so only the
|
|
22
|
+
* first one gets a session: one session per key per process, however many
|
|
23
|
+
* callers wait, and first-come order within the process.
|
|
24
|
+
*/
|
|
25
|
+
export class PostgresStore {
|
|
26
|
+
#inProcess = new MemoryStore();
|
|
27
|
+
#options;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.#options = options;
|
|
30
|
+
}
|
|
31
|
+
async acquire(key, options) {
|
|
32
|
+
const signal = options?.signal;
|
|
33
|
+
await using turn = new AsyncDisposableStack();
|
|
34
|
+
// Joining the in-process queue is synchronous, so the order is the order of the calls.
|
|
35
|
+
turn.use(await this.#inProcess.acquire(key, { signal }));
|
|
36
|
+
const session = await this.#session(key, signal);
|
|
37
|
+
await session.wait(signal);
|
|
38
|
+
return withTurn(await session.hold(), turn);
|
|
39
|
+
}
|
|
40
|
+
async tryAcquire(key) {
|
|
41
|
+
await using turn = new AsyncDisposableStack();
|
|
42
|
+
const inProcess = await this.#inProcess.tryAcquire(key);
|
|
43
|
+
if (!inProcess)
|
|
44
|
+
return undefined;
|
|
45
|
+
turn.use(inProcess);
|
|
46
|
+
const session = await this.#session(key, undefined);
|
|
47
|
+
if (!(await session.try()))
|
|
48
|
+
return undefined;
|
|
49
|
+
return withTurn(await session.hold(), turn);
|
|
50
|
+
}
|
|
51
|
+
/** Reads `pg_locks` on a short session, and never runs an advisory lock function. */
|
|
52
|
+
async isHeld(key) {
|
|
53
|
+
const session = new OwnedSession(await this.#options.connect());
|
|
54
|
+
try {
|
|
55
|
+
const [row] = await session.query(heldQuery, [lockNumber(key)]);
|
|
56
|
+
session.giveBack();
|
|
57
|
+
return row?.held === true;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
session.destroy();
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Gets a session for `key`. A cancel never waits for the pool: a session that comes later goes back at once. */
|
|
65
|
+
async #session(key, signal) {
|
|
66
|
+
const connecting = this.#options.connect();
|
|
67
|
+
try {
|
|
68
|
+
return new KeySession(new OwnedSession(await untilAborted(connecting, signal)), key, this.#options);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (signal?.aborted && error === signal.reason) {
|
|
72
|
+
void connecting.then((late) => late.release(false), () => { });
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The number of the advisory lock of `key`: the first 8 bytes of the SHA-256
|
|
80
|
+
* digest of its UTF-16 code units, as a signed 64-bit integer. The code units,
|
|
81
|
+
* not UTF-8, so that keys that differ only in a lone surrogate get different
|
|
82
|
+
* numbers. Sent as text, because a JavaScript number cannot hold 64 bits.
|
|
83
|
+
*/
|
|
84
|
+
function lockNumber(key) {
|
|
85
|
+
return createHash('sha256')
|
|
86
|
+
.update(Buffer.from(key, 'utf16le'))
|
|
87
|
+
.digest()
|
|
88
|
+
.readBigInt64BE(0)
|
|
89
|
+
.toString();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A session from `connect` that the store owns until it gives it back or
|
|
93
|
+
* destroys it, once. Its `ended` signal aborts when the session emits `error`
|
|
94
|
+
* or `end`. Its listeners stay on a destroyed session, because node-postgres
|
|
95
|
+
* can emit `error` again after the close.
|
|
96
|
+
*/
|
|
97
|
+
class OwnedSession {
|
|
98
|
+
#session;
|
|
99
|
+
#ended = new AbortController();
|
|
100
|
+
#owned = new AbortController();
|
|
101
|
+
#onError = (error) => this.#ended.abort(error);
|
|
102
|
+
#onEnd = () => this.#ended.abort(new Error('The Postgres session ended.'));
|
|
103
|
+
constructor(session) {
|
|
104
|
+
this.#session = session;
|
|
105
|
+
session.on('error', this.#onError);
|
|
106
|
+
session.on('end', this.#onEnd);
|
|
107
|
+
}
|
|
108
|
+
get ended() {
|
|
109
|
+
return this.#ended.signal;
|
|
110
|
+
}
|
|
111
|
+
async query(text, values) {
|
|
112
|
+
return (await this.#session.query(text, values)).rows;
|
|
113
|
+
}
|
|
114
|
+
/** The session holds nothing: it goes back to the pool with its own settings. */
|
|
115
|
+
giveBack() {
|
|
116
|
+
if (this.#owned.signal.aborted)
|
|
117
|
+
return;
|
|
118
|
+
this.#owned.abort();
|
|
119
|
+
this.#session.off('error', this.#onError);
|
|
120
|
+
this.#session.off('end', this.#onEnd);
|
|
121
|
+
this.#session.release(false);
|
|
122
|
+
}
|
|
123
|
+
/** The lock state of the session is not known: the server releases each lock of a session that ends. */
|
|
124
|
+
destroy() {
|
|
125
|
+
if (this.#owned.signal.aborted)
|
|
126
|
+
return;
|
|
127
|
+
this.#owned.abort();
|
|
128
|
+
this.#session.release(true);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** One session for one key: it waits for the lock or tries it, then holds it with a heartbeat. */
|
|
132
|
+
class KeySession {
|
|
133
|
+
#session;
|
|
134
|
+
#key;
|
|
135
|
+
#number;
|
|
136
|
+
#options;
|
|
137
|
+
#lease;
|
|
138
|
+
/** Aborts when the store stops holding: a release, or a lost session. */
|
|
139
|
+
#done = new AbortController();
|
|
140
|
+
constructor(session, key, options) {
|
|
141
|
+
this.#session = session;
|
|
142
|
+
this.#key = key;
|
|
143
|
+
this.#number = lockNumber(key);
|
|
144
|
+
this.#options = options;
|
|
145
|
+
this.#lease = new LeaseController(key);
|
|
146
|
+
}
|
|
147
|
+
/** Waits in Postgres' lock queue. A cancel closes the session, and the end of the session releases a late grant. */
|
|
148
|
+
async wait(signal) {
|
|
149
|
+
try {
|
|
150
|
+
// A timeout of the pool must not stop the wait.
|
|
151
|
+
await this.#session.query('SET statement_timeout = 0; SET lock_timeout = 0', []);
|
|
152
|
+
await untilAborted(this.#session.query('SELECT pg_advisory_lock($1::bigint)', [
|
|
153
|
+
this.#number,
|
|
154
|
+
]), signal);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
this.#session.destroy();
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async try() {
|
|
162
|
+
try {
|
|
163
|
+
const [row] = await this.#session.query('SELECT pg_try_advisory_lock($1::bigint) AS locked', [this.#number]);
|
|
164
|
+
if (row?.locked === true)
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
this.#session.destroy();
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
this.#session.giveBack();
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
/** Mints the token of a granted lock and starts the heartbeat. A failed mint releases the key. */
|
|
175
|
+
async hold() {
|
|
176
|
+
let token;
|
|
177
|
+
try {
|
|
178
|
+
const [row] = await this.#session.query('SELECT nextval($1::regclass)::text AS token', [this.#options.sequence]);
|
|
179
|
+
if (typeof row?.token !== 'string') {
|
|
180
|
+
throw new Error(`nextval of sequence ${JSON.stringify(this.#options.sequence)} gave no number.`);
|
|
181
|
+
}
|
|
182
|
+
token = new FencingToken(BigInt(row.token));
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
await this.#release();
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
const ended = this.#session.ended;
|
|
189
|
+
ended.addEventListener('abort', () => this.#lose(ended.reason), {
|
|
190
|
+
once: true,
|
|
191
|
+
signal: this.#done.signal,
|
|
192
|
+
});
|
|
193
|
+
void this.#beat();
|
|
194
|
+
return {
|
|
195
|
+
token,
|
|
196
|
+
signal: this.#lease.signal,
|
|
197
|
+
[Symbol.asyncDispose]: () => this.#release(),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/** Sends `SELECT 1` each interval. A failed or late answer loses the lease. */
|
|
201
|
+
async #beat() {
|
|
202
|
+
const interval = this.#options.heartbeatInterval;
|
|
203
|
+
const done = this.#done.signal;
|
|
204
|
+
try {
|
|
205
|
+
for (;;) {
|
|
206
|
+
await delay(interval, undefined, { signal: done });
|
|
207
|
+
await untilAborted(this.#session.query('SELECT 1', []), AbortSignal.any([done, AbortSignal.timeout(interval)]));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (cause) {
|
|
211
|
+
if (!done.aborted)
|
|
212
|
+
this.#lose(cause);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** Another holder can have the key now. */
|
|
216
|
+
#lose(cause) {
|
|
217
|
+
if (this.#done.signal.aborted)
|
|
218
|
+
return;
|
|
219
|
+
this.#done.abort();
|
|
220
|
+
this.#lease.lose(cause);
|
|
221
|
+
this.#session.destroy();
|
|
222
|
+
}
|
|
223
|
+
async #release() {
|
|
224
|
+
if (this.#done.signal.aborted)
|
|
225
|
+
return;
|
|
226
|
+
this.#done.abort();
|
|
227
|
+
try {
|
|
228
|
+
const [row] = await this.#session.query('SELECT pg_advisory_unlock($1::bigint) AS released', [this.#number]);
|
|
229
|
+
if (row?.released !== true) {
|
|
230
|
+
throw new Error(`The session did not hold the lock of ${JSON.stringify(this.#key)}.`);
|
|
231
|
+
}
|
|
232
|
+
await this.#session.query('RESET statement_timeout; RESET lock_timeout', []);
|
|
233
|
+
}
|
|
234
|
+
catch (cause) {
|
|
235
|
+
// A plain release would give the next borrower a lock that this session may still hold.
|
|
236
|
+
this.#lease.lose(cause);
|
|
237
|
+
this.#session.destroy();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
this.#lease.end();
|
|
241
|
+
this.#session.giveBack();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/** Releases the Postgres lock first, then lets the next caller in this process continue. */
|
|
245
|
+
function withTurn(handle, turn) {
|
|
246
|
+
turn.use(handle);
|
|
247
|
+
const release = turn.move();
|
|
248
|
+
return {
|
|
249
|
+
token: handle.token,
|
|
250
|
+
signal: handle.signal,
|
|
251
|
+
[Symbol.asyncDispose]: () => release.disposeAsync(),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=postgres-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-store.js","sourceRoot":"","sources":["../../../src/lock-stores/postgres/postgres-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { SocketStore, type SocketRole, type SocketStoreOptions, } from './socket-store.ts';
|
|
2
|
+
export { ProtocolVersionError } from './protocol-version-error.ts';
|
|
3
|
+
export { UnsupportedRequestError } from '../remote/unsupported-request-error.ts';
|
|
4
|
+
export { CoordinatorUnavailableError } from '../remote/coordinator-unavailable-error.ts';
|
|
5
|
+
export { GrantFailedError } from '../remote/grant-failed-error.ts';
|
|
6
|
+
export { NetworkDirectoryError } from '@zukhruf/fs';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,KAAK,UAAU,EACf,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { SocketStore, } from "./socket-store.js";
|
|
2
|
+
export { ProtocolVersionError } from "./protocol-version-error.js";
|
|
3
|
+
export { UnsupportedRequestError } from "../remote/unsupported-request-error.js";
|
|
4
|
+
export { CoordinatorUnavailableError } from "../remote/coordinator-unavailable-error.js";
|
|
5
|
+
export { GrantFailedError } from "../remote/grant-failed-error.js";
|
|
6
|
+
export { NetworkDirectoryError } from '@zukhruf/fs';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,GAGZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AACjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/sqlite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lock-stores/sqlite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ThreadLockCoordinator, type ThreadLockCoordinatorOptions, } from './thread-lock-coordinator.ts';
|
|
2
|
+
export { ThreadStore } from './thread-store.ts';
|
|
3
|
+
export { CoordinatorUnavailableError } from '../remote/coordinator-unavailable-error.ts';
|
|
4
|
+
export { GrantFailedError } from '../remote/grant-failed-error.ts';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/thread/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ThreadLockCoordinator, } from "./thread-lock-coordinator.js";
|
|
2
|
+
export { ThreadStore } from "./thread-store.js";
|
|
3
|
+
export { CoordinatorUnavailableError } from "../remote/coordinator-unavailable-error.js";
|
|
4
|
+
export { GrantFailedError } from "../remote/grant-failed-error.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lock-stores/thread/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAEtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zukhruf/mutex",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.40",
|
|
4
4
|
"description": "A mutex with interchangeable lock stores, from one object to every process on a host, with fencing tokens on every lease.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,6 +26,13 @@
|
|
|
26
26
|
"types": "./dist/index.d.ts",
|
|
27
27
|
"import": "./dist/index.js",
|
|
28
28
|
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./remote": null,
|
|
31
|
+
"./*": {
|
|
32
|
+
"@zukhruf/source": "./src/lock-stores/*/index.ts",
|
|
33
|
+
"types": "./dist/lock-stores/*/index.d.ts",
|
|
34
|
+
"import": "./dist/lock-stores/*/index.js",
|
|
35
|
+
"default": "./dist/lock-stores/*/index.js"
|
|
29
36
|
}
|
|
30
37
|
},
|
|
31
38
|
"files": [
|
|
@@ -38,14 +45,16 @@
|
|
|
38
45
|
"access": "public"
|
|
39
46
|
},
|
|
40
47
|
"dependencies": {
|
|
41
|
-
"@zukhruf/async": "^0.3.
|
|
42
|
-
"@zukhruf/election": "^0.3.
|
|
43
|
-
"@zukhruf/fencing": "^0.3.
|
|
44
|
-
"@zukhruf/fs": "^0.3.
|
|
45
|
-
"@zukhruf/lease": "^0.3.
|
|
48
|
+
"@zukhruf/async": "^0.3.40",
|
|
49
|
+
"@zukhruf/election": "^0.3.40",
|
|
50
|
+
"@zukhruf/fencing": "^0.3.40",
|
|
51
|
+
"@zukhruf/fs": "^0.3.40",
|
|
52
|
+
"@zukhruf/lease": "^0.3.40"
|
|
46
53
|
},
|
|
47
54
|
"devDependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"
|
|
55
|
+
"@types/pg": "^8.23.1",
|
|
56
|
+
"@zukhruf/testing": "^0.3.40",
|
|
57
|
+
"hono": "^4.13.13",
|
|
58
|
+
"pg": "^8.23.1"
|
|
50
59
|
}
|
|
51
60
|
}
|