@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,115 @@
1
+ import {
2
+ PutShareLinkAttachmentChunkInputValidator,
3
+ DeleteShareLinkAttachmentChunksInputValidator,
4
+ } from '@learncard/types';
5
+ import type {
6
+ PutShareLinkAttachmentChunkInput,
7
+ DeleteShareLinkAttachmentChunksInput,
8
+ } from '@learncard/types';
9
+ import type { ShareContentClient } from './share-content-client';
10
+ import {
11
+ beginShareAttachmentChunk,
12
+ completeShareAttachmentChunk,
13
+ deleteUnreferencedShareAttachment,
14
+ shareAttachmentTuple,
15
+ type ShareAttachmentBinding,
16
+ } from '../accesslayer/share-link/attachment';
17
+ import { ShareLinkCoordinatorError } from './share-link-coordinator/types';
18
+ import type { ShareOwnerContext } from './share-link-coordinator/types';
19
+ import { isShareLinkRepositoryError } from '../accesslayer/share-link/errors';
20
+
21
+ export type ShareLinkAttachmentOwnerApi = {
22
+ put: (
23
+ input: PutShareLinkAttachmentChunkInput,
24
+ owner: ShareOwnerContext
25
+ ) => Promise<{ ok: true }>;
26
+ delete: (
27
+ input: DeleteShareLinkAttachmentChunksInput,
28
+ owner: ShareOwnerContext
29
+ ) => Promise<{ ok: boolean }>;
30
+ };
31
+
32
+ export const createShareLinkAttachmentOwnerApi = (
33
+ client: Pick<ShareContentClient, 'put' | 'delete'>,
34
+ repository = {
35
+ begin: beginShareAttachmentChunk,
36
+ complete: completeShareAttachmentChunk,
37
+ deleteUnreferenced: deleteUnreferencedShareAttachment,
38
+ }
39
+ ): ShareLinkAttachmentOwnerApi => {
40
+ const bindingFor = (
41
+ input: DeleteShareLinkAttachmentChunksInput,
42
+ owner: ShareOwnerContext
43
+ ): ShareAttachmentBinding => ({
44
+ namespace: owner.namespace,
45
+ ownerProfileId: owner.ownerProfileId,
46
+ shareId: input.id,
47
+ contentVersion: input.contentVersion,
48
+ attachmentId: input.attachmentId,
49
+ chunkCount: input.chunkCount,
50
+ });
51
+ return {
52
+ put: async (input, owner) => {
53
+ const parsed = PutShareLinkAttachmentChunkInputValidator.safeParse(input);
54
+ if (!parsed.success)
55
+ throw new ShareLinkCoordinatorError('INVALID_INPUT', 'invalid attachment');
56
+ const value = parsed.data;
57
+ const binding = bindingFor(value, owner);
58
+ try {
59
+ await repository.begin(binding, value.chunkIndex);
60
+ const result = await client.put({
61
+ ...shareAttachmentTuple(binding, value.chunkIndex),
62
+ envelope: value.envelope,
63
+ ownerEncryptedRecovery: value.ownerEncryptedRecovery,
64
+ });
65
+ if (!result.ok)
66
+ throw new ShareLinkCoordinatorError(
67
+ result.error === 'CONFLICT' ? 'CONFLICT' : 'UNAVAILABLE',
68
+ 'attachment upload unavailable'
69
+ );
70
+ await repository.complete(binding, value.chunkIndex);
71
+ return { ok: true };
72
+ } catch (error) {
73
+ if (error instanceof ShareLinkCoordinatorError) throw error;
74
+ const code = isShareLinkRepositoryError(error) ? error.code : undefined;
75
+ throw new ShareLinkCoordinatorError(
76
+ code === 'CONFLICT'
77
+ ? 'CONFLICT'
78
+ : code === 'NOT_FOUND'
79
+ ? 'NOT_FOUND'
80
+ : code === 'PRECONDITION_FAILED' || code === 'INVALID_INPUT'
81
+ ? code
82
+ : 'UNAVAILABLE',
83
+ 'attachment upload unavailable'
84
+ );
85
+ }
86
+ },
87
+ delete: async (input, owner) => {
88
+ const parsed = DeleteShareLinkAttachmentChunksInputValidator.safeParse(input);
89
+ if (!parsed.success)
90
+ throw new ShareLinkCoordinatorError('INVALID_INPUT', 'invalid attachment');
91
+ const binding = bindingFor(parsed.data, owner);
92
+ let allowed: boolean;
93
+ try {
94
+ allowed = await repository.deleteUnreferenced(binding);
95
+ } catch {
96
+ throw new ShareLinkCoordinatorError(
97
+ 'UNAVAILABLE',
98
+ 'attachment cleanup unavailable'
99
+ );
100
+ }
101
+ if (!allowed) return { ok: false };
102
+ // Durable jobs were queued before I/O; interrupted/failed deletes are retried by maintenance.
103
+ let ok = true;
104
+ for (let index = 0; index < binding.chunkCount; index++) {
105
+ try {
106
+ const result = await client.delete(shareAttachmentTuple(binding, index));
107
+ if (!result.ok) ok = false;
108
+ } catch {
109
+ ok = false;
110
+ }
111
+ }
112
+ return { ok };
113
+ },
114
+ };
115
+ };
@@ -0,0 +1,41 @@
1
+ import { shareAttachmentTuple } from '../../accesslayer/share-link/attachment';
2
+ import type { ShareLinkReservationRecord } from '../../accesslayer/share-link/types';
3
+ import type { ShareContentClient } from '../share-content-client';
4
+ import type { MaintenanceBudget } from './types';
5
+ import { budgetAllows } from './budget-helpers';
6
+
7
+ /** Verify the complete immutable set before releasing the atomic commit fence. */
8
+ export const verifyShareAttachmentStats = async (
9
+ client: Pick<ShareContentClient, 'stat'>,
10
+ reservation: ShareLinkReservationRecord,
11
+ budget?: MaintenanceBudget,
12
+ remoteAndFinalizeCostMs = 0
13
+ ): Promise<boolean> => {
14
+ if (reservation.contentVersion === null || !reservation.attachmentId) return true;
15
+ const count = reservation.attachmentChunkCount;
16
+ if (!count || count < 1 || count > 16) return false;
17
+ const binding = {
18
+ namespace: reservation.namespace,
19
+ ownerProfileId: reservation.ownerProfileId,
20
+ shareId: reservation.shareId,
21
+ contentVersion: reservation.contentVersion,
22
+ attachmentId: reservation.attachmentId,
23
+ chunkCount: count,
24
+ };
25
+ for (let index = 0; index < count; index++) {
26
+ // Each bounded HTTP call must leave enough time for finalization and the
27
+ // pass reserve. A many-chunk asset cannot overrun the maintenance budget.
28
+ if (!budgetAllows(budget, remoteAndFinalizeCostMs)) return false;
29
+ const tuple = shareAttachmentTuple(binding, index);
30
+ const result = await client.stat(tuple);
31
+ if (!result.ok || result.value.kind !== 'active') return false;
32
+ const active = result.value;
33
+ if (
34
+ Object.entries(tuple).some(
35
+ ([key, value]) => active[key as keyof typeof active] !== value
36
+ )
37
+ )
38
+ return false;
39
+ }
40
+ return true;
41
+ };
@@ -0,0 +1,53 @@
1
+ import type { MaintenanceBudget } from './types';
2
+
3
+ /**
4
+ * True only for the maintenance budget that opted into whole-unit admission.
5
+ * Legacy/direct callers that pass a bare budget keep the reviewed `exhausted`
6
+ * gate and are never subjected to the extra mid-unit rechecks.
7
+ */
8
+ export const budgetHasUnitReserve = (budget: MaintenanceBudget | undefined): boolean =>
9
+ budget !== undefined && typeof budget.reserveMs === 'function';
10
+
11
+ /**
12
+ * Cooperative budget helpers shared by the recovery and cleanup runners.
13
+ *
14
+ * `budgetAllows` is deliberately conservative only when the caller opted into the
15
+ * maintenance reserve (`reserveMs`). Direct/legacy callers that pass a bare budget
16
+ * keep the reviewed `exhausted()` behaviour, so this cannot silently disable a
17
+ * one-shot runner that predates the maintenance scheduler.
18
+ */
19
+ export const budgetAllows = (budget: MaintenanceBudget | undefined, costMs: number): boolean => {
20
+ if (!budget) return true;
21
+
22
+ if (typeof budget.reserveMs !== 'function' || !Number.isFinite(costMs)) {
23
+ return !budget.exhausted();
24
+ }
25
+
26
+ const remaining = budget.remainingMs();
27
+ if (!Number.isFinite(remaining)) return false;
28
+
29
+ return remaining - budget.reserveMs() >= Math.max(0, costMs);
30
+ };
31
+
32
+ /**
33
+ * Bound a per-call managed-transaction timeout by the time actually left before
34
+ * the pass reserve. The result is always >= 1 so an explicit maintenance
35
+ * transaction still has a positive server-side bound; it can never extend the
36
+ * configured timeout.
37
+ */
38
+ export const boundTransactionTimeout = (
39
+ configuredTimeoutMs: number | undefined,
40
+ budget: MaintenanceBudget | undefined
41
+ ): number | undefined => {
42
+ if (configuredTimeoutMs === undefined) return undefined;
43
+ if (!budget) return configuredTimeoutMs;
44
+
45
+ const remaining = budget.remainingMs();
46
+ if (!Number.isFinite(remaining)) return configuredTimeoutMs;
47
+
48
+ const reserved = typeof budget.reserveMs === 'function' ? budget.reserveMs() : 0;
49
+ const available = remaining - reserved;
50
+ if (!Number.isFinite(available)) return configuredTimeoutMs;
51
+
52
+ return Math.max(1, Math.trunc(Math.min(configuredTimeoutMs, available)));
53
+ };
@@ -0,0 +1,286 @@
1
+ import {
2
+ SHARE_LINK_CLEANUP_DEFAULT_BATCH,
3
+ SHARE_LINK_CLEANUP_MAX_BATCH,
4
+ } from '../share-link-lifecycle';
5
+ import {
6
+ isTransientShareContentError,
7
+ type ShareContentClientErrorCode,
8
+ } from '../share-content-client/types';
9
+ import {
10
+ ShareLinkCoordinatorError,
11
+ type CleanupRunnerDependencies,
12
+ type CleanupRunSummary,
13
+ type ShareCleanupCategory,
14
+ } from './types';
15
+ import { budgetAllows, budgetHasUnitReserve, boundTransactionTimeout } from './budget-helpers';
16
+
17
+ const OPAQUE_IDENTIFIER_RE = /^[A-Za-z0-9._~-]+$/;
18
+
19
+ const isOpaqueIdentifier = (value: unknown, maxLength = 128): value is string =>
20
+ typeof value === 'string' &&
21
+ value.length >= 1 &&
22
+ value.length <= maxLength &&
23
+ OPAQUE_IDENTIFIER_RE.test(value);
24
+
25
+ /**
26
+ * Validate the operator-configured scope before any graph or remote I/O. A
27
+ * missing or malformed namespace means the pass does nothing at all.
28
+ */
29
+ const assertRunnerScope = (dependencies: CleanupRunnerDependencies): void => {
30
+ if (!isOpaqueIdentifier(dependencies.claimant)) {
31
+ throw new ShareLinkCoordinatorError(
32
+ 'INVALID_INPUT',
33
+ 'claimant must be an opaque configured identifier'
34
+ );
35
+ }
36
+ if (!isOpaqueIdentifier(dependencies.namespace)) {
37
+ throw new ShareLinkCoordinatorError(
38
+ 'INVALID_INPUT',
39
+ 'namespace must be an opaque configured identifier'
40
+ );
41
+ }
42
+ };
43
+
44
+ const clampTotal = (limit: number | undefined): number => {
45
+ if (!Number.isSafeInteger(limit) || (limit as number) < 1)
46
+ return SHARE_LINK_CLEANUP_DEFAULT_BATCH;
47
+
48
+ return Math.min(limit as number, SHARE_LINK_CLEANUP_MAX_BATCH);
49
+ };
50
+
51
+ const clampBatchSize = (batchSize: number | undefined, total: number): number => {
52
+ if (!Number.isSafeInteger(batchSize) || (batchSize as number) < 1) return total;
53
+
54
+ return Math.min(batchSize as number, total);
55
+ };
56
+
57
+ const recordCategory = (
58
+ categories: Partial<Record<ShareCleanupCategory, number>>,
59
+ category: ShareCleanupCategory
60
+ ): void => {
61
+ categories[category] = (categories[category] ?? 0) + 1;
62
+ };
63
+
64
+ /**
65
+ * Cap the managed-transaction timeout by the time left in the pass so a graph
66
+ * unit cannot outlive a cooperative deadline. No budget means the configured
67
+ * repository default is used unchanged.
68
+ */
69
+ const boundTransactionTimeoutMs = (
70
+ configuredTimeoutMs: number | undefined,
71
+ budget: CleanupRunnerDependencies['budget']
72
+ ): number | undefined => boundTransactionTimeout(configuredTimeoutMs, budget);
73
+
74
+ /**
75
+ * Worst-case bounded cost of one whole cleanup unit:
76
+ * claim (graph) + delete (HTTP) + complete (graph).
77
+ * The HTTP allowance is fixed when the transport client is constructed, so it
78
+ * cannot be reduced per call; the graph calls are bounded per call below.
79
+ */
80
+ const cleanupUnitCostMs = (dependencies: CleanupRunnerDependencies): number =>
81
+ (dependencies.requestTimeoutMs ?? 0) + 2 * (dependencies.transactionTimeoutMs ?? 0);
82
+
83
+ /** Cost of the remote/complete tail once a job has already been claimed. */
84
+ const cleanupRemoteCostMs = (dependencies: CleanupRunnerDependencies): number =>
85
+ (dependencies.requestTimeoutMs ?? 0) + (dependencies.transactionTimeoutMs ?? 0);
86
+
87
+ type DeleteOutcome =
88
+ | { ok: true }
89
+ | { ok: false; transient: true; error: ShareContentClientErrorCode }
90
+ | { ok: false; category: ShareCleanupCategory };
91
+
92
+ type ClaimedCleanupJob = Awaited<
93
+ ReturnType<CleanupRunnerDependencies['repository']['claimCleanupJobs']>
94
+ >['jobs'][number];
95
+
96
+ /**
97
+ * Bounded, one-shot cleanup runner for durable C3 cleanup jobs.
98
+ *
99
+ * It is deliberately NOT a scheduler: nothing registers it, no interval is
100
+ * created, and no ShareLink routes or workers are enabled by importing it. The
101
+ * maintenance scheduler owns cadence.
102
+ *
103
+ * For every claimed job it sends the exact immutable binding —
104
+ * `{ namespace, ownerProfileId, shareId, contentVersion, objectId: objectRef,
105
+ * operationId }` — to LearnCloud `delete`. `operationId` is the ORIGINAL object
106
+ * operation id recorded by C3, never the latest metadata operation.
107
+ *
108
+ * A job is completed only on a proven LearnCloud deletion/tombstone response.
109
+ * Transient/unavailable responses are returned to the queue with C3's capped
110
+ * exponential backoff. Any other response leaves the job claimed (never
111
+ * completed) so the fenced claim expires and another worker can retry; this
112
+ * never pretends a deletion happened. The returned summary contains aggregate
113
+ * counts and fixed categories only — never an object ref or exception text.
114
+ */
115
+ export const runShareContentCleanupOnce = async (
116
+ dependencies: CleanupRunnerDependencies
117
+ ): Promise<CleanupRunSummary> => {
118
+ assertRunnerScope(dependencies);
119
+
120
+ const { repository, client } = dependencies;
121
+ const total = clampTotal(dependencies.limit);
122
+ const batchSize = clampBatchSize(dependencies.claimBatchSize, total);
123
+
124
+ const summary: CleanupRunSummary = {
125
+ claimed: 0,
126
+ completed: 0,
127
+ retried: 0,
128
+ claimLost: 0,
129
+ skipped: 0,
130
+ categories: {},
131
+ };
132
+
133
+ // Claim just-in-time so a deadline can never strand a whole claimed batch.
134
+ // The default (batch size >= total) keeps the reviewed single-claim
135
+ // behaviour; maintenance sets a small batch size and re-checks the budget
136
+ // before every claim.
137
+ const singleShot = batchSize >= total;
138
+
139
+ while (summary.claimed < total) {
140
+ // Whole-unit admission: a claim is only started when the remaining budget
141
+ // can also cover the delete and the completion (or the retry write).
142
+ if (!budgetAllows(dependencies.budget, cleanupUnitCostMs(dependencies))) break;
143
+
144
+ const remaining = total - summary.claimed;
145
+ const claimLimit = singleShot ? total : Math.min(batchSize, remaining);
146
+
147
+ let claim: Awaited<ReturnType<CleanupRunnerDependencies['repository']['claimCleanupJobs']>>;
148
+ try {
149
+ claim = await repository.claimCleanupJobs({
150
+ namespace: dependencies.namespace,
151
+ claimant: dependencies.claimant,
152
+ limit: claimLimit,
153
+ claimMs: dependencies.claimMs,
154
+ transactionTimeoutMs: boundTransactionTimeoutMs(
155
+ dependencies.transactionTimeoutMs,
156
+ dependencies.budget
157
+ ),
158
+ noInlineRetry: dependencies.noInlineRetry,
159
+ // Omit production time so the repository samples it after the lock.
160
+ now: dependencies.now?.(),
161
+ });
162
+ } catch {
163
+ // Could not confirm a claim; a later pass retries after the lease.
164
+ summary.claimLost += 1;
165
+ recordCategory(summary.categories, 'claim_error');
166
+ break;
167
+ }
168
+
169
+ if (claim.jobs.length === 0) break;
170
+
171
+ let stoppedBeforeRemote = false;
172
+
173
+ for (const job of claim.jobs) {
174
+ summary.claimed += 1;
175
+
176
+ // Recheck after the awaited claim, before the network call: the claim
177
+ // may have waited on a lock. If the tail no longer fits, leave the job
178
+ // fenced (its lease will lapse) and start no further call. Only the
179
+ // maintenance budget opts into this mid-unit reserve.
180
+ if (
181
+ budgetHasUnitReserve(dependencies.budget) &&
182
+ !budgetAllows(dependencies.budget, cleanupRemoteCostMs(dependencies))
183
+ ) {
184
+ recordCategory(summary.categories, 'budget_exhausted');
185
+ stoppedBeforeRemote = true;
186
+ break;
187
+ }
188
+
189
+ const deletion = await safeDelete(client, job);
190
+
191
+ if (deletion.ok) {
192
+ const completion = await safeComplete(repository, {
193
+ objectRef: job.objectRef,
194
+ claimToken: claim.claimToken,
195
+ outcome: 'completed',
196
+ transactionTimeoutMs: boundTransactionTimeoutMs(
197
+ dependencies.transactionTimeoutMs,
198
+ dependencies.budget
199
+ ),
200
+ noInlineRetry: dependencies.noInlineRetry,
201
+ now: dependencies.now?.(),
202
+ });
203
+
204
+ if (completion?.outcome === 'completed') summary.completed += 1;
205
+ else {
206
+ summary.claimLost += 1;
207
+ recordCategory(summary.categories, 'claim_lost');
208
+ }
209
+
210
+ continue;
211
+ }
212
+
213
+ if ('transient' in deletion) {
214
+ const completion = await safeComplete(repository, {
215
+ objectRef: job.objectRef,
216
+ claimToken: claim.claimToken,
217
+ outcome: 'retry',
218
+ errorMessage: deletion.error,
219
+ transactionTimeoutMs: boundTransactionTimeoutMs(
220
+ dependencies.transactionTimeoutMs,
221
+ dependencies.budget
222
+ ),
223
+ noInlineRetry: dependencies.noInlineRetry,
224
+ now: dependencies.now?.(),
225
+ });
226
+
227
+ if (completion?.outcome === 'retry') summary.retried += 1;
228
+ else {
229
+ summary.claimLost += 1;
230
+ recordCategory(summary.categories, 'claim_lost');
231
+ }
232
+
233
+ continue;
234
+ }
235
+
236
+ // Deterministic/authorization failure: never complete. Leave the claim to
237
+ // expire so the bounded fence lets another worker retry later.
238
+ summary.skipped += 1;
239
+ recordCategory(summary.categories, deletion.category);
240
+ }
241
+
242
+ if (singleShot || stoppedBeforeRemote) break;
243
+ }
244
+
245
+ return summary;
246
+ };
247
+
248
+ const safeDelete = async (
249
+ client: CleanupRunnerDependencies['client'],
250
+ job: ClaimedCleanupJob
251
+ ): Promise<DeleteOutcome> => {
252
+ let result: Awaited<ReturnType<CleanupRunnerDependencies['client']['delete']>>;
253
+
254
+ try {
255
+ result = await client.delete({
256
+ namespace: job.namespace,
257
+ ownerProfileId: job.ownerProfileId,
258
+ shareId: job.shareId,
259
+ contentVersion: job.contentVersion,
260
+ objectId: job.objectRef,
261
+ operationId: job.operationId,
262
+ });
263
+ } catch {
264
+ return { ok: false, category: 'client_error' };
265
+ }
266
+
267
+ if (result.ok) return { ok: true };
268
+ if (isTransientShareContentError(result.error)) {
269
+ return { ok: false, transient: true, error: result.error };
270
+ }
271
+
272
+ return { ok: false, category: result.error };
273
+ };
274
+
275
+ const safeComplete = async (
276
+ repository: CleanupRunnerDependencies['repository'],
277
+ input: Parameters<CleanupRunnerDependencies['repository']['completeCleanupJob']>[0]
278
+ ): Promise<Awaited<
279
+ ReturnType<CleanupRunnerDependencies['repository']['completeCleanupJob']>
280
+ > | null> => {
281
+ try {
282
+ return await repository.completeCleanupJob(input);
283
+ } catch {
284
+ return null;
285
+ }
286
+ };