@learncard/network-brain-service 3.18.0 → 3.19.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 (478) hide show
  1. package/.env.example +38 -2
  2. package/CHANGELOG.md +118 -0
  3. package/README.md +42 -0
  4. package/compose.inbox.yml +7 -0
  5. package/dist/accesslayer/auth-grant/read.d.ts +1 -0
  6. package/dist/accesslayer/auth-grant/read.d.ts.map +1 -1
  7. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  8. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  9. package/dist/accesslayer/credential-activity/create.d.ts.map +1 -1
  10. package/dist/accesslayer/credential-refresh/email-delivery.d.ts +18 -0
  11. package/dist/accesslayer/credential-refresh/email-delivery.d.ts.map +1 -0
  12. package/dist/accesslayer/credential-refresh/index.d.ts +1 -0
  13. package/dist/accesslayer/credential-refresh/index.d.ts.map +1 -1
  14. package/dist/accesslayer/credential-refresh/read.d.ts.map +1 -1
  15. package/dist/accesslayer/credential-refresh/update.d.ts.map +1 -1
  16. package/dist/accesslayer/inbox-batch/store.d.ts +33 -0
  17. package/dist/accesslayer/inbox-batch/store.d.ts.map +1 -0
  18. package/dist/accesslayer/inbox-credential/create.d.ts +5 -0
  19. package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
  20. package/dist/accesslayer/inbox-credential/refresh.d.ts +71 -0
  21. package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -0
  22. package/dist/accesslayer/inbox-credential/update.d.ts +1 -1
  23. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  24. package/dist/accesslayer/presentation/read.d.ts.map +1 -1
  25. package/dist/accesslayer/presentation/relationships/create.d.ts +2 -2
  26. package/dist/accesslayer/presentation/relationships/create.d.ts.map +1 -1
  27. package/dist/accesslayer/presentation/relationships/read.d.ts.map +1 -1
  28. package/dist/accesslayer/share-link/abandon.d.ts +3 -0
  29. package/dist/accesslayer/share-link/abandon.d.ts.map +1 -0
  30. package/dist/accesslayer/share-link/cleanup.d.ts +6 -0
  31. package/dist/accesslayer/share-link/cleanup.d.ts.map +1 -0
  32. package/dist/accesslayer/share-link/errors.d.ts +8 -0
  33. package/dist/accesslayer/share-link/errors.d.ts.map +1 -0
  34. package/dist/accesslayer/share-link/finalize.d.ts +3 -0
  35. package/dist/accesslayer/share-link/finalize.d.ts.map +1 -0
  36. package/dist/accesslayer/share-link/helpers.d.ts +56 -0
  37. package/dist/accesslayer/share-link/helpers.d.ts.map +1 -0
  38. package/dist/accesslayer/share-link/index.d.ts +12 -0
  39. package/dist/accesslayer/share-link/index.d.ts.map +1 -0
  40. package/dist/accesslayer/share-link/list.d.ts +5 -0
  41. package/dist/accesslayer/share-link/list.d.ts.map +1 -0
  42. package/dist/accesslayer/share-link/read.d.ts +5 -0
  43. package/dist/accesslayer/share-link/read.d.ts.map +1 -0
  44. package/dist/accesslayer/share-link/receipt.d.ts +11 -0
  45. package/dist/accesslayer/share-link/receipt.d.ts.map +1 -0
  46. package/dist/accesslayer/share-link/recovery.d.ts +6 -0
  47. package/dist/accesslayer/share-link/recovery.d.ts.map +1 -0
  48. package/dist/accesslayer/share-link/reserve.d.ts +4 -0
  49. package/dist/accesslayer/share-link/reserve.d.ts.map +1 -0
  50. package/dist/accesslayer/share-link/revoke.d.ts +3 -0
  51. package/dist/accesslayer/share-link/revoke.d.ts.map +1 -0
  52. package/dist/accesslayer/share-link/transaction-execution.d.ts +18 -0
  53. package/dist/accesslayer/share-link/transaction-execution.d.ts.map +1 -0
  54. package/dist/accesslayer/share-link/transaction.d.ts +15 -0
  55. package/dist/accesslayer/share-link/transaction.d.ts.map +1 -0
  56. package/dist/accesslayer/share-link/types.d.ts +370 -0
  57. package/dist/accesslayer/share-link/types.d.ts.map +1 -0
  58. package/dist/app.d.ts +4 -0
  59. package/dist/app.d.ts.map +1 -1
  60. package/dist/cache/atomic-counter.test-helpers.d.ts +3 -0
  61. package/dist/cache/atomic-counter.test-helpers.d.ts.map +1 -0
  62. package/dist/cache/atomic-counter.test.d.ts +2 -0
  63. package/dist/cache/atomic-counter.test.d.ts.map +1 -0
  64. package/dist/cache/index.d.ts +1 -1
  65. package/dist/cache/index.d.ts.map +1 -1
  66. package/dist/config/environment.d.ts +55 -0
  67. package/dist/config/environment.d.ts.map +1 -1
  68. package/dist/constants/auth-grant.d.ts +2 -0
  69. package/dist/constants/auth-grant.d.ts.map +1 -1
  70. package/dist/credential-refresh.d.ts.map +1 -1
  71. package/dist/helpers/activity.helpers.d.ts +4 -0
  72. package/dist/helpers/activity.helpers.d.ts.map +1 -1
  73. package/dist/helpers/boost.helpers.d.ts.map +1 -1
  74. package/dist/helpers/contact-method.helpers.d.ts.map +1 -1
  75. package/dist/helpers/contact-method.helpers.test.d.ts +2 -0
  76. package/dist/helpers/contact-method.helpers.test.d.ts.map +1 -0
  77. package/dist/helpers/credential-refresh-email-content.helpers.d.ts +44 -0
  78. package/dist/helpers/credential-refresh-email-content.helpers.d.ts.map +1 -0
  79. package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts +2 -0
  80. package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts.map +1 -0
  81. package/dist/helpers/credential-refresh-email.helpers.d.ts +17 -0
  82. package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -0
  83. package/dist/helpers/credential-refresh-proof.helpers.d.ts +4 -0
  84. package/dist/helpers/credential-refresh-proof.helpers.d.ts.map +1 -0
  85. package/dist/helpers/credential-refresh-proof.helpers.test.d.ts +2 -0
  86. package/dist/helpers/credential-refresh-proof.helpers.test.d.ts.map +1 -0
  87. package/dist/helpers/credential-refresh.helpers.d.ts +45 -2
  88. package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
  89. package/dist/helpers/credential.helpers.d.ts +2 -1
  90. package/dist/helpers/credential.helpers.d.ts.map +1 -1
  91. package/dist/helpers/credentialSubject.helpers.d.ts +3 -0
  92. package/dist/helpers/credentialSubject.helpers.d.ts.map +1 -0
  93. package/dist/helpers/credentialSubject.helpers.test.d.ts +2 -0
  94. package/dist/helpers/credentialSubject.helpers.test.d.ts.map +1 -0
  95. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  96. package/dist/helpers/inbox-batch-http.helpers.d.ts +15 -0
  97. package/dist/helpers/inbox-batch-http.helpers.d.ts.map +1 -0
  98. package/dist/helpers/inbox-batch-http.helpers.test.d.ts +2 -0
  99. package/dist/helpers/inbox-batch-http.helpers.test.d.ts.map +1 -0
  100. package/dist/helpers/inbox-batch-status.helpers.d.ts +3 -0
  101. package/dist/helpers/inbox-batch-status.helpers.d.ts.map +1 -0
  102. package/dist/helpers/inbox-batch.helpers.d.ts +10 -0
  103. package/dist/helpers/inbox-batch.helpers.d.ts.map +1 -0
  104. package/dist/helpers/inbox-batch.helpers.test.d.ts +2 -0
  105. package/dist/helpers/inbox-batch.helpers.test.d.ts.map +1 -0
  106. package/dist/helpers/inbox-issuance-error.helpers.d.ts +13 -0
  107. package/dist/helpers/inbox-issuance-error.helpers.d.ts.map +1 -0
  108. package/dist/helpers/inbox-queue.helpers.d.ts +11 -0
  109. package/dist/helpers/inbox-queue.helpers.d.ts.map +1 -0
  110. package/dist/helpers/inbox-queue.helpers.test.d.ts +2 -0
  111. package/dist/helpers/inbox-queue.helpers.test.d.ts.map +1 -0
  112. package/dist/helpers/inbox-refresh.helpers.d.ts +186 -0
  113. package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -0
  114. package/dist/helpers/inbox.helpers.d.ts +9 -1
  115. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  116. package/dist/helpers/notificationMessages.d.ts +1 -1
  117. package/dist/helpers/notificationMessages.d.ts.map +1 -1
  118. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  119. package/dist/helpers/presentation-metadata.d.ts +4 -0
  120. package/dist/helpers/presentation-metadata.d.ts.map +1 -0
  121. package/dist/helpers/presentation.helpers.d.ts +1 -1
  122. package/dist/helpers/presentation.helpers.d.ts.map +1 -1
  123. package/dist/helpers/profile.helpers.d.ts +1 -0
  124. package/dist/helpers/profile.helpers.d.ts.map +1 -1
  125. package/dist/helpers/rateLimit.helpers.d.ts.map +1 -1
  126. package/dist/helpers/refresh-send-intent.helpers.d.ts +51 -0
  127. package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -0
  128. package/dist/helpers/share-content-client/adapters.d.ts +10 -0
  129. package/dist/helpers/share-content-client/adapters.d.ts.map +1 -0
  130. package/dist/helpers/share-content-client/canonical.d.ts +7 -0
  131. package/dist/helpers/share-content-client/canonical.d.ts.map +1 -0
  132. package/dist/helpers/share-content-client/client.d.ts +5 -0
  133. package/dist/helpers/share-content-client/client.d.ts.map +1 -0
  134. package/dist/helpers/share-content-client/config.d.ts +11 -0
  135. package/dist/helpers/share-content-client/config.d.ts.map +1 -0
  136. package/dist/helpers/share-content-client/index.d.ts +6 -0
  137. package/dist/helpers/share-content-client/index.d.ts.map +1 -0
  138. package/dist/helpers/share-content-client/responses.d.ts +16 -0
  139. package/dist/helpers/share-content-client/responses.d.ts.map +1 -0
  140. package/dist/helpers/share-content-client/types.d.ts +145 -0
  141. package/dist/helpers/share-content-client/types.d.ts.map +1 -0
  142. package/dist/helpers/share-link-coordinator/budget-helpers.d.ts +5 -0
  143. package/dist/helpers/share-link-coordinator/budget-helpers.d.ts.map +1 -0
  144. package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts +3 -0
  145. package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts.map +1 -0
  146. package/dist/helpers/share-link-coordinator/coordinator.d.ts +4 -0
  147. package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -0
  148. package/dist/helpers/share-link-coordinator/index.d.ts +5 -0
  149. package/dist/helpers/share-link-coordinator/index.d.ts.map +1 -0
  150. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts +9 -0
  151. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -0
  152. package/dist/helpers/share-link-coordinator/runtime.d.ts +11 -0
  153. package/dist/helpers/share-link-coordinator/runtime.d.ts.map +1 -0
  154. package/dist/helpers/share-link-coordinator/stat.d.ts +4 -0
  155. package/dist/helpers/share-link-coordinator/stat.d.ts.map +1 -0
  156. package/dist/helpers/share-link-coordinator/types.d.ts +160 -0
  157. package/dist/helpers/share-link-coordinator/types.d.ts.map +1 -0
  158. package/dist/helpers/share-link-lifecycle/index.d.ts +5 -0
  159. package/dist/helpers/share-link-lifecycle/index.d.ts.map +1 -0
  160. package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts +4 -0
  161. package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts.map +1 -0
  162. package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts +21 -0
  163. package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts.map +1 -0
  164. package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts +9 -0
  165. package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts.map +1 -0
  166. package/dist/helpers/share-link-lifecycle/types.d.ts +14 -0
  167. package/dist/helpers/share-link-lifecycle/types.d.ts.map +1 -0
  168. package/dist/helpers/share-link-maintenance/budget.d.ts +12 -0
  169. package/dist/helpers/share-link-maintenance/budget.d.ts.map +1 -0
  170. package/dist/helpers/share-link-maintenance/config.d.ts +48 -0
  171. package/dist/helpers/share-link-maintenance/config.d.ts.map +1 -0
  172. package/dist/helpers/share-link-maintenance/index.d.ts +8 -0
  173. package/dist/helpers/share-link-maintenance/index.d.ts.map +1 -0
  174. package/dist/helpers/share-link-maintenance/runner.d.ts +19 -0
  175. package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -0
  176. package/dist/helpers/share-link-maintenance/runtime.d.ts +36 -0
  177. package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -0
  178. package/dist/helpers/share-link-maintenance/scheduler.d.ts +18 -0
  179. package/dist/helpers/share-link-maintenance/scheduler.d.ts.map +1 -0
  180. package/dist/helpers/share-link-maintenance/telemetry.d.ts +5 -0
  181. package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -0
  182. package/dist/helpers/share-link-maintenance/types.d.ts +53 -0
  183. package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -0
  184. package/dist/helpers/share-link-owner/config.d.ts +16 -0
  185. package/dist/helpers/share-link-owner/config.d.ts.map +1 -0
  186. package/dist/helpers/share-link-owner/diagnostics.d.ts +26 -0
  187. package/dist/helpers/share-link-owner/diagnostics.d.ts.map +1 -0
  188. package/dist/helpers/share-link-owner/lazy-initializer.d.ts +2 -0
  189. package/dist/helpers/share-link-owner/lazy-initializer.d.ts.map +1 -0
  190. package/dist/helpers/share-link-owner-projection.d.ts +4 -0
  191. package/dist/helpers/share-link-owner-projection.d.ts.map +1 -0
  192. package/dist/helpers/share-link-passcode-abuse.d.ts +2 -0
  193. package/dist/helpers/share-link-passcode-abuse.d.ts.map +1 -0
  194. package/dist/helpers/share-link-passcode.d.ts +6 -0
  195. package/dist/helpers/share-link-passcode.d.ts.map +1 -0
  196. package/dist/helpers/share-link-policy/index.d.ts +4 -0
  197. package/dist/helpers/share-link-policy/index.d.ts.map +1 -0
  198. package/dist/helpers/share-link-policy/production.d.ts +13 -0
  199. package/dist/helpers/share-link-policy/production.d.ts.map +1 -0
  200. package/dist/helpers/share-link-policy/resolver.d.ts +6 -0
  201. package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -0
  202. package/dist/helpers/share-link-policy/types.d.ts +16 -0
  203. package/dist/helpers/share-link-policy/types.d.ts.map +1 -0
  204. package/dist/helpers/share-link-public/config.d.ts +13 -0
  205. package/dist/helpers/share-link-public/config.d.ts.map +1 -0
  206. package/dist/helpers/share-link-view-notification.d.ts +2 -0
  207. package/dist/helpers/share-link-view-notification.d.ts.map +1 -0
  208. package/dist/helpers/shim.d.ts +1 -1
  209. package/dist/helpers/shim.d.ts.map +1 -1
  210. package/dist/inbox-queue-local.d.ts +2 -0
  211. package/dist/inbox-queue-local.d.ts.map +1 -0
  212. package/dist/index.js +2 -2
  213. package/dist/models/AuthGrant.d.ts.map +1 -1
  214. package/dist/models/CredentialActivity.d.ts +1 -0
  215. package/dist/models/CredentialActivity.d.ts.map +1 -1
  216. package/dist/models/CredentialRefresh.d.ts +5 -2
  217. package/dist/models/CredentialRefresh.d.ts.map +1 -1
  218. package/dist/models/InboxCredential.d.ts +1 -0
  219. package/dist/models/InboxCredential.d.ts.map +1 -1
  220. package/dist/models/Presentation.d.ts +2 -0
  221. package/dist/models/Presentation.d.ts.map +1 -1
  222. package/dist/models/Profile.d.ts +2 -0
  223. package/dist/models/Profile.d.ts.map +1 -1
  224. package/dist/models/ShareLink.d.ts +44 -0
  225. package/dist/models/ShareLink.d.ts.map +1 -0
  226. package/dist/models/ShareViewReceipt.d.ts +22 -0
  227. package/dist/models/ShareViewReceipt.d.ts.map +1 -0
  228. package/dist/models/credential-refresh-constraints.d.ts.map +1 -1
  229. package/dist/models/index.d.ts +1 -0
  230. package/dist/models/index.d.ts.map +1 -1
  231. package/dist/models/share-link-constraints.d.ts +3 -0
  232. package/dist/models/share-link-constraints.d.ts.map +1 -0
  233. package/dist/openapi.d.ts.map +1 -1
  234. package/dist/public-share-link-http.d.ts +4 -0
  235. package/dist/public-share-link-http.d.ts.map +1 -0
  236. package/dist/routes/act-as.test.d.ts +2 -0
  237. package/dist/routes/act-as.test.d.ts.map +1 -0
  238. package/dist/routes/activity.d.ts +3 -0
  239. package/dist/routes/activity.d.ts.map +1 -1
  240. package/dist/routes/app-store.d.ts.map +1 -1
  241. package/dist/routes/auth-grants.d.ts +10 -0
  242. package/dist/routes/auth-grants.d.ts.map +1 -1
  243. package/dist/routes/boosts.d.ts +494 -0
  244. package/dist/routes/boosts.d.ts.map +1 -1
  245. package/dist/routes/contracts.d.ts +2 -2
  246. package/dist/routes/contracts.d.ts.map +1 -1
  247. package/dist/routes/credential-refreshes.d.ts.map +1 -1
  248. package/dist/routes/credentials.d.ts +1 -1
  249. package/dist/routes/credentials.d.ts.map +1 -1
  250. package/dist/routes/inbox.d.ts +953 -11
  251. package/dist/routes/inbox.d.ts.map +1 -1
  252. package/dist/routes/index.d.ts +154 -85
  253. package/dist/routes/index.d.ts.map +1 -1
  254. package/dist/routes/presentations.d.ts +1 -0
  255. package/dist/routes/presentations.d.ts.map +1 -1
  256. package/dist/routes/profile-manager.d.ts.map +1 -1
  257. package/dist/routes/profiles.d.ts.map +1 -1
  258. package/dist/routes/public-share-links.d.ts +230 -0
  259. package/dist/routes/public-share-links.d.ts.map +1 -0
  260. package/dist/routes/share-links.d.ts +844 -0
  261. package/dist/routes/share-links.d.ts.map +1 -0
  262. package/dist/routes/workflows.d.ts.map +1 -1
  263. package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
  264. package/dist/services/delivery/adapters/postmark.adapter.test.d.ts +2 -0
  265. package/dist/services/delivery/adapters/postmark.adapter.test.d.ts.map +1 -0
  266. package/dist/services/registry/registry.factory.d.ts.map +1 -1
  267. package/dist/types/activity.d.ts +4 -0
  268. package/dist/types/activity.d.ts.map +1 -1
  269. package/dist/types/consentflowcontract.d.ts +1 -1
  270. package/dist/types/credential-refresh.d.ts +10 -1
  271. package/dist/types/credential-refresh.d.ts.map +1 -1
  272. package/dist/types/inbox-batch.d.ts +38 -0
  273. package/dist/types/inbox-batch.d.ts.map +1 -0
  274. package/inbox-e2e-setup.ts +53 -1
  275. package/inbox-elasticmq.conf +10 -0
  276. package/lambda.ts +64 -16
  277. package/package.json +24 -16
  278. package/scripts/seed-demo-persona.ts +425 -0
  279. package/scripts/setup-inbox-queues.ts +60 -0
  280. package/serverless-local.yml +2 -0
  281. package/serverless.yml +111 -0
  282. package/shareLinkMaintenanceLambda.ts +49 -0
  283. package/src/accesslayer/auth-grant/read.ts +2 -1
  284. package/src/accesslayer/consentflowcontract/relationships/create.ts +9 -16
  285. package/src/accesslayer/consentflowcontract/relationships/update.ts +15 -28
  286. package/src/accesslayer/credential-activity/create.ts +1 -0
  287. package/src/accesslayer/credential-refresh/email-delivery.ts +116 -0
  288. package/src/accesslayer/credential-refresh/index.ts +1 -0
  289. package/src/accesslayer/credential-refresh/read.ts +8 -2
  290. package/src/accesslayer/credential-refresh/update.ts +4 -0
  291. package/src/accesslayer/inbox-batch/store.ts +469 -0
  292. package/src/accesslayer/inbox-credential/create.ts +51 -19
  293. package/src/accesslayer/inbox-credential/refresh.ts +221 -0
  294. package/src/accesslayer/inbox-credential/update.ts +1 -1
  295. package/src/accesslayer/presentation/read.ts +57 -25
  296. package/src/accesslayer/presentation/relationships/create.ts +54 -11
  297. package/src/accesslayer/presentation/relationships/read.ts +21 -6
  298. package/src/accesslayer/share-link/abandon.ts +100 -0
  299. package/src/accesslayer/share-link/cleanup.ts +271 -0
  300. package/src/accesslayer/share-link/errors.ts +32 -0
  301. package/src/accesslayer/share-link/finalize.ts +265 -0
  302. package/src/accesslayer/share-link/helpers.ts +419 -0
  303. package/src/accesslayer/share-link/index.ts +11 -0
  304. package/src/accesslayer/share-link/list.ts +162 -0
  305. package/src/accesslayer/share-link/read.ts +107 -0
  306. package/src/accesslayer/share-link/receipt.ts +548 -0
  307. package/src/accesslayer/share-link/recovery.ts +545 -0
  308. package/src/accesslayer/share-link/reserve.ts +684 -0
  309. package/src/accesslayer/share-link/revoke.ts +197 -0
  310. package/src/accesslayer/share-link/transaction-execution.ts +72 -0
  311. package/src/accesslayer/share-link/transaction.ts +126 -0
  312. package/src/accesslayer/share-link/types.ts +601 -0
  313. package/src/app.ts +6 -0
  314. package/src/cache/atomic-counter.test-helpers.ts +27 -0
  315. package/src/cache/atomic-counter.test.ts +6 -0
  316. package/src/cache/index.ts +25 -13
  317. package/src/config/environment.ts +40 -0
  318. package/src/constants/auth-grant.ts +5 -0
  319. package/src/credential-refresh.ts +5 -1
  320. package/src/docker-entry.ts +65 -0
  321. package/src/helpers/activity.helpers.ts +4 -0
  322. package/src/helpers/app-counter.helpers.ts +1 -1
  323. package/src/helpers/boost.helpers.ts +4 -31
  324. package/src/helpers/contact-method.helpers.test.ts +43 -0
  325. package/src/helpers/contact-method.helpers.ts +3 -4
  326. package/src/helpers/credential-refresh-email-content.helpers.test.ts +222 -0
  327. package/src/helpers/credential-refresh-email-content.helpers.ts +155 -0
  328. package/src/helpers/credential-refresh-email.helpers.ts +201 -0
  329. package/src/helpers/credential-refresh-proof.helpers.test.ts +103 -0
  330. package/src/helpers/credential-refresh-proof.helpers.ts +43 -0
  331. package/src/helpers/credential-refresh.helpers.ts +375 -99
  332. package/src/helpers/credential.helpers.ts +4 -1
  333. package/src/helpers/credentialSubject.helpers.test.ts +114 -0
  334. package/src/helpers/credentialSubject.helpers.ts +40 -0
  335. package/src/helpers/finalize-inbox.helpers.ts +29 -14
  336. package/src/helpers/inbox-batch-http.helpers.test.ts +188 -0
  337. package/src/helpers/inbox-batch-http.helpers.ts +150 -0
  338. package/src/helpers/inbox-batch-status.helpers.ts +11 -0
  339. package/src/helpers/inbox-batch.helpers.test.ts +668 -0
  340. package/src/helpers/inbox-batch.helpers.ts +374 -0
  341. package/src/helpers/inbox-issuance-error.helpers.ts +25 -0
  342. package/src/helpers/inbox-queue.helpers.test.ts +253 -0
  343. package/src/helpers/inbox-queue.helpers.ts +372 -0
  344. package/src/helpers/inbox-refresh.helpers.ts +432 -0
  345. package/src/helpers/inbox.helpers.ts +275 -36
  346. package/src/helpers/notificationMessages.ts +18 -1
  347. package/src/helpers/notifications.helpers.ts +4 -2
  348. package/src/helpers/presentation-metadata.ts +41 -0
  349. package/src/helpers/presentation.helpers.ts +27 -20
  350. package/src/helpers/profile.helpers.ts +7 -0
  351. package/src/helpers/rateLimit.helpers.ts +2 -5
  352. package/src/helpers/refresh-send-intent.helpers.ts +273 -0
  353. package/src/helpers/share-content-client/adapters.ts +55 -0
  354. package/src/helpers/share-content-client/canonical.ts +25 -0
  355. package/src/helpers/share-content-client/client.ts +440 -0
  356. package/src/helpers/share-content-client/config.ts +189 -0
  357. package/src/helpers/share-content-client/index.ts +27 -0
  358. package/src/helpers/share-content-client/responses.ts +273 -0
  359. package/src/helpers/share-content-client/types.ts +253 -0
  360. package/src/helpers/share-link-coordinator/budget-helpers.ts +53 -0
  361. package/src/helpers/share-link-coordinator/cleanup-runner.ts +286 -0
  362. package/src/helpers/share-link-coordinator/coordinator.ts +714 -0
  363. package/src/helpers/share-link-coordinator/index.ts +8 -0
  364. package/src/helpers/share-link-coordinator/recovery-runner.ts +428 -0
  365. package/src/helpers/share-link-coordinator/runtime.ts +88 -0
  366. package/src/helpers/share-link-coordinator/stat.ts +30 -0
  367. package/src/helpers/share-link-coordinator/types.ts +353 -0
  368. package/src/helpers/share-link-lifecycle/index.ts +4 -0
  369. package/src/helpers/share-link-lifecycle/lease.helpers.ts +35 -0
  370. package/src/helpers/share-link-lifecycle/recovery.helpers.ts +173 -0
  371. package/src/helpers/share-link-lifecycle/request-hash.helpers.ts +130 -0
  372. package/src/helpers/share-link-lifecycle/types.ts +58 -0
  373. package/src/helpers/share-link-maintenance/budget.ts +63 -0
  374. package/src/helpers/share-link-maintenance/config.ts +312 -0
  375. package/src/helpers/share-link-maintenance/index.ts +24 -0
  376. package/src/helpers/share-link-maintenance/runner.ts +223 -0
  377. package/src/helpers/share-link-maintenance/runtime.ts +334 -0
  378. package/src/helpers/share-link-maintenance/scheduler.ts +126 -0
  379. package/src/helpers/share-link-maintenance/telemetry.ts +70 -0
  380. package/src/helpers/share-link-maintenance/types.ts +89 -0
  381. package/src/helpers/share-link-owner/config.ts +48 -0
  382. package/src/helpers/share-link-owner/diagnostics.ts +89 -0
  383. package/src/helpers/share-link-owner/lazy-initializer.ts +29 -0
  384. package/src/helpers/share-link-owner-projection.ts +52 -0
  385. package/src/helpers/share-link-passcode-abuse.ts +36 -0
  386. package/src/helpers/share-link-passcode.ts +47 -0
  387. package/src/helpers/share-link-policy/index.ts +7 -0
  388. package/src/helpers/share-link-policy/production.ts +88 -0
  389. package/src/helpers/share-link-policy/resolver.ts +108 -0
  390. package/src/helpers/share-link-policy/types.ts +48 -0
  391. package/src/helpers/share-link-public/config.ts +41 -0
  392. package/src/helpers/share-link-view-notification.ts +18 -0
  393. package/src/helpers/shim.ts +32 -31
  394. package/src/inbox-queue-local.ts +28 -0
  395. package/src/models/AuthGrant.ts +1 -0
  396. package/src/models/CredentialActivity.ts +3 -5
  397. package/src/models/CredentialRefresh.ts +4 -2
  398. package/src/models/InboxCredential.ts +2 -0
  399. package/src/models/Presentation.ts +2 -2
  400. package/src/models/Profile.ts +3 -2
  401. package/src/models/ShareLink.ts +130 -0
  402. package/src/models/ShareViewReceipt.ts +69 -0
  403. package/src/models/credential-refresh-constraints.test.ts +8 -3
  404. package/src/models/credential-refresh-constraints.ts +5 -0
  405. package/src/models/index.ts +2 -0
  406. package/src/models/share-link-constraints.ts +93 -0
  407. package/src/openapi.ts +8 -0
  408. package/src/public-share-link-http.ts +49 -0
  409. package/src/routes/act-as.test.ts +239 -0
  410. package/src/routes/app-store.ts +4 -13
  411. package/src/routes/auth-grants.ts +13 -11
  412. package/src/routes/boosts.ts +916 -103
  413. package/src/routes/contracts.ts +2 -11
  414. package/src/routes/credential-refreshes.test.ts +8 -2
  415. package/src/routes/credential-refreshes.ts +4 -1
  416. package/src/routes/credentials.ts +2 -1
  417. package/src/routes/inbox.ts +95 -49
  418. package/src/routes/index.ts +216 -46
  419. package/src/routes/presentations.ts +19 -3
  420. package/src/routes/profile-manager.ts +10 -2
  421. package/src/routes/profiles.ts +17 -6
  422. package/src/routes/public-share-links.ts +814 -0
  423. package/src/routes/share-links.ts +640 -0
  424. package/src/routes/skills.ts +1 -1
  425. package/src/routes/workflows.ts +43 -24
  426. package/src/services/delivery/adapters/postmark.adapter.test.ts +54 -0
  427. package/src/services/delivery/adapters/postmark.adapter.ts +16 -1
  428. package/src/services/registry/registry.factory.ts +2 -1
  429. package/src/types/activity.ts +2 -0
  430. package/src/types/credential-refresh.ts +21 -2
  431. package/src/types/inbox-batch.ts +46 -0
  432. package/test/atomic-counter.e2e.ts +12 -0
  433. package/test/auth-grant.spec.ts +21 -21
  434. package/test/credential-refresh-email.spec.ts +663 -0
  435. package/test/credential-refresh-publication.spec.ts +2 -0
  436. package/test/dids.spec.ts +10 -1
  437. package/test/guardian-gated-routes.spec.ts +13 -0
  438. package/test/helpers/inbox-e2e-environment.ts +6 -0
  439. package/test/helpers/share-link-fixtures.ts +94 -0
  440. package/test/inbox-batch.spec.ts +1338 -0
  441. package/test/inbox-refresh.spec.ts +534 -0
  442. package/test/managed-recipient-inbox.spec.ts +130 -0
  443. package/test/notificationMessages.spec.ts +13 -0
  444. package/test/openapi.spec.ts +61 -1
  445. package/test/presentations.spec.ts +54 -6
  446. package/test/profiles.spec.ts +67 -1
  447. package/test/refresh-send.spec.ts +1799 -0
  448. package/test/registry.spec.ts +56 -11
  449. package/test/share-content-client.contract.spec.ts +365 -0
  450. package/test/share-content-client.unit.spec.ts +462 -0
  451. package/test/share-link-abuse.unit.spec.ts +65 -0
  452. package/test/share-link-coordinator.neo4j.spec.ts +684 -0
  453. package/test/share-link-coordinator.unit.spec.ts +990 -0
  454. package/test/share-link-dependency-diagnostics.unit.spec.ts +127 -0
  455. package/test/share-link-lifecycle.neo4j.spec.ts +1268 -0
  456. package/test/share-link-lifecycle.unit.spec.ts +205 -0
  457. package/test/share-link-list-cursor.unit.spec.ts +86 -0
  458. package/test/share-link-list.neo4j.spec.ts +324 -0
  459. package/test/share-link-maintenance.neo4j.spec.ts +355 -0
  460. package/test/share-link-maintenance.unit.spec.ts +1113 -0
  461. package/test/share-link-owner-policy.unit.spec.ts +210 -0
  462. package/test/share-link-owner-route.unit.spec.ts +752 -0
  463. package/test/share-link-owner-runtime.unit.spec.ts +102 -0
  464. package/test/share-link-passcode.unit.spec.ts +29 -0
  465. package/test/share-link-policy.unit.spec.ts +135 -0
  466. package/test/share-link-presentation-metadata.unit.spec.ts +49 -0
  467. package/test/share-link-production-policy.unit.spec.ts +77 -0
  468. package/test/share-link-public-config.unit.spec.ts +64 -0
  469. package/test/share-link-public-http-adapter.unit.spec.ts +315 -0
  470. package/test/share-link-public-route.unit.spec.ts +800 -0
  471. package/test/share-link-receipt.neo4j.spec.ts +943 -0
  472. package/test/share-link-recovery.neo4j.spec.ts +935 -0
  473. package/test/share-link-recovery.unit.spec.ts +726 -0
  474. package/vitest.config.ts +10 -0
  475. package/vitest.inbox-e2e.config.ts +5 -1
  476. package/vitest.integration.config.ts +2 -0
  477. package/vitest.share-links-neo4j.config.ts +19 -0
  478. package/vitest.share-links-unit.config.ts +28 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * LC-2187 share-link lifecycle constants and literals.
3
+ *
4
+ * This module is intentionally storage- and transport-agnostic: routes, the
5
+ * LearnCloud client and the reconciliation worker all derive their bounds from
6
+ * here so the Brain repository and the tests agree on one set of numbers.
7
+ */
8
+
9
+ /** Operation kinds that are bound to an idempotency record. */
10
+ export type ShareLinkOperationKind = 'create' | 'update' | 'revoke';
11
+
12
+ export const SHARE_LINK_OPERATION_KINDS: readonly ShareLinkOperationKind[] = [
13
+ 'create',
14
+ 'update',
15
+ 'revoke',
16
+ ];
17
+
18
+ /**
19
+ * Default bounded lease for one in-flight share content operation. A worker that
20
+ * cannot finish staging + finalize within the lease loses its fence and must not
21
+ * commit; a later reservation supersedes it with a higher generation.
22
+ */
23
+ export const SHARE_LINK_DEFAULT_LEASE_MS = 2 * 60 * 1000;
24
+
25
+ /**
26
+ * Retain completed operation records for 30 days.
27
+ *
28
+ * Pruning is an explicit operator action, not an automatic replay path. After a
29
+ * record is pruned, object-reference uniqueness, the share version compare-and-set
30
+ * and the `stopped` terminal state still prevent a replay from overwriting newer
31
+ * content.
32
+ */
33
+ export const SHARE_LINK_OPERATION_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
34
+
35
+ /** Cleanup retry backoff bounds (exponential, capped). */
36
+ export const SHARE_LINK_CLEANUP_BASE_BACKOFF_MS = 30 * 1000;
37
+ export const SHARE_LINK_CLEANUP_MAX_BACKOFF_MS = 24 * 60 * 60 * 1000;
38
+
39
+ /** Default cap on cleanup jobs claimed in one bounded batch. */
40
+ export const SHARE_LINK_CLEANUP_DEFAULT_BATCH = 25;
41
+ export const SHARE_LINK_CLEANUP_MAX_BATCH = 100;
42
+
43
+ /** Default lease for a claimed cleanup job. */
44
+ export const SHARE_LINK_CLEANUP_DEFAULT_CLAIM_MS = 5 * 60 * 1000;
45
+
46
+ /**
47
+ * Hard bounds for a caller-supplied cleanup claim lease. A configured
48
+ * maintenance lease is clamped into this window so a bad deployment cannot hold
49
+ * (or instantly drop) work; the repository default is unchanged.
50
+ */
51
+ export const SHARE_LINK_CLEANUP_MIN_CLAIM_MS = 1_000;
52
+ export const SHARE_LINK_CLEANUP_MAX_CLAIM_MS = 30 * 60 * 1000;
53
+
54
+ /** Reasons a durable cleanup job can be created. */
55
+ export type ShareContentCleanupReason = 'superseded' | 'abandoned' | 'stopped';
56
+
57
+ /** Lifecycle status of a durable cleanup job. */
58
+ export type ShareContentCleanupStatus = 'queued' | 'claimed' | 'completed';
@@ -0,0 +1,63 @@
1
+ import type { MaintenanceBudget } from '../share-link-coordinator';
2
+
3
+ /**
4
+ * Monotonic wall-clock budget for one maintenance pass.
5
+ *
6
+ * The runner checks `exhausted()` before each claim; once the remaining time is
7
+ * at or below `finalizeReserveMs` it must stop starting new graph/remote work so
8
+ * the pass can still log and return a summary. This is a cooperative deadline,
9
+ * not a hard kill: it never uses `Promise.race`, which would leave an untracked
10
+ * mutation running after the pass returned.
11
+ */
12
+ export type MaintenancePassBudget = MaintenanceBudget & {
13
+ elapsedMs: () => number;
14
+ configuredMs: number;
15
+ };
16
+
17
+ const defaultMonotonicNow = (): number =>
18
+ typeof performance !== 'undefined' && typeof performance.now === 'function'
19
+ ? performance.now()
20
+ : Date.now();
21
+
22
+ /**
23
+ * Resolve the pass budget from a Lambda remaining-time value.
24
+ *
25
+ * `undefined` means "no Lambda signal" and keeps the configured budget. Any
26
+ * other value failing the finite/positive check — NaN, Infinity, negative or
27
+ * zero — fails closed to a zero budget so the pass does no work.
28
+ */
29
+ export const resolveMaintenanceBudgetMs = (
30
+ configuredBudgetMs: number,
31
+ remainingTimeMs: number | undefined
32
+ ): number => {
33
+ if (remainingTimeMs === undefined) return configuredBudgetMs;
34
+ if (!Number.isFinite(remainingTimeMs) || remainingTimeMs <= 0) return 0;
35
+
36
+ return Math.min(configuredBudgetMs, Math.trunc(remainingTimeMs));
37
+ };
38
+
39
+ export const createMaintenancePassBudget = (input: {
40
+ budgetMs: number;
41
+ finalizeReserveMs: number;
42
+ monotonicNow?: () => number;
43
+ }): MaintenancePassBudget => {
44
+ const monotonicNow = input.monotonicNow ?? defaultMonotonicNow;
45
+ const configuredMs = Number.isFinite(input.budgetMs)
46
+ ? Math.max(0, Math.trunc(input.budgetMs))
47
+ : 0;
48
+ const reserveMs = Number.isFinite(input.finalizeReserveMs)
49
+ ? Math.max(0, Math.trunc(input.finalizeReserveMs))
50
+ : 0;
51
+ const startedAt = monotonicNow();
52
+ const deadline = startedAt + configuredMs;
53
+
54
+ const remainingMs = (): number => deadline - monotonicNow();
55
+
56
+ return {
57
+ configuredMs,
58
+ elapsedMs: () => monotonicNow() - startedAt,
59
+ remainingMs,
60
+ reserveMs: () => reserveMs,
61
+ exhausted: () => remainingMs() <= reserveMs,
62
+ };
63
+ };
@@ -0,0 +1,312 @@
1
+ import {
2
+ SHARE_LINK_CLEANUP_DEFAULT_BATCH,
3
+ SHARE_LINK_CLEANUP_DEFAULT_CLAIM_MS,
4
+ SHARE_LINK_CLEANUP_MAX_BATCH,
5
+ SHARE_LINK_CLEANUP_MAX_CLAIM_MS,
6
+ SHARE_LINK_CLEANUP_MIN_CLAIM_MS,
7
+ SHARE_LINK_DEFAULT_LEASE_MS,
8
+ } from '../share-link-lifecycle';
9
+ import {
10
+ SHARE_LINK_RECOVERY_DEFAULT_BATCH,
11
+ SHARE_LINK_RECOVERY_MAX_BATCH,
12
+ } from '../share-link-lifecycle/recovery.helpers';
13
+ import { validateShareContentOrigin } from '../share-content-client/config';
14
+ import type { ShareLinkMaintenanceConfig, ShareLinkMaintenanceConfigResolution } from './types';
15
+
16
+ /**
17
+ * Operator-facing configuration for the automatic share-link maintenance pass.
18
+ *
19
+ * Every value is read and validated here rather than in the global environment
20
+ * schema so malformed maintenance settings fail closed to a fixed category
21
+ * instead of preventing the whole Brain service from booting. Absent or
22
+ * invalid configuration must perform no graph or remote I/O and must not
23
+ * initialize a signer.
24
+ */
25
+
26
+ export const SHARE_LINK_MAINTENANCE_ENV = {
27
+ NAMESPACE: 'SHARE_LINK_MAINTENANCE_NAMESPACE',
28
+ ORIGIN: 'SHARE_LINK_MAINTENANCE_ORIGIN',
29
+ AUDIENCE: 'SHARE_LINK_MAINTENANCE_AUDIENCE',
30
+ ALLOW_INSECURE_LOOPBACK: 'SHARE_LINK_MAINTENANCE_ALLOW_INSECURE_LOOPBACK',
31
+ CLAIMANT: 'SHARE_LINK_MAINTENANCE_CLAIMANT',
32
+ RECOVERY_LIMIT: 'SHARE_LINK_MAINTENANCE_RECOVERY_LIMIT',
33
+ CLEANUP_LIMIT: 'SHARE_LINK_MAINTENANCE_CLEANUP_LIMIT',
34
+ RECEIPT_PRUNE_LIMIT: 'SHARE_LINK_MAINTENANCE_RECEIPT_PRUNE_LIMIT',
35
+ LEASE_MS: 'SHARE_LINK_MAINTENANCE_LEASE_MS',
36
+ CLAIM_MS: 'SHARE_LINK_MAINTENANCE_CLAIM_MS',
37
+ BUDGET_MS: 'SHARE_LINK_MAINTENANCE_BUDGET_MS',
38
+ INTERVAL_MS: 'SHARE_LINK_MAINTENANCE_INTERVAL_MS',
39
+ REQUEST_TIMEOUT_MS: 'SHARE_LINK_MAINTENANCE_REQUEST_TIMEOUT_MS',
40
+ FINALIZE_RESERVE_MS: 'SHARE_LINK_MAINTENANCE_FINALIZE_RESERVE_MS',
41
+ GRAPH_TIMEOUT_MS: 'SHARE_LINK_MAINTENANCE_GRAPH_TIMEOUT_MS',
42
+ } as const;
43
+
44
+ export const SHARE_LINK_MAINTENANCE_DEFAULTS = Object.freeze({
45
+ claimant: 'brain-share-link-maintenance',
46
+ recoveryLimit: SHARE_LINK_RECOVERY_DEFAULT_BATCH,
47
+ cleanupLimit: SHARE_LINK_CLEANUP_DEFAULT_BATCH,
48
+ receiptPruneLimit: 200,
49
+ leaseMs: SHARE_LINK_DEFAULT_LEASE_MS,
50
+ claimMs: SHARE_LINK_CLEANUP_DEFAULT_CLAIM_MS,
51
+ budgetMs: 60_000,
52
+ intervalMs: 300_000,
53
+ requestTimeoutMs: 10_000,
54
+ finalizeReserveMs: 5_000,
55
+ graphTimeoutMs: 15_000,
56
+ });
57
+
58
+ export const SHARE_LINK_MAINTENANCE_INVALID_CATEGORY =
59
+ 'share_link_maintenance_configuration_invalid' as const;
60
+
61
+ /**
62
+ * Transport/service-client portion of the share-content configuration.
63
+ *
64
+ * Owner/public APIs and maintenance use the same validated namespace, origin,
65
+ * and audience. No backend rollout flag is required.
66
+ */
67
+ export type ShareLinkServiceTransportConfig = {
68
+ namespace: string;
69
+ origin: string;
70
+ audience: string;
71
+ allowInsecureLoopback: boolean;
72
+ };
73
+
74
+ export type ShareLinkServiceTransportConfigResolution =
75
+ { status: 'enabled'; config: ShareLinkServiceTransportConfig } | { status: 'invalid' };
76
+
77
+ /** Whether any required service wiring has been supplied (empty deployment defaults are absent). */
78
+ export const hasShareLinkServiceTransportConfig = (source: Record<string, unknown>): boolean =>
79
+ [
80
+ SHARE_LINK_MAINTENANCE_ENV.NAMESPACE,
81
+ SHARE_LINK_MAINTENANCE_ENV.ORIGIN,
82
+ SHARE_LINK_MAINTENANCE_ENV.AUDIENCE,
83
+ ].some(key => source[key] !== undefined && source[key] !== null && source[key] !== '');
84
+
85
+ /**
86
+ * Validate service-client transport fields without echoing values or exceptions.
87
+ */
88
+ export const resolveShareLinkServiceTransportConfig = (
89
+ raw: unknown
90
+ ): ShareLinkServiceTransportConfigResolution => {
91
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
92
+ return { status: 'invalid' };
93
+ }
94
+
95
+ const source = raw as Record<string, unknown>;
96
+ const allowInsecureLoopback = parseDeliberateBoolean(
97
+ source[SHARE_LINK_MAINTENANCE_ENV.ALLOW_INSECURE_LOOPBACK]
98
+ );
99
+ if (allowInsecureLoopback === null) return { status: 'invalid' };
100
+ const insecureLoopbackAllowed = allowInsecureLoopback === true;
101
+
102
+ const origin = validateShareContentOrigin(
103
+ source[SHARE_LINK_MAINTENANCE_ENV.ORIGIN],
104
+ insecureLoopbackAllowed
105
+ );
106
+ if (!origin.ok) return { status: 'invalid' };
107
+
108
+ const namespace = source[SHARE_LINK_MAINTENANCE_ENV.NAMESPACE];
109
+ if (!isOpaqueIdentifier(namespace)) return { status: 'invalid' };
110
+
111
+ const audience = source[SHARE_LINK_MAINTENANCE_ENV.AUDIENCE];
112
+ if (typeof audience !== 'string' || !audience.startsWith('did:')) {
113
+ return { status: 'invalid' };
114
+ }
115
+
116
+ return {
117
+ status: 'enabled',
118
+ config: {
119
+ namespace,
120
+ origin: origin.origin,
121
+ audience,
122
+ allowInsecureLoopback: insecureLoopbackAllowed,
123
+ },
124
+ };
125
+ };
126
+
127
+ const OPAQUE_IDENTIFIER_RE = /^[A-Za-z0-9._~-]+$/;
128
+
129
+ const isOpaqueIdentifier = (value: unknown, maxLength = 128): value is string =>
130
+ typeof value === 'string' &&
131
+ value.length >= 1 &&
132
+ value.length <= maxLength &&
133
+ OPAQUE_IDENTIFIER_RE.test(value);
134
+
135
+ const MISSING = Symbol('missing');
136
+
137
+ /** Deliberate boolean parse: only booleans and the exact strings true/false. */
138
+ const parseDeliberateBoolean = (value: unknown): boolean | typeof MISSING | null => {
139
+ if (value === undefined || value === null || value === '') return MISSING;
140
+ if (typeof value === 'boolean') return value;
141
+ if (typeof value === 'string') {
142
+ const normalized = value.trim().toLowerCase();
143
+ if (normalized === 'true') return true;
144
+ if (normalized === 'false') return false;
145
+ }
146
+
147
+ return null;
148
+ };
149
+
150
+ type BoundedIntegerOptions = { minimum: number; maximum: number; fallback: number };
151
+
152
+ const parseBoundedInteger = (
153
+ value: unknown,
154
+ options: BoundedIntegerOptions
155
+ ): number | typeof MISSING | null => {
156
+ if (value === undefined || value === null || value === '') return MISSING;
157
+
158
+ let parsed: number;
159
+ if (typeof value === 'number') parsed = value;
160
+ else if (typeof value === 'string' && /^[0-9]+$/.test(value.trim()))
161
+ parsed = Number(value.trim());
162
+ else return null;
163
+
164
+ if (!Number.isSafeInteger(parsed) || parsed < options.minimum || parsed > options.maximum)
165
+ return null;
166
+
167
+ return parsed;
168
+ };
169
+
170
+ const invalid = (): ShareLinkMaintenanceConfigResolution => ({
171
+ status: 'invalid',
172
+ category: SHARE_LINK_MAINTENANCE_INVALID_CATEGORY,
173
+ });
174
+
175
+ /**
176
+ * Resolve maintenance configuration from a raw record (typically `process.env`).
177
+ * Never throws and never echoes a value.
178
+ */
179
+ export const resolveShareLinkMaintenanceConfig = (
180
+ raw: unknown
181
+ ): ShareLinkMaintenanceConfigResolution => {
182
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return invalid();
183
+
184
+ const source = raw as Record<string, unknown>;
185
+ if (!hasShareLinkServiceTransportConfig(source)) return { status: 'disabled' };
186
+
187
+ const transport = resolveShareLinkServiceTransportConfig(source);
188
+ if (transport.status !== 'enabled') return invalid();
189
+
190
+ const { namespace, origin, audience, allowInsecureLoopback } = transport.config;
191
+
192
+ const claimantValue = source[SHARE_LINK_MAINTENANCE_ENV.CLAIMANT];
193
+ const claimant =
194
+ claimantValue === undefined || claimantValue === ''
195
+ ? SHARE_LINK_MAINTENANCE_DEFAULTS.claimant
196
+ : claimantValue;
197
+ if (!isOpaqueIdentifier(claimant)) return invalid();
198
+
199
+ const numericFields: Array<[string, BoundedIntegerOptions]> = [
200
+ [
201
+ SHARE_LINK_MAINTENANCE_ENV.RECOVERY_LIMIT,
202
+ {
203
+ minimum: 1,
204
+ maximum: SHARE_LINK_RECOVERY_MAX_BATCH,
205
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.recoveryLimit,
206
+ },
207
+ ],
208
+ [
209
+ SHARE_LINK_MAINTENANCE_ENV.CLEANUP_LIMIT,
210
+ {
211
+ minimum: 1,
212
+ maximum: SHARE_LINK_CLEANUP_MAX_BATCH,
213
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.cleanupLimit,
214
+ },
215
+ ],
216
+ [
217
+ SHARE_LINK_MAINTENANCE_ENV.RECEIPT_PRUNE_LIMIT,
218
+ {
219
+ minimum: 1,
220
+ maximum: 1_000,
221
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.receiptPruneLimit,
222
+ },
223
+ ],
224
+ [
225
+ SHARE_LINK_MAINTENANCE_ENV.LEASE_MS,
226
+ {
227
+ minimum: 1_000,
228
+ maximum: 30 * 60 * 1000,
229
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.leaseMs,
230
+ },
231
+ ],
232
+ [
233
+ SHARE_LINK_MAINTENANCE_ENV.CLAIM_MS,
234
+ {
235
+ minimum: SHARE_LINK_CLEANUP_MIN_CLAIM_MS,
236
+ maximum: SHARE_LINK_CLEANUP_MAX_CLAIM_MS,
237
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.claimMs,
238
+ },
239
+ ],
240
+ [
241
+ SHARE_LINK_MAINTENANCE_ENV.BUDGET_MS,
242
+ {
243
+ minimum: 1_000,
244
+ maximum: 240_000,
245
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.budgetMs,
246
+ },
247
+ ],
248
+ [
249
+ SHARE_LINK_MAINTENANCE_ENV.INTERVAL_MS,
250
+ {
251
+ minimum: 1_000,
252
+ maximum: 60 * 60 * 1000,
253
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.intervalMs,
254
+ },
255
+ ],
256
+ [
257
+ SHARE_LINK_MAINTENANCE_ENV.REQUEST_TIMEOUT_MS,
258
+ {
259
+ minimum: 100,
260
+ maximum: 30_000,
261
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.requestTimeoutMs,
262
+ },
263
+ ],
264
+ [
265
+ SHARE_LINK_MAINTENANCE_ENV.FINALIZE_RESERVE_MS,
266
+ {
267
+ minimum: 100,
268
+ maximum: 120_000,
269
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.finalizeReserveMs,
270
+ },
271
+ ],
272
+ [
273
+ SHARE_LINK_MAINTENANCE_ENV.GRAPH_TIMEOUT_MS,
274
+ {
275
+ minimum: 100,
276
+ maximum: 120_000,
277
+ fallback: SHARE_LINK_MAINTENANCE_DEFAULTS.graphTimeoutMs,
278
+ },
279
+ ],
280
+ ];
281
+
282
+ const resolved: Record<string, number> = {};
283
+ for (const [key, options] of numericFields) {
284
+ const parsed = parseBoundedInteger(source[key], options);
285
+ if (parsed === null) return invalid();
286
+ resolved[key] = parsed === MISSING ? options.fallback : parsed;
287
+ }
288
+
289
+ const budgetMs = resolved[SHARE_LINK_MAINTENANCE_ENV.BUDGET_MS] as number;
290
+ const finalizeReserveMs = resolved[SHARE_LINK_MAINTENANCE_ENV.FINALIZE_RESERVE_MS] as number;
291
+ if (finalizeReserveMs >= budgetMs) return invalid();
292
+
293
+ const config: ShareLinkMaintenanceConfig = {
294
+ namespace,
295
+ origin,
296
+ audience,
297
+ allowInsecureLoopback,
298
+ claimant,
299
+ recoveryLimit: resolved[SHARE_LINK_MAINTENANCE_ENV.RECOVERY_LIMIT] as number,
300
+ cleanupLimit: resolved[SHARE_LINK_MAINTENANCE_ENV.CLEANUP_LIMIT] as number,
301
+ receiptPruneLimit: resolved[SHARE_LINK_MAINTENANCE_ENV.RECEIPT_PRUNE_LIMIT] as number,
302
+ leaseMs: resolved[SHARE_LINK_MAINTENANCE_ENV.LEASE_MS] as number,
303
+ claimMs: resolved[SHARE_LINK_MAINTENANCE_ENV.CLAIM_MS] as number,
304
+ budgetMs,
305
+ intervalMs: resolved[SHARE_LINK_MAINTENANCE_ENV.INTERVAL_MS] as number,
306
+ requestTimeoutMs: resolved[SHARE_LINK_MAINTENANCE_ENV.REQUEST_TIMEOUT_MS] as number,
307
+ finalizeReserveMs,
308
+ graphTimeoutMs: resolved[SHARE_LINK_MAINTENANCE_ENV.GRAPH_TIMEOUT_MS] as number,
309
+ };
310
+
311
+ return { status: 'enabled', config };
312
+ };
@@ -0,0 +1,24 @@
1
+ export * from './types';
2
+ export {
3
+ SHARE_LINK_MAINTENANCE_DEFAULTS,
4
+ SHARE_LINK_MAINTENANCE_ENV,
5
+ SHARE_LINK_MAINTENANCE_INVALID_CATEGORY,
6
+ resolveShareLinkMaintenanceConfig,
7
+ } from './config';
8
+ export { createMaintenancePassBudget, type MaintenancePassBudget } from './budget';
9
+ export {
10
+ createConsoleMaintenanceLogger,
11
+ safeLogMaintenance,
12
+ toMaintenanceLogEvent,
13
+ } from './telemetry';
14
+ export { runShareLinkMaintenancePass, type ShareLinkMaintenanceRunnerDependencies } from './runner';
15
+ export {
16
+ createShareLinkMaintenanceScheduler,
17
+ type ShareLinkMaintenanceScheduler,
18
+ type ShareLinkMaintenanceSchedulerDependencies,
19
+ } from './scheduler';
20
+ export {
21
+ createShareLinkMaintenanceRuntime,
22
+ type CreateShareLinkMaintenanceRuntimeOptions,
23
+ type ShareLinkMaintenanceRuntime,
24
+ } from './runtime';
@@ -0,0 +1,223 @@
1
+ import type {
2
+ CleanupRunnerDependencies,
3
+ RecoveryRunnerDependencies,
4
+ } from '../share-link-coordinator';
5
+ import { runShareContentCleanupOnce } from '../share-link-coordinator/cleanup-runner';
6
+ import { budgetAllows } from '../share-link-coordinator/budget-helpers';
7
+ import { runShareLinkRecoveryOnce } from '../share-link-coordinator/recovery-runner';
8
+ import type { ShareContentClient } from '../share-content-client/types';
9
+ import type { PruneShareViewReceiptsInput } from '../../accesslayer/share-link/types';
10
+ import {
11
+ createMaintenancePassBudget,
12
+ resolveMaintenanceBudgetMs,
13
+ type MaintenancePassBudget,
14
+ } from './budget';
15
+ import { safeLogMaintenance } from './telemetry';
16
+ import type {
17
+ ShareLinkMaintenanceConfig,
18
+ ShareLinkMaintenanceFailureCategory,
19
+ ShareLinkMaintenanceLogger,
20
+ ShareLinkMaintenanceRunSummary,
21
+ } from './types';
22
+
23
+ export type ShareLinkMaintenanceRunnerDependencies = {
24
+ config: ShareLinkMaintenanceConfig;
25
+ recoveryRepository: RecoveryRunnerDependencies['repository'];
26
+ cleanupRepository: CleanupRunnerDependencies['repository'];
27
+ /**
28
+ * Namespace-scoped, bounded receipt prune. Injected so DB-free unit tests
29
+ * never load the Neo4j `@instance`; production passes the real repository.
30
+ */
31
+ pruneReceipts: (input: PruneShareViewReceiptsInput) => Promise<number>;
32
+ client: Pick<ShareContentClient, 'stat' | 'delete'>;
33
+ logger: ShareLinkMaintenanceLogger;
34
+ /** Monotonic clock for the cooperative budget; injectable for tests. */
35
+ monotonicNow?: () => number;
36
+ /** Wall clock passed through to the repositories; production omits it. */
37
+ now?: () => Date;
38
+ /**
39
+ * Optional Lambda remaining-time source, in milliseconds. Ignored when a
40
+ * pre-started `budget` is supplied.
41
+ */
42
+ remainingTimeMs?: () => number;
43
+ /**
44
+ * Optional budget already started by the runtime BEFORE dependency
45
+ * initialization. Reused so a slow cold start consumes the invocation
46
+ * deadline rather than receiving a fresh post-setup budget.
47
+ */
48
+ budget?: MaintenancePassBudget;
49
+ };
50
+
51
+ /**
52
+ * Bounded graph cost of the very first recovery stage (discovery read). If this
53
+ * does not fit, neither recovery nor cleanup can start.
54
+ */
55
+ const recoveryDiscoveryCostMs = (config: ShareLinkMaintenanceConfig): number =>
56
+ config.graphTimeoutMs;
57
+
58
+ /**
59
+ * Worst-case bounded cost of one whole cleanup unit:
60
+ * claim (graph) + delete (HTTP) + complete (graph).
61
+ */
62
+ const cleanupUnitCostMs = (config: ShareLinkMaintenanceConfig): number =>
63
+ config.requestTimeoutMs + 2 * config.graphTimeoutMs;
64
+
65
+ /**
66
+ * Bounded cost of one namespace-scoped receipt-prune read transaction.
67
+ */
68
+ const receiptPruneCostMs = (config: ShareLinkMaintenanceConfig): number => config.graphTimeoutMs;
69
+
70
+ /**
71
+ * One bounded maintenance invocation: namespace-scoped recovery followed by
72
+ * namespace-scoped cleanup, each with its own count limit and a shared elapsed
73
+ * deadline.
74
+ *
75
+ * The deadline is cooperative (see `createMaintenancePassBudget`): it is checked
76
+ * before every claim, never enforced with `Promise.race`, so a remote delete or
77
+ * graph mutation that has already started is always awaited to a tracked result.
78
+ * Admission requires the remaining time to cover the whole next unit — the
79
+ * claim, the bounded HTTP call and the finalizing graph write — plus the
80
+ * finalization/telemetry reserve. The HTTP allowance is fixed when the transport
81
+ * client is built, so it cannot be shrunk per call; graph transaction timeouts
82
+ * are dynamically capped to the time actually left.
83
+ *
84
+ * Every failure is folded into a fixed category; the summary exposes aggregate
85
+ * counts and durations only.
86
+ */
87
+ export const runShareLinkMaintenancePass = async (
88
+ dependencies: ShareLinkMaintenanceRunnerDependencies
89
+ ): Promise<ShareLinkMaintenanceRunSummary> => {
90
+ const { config, logger } = dependencies;
91
+
92
+ const budget =
93
+ dependencies.budget ??
94
+ createMaintenancePassBudget({
95
+ budgetMs: resolveMaintenanceBudgetMs(config.budgetMs, dependencies.remainingTimeMs?.()),
96
+ finalizeReserveMs: config.finalizeReserveMs,
97
+ monotonicNow: dependencies.monotonicNow,
98
+ });
99
+
100
+ const categories: Partial<Record<ShareLinkMaintenanceFailureCategory, number>> = {};
101
+
102
+ if (budget.exhausted()) {
103
+ const summary: ShareLinkMaintenanceRunSummary = {
104
+ status: 'deadline_exhausted',
105
+ durationMs: budget.elapsedMs(),
106
+ budgetMs: budget.configuredMs,
107
+ recovery: null,
108
+ cleanup: null,
109
+ receiptPrune: null,
110
+ categories,
111
+ };
112
+
113
+ safeLogMaintenance(logger, summary);
114
+
115
+ return summary;
116
+ }
117
+
118
+ let recovery: ShareLinkMaintenanceRunSummary['recovery'] = null;
119
+ let cleanup: ShareLinkMaintenanceRunSummary['cleanup'] = null;
120
+ let receiptPrune: ShareLinkMaintenanceRunSummary['receiptPrune'] = null;
121
+ let recoveryAttempted = false;
122
+ let cleanupAttempted = false;
123
+ let receiptPruneAttempted = false;
124
+ let budgetStoppedRecovery = false;
125
+ let budgetStoppedCleanup = false;
126
+ let budgetStoppedReceiptPrune = false;
127
+
128
+ if (budgetAllows(budget, recoveryDiscoveryCostMs(config))) {
129
+ recoveryAttempted = true;
130
+
131
+ try {
132
+ recovery = await runShareLinkRecoveryOnce({
133
+ repository: dependencies.recoveryRepository,
134
+ client: dependencies.client,
135
+ claimant: config.claimant,
136
+ namespace: config.namespace,
137
+ limit: config.recoveryLimit,
138
+ leaseMs: config.leaseMs,
139
+ budget,
140
+ transactionTimeoutMs: config.graphTimeoutMs,
141
+ requestTimeoutMs: config.requestTimeoutMs,
142
+ noInlineRetry: true,
143
+ now: dependencies.now,
144
+ });
145
+ } catch {
146
+ categories.share_link_maintenance_recovery_failed = 1;
147
+ }
148
+ } else {
149
+ budgetStoppedRecovery = true;
150
+ }
151
+
152
+ if (budgetAllows(budget, cleanupUnitCostMs(config))) {
153
+ cleanupAttempted = true;
154
+
155
+ try {
156
+ cleanup = await runShareContentCleanupOnce({
157
+ repository: dependencies.cleanupRepository,
158
+ client: dependencies.client,
159
+ claimant: config.claimant,
160
+ namespace: config.namespace,
161
+ limit: config.cleanupLimit,
162
+ claimMs: config.claimMs,
163
+ // One job per claim so a lapsed deadline can never strand a batch.
164
+ claimBatchSize: 1,
165
+ transactionTimeoutMs: config.graphTimeoutMs,
166
+ requestTimeoutMs: config.requestTimeoutMs,
167
+ noInlineRetry: true,
168
+ budget,
169
+ now: dependencies.now,
170
+ });
171
+ } catch {
172
+ categories.share_link_maintenance_cleanup_failed = 1;
173
+ }
174
+ } else {
175
+ budgetStoppedCleanup = true;
176
+ }
177
+
178
+ if (budgetAllows(budget, receiptPruneCostMs(config))) {
179
+ receiptPruneAttempted = true;
180
+
181
+ try {
182
+ const pruned = await dependencies.pruneReceipts({
183
+ namespace: config.namespace,
184
+ limit: config.receiptPruneLimit,
185
+ now: dependencies.now?.(),
186
+ // One bounded explicit read transaction, no inline retry: the
187
+ // scheduler cadence owns retry.
188
+ transaction: { timeoutMs: config.graphTimeoutMs, noInlineRetry: true },
189
+ });
190
+ receiptPrune = { pruned };
191
+ } catch {
192
+ categories.share_link_maintenance_receipt_prune_failed = 1;
193
+ }
194
+ } else {
195
+ budgetStoppedReceiptPrune = true;
196
+ }
197
+
198
+ const failed =
199
+ (recoveryAttempted && recovery === null) ||
200
+ (cleanupAttempted && cleanup === null) ||
201
+ (receiptPruneAttempted && receiptPrune === null);
202
+ const deadlineStopped =
203
+ budgetStoppedRecovery || budgetStoppedCleanup || budgetStoppedReceiptPrune;
204
+ const status = failed
205
+ ? 'failed'
206
+ : budget.exhausted() || deadlineStopped
207
+ ? 'deadline_exhausted'
208
+ : 'completed';
209
+
210
+ const summary: ShareLinkMaintenanceRunSummary = {
211
+ status,
212
+ durationMs: budget.elapsedMs(),
213
+ budgetMs: budget.configuredMs,
214
+ recovery,
215
+ cleanup,
216
+ receiptPrune,
217
+ categories,
218
+ };
219
+
220
+ safeLogMaintenance(logger, summary);
221
+
222
+ return summary;
223
+ };