keri-ts 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) hide show
  1. package/README.md +30 -2
  2. package/esm/keri/npm/src/acdc/authoring.js +195 -0
  3. package/esm/keri/npm/src/acdc/custody.js +164 -0
  4. package/esm/keri/npm/src/acdc/dhking.js +244 -32
  5. package/esm/keri/npm/src/acdc/regbasing.js +10 -0
  6. package/esm/keri/npm/src/acdc/salting.js +16 -1
  7. package/esm/keri/npm/src/acdc/scheming.js +93 -54
  8. package/esm/keri/npm/src/acdc/unblinding.js +118 -95
  9. package/esm/keri/npm/src/app/bootstrap/authenticating.js +4 -6
  10. package/esm/keri/npm/src/app/bootstrap/oobi-resolving.js +41 -0
  11. package/esm/keri/npm/src/app/bootstrap/oobiery.js +4 -6
  12. package/esm/keri/npm/src/app/credentials/credential-revoking.js +1 -0
  13. package/esm/keri/npm/src/app/credentials/credential-workflows.js +2 -0
  14. package/esm/keri/npm/src/app/credentials/registry-settling.js +44 -0
  15. package/esm/keri/npm/src/app/credentials/root-replicating.js +68 -60
  16. package/esm/keri/npm/src/app/credentials/root-replicator.js +198 -0
  17. package/esm/keri/npm/src/app/credentials/tel-completion.js +36 -0
  18. package/esm/keri/npm/src/app/delivery/forwarding.js +16 -3
  19. package/esm/keri/npm/src/app/exchanges/exchanging.js +14 -4
  20. package/esm/keri/npm/src/app/identity/challenging.js +50 -0
  21. package/esm/keri/npm/src/app/identity/delegating.js +77 -77
  22. package/esm/keri/npm/src/app/identity/delegation-approval.js +333 -0
  23. package/esm/keri/npm/src/app/identity/event-completion.js +154 -0
  24. package/esm/keri/npm/src/app/identity/habbing.js +129 -6
  25. package/esm/keri/npm/src/app/identity/keeping.js +77 -4
  26. package/esm/keri/npm/src/app/ipex/group-ipex.js +327 -0
  27. package/esm/keri/npm/src/app/ipex/group-issuance.js +902 -0
  28. package/esm/keri/npm/src/app/ipex/ipex-admitting.js +120 -0
  29. package/esm/keri/npm/src/app/ipex/ipex-conversation.js +197 -0
  30. package/esm/keri/npm/src/app/ipex/ipex-discloser.js +591 -0
  31. package/esm/keri/npm/src/app/ipex/ipex-granting.js +93 -0
  32. package/esm/keri/npm/src/app/ipex/ipex-grouping.js +34 -11
  33. package/esm/keri/npm/src/app/ipex/issuance.js +888 -0
  34. package/esm/keri/npm/src/app/ipex/pending-presentations.js +46 -0
  35. package/esm/keri/npm/src/app/ipex/presentation-files.js +200 -0
  36. package/esm/keri/npm/src/app/ipex/presenting.js +401 -0
  37. package/esm/keri/npm/src/app/ipex/receive-issuance.js +86 -0
  38. package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +660 -0
  39. package/esm/keri/npm/src/app/mailbox/ipex-arrival.js +103 -0
  40. package/esm/keri/npm/src/app/mailbox/kel-publisher.js +322 -0
  41. package/esm/keri/npm/src/app/mailbox/kel-publishing.js +65 -0
  42. package/esm/keri/npm/src/app/mailbox/kel-submission.js +24 -0
  43. package/esm/keri/npm/src/app/mailbox/mailbox-admin.js +208 -0
  44. package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +212 -47
  45. package/esm/keri/npm/src/app/mailbox/mailboxing.js +10 -5
  46. package/esm/keri/npm/src/app/multisig/counseling.js +482 -0
  47. package/esm/keri/npm/src/app/multisig/group-members.js +18 -0
  48. package/esm/keri/npm/src/app/multisig/multisig-workflows.js +94 -139
  49. package/esm/keri/npm/src/app/multisig/proposals.js +281 -0
  50. package/esm/keri/npm/src/app/multisig/proposing.js +122 -0
  51. package/esm/keri/npm/src/app/notifications/signaling.js +5 -5
  52. package/esm/keri/npm/src/app/protocol/parsering.js +26 -1
  53. package/esm/keri/npm/src/app/protocol/protocol-processing.js +9 -3
  54. package/esm/keri/npm/src/app/queries/query-workflows.js +98 -0
  55. package/esm/keri/npm/src/app/runtime/agent-runtime.js +106 -55
  56. package/esm/keri/npm/src/app/runtime/cue-runtime.js +3 -6
  57. package/esm/keri/npm/src/app/runtime/delivery-outbox.js +87 -0
  58. package/esm/keri/npm/src/app/runtime/doing.js +17 -0
  59. package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +207 -0
  60. package/esm/keri/npm/src/app/runtime/operation-owners.js +151 -0
  61. package/esm/keri/npm/src/app/runtime/retry-backoff.js +93 -0
  62. package/esm/keri/npm/src/app/runtime/workflow-contract.js +80 -0
  63. package/esm/keri/npm/src/app/transport/http-client.js +25 -6
  64. package/esm/keri/npm/src/app/witness/http-endpoints.js +4 -4
  65. package/esm/keri/npm/src/cli/common/event-completion.js +49 -0
  66. package/esm/keri/npm/src/cli/common/runtime-workers.js +23 -0
  67. package/esm/keri/npm/src/cli/credentials/ipex.js +157 -190
  68. package/esm/keri/npm/src/cli/credentials/vc.js +53 -72
  69. package/esm/keri/npm/src/cli/endpoints/did.js +12 -3
  70. package/esm/keri/npm/src/cli/endpoints/oobi.js +10 -13
  71. package/esm/keri/npm/src/cli/identity/challenge.js +8 -34
  72. package/esm/keri/npm/src/cli/identity/delegate.js +81 -258
  73. package/esm/keri/npm/src/cli/identity/incept.js +15 -32
  74. package/esm/keri/npm/src/cli/identity/interact.js +5 -17
  75. package/esm/keri/npm/src/cli/identity/rotate.js +8 -36
  76. package/esm/keri/npm/src/cli/index.js +3 -1
  77. package/esm/keri/npm/src/cli/messaging/group-join.js +79 -0
  78. package/esm/keri/npm/src/cli/messaging/group-wait.js +88 -0
  79. package/esm/keri/npm/src/cli/messaging/multisig.js +163 -284
  80. package/esm/keri/npm/src/cli/messaging/root-copy-lines.js +31 -0
  81. package/esm/keri/npm/src/core/kel/eventing.js +66 -10
  82. package/esm/keri/npm/src/core/mailbox/kram-policy.js +39 -6
  83. package/esm/keri/npm/src/core/mailbox/kraming.js +2 -0
  84. package/esm/keri/npm/src/core/records.js +23 -0
  85. package/esm/keri/npm/src/core/routing.js +13 -0
  86. package/esm/keri/npm/src/db/basing.js +34 -2
  87. package/esm/keri/npm/src/db/core/indexeddb-persistence.js +221 -0
  88. package/esm/keri/npm/src/db/indexeddb.js +98 -0
  89. package/esm/keri/npm/src/db/keeping.js +11 -2
  90. package/esm/keri/npm/src/db/store-lock.js +150 -0
  91. package/esm/keri/npm/src/npm/db-indexeddb.browser.js +2 -0
  92. package/esm/keri/npm/src/npm/db-indexeddb.js +3 -0
  93. package/esm/keri/npm/src/npm/db.browser.js +2 -0
  94. package/esm/keri/npm/src/npm/index.browser.js +9 -0
  95. package/esm/keri/npm/src/npm/runtime-web.browser.js +2 -0
  96. package/esm/keri/npm/src/npm/runtime-web.js +3 -0
  97. package/esm/keri/npm/src/npm/runtime.browser.js +2 -0
  98. package/esm/keri/npm/src/runtime/index.js +51 -4
  99. package/esm/keri/npm/src/runtime/web/output-barrier.js +47 -0
  100. package/esm/keri/npm/src/runtime/web/persistent-storage.js +37 -0
  101. package/esm/keri/npm/src/runtime/web.js +10 -0
  102. package/esm/keri/npm/src/vdr/tel-publication.js +16 -0
  103. package/esm/keri/npm/src/version.js +2 -2
  104. package/package.json +15 -2
  105. package/types/acdc/authoring.d.ts +54 -0
  106. package/types/acdc/authoring.d.ts.map +1 -0
  107. package/types/acdc/custody.d.ts +77 -0
  108. package/types/acdc/custody.d.ts.map +1 -0
  109. package/types/acdc/dhking.d.ts +94 -3
  110. package/types/acdc/dhking.d.ts.map +1 -1
  111. package/types/acdc/regbasing.d.ts +4 -0
  112. package/types/acdc/regbasing.d.ts.map +1 -1
  113. package/types/acdc/salting.d.ts +6 -1
  114. package/types/acdc/salting.d.ts.map +1 -1
  115. package/types/acdc/scheming.d.ts +2 -0
  116. package/types/acdc/scheming.d.ts.map +1 -1
  117. package/types/acdc/unblinding.d.ts +58 -19
  118. package/types/acdc/unblinding.d.ts.map +1 -1
  119. package/types/app/bootstrap/authenticating.d.ts +3 -1
  120. package/types/app/bootstrap/authenticating.d.ts.map +1 -1
  121. package/types/app/bootstrap/oobi-resolving.d.ts +92 -0
  122. package/types/app/bootstrap/oobi-resolving.d.ts.map +1 -0
  123. package/types/app/bootstrap/oobiery.d.ts +3 -1
  124. package/types/app/bootstrap/oobiery.d.ts.map +1 -1
  125. package/types/app/credentials/credential-revoking.d.ts +3 -0
  126. package/types/app/credentials/credential-revoking.d.ts.map +1 -1
  127. package/types/app/credentials/credential-workflows.d.ts +5 -0
  128. package/types/app/credentials/credential-workflows.d.ts.map +1 -1
  129. package/types/app/credentials/registry-settling.d.ts +85 -0
  130. package/types/app/credentials/registry-settling.d.ts.map +1 -0
  131. package/types/app/credentials/root-replicating.d.ts +56 -44
  132. package/types/app/credentials/root-replicating.d.ts.map +1 -1
  133. package/types/app/credentials/root-replicator.d.ts +105 -0
  134. package/types/app/credentials/root-replicator.d.ts.map +1 -0
  135. package/types/app/credentials/tel-completion.d.ts +55 -0
  136. package/types/app/credentials/tel-completion.d.ts.map +1 -0
  137. package/types/app/delivery/forwarding.d.ts +6 -0
  138. package/types/app/delivery/forwarding.d.ts.map +1 -1
  139. package/types/app/exchanges/exchanging.d.ts.map +1 -1
  140. package/types/app/identity/challenging.d.ts +54 -0
  141. package/types/app/identity/challenging.d.ts.map +1 -1
  142. package/types/app/identity/delegating.d.ts +21 -4
  143. package/types/app/identity/delegating.d.ts.map +1 -1
  144. package/types/app/identity/delegation-approval.d.ts +228 -0
  145. package/types/app/identity/delegation-approval.d.ts.map +1 -0
  146. package/types/app/identity/event-completion.d.ts +151 -0
  147. package/types/app/identity/event-completion.d.ts.map +1 -0
  148. package/types/app/identity/habbing.d.ts +67 -2
  149. package/types/app/identity/habbing.d.ts.map +1 -1
  150. package/types/app/identity/keeping.d.ts +49 -3
  151. package/types/app/identity/keeping.d.ts.map +1 -1
  152. package/types/app/ipex/group-ipex.d.ts +247 -0
  153. package/types/app/ipex/group-ipex.d.ts.map +1 -0
  154. package/types/app/ipex/group-issuance.d.ts +281 -0
  155. package/types/app/ipex/group-issuance.d.ts.map +1 -0
  156. package/types/app/ipex/ipex-admitting.d.ts +47 -0
  157. package/types/app/ipex/ipex-admitting.d.ts.map +1 -0
  158. package/types/app/ipex/ipex-conversation.d.ts +195 -0
  159. package/types/app/ipex/ipex-conversation.d.ts.map +1 -0
  160. package/types/app/ipex/ipex-discloser.d.ts +156 -0
  161. package/types/app/ipex/ipex-discloser.d.ts.map +1 -0
  162. package/types/app/ipex/ipex-granting.d.ts +145 -0
  163. package/types/app/ipex/ipex-granting.d.ts.map +1 -0
  164. package/types/app/ipex/ipex-grouping.d.ts +18 -0
  165. package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
  166. package/types/app/ipex/issuance.d.ts +443 -0
  167. package/types/app/ipex/issuance.d.ts.map +1 -0
  168. package/types/app/ipex/pending-presentations.d.ts +25 -0
  169. package/types/app/ipex/pending-presentations.d.ts.map +1 -0
  170. package/types/app/ipex/presentation-files.d.ts +47 -0
  171. package/types/app/ipex/presentation-files.d.ts.map +1 -0
  172. package/types/app/ipex/presenting.d.ts +298 -0
  173. package/types/app/ipex/presenting.d.ts.map +1 -0
  174. package/types/app/ipex/receive-issuance.d.ts +99 -0
  175. package/types/app/ipex/receive-issuance.d.ts.map +1 -0
  176. package/types/app/ipex/unblinding-workflow.d.ts +255 -0
  177. package/types/app/ipex/unblinding-workflow.d.ts.map +1 -0
  178. package/types/app/mailbox/ipex-arrival.d.ts +104 -0
  179. package/types/app/mailbox/ipex-arrival.d.ts.map +1 -0
  180. package/types/app/mailbox/kel-publisher.d.ts +141 -0
  181. package/types/app/mailbox/kel-publisher.d.ts.map +1 -0
  182. package/types/app/mailbox/kel-publishing.d.ts +70 -0
  183. package/types/app/mailbox/kel-publishing.d.ts.map +1 -0
  184. package/types/app/mailbox/kel-submission.d.ts +18 -0
  185. package/types/app/mailbox/kel-submission.d.ts.map +1 -0
  186. package/types/app/mailbox/mailbox-admin.d.ts +113 -0
  187. package/types/app/mailbox/mailbox-admin.d.ts.map +1 -0
  188. package/types/app/mailbox/mailbox-poller.d.ts +92 -3
  189. package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
  190. package/types/app/mailbox/mailboxing.d.ts +3 -0
  191. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  192. package/types/app/multisig/counseling.d.ts +196 -0
  193. package/types/app/multisig/counseling.d.ts.map +1 -0
  194. package/types/app/multisig/group-members.d.ts +11 -0
  195. package/types/app/multisig/group-members.d.ts.map +1 -1
  196. package/types/app/multisig/grouping.d.ts +39 -20
  197. package/types/app/multisig/grouping.d.ts.map +1 -1
  198. package/types/app/multisig/multisig-workflows.d.ts +91 -22
  199. package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
  200. package/types/app/multisig/proposals.d.ts +129 -0
  201. package/types/app/multisig/proposals.d.ts.map +1 -0
  202. package/types/app/multisig/proposing.d.ts +125 -0
  203. package/types/app/multisig/proposing.d.ts.map +1 -0
  204. package/types/app/notifications/signaling.d.ts +3 -1
  205. package/types/app/notifications/signaling.d.ts.map +1 -1
  206. package/types/app/protocol/parsering.d.ts +13 -0
  207. package/types/app/protocol/parsering.d.ts.map +1 -1
  208. package/types/app/protocol/protocol-processing.d.ts +7 -1
  209. package/types/app/protocol/protocol-processing.d.ts.map +1 -1
  210. package/types/app/queries/query-workflows.d.ts +98 -0
  211. package/types/app/queries/query-workflows.d.ts.map +1 -0
  212. package/types/app/runtime/agent-runtime.d.ts +72 -25
  213. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  214. package/types/app/runtime/cue-runtime.d.ts +2 -1
  215. package/types/app/runtime/cue-runtime.d.ts.map +1 -1
  216. package/types/app/runtime/delivery-outbox.d.ts +68 -0
  217. package/types/app/runtime/delivery-outbox.d.ts.map +1 -0
  218. package/types/app/runtime/doing.d.ts +27 -0
  219. package/types/app/runtime/doing.d.ts.map +1 -0
  220. package/types/app/runtime/ipex-auxiliaries.d.ts +74 -0
  221. package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -0
  222. package/types/app/runtime/operation-owners.d.ts +137 -0
  223. package/types/app/runtime/operation-owners.d.ts.map +1 -0
  224. package/types/app/runtime/retry-backoff.d.ts +49 -0
  225. package/types/app/runtime/retry-backoff.d.ts.map +1 -0
  226. package/types/app/runtime/workflow-contract.d.ts +79 -0
  227. package/types/app/runtime/workflow-contract.d.ts.map +1 -0
  228. package/types/app/transport/http-client.d.ts.map +1 -1
  229. package/types/cli/common/event-completion.d.ts +26 -0
  230. package/types/cli/common/event-completion.d.ts.map +1 -0
  231. package/types/cli/common/runtime-workers.d.ts +23 -0
  232. package/types/cli/common/runtime-workers.d.ts.map +1 -0
  233. package/types/cli/credentials/ipex.d.ts.map +1 -1
  234. package/types/cli/credentials/vc.d.ts +1 -1
  235. package/types/cli/credentials/vc.d.ts.map +1 -1
  236. package/types/cli/endpoints/did.d.ts.map +1 -1
  237. package/types/cli/endpoints/oobi.d.ts +3 -1
  238. package/types/cli/endpoints/oobi.d.ts.map +1 -1
  239. package/types/cli/identity/challenge.d.ts +1 -1
  240. package/types/cli/identity/challenge.d.ts.map +1 -1
  241. package/types/cli/identity/delegate.d.ts +14 -1
  242. package/types/cli/identity/delegate.d.ts.map +1 -1
  243. package/types/cli/identity/incept.d.ts.map +1 -1
  244. package/types/cli/identity/interact.d.ts.map +1 -1
  245. package/types/cli/identity/rotate.d.ts.map +1 -1
  246. package/types/cli/index.d.ts +3 -1
  247. package/types/cli/index.d.ts.map +1 -1
  248. package/types/cli/messaging/group-join.d.ts +53 -0
  249. package/types/cli/messaging/group-join.d.ts.map +1 -0
  250. package/types/cli/messaging/group-wait.d.ts +34 -0
  251. package/types/cli/messaging/group-wait.d.ts.map +1 -0
  252. package/types/cli/messaging/multisig.d.ts +28 -5
  253. package/types/cli/messaging/multisig.d.ts.map +1 -1
  254. package/types/cli/messaging/root-copy-lines.d.ts +39 -0
  255. package/types/cli/messaging/root-copy-lines.d.ts.map +1 -0
  256. package/types/core/cues.d.ts +11 -0
  257. package/types/core/cues.d.ts.map +1 -1
  258. package/types/core/kel/eventing.d.ts.map +1 -1
  259. package/types/core/mailbox/kram-policy.d.ts +14 -0
  260. package/types/core/mailbox/kram-policy.d.ts.map +1 -1
  261. package/types/core/mailbox/kraming.d.ts +1 -1
  262. package/types/core/mailbox/kraming.d.ts.map +1 -1
  263. package/types/core/records.d.ts +61 -0
  264. package/types/core/records.d.ts.map +1 -1
  265. package/types/core/routing.d.ts +6 -0
  266. package/types/core/routing.d.ts.map +1 -1
  267. package/types/db/basing.d.ts +8 -2
  268. package/types/db/basing.d.ts.map +1 -1
  269. package/types/db/core/indexeddb-persistence.d.ts +59 -0
  270. package/types/db/core/indexeddb-persistence.d.ts.map +1 -0
  271. package/types/db/indexeddb.d.ts +37 -0
  272. package/types/db/indexeddb.d.ts.map +1 -0
  273. package/types/db/keeping.d.ts +5 -2
  274. package/types/db/keeping.d.ts.map +1 -1
  275. package/types/db/store-lock.d.ts +72 -0
  276. package/types/db/store-lock.d.ts.map +1 -0
  277. package/types/npm/db-indexeddb.d.ts +4 -0
  278. package/types/npm/db-indexeddb.d.ts.map +1 -0
  279. package/types/npm/runtime-web.d.ts +4 -0
  280. package/types/npm/runtime-web.d.ts.map +1 -0
  281. package/types/runtime/index.d.ts +41 -6
  282. package/types/runtime/index.d.ts.map +1 -1
  283. package/types/runtime/web/output-barrier.d.ts +30 -0
  284. package/types/runtime/web/output-barrier.d.ts.map +1 -0
  285. package/types/runtime/web/persistent-storage.d.ts +36 -0
  286. package/types/runtime/web/persistent-storage.d.ts.map +1 -0
  287. package/types/runtime/web.d.ts +11 -0
  288. package/types/runtime/web.d.ts.map +1 -0
  289. package/types/vdr/tel-publication.d.ts +6 -0
  290. package/types/vdr/tel-publication.d.ts.map +1 -1
  291. package/types/version.d.ts +2 -2
  292. package/esm/keri/npm/src/cli/common/tel-completion.js +0 -24
  293. package/types/cli/common/tel-completion.d.ts +0 -8
  294. package/types/cli/common/tel-completion.d.ts.map +0 -1
@@ -175,10 +175,10 @@ export class Kevery {
175
175
  return false;
176
176
  const query = envelope.serder.ked?.q;
177
177
  const pre = typeof query?.i === "string" ? query.i : null;
178
- if (!pre || queryRequester(envelope) !== pre)
178
+ if (!pre)
179
179
  return false;
180
180
  const recipient = this.db.getKever(pre, { refresh: true });
181
- return !!recipient && authenticatesMailboxQuery(envelope, recipient);
181
+ return !!recipient && authenticatesMailboxQuery(envelope, recipient, this.db);
182
182
  }
183
183
  /**
184
184
  * Decide how one live query should be handled without mutating escrow state.
@@ -280,14 +280,11 @@ export class Kevery {
280
280
  }]);
281
281
  }
282
282
  case "mbx": {
283
- if (dest !== pre) {
284
- return dropQuery("unauthorizedMailboxRequester");
285
- }
286
283
  const recipient = this.db.getKever(pre, { refresh: true });
287
284
  if (!recipient) {
288
285
  return escrowQuery("missingKever");
289
286
  }
290
- if (!authenticatesMailboxQuery(envelope, recipient)) {
287
+ if (!authenticatesMailboxQuery(envelope, recipient, this.db)) {
291
288
  return dropQuery("unauthorizedMailboxRequester");
292
289
  }
293
290
  return acceptQuery([{
@@ -2375,22 +2372,81 @@ function queryRequester(envelope) {
2375
2372
  * Mailbox reads authorize private recipient bytes, unlike public KEL queries.
2376
2373
  * Require the recipient's current signing threshold; merely naming its prefix
2377
2374
  * in endorsement material is not authentication. Repeated indices count once.
2375
+ *
2376
+ * Who may read a transferable recipient's mailbox on this host: the recipient itself, and, for a group, any
2377
+ * signing member that signs the poll as itself (the shape KERIpy's `GroupHab.query` emits: `q.i` is the group,
2378
+ * sender and signer are the member) when {@link provesGroupKeyControl} holds. Forwarded mail is plaintext CESR
2379
+ * unless the sender used ESSR, so this check is the read protection. KERIpy hosts are open: they do no read
2380
+ * authorization of `mbx` queries, so a poll KERIpy accepts can be refused here.
2381
+ *
2382
+ * Two refusals are by design. The host must hold the member's KEL: the keri-ts poller and `KelPublisher` submit it
2383
+ * (see `collectKelSubmission`), but a KERIpy member's poller does not, so its KEL has to reach the host by OOBI or
2384
+ * a mailbox add, and until then its poll is refused as unauthorized. And a rotation-only member (named in the
2385
+ * group's `rmids` but not its `smids`) holds no current group key, so it is refused until a rotation makes it a
2386
+ * signing member.
2378
2387
  */
2379
- function authenticatesMailboxQuery(envelope, recipient) {
2388
+ function authenticatesMailboxQuery(envelope, recipient, db) {
2380
2389
  if (!recipient.transferable) {
2381
2390
  return (envelope.cigars ?? []).some((cigar) => cigar.verfer?.qb64 === recipient.pre
2382
2391
  && !cigar.verfer.transferable
2383
2392
  && cigar.verfer.verify(cigar.raw, envelope.serder.raw));
2384
2393
  }
2385
- if (envelope.source?.qb64 !== recipient.pre)
2394
+ const requester = envelope.source?.qb64;
2395
+ if (!requester)
2396
+ return false;
2397
+ if (requester === recipient.pre)
2398
+ return satisfiesThreshold(envelope, recipient);
2399
+ // A group's poll is signed by one of its local members (the poller cannot sign for the group alone). Accept a
2400
+ // member whose own key state this host holds when its signatures meet its own current threshold and it provably
2401
+ // controlled one of the group's current keys.
2402
+ const member = db.getKever(requester, { refresh: true });
2403
+ if (!member?.transferable || !satisfiesThreshold(envelope, member))
2386
2404
  return false;
2405
+ return provesGroupKeyControl(envelope, member, recipient, db);
2406
+ }
2407
+ /**
2408
+ * Whether `member` provably controlled a key in `group`'s current key list, by either of two proofs.
2409
+ *
2410
+ * Listing a group key is not control of it: anyone can incept an identifier whose key list names a victim's
2411
+ * public key beside their own, so a key list or an event's `k` field proves nothing. Only a signature by that
2412
+ * key does. The proofs are:
2413
+ * - the poll itself carries a signature that verifies against a key at that index in `member`'s current key list
2414
+ * that is also in the group's current key list; or
2415
+ * - some establishment event in `member`'s KEL, as this host holds it, lists a group-current key at index i and
2416
+ * the host holds a signature by that index-i key on that same event that verifies. That is proof of possession
2417
+ * at the time the member used the key, and the KEL chain ties that event to the member's current keys, so a
2418
+ * member that rotated its own identifier ahead of the group is still recognised.
2419
+ */
2420
+ function provesGroupKeyControl(envelope, member, group, db) {
2421
+ const groupKeys = new Set(group.verfers.map((verfer) => verfer.qb64));
2422
+ const signedPoll = (envelope.sigers ?? []).some((siger) => {
2423
+ const verfer = member.verfers[siger.index];
2424
+ return !!verfer && groupKeys.has(verfer.qb64) && verfer.verify(siger.raw, envelope.serder.raw);
2425
+ });
2426
+ if (signedPoll)
2427
+ return true;
2428
+ for (const [, said] of db.getKelItemIter(member.pre)) {
2429
+ const event = db.getEvtSerder(member.pre, said);
2430
+ if (!event?.estive)
2431
+ continue;
2432
+ const verfers = event.verfers;
2433
+ for (const siger of db.sigs.get(dgKey(member.pre, said))) {
2434
+ const verfer = verfers[siger.index];
2435
+ if (verfer && groupKeys.has(verfer.qb64) && verfer.verify(siger.raw, event.raw))
2436
+ return true;
2437
+ }
2438
+ }
2439
+ return false;
2440
+ }
2441
+ /** The indexed signatures on `envelope` that verify against `kever`'s current keys meet its signing threshold. */
2442
+ function satisfiesThreshold(envelope, kever) {
2387
2443
  const indices = new Set();
2388
2444
  for (const siger of envelope.sigers ?? []) {
2389
- const verfer = recipient.verfers[siger.index];
2445
+ const verfer = kever.verfers[siger.index];
2390
2446
  if (verfer && verfer.verify(siger.raw, envelope.serder.raw))
2391
2447
  indices.add(siger.index);
2392
2448
  }
2393
- return recipient.tholder?.satisfy([...indices]) ?? false;
2449
+ return kever.tholder?.satisfy([...indices]) ?? false;
2394
2450
  }
2395
2451
  /** Return the reply destination derived from one normalized query envelope. */
2396
2452
  function queryReplyDest(envelope) {
@@ -1,8 +1,34 @@
1
1
  /** KRAM cache selection and safe, receiver-clocked configuration transitions. */
2
2
  import { intToB64 } from "cesr-ts";
3
3
  import { CacheTypeRecord } from "../records.js";
4
+ /**
5
+ * Windows an enabled policy uses when no caches are configured: one catchall row, in milliseconds
6
+ * `[d, sl, ll, xl, psl, pll, pxl]` (1 s drift, 5 s single-key, 60 s multi-key and 5 min exchange acceptance,
7
+ * pruned no earlier than they expire). KERIpy defines no built-in windows and requires `kram.caches` in
8
+ * configuration; these are the values its own tests and the keri-ts interop suites use, so an unconfigured
9
+ * runtime behaves like a configured KERIpy peer.
10
+ */
11
+ export const DEFAULT_KRAM_CACHES = Object.freeze({
12
+ "~": Object.freeze([1000, 5000, 60000, 300000, 5000, 60000, 300000]),
13
+ });
4
14
  /** Only this key owns the policy transition metadata in the existing settings table. */
5
15
  const policyKey = "kram-policy";
16
+ /** The stored cache type for exact route, then message type, then the catchall; null when none is configured. */
17
+ function storedCacheType(db, ilk, route) {
18
+ const record = (route ? db.ctyp.get(`${ilk}.R.${route}`) : null) ?? db.ctyp.get(ilk) ?? db.ctyp.get("~");
19
+ if (!record)
20
+ return null;
21
+ const [d, sl, ll, xl, psl, pll, pxl] = values(record);
22
+ return { d, sl, ll, xl, psl, pll, pxl };
23
+ }
24
+ /**
25
+ * How long a single-key query on `route` stays timely under the windows this store was configured with, in
26
+ * milliseconds: the single-key lag. A sender uses its own windows as the estimate of its peer's, as KERIpy's
27
+ * `requery` does. Null when this store has no KRAM cache types (KRAM never ran on it).
28
+ */
29
+ export function singleKeyQueryWindow(db, route) {
30
+ return storedCacheType(db, "qry", route)?.sl ?? null;
31
+ }
6
32
  /** Validate caller configuration before any durable policy mutation. */
7
33
  function windows(value, transitional = false) {
8
34
  if (!Array.isArray(value) || value.length !== 7 || value.some((v) => !Number.isSafeInteger(v) || v < 0)) {
@@ -148,11 +174,20 @@ export class KramPolicy {
148
174
  return [intToB64(version[0], 1) + intToB64(version[1], 2), ...(type ? [type] : []), ...(route ? [route] : [])]
149
175
  .join(".") + (route ? "" : ".");
150
176
  });
151
- const requested = configuration(config.caches);
177
+ const requested = configuration(config.caches ?? (this.enabled ? DEFAULT_KRAM_CACHES : undefined));
152
178
  const stored = db.hbys.get(policyKey);
153
179
  this.storedState = stored;
154
180
  if (stored !== null) {
155
181
  this.state = policyState(stored, db);
182
+ if (!Object.keys(this.state.caches).length && Object.keys(requested).length) {
183
+ // A store first opened without caches (KRAM off) has no rows to protect: install the windows directly.
184
+ this.state = { caches: requested, pending: {} };
185
+ this.commit(() => {
186
+ for (const [key, tuple] of Object.entries(requested))
187
+ db.ctyp.pin(key, cacheRecord(tuple));
188
+ });
189
+ return;
190
+ }
156
191
  const identical = sameKeys(Object.keys(requested), Object.keys(this.state.caches))
157
192
  && Object.keys(requested).every((key) => requested[key].every((v, i) => v === this.state.caches[key][i]));
158
193
  if (config.caches !== undefined && !identical)
@@ -173,12 +208,10 @@ export class KramPolicy {
173
208
  }
174
209
  /** Select exact route, message type, then catchall; absence is a configuration failure. */
175
210
  cacheType(ilk, route) {
176
- const record = (route ? this.db.ctyp.get(`${ilk}.R.${route}`) : null) ?? this.db.ctyp.get(ilk)
177
- ?? this.db.ctyp.get("~");
178
- if (!record)
211
+ const type = storedCacheType(this.db, ilk, route);
212
+ if (!type)
179
213
  throw new Error("No KRAM cache type for message");
180
- const [d, sl, ll, xl, psl, pll, pxl] = values(record);
181
- return { d, sl, ll, xl, psl, pll, pxl };
214
+ return type;
182
215
  }
183
216
  /** Stage expansions and reject coverage holes before changing interoperable rows. */
184
217
  change(caches, override) {
@@ -3,6 +3,8 @@ import { Deck } from "../deck.js";
3
3
  import { KramPolicy } from "./kram-policy.js";
4
4
  import { KramPruner } from "./kram-pruning.js";
5
5
  import { stageKram } from "./kram-staging.js";
6
+ /** Explicit receiver-owned cache and exemption configuration. */
7
+ export { DEFAULT_KRAM_CACHES } from "./kram-policy.js";
6
8
  /** Optional receiver intake returns ordinary decisions without owning parser input. */
7
9
  export class Kramer {
8
10
  constructor(db, { config = {}, cues = new Deck(), now = Date.now } = {}) {
@@ -350,3 +350,26 @@ export class TxnMsgCacheRecord extends RawRecord {
350
350
  */
351
351
  export class WellKnownAuthN extends RawRecord {
352
352
  }
353
+ /**
354
+ * A `/registry/root` copy the Poster reported sent: coordinates only, never the root.
355
+ *
356
+ * keri-ts only. Stored through `Komer` in `Baser.rrsn`, keyed (group, registry, recipient).
357
+ */
358
+ export class RootSentRecord extends RawRecord {
359
+ }
360
+ /**
361
+ * The KEL a mailbox was last given for an identifier: coordinates only. A 2xx is not acceptance (the host may
362
+ * escrow), which is why the poller still repairs a 403.
363
+ *
364
+ * keri-ts only. Stored through `Komer` in `Baser.kpub`, keyed (identifier, mailbox).
365
+ */
366
+ export class KelPublishedRecord extends RawRecord {
367
+ }
368
+ /**
369
+ * Public derivation coordinates of one blinded registry update: which credential, state and binding the
370
+ * update's opening is derived for. Never a salt or an opening.
371
+ *
372
+ * keri-ts only. Stored through `Komer` in `Baser.dvus`, keyed by the BUP SAID.
373
+ */
374
+ export class DerivedUpdateRecord extends RawRecord {
375
+ }
@@ -321,6 +321,17 @@ export class Revery {
321
321
  writable: true,
322
322
  value: void 0
323
323
  });
324
+ /**
325
+ * Told after an `/end/role` reply is accepted, with the controller, role, endpoint and whether the role is
326
+ * allowed. The runtime uses it to have the mailbox poller and KEL publisher look at a controller whose mailbox
327
+ * role just changed, rather than at their next sweep.
328
+ */
329
+ Object.defineProperty(this, "onEndRole", {
330
+ enumerable: true,
331
+ configurable: true,
332
+ writable: true,
333
+ value: void 0
334
+ });
324
335
  this.db = db;
325
336
  this.rtr = rtr ?? new Router();
326
337
  this.cues = cues ?? new Deck();
@@ -843,6 +854,8 @@ export class BasicReplyRouteHandler {
843
854
  });
844
855
  if (queryCues.length)
845
856
  this.rvy.cues.extend(queryCues);
857
+ if (decision.kind === "accept")
858
+ this.rvy.onEndRole?.(cid, role, eid, allowed);
846
859
  return decision;
847
860
  }
848
861
  /**
@@ -5,7 +5,7 @@ import { attachmentCounterPayloadQb64b, attachmentCounterQb64b, attachmentOrdina
5
5
  import { DatabaseOperationError, ValidationError } from "../core/errors.js";
6
6
  import { Kever } from "../core/kel/kever.js";
7
7
  import { consoleLogger } from "../core/logger.js";
8
- import { CacheTypeRecord, EndpointRecord, EventSourceRecord, HabitatRecord, KeyStateRecord, LocationRecord, MsgCacheRecord, ObservedRecord, OobiRecord, TopicsRecord, TxnMsgCacheRecord, WellKnownAuthN, } from "../core/records.js";
8
+ import { CacheTypeRecord, EndpointRecord, EventSourceRecord, HabitatRecord, KelPublishedRecord, KeyStateRecord, LocationRecord, MsgCacheRecord, ObservedRecord, OobiRecord, RootSentRecord, TopicsRecord, TxnMsgCacheRecord, WellKnownAuthN, } from "../core/records.js";
9
9
  import { encodeDateTimeToDater, makeNowIso8601 } from "../time/mod.js";
10
10
  import { dgKey, splitOnKey } from "./core/keys.js";
11
11
  import { StoreOwner } from "./core/store-owner.js";
@@ -13,7 +13,7 @@ import { ExchangeRecord, ExchangeRegistryStateRecord } from "./exchange/exchange
13
13
  import { IoSetKomer, Komer } from "./koming.js";
14
14
  import { CapturedKelReplay } from "./replay/kel-replay.js";
15
15
  import { CapturedWitnessReplay } from "./replay/witness-replay.js";
16
- import { B64OnIoDupSuber, BytesIoSetSuber, CatCesrIoSetSuber, CatCesrSuber, CesrIoSetSuber, CesrOnSuber, CesrSuber, IoSetSuber, OnIoDupSuber, OnSuber, SchemerSuber, SerderSuber, Suber, } from "./subing.js";
16
+ import { B64OnIoDupSuber, BytesIoSetSuber, BytesSuber, CatCesrIoSetSuber, CatCesrSuber, CesrIoSetSuber, CesrOnSuber, CesrSuber, IoSetSuber, OnIoDupSuber, OnSuber, SchemerSuber, SerderSuber, Suber, } from "./subing.js";
17
17
  function isMissingReloadEventError(error) {
18
18
  return error instanceof Error
19
19
  && error.message.startsWith("Missing accepted event for reloaded Kever state ");
@@ -510,6 +510,20 @@ export class Baser extends StoreOwner {
510
510
  writable: true,
511
511
  value: void 0
512
512
  });
513
+ /** keri-ts only. Sent `/registry/root` copies; (group, registry, recipient) -> recipient key sealed to and send time. Never a root. */
514
+ Object.defineProperty(this, "rrsn", {
515
+ enumerable: true,
516
+ configurable: true,
517
+ writable: true,
518
+ value: void 0
519
+ });
520
+ /** keri-ts only. The KEL a mailbox was last given: (identifier, mailbox) -> establishment SAID, time and outcome. */
521
+ Object.defineProperty(this, "kpub", {
522
+ enumerable: true,
523
+ configurable: true,
524
+ writable: true,
525
+ value: void 0
526
+ });
513
527
  Object.defineProperty(this, "chas", {
514
528
  enumerable: true,
515
529
  configurable: true,
@@ -714,6 +728,18 @@ export class Baser extends StoreOwner {
714
728
  writable: true,
715
729
  value: void 0
716
730
  }); // Shared KRAM exchange opener time keyed by XID.
731
+ Object.defineProperty(this, "heldq", {
732
+ enumerable: true,
733
+ configurable: true,
734
+ writable: true,
735
+ value: void 0
736
+ }); // Authenticated DH queries received before consent, keyed by XID.
737
+ Object.defineProperty(this, "heldu", {
738
+ enumerable: true,
739
+ configurable: true,
740
+ writable: true,
741
+ value: void 0
742
+ }); // Authenticated Unblind queries a discloser owes a reply to, keyed by `(discloser, XID)`.
717
743
  Object.defineProperty(this, "tmsc", {
718
744
  enumerable: true,
719
745
  configurable: true,
@@ -1094,6 +1120,10 @@ export class Baser extends StoreOwner {
1094
1120
  });
1095
1121
  // Registry root copies awaiting automatic retention, keyed by exchange SAID.
1096
1122
  this.rrpe = new CesrSuber(this.store, { subkey: "rrpe.", ctor: Dater });
1123
+ // Registry root copies sent, keyed by group, registry and recipient.
1124
+ this.rrsn = new Komer(this.store, { subkey: "rrsn.", recordClass: RootSentRecord });
1125
+ // KEL last given to each mailbox, keyed by identifier and mailbox.
1126
+ this.kpub = new Komer(this.store, { subkey: "kpub.", recordClass: KelPublishedRecord });
1097
1127
  // Accepted signed challenge-response exchange-message SAIDs keyed by the
1098
1128
  // signer prefix.
1099
1129
  this.chas = new CesrIoSetSuber(this.store, {
@@ -1258,6 +1288,8 @@ export class Baser extends StoreOwner {
1258
1288
  subkey: "tmsc.",
1259
1289
  recordClass: TxnMsgCacheRecord,
1260
1290
  });
1291
+ this.heldq = new BytesSuber(this.store, { subkey: "heldq." });
1292
+ this.heldu = new BytesSuber(this.store, { subkey: "heldu." });
1261
1293
  this.ulgs = new CesrIoSetSuber(this.store, { subkey: "ulgs.", ctor: Prefixer });
1262
1294
  this.cigs = new CatCesrIoSetSuber(this.store, { subkey: "cigs.", ctor: [Verfer, Cigar] });
1263
1295
  // KRAM partially signed multi-key messages keyed by `(AID, MID)`.
@@ -0,0 +1,221 @@
1
+ /**
2
+ * IndexedDB `Persistence` for `WebDBer`.
3
+ *
4
+ * One database per store family and identity holds every logical table in a
5
+ * single `rows` object store, plus a `meta` store for the version marker and each table's
6
+ * dupsort flag. Tables are not object stores. Adding a table is therefore a
7
+ * plain write, never a schema `versionchange`, which another open connection
8
+ * could block indefinitely. This departs from the plan's "one object store per
9
+ * table", which ADR-0015 permits as the alternative to handling `versionchange`.
10
+ *
11
+ * Rows have out-of-line array keys `[table, keyBytes, valueOrEmpty]`. On a
12
+ * dupsort table the third part is the duplicate value, so each duplicate is its
13
+ * own row. On a plain table it is empty and the record holds the value. Empty
14
+ * dupsort values are rejected upstream, so an empty third part always means
15
+ * "plain". Binary parts are `ArrayBuffer`s. IndexedDB's key order is never
16
+ * relied on, because `WebDBer` sorts what it loads.
17
+ *
18
+ * The family is part of the database name because a composition's stores share
19
+ * one `name` and `base`; without it a Baser and Keeper would share a database,
20
+ * a version marker and a `clear`. A temp store is named apart from a durable one
21
+ * so clearing it cannot delete durable data of the same name.
22
+ *
23
+ * `write` is one `readwrite` transaction with strict durability and settles on
24
+ * the transaction's `complete` event, never on a request's `success`.
25
+ *
26
+ * Another tab's upgrade or delete fires `versionchange` here. The connection
27
+ * closes at once so that tab is never blocked, and this adapter is then lost:
28
+ * its next `load` or `write` rejects, which latches the queue, because writing
29
+ * on after another context deleted the database would break prefix durability.
30
+ * Recovery is a new open, whose `meta` reconnects. Single-writer exclusion is
31
+ * the composition's job, not this adapter's.
32
+ *
33
+ * A blocked `clear` rejects. Our own connection is already closed, and ours
34
+ * close on `versionchange`, so only a foreign connection can block. Reads settle on
35
+ * their transaction's `complete`, like writes: WebKit reports `blocked` for a
36
+ * connection whose close is still waiting on a finishing read transaction. The browser
37
+ * keeps the delete request pending and performs it once that connection closes,
38
+ * which is what `clear` asked for.
39
+ */
40
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
41
+ if (kind === "m") throw new TypeError("Private method is not writable");
42
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
43
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
44
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
45
+ };
46
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
47
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
48
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
49
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
50
+ };
51
+ var _IndexedDbPersistence_instances, _IndexedDbPersistence_databaseName, _IndexedDbPersistence_factory, _IndexedDbPersistence_keyRange, _IndexedDbPersistence_dupsort, _IndexedDbPersistence_db, _IndexedDbPersistence_lost, _IndexedDbPersistence_connect, _IndexedDbPersistence_connection;
52
+ import { DatabaseOperationError } from "../../core/errors.js";
53
+ const ROWS = "rows";
54
+ const META = "meta";
55
+ const VERSION_KEY = "version";
56
+ const TABLE_PREFIX = "table:";
57
+ const EMPTY = new ArrayBuffer(0);
58
+ /** An exact-length `ArrayBuffer` copy, since `.buffer` of a view may span more bytes. */
59
+ const buffer = (bytes) => bytes.slice().buffer;
60
+ /** Settle with a request's result. Only valid inside a transaction that is still open. */
61
+ function result(request) {
62
+ return new Promise((resolve, reject) => {
63
+ request.onsuccess = () => resolve(request.result);
64
+ request.onerror = () => reject(request.error);
65
+ });
66
+ }
67
+ /** Settle on `complete` or fail with the real cause on `abort` or `error`. */
68
+ function completion(transaction) {
69
+ return new Promise((resolve, reject) => {
70
+ let cause;
71
+ transaction.oncomplete = () => resolve();
72
+ transaction.onerror = (event) => cause ??= event.target?.error;
73
+ transaction.onabort = () => reject(new DatabaseOperationError("IndexedDB transaction aborted", {
74
+ cause: transaction.error ?? cause,
75
+ }));
76
+ });
77
+ }
78
+ /** `Persistence` over IndexedDB. Methods are called one at a time by `WebDBer`. */
79
+ export class IndexedDbPersistence {
80
+ /**
81
+ * @param family Store family; with `identity` it chooses the database.
82
+ * @param identity Store identity; `name`, `base` and `temp` choose the database.
83
+ * @param factory Defaults to the global `indexedDB`.
84
+ * @param keyRange Defaults to the global `IDBKeyRange`; inject it with `factory` outside a browser.
85
+ */
86
+ constructor(family, identity, factory = globalThis.indexedDB, keyRange = globalThis.IDBKeyRange) {
87
+ _IndexedDbPersistence_instances.add(this);
88
+ _IndexedDbPersistence_databaseName.set(this, void 0);
89
+ _IndexedDbPersistence_factory.set(this, void 0);
90
+ _IndexedDbPersistence_keyRange.set(this, void 0);
91
+ /** Dupsort flag per table, learned from `meta` and from each batch that creates one. */
92
+ _IndexedDbPersistence_dupsort.set(this, new Map());
93
+ _IndexedDbPersistence_db.set(this, null);
94
+ _IndexedDbPersistence_lost.set(this, null);
95
+ const place = [identity.base ?? "", identity.name ?? "", family.tail, identity.temp ? "temp" : "durable"];
96
+ __classPrivateFieldSet(this, _IndexedDbPersistence_databaseName, `keri-ts:${JSON.stringify(place)}`, "f");
97
+ __classPrivateFieldSet(this, _IndexedDbPersistence_factory, factory, "f");
98
+ __classPrivateFieldSet(this, _IndexedDbPersistence_keyRange, keyRange, "f");
99
+ }
100
+ async meta() {
101
+ const db = await __classPrivateFieldGet(this, _IndexedDbPersistence_instances, "m", _IndexedDbPersistence_connect).call(this);
102
+ const transaction = db.transaction(META, "readonly");
103
+ const store = transaction.objectStore(META);
104
+ const range = __classPrivateFieldGet(this, _IndexedDbPersistence_keyRange, "f").bound(TABLE_PREFIX, `${TABLE_PREFIX}￿`);
105
+ // Settle on `complete`: a connection with a transaction still finishing is not yet closed.
106
+ const [, version, names, flags] = await Promise.all([
107
+ completion(transaction),
108
+ result(store.get(VERSION_KEY)),
109
+ result(store.getAllKeys(range)),
110
+ result(store.getAll(range)),
111
+ ]);
112
+ __classPrivateFieldGet(this, _IndexedDbPersistence_dupsort, "f").clear();
113
+ const tables = names.map((key, index) => {
114
+ const name = String(key).slice(TABLE_PREFIX.length);
115
+ const dupsort = flags[index] === true;
116
+ __classPrivateFieldGet(this, _IndexedDbPersistence_dupsort, "f").set(name, dupsort);
117
+ return { name, dupsort };
118
+ });
119
+ return { version: typeof version === "string" ? version : null, tables };
120
+ }
121
+ async load(table) {
122
+ const transaction = __classPrivateFieldGet(this, _IndexedDbPersistence_instances, "m", _IndexedDbPersistence_connection).call(this).transaction(ROWS, "readonly");
123
+ const store = transaction.objectStore(ROWS);
124
+ // Binary sorts below array, so `[table, []]` bounds every `[table, key, value]`.
125
+ const range = __classPrivateFieldGet(this, _IndexedDbPersistence_keyRange, "f").bound([table], [table, []]);
126
+ const [, keys, records] = await Promise.all([
127
+ completion(transaction),
128
+ result(store.getAllKeys(range)),
129
+ result(store.getAll(range)),
130
+ ]);
131
+ return keys.map((row, index) => {
132
+ const [, key, duplicate] = row;
133
+ const value = duplicate.byteLength > 0 ? duplicate : records[index];
134
+ return { key: new Uint8Array(key), value: new Uint8Array(value) };
135
+ });
136
+ }
137
+ write(batch) {
138
+ let transaction;
139
+ try {
140
+ transaction = __classPrivateFieldGet(this, _IndexedDbPersistence_instances, "m", _IndexedDbPersistence_connection).call(this).transaction([ROWS, META], "readwrite", { durability: "strict" });
141
+ }
142
+ catch (error) {
143
+ return Promise.reject(error);
144
+ }
145
+ const done = completion(transaction);
146
+ try {
147
+ const meta = transaction.objectStore(META);
148
+ const rows = transaction.objectStore(ROWS);
149
+ for (const { name, dupsort } of batch.tables) {
150
+ __classPrivateFieldGet(this, _IndexedDbPersistence_dupsort, "f").set(name, dupsort);
151
+ meta.put(dupsort, `${TABLE_PREFIX}${name}`);
152
+ }
153
+ if (batch.version !== undefined)
154
+ meta.put(batch.version, VERSION_KEY);
155
+ for (const { table, key, value } of batch.puts) {
156
+ const dupsort = __classPrivateFieldGet(this, _IndexedDbPersistence_dupsort, "f").get(table);
157
+ if (dupsort === undefined)
158
+ throw new DatabaseOperationError(`Unknown table ${table}`);
159
+ rows.put(dupsort ? null : buffer(value), [table, buffer(key), dupsort ? buffer(value) : EMPTY]);
160
+ }
161
+ for (const { table, key, value } of batch.deletes) {
162
+ rows.delete([table, buffer(key), value ? buffer(value) : EMPTY]);
163
+ }
164
+ }
165
+ catch (error) {
166
+ transaction.abort();
167
+ return done.catch(() => {
168
+ throw error;
169
+ });
170
+ }
171
+ return done;
172
+ }
173
+ async clear() {
174
+ await this.close();
175
+ await new Promise((resolve, reject) => {
176
+ const request = __classPrivateFieldGet(this, _IndexedDbPersistence_factory, "f").deleteDatabase(__classPrivateFieldGet(this, _IndexedDbPersistence_databaseName, "f"));
177
+ request.onsuccess = () => resolve();
178
+ request.onerror = () => reject(request.error);
179
+ request.onblocked = () => reject(new DatabaseOperationError(`Cannot clear ${__classPrivateFieldGet(this, _IndexedDbPersistence_databaseName, "f")}: another connection holds it open; the delete stays pending until it closes`));
180
+ });
181
+ __classPrivateFieldGet(this, _IndexedDbPersistence_dupsort, "f").clear();
182
+ }
183
+ close() {
184
+ __classPrivateFieldGet(this, _IndexedDbPersistence_db, "f")?.close();
185
+ __classPrivateFieldSet(this, _IndexedDbPersistence_db, null, "f");
186
+ __classPrivateFieldSet(this, _IndexedDbPersistence_lost, null, "f");
187
+ return Promise.resolve();
188
+ }
189
+ }
190
+ _IndexedDbPersistence_databaseName = new WeakMap(), _IndexedDbPersistence_factory = new WeakMap(), _IndexedDbPersistence_keyRange = new WeakMap(), _IndexedDbPersistence_dupsort = new WeakMap(), _IndexedDbPersistence_db = new WeakMap(), _IndexedDbPersistence_lost = new WeakMap(), _IndexedDbPersistence_instances = new WeakSet(), _IndexedDbPersistence_connect = function _IndexedDbPersistence_connect() {
191
+ if (__classPrivateFieldGet(this, _IndexedDbPersistence_db, "f"))
192
+ return Promise.resolve(__classPrivateFieldGet(this, _IndexedDbPersistence_db, "f"));
193
+ if (!__classPrivateFieldGet(this, _IndexedDbPersistence_factory, "f"))
194
+ throw new DatabaseOperationError("IndexedDB is not available in this environment");
195
+ return new Promise((resolve, reject) => {
196
+ const request = __classPrivateFieldGet(this, _IndexedDbPersistence_factory, "f").open(__classPrivateFieldGet(this, _IndexedDbPersistence_databaseName, "f"), 1);
197
+ request.onupgradeneeded = () => {
198
+ const db = request.result;
199
+ db.createObjectStore(ROWS);
200
+ db.createObjectStore(META);
201
+ };
202
+ request.onerror = () => reject(request.error);
203
+ request.onsuccess = () => {
204
+ const db = request.result;
205
+ db.onversionchange = () => {
206
+ db.close();
207
+ if (__classPrivateFieldGet(this, _IndexedDbPersistence_db, "f") === db) {
208
+ __classPrivateFieldSet(this, _IndexedDbPersistence_db, null, "f");
209
+ __classPrivateFieldSet(this, _IndexedDbPersistence_lost, new DatabaseOperationError("IndexedDB connection closed by another context's versionchange"), "f");
210
+ }
211
+ };
212
+ __classPrivateFieldSet(this, _IndexedDbPersistence_lost, null, "f");
213
+ __classPrivateFieldSet(this, _IndexedDbPersistence_db, db, "f");
214
+ resolve(db);
215
+ };
216
+ });
217
+ }, _IndexedDbPersistence_connection = function _IndexedDbPersistence_connection() {
218
+ if (__classPrivateFieldGet(this, _IndexedDbPersistence_db, "f"))
219
+ return __classPrivateFieldGet(this, _IndexedDbPersistence_db, "f");
220
+ throw __classPrivateFieldGet(this, _IndexedDbPersistence_lost, "f") ?? new DatabaseOperationError("IndexedDB persistence is not connected");
221
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Browser IndexedDB backend surface for `keri-ts/db/indexeddb`.
3
+ *
4
+ * Kept off `keri-ts/db` because `IndexedDbPersistence` names DOM types
5
+ * (`IDBFactory`, `IDBKeyRange`) that Node consumers should not have to type-check.
6
+ */
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
14
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
15
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
16
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
17
+ };
18
+ var _LockedWebDBer_instances, _LockedWebDBer_group, _LockedWebDBer_identity, _LockedWebDBer_ref, _LockedWebDBer_reopening, _LockedWebDBer_letGo, _LockedStoreOpener_group;
19
+ import { call } from "effection";
20
+ import { IndexedDbPersistence } from "./core/indexeddb-persistence.js";
21
+ import { WebDBer, WebStoreOpener } from "./core/webdber.js";
22
+ import { acquireStoreLock, StoreLockGroup, StoreLockHeldError, storeLockKey, StoreLockUnsupportedError, } from "./store-lock.js";
23
+ export { acquireStoreLock, IndexedDbPersistence, StoreLockHeldError, storeLockKey, StoreLockUnsupportedError, };
24
+ /** A `WebDBer` that holds a reference on its opener's lock for as long as it is open. */
25
+ class LockedWebDBer extends WebDBer {
26
+ constructor(identity, persistence, queue, group) {
27
+ super(identity, persistence, queue);
28
+ _LockedWebDBer_instances.add(this);
29
+ _LockedWebDBer_group.set(this, void 0);
30
+ _LockedWebDBer_identity.set(this, void 0);
31
+ _LockedWebDBer_ref.set(this, void 0);
32
+ _LockedWebDBer_reopening.set(this, false);
33
+ __classPrivateFieldSet(this, _LockedWebDBer_group, group, "f");
34
+ __classPrivateFieldSet(this, _LockedWebDBer_identity, identity, "f");
35
+ }
36
+ /** Take the lock before `WebDBer.reopen` loads anything; any failure or refusal to open lets it go. */
37
+ *reopen(options = {}) {
38
+ // Assigned inside the promise so a halt while waiting cannot orphan the reference.
39
+ yield* call(async () => {
40
+ __classPrivateFieldSet(this, _LockedWebDBer_ref, __classPrivateFieldGet(this, _LockedWebDBer_ref, "f") ?? await __classPrivateFieldGet(this, _LockedWebDBer_group, "f").acquire(__classPrivateFieldGet(this, _LockedWebDBer_identity, "f")), "f");
41
+ });
42
+ __classPrivateFieldSet(this, _LockedWebDBer_reopening, true, "f");
43
+ let opened = false;
44
+ try {
45
+ opened = yield* super.reopen(options);
46
+ return opened;
47
+ }
48
+ finally {
49
+ __classPrivateFieldSet(this, _LockedWebDBer_reopening, false, "f");
50
+ if (!opened)
51
+ yield* __classPrivateFieldGet(this, _LockedWebDBer_instances, "m", _LockedWebDBer_letGo).call(this);
52
+ }
53
+ }
54
+ /** Close, then release the lock even when closing fails. `reopen`'s own inner close keeps it. */
55
+ *close(clear = false) {
56
+ try {
57
+ return yield* super.close(clear);
58
+ }
59
+ finally {
60
+ if (!__classPrivateFieldGet(this, _LockedWebDBer_reopening, "f"))
61
+ yield* __classPrivateFieldGet(this, _LockedWebDBer_instances, "m", _LockedWebDBer_letGo).call(this);
62
+ }
63
+ }
64
+ }
65
+ _LockedWebDBer_group = new WeakMap(), _LockedWebDBer_identity = new WeakMap(), _LockedWebDBer_ref = new WeakMap(), _LockedWebDBer_reopening = new WeakMap(), _LockedWebDBer_instances = new WeakSet(), _LockedWebDBer_letGo = function* _LockedWebDBer_letGo() {
66
+ const ref = __classPrivateFieldGet(this, _LockedWebDBer_ref, "f");
67
+ __classPrivateFieldSet(this, _LockedWebDBer_ref, undefined, "f");
68
+ if (ref)
69
+ yield* call(() => ref.release());
70
+ };
71
+ /** `WebStoreOpener` whose stores share one reference-counted lock per store identity. */
72
+ class LockedStoreOpener extends WebStoreOpener {
73
+ constructor(persistenceFor, locks) {
74
+ super(persistenceFor);
75
+ _LockedStoreOpener_group.set(this, void 0);
76
+ __classPrivateFieldSet(this, _LockedStoreOpener_group, new StoreLockGroup(locks), "f");
77
+ }
78
+ create(family, identity) {
79
+ return new LockedWebDBer(identity, this.persistenceFor(family, identity), this.queue, __classPrivateFieldGet(this, _LockedStoreOpener_group, "f"));
80
+ }
81
+ }
82
+ _LockedStoreOpener_group = new WeakMap();
83
+ /**
84
+ * A `WebStoreOpener` whose stores persist to IndexedDB, one database per store
85
+ * family and identity. Inject `factory` and `keyRange` together outside a browser.
86
+ *
87
+ * Single-writer by default: the first store to open an identity (`base`, `name`, `temp`) takes a
88
+ * Web Locks lock, shared by every family the opener creates for it and released when the last of
89
+ * them closes. A second opener, tab or window on that identity is refused with
90
+ * `StoreLockHeldError`, and an engine without `navigator.locks` with `StoreLockUnsupportedError`;
91
+ * nothing runs unlocked unless `unsafeSkipStoreLock` says so.
92
+ */
93
+ export function indexedDbOpener(options = {}) {
94
+ const persistenceFor = (family, identity) => new IndexedDbPersistence(family, identity, options.factory, options.keyRange);
95
+ return options.unsafeSkipStoreLock
96
+ ? new WebStoreOpener(persistenceFor)
97
+ : new LockedStoreOpener(persistenceFor, options.locks === undefined ? {} : { locks: options.locks });
98
+ }