keri-ts 2.0.1 → 2.1.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.
Files changed (229) hide show
  1. package/esm/keri/npm/src/acdc/chaining.js +223 -89
  2. package/esm/keri/npm/src/acdc/observing.js +2 -2
  3. package/esm/keri/npm/src/acdc/regbasing.js +20 -29
  4. package/esm/keri/npm/src/acdc/regengine.js +4 -6
  5. package/esm/keri/npm/src/acdc/registering.js +13 -10
  6. package/esm/keri/npm/src/acdc/registraring.js +4 -9
  7. package/esm/keri/npm/src/acdc/salting.js +127 -25
  8. package/esm/keri/npm/src/acdc/unblinding.js +2 -4
  9. package/esm/keri/npm/src/app/bootstrap/oobiery.js +1 -1
  10. package/esm/keri/npm/src/app/credentials/root-replicating.js +123 -0
  11. package/esm/keri/npm/src/app/credentials/verifying.js +12 -3
  12. package/esm/keri/npm/src/app/delivery/forwarding.js +49 -7
  13. package/esm/keri/npm/src/app/exchanges/exchanging.js +11 -12
  14. package/esm/keri/npm/src/app/identity/configing.js +17 -43
  15. package/esm/keri/npm/src/app/identity/habbing.js +54 -35
  16. package/esm/keri/npm/src/app/identity/keeping.js +1 -1
  17. package/esm/keri/npm/src/app/identity/lmdb-habery.js +12 -0
  18. package/esm/keri/npm/src/app/ipex/ipex-graph.js +7 -70
  19. package/esm/keri/npm/src/app/ipex/ipex-grouping.js +8 -5
  20. package/esm/keri/npm/src/app/ipex/ipex-v2.js +21 -5
  21. package/esm/keri/npm/src/app/ipex/ipexing.js +1 -1
  22. package/esm/keri/npm/src/app/mailbox/mailboxing.js +5 -9
  23. package/esm/keri/npm/src/app/multisig/group-members.js +14 -0
  24. package/esm/keri/npm/src/app/multisig/grouping.js +4 -3
  25. package/esm/keri/npm/src/app/multisig/multisig-workflows.js +7 -2
  26. package/esm/keri/npm/src/app/notifications/notifying.js +5 -9
  27. package/esm/keri/npm/src/app/protocol/protocol-processing.js +35 -3
  28. package/esm/keri/npm/src/app/queries/witness-inquisitor.js +2 -2
  29. package/esm/keri/npm/src/app/runtime/agent-runtime.js +7 -10
  30. package/esm/keri/npm/src/app/transport/byte-source.js +6 -42
  31. package/esm/keri/npm/src/app/transport/cesr-egress.js +77 -13
  32. package/esm/keri/npm/src/app/transport/file-bytes.js +41 -0
  33. package/esm/keri/npm/src/app/transport/http-messenger.js +14 -9
  34. package/esm/keri/npm/src/app/transport/message-source.js +1 -1
  35. package/esm/keri/npm/src/app/transport/messengering.js +3 -4
  36. package/esm/keri/npm/src/app/transport/node-http-client.js +21 -0
  37. package/esm/keri/npm/src/app/transport/tcp-dialer.js +14 -0
  38. package/esm/keri/npm/src/app/witness/receipt-client.js +2 -2
  39. package/esm/keri/npm/src/cli/common/context.js +2 -2
  40. package/esm/keri/npm/src/cli/common/existing.js +3 -5
  41. package/esm/keri/npm/src/cli/credentials/ipex.js +2 -1
  42. package/esm/keri/npm/src/cli/endpoints/oobi-clean.js +4 -3
  43. package/esm/keri/npm/src/cli/endpoints/oobi.js +2 -2
  44. package/esm/keri/npm/src/cli/identity/challenge.js +2 -2
  45. package/esm/keri/npm/src/cli/identity/incept.js +2 -2
  46. package/esm/keri/npm/src/cli/identity/init.js +4 -3
  47. package/esm/keri/npm/src/cli/identity/interact.js +2 -2
  48. package/esm/keri/npm/src/cli/identity/rotate.js +2 -2
  49. package/esm/keri/npm/src/cli/messaging/exchange.js +2 -2
  50. package/esm/keri/npm/src/cli/messaging/multisig.js +61 -3
  51. package/esm/keri/npm/src/cli/tools/annotate.js +2 -1
  52. package/esm/keri/npm/src/cli/tools/benchmark.js +2 -1
  53. package/esm/keri/npm/src/cli/tools/db-dump.js +1 -4
  54. package/esm/keri/npm/src/core/bytes.js +9 -0
  55. package/esm/keri/npm/src/core/kel/delegation-ancestry.js +92 -0
  56. package/esm/keri/npm/src/core/kel/eventing.js +1 -2
  57. package/esm/keri/npm/src/core/kel/kever.js +11 -9
  58. package/esm/keri/npm/src/core/mailbox/kram-policy.js +2 -3
  59. package/esm/keri/npm/src/core/mailbox/kram-pruning.js +15 -22
  60. package/esm/keri/npm/src/core/mailbox/kram-staging.js +2 -2
  61. package/esm/keri/npm/src/core/routing.js +2 -2
  62. package/esm/keri/npm/src/db/basing.js +149 -203
  63. package/esm/keri/npm/src/db/core/dber.js +1062 -0
  64. package/esm/keri/npm/src/db/core/index.js +4 -2
  65. package/esm/keri/npm/src/db/core/lmdb-opener.js +30 -0
  66. package/esm/keri/npm/src/db/core/lmdber.js +199 -1067
  67. package/esm/keri/npm/src/db/core/memstore.js +532 -0
  68. package/esm/keri/npm/src/db/core/path-manager.js +109 -223
  69. package/esm/keri/npm/src/db/core/store-owner.js +104 -0
  70. package/esm/keri/npm/src/db/core/webdber.js +252 -0
  71. package/esm/keri/npm/src/db/escrowing.js +2 -2
  72. package/esm/keri/npm/src/db/exchange/exchange-nests.js +3 -2
  73. package/esm/keri/npm/src/db/keeping.js +27 -121
  74. package/esm/keri/npm/src/db/koming.js +1 -1
  75. package/esm/keri/npm/src/db/lmdb-stores.js +68 -0
  76. package/esm/keri/npm/src/db/lmdb.js +6 -0
  77. package/esm/keri/npm/src/db/mailbox/mailboxing.js +12 -65
  78. package/esm/keri/npm/src/db/noting.js +11 -55
  79. package/esm/keri/npm/src/db/reger.js +43 -105
  80. package/esm/keri/npm/src/db/replay/witness-replay.js +2 -20
  81. package/esm/keri/npm/src/db/subing.js +1 -1
  82. package/esm/keri/npm/src/npm/db-lmdb.js +3 -0
  83. package/esm/keri/npm/src/npm/runtime-node.js +3 -0
  84. package/esm/keri/npm/src/runtime/index.js +9 -10
  85. package/esm/keri/npm/src/runtime/node.js +14 -0
  86. package/esm/keri/npm/src/vdr/eventing.js +1 -1
  87. package/esm/keri/npm/src/vdr/tel-publication.js +3 -3
  88. package/esm/keri/npm/src/version.js +2 -2
  89. package/package.json +11 -4
  90. package/types/acdc/chaining.d.ts +56 -6
  91. package/types/acdc/chaining.d.ts.map +1 -1
  92. package/types/acdc/regbasing.d.ts +7 -6
  93. package/types/acdc/regbasing.d.ts.map +1 -1
  94. package/types/acdc/regengine.d.ts.map +1 -1
  95. package/types/acdc/registering.d.ts +8 -4
  96. package/types/acdc/registering.d.ts.map +1 -1
  97. package/types/acdc/registraring.d.ts.map +1 -1
  98. package/types/acdc/salting.d.ts +53 -6
  99. package/types/acdc/salting.d.ts.map +1 -1
  100. package/types/acdc/unblinding.d.ts.map +1 -1
  101. package/types/app/credentials/root-replicating.d.ts +85 -0
  102. package/types/app/credentials/root-replicating.d.ts.map +1 -0
  103. package/types/app/credentials/verifying.d.ts +2 -1
  104. package/types/app/credentials/verifying.d.ts.map +1 -1
  105. package/types/app/delivery/forwarding.d.ts +10 -0
  106. package/types/app/delivery/forwarding.d.ts.map +1 -1
  107. package/types/app/exchanges/exchanging.d.ts +2 -0
  108. package/types/app/exchanges/exchanging.d.ts.map +1 -1
  109. package/types/app/identity/configing.d.ts +7 -5
  110. package/types/app/identity/configing.d.ts.map +1 -1
  111. package/types/app/identity/habbing.d.ts +23 -5
  112. package/types/app/identity/habbing.d.ts.map +1 -1
  113. package/types/app/identity/keeping.d.ts +1 -1
  114. package/types/app/identity/lmdb-habery.d.ts +6 -0
  115. package/types/app/identity/lmdb-habery.d.ts.map +1 -0
  116. package/types/app/ipex/ipex-graph.d.ts +4 -4
  117. package/types/app/ipex/ipex-graph.d.ts.map +1 -1
  118. package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
  119. package/types/app/ipex/ipex-v2.d.ts +11 -2
  120. package/types/app/ipex/ipex-v2.d.ts.map +1 -1
  121. package/types/app/ipex/ipexing.d.ts +3 -0
  122. package/types/app/ipex/ipexing.d.ts.map +1 -1
  123. package/types/app/mailbox/mailboxing.d.ts +1 -1
  124. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  125. package/types/app/multisig/group-members.d.ts +9 -0
  126. package/types/app/multisig/group-members.d.ts.map +1 -1
  127. package/types/app/multisig/grouping.d.ts.map +1 -1
  128. package/types/app/multisig/multisig-workflows.d.ts +2 -1
  129. package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
  130. package/types/app/notifications/notifying.d.ts +1 -1
  131. package/types/app/notifications/notifying.d.ts.map +1 -1
  132. package/types/app/protocol/parsering.d.ts +3 -1
  133. package/types/app/protocol/parsering.d.ts.map +1 -1
  134. package/types/app/protocol/protocol-processing.d.ts +2 -0
  135. package/types/app/protocol/protocol-processing.d.ts.map +1 -1
  136. package/types/app/runtime/agent-runtime.d.ts +7 -0
  137. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  138. package/types/app/transport/byte-source.d.ts +13 -10
  139. package/types/app/transport/byte-source.d.ts.map +1 -1
  140. package/types/app/transport/cesr-egress.d.ts +21 -7
  141. package/types/app/transport/cesr-egress.d.ts.map +1 -1
  142. package/types/app/transport/file-bytes.d.ts +11 -0
  143. package/types/app/transport/file-bytes.d.ts.map +1 -0
  144. package/types/app/transport/http-client.d.ts +3 -0
  145. package/types/app/transport/http-client.d.ts.map +1 -1
  146. package/types/app/transport/http-messenger.d.ts +15 -10
  147. package/types/app/transport/http-messenger.d.ts.map +1 -1
  148. package/types/app/transport/message-source.d.ts +1 -1
  149. package/types/app/transport/messengering.d.ts +2 -2
  150. package/types/app/transport/messengering.d.ts.map +1 -1
  151. package/types/app/transport/node-http-client.d.ts +10 -0
  152. package/types/app/transport/node-http-client.d.ts.map +1 -0
  153. package/types/app/transport/tcp-dialer.d.ts +21 -0
  154. package/types/app/transport/tcp-dialer.d.ts.map +1 -0
  155. package/types/cli/common/existing.d.ts.map +1 -1
  156. package/types/cli/credentials/ipex.d.ts.map +1 -1
  157. package/types/cli/endpoints/oobi-clean.d.ts.map +1 -1
  158. package/types/cli/identity/init.d.ts.map +1 -1
  159. package/types/cli/index.d.ts +1 -1
  160. package/types/cli/index.d.ts.map +1 -1
  161. package/types/cli/messaging/multisig.d.ts +10 -1
  162. package/types/cli/messaging/multisig.d.ts.map +1 -1
  163. package/types/cli/tools/annotate.d.ts.map +1 -1
  164. package/types/cli/tools/benchmark.d.ts.map +1 -1
  165. package/types/cli/tools/db-dump.d.ts.map +1 -1
  166. package/types/core/bytes.d.ts +2 -0
  167. package/types/core/bytes.d.ts.map +1 -1
  168. package/types/core/kel/delegation-ancestry.d.ts +41 -0
  169. package/types/core/kel/delegation-ancestry.d.ts.map +1 -0
  170. package/types/core/kel/eventing.d.ts.map +1 -1
  171. package/types/core/kel/kever.d.ts +5 -4
  172. package/types/core/kel/kever.d.ts.map +1 -1
  173. package/types/core/mailbox/kram-policy.d.ts.map +1 -1
  174. package/types/core/mailbox/kram-pruning.d.ts +1 -3
  175. package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
  176. package/types/db/basing.d.ts +14 -23
  177. package/types/db/basing.d.ts.map +1 -1
  178. package/types/db/core/dber.d.ts +505 -0
  179. package/types/db/core/dber.d.ts.map +1 -0
  180. package/types/db/core/index.d.ts +4 -2
  181. package/types/db/core/index.d.ts.map +1 -1
  182. package/types/db/core/lmdb-opener.d.ts +17 -0
  183. package/types/db/core/lmdb-opener.d.ts.map +1 -0
  184. package/types/db/core/lmdber.d.ts +60 -383
  185. package/types/db/core/lmdber.d.ts.map +1 -1
  186. package/types/db/core/memstore.d.ts +129 -0
  187. package/types/db/core/memstore.d.ts.map +1 -0
  188. package/types/db/core/path-manager.d.ts +22 -45
  189. package/types/db/core/path-manager.d.ts.map +1 -1
  190. package/types/db/core/store-owner.d.ts +59 -0
  191. package/types/db/core/store-owner.d.ts.map +1 -0
  192. package/types/db/core/webdber.d.ts +127 -0
  193. package/types/db/core/webdber.d.ts.map +1 -0
  194. package/types/db/escrowing.d.ts +3 -3
  195. package/types/db/escrowing.d.ts.map +1 -1
  196. package/types/db/exchange/exchange-nests.d.ts.map +1 -1
  197. package/types/db/keeping.d.ts +9 -23
  198. package/types/db/keeping.d.ts.map +1 -1
  199. package/types/db/koming.d.ts +7 -8
  200. package/types/db/koming.d.ts.map +1 -1
  201. package/types/db/lmdb-stores.d.ts +45 -0
  202. package/types/db/lmdb-stores.d.ts.map +1 -0
  203. package/types/db/lmdb.d.ts +7 -0
  204. package/types/db/lmdb.d.ts.map +1 -0
  205. package/types/db/mailbox/mailboxing.d.ts +7 -21
  206. package/types/db/mailbox/mailboxing.d.ts.map +1 -1
  207. package/types/db/noting.d.ts +7 -12
  208. package/types/db/noting.d.ts.map +1 -1
  209. package/types/db/reger.d.ts +7 -11
  210. package/types/db/reger.d.ts.map +1 -1
  211. package/types/db/replay/witness-replay.d.ts +0 -2
  212. package/types/db/replay/witness-replay.d.ts.map +1 -1
  213. package/types/db/subing.d.ts +24 -25
  214. package/types/db/subing.d.ts.map +1 -1
  215. package/types/npm/db-lmdb.d.ts +4 -0
  216. package/types/npm/db-lmdb.d.ts.map +1 -0
  217. package/types/npm/runtime-node.d.ts +4 -0
  218. package/types/npm/runtime-node.d.ts.map +1 -0
  219. package/types/runtime/index.d.ts +9 -10
  220. package/types/runtime/index.d.ts.map +1 -1
  221. package/types/runtime/node.d.ts +15 -0
  222. package/types/runtime/node.d.ts.map +1 -0
  223. package/types/version.d.ts +2 -2
  224. package/esm/keri/npm/src/acdc/relating.js +0 -59
  225. package/esm/keri/npm/src/db/baser-defaults.js +0 -11
  226. package/types/acdc/relating.d.ts +0 -21
  227. package/types/acdc/relating.d.ts.map +0 -1
  228. package/types/db/baser-defaults.d.ts +0 -5
  229. package/types/db/baser-defaults.d.ts.map +0 -1
@@ -5,7 +5,8 @@
5
5
  * Uses composition pattern instead of inheritance.
6
6
  */
7
7
  import * as dntShim from "../../_dnt.shims.js";
8
- import { isAbsolute, join } from "../../deps/jsr.io/@std/path/1.1.6/mod.js";
8
+ import { dirname, extname, isAbsolute, join, resolve } from "../../deps/jsr.io/@std/path/1.1.6/mod.js";
9
+ import { accessSync, constants } from "node:fs";
9
10
  import { action } from "effection";
10
11
  import { InvalidPathNameError, PathError } from "../../core/errors.js";
11
12
  import { consoleLogger } from "../../core/logger.js";
@@ -20,9 +21,27 @@ function isNotFoundLike(error) {
20
21
  return error instanceof dntShim.Deno.errors.NotFound
21
22
  || hasErrorCode(error, ["ENOENT"]);
22
23
  }
23
- function isPermissionDeniedLike(error) {
24
- return error instanceof dntShim.Deno.errors.PermissionDenied
25
- || hasErrorCode(error, ["EACCES", "EPERM"]);
24
+ /** The temp root hio uses: `/tmp` on macOS, otherwise Python's `tempfile.gettempdir` order. */
25
+ function tempHeadDir() {
26
+ if (dntShim.Deno.build.os === "darwin")
27
+ return "/tmp";
28
+ return dntShim.Deno.env.get("TMPDIR") || dntShim.Deno.env.get("TEMP") || dntShim.Deno.env.get("TMP") || "/tmp";
29
+ }
30
+ /** True when this process may read and write `path`, like `os.access(path, F_OK | R_OK | W_OK)`. */
31
+ function isAccessible(path) {
32
+ try {
33
+ accessSync(path, constants.F_OK | constants.R_OK | constants.W_OK);
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ /** Refuse an absolute name or base, which would escape the head directory. */
41
+ function assertRelative(value, label) {
42
+ if (isAbsolute(value) || /^[a-zA-Z]:[\\/]/.test(value)) {
43
+ throw new InvalidPathNameError(`Not relative ${label}=${value} path.`);
44
+ }
26
45
  }
27
46
  /** Defaults for the path manager */
28
47
  export const PATH_DEFAULTS = {
@@ -32,7 +51,6 @@ export const PATH_DEFAULTS = {
32
51
  altHeadDirPath: "~",
33
52
  altTailDirPath: ".tufa/db",
34
53
  altCleanTailDirPath: ".tufa/clean/db",
35
- tempHeadDir: "/tmp",
36
54
  tempPrefix: "keri_lmdb_",
37
55
  tempSuffix: "_test",
38
56
  perm: 0o1700, // sticky + owner rwx
@@ -50,10 +68,11 @@ export const PATH_DEFAULTS = {
50
68
  * - centralize path naming constraints so higher-level DB/config wrappers do
51
69
  * not each reimplement filesystem policy
52
70
  *
53
- * Current `keri-ts` difference:
54
- * - this is a local abstraction, not a direct KERIpy class port; it encodes
55
- * the same path-layout intent while handling Deno/Node runtime differences in
56
- * one place
71
+ * Current `keri-ts` differences from hio `Filer`, which it otherwise follows:
72
+ * - composition rather than inheritance, so stores own one instance
73
+ * - a temp store uses one stable directory per name instead of a fresh
74
+ * `mkdtemp` per open, so its data survives a close and reopen; temp stores are
75
+ * never shared with KERIpy
57
76
  */
58
77
  export class PathManager {
59
78
  constructor(options = {}, defaults) {
@@ -148,11 +167,20 @@ export class PathManager {
148
167
  writable: true,
149
168
  value: void 0
150
169
  });
170
+ // the temporary directory holding `path`, when temp
171
+ Object.defineProperty(this, "tempRoot", {
172
+ enumerable: true,
173
+ configurable: true,
174
+ writable: true,
175
+ value: null
176
+ });
151
177
  this.defaults = { ...PATH_DEFAULTS, ...defaults };
152
- this._name = options.name || "main";
153
- this.base = options.base || "";
178
+ this._name = options.name ?? "main";
179
+ this.base = options.base ?? "";
180
+ assertRelative(this._name, "name");
181
+ assertRelative(this.base, "base");
154
182
  this.temp = options.temp || false;
155
- this.headDirPath = options.headDirPath || this.defaults.headDirPath;
183
+ this.headDirPath = options.headDirPath ?? this.defaults.headDirPath;
156
184
  this.perm = options.perm ?? this.defaults.perm;
157
185
  this.path = null;
158
186
  this.filed = options.filed || false;
@@ -168,18 +196,9 @@ export class PathManager {
168
196
  return this._name;
169
197
  }
170
198
  set name(value) {
171
- // Check if path is absolute
172
- if (isAbsolute(value) || /^[a-zA-Z]:[\\/]/.test(value)) {
173
- throw new InvalidPathNameError(`Not relative name=${value} path.`);
174
- }
199
+ assertRelative(value, "name");
175
200
  this._name = value;
176
201
  }
177
- _getTempPath() {
178
- const tempDir = dntShim.Deno.env.get("TMPDIR") || dntShim.Deno.env.get("TMP")
179
- || dntShim.Deno.env.get("TEMP") || "/tmp";
180
- const tempName = `${this.defaults.tempPrefix}${this.name}${this.defaults.tempSuffix}`;
181
- return join(tempDir, tempName);
182
- }
183
202
  /** Resolve user home directory with a Windows-aware fallback chain. */
184
203
  _resolveHomeDir() {
185
204
  const home = dntShim.Deno.env.get("HOME");
@@ -205,95 +224,21 @@ export class PathManager {
205
224
  }
206
225
  return path;
207
226
  }
208
- _getPrimaryPath(headDirPath, clean) {
209
- // head / tail / base / name
210
- // Expand ~ to HOME directory
211
- let head = headDirPath;
212
- head = this._pathExpandTilde(head);
213
- let tail;
214
- if (clean) {
215
- tail = this.defaults.cleanTailDirPath;
216
- }
217
- else {
218
- tail = this.defaults.tailDirPath;
219
- }
220
- return this.base
221
- ? join(head, tail, this.base, this.name)
222
- : join(head, tail, this.name);
223
- }
224
- _getAltPath(clean) {
225
- // HOME or ~ / tail / base / name
226
- let head = dntShim.Deno.env.get("HOME") || "~";
227
- head = this._pathExpandTilde(head);
228
- let tail;
229
- if (clean) {
230
- tail = this.defaults.altCleanTailDirPath;
231
- }
232
- else {
233
- tail = this.defaults.altTailDirPath;
234
- }
235
- return this.base
236
- ? join(head, tail, this.base, this.name)
237
- : join(head, tail, this.name);
238
- }
239
- /*
240
- * Creates a file path based on head, tail, base, and name. Ensure path is created and optionally reuse it.
241
- * @param options path creation options
242
- * @returns File path to a persistent file or directory
243
- */
244
- _getPersistentPaths(options = {}) {
245
- const headDirPath = options.headDirPath ?? this.headDirPath;
246
- const clean = options.clean || false;
247
- const primary = this._getPrimaryPath(headDirPath, clean);
248
- const alt = this._getAltPath(clean);
249
- return [primary, alt];
250
- }
251
- _getPaths(options = {}) {
252
- const [primary, alt] = this._getPersistentPaths(options);
253
- const tempPath = this._getTempPath();
254
- return [primary, alt, tempPath];
255
- }
256
227
  /**
257
228
  * Helper: Convert Promise-based file system operations to Effection operations
258
229
  * This ensures proper structured concurrency and cancellation support
259
230
  */
231
+ /** True when `path` exists; any failure to stat means absent, like `os.path.exists`. */
260
232
  *statOp(path) {
261
- return yield* action((resolve, reject) => {
262
- dntShim.Deno.stat(path)
263
- .then(() => resolve(true))
264
- .catch((error) => {
265
- if (isNotFoundLike(error)) {
266
- resolve(false);
267
- }
268
- else {
269
- reject(error);
270
- }
271
- });
233
+ return yield* action((resolve) => {
234
+ dntShim.Deno.stat(path).then(() => resolve(true)).catch(() => resolve(false));
272
235
  return () => { };
273
236
  });
274
237
  }
275
- *accessOp(path) {
276
- return yield* action((resolve, reject) => {
277
- // In Deno, we check access by stating or trying to read/write.
278
- // Simplified to checking existence and relying on OS permissions for now
279
- dntShim.Deno.stat(path)
280
- .then(() => resolve(true))
281
- .catch(() => resolve(false));
282
- return () => { };
283
- });
284
- }
285
- *mkdirOp(path, perm) {
286
- return yield* action((resolve, reject) => {
287
- dntShim.Deno.mkdir(path, { recursive: true, mode: perm })
288
- .then(() => resolve(true))
289
- .catch((error) => {
290
- if (isPermissionDeniedLike(error)) {
291
- resolve(false);
292
- }
293
- else {
294
- reject(error);
295
- }
296
- });
238
+ /** Create a directory and its parents with default permissions, like `os.makedirs`. */
239
+ *mkdirOp(path) {
240
+ yield* action((resolve, reject) => {
241
+ dntShim.Deno.mkdir(path, { recursive: true }).then(() => resolve()).catch(reject);
297
242
  return () => { };
298
243
  });
299
244
  }
@@ -336,152 +281,93 @@ export class PathManager {
336
281
  });
337
282
  }
338
283
  /**
339
- * Reopen/create the directory or file path.
340
- * Replicates KERIpy/HIO Filer.remake logic:
341
- * - Tries primary path (/usr/local/var/keri/*) first
342
- * - Falls back to alt path (~/.tufa/*) on OS errors or access issues
343
- *
344
- * Returns Effection Operation with true if path is created and accessible, false otherwise.
284
+ * Close, then open the path, mirroring hio `Filer.reopen`. An existing path
285
+ * is kept only when `reuse` is set; otherwise the path is made again.
345
286
  */
346
287
  *reopen(options = {}) {
347
- this._applyOptions(options);
348
- const headDirPath = options.headDirPath ?? this.headDirPath;
349
- const clean = options.clean || false;
350
- const reuse = options.reuse || false;
351
- const clear = options.clear || false;
352
- const [primary, alt, tempPath] = this._getPaths({
353
- ...options,
354
- headDirPath,
355
- clean,
356
- });
357
- const resolved = yield* this._resolvePath({
358
- primary,
359
- alt,
360
- tempPath,
361
- headDirPath,
362
- reuse,
363
- });
364
- this.headDirPath = resolved.headDirPath;
365
- if (clear) {
366
- yield* this._clearPath(resolved.path);
367
- }
368
- if (!this.filed) {
369
- yield* this._ensureDirectoryExists(resolved.path);
370
- }
371
- this.path = resolved.path;
372
- this.opened = true;
373
- return this.opened;
374
- }
375
- /** Update instance fields from caller-provided options. */
376
- _applyOptions(options) {
288
+ yield* this.close(options.clear ?? false);
377
289
  this.temp = options.temp ?? this.temp;
290
+ this.headDirPath = options.headDirPath ?? this.headDirPath;
378
291
  this.perm = options.perm ?? this.perm;
379
292
  this.mode = options.mode ?? this.mode;
380
293
  this.fext = options.fext ?? this.fext;
381
- }
382
- /**
383
- * Select which concrete path to use: temp, primary with alt fallback,
384
- * reuse with alt fallback, or primary directly (custom headDirPath).
385
- */
386
- *_resolvePath(params) {
387
- if (this.temp) {
388
- return { path: params.tempPath, headDirPath: params.headDirPath };
294
+ if (!this.path || !options.reuse || !(yield* this.statOp(this.path))) {
295
+ this.path = yield* this.remake(options.clean ?? false);
389
296
  }
390
- if (params.reuse) {
391
- return yield* this._reuseOrFallback(params.primary, params.alt, params.headDirPath);
392
- }
393
- return yield* this._createOrFallback(params.primary, params.alt, params.headDirPath);
394
- }
395
- /**
396
- * Try to use or create the primary path. If creation fails (e.g. EACCES),
397
- * fall back to the alt path. Throws if alt path also fails.
398
- */
399
- *_createOrFallback(primary, alt, headDirPath) {
400
- const exists = yield* this.statOp(primary);
401
- if (exists) {
402
- return { path: primary, headDirPath };
403
- }
404
- this.logger.debug(`Creating directory at ${primary}`);
405
- const created = yield* this.mkdirOp(primary, this.perm);
406
- if (created) {
407
- return { path: primary, headDirPath };
408
- }
409
- this.logger.debug(`Failed to create primary path, falling back to alt path`);
410
- const altReady = yield* this._ensurePathAccessible(alt);
411
- if (!altReady) {
412
- this.logger.error(`Alt path not available at ${alt}`);
413
- throw new PathError(`Alt path not available at ${alt}`);
414
- }
415
- return { path: alt, headDirPath: this.defaults.altHeadDirPath };
297
+ this.opened = true;
298
+ return true;
416
299
  }
417
300
  /**
418
- * Verify the primary path exists and is accessible for reuse.
419
- * If not accessible and using the default head dir, fall back to alt.
301
+ * Choose and create the path, mirroring hio `Filer.remake`. `headDirPath` is
302
+ * left alone on fallback, so each open retries the primary location first.
420
303
  */
421
- *_reuseOrFallback(primary, alt, headDirPath) {
422
- const exists = yield* this.statOp(primary);
423
- const accessible = exists ? yield* this.accessOp(primary) : false;
424
- if (exists && accessible) {
425
- return { path: primary, headDirPath };
304
+ *remake(clean) {
305
+ const tail = clean ? this.defaults.cleanTailDirPath : this.defaults.tailDirPath;
306
+ const name = this.fileName();
307
+ if (this.temp) {
308
+ this.tempRoot = join(tempHeadDir(), `${this.defaults.tempPrefix}${this.name}${this.defaults.tempSuffix}`);
309
+ const path = this.filed || this.extensioned ? join(this.tempRoot, name) : this.tempRoot;
310
+ yield* this.makePath(path);
311
+ return path;
426
312
  }
427
- this.logger.debug(`Reuse path unavailable, attempting to (re)create primary path`);
428
- const primaryReady = yield* this._ensurePathAccessible(primary);
429
- if (primaryReady) {
430
- return { path: primary, headDirPath };
313
+ let path = resolve(this._pathExpandTilde(join(this.headDirPath, tail, this.base, name)));
314
+ if (clean && (yield* this.statOp(path)))
315
+ yield* this.rmOp(path);
316
+ if (!(yield* this.statOp(path))) {
317
+ try {
318
+ yield* this.makePath(path);
319
+ }
320
+ catch (cause) {
321
+ this.logger.debug(`Failed to create ${path}, falling back to alt path`);
322
+ path = yield* this.makeAltPath(clean, name, cause);
323
+ }
431
324
  }
432
- this.logger.debug(`Primary path unavailable, trying alt path`);
433
- const altReady = yield* this._ensurePathAccessible(alt);
434
- if (!altReady) {
435
- this.logger.debug(`Alt path not available: ${alt}`);
325
+ else if (!isAccessible(path)) {
326
+ path = yield* this.makeAltPath(clean, name);
436
327
  }
437
- return { path: alt, headDirPath: this.defaults.altHeadDirPath };
328
+ if (!this.extensioned && dntShim.Deno.build.os !== "windows")
329
+ dntShim.Deno.chmodSync(path, this.perm);
330
+ return path;
438
331
  }
439
- /**
440
- * Ensure a path exists and is accessible, creating it if necessary.
441
- * Returns true if the path is ready for use.
442
- */
443
- *_ensurePathAccessible(path) {
444
- const exists = yield* this.statOp(path);
445
- if (!exists) {
446
- this.logger.debug(`Creating directory at ${path}`);
447
- return yield* this.mkdirOp(path, this.perm);
448
- }
449
- return yield* this.accessOp(path);
332
+ /** Name the path uses, with `fext` added to an extensionless file name. */
333
+ fileName() {
334
+ return (this.filed || this.extensioned) && !extname(this.name) ? `${this.name}.${this.fext}` : this.name;
450
335
  }
451
- /** Remove existing file or directory at path. */
452
- *_clearPath(path) {
453
- const stat = yield* this.statFileOp(path);
454
- if (stat.isDirectory || stat.isFile) {
455
- yield* this.rmOp(path);
336
+ /** Create a directory path, or a file path's parents and the file when `filed`. */
337
+ *makePath(path) {
338
+ if (!this.filed && !this.extensioned) {
339
+ yield* this.mkdirOp(path);
340
+ return;
456
341
  }
342
+ yield* this.mkdirOp(dirname(path));
343
+ if (this.filed)
344
+ dntShim.Deno.openSync(path, { read: true, write: true, create: true, mode: this.perm }).close();
457
345
  }
458
- /** Ensure the directory at path exists and is accessible, creating it if needed. */
459
- *_ensureDirectoryExists(path) {
460
- const exists = yield* this.statOp(path);
461
- if (!exists) {
462
- this.logger.debug(`Creating directory at ${path}`);
463
- const created = yield* this.mkdirOp(path, this.perm);
464
- if (!created) {
465
- this.logger.debug(`Failed to create directory at ${path}`);
466
- }
346
+ /** Make the home-directory alternate, failing when it is unavailable too. */
347
+ *makeAltPath(clean, name, cause) {
348
+ const tail = clean ? this.defaults.altCleanTailDirPath : this.defaults.altTailDirPath;
349
+ const path = resolve(this._pathExpandTilde(this.defaults.altHeadDirPath), tail, this.base, name);
350
+ try {
351
+ if (!(yield* this.statOp(path)))
352
+ yield* this.makePath(path);
467
353
  }
468
- else {
469
- const accessible = yield* this.accessOp(path);
470
- if (!accessible) {
471
- this.logger.debug(`Path exists but is not accessible: ${path}`);
472
- }
354
+ catch (error) {
355
+ this.logger.error(`Alt path not available at ${path}`);
356
+ throw new PathError(`Alt path not available at ${path}`, { cause: cause ?? error });
473
357
  }
358
+ return path;
474
359
  }
475
360
  /**
476
- * Close the path manager
477
- * If clear is true, removes the directory/file
478
- * Uses Effection for structured concurrency
361
+ * Mark closed, removing the path when `clear` is set. The path is retained so
362
+ * a later `reopen({ reuse: true })` finds it, as hio does.
479
363
  */
480
364
  *close(clear = false) {
481
365
  if (clear && this.path) {
482
366
  yield* this.rmOp(this.path);
367
+ if (this.tempRoot)
368
+ yield* this.rmOp(this.tempRoot);
369
+ this.tempRoot = null;
483
370
  }
484
- this.path = null;
485
371
  this.opened = false;
486
372
  return true;
487
373
  }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Composition seam between typed domain stores and a `DBer` backend.
3
+ *
4
+ * ADR-0015 owns the store contract. A `StoreOwner` is a domain store such as
5
+ * `Noter` or `Reger`: it borrows no backend knowledge and delegates lifecycle
6
+ * and transactions to the one store it composes over.
7
+ */
8
+ import { DatabaseNotOpenError } from "../../core/errors.js";
9
+ /** A domain store composed over one `DBer`. */
10
+ export class StoreOwner {
11
+ constructor(store) {
12
+ Object.defineProperty(this, "store", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: store
17
+ });
18
+ }
19
+ get name() {
20
+ return this.store.name;
21
+ }
22
+ get base() {
23
+ return this.store.base;
24
+ }
25
+ get opened() {
26
+ return this.store.opened;
27
+ }
28
+ get readonly() {
29
+ return this.store.readonly;
30
+ }
31
+ get temp() {
32
+ return this.store.temp;
33
+ }
34
+ get inWriteTransaction() {
35
+ return this.store.inWriteTransaction;
36
+ }
37
+ /** Filesystem path on an LMDB-backed store; `null` elsewhere. */
38
+ get path() {
39
+ return this.store.path ?? null;
40
+ }
41
+ /** Native LMDB environment for low-level tests only; `null` on other backends. */
42
+ get env() {
43
+ return this.store.env ?? null;
44
+ }
45
+ get writeVersion() {
46
+ return this.store.writeVersion;
47
+ }
48
+ transaction(fn) {
49
+ return this.store.transaction(fn);
50
+ }
51
+ flushed() {
52
+ return this.store.flushed();
53
+ }
54
+ refreshRead() {
55
+ this.store.refreshRead();
56
+ }
57
+ hasTable(name) {
58
+ return this.store.hasTable(name);
59
+ }
60
+ getVer() {
61
+ return this.store.getVer();
62
+ }
63
+ setVer(val) {
64
+ this.store.setVer(val);
65
+ }
66
+ close(clear = false) {
67
+ return this.store.close(clear);
68
+ }
69
+ }
70
+ /**
71
+ * Open a ready owner for its caller, closing an unreturned acquisition on
72
+ * failure or cancellation without masking the original error.
73
+ */
74
+ export function* openOwner(owner, label, options) {
75
+ let delivered = false;
76
+ let failure;
77
+ try {
78
+ if (!(yield* owner.reopen(options)))
79
+ throw new DatabaseNotOpenError(`Failed to open ${label}`);
80
+ delivered = true;
81
+ return owner;
82
+ }
83
+ catch (error) {
84
+ failure = { error };
85
+ throw error;
86
+ }
87
+ finally {
88
+ if (!delivered) {
89
+ try {
90
+ yield* owner.close();
91
+ }
92
+ catch (error) {
93
+ if (failure) {
94
+ // deno-lint-ignore no-unsafe-finally -- Preserve the operation failure alongside shutdown failure.
95
+ throw new AggregateError([failure.error, error], "Database acquisition and close failed", {
96
+ cause: failure.error,
97
+ });
98
+ }
99
+ // deno-lint-ignore no-unsafe-finally -- A cancelled acquisition still reports its close failure.
100
+ throw error;
101
+ }
102
+ }
103
+ }
104
+ }