@zukhruf/mutex 0.3.0 → 0.3.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.
Files changed (28) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/lock-stores/file-system/file-lock-store.d.ts +6 -3
  7. package/dist/lock-stores/file-system/file-lock-store.d.ts.map +1 -1
  8. package/dist/lock-stores/file-system/file-lock-store.js +38 -12
  9. package/dist/lock-stores/file-system/file-lock-store.js.map +1 -1
  10. package/dist/lock-stores/socket/electing-connector.d.ts.map +1 -1
  11. package/dist/lock-stores/socket/electing-connector.js +61 -5
  12. package/dist/lock-stores/socket/electing-connector.js.map +1 -1
  13. package/dist/lock-stores/socket/handshake.d.ts +27 -0
  14. package/dist/lock-stores/socket/handshake.d.ts.map +1 -0
  15. package/dist/lock-stores/socket/handshake.js +80 -0
  16. package/dist/lock-stores/socket/handshake.js.map +1 -0
  17. package/dist/lock-stores/socket/lock-server.d.ts.map +1 -1
  18. package/dist/lock-stores/socket/lock-server.js +7 -1
  19. package/dist/lock-stores/socket/lock-server.js.map +1 -1
  20. package/dist/lock-stores/socket/protocol-version-error.d.ts +12 -0
  21. package/dist/lock-stores/socket/protocol-version-error.d.ts.map +1 -0
  22. package/dist/lock-stores/socket/protocol-version-error.js +19 -0
  23. package/dist/lock-stores/socket/protocol-version-error.js.map +1 -0
  24. package/dist/shared/sqlite/is-not-a-database.d.ts +3 -0
  25. package/dist/shared/sqlite/is-not-a-database.d.ts.map +1 -0
  26. package/dist/shared/sqlite/is-not-a-database.js +9 -0
  27. package/dist/shared/sqlite/is-not-a-database.js.map +1 -0
  28. package/package.json +1 -1
package/README.md CHANGED
@@ -100,7 +100,7 @@ See [Fencing tokens](./docs/concepts/fencing-tokens.md) and the recipe [Protect
100
100
  - [Leader election](./docs/concepts/leader-election.md): how `SocketStore` selects its coordinator.
101
101
  - [Failure modes](./docs/concepts/failure-modes.md): what each lock store does when something stops.
102
102
 
103
- **Lock stores**: one page for each lock store, with What, Why, When, When not, How it works, Failure modes, Options, and Evidence. See the table above.
103
+ **Lock stores**: one page for each lock store, with What, Why, When, When not, How it works, Acquire modes, Failure modes, Options, and Evidence. See the table above.
104
104
 
105
105
  **Recipes**: one use case each, with a full program that you can run.
106
106
 
package/dist/index.d.ts CHANGED
@@ -24,5 +24,6 @@ export { FileLockStore, type FileLockStoreOptions, } from './lock-stores/file-sy
24
24
  export { TicketQueueFileStore } from './lock-stores/file-system/ticket-queue-file-store.ts';
25
25
  export { LockFileStore } from './lock-stores/file-system/lock-file-store.ts';
26
26
  export { SqliteStore } from './lock-stores/sqlite/sqlite-store.ts';
27
+ export { ProtocolVersionError } from './lock-stores/socket/protocol-version-error.ts';
27
28
  export { SocketStore, type SocketRole, type SocketStoreOptions, } from './lock-stores/socket/socket-store.ts';
28
29
  //# sourceMappingURL=index.d.ts.map
@@ -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,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,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;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,GACxB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,uDAAuD,CAAC;AACpG,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,GAC1B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,WAAW,EACX,KAAK,UAAU,EACf,KAAK,kBAAkB,GACxB,MAAM,sCAAsC,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,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,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;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,GACxB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,uDAAuD,CAAC;AACpG,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,GAC1B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,OAAO,EACL,WAAW,EACX,KAAK,UAAU,EACf,KAAK,kBAAkB,GACxB,MAAM,sCAAsC,CAAC"}
package/dist/index.js CHANGED
@@ -21,5 +21,6 @@ export { FileLockStore, } from "./lock-stores/file-system/file-lock-store.js";
21
21
  export { TicketQueueFileStore } from "./lock-stores/file-system/ticket-queue-file-store.js";
22
22
  export { LockFileStore } from "./lock-stores/file-system/lock-file-store.js";
23
23
  export { SqliteStore } from "./lock-stores/sqlite/sqlite-store.js";
24
+ export { ProtocolVersionError } from "./lock-stores/socket/protocol-version-error.js";
24
25
  export { SocketStore, } from "./lock-stores/socket/socket-store.js";
25
26
  //# 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,EAAc,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAS3D,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,GAEf,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EACL,WAAW,GAEZ,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,GAEtB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,kBAAkB,GAEnB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,uDAAuD,CAAC;AACpG,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EACL,aAAa,GAEd,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,WAAW,GAGZ,MAAM,sCAAsC,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,EAAc,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAS3D,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EACL,cAAc,GAEf,MAAM,4CAA4C,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EACL,WAAW,GAEZ,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,qBAAqB,GAEtB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EACL,kBAAkB,GAEnB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,uDAAuD,CAAC;AACpG,OAAO,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,EACL,aAAa,GAEd,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sDAAsD,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gDAAgD,CAAC;AACtF,OAAO,EACL,WAAW,GAGZ,MAAM,sCAAsC,CAAC"}
@@ -26,9 +26,12 @@ export declare abstract class FileLockStore implements LockStore {
26
26
  protected poll<T>(attempt: () => Promise<T | undefined>, { signal, keepAlive }?: PollOptions): Promise<T>;
27
27
  /**
28
28
  * Serializes evicting a dead holder. Without it, two waiters that saw the
29
- * same dead holder could each remove a lock the other had just taken.
30
- * A process dying inside `task` leaves `<path>.reclaim` behind, which then
31
- * has to be removed by hand.
29
+ * same dead holder could each remove a lock the other had just taken. The
30
+ * kernel holds this lock, so a waiter that dies while it evicts frees it at
31
+ * once. Its file stays for the next eviction: deleting it while waiters use
32
+ * it would let two of them lock two different files. Nothing may open that
33
+ * file except through SQLite: closing any other handle to it drops this
34
+ * process's lock without a word.
32
35
  */
33
36
  protected withReclaimLock(path: string, task: () => Promise<void>): Promise<void>;
34
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-lock-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EAAE,KAAK,KAAK,EAAY,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAM3E,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,8FAA8F;AAC9F,8BAAsB,aAAc,YAAW,SAAS;;gBAMpD,SAAS,EAAE,MAAM,EACjB,EACE,YAAiB,EACjB,MAAuC,GACxC,GAAE,oBAAyB;IAOxB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC;IAMrE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAKzD,kFAAkF;IAClF,SAAS,CAAC,QAAQ,CAAC,IAAI,CACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,eAAe,CAAC;IAE3B,mEAAmE;IACnE,SAAS,CAAC,QAAQ,CAAC,OAAO,CACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;cAEvB,IAAI,CAAC,CAAC,EACpB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,EACrC,EAAE,MAAM,EAAE,SAAgB,EAAE,GAAE,WAAgB,GAC7C,OAAO,CAAC,CAAC,CAAC;IAab;;;;;OAKG;cACa,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;CAexE"}
1
+ {"version":3,"file":"file-lock-store.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EAAE,KAAK,KAAK,EAAY,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAK3E,MAAM,WAAW,oBAAoB;IACnC,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,8FAA8F;AAC9F,8BAAsB,aAAc,YAAW,SAAS;;gBAMpD,SAAS,EAAE,MAAM,EACjB,EACE,YAAiB,EACjB,MAAuC,GACxC,GAAE,oBAAyB;IAOxB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC;IAMrE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAKzD,kFAAkF;IAClF,SAAS,CAAC,QAAQ,CAAC,IAAI,CACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,eAAe,CAAC;IAE3B,mEAAmE;IACnE,SAAS,CAAC,QAAQ,CAAC,OAAO,CACxB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;cAEvB,IAAI,CAAC,CAAC,EACpB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,EACrC,EAAE,MAAM,EAAE,SAAgB,EAAE,GAAE,WAAgB,GAC7C,OAAO,CAAC,CAAC,CAAC;IAab;;;;;;;;OAQG;cACa,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;CAoBxE"}
@@ -1,13 +1,13 @@
1
- import { mkdir, unlink } from 'node:fs/promises';
1
+ import { mkdir } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
+ import { DatabaseSync } from 'node:sqlite';
3
4
  import { setTimeout as delay } from 'node:timers/promises';
4
5
  import { FileTokenSource } from "../../fencing/file-token-source.js";
5
6
  import { assertLocalDirectory } from "../../local-directory/local-directory.js";
6
7
  import { leaseFor } from "../../mutex/lease.js";
7
- import { createExclusive } from "../../shared/fs/create-exclusive.js";
8
- import { patiently } from "../../shared/fs/patiently.js";
9
8
  import { safeFileName } from "../../shared/fs/safe-file-name.js";
10
- import { Owner } from "./owner.js";
9
+ import { isBusy } from "../../shared/sqlite/is-busy.js";
10
+ import { isNotADatabase } from "../../shared/sqlite/is-not-a-database.js";
11
11
  /** Shares one lock directory between processes: each key maps to `<directory>/<key>.lock`. */
12
12
  export class FileLockStore {
13
13
  #directory;
@@ -43,19 +43,28 @@ export class FileLockStore {
43
43
  }
44
44
  /**
45
45
  * Serializes evicting a dead holder. Without it, two waiters that saw the
46
- * same dead holder could each remove a lock the other had just taken.
47
- * A process dying inside `task` leaves `<path>.reclaim` behind, which then
48
- * has to be removed by hand.
46
+ * same dead holder could each remove a lock the other had just taken. The
47
+ * kernel holds this lock, so a waiter that dies while it evicts frees it at
48
+ * once. Its file stays for the next eviction: deleting it while waiters use
49
+ * it would let two of them lock two different files. Nothing may open that
50
+ * file except through SQLite: closing any other handle to it drops this
51
+ * process's lock without a word.
49
52
  */
50
53
  async withReclaimLock(path, task) {
51
- const reclaim = `${path}.reclaim`;
52
- if (!(await createExclusive(reclaim, Owner.current().serialize())))
53
- return;
54
+ const reclaimPath = `${path}.reclaim`;
55
+ const reclaim = new DatabaseSync(reclaimPath, { timeout: 0 });
54
56
  try {
55
- await task();
57
+ if (!claim(reclaim, reclaimPath))
58
+ return;
59
+ try {
60
+ await task();
61
+ }
62
+ finally {
63
+ reclaim.exec('ROLLBACK');
64
+ }
56
65
  }
57
66
  finally {
58
- await patiently(() => unlink(reclaim));
67
+ reclaim.close();
59
68
  }
60
69
  }
61
70
  async #pathFor(key) {
@@ -64,4 +73,21 @@ export class FileLockStore {
64
73
  return join(this.#directory, `${safeFileName(key)}.lock`);
65
74
  }
66
75
  }
76
+ /** Whether this connection now holds the reclaim lock; another waiter is evicting if not. */
77
+ function claim(reclaim, path) {
78
+ try {
79
+ // A journal on disk would outlive a waiter that dies while it holds the lock.
80
+ reclaim.exec('PRAGMA journal_mode = MEMORY');
81
+ reclaim.exec('BEGIN EXCLUSIVE');
82
+ return true;
83
+ }
84
+ catch (error) {
85
+ if (isBusy(error))
86
+ return false;
87
+ if (isNotADatabase(error)) {
88
+ throw new Error(`The reclaim file ${JSON.stringify(path)} was left by an older version of @zukhruf/mutex. Stop the processes that run that version, then delete the file.`, { cause: error });
89
+ }
90
+ throw error;
91
+ }
92
+ }
67
93
  //# sourceMappingURL=file-lock-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-lock-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-lock-store.js","sourceRoot":"","sources":["../../../src/lock-stores/file-system/file-lock-store.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"electing-connector.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAQhF,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,yEAAyE;IACzE,SAAS,IAAI,IAAI,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,eAAe;;gBAG3C,OAAO,EAAE,wBAAwB;IAIvC,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAoC9D"}
1
+ {"version":3,"file":"electing-connector.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAEtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAgBhF,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,yEAAyE;IACzE,SAAS,IAAI,IAAI,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,eAAe;;gBAG3C,OAAO,EAAE,wBAAwB;IAIvC,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAyE9D"}
@@ -1,7 +1,15 @@
1
1
  import { connect } from 'node:net';
2
2
  import { setTimeout as delay } from 'node:timers/promises';
3
+ import { untilAborted } from "../../shared/until-aborted.js";
3
4
  import { isLockResponse, } from "../remote/protocol.js";
5
+ import { PROTOCOL_VERSION, greet } from "./handshake.js";
6
+ import { ProtocolVersionError } from "./protocol-version-error.js";
4
7
  import { SocketConnection } from "./socket-connection.js";
8
+ /**
9
+ * How long a leader that hung up on the hello may take to end its term. One
10
+ * that stops ends it within moments; one from before the handshake keeps it.
11
+ */
12
+ const HANDSHAKE_PATIENCE = 1000;
5
13
  /**
6
14
  * Reaches the leader's socket, and when nobody serves it, campaigns to become
7
15
  * the leader. Never gives up: a lost leader is always replaced by a candidate.
@@ -13,14 +21,17 @@ export class ElectingConnector {
13
21
  this.#options = options;
14
22
  }
15
23
  async connect(signal) {
16
- const { socketPath, election, pollInterval, serve, connected } = this.#options;
24
+ const { socketPath, election, pollInterval, serve } = this.#options;
17
25
  for (;;) {
18
26
  const leader = await reachUnlessAborted(socketPath, signal);
19
27
  if (leader) {
20
- connected();
21
- return new SocketConnection(leader, isLockResponse);
28
+ const connection = await this.#follow(leader, signal);
29
+ if (connection)
30
+ return connection;
22
31
  }
23
- const leadership = await election.campaign({ timeout: pollInterval });
32
+ const leadership = await election.campaign({
33
+ timeout: leader ? HANDSHAKE_PATIENCE : pollInterval,
34
+ });
24
35
  if (signal.aborted)
25
36
  await leadership?.resign();
26
37
  signal.throwIfAborted();
@@ -36,7 +47,22 @@ export class ElectingConnector {
36
47
  // This process leads now, so it reaches its own server without following anyone.
37
48
  const own = await reachUnlessAborted(socketPath, signal);
38
49
  if (own) {
39
- return new SocketConnection(own, isLockResponse);
50
+ if ((await greetUnlessAborted(own, signal)).kind === 'welcome') {
51
+ return new SocketConnection(own, isLockResponse);
52
+ }
53
+ own.destroy();
54
+ }
55
+ }
56
+ else if (leader) {
57
+ // The term is still held: either the leader that hung up predates the
58
+ // handshake, or a successor took over while it stopped. Only the
59
+ // successor answers a second hello.
60
+ const again = await reachUnlessAborted(socketPath, signal);
61
+ if (again) {
62
+ const connection = await this.#follow(again, signal);
63
+ if (connection)
64
+ return connection;
65
+ throw new ProtocolVersionError(PROTOCOL_VERSION, undefined);
40
66
  }
41
67
  }
42
68
  else {
@@ -44,6 +70,26 @@ export class ElectingConnector {
44
70
  }
45
71
  }
46
72
  }
73
+ /** Follows the leader on `socket` if it speaks this protocol; `undefined` when it hung up on the hello. */
74
+ async #follow(socket, signal) {
75
+ const greeting = await greetUnlessAborted(socket, signal);
76
+ if (greeting.kind !== 'welcome') {
77
+ socket.destroy();
78
+ if (greeting.kind === 'refused') {
79
+ throw new ProtocolVersionError(PROTOCOL_VERSION, greeting.version);
80
+ }
81
+ return undefined;
82
+ }
83
+ try {
84
+ this.#options.connected();
85
+ }
86
+ catch (error) {
87
+ // Nobody will use this socket, and an open one would keep the process alive.
88
+ socket.destroy();
89
+ throw error;
90
+ }
91
+ return new SocketConnection(socket, isLockResponse);
92
+ }
47
93
  }
48
94
  /** Like `reach`, but a socket reached after `signal` aborted is destroyed, not returned. */
49
95
  async function reachUnlessAborted(socketPath, signal) {
@@ -69,4 +115,14 @@ function reach(socketPath) {
69
115
  });
70
116
  });
71
117
  }
118
+ /** Greets the leader on `socket`; a store that closes meanwhile gives the socket up. */
119
+ async function greetUnlessAborted(socket, signal) {
120
+ try {
121
+ return await untilAborted(greet(socket), signal);
122
+ }
123
+ catch (error) {
124
+ socket.destroy();
125
+ throw error;
126
+ }
127
+ }
72
128
  //# sourceMappingURL=electing-connector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"electing-connector.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"electing-connector.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/electing-connector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { Socket } from 'node:net';
2
+ /**
3
+ * The version of the messages a socket store's processes exchange. It changes
4
+ * only when they change, so processes that run different package versions
5
+ * with the same protocol still share a directory.
6
+ */
7
+ export declare const PROTOCOL_VERSION = 1;
8
+ /** What a leader answered to this process's `hello`. */
9
+ export type Greeting = {
10
+ kind: 'welcome';
11
+ } | {
12
+ kind: 'refused';
13
+ version: number;
14
+ }
15
+ /** The leader closed the connection without an answer: it stops, or it predates the handshake. */
16
+ | {
17
+ kind: 'closed';
18
+ };
19
+ /** Says which protocol this process speaks, and reads the leader's answer. */
20
+ export declare function greet(socket: Socket): Promise<Greeting>;
21
+ /**
22
+ * Reads a new peer's `hello`. A peer that speaks this protocol is welcomed;
23
+ * any other first line is refused with this leader's version, and the
24
+ * connection ends once that answer is written.
25
+ */
26
+ export declare function welcome(socket: Socket): Promise<boolean>;
27
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/handshake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIvC;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,wDAAwD;AACxD,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACtC,kGAAkG;GAChG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvB,8EAA8E;AAC9E,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAc7D;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgB9D"}
@@ -0,0 +1,80 @@
1
+ import { isRecord } from "../../shared/is-record.js";
2
+ /**
3
+ * The version of the messages a socket store's processes exchange. It changes
4
+ * only when they change, so processes that run different package versions
5
+ * with the same protocol still share a directory.
6
+ */
7
+ export const PROTOCOL_VERSION = 1;
8
+ /** Says which protocol this process speaks, and reads the leader's answer. */
9
+ export async function greet(socket) {
10
+ socket.write(`${JSON.stringify({ op: 'hello', version: PROTOCOL_VERSION })}\n`);
11
+ const answer = parse(await readLine(socket));
12
+ if (isRecord(answer) && answer.op === 'welcome')
13
+ return { kind: 'welcome' };
14
+ if (isRecord(answer) &&
15
+ answer.op === 'refused' &&
16
+ typeof answer.version === 'number') {
17
+ return { kind: 'refused', version: answer.version };
18
+ }
19
+ return { kind: 'closed' };
20
+ }
21
+ /**
22
+ * Reads a new peer's `hello`. A peer that speaks this protocol is welcomed;
23
+ * any other first line is refused with this leader's version, and the
24
+ * connection ends once that answer is written.
25
+ */
26
+ export async function welcome(socket) {
27
+ const hello = parse(await readLine(socket));
28
+ if (isRecord(hello) &&
29
+ hello.op === 'hello' &&
30
+ hello.version === PROTOCOL_VERSION) {
31
+ socket.write(`${JSON.stringify({ op: 'welcome' })}\n`);
32
+ return true;
33
+ }
34
+ // Destroyed once the answer is written: a paused socket would never see a silent peer hang up.
35
+ socket.end(`${JSON.stringify({ op: 'refused', version: PROTOCOL_VERSION })}\n`, () => socket.destroy());
36
+ return false;
37
+ }
38
+ function parse(line) {
39
+ if (line === undefined)
40
+ return undefined;
41
+ try {
42
+ return JSON.parse(line);
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ }
48
+ /**
49
+ * Reads exactly one line and puts back whatever arrived after it, so the
50
+ * reader that takes over the socket next sees every later message. Resolves
51
+ * `undefined` when the socket closes first.
52
+ */
53
+ function readLine(socket) {
54
+ const { promise, resolve } = Promise.withResolvers();
55
+ const chunks = [];
56
+ const onData = (chunk) => {
57
+ const end = chunk.indexOf(0x0a);
58
+ if (end === -1) {
59
+ chunks.push(chunk);
60
+ return;
61
+ }
62
+ socket.off('data', onData);
63
+ socket.off('close', onClose);
64
+ socket.pause();
65
+ const rest = chunk.subarray(end + 1);
66
+ if (rest.length > 0)
67
+ socket.unshift(rest);
68
+ resolve(Buffer.concat([...chunks, chunk.subarray(0, end)]).toString('utf8'));
69
+ };
70
+ const onClose = () => {
71
+ socket.off('data', onData);
72
+ resolve(undefined);
73
+ };
74
+ // Every error is followed by `close`, which reports the lost peer as no answer.
75
+ socket.on('error', () => { });
76
+ socket.on('data', onData);
77
+ socket.once('close', onClose);
78
+ return promise;
79
+ }
80
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/handshake.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"lock-server.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAUtE,MAAM,WAAW,iBAAiB;IAChC,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,UAAU;;IAKrB,OAAO;WAUM,KAAK,CAChB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU,EACtB,EAAE,WAAW,EAAE,EAAE,iBAAiB,GACjC,OAAO,CAAC,UAAU,CAAC;IAiCtB;;;;;;OAMG;IACG,KAAK;CAQZ"}
1
+ {"version":3,"file":"lock-server.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAWtE,MAAM,WAAW,iBAAiB;IAChC,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,UAAU;;IAKrB,OAAO;WAUM,KAAK,CAChB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,UAAU,EACtB,EAAE,WAAW,EAAE,EAAE,iBAAiB,GACjC,OAAO,CAAC,UAAU,CAAC;IAwCtB;;;;;;OAMG;IACG,KAAK;CAQZ"}
@@ -4,6 +4,7 @@ import { EpochTokenSource } from "../../fencing/epoch-token-source.js";
4
4
  import { isErrno } from "../../shared/fs/errno.js";
5
5
  import { LockCoordinator } from "../remote/lock-coordinator.js";
6
6
  import { isLockRequest, } from "../remote/protocol.js";
7
+ import { welcome } from "./handshake.js";
7
8
  import { SocketConnection } from "./socket-connection.js";
8
9
  /**
9
10
  * Serves a `LockCoordinator` on a Unix socket for one leadership term. Tokens
@@ -37,7 +38,12 @@ export class LockServer {
37
38
  socket.unref();
38
39
  connections.add(socket);
39
40
  socket.once('close', () => connections.delete(socket));
40
- coordinator.serve(new SocketConnection(socket, isLockRequest));
41
+ // A peer that speaks another protocol is refused before it can send anything this coordinator would misread.
42
+ void welcome(socket).then((speaksOurs) => {
43
+ if (!speaksOurs)
44
+ return;
45
+ coordinator.serve(new SocketConnection(socket, isLockRequest));
46
+ });
41
47
  });
42
48
  await new Promise((resolve, reject) => {
43
49
  server.once('error', reject);
@@ -1 +1 @@
1
- {"version":3,"file":"lock-server.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lock-server.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/lock-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The socket store's leader speaks another protocol, so this process cannot
3
+ * use it. Run one version of the protocol per lock directory.
4
+ */
5
+ export declare class ProtocolVersionError extends Error {
6
+ /** The protocol this process speaks. */
7
+ readonly ours: number;
8
+ /** The protocol the leader speaks, or `undefined` when it closed the handshake without saying. */
9
+ readonly theirs: number | undefined;
10
+ constructor(ours: number, theirs: number | undefined);
11
+ }
12
+ //# sourceMappingURL=protocol-version-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-version-error.d.ts","sourceRoot":"","sources":["../../../src/lock-stores/socket/protocol-version-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;gBAExB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS;CAUrD"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The socket store's leader speaks another protocol, so this process cannot
3
+ * use it. Run one version of the protocol per lock directory.
4
+ */
5
+ export class ProtocolVersionError extends Error {
6
+ /** The protocol this process speaks. */
7
+ ours;
8
+ /** The protocol the leader speaks, or `undefined` when it closed the handshake without saying. */
9
+ theirs;
10
+ constructor(ours, theirs) {
11
+ super(theirs === undefined
12
+ ? `The socket store's leader closed the handshake without an answer while it still leads, so it predates protocol version ${ours}. Stop the processes that run the older version of @zukhruf/mutex.`
13
+ : `The socket store's leader speaks protocol version ${theirs}, and this process speaks version ${ours}. Run one version per lock directory.`);
14
+ this.name = 'ProtocolVersionError';
15
+ this.ours = ours;
16
+ this.theirs = theirs;
17
+ }
18
+ }
19
+ //# sourceMappingURL=protocol-version-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-version-error.js","sourceRoot":"","sources":["../../../src/lock-stores/socket/protocol-version-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,wCAAwC;IAC/B,IAAI,CAAS;IACtB,kGAAkG;IACzF,MAAM,CAAqB;IAEpC,YAAY,IAAY,EAAE,MAA0B;QAClD,KAAK,CACH,MAAM,KAAK,SAAS;YAClB,CAAC,CAAC,0HAA0H,IAAI,oEAAoE;YACpM,CAAC,CAAC,qDAAqD,MAAM,qCAAqC,IAAI,uCAAuC,CAChJ,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ /** Whether `error` means the file is not a SQLite database, for example a file that an older version wrote. */
2
+ export declare function isNotADatabase(error: unknown): boolean;
3
+ //# sourceMappingURL=is-not-a-database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-not-a-database.d.ts","sourceRoot":"","sources":["../../../src/shared/sqlite/is-not-a-database.ts"],"names":[],"mappings":"AAEA,+GAA+G;AAC/G,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOtD"}
@@ -0,0 +1,9 @@
1
+ const SQLITE_NOTADB = 26;
2
+ /** Whether `error` means the file is not a SQLite database, for example a file that an older version wrote. */
3
+ export function isNotADatabase(error) {
4
+ return (error instanceof Error &&
5
+ 'errcode' in error &&
6
+ typeof error.errcode === 'number' &&
7
+ (error.errcode & 0xff) === SQLITE_NOTADB);
8
+ }
9
+ //# sourceMappingURL=is-not-a-database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-not-a-database.js","sourceRoot":"","sources":["../../../src/shared/sqlite/is-not-a-database.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,+GAA+G;AAC/G,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,KAAK,YAAY,KAAK;QACtB,SAAS,IAAI,KAAK;QAClB,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,aAAa,CACzC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zukhruf/mutex",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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": {