@learncard/network-brain-service 3.19.0 → 4.0.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 (507) hide show
  1. package/.env.example +47 -82
  2. package/AGENTS.md +52 -0
  3. package/CHANGELOG.md +147 -0
  4. package/README.md +51 -2
  5. package/bootstrapLambda.test.ts +72 -0
  6. package/config/config.dev.json +29 -0
  7. package/config/config.example.json +29 -0
  8. package/config/config.json +1 -0
  9. package/config/config.production.json +29 -0
  10. package/config/config.scouts.dev.json +24 -0
  11. package/config/config.scouts.production.json +24 -0
  12. package/contractEventsLambda.ts +18 -0
  13. package/contractEventsLambdaApp.ts +19 -0
  14. package/didWebLambda.ts +13 -18
  15. package/didWebLambdaApp.ts +22 -0
  16. package/dist/accesslayer/app-manifest-version/create.d.ts +10 -0
  17. package/dist/accesslayer/app-manifest-version/create.d.ts.map +1 -0
  18. package/dist/accesslayer/app-manifest-version/read.d.ts +11 -0
  19. package/dist/accesslayer/app-manifest-version/read.d.ts.map +1 -0
  20. package/dist/accesslayer/app-manifest-version/update.d.ts +7 -0
  21. package/dist/accesslayer/app-manifest-version/update.d.ts.map +1 -0
  22. package/dist/accesslayer/app-store-listing/read.d.ts +2 -1
  23. package/dist/accesslayer/app-store-listing/read.d.ts.map +1 -1
  24. package/dist/accesslayer/app-store-listing/relationships/create.d.ts +1 -0
  25. package/dist/accesslayer/app-store-listing/relationships/create.d.ts.map +1 -1
  26. package/dist/accesslayer/app-store-listing/relationships/read.d.ts +2 -0
  27. package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
  28. package/dist/accesslayer/app-store-listing/update.d.ts +1 -0
  29. package/dist/accesslayer/app-store-listing/update.d.ts.map +1 -1
  30. package/dist/accesslayer/boost/relationships/create.d.ts +2 -1
  31. package/dist/accesslayer/boost/relationships/create.d.ts.map +1 -1
  32. package/dist/accesslayer/consentflowcontract/create.d.ts +9 -1
  33. package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
  34. package/dist/accesslayer/consentflowcontract/read.d.ts +34 -3
  35. package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
  36. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +4 -1
  37. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  38. package/dist/accesslayer/consentflowcontract/relationships/delete.d.ts.map +1 -1
  39. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +3 -2
  40. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  41. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts +12 -0
  42. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts.map +1 -0
  43. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts +12 -0
  44. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts.map +1 -0
  45. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +8 -5
  46. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  47. package/dist/accesslayer/credential/create.d.ts +2 -1
  48. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  49. package/dist/accesslayer/credential/relationships/create.d.ts +4 -3
  50. package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
  51. package/dist/accesslayer/inbox-credential/relationships/create.d.ts +2 -1
  52. package/dist/accesslayer/inbox-credential/relationships/create.d.ts.map +1 -1
  53. package/dist/accesslayer/inbox-credential/update.d.ts +2 -1
  54. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  55. package/dist/accesslayer/presentation/read.d.ts.map +1 -1
  56. package/dist/accesslayer/presentation/relationships/create.d.ts +2 -2
  57. package/dist/accesslayer/presentation/relationships/create.d.ts.map +1 -1
  58. package/dist/accesslayer/presentation/relationships/read.d.ts.map +1 -1
  59. package/dist/accesslayer/share-link/abandon.d.ts +3 -0
  60. package/dist/accesslayer/share-link/abandon.d.ts.map +1 -0
  61. package/dist/accesslayer/share-link/attachment.d.ts +37 -0
  62. package/dist/accesslayer/share-link/attachment.d.ts.map +1 -0
  63. package/dist/accesslayer/share-link/cleanup.d.ts +6 -0
  64. package/dist/accesslayer/share-link/cleanup.d.ts.map +1 -0
  65. package/dist/accesslayer/share-link/errors.d.ts +8 -0
  66. package/dist/accesslayer/share-link/errors.d.ts.map +1 -0
  67. package/dist/accesslayer/share-link/finalize.d.ts +3 -0
  68. package/dist/accesslayer/share-link/finalize.d.ts.map +1 -0
  69. package/dist/accesslayer/share-link/helpers.d.ts +59 -0
  70. package/dist/accesslayer/share-link/helpers.d.ts.map +1 -0
  71. package/dist/accesslayer/share-link/index.d.ts +12 -0
  72. package/dist/accesslayer/share-link/index.d.ts.map +1 -0
  73. package/dist/accesslayer/share-link/list.d.ts +5 -0
  74. package/dist/accesslayer/share-link/list.d.ts.map +1 -0
  75. package/dist/accesslayer/share-link/read.d.ts +5 -0
  76. package/dist/accesslayer/share-link/read.d.ts.map +1 -0
  77. package/dist/accesslayer/share-link/receipt.d.ts +11 -0
  78. package/dist/accesslayer/share-link/receipt.d.ts.map +1 -0
  79. package/dist/accesslayer/share-link/recovery.d.ts +6 -0
  80. package/dist/accesslayer/share-link/recovery.d.ts.map +1 -0
  81. package/dist/accesslayer/share-link/reserve.d.ts +4 -0
  82. package/dist/accesslayer/share-link/reserve.d.ts.map +1 -0
  83. package/dist/accesslayer/share-link/revoke.d.ts +3 -0
  84. package/dist/accesslayer/share-link/revoke.d.ts.map +1 -0
  85. package/dist/accesslayer/share-link/transaction-execution.d.ts +18 -0
  86. package/dist/accesslayer/share-link/transaction-execution.d.ts.map +1 -0
  87. package/dist/accesslayer/share-link/transaction.d.ts +15 -0
  88. package/dist/accesslayer/share-link/transaction.d.ts.map +1 -0
  89. package/dist/accesslayer/share-link/types.d.ts +375 -0
  90. package/dist/accesslayer/share-link/types.d.ts.map +1 -0
  91. package/dist/app.d.ts +4 -0
  92. package/dist/app.d.ts.map +1 -1
  93. package/dist/cache/app-store.caches.d.ts +49 -0
  94. package/dist/cache/app-store.caches.d.ts.map +1 -1
  95. package/dist/config/applyDockerStageConfig.d.ts +2 -0
  96. package/dist/config/applyDockerStageConfig.d.ts.map +1 -0
  97. package/dist/config/environment.d.ts +14 -0
  98. package/dist/config/environment.d.ts.map +1 -1
  99. package/dist/config/serverlessFunctionEnv.test.d.ts +2 -0
  100. package/dist/config/serverlessFunctionEnv.test.d.ts.map +1 -0
  101. package/dist/config/stageConfig.d.ts +112 -0
  102. package/dist/config/stageConfig.d.ts.map +1 -0
  103. package/dist/config/stageConfig.test.d.ts +2 -0
  104. package/dist/config/stageConfig.test.d.ts.map +1 -0
  105. package/dist/constants/auth-grant.d.ts +2 -0
  106. package/dist/constants/auth-grant.d.ts.map +1 -1
  107. package/dist/didkit_wasm_bg.wasm +0 -0
  108. package/dist/docker-entry.d.ts +1 -1
  109. package/dist/docker-entry.d.ts.map +1 -1
  110. package/dist/helpers/app-manifest.helpers.d.ts +5 -0
  111. package/dist/helpers/app-manifest.helpers.d.ts.map +1 -0
  112. package/dist/helpers/boost.helpers.d.ts.map +1 -1
  113. package/dist/helpers/claim-hooks.helpers.d.ts +2 -1
  114. package/dist/helpers/claim-hooks.helpers.d.ts.map +1 -1
  115. package/dist/helpers/consent-referral.helpers.d.ts +2 -0
  116. package/dist/helpers/consent-referral.helpers.d.ts.map +1 -0
  117. package/dist/helpers/consent-scopes.helpers.d.ts +7 -0
  118. package/dist/helpers/consent-scopes.helpers.d.ts.map +1 -0
  119. package/dist/helpers/contract-event-maintenance.helpers.d.ts +3 -0
  120. package/dist/helpers/contract-event-maintenance.helpers.d.ts.map +1 -0
  121. package/dist/helpers/contract-events.helpers.d.ts +25 -0
  122. package/dist/helpers/contract-events.helpers.d.ts.map +1 -0
  123. package/dist/helpers/contract-requests.helpers.d.ts +19 -0
  124. package/dist/helpers/contract-requests.helpers.d.ts.map +1 -0
  125. package/dist/helpers/credentialSubject.helpers.d.ts +3 -0
  126. package/dist/helpers/credentialSubject.helpers.d.ts.map +1 -0
  127. package/dist/helpers/credentialSubject.helpers.test.d.ts +2 -0
  128. package/dist/helpers/credentialSubject.helpers.test.d.ts.map +1 -0
  129. package/dist/helpers/finalize-inbox.helpers.d.ts +3 -3
  130. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  131. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  132. package/dist/helpers/inline-template.helpers.d.ts +45 -0
  133. package/dist/helpers/inline-template.helpers.d.ts.map +1 -0
  134. package/dist/helpers/notificationMessages.d.ts +1 -1
  135. package/dist/helpers/notificationMessages.d.ts.map +1 -1
  136. package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
  137. package/dist/helpers/notifications.helpers.d.ts +6 -0
  138. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  139. package/dist/helpers/presentation-metadata.d.ts +4 -0
  140. package/dist/helpers/presentation-metadata.d.ts.map +1 -0
  141. package/dist/helpers/presentation.helpers.d.ts +1 -1
  142. package/dist/helpers/presentation.helpers.d.ts.map +1 -1
  143. package/dist/helpers/profile.helpers.d.ts +2 -0
  144. package/dist/helpers/profile.helpers.d.ts.map +1 -1
  145. package/dist/helpers/share-content-client/adapters.d.ts +10 -0
  146. package/dist/helpers/share-content-client/adapters.d.ts.map +1 -0
  147. package/dist/helpers/share-content-client/canonical.d.ts +7 -0
  148. package/dist/helpers/share-content-client/canonical.d.ts.map +1 -0
  149. package/dist/helpers/share-content-client/client.d.ts +5 -0
  150. package/dist/helpers/share-content-client/client.d.ts.map +1 -0
  151. package/dist/helpers/share-content-client/config.d.ts +11 -0
  152. package/dist/helpers/share-content-client/config.d.ts.map +1 -0
  153. package/dist/helpers/share-content-client/index.d.ts +6 -0
  154. package/dist/helpers/share-content-client/index.d.ts.map +1 -0
  155. package/dist/helpers/share-content-client/responses.d.ts +16 -0
  156. package/dist/helpers/share-content-client/responses.d.ts.map +1 -0
  157. package/dist/helpers/share-content-client/types.d.ts +145 -0
  158. package/dist/helpers/share-content-client/types.d.ts.map +1 -0
  159. package/dist/helpers/share-link-attachment-access.d.ts +4 -0
  160. package/dist/helpers/share-link-attachment-access.d.ts.map +1 -0
  161. package/dist/helpers/share-link-attachment.d.ts +18 -0
  162. package/dist/helpers/share-link-attachment.d.ts.map +1 -0
  163. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts +5 -0
  164. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts.map +1 -0
  165. package/dist/helpers/share-link-coordinator/budget-helpers.d.ts +5 -0
  166. package/dist/helpers/share-link-coordinator/budget-helpers.d.ts.map +1 -0
  167. package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts +3 -0
  168. package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts.map +1 -0
  169. package/dist/helpers/share-link-coordinator/coordinator.d.ts +4 -0
  170. package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -0
  171. package/dist/helpers/share-link-coordinator/index.d.ts +5 -0
  172. package/dist/helpers/share-link-coordinator/index.d.ts.map +1 -0
  173. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts +9 -0
  174. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -0
  175. package/dist/helpers/share-link-coordinator/runtime.d.ts +11 -0
  176. package/dist/helpers/share-link-coordinator/runtime.d.ts.map +1 -0
  177. package/dist/helpers/share-link-coordinator/stat.d.ts +4 -0
  178. package/dist/helpers/share-link-coordinator/stat.d.ts.map +1 -0
  179. package/dist/helpers/share-link-coordinator/types.d.ts +160 -0
  180. package/dist/helpers/share-link-coordinator/types.d.ts.map +1 -0
  181. package/dist/helpers/share-link-lifecycle/index.d.ts +5 -0
  182. package/dist/helpers/share-link-lifecycle/index.d.ts.map +1 -0
  183. package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts +4 -0
  184. package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts.map +1 -0
  185. package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts +21 -0
  186. package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts.map +1 -0
  187. package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts +9 -0
  188. package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts.map +1 -0
  189. package/dist/helpers/share-link-lifecycle/types.d.ts +14 -0
  190. package/dist/helpers/share-link-lifecycle/types.d.ts.map +1 -0
  191. package/dist/helpers/share-link-maintenance/budget.d.ts +12 -0
  192. package/dist/helpers/share-link-maintenance/budget.d.ts.map +1 -0
  193. package/dist/helpers/share-link-maintenance/config.d.ts +48 -0
  194. package/dist/helpers/share-link-maintenance/config.d.ts.map +1 -0
  195. package/dist/helpers/share-link-maintenance/index.d.ts +8 -0
  196. package/dist/helpers/share-link-maintenance/index.d.ts.map +1 -0
  197. package/dist/helpers/share-link-maintenance/runner.d.ts +20 -0
  198. package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -0
  199. package/dist/helpers/share-link-maintenance/runtime.d.ts +38 -0
  200. package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -0
  201. package/dist/helpers/share-link-maintenance/scheduler.d.ts +18 -0
  202. package/dist/helpers/share-link-maintenance/scheduler.d.ts.map +1 -0
  203. package/dist/helpers/share-link-maintenance/telemetry.d.ts +5 -0
  204. package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -0
  205. package/dist/helpers/share-link-maintenance/types.d.ts +56 -0
  206. package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -0
  207. package/dist/helpers/share-link-owner/config.d.ts +16 -0
  208. package/dist/helpers/share-link-owner/config.d.ts.map +1 -0
  209. package/dist/helpers/share-link-owner/diagnostics.d.ts +26 -0
  210. package/dist/helpers/share-link-owner/diagnostics.d.ts.map +1 -0
  211. package/dist/helpers/share-link-owner/lazy-initializer.d.ts +2 -0
  212. package/dist/helpers/share-link-owner/lazy-initializer.d.ts.map +1 -0
  213. package/dist/helpers/share-link-owner-projection.d.ts +4 -0
  214. package/dist/helpers/share-link-owner-projection.d.ts.map +1 -0
  215. package/dist/helpers/share-link-passcode-abuse.d.ts +2 -0
  216. package/dist/helpers/share-link-passcode-abuse.d.ts.map +1 -0
  217. package/dist/helpers/share-link-passcode.d.ts +6 -0
  218. package/dist/helpers/share-link-passcode.d.ts.map +1 -0
  219. package/dist/helpers/share-link-policy/index.d.ts +4 -0
  220. package/dist/helpers/share-link-policy/index.d.ts.map +1 -0
  221. package/dist/helpers/share-link-policy/production.d.ts +11 -0
  222. package/dist/helpers/share-link-policy/production.d.ts.map +1 -0
  223. package/dist/helpers/share-link-policy/resolver.d.ts +6 -0
  224. package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -0
  225. package/dist/helpers/share-link-policy/types.d.ts +22 -0
  226. package/dist/helpers/share-link-policy/types.d.ts.map +1 -0
  227. package/dist/helpers/share-link-public/config.d.ts +13 -0
  228. package/dist/helpers/share-link-public/config.d.ts.map +1 -0
  229. package/dist/helpers/share-link-view-notification.d.ts +2 -0
  230. package/dist/helpers/share-link-view-notification.d.ts.map +1 -0
  231. package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
  232. package/dist/helpers/smartResume.helpers.d.ts +3 -0
  233. package/dist/helpers/smartResume.helpers.d.ts.map +1 -0
  234. package/dist/index.js +2 -2
  235. package/dist/models/AppManifestVersion.d.ts +20 -0
  236. package/dist/models/AppManifestVersion.d.ts.map +1 -0
  237. package/dist/models/AppStoreListing.d.ts +13 -0
  238. package/dist/models/AppStoreListing.d.ts.map +1 -1
  239. package/dist/models/ConsentFlowContract.d.ts +13 -2
  240. package/dist/models/ConsentFlowContract.d.ts.map +1 -1
  241. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  242. package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
  243. package/dist/models/Integration.d.ts +2 -0
  244. package/dist/models/Integration.d.ts.map +1 -1
  245. package/dist/models/Presentation.d.ts +2 -0
  246. package/dist/models/Presentation.d.ts.map +1 -1
  247. package/dist/models/Profile.d.ts +2 -0
  248. package/dist/models/Profile.d.ts.map +1 -1
  249. package/dist/models/ShareLink.d.ts +46 -0
  250. package/dist/models/ShareLink.d.ts.map +1 -0
  251. package/dist/models/ShareViewReceipt.d.ts +22 -0
  252. package/dist/models/ShareViewReceipt.d.ts.map +1 -0
  253. package/dist/models/index.d.ts +2 -0
  254. package/dist/models/index.d.ts.map +1 -1
  255. package/dist/models/share-link-constraints.d.ts +3 -0
  256. package/dist/models/share-link-constraints.d.ts.map +1 -0
  257. package/dist/openapi.d.ts +1 -1
  258. package/dist/openapi.d.ts.map +1 -1
  259. package/dist/public-share-link-http.d.ts +4 -0
  260. package/dist/public-share-link-http.d.ts.map +1 -0
  261. package/dist/routes/app-store.d.ts +619 -0
  262. package/dist/routes/app-store.d.ts.map +1 -1
  263. package/dist/routes/boosts.d.ts +2 -2
  264. package/dist/routes/boosts.d.ts.map +1 -1
  265. package/dist/routes/contracts.d.ts +148 -5
  266. package/dist/routes/contracts.d.ts.map +1 -1
  267. package/dist/routes/credentials.d.ts +35 -1
  268. package/dist/routes/credentials.d.ts.map +1 -1
  269. package/dist/routes/inbox.d.ts +52 -2
  270. package/dist/routes/inbox.d.ts.map +1 -1
  271. package/dist/routes/index.d.ts +153 -0
  272. package/dist/routes/index.d.ts.map +1 -1
  273. package/dist/routes/presentations.d.ts +1 -0
  274. package/dist/routes/presentations.d.ts.map +1 -1
  275. package/dist/routes/profile-manager.d.ts.map +1 -1
  276. package/dist/routes/profiles.d.ts.map +1 -1
  277. package/dist/routes/public-share-links.d.ts +285 -0
  278. package/dist/routes/public-share-links.d.ts.map +1 -0
  279. package/dist/routes/share-links.d.ts +1005 -0
  280. package/dist/routes/share-links.d.ts.map +1 -0
  281. package/dist/routes/test.d.ts +5 -0
  282. package/dist/routes/test.d.ts.map +1 -1
  283. package/dist/routes/workflows.d.ts.map +1 -1
  284. package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
  285. package/dist/types/app-manifest.d.ts +339 -0
  286. package/dist/types/app-manifest.d.ts.map +1 -0
  287. package/dist/types/app-store-listing.d.ts +34 -0
  288. package/dist/types/app-store-listing.d.ts.map +1 -1
  289. package/dist/types/consentflowcontract.d.ts +31 -4
  290. package/dist/types/consentflowcontract.d.ts.map +1 -1
  291. package/dist/types/credential-refresh.d.ts +1 -1
  292. package/esbuildPlugins.cjs +1 -0
  293. package/lambda.ts +49 -192
  294. package/lambdaApp.ts +203 -0
  295. package/package.json +25 -17
  296. package/scripts/export-openapi.ts +20 -0
  297. package/scripts/offline-schema-environment.ts +18 -0
  298. package/scripts/seed-demo-persona.ts +425 -0
  299. package/scripts/verify-python-contracts.ts +72 -0
  300. package/serverless-local.yml +3 -6
  301. package/serverless.function-env.cjs +50 -0
  302. package/serverless.local.yml +0 -1
  303. package/serverless.yml +44 -55
  304. package/shareLinkMaintenanceLambda.ts +18 -0
  305. package/shareLinkMaintenanceLambdaApp.ts +50 -0
  306. package/src/accesslayer/app-manifest-version/create.ts +57 -0
  307. package/src/accesslayer/app-manifest-version/read.ts +129 -0
  308. package/src/accesslayer/app-manifest-version/update.ts +61 -0
  309. package/src/accesslayer/app-store-listing/read.ts +6 -0
  310. package/src/accesslayer/app-store-listing/relationships/create.ts +20 -0
  311. package/src/accesslayer/app-store-listing/relationships/read.ts +24 -1
  312. package/src/accesslayer/app-store-listing/update.ts +15 -0
  313. package/src/accesslayer/boost/relationships/create.ts +8 -2
  314. package/src/accesslayer/consentflowcontract/create.ts +81 -8
  315. package/src/accesslayer/consentflowcontract/read.ts +84 -10
  316. package/src/accesslayer/consentflowcontract/relationships/create.ts +125 -109
  317. package/src/accesslayer/consentflowcontract/relationships/delete.ts +1 -0
  318. package/src/accesslayer/consentflowcontract/relationships/read.ts +184 -108
  319. package/src/accesslayer/consentflowcontract/relationships/recipients.ts +125 -0
  320. package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +125 -0
  321. package/src/accesslayer/consentflowcontract/relationships/update.ts +255 -217
  322. package/src/accesslayer/credential/create.test.ts +2 -115
  323. package/src/accesslayer/credential/create.ts +11 -6
  324. package/src/accesslayer/credential/relationships/create.ts +17 -10
  325. package/src/accesslayer/inbox-credential/relationships/create.ts +70 -48
  326. package/src/accesslayer/inbox-credential/update.ts +4 -2
  327. package/src/accesslayer/presentation/read.ts +57 -25
  328. package/src/accesslayer/presentation/relationships/create.ts +54 -11
  329. package/src/accesslayer/presentation/relationships/read.ts +21 -6
  330. package/src/accesslayer/share-link/abandon.ts +120 -0
  331. package/src/accesslayer/share-link/attachment.ts +360 -0
  332. package/src/accesslayer/share-link/cleanup.ts +271 -0
  333. package/src/accesslayer/share-link/errors.ts +32 -0
  334. package/src/accesslayer/share-link/finalize.ts +313 -0
  335. package/src/accesslayer/share-link/helpers.ts +450 -0
  336. package/src/accesslayer/share-link/index.ts +11 -0
  337. package/src/accesslayer/share-link/list.ts +162 -0
  338. package/src/accesslayer/share-link/read.ts +107 -0
  339. package/src/accesslayer/share-link/receipt.ts +548 -0
  340. package/src/accesslayer/share-link/recovery.ts +565 -0
  341. package/src/accesslayer/share-link/reserve.ts +763 -0
  342. package/src/accesslayer/share-link/revoke.ts +232 -0
  343. package/src/accesslayer/share-link/transaction-execution.ts +72 -0
  344. package/src/accesslayer/share-link/transaction.ts +126 -0
  345. package/src/accesslayer/share-link/types.ts +606 -0
  346. package/src/app.ts +6 -0
  347. package/src/cache/app-store.caches.ts +35 -0
  348. package/src/config/applyDockerStageConfig.ts +6 -0
  349. package/src/config/environment.test.ts +17 -12
  350. package/src/config/environment.ts +15 -0
  351. package/src/config/serverlessFunctionEnv.test.ts +105 -0
  352. package/src/config/stageConfig.test.ts +25 -0
  353. package/src/config/stageConfig.ts +24 -0
  354. package/src/constants/auth-grant.ts +5 -0
  355. package/src/docker-entry.ts +81 -0
  356. package/src/helpers/app-manifest.helpers.ts +185 -0
  357. package/src/helpers/boost.helpers.ts +4 -31
  358. package/src/helpers/claim-hooks.helpers.ts +20 -7
  359. package/src/helpers/consent-referral.helpers.ts +6 -0
  360. package/src/helpers/consent-scopes.helpers.ts +58 -0
  361. package/src/helpers/contract-event-maintenance.helpers.ts +43 -0
  362. package/src/helpers/contract-events.helpers.ts +540 -0
  363. package/src/helpers/contract-requests.helpers.ts +211 -0
  364. package/src/helpers/credentialSubject.helpers.test.ts +114 -0
  365. package/src/helpers/credentialSubject.helpers.ts +40 -0
  366. package/src/helpers/finalize-inbox.helpers.ts +163 -44
  367. package/src/helpers/inbox.helpers.ts +58 -70
  368. package/src/helpers/inline-template.helpers.ts +560 -0
  369. package/src/helpers/notificationMessages.ts +77 -1
  370. package/src/helpers/notificationQueue.helpers.ts +23 -5
  371. package/src/helpers/notifications.helpers.ts +25 -1
  372. package/src/helpers/presentation-metadata.ts +41 -0
  373. package/src/helpers/presentation.helpers.ts +27 -20
  374. package/src/helpers/profile.helpers.ts +13 -0
  375. package/src/helpers/share-content-client/adapters.ts +55 -0
  376. package/src/helpers/share-content-client/canonical.ts +25 -0
  377. package/src/helpers/share-content-client/client.ts +440 -0
  378. package/src/helpers/share-content-client/config.ts +189 -0
  379. package/src/helpers/share-content-client/index.ts +27 -0
  380. package/src/helpers/share-content-client/responses.ts +273 -0
  381. package/src/helpers/share-content-client/types.ts +253 -0
  382. package/src/helpers/share-link-attachment-access.ts +87 -0
  383. package/src/helpers/share-link-attachment.ts +115 -0
  384. package/src/helpers/share-link-coordinator/attachment-stat.ts +41 -0
  385. package/src/helpers/share-link-coordinator/budget-helpers.ts +53 -0
  386. package/src/helpers/share-link-coordinator/cleanup-runner.ts +286 -0
  387. package/src/helpers/share-link-coordinator/coordinator.ts +727 -0
  388. package/src/helpers/share-link-coordinator/index.ts +8 -0
  389. package/src/helpers/share-link-coordinator/recovery-runner.ts +443 -0
  390. package/src/helpers/share-link-coordinator/runtime.ts +88 -0
  391. package/src/helpers/share-link-coordinator/stat.ts +30 -0
  392. package/src/helpers/share-link-coordinator/types.ts +353 -0
  393. package/src/helpers/share-link-lifecycle/index.ts +4 -0
  394. package/src/helpers/share-link-lifecycle/lease.helpers.ts +35 -0
  395. package/src/helpers/share-link-lifecycle/recovery.helpers.ts +173 -0
  396. package/src/helpers/share-link-lifecycle/request-hash.helpers.ts +130 -0
  397. package/src/helpers/share-link-lifecycle/types.ts +58 -0
  398. package/src/helpers/share-link-maintenance/budget.ts +63 -0
  399. package/src/helpers/share-link-maintenance/config.ts +312 -0
  400. package/src/helpers/share-link-maintenance/index.ts +24 -0
  401. package/src/helpers/share-link-maintenance/runner.ts +251 -0
  402. package/src/helpers/share-link-maintenance/runtime.ts +340 -0
  403. package/src/helpers/share-link-maintenance/scheduler.ts +126 -0
  404. package/src/helpers/share-link-maintenance/telemetry.ts +73 -0
  405. package/src/helpers/share-link-maintenance/types.ts +91 -0
  406. package/src/helpers/share-link-owner/config.ts +48 -0
  407. package/src/helpers/share-link-owner/diagnostics.ts +89 -0
  408. package/src/helpers/share-link-owner/lazy-initializer.ts +29 -0
  409. package/src/helpers/share-link-owner-projection.ts +54 -0
  410. package/src/helpers/share-link-passcode-abuse.ts +36 -0
  411. package/src/helpers/share-link-passcode.ts +47 -0
  412. package/src/helpers/share-link-policy/index.ts +7 -0
  413. package/src/helpers/share-link-policy/production.ts +108 -0
  414. package/src/helpers/share-link-policy/resolver.ts +113 -0
  415. package/src/helpers/share-link-policy/types.ts +56 -0
  416. package/src/helpers/share-link-public/config.ts +41 -0
  417. package/src/helpers/share-link-view-notification.ts +18 -0
  418. package/src/helpers/signingAuthority.helpers.ts +23 -14
  419. package/src/helpers/smartResume.helpers.ts +123 -0
  420. package/src/models/AppManifestVersion.ts +43 -0
  421. package/src/models/AppStoreListing.ts +32 -0
  422. package/src/models/ConsentFlowContract.ts +34 -3
  423. package/src/models/ConsentFlowTerms.ts +10 -0
  424. package/src/models/ConsentFlowTransaction.ts +3 -0
  425. package/src/models/Integration.ts +2 -0
  426. package/src/models/Presentation.ts +2 -2
  427. package/src/models/Profile.ts +3 -2
  428. package/src/models/ShareLink.ts +134 -0
  429. package/src/models/ShareViewReceipt.ts +69 -0
  430. package/src/models/index.ts +25 -0
  431. package/src/models/share-link-constraints.ts +98 -0
  432. package/src/openapi.ts +44 -8
  433. package/src/public-share-link-http.ts +49 -0
  434. package/src/routes/app-store.ts +1056 -38
  435. package/src/routes/boosts.ts +14 -25
  436. package/src/routes/contracts.ts +384 -273
  437. package/src/routes/inbox.ts +4 -2
  438. package/src/routes/index.ts +142 -44
  439. package/src/routes/presentations.ts +19 -3
  440. package/src/routes/profile-manager.ts +29 -2
  441. package/src/routes/profiles.ts +29 -6
  442. package/src/routes/public-share-links.ts +983 -0
  443. package/src/routes/share-links.ts +709 -0
  444. package/src/routes/workflows.ts +3 -2
  445. package/src/services/delivery/adapters/postmark.adapter.ts +4 -1
  446. package/src/types/app-manifest.ts +28 -0
  447. package/src/types/app-store-listing.ts +13 -0
  448. package/src/types/consentflowcontract.ts +25 -4
  449. package/test/app-manifest.helpers.spec.ts +170 -0
  450. package/test/app-manifest.routes.spec.ts +299 -0
  451. package/test/app-store-listing-updates.spec.ts +236 -0
  452. package/test/app-store-listing.spec.ts +65 -3
  453. package/test/consent-data-boundaries.spec.ts +352 -0
  454. package/test/consent-recipients.spec.ts +513 -0
  455. package/test/consent-scopes.helpers.spec.ts +67 -0
  456. package/test/consentflow.spec.ts +63 -2
  457. package/test/contract-requests.spec.ts +1588 -0
  458. package/test/credential-storage.neo4j.spec.ts +122 -0
  459. package/test/dids.spec.ts +10 -1
  460. package/test/guardian-gated-routes.spec.ts +49 -0
  461. package/test/helpers/share-link-fixtures.ts +94 -0
  462. package/test/inbox-security.e2e.ts +26 -10
  463. package/test/inbox.spec.ts +184 -8
  464. package/test/inline-template.helpers.spec.ts +49 -0
  465. package/test/managed-recipient-inbox.spec.ts +152 -0
  466. package/test/notificationMessages.spec.ts +13 -0
  467. package/test/openapi.spec.ts +14 -53
  468. package/test/presentations.spec.ts +54 -6
  469. package/test/profiles.spec.ts +97 -1
  470. package/test/share-content-client.contract.spec.ts +365 -0
  471. package/test/share-content-client.unit.spec.ts +462 -0
  472. package/test/share-link-abuse.unit.spec.ts +65 -0
  473. package/test/share-link-attachment.neo4j.spec.ts +262 -0
  474. package/test/share-link-attachment.unit.spec.ts +244 -0
  475. package/test/share-link-coordinator.neo4j.spec.ts +684 -0
  476. package/test/share-link-coordinator.unit.spec.ts +990 -0
  477. package/test/share-link-dependency-diagnostics.unit.spec.ts +127 -0
  478. package/test/share-link-lifecycle.neo4j.spec.ts +1411 -0
  479. package/test/share-link-lifecycle.unit.spec.ts +205 -0
  480. package/test/share-link-list-cursor.unit.spec.ts +86 -0
  481. package/test/share-link-list.neo4j.spec.ts +324 -0
  482. package/test/share-link-maintenance.neo4j.spec.ts +355 -0
  483. package/test/share-link-maintenance.unit.spec.ts +1140 -0
  484. package/test/share-link-owner-policy.unit.spec.ts +210 -0
  485. package/test/share-link-owner-route.unit.spec.ts +820 -0
  486. package/test/share-link-owner-runtime.unit.spec.ts +102 -0
  487. package/test/share-link-passcode.unit.spec.ts +29 -0
  488. package/test/share-link-policy.unit.spec.ts +149 -0
  489. package/test/share-link-presentation-metadata.unit.spec.ts +49 -0
  490. package/test/share-link-production-policy.unit.spec.ts +126 -0
  491. package/test/share-link-public-config.unit.spec.ts +64 -0
  492. package/test/share-link-public-http-adapter.unit.spec.ts +315 -0
  493. package/test/share-link-public-route.unit.spec.ts +1079 -0
  494. package/test/share-link-receipt.neo4j.spec.ts +943 -0
  495. package/test/share-link-recovery.neo4j.spec.ts +935 -0
  496. package/test/share-link-recovery.unit.spec.ts +726 -0
  497. package/test/share-link-service-policy.neo4j.spec.ts +403 -0
  498. package/test/signing-authority-encryption.spec.ts +75 -0
  499. package/test/smart-resume-consent.spec.ts +233 -0
  500. package/test/smart-resume-upload.spec.ts +65 -0
  501. package/tsconfig.build.json +1 -1
  502. package/tsconfig.json +1 -0
  503. package/vitest.config.ts +14 -0
  504. package/vitest.inbox-e2e.config.ts +2 -0
  505. package/vitest.share-links-neo4j.config.ts +19 -0
  506. package/vitest.share-links-unit.config.ts +28 -0
  507. package/workerLambda.test.ts +100 -0
@@ -0,0 +1,565 @@
1
+ import { int } from 'neo4j-driver';
2
+
3
+ import {
4
+ clampShareLinkRecoveryBatch,
5
+ classifyOperationBinding,
6
+ classifyReservationBinding,
7
+ computeLeaseExpiry,
8
+ isLeaseActive,
9
+ } from '@helpers/share-link-lifecycle';
10
+ import { neogma } from '@instance';
11
+
12
+ import { ensureShareLinkConstraints } from '../../models/share-link-constraints';
13
+ import { failShareLink } from './errors';
14
+ import { queueShareAttachmentCleanup } from './attachment';
15
+ import {
16
+ deleteReservation,
17
+ enqueueCleanupJob,
18
+ lockShare,
19
+ markOperationAbandoned,
20
+ readNodeProperties,
21
+ readReservation,
22
+ toShareLinkOperationRecord,
23
+ toShareLinkRecord,
24
+ toShareLinkReservationRecord,
25
+ } from './helpers';
26
+ import { getShareLink } from './read';
27
+ import { withShareLinkRead, withShareLinkTransaction, type Neo4jQueryResult } from './transaction';
28
+ import type {
29
+ AbandonRecoveredReservationInput,
30
+ AbandonRecoveredReservationResult,
31
+ ClaimRecoverableReservationInput,
32
+ ClaimRecoverableReservationResult,
33
+ DiscoverRecoverableReservationsInput,
34
+ DiscoverRecoverableReservationsResult,
35
+ ShareLinkRecoveryKey,
36
+ ShareLinkRecoveryTarget,
37
+ } from './types';
38
+
39
+ type GraphRunner = {
40
+ run: (query: string, params?: Record<string, unknown>) => Promise<Neo4jQueryResult>;
41
+ };
42
+
43
+ const isOpaqueIdentifier = (value: unknown, maxLength = 128): value is string =>
44
+ typeof value === 'string' &&
45
+ value.length >= 1 &&
46
+ value.length <= maxLength &&
47
+ /^[A-Za-z0-9._~-]+$/.test(value);
48
+
49
+ const assertRecoveryKey = (input: ShareLinkRecoveryKey): void => {
50
+ if (
51
+ !isOpaqueIdentifier(input.namespace) ||
52
+ !isOpaqueIdentifier(input.ownerProfileId) ||
53
+ !isOpaqueIdentifier(input.shareId) ||
54
+ !isOpaqueIdentifier(input.operationId)
55
+ ) {
56
+ failShareLink('INVALID_INPUT', 'recovery key must be well-formed opaque identifiers');
57
+ }
58
+ };
59
+
60
+ function assertLeaseOwner(leaseOwner: unknown): asserts leaseOwner is string {
61
+ if (!isOpaqueIdentifier(leaseOwner)) {
62
+ failShareLink('INVALID_INPUT', 'leaseOwner must be a well-formed opaque identifier');
63
+ }
64
+ }
65
+
66
+ /** Do not let permissive legacy record converters normalize corrupt fences. */
67
+ const hasValidShareFence = (props: Record<string, unknown>): boolean =>
68
+ ['version', 'contentVersion', 'generation'].every(
69
+ field =>
70
+ Number.isSafeInteger(props[field]) &&
71
+ (props[field] as number) >= 1 &&
72
+ (props[field] as number) < Number.MAX_SAFE_INTEGER
73
+ ) &&
74
+ typeof props.status === 'string' &&
75
+ ['pending', 'active', 'stopped'].includes(props.status) &&
76
+ typeof props.contentState === 'string' &&
77
+ ['staging', 'finalized', 'content_missing'].includes(props.contentState);
78
+
79
+ /** Metadata will be activated without the original request, so validate it before conversion. */
80
+ const hasValidReservationIntent = (props: Record<string, unknown>): boolean =>
81
+ typeof props.title === 'string' &&
82
+ (props.note == null || typeof props.note === 'string') &&
83
+ (props.expiresAt == null ||
84
+ (typeof props.expiresAt === 'string' && Number.isFinite(Date.parse(props.expiresAt)))) &&
85
+ Number.isSafeInteger(props.selectedCount) &&
86
+ (props.selectedCount as number) >= 1 &&
87
+ (props.selectedCount as number) <= 50 &&
88
+ (props.opKind !== 'create' ||
89
+ (typeof props.objectRef === 'string' && props.contentVersion === 1)) &&
90
+ (props.opKind !== 'update' ||
91
+ props.contentVersion == null ||
92
+ props.contentVersion === (props.baseContentVersion as number) + 1);
93
+
94
+ /** Binds the idempotency operation record to the exact scoped key. */
95
+ const readOperationForRecovery = async (
96
+ runner: GraphRunner,
97
+ input: ShareLinkRecoveryKey
98
+ ): Promise<Record<string, unknown> | null> => {
99
+ const result = await runner.run(
100
+ `MATCH (o:ShareLinkOperation {operationId: $operationId})
101
+ WHERE o.namespace = $namespace
102
+ AND o.ownerProfileId = $ownerProfileId
103
+ AND o.shareId = $shareId
104
+ RETURN o LIMIT 1`,
105
+ input
106
+ );
107
+
108
+ return readNodeProperties(result, 'o');
109
+ };
110
+
111
+ /**
112
+ * Read-only scoped recovery lookup derived entirely from persisted Brain state.
113
+ *
114
+ * A reservation that matches the exact namespace/owner/share/operation key is
115
+ * returned with its committed share snapshot. When the reservation is already
116
+ * consumed but the operation committed — a finalize whose response was lost —
117
+ * the current committed share is returned instead, so a retry can observe success
118
+ * without re-uploading or reconstructing the deleted reservation. A wrong
119
+ * namespace/owner/operation returns `absent`; this function performs no remote
120
+ * I/O and never trusts a caller-supplied object ref, hash or generation.
121
+ */
122
+ export const readShareLinkRecoveryTarget = async (
123
+ input: ShareLinkRecoveryKey
124
+ ): Promise<ShareLinkRecoveryTarget> => {
125
+ assertRecoveryKey(input);
126
+
127
+ const share = await getShareLink({
128
+ shareId: input.shareId,
129
+ namespace: input.namespace,
130
+ ownerProfileId: input.ownerProfileId,
131
+ });
132
+
133
+ if (!share) return { state: 'absent' };
134
+
135
+ const reservationResult = await neogma.queryRunner.run(
136
+ 'MATCH (r:ShareLinkReservation {shareId: $shareId}) RETURN r LIMIT 1',
137
+ { shareId: input.shareId }
138
+ );
139
+ const reservationProps = readNodeProperties(reservationResult, 'r');
140
+
141
+ if (reservationProps && classifyReservationBinding(reservationProps, input) === 'ok') {
142
+ return {
143
+ state: 'reservation',
144
+ share,
145
+ reservation: toShareLinkReservationRecord(reservationProps),
146
+ };
147
+ }
148
+
149
+ const operationProps = await readOperationForRecovery(neogma.queryRunner, input);
150
+
151
+ if (operationProps && classifyOperationBinding(operationProps, input) === 'ok') {
152
+ const operation = toShareLinkOperationRecord(operationProps);
153
+
154
+ if (operation.status === 'committed') {
155
+ return { state: 'committed', share };
156
+ }
157
+ }
158
+
159
+ return { state: 'absent' };
160
+ };
161
+
162
+ /**
163
+ * Enumerates only *expired in-flight* reservation keys for one explicit
164
+ * namespace. The bounded query joins the share and idempotency operation so it
165
+ * returns neither active leases nor stale/foreign bindings; malformed records
166
+ * fail to match and are therefore never offered for reclamation.
167
+ */
168
+ export const discoverRecoverableReservations = async (
169
+ input: DiscoverRecoverableReservationsInput
170
+ ): Promise<DiscoverRecoverableReservationsResult> => {
171
+ await ensureShareLinkConstraints();
172
+
173
+ if (!isOpaqueIdentifier(input.namespace)) {
174
+ failShareLink('INVALID_INPUT', 'discovery requires an explicit opaque namespace');
175
+ }
176
+
177
+ const now = input.now ?? new Date();
178
+ const limit = clampShareLinkRecoveryBatch(input.limit);
179
+
180
+ const result = await withShareLinkRead(
181
+ runner =>
182
+ runner.run(
183
+ `MATCH (r:ShareLinkReservation {namespace: $namespace})
184
+ MATCH (s:ShareLink {id: r.shareId})
185
+ MATCH (o:ShareLinkOperation {operationId: r.operationId})
186
+ WHERE r.leaseExpiresAt <= $now
187
+ AND s.namespace = $namespace
188
+ AND s.ownerProfileId = r.ownerProfileId
189
+ AND s.status <> 'stopped'
190
+ AND s.generation = r.generation
191
+ AND s.version = r.baseVersion
192
+ AND s.contentVersion = r.baseContentVersion
193
+ AND o.status = 'in_progress'
194
+ AND o.shareId = r.shareId
195
+ AND o.namespace = r.namespace
196
+ AND o.ownerProfileId = r.ownerProfileId
197
+ RETURN DISTINCT r.namespace AS namespace,
198
+ r.ownerProfileId AS ownerProfileId,
199
+ r.shareId AS shareId,
200
+ r.operationId AS operationId,
201
+ r.leaseExpiresAt AS leaseExpiresAt
202
+ ORDER BY leaseExpiresAt ASC, shareId ASC
203
+ LIMIT $limit`,
204
+ { namespace: input.namespace, now: now.toISOString(), limit: int(limit) }
205
+ ),
206
+ { timeoutMs: input.transactionTimeoutMs, noInlineRetry: input.noInlineRetry }
207
+ );
208
+
209
+ const keys: ShareLinkRecoveryKey[] = [];
210
+
211
+ for (const record of result.records) {
212
+ const namespace = record.get('namespace');
213
+ const ownerProfileId = record.get('ownerProfileId');
214
+ const shareId = record.get('shareId');
215
+ const operationId = record.get('operationId');
216
+
217
+ if (
218
+ typeof namespace === 'string' &&
219
+ typeof ownerProfileId === 'string' &&
220
+ typeof shareId === 'string' &&
221
+ typeof operationId === 'string'
222
+ ) {
223
+ keys.push({ namespace, ownerProfileId, shareId, operationId });
224
+ }
225
+ }
226
+
227
+ return { keys };
228
+ };
229
+
230
+ /**
231
+ * Atomically claims one exact expired reservation for a recovery worker.
232
+ *
233
+ * Under the share write lock the persisted reservation, operation and share are
234
+ * re-read and validated against the current version/content version, generation,
235
+ * owner/namespace/operation binding and non-stopped state before anything is
236
+ * written. A claim then bumps the share and reservation generation together and
237
+ * renews only the lease owner/expiry, so two claimers cannot both win and the
238
+ * immutable object tuple, request hashes, metadata and original operation id are
239
+ * preserved. Any malformed persisted binding fails closed.
240
+ */
241
+ export const claimRecoverableReservation = async (
242
+ input: ClaimRecoverableReservationInput
243
+ ): Promise<ClaimRecoverableReservationResult> => {
244
+ await ensureShareLinkConstraints();
245
+ assertRecoveryKey(input);
246
+ assertLeaseOwner(input.leaseOwner);
247
+
248
+ return withShareLinkTransaction(
249
+ async tx => {
250
+ const lockedProps = await lockShare(tx, input.shareId);
251
+ // Production uses the actual clock after the lock is held; an injected
252
+ // test clock is allowed for deterministic lease windows.
253
+ const now = input.now ?? new Date();
254
+ const nowIso = now.toISOString();
255
+
256
+ if (!lockedProps) return { outcome: 'not_claimable', reason: 'absent' };
257
+
258
+ const share = toShareLinkRecord(lockedProps);
259
+
260
+ if (
261
+ share.id !== input.shareId ||
262
+ share.namespace !== input.namespace ||
263
+ share.ownerProfileId !== input.ownerProfileId
264
+ ) {
265
+ return { outcome: 'not_claimable', reason: 'absent' };
266
+ }
267
+ if (!hasValidShareFence(lockedProps)) {
268
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
269
+ }
270
+
271
+ const reservationProps = await readReservation(tx, input.shareId);
272
+
273
+ if (!reservationProps) {
274
+ return { outcome: 'not_claimable', reason: 'absent' };
275
+ }
276
+
277
+ const binding = classifyReservationBinding(reservationProps, input);
278
+
279
+ if (binding === 'malformed_binding' || !hasValidReservationIntent(reservationProps)) {
280
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
281
+ }
282
+ if (binding === 'binding_mismatch') {
283
+ return { outcome: 'not_claimable', reason: 'binding_mismatch' };
284
+ }
285
+
286
+ const reservation = toShareLinkReservationRecord(reservationProps);
287
+
288
+ if (share.status === 'stopped') {
289
+ return { outcome: 'not_claimable', reason: 'share_not_recoverable' };
290
+ }
291
+ if (share.generation !== reservation.generation) {
292
+ return { outcome: 'not_claimable', reason: 'stale_generation' };
293
+ }
294
+ if (
295
+ share.version !== reservation.baseVersion ||
296
+ share.contentVersion !== reservation.baseContentVersion
297
+ ) {
298
+ return { outcome: 'not_claimable', reason: 'version_changed' };
299
+ }
300
+ if (isLeaseActive(reservation.leaseExpiresAt, now)) {
301
+ return { outcome: 'not_claimable', reason: 'lease_active' };
302
+ }
303
+
304
+ const operationProps = await readOperationForRecovery(tx, input);
305
+
306
+ if (!operationProps) {
307
+ return { outcome: 'not_claimable', reason: 'operation_not_in_progress' };
308
+ }
309
+
310
+ const operationBinding = classifyOperationBinding(operationProps, {
311
+ ...input,
312
+ opKind: reservation.opKind,
313
+ clientRequestId: reservation.clientRequestId,
314
+ });
315
+
316
+ if (operationBinding === 'malformed_binding') {
317
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
318
+ }
319
+ if (
320
+ !/^[a-f0-9]{64}$/.test(reservation.requestHash) ||
321
+ typeof operationProps.requestHash !== 'string' ||
322
+ !/^[a-f0-9]{64}$/.test(operationProps.requestHash)
323
+ ) {
324
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
325
+ }
326
+ if (reservation.requestHash !== operationProps.requestHash) {
327
+ return { outcome: 'not_claimable', reason: 'binding_mismatch' };
328
+ }
329
+ if (operationBinding === 'binding_mismatch') {
330
+ return { outcome: 'not_claimable', reason: 'binding_mismatch' };
331
+ }
332
+ if (toShareLinkOperationRecord(operationProps).status !== 'in_progress') {
333
+ return { outcome: 'not_claimable', reason: 'operation_not_in_progress' };
334
+ }
335
+
336
+ const nextGeneration = reservation.generation + 1;
337
+ const leaseExpiresAt = computeLeaseExpiry(now, input.leaseMs);
338
+
339
+ await tx.run(
340
+ `MATCH (s:ShareLink {id: $shareId})
341
+ SET s.generation = $generation,
342
+ s.lockTick = coalesce(s.lockTick, 0) + 1,
343
+ s.updatedAt = $now`,
344
+ { shareId: input.shareId, generation: nextGeneration, now: nowIso }
345
+ );
346
+ await tx.run(
347
+ `MATCH (r:ShareLinkReservation {shareId: $shareId})
348
+ SET r.generation = $generation,
349
+ r.leaseOwner = $leaseOwner,
350
+ r.leaseExpiresAt = $leaseExpiresAt,
351
+ r.updatedAt = $now`,
352
+ {
353
+ shareId: input.shareId,
354
+ generation: nextGeneration,
355
+ leaseOwner: input.leaseOwner,
356
+ leaseExpiresAt,
357
+ now: nowIso,
358
+ }
359
+ );
360
+
361
+ const claimedShareProps = await tx.run(
362
+ 'MATCH (s:ShareLink {id: $shareId}) RETURN s LIMIT 1',
363
+ { shareId: input.shareId }
364
+ );
365
+ const claimedReservationProps = await readReservation(tx, input.shareId);
366
+
367
+ const claimedShare = readNodeProperties(claimedShareProps, 's');
368
+ const claimedReservation = claimedReservationProps;
369
+
370
+ if (!claimedShare || !claimedReservation) {
371
+ failShareLink('CONFLICT', 'failed to persist the recovery claim');
372
+ }
373
+
374
+ return {
375
+ outcome: 'claimed',
376
+ share: toShareLinkRecord(claimedShare),
377
+ reservation: toShareLinkReservationRecord(claimedReservation),
378
+ };
379
+ },
380
+ { timeoutMs: input.transactionTimeoutMs, noInlineRetry: input.noInlineRetry }
381
+ );
382
+ };
383
+
384
+ /**
385
+ * Fenced recovery abandon.
386
+ *
387
+ * This is deliberately separate from the internal `abandonReservation`: it
388
+ * requires both the claimed generation and lease owner AND an active lease, so a
389
+ * worker whose lease already lapsed — or a stale generation, even when the
390
+ * claimant string is reused — cannot destroy work another worker now owns.
391
+ * Intentional stop/revoke semantics still use the internal path and are
392
+ * untouched.
393
+ */
394
+ export const abandonRecoveredReservation = async (
395
+ input: AbandonRecoveredReservationInput
396
+ ): Promise<AbandonRecoveredReservationResult> => {
397
+ await ensureShareLinkConstraints();
398
+ assertRecoveryKey(input);
399
+ assertLeaseOwner(input.leaseOwner);
400
+
401
+ if (!Number.isSafeInteger(input.generation) || input.generation < 1) {
402
+ failShareLink('INVALID_INPUT', 'generation must be a positive safe integer');
403
+ }
404
+
405
+ return withShareLinkTransaction(
406
+ async tx => {
407
+ const lockedProps = await lockShare(tx, input.shareId);
408
+ const now = input.now ?? new Date();
409
+ const nowIso = now.toISOString();
410
+
411
+ if (!lockedProps) return { outcome: 'not_claimable', reason: 'absent' };
412
+
413
+ const share = toShareLinkRecord(lockedProps);
414
+
415
+ if (
416
+ share.id !== input.shareId ||
417
+ share.namespace !== input.namespace ||
418
+ share.ownerProfileId !== input.ownerProfileId
419
+ ) {
420
+ return { outcome: 'not_claimable', reason: 'absent' };
421
+ }
422
+ if (!hasValidShareFence(lockedProps)) {
423
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
424
+ }
425
+
426
+ const reservationProps = await readReservation(tx, input.shareId);
427
+
428
+ if (!reservationProps) {
429
+ const operationProps = await readOperationForRecovery(tx, input);
430
+
431
+ if (
432
+ operationProps &&
433
+ classifyOperationBinding(operationProps, input) === 'ok' &&
434
+ toShareLinkOperationRecord(operationProps).status === 'committed'
435
+ ) {
436
+ return { outcome: 'already_finalized', share };
437
+ }
438
+
439
+ return { outcome: 'already_absent' };
440
+ }
441
+
442
+ const binding = classifyReservationBinding(reservationProps, input);
443
+
444
+ if (binding === 'malformed_binding' || !hasValidReservationIntent(reservationProps)) {
445
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
446
+ }
447
+ if (binding === 'binding_mismatch') {
448
+ return { outcome: 'already_absent' };
449
+ }
450
+
451
+ const reservation = toShareLinkReservationRecord(reservationProps);
452
+
453
+ if (
454
+ share.status === 'stopped' ||
455
+ (reservation.objectRef && share.activeObjectRef === reservation.objectRef)
456
+ ) {
457
+ return { outcome: 'not_claimable', reason: 'share_not_recoverable' };
458
+ }
459
+ if (share.generation !== reservation.generation) {
460
+ return { outcome: 'not_claimable', reason: 'stale_generation' };
461
+ }
462
+ if (
463
+ share.version !== reservation.baseVersion ||
464
+ share.contentVersion !== reservation.baseContentVersion
465
+ ) {
466
+ return { outcome: 'not_claimable', reason: 'version_changed' };
467
+ }
468
+
469
+ if (
470
+ reservation.generation !== input.generation ||
471
+ reservation.leaseOwner !== input.leaseOwner
472
+ ) {
473
+ return { outcome: 'not_claimable', reason: 'stale_generation' };
474
+ }
475
+ if (!isLeaseActive(reservation.leaseExpiresAt, now)) {
476
+ return { outcome: 'not_claimable', reason: 'lease_lapsed' };
477
+ }
478
+
479
+ const operationProps = await readOperationForRecovery(tx, input);
480
+
481
+ if (!operationProps) {
482
+ return { outcome: 'not_claimable', reason: 'operation_not_in_progress' };
483
+ }
484
+
485
+ const operationBinding = classifyOperationBinding(operationProps, {
486
+ ...input,
487
+ opKind: reservation.opKind,
488
+ clientRequestId: reservation.clientRequestId,
489
+ });
490
+
491
+ if (operationBinding === 'malformed_binding') {
492
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
493
+ }
494
+ if (
495
+ !/^[a-f0-9]{64}$/.test(reservation.requestHash) ||
496
+ typeof operationProps.requestHash !== 'string' ||
497
+ !/^[a-f0-9]{64}$/.test(operationProps.requestHash)
498
+ ) {
499
+ return { outcome: 'not_claimable', reason: 'malformed_binding' };
500
+ }
501
+ if (reservation.requestHash !== operationProps.requestHash) {
502
+ return { outcome: 'not_claimable', reason: 'binding_mismatch' };
503
+ }
504
+ if (operationBinding === 'binding_mismatch') {
505
+ return { outcome: 'not_claimable', reason: 'binding_mismatch' };
506
+ }
507
+
508
+ const operation = toShareLinkOperationRecord(operationProps);
509
+
510
+ if (operation.status === 'committed') {
511
+ return { outcome: 'already_finalized', share };
512
+ }
513
+ if (operation.status !== 'in_progress') {
514
+ return { outcome: 'not_claimable', reason: 'operation_not_in_progress' };
515
+ }
516
+
517
+ let cleanupQueuedFor: string | null = null;
518
+ if (
519
+ reservation.attachmentId &&
520
+ reservation.attachmentChunkCount &&
521
+ reservation.contentVersion
522
+ ) {
523
+ await queueShareAttachmentCleanup(
524
+ tx,
525
+ {
526
+ namespace: reservation.namespace,
527
+ ownerProfileId: reservation.ownerProfileId,
528
+ shareId: reservation.shareId,
529
+ contentVersion: reservation.contentVersion,
530
+ attachmentId: reservation.attachmentId,
531
+ chunkCount: reservation.attachmentChunkCount,
532
+ },
533
+ 'abandoned',
534
+ nowIso
535
+ );
536
+ }
537
+
538
+ if (reservation.objectRef) {
539
+ await enqueueCleanupJob(tx, {
540
+ objectRef: reservation.objectRef,
541
+ operationId: reservation.operationId,
542
+ namespace: reservation.namespace,
543
+ ownerProfileId: reservation.ownerProfileId,
544
+ shareId: reservation.shareId,
545
+ contentVersion: reservation.contentVersion ?? reservation.baseContentVersion,
546
+ reason: 'abandoned',
547
+ now: nowIso,
548
+ });
549
+ cleanupQueuedFor = reservation.objectRef;
550
+ }
551
+
552
+ await markOperationAbandoned(tx, {
553
+ namespace: reservation.namespace,
554
+ ownerProfileId: reservation.ownerProfileId,
555
+ opKind: reservation.opKind,
556
+ clientRequestId: reservation.clientRequestId,
557
+ now: nowIso,
558
+ });
559
+ await deleteReservation(tx, input.shareId);
560
+
561
+ return { outcome: 'abandoned', cleanupQueuedFor };
562
+ },
563
+ { timeoutMs: input.transactionTimeoutMs, noInlineRetry: input.noInlineRetry }
564
+ );
565
+ };