@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,709 @@
1
+ import { TRPCError } from '@trpc/server';
2
+
3
+ import {
4
+ CreateShareLinkInputValidator,
5
+ ListShareLinksInputValidator,
6
+ PaginatedShareLinksValidator,
7
+ ShareLinkOwnerCommitOutputValidator,
8
+ ShareLinkOwnerContentOutputValidator,
9
+ ShareLinkOwnerRecoveryOutputValidator,
10
+ ShareLinkOwnerStatusOutputValidator,
11
+ ShareLinkOperationKeyInputValidator,
12
+ ShareOwnerRecoveryValidator,
13
+ UpdateShareLinkInputValidator,
14
+ PutShareLinkAttachmentChunkInputValidator,
15
+ PutShareLinkAttachmentChunkOutputValidator,
16
+ DeleteShareLinkAttachmentChunksInputValidator,
17
+ DeleteShareLinkAttachmentChunksOutputValidator,
18
+ } from '@learncard/types';
19
+
20
+ import { t, profileRouteWithoutInputCapture } from '@routes';
21
+ import {
22
+ AUTH_GRANT_SHARE_LINKS_READ_SCOPE,
23
+ AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
24
+ } from 'src/constants/auth-grant';
25
+ import { enforceRateLimits } from '@helpers/rateLimit.helpers';
26
+ import { toOwnerShareLink } from '@helpers/share-link-owner-projection';
27
+ import { createRetryableLazyInitializer } from '@helpers/share-link-owner/lazy-initializer';
28
+ import { resolveShareLinkOwnerApiConfig } from '@helpers/share-link-owner/config';
29
+ import type { ShareLinkOwnerApiConfigResolution } from '@helpers/share-link-owner/config';
30
+ import {
31
+ createBoundedShareLinkDiagnosticReporter,
32
+ createShareLinkDependencyResolver,
33
+ SHARE_LINK_COMPOSITION_DIAGNOSTIC,
34
+ } from '@helpers/share-link-owner/diagnostics';
35
+ import { createShareLinkPolicyResolver } from '@helpers/share-link-policy/resolver';
36
+ import { ShareLinkCoordinatorError } from '@helpers/share-link-coordinator';
37
+ import { getShareLinkRequestHashSecret } from '@helpers/share-link-lifecycle';
38
+ import type {
39
+ RecoveryRunnerDependencies,
40
+ ShareLinkCoordinator,
41
+ ShareLinkCoordinatorErrorCode,
42
+ } from '@helpers/share-link-coordinator';
43
+ import { decodeShareLinkListCursor } from '@accesslayer/share-link/list';
44
+ import type { ShareLinkAttachmentOwnerApi } from '@helpers/share-link-attachment';
45
+ import type {
46
+ ListShareLinksInput,
47
+ ListShareLinksResult,
48
+ ShareLinkListCursor,
49
+ } from '@accesslayer/share-link/types';
50
+
51
+ /**
52
+ * LC-2187 owner share-link APIs (D1).
53
+ *
54
+ * Disabled by default and fail-closed: the namespace is taken exclusively from
55
+ * trusted server configuration (never from the caller, tenant header or Host),
56
+ * the owner exclusively from the authenticated profile, and the default policy
57
+ * exclusively from server-side sources. There are no public resolve/content/ack
58
+ * routes in D1.
59
+ */
60
+
61
+ export type ShareLinkRouterDependencies = {
62
+ readonly namespace: string;
63
+ readonly coordinator: ShareLinkCoordinator;
64
+ readonly recovery: RecoveryRunnerDependencies;
65
+ readonly attachments?: ShareLinkAttachmentOwnerApi;
66
+ readonly enforceAttachmentWriteRateLimit?: (ownerProfileId: string) => Promise<void>;
67
+ /**
68
+ * Bounded, namespace+owner-scoped keyset listing over the immutable
69
+ * `(createdAt, id)` key. Receives only trusted scope; the route decodes and
70
+ * validates any caller cursor first.
71
+ */
72
+ readonly listShareLinks: (input: ListShareLinksInput) => Promise<ListShareLinksResult>;
73
+ /** Per-owner create/update/revoke/retry abuse limit. */
74
+ readonly enforceOwnerWriteRateLimit: (ownerProfileId: string) => Promise<void>;
75
+ /**
76
+ * Freshly re-resolve authoritative view-counting eligibility for the owner.
77
+ * Used only to decide whether `viewCount`/`lastViewedAt` may appear in the
78
+ * owner projection; a resolver failure fails closed to `false`.
79
+ */
80
+ readonly resolveViewCountingEligibility: (ownerProfileId: string) => Promise<boolean>;
81
+ };
82
+
83
+ /** Eligibility for projection: an unavailable resolver never leaks a count. */
84
+ const safeEligibility = async (
85
+ dependencies: ShareLinkRouterDependencies,
86
+ ownerProfileId: string
87
+ ): Promise<boolean> => {
88
+ try {
89
+ return (await dependencies.resolveViewCountingEligibility(ownerProfileId)) === true;
90
+ } catch {
91
+ return false;
92
+ }
93
+ };
94
+
95
+ function notFound(): never {
96
+ throw new TRPCError({ code: 'NOT_FOUND' });
97
+ }
98
+
99
+ /**
100
+ * Fixed, sanitized tRPC mapping for coordinator/repository failures.
101
+ *
102
+ * Raw messages, stack traces and `cause` chains from the graph/remote layer
103
+ * must never cross the route boundary or reach telemetry. Every documented
104
+ * coordinator code maps to one stable tRPC code and one safe message.
105
+ */
106
+ const mapCoordinatorError = (code: ShareLinkCoordinatorErrorCode): TRPCError => {
107
+ switch (code) {
108
+ case 'INVALID_INPUT':
109
+ return new TRPCError({ code: 'BAD_REQUEST', message: 'invalid share-link request' });
110
+ case 'NOT_FOUND':
111
+ return new TRPCError({ code: 'NOT_FOUND', message: 'share-link not found' });
112
+ case 'CONFLICT':
113
+ case 'OPERATION_IN_FLIGHT':
114
+ case 'LEASE_EXPIRED':
115
+ case 'STALE_GENERATION':
116
+ return new TRPCError({ code: 'CONFLICT', message: 'share-link state conflict' });
117
+ case 'PRECONDITION_FAILED':
118
+ return new TRPCError({
119
+ code: 'PRECONDITION_FAILED',
120
+ message: 'share-link precondition failed',
121
+ });
122
+ case 'UNAUTHORIZED':
123
+ return new TRPCError({
124
+ code: 'UNAUTHORIZED',
125
+ message: 'share-link operation is not authorized',
126
+ });
127
+ case 'PAYLOAD_TOO_LARGE':
128
+ return new TRPCError({
129
+ code: 'PAYLOAD_TOO_LARGE',
130
+ message: 'share-link payload is too large',
131
+ });
132
+ case 'UNAVAILABLE':
133
+ return new TRPCError({
134
+ code: 'SERVICE_UNAVAILABLE',
135
+ message: 'share-link service is unavailable',
136
+ });
137
+ case 'SIGNING_FAILED':
138
+ case 'UNEXPECTED':
139
+ return new TRPCError({
140
+ code: 'INTERNAL_SERVER_ERROR',
141
+ message: 'share-link operation failed',
142
+ });
143
+ }
144
+ };
145
+
146
+ /** Run a coordinator/recovery call, converting only its typed failures. */
147
+ const runCoordinated = async <T>(operation: () => Promise<T>): Promise<T> => {
148
+ try {
149
+ return await operation();
150
+ } catch (error) {
151
+ if (error instanceof TRPCError) throw error;
152
+ if (error instanceof ShareLinkCoordinatorError) throw mapCoordinatorError(error.code);
153
+
154
+ throw new TRPCError({
155
+ code: 'INTERNAL_SERVER_ERROR',
156
+ message: 'share-link operation failed',
157
+ });
158
+ }
159
+ };
160
+
161
+ /**
162
+ * Complete-request byte bounding lives in the route base
163
+ * (`openRouteWithoutInputCapture`) so it runs before authentication and before
164
+ * Zod parsing; see `@routes`.
165
+ */
166
+
167
+ const openapi = (method: 'GET' | 'POST', path: `/${string}`, summary: string) => ({
168
+ protect: true as const,
169
+ method,
170
+ path,
171
+ tags: ['ShareLinks'],
172
+ summary,
173
+ });
174
+
175
+ const projectCommit = (
176
+ result:
177
+ | { status: 'committed' | 'replayed'; share: Parameters<typeof toOwnerShareLink>[0] }
178
+ | { status: 'pending'; reservation: { operationId: string } },
179
+ id: string,
180
+ viewCountingEnabled: boolean
181
+ ) =>
182
+ result.status === 'pending'
183
+ ? { status: 'pending' as const, id, operationId: result.reservation.operationId }
184
+ : {
185
+ status: 'completed' as const,
186
+ share: toOwnerShareLink(result.share, viewCountingEnabled),
187
+ };
188
+
189
+ /**
190
+ * Build the owner router over injected dependencies. Route-boundary tests pass
191
+ * fakes; production passes the lazily-built runtime.
192
+ */
193
+ export const createShareLinksRouter = (
194
+ getDependencies: () => Promise<ShareLinkRouterDependencies | null>
195
+ ) => {
196
+ const resolve = async (): Promise<ShareLinkRouterDependencies> => {
197
+ let dependencies: ShareLinkRouterDependencies | null;
198
+
199
+ try {
200
+ dependencies = await getDependencies();
201
+ } catch {
202
+ // Initialization failed transiently. Never surface the raw failure;
203
+ // the next request retries initialization.
204
+ throw new TRPCError({
205
+ code: 'INTERNAL_SERVER_ERROR',
206
+ message: 'share-link service is unavailable',
207
+ });
208
+ }
209
+
210
+ if (!dependencies) notFound();
211
+
212
+ return dependencies;
213
+ };
214
+
215
+ return t.router({
216
+ putAttachmentChunk: profileRouteWithoutInputCapture
217
+ .meta({
218
+ openapi: openapi(
219
+ 'POST',
220
+ '/share-links/attachment-chunk/put',
221
+ 'Stage encrypted share attachment chunk'
222
+ ),
223
+ requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
224
+ })
225
+ .input(PutShareLinkAttachmentChunkInputValidator)
226
+ .output(PutShareLinkAttachmentChunkOutputValidator)
227
+ .mutation(async ({ ctx, input }) => {
228
+ const dependencies = await resolve();
229
+ if (!dependencies.attachments) notFound();
230
+ await (
231
+ dependencies.enforceAttachmentWriteRateLimit ??
232
+ dependencies.enforceOwnerWriteRateLimit
233
+ )(ctx.user.profile.profileId);
234
+ return runCoordinated(() =>
235
+ dependencies.attachments!.put(input, {
236
+ namespace: dependencies.namespace,
237
+ ownerProfileId: ctx.user.profile.profileId,
238
+ })
239
+ );
240
+ }),
241
+ deleteAttachmentChunks: profileRouteWithoutInputCapture
242
+ .meta({
243
+ openapi: openapi(
244
+ 'POST',
245
+ '/share-links/attachment-chunks/delete',
246
+ 'Delete unreferenced encrypted attachment'
247
+ ),
248
+ requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
249
+ })
250
+ .input(DeleteShareLinkAttachmentChunksInputValidator)
251
+ .output(DeleteShareLinkAttachmentChunksOutputValidator)
252
+ .mutation(async ({ ctx, input }) => {
253
+ const dependencies = await resolve();
254
+ if (!dependencies.attachments) notFound();
255
+ await (
256
+ dependencies.enforceAttachmentWriteRateLimit ??
257
+ dependencies.enforceOwnerWriteRateLimit
258
+ )(ctx.user.profile.profileId);
259
+ return runCoordinated(() =>
260
+ dependencies.attachments!.delete(input, {
261
+ namespace: dependencies.namespace,
262
+ ownerProfileId: ctx.user.profile.profileId,
263
+ })
264
+ );
265
+ }),
266
+ create: profileRouteWithoutInputCapture
267
+ .meta({
268
+ openapi: openapi('POST', '/share-links/create', 'Create an owner share link'),
269
+ requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
270
+ })
271
+ .input(CreateShareLinkInputValidator)
272
+ .output(ShareLinkOwnerCommitOutputValidator)
273
+ .mutation(async ({ ctx, input }) => {
274
+ const dependencies = await resolve();
275
+ await dependencies.enforceOwnerWriteRateLimit(ctx.user.profile.profileId);
276
+
277
+ const result = await runCoordinated(() =>
278
+ dependencies.coordinator.createShareLink(input, {
279
+ namespace: dependencies.namespace,
280
+ ownerProfileId: ctx.user.profile.profileId,
281
+ })
282
+ );
283
+
284
+ return projectCommit(
285
+ result,
286
+ input.id,
287
+ await safeEligibility(dependencies, ctx.user.profile.profileId)
288
+ );
289
+ }),
290
+
291
+ update: profileRouteWithoutInputCapture
292
+ .meta({
293
+ openapi: openapi('POST', '/share-links/update', 'Update an owner share link'),
294
+ requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
295
+ })
296
+ .input(UpdateShareLinkInputValidator)
297
+ .output(ShareLinkOwnerCommitOutputValidator)
298
+ .mutation(async ({ ctx, input }) => {
299
+ const dependencies = await resolve();
300
+ await dependencies.enforceOwnerWriteRateLimit(ctx.user.profile.profileId);
301
+
302
+ const result = await runCoordinated(() =>
303
+ dependencies.coordinator.updateShareLink(input, {
304
+ namespace: dependencies.namespace,
305
+ ownerProfileId: ctx.user.profile.profileId,
306
+ })
307
+ );
308
+
309
+ return projectCommit(
310
+ result,
311
+ input.id,
312
+ await safeEligibility(dependencies, ctx.user.profile.profileId)
313
+ );
314
+ }),
315
+
316
+ revoke: profileRouteWithoutInputCapture
317
+ .meta({
318
+ openapi: openapi('POST', '/share-links/revoke', 'Revoke an owner share link'),
319
+ requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
320
+ })
321
+ .input(
322
+ ShareLinkOperationKeyInputValidator.pick({ id: true }).extend({
323
+ expectedVersion: UpdateShareLinkInputValidator.shape.expectedVersion.optional(),
324
+ clientRequestId: CreateShareLinkInputValidator.shape.clientRequestId.optional(),
325
+ })
326
+ )
327
+ .output(ShareLinkOwnerCommitOutputValidator)
328
+ .mutation(async ({ ctx, input }) => {
329
+ const dependencies = await resolve();
330
+ await dependencies.enforceOwnerWriteRateLimit(ctx.user.profile.profileId);
331
+
332
+ const result = await runCoordinated(() =>
333
+ dependencies.coordinator.revokeShareLink(input, {
334
+ namespace: dependencies.namespace,
335
+ ownerProfileId: ctx.user.profile.profileId,
336
+ })
337
+ );
338
+
339
+ // `completed` is the outer terminal tag; the authoritative state
340
+ // (here `stopped`) lives on `share.status`.
341
+ return {
342
+ status: 'completed' as const,
343
+ share: toOwnerShareLink(
344
+ result.share,
345
+ await safeEligibility(dependencies, ctx.user.profile.profileId)
346
+ ),
347
+ };
348
+ }),
349
+
350
+ list: profileRouteWithoutInputCapture
351
+ .meta({
352
+ // A collection path with no trailing segment, so it can never be
353
+ // shadowed by (or shadow) the `GET /share-links/{id}` owner route
354
+ // or any public `/public/share-links/...` route.
355
+ openapi: openapi('GET', '/share-links', 'List owner share links'),
356
+ requiredScope: AUTH_GRANT_SHARE_LINKS_READ_SCOPE,
357
+ })
358
+ .input(ListShareLinksInputValidator)
359
+ .output(PaginatedShareLinksValidator)
360
+ .query(async ({ ctx, input }) => {
361
+ const dependencies = await resolve();
362
+
363
+ let cursor: ShareLinkListCursor | null = null;
364
+ if (input.cursor !== undefined) {
365
+ cursor = decodeShareLinkListCursor(input.cursor);
366
+ // Malformed/oversized/impossible/foreign-version cursors fail
367
+ // closed with one fixed message and never reach the graph.
368
+ if (cursor === null) {
369
+ throw new TRPCError({
370
+ code: 'BAD_REQUEST',
371
+ message: 'invalid share-link cursor',
372
+ });
373
+ }
374
+ }
375
+
376
+ let page: ListShareLinksResult;
377
+ try {
378
+ page = await dependencies.listShareLinks({
379
+ namespace: dependencies.namespace,
380
+ ownerProfileId: ctx.user.profile.profileId,
381
+ limit: input.limit,
382
+ cursor,
383
+ });
384
+ } catch {
385
+ // Fixed safe mapping: never surface a raw repository message.
386
+ throw new TRPCError({
387
+ code: 'INTERNAL_SERVER_ERROR',
388
+ message: 'share-link list is unavailable',
389
+ });
390
+ }
391
+
392
+ // Eligibility is re-resolved exactly once per request; a failure
393
+ // or non-`true` answer suppresses viewCount/lastViewedAt for
394
+ // every record (fail-closed).
395
+ const viewCountingEnabled = await safeEligibility(
396
+ dependencies,
397
+ ctx.user.profile.profileId
398
+ );
399
+
400
+ return {
401
+ records: page.records.map(record =>
402
+ toOwnerShareLink(record, viewCountingEnabled)
403
+ ),
404
+ hasMore: page.hasMore,
405
+ ...(page.nextCursor === null ? {} : { cursor: page.nextCursor }),
406
+ };
407
+ }),
408
+
409
+ get: profileRouteWithoutInputCapture
410
+ .meta({
411
+ openapi: openapi('GET', '/share-links/{id}', 'Get owner share-link metadata'),
412
+ requiredScope: AUTH_GRANT_SHARE_LINKS_READ_SCOPE,
413
+ })
414
+ .input(ShareLinkOperationKeyInputValidator.pick({ id: true }))
415
+ .output(ShareLinkOwnerStatusOutputValidator)
416
+ .query(async ({ ctx, input }) => {
417
+ const dependencies = await resolve();
418
+ const share = await runCoordinated(() =>
419
+ dependencies.coordinator.getShareLink(input.id, {
420
+ namespace: dependencies.namespace,
421
+ ownerProfileId: ctx.user.profile.profileId,
422
+ })
423
+ );
424
+
425
+ return share === null
426
+ ? { status: 'not_found' as const, id: input.id }
427
+ : {
428
+ status: 'found' as const,
429
+ share: toOwnerShareLink(
430
+ share,
431
+ await safeEligibility(dependencies, ctx.user.profile.profileId)
432
+ ),
433
+ };
434
+ }),
435
+
436
+ getOperationStatus: profileRouteWithoutInputCapture
437
+ .meta({
438
+ openapi: openapi(
439
+ 'GET',
440
+ '/share-links/operations/{operationId}',
441
+ 'Get owner scoped operation status'
442
+ ),
443
+ requiredScope: AUTH_GRANT_SHARE_LINKS_READ_SCOPE,
444
+ })
445
+ .input(ShareLinkOperationKeyInputValidator)
446
+ .output(ShareLinkOwnerStatusOutputValidator)
447
+ .query(async ({ ctx, input }) => {
448
+ const dependencies = await resolve();
449
+ const target = await runCoordinated(() =>
450
+ dependencies.recovery.repository.readShareLinkRecoveryTarget({
451
+ namespace: dependencies.namespace,
452
+ ownerProfileId: ctx.user.profile.profileId,
453
+ shareId: input.id,
454
+ operationId: input.operationId,
455
+ })
456
+ );
457
+
458
+ if (target.state === 'committed') {
459
+ return {
460
+ status: 'found' as const,
461
+ share: toOwnerShareLink(
462
+ target.share,
463
+ await safeEligibility(dependencies, ctx.user.profile.profileId)
464
+ ),
465
+ };
466
+ }
467
+ if (target.state === 'reservation') {
468
+ return {
469
+ status: 'pending' as const,
470
+ id: input.id,
471
+ operationId: input.operationId,
472
+ };
473
+ }
474
+
475
+ return { status: 'not_found' as const, id: input.id };
476
+ }),
477
+
478
+ retry: profileRouteWithoutInputCapture
479
+ .meta({
480
+ openapi: openapi('POST', '/share-links/retry', 'Retry an owner operation'),
481
+ requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
482
+ })
483
+ .input(ShareLinkOperationKeyInputValidator)
484
+ .output(ShareLinkOwnerStatusOutputValidator)
485
+ .mutation(async ({ ctx, input }) => {
486
+ const dependencies = await resolve();
487
+ await dependencies.enforceOwnerWriteRateLimit(ctx.user.profile.profileId);
488
+
489
+ // Persisted-key recovery only: the server re-derives the immutable
490
+ // object/lease/generation bindings from durable Brain state. A
491
+ // caller-supplied descriptor is never accepted.
492
+ const result = await runCoordinated(async () => {
493
+ const { recoverShareLinkOperation } =
494
+ await import('@helpers/share-link-coordinator/recovery-runner');
495
+
496
+ return recoverShareLinkOperation(dependencies.recovery, {
497
+ namespace: dependencies.namespace,
498
+ ownerProfileId: ctx.user.profile.profileId,
499
+ shareId: input.id,
500
+ operationId: input.operationId,
501
+ });
502
+ });
503
+
504
+ if (result.status === 'committed') {
505
+ return {
506
+ status: 'found' as const,
507
+ share: toOwnerShareLink(
508
+ result.share,
509
+ await safeEligibility(dependencies, ctx.user.profile.profileId)
510
+ ),
511
+ };
512
+ }
513
+ if (result.status === 'abandoned' || result.status === 'absent') {
514
+ return { status: 'not_found' as const, id: input.id };
515
+ }
516
+
517
+ return {
518
+ status: 'pending' as const,
519
+ id: input.id,
520
+ operationId: input.operationId,
521
+ };
522
+ }),
523
+
524
+ getRecovery: profileRouteWithoutInputCapture
525
+ .meta({
526
+ openapi: openapi(
527
+ 'GET',
528
+ '/share-links/{id}/recovery',
529
+ 'Get owner-encrypted recovery (owner only)'
530
+ ),
531
+ requiredScope: AUTH_GRANT_SHARE_LINKS_READ_SCOPE,
532
+ })
533
+ .input(ShareLinkOperationKeyInputValidator.pick({ id: true }))
534
+ .output(ShareLinkOwnerRecoveryOutputValidator)
535
+ .query(async ({ ctx, input }) => {
536
+ const dependencies = await resolve();
537
+ const result = await runCoordinated(() =>
538
+ dependencies.coordinator.readOwnerRecovery(input.id, {
539
+ namespace: dependencies.namespace,
540
+ ownerProfileId: ctx.user.profile.profileId,
541
+ })
542
+ );
543
+
544
+ if (!result.ok) notFound();
545
+
546
+ const parsed = ShareOwnerRecoveryValidator.safeParse(
547
+ result.value.ownerEncryptedRecovery
548
+ );
549
+ if (!parsed.success) notFound();
550
+
551
+ return { recovery: parsed.data };
552
+ }),
553
+
554
+ getContent: profileRouteWithoutInputCapture
555
+ .meta({
556
+ openapi: openapi(
557
+ 'GET',
558
+ '/share-links/{id}/content',
559
+ 'Get encrypted share content (owner only)'
560
+ ),
561
+ requiredScope: AUTH_GRANT_SHARE_LINKS_READ_SCOPE,
562
+ })
563
+ .input(ShareLinkOperationKeyInputValidator.pick({ id: true }))
564
+ .output(ShareLinkOwnerContentOutputValidator)
565
+ .query(async ({ ctx, input }) => {
566
+ const dependencies = await resolve();
567
+ const result = await runCoordinated(() =>
568
+ dependencies.coordinator.fetchShareContent(
569
+ input.id,
570
+ {
571
+ namespace: dependencies.namespace,
572
+ ownerProfileId: ctx.user.profile.profileId,
573
+ },
574
+ { allowExpired: true }
575
+ )
576
+ );
577
+
578
+ if (!result.ok) notFound();
579
+
580
+ return ShareLinkOwnerContentOutputValidator.parse({
581
+ id: input.id,
582
+ contentVersion: result.value.contentVersion,
583
+ envelope: result.value.envelope,
584
+ });
585
+ }),
586
+ });
587
+ };
588
+
589
+ export type ShareLinksRouter = ReturnType<typeof createShareLinksRouter>;
590
+
591
+ /** Per-hour owner write budget. Fails closed when the cache is unavailable. */
592
+ export const enforceOwnerShareWriteRateLimit = async (
593
+ namespace: string,
594
+ ownerProfileId: string
595
+ ): Promise<void> => {
596
+ await enforceRateLimits([
597
+ {
598
+ key: `share-link-owner-write:${namespace}:${ownerProfileId}`,
599
+ limit: 60,
600
+ windowSeconds: 60 * 60,
601
+ description: 'owner share-link writes',
602
+ },
603
+ ]);
604
+ };
605
+
606
+ /**
607
+ * Lazy production composition. Config is resolved before any signer/graph import.
608
+ * A disabled configuration is inert and silent; malformed wiring emits a bounded
609
+ * sanitized diagnostic and fails closed to NOT_FOUND. A failed build is never
610
+ * cached, so a later request retries with non-null dependencies.
611
+ */
612
+ const initializeProductionDependencies =
613
+ createRetryableLazyInitializer<ShareLinkRouterDependencies>(() =>
614
+ buildProductionDependencies(
615
+ resolveShareLinkOwnerApiConfig(process.env as Record<string, unknown>) as Extract<
616
+ ShareLinkOwnerApiConfigResolution,
617
+ { status: 'enabled' }
618
+ >
619
+ )
620
+ );
621
+
622
+ export const getProductionDependencies =
623
+ createShareLinkDependencyResolver<ShareLinkRouterDependencies>({
624
+ resolveConfig: () => resolveShareLinkOwnerApiConfig(process.env as Record<string, unknown>),
625
+ initializeDependencies: initializeProductionDependencies,
626
+ reportDiagnostic: createBoundedShareLinkDiagnosticReporter(),
627
+ configurationInvalidCategory: SHARE_LINK_COMPOSITION_DIAGNOSTIC.OWNER_CONFIGURATION_INVALID,
628
+ initializationFailedCategory: SHARE_LINK_COMPOSITION_DIAGNOSTIC.OWNER_INITIALIZATION_FAILED,
629
+ });
630
+
631
+ const buildProductionDependencies = async (
632
+ config: Extract<ShareLinkOwnerApiConfigResolution, { status: 'enabled' }>
633
+ ): Promise<ShareLinkRouterDependencies> => {
634
+ // Validate before accepting owner operations, not on the first write.
635
+ getShareLinkRequestHashSecret();
636
+ const [
637
+ { getServerDidWebDID },
638
+ { createDidWebLearnCardTokenSigner },
639
+ clientModule,
640
+ runtimeModule,
641
+ { ensureShareLinkConstraints },
642
+ { createProductionShareLinkPolicySource },
643
+ listRepository,
644
+ ] = await Promise.all([
645
+ import('@helpers/learnCard.helpers'),
646
+ import('@helpers/share-content-client/adapters'),
647
+ import('@helpers/share-content-client'),
648
+ import('@helpers/share-link-coordinator/runtime'),
649
+ import('../models/share-link-constraints'),
650
+ import('@helpers/share-link-policy/production'),
651
+ import('@accesslayer/share-link/list'),
652
+ ]);
653
+
654
+ const clientConfig = clientModule.resolveShareContentClientConfig({
655
+ enabled: true,
656
+ origin: config.origin,
657
+ namespace: config.namespace,
658
+ audience: config.audience,
659
+ signerDid: getServerDidWebDID(),
660
+ allowInsecureLoopback: config.allowInsecureLoopback,
661
+ signer: createDidWebLearnCardTokenSigner(),
662
+ });
663
+
664
+ if (!clientConfig.enabled) {
665
+ // Fail the build so the retryable lazy initializer does not cache it and
666
+ // a later request retries once the wiring is corrected.
667
+ throw new Error('share-content client configuration is invalid');
668
+ }
669
+
670
+ const client = clientModule.createShareContentClient(clientConfig);
671
+ const policyResolver = createShareLinkPolicyResolver(createProductionShareLinkPolicySource());
672
+ const coordinator = runtimeModule.createRuntimeShareLinkCoordinator({
673
+ client,
674
+ leaseOwner: 'brain-share-link-owner-api',
675
+ policyResolver,
676
+ });
677
+ const recovery = runtimeModule.createRuntimeRecoveryRunnerDependencies({
678
+ client,
679
+ claimant: 'brain-share-link-owner-api',
680
+ namespace: config.namespace,
681
+ });
682
+
683
+ await ensureShareLinkConstraints();
684
+
685
+ return {
686
+ namespace: config.namespace,
687
+ coordinator,
688
+ recovery,
689
+ attachments: (
690
+ await import('@helpers/share-link-attachment')
691
+ ).createShareLinkAttachmentOwnerApi(client),
692
+ enforceAttachmentWriteRateLimit: ownerProfileId =>
693
+ enforceRateLimits([
694
+ {
695
+ key: `share-link-attachment-write:${config.namespace}:${ownerProfileId}`,
696
+ limit: 120,
697
+ windowSeconds: 60 * 60,
698
+ description: 'owner share-link attachment writes',
699
+ },
700
+ ]),
701
+ listShareLinks: listRepository.listShareLinks,
702
+ enforceOwnerWriteRateLimit: ownerProfileId =>
703
+ enforceOwnerShareWriteRateLimit(config.namespace, ownerProfileId),
704
+ resolveViewCountingEligibility: async ownerProfileId =>
705
+ (await policyResolver.resolve(ownerProfileId)).viewCountingEnabled,
706
+ };
707
+ };
708
+
709
+ export const shareLinksRouter = createShareLinksRouter(getProductionDependencies);