@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,126 @@
1
+ import { safeLogMaintenance } from './telemetry';
2
+ import type { ShareLinkMaintenanceLogger, ShareLinkMaintenanceRunSummary } from './types';
3
+
4
+ export type ShareLinkMaintenanceSchedulerDependencies = {
5
+ runOnce: (options?: { remainingTimeMs?: number }) => Promise<unknown>;
6
+ intervalMs: number;
7
+ logger: ShareLinkMaintenanceLogger;
8
+ setTimeoutFn?: (handler: () => void, timeoutMs: number) => unknown;
9
+ clearTimeoutFn?: (handle: unknown) => void;
10
+ };
11
+
12
+ export type ShareLinkMaintenanceScheduler = {
13
+ /** Idempotent; schedules the first bounded pass one interval from now. */
14
+ start: () => void;
15
+ /** Stops scheduling and drains an in-flight pass. */
16
+ stop: () => Promise<void>;
17
+ /** Runs one pass now, coalescing with an in-flight pass (no overlap). */
18
+ trigger: () => Promise<void>;
19
+ isRunning: () => boolean;
20
+ };
21
+
22
+ const defaultSetTimeout = (handler: () => void, timeoutMs: number): unknown =>
23
+ setTimeout(handler, timeoutMs);
24
+
25
+ const defaultClearTimeout = (handle: unknown): void => {
26
+ clearTimeout(handle as ReturnType<typeof setTimeout>);
27
+ };
28
+
29
+ const fixedFailureSummary = (intervalMs: number): ShareLinkMaintenanceRunSummary => ({
30
+ status: 'failed',
31
+ durationMs: 0,
32
+ budgetMs: intervalMs,
33
+ recovery: null,
34
+ cleanup: null,
35
+ receiptPrune: null,
36
+ categories: { share_link_maintenance_pass_failed: 1 },
37
+ });
38
+
39
+ /**
40
+ * Non-overlapping maintenance loop.
41
+ *
42
+ * The next tick is scheduled only *after* the awaited pass settles, so a slow
43
+ * pass can never stack timers. `trigger` coalesces with an in-flight pass, which
44
+ * is the per-process overlap guard; other processes are fenced by the durable
45
+ * repository claims. `stop` is terminal: it clears the pending tick, awaits the
46
+ * in-flight pass, and rejects any later explicit trigger or already-queued timer
47
+ * callback. Nothing here registers a timer at module import, and a logger failure
48
+ * cannot corrupt the loop state.
49
+ */
50
+ export const createShareLinkMaintenanceScheduler = (
51
+ dependencies: ShareLinkMaintenanceSchedulerDependencies
52
+ ): ShareLinkMaintenanceScheduler => {
53
+ const setTimeoutFn = dependencies.setTimeoutFn ?? defaultSetTimeout;
54
+ const clearTimeoutFn = dependencies.clearTimeoutFn ?? defaultClearTimeout;
55
+
56
+ let started = false;
57
+ let stopped = false;
58
+ let timer: unknown = null;
59
+ let inFlight: Promise<void> | null = null;
60
+
61
+ const scheduleNext = (): void => {
62
+ if (stopped || !started) return;
63
+
64
+ timer = setTimeoutFn(() => {
65
+ void tick();
66
+ }, dependencies.intervalMs);
67
+ };
68
+
69
+ const tick = async (): Promise<void> => {
70
+ await trigger();
71
+ scheduleNext();
72
+ };
73
+
74
+ const trigger = (): Promise<void> => {
75
+ // Stop is terminal: nothing, including an explicit trigger or an already
76
+ // queued timer callback, may start work after `stop()`.
77
+ if (stopped) return Promise.resolve();
78
+
79
+ if (inFlight) return inFlight;
80
+
81
+ // An `async` wrapper converts a synchronous `runOnce` throw into a
82
+ // rejected promise, and `.finally` always settles on a microtask, so the
83
+ // assignment below can never race the clearing callback and leave
84
+ // `inFlight` stuck on an already-settled promise.
85
+ const run = async (): Promise<void> => {
86
+ try {
87
+ await dependencies.runOnce();
88
+ } catch {
89
+ safeLogMaintenance(
90
+ dependencies.logger,
91
+ fixedFailureSummary(dependencies.intervalMs)
92
+ );
93
+ }
94
+ };
95
+
96
+ const pass = run().finally(() => {
97
+ if (inFlight === pass) inFlight = null;
98
+ });
99
+
100
+ inFlight = pass;
101
+
102
+ return pass;
103
+ };
104
+
105
+ return {
106
+ start: () => {
107
+ if (started || stopped) return;
108
+
109
+ started = true;
110
+ scheduleNext();
111
+ },
112
+ stop: async () => {
113
+ stopped = true;
114
+ started = false;
115
+
116
+ if (timer !== null) {
117
+ clearTimeoutFn(timer);
118
+ timer = null;
119
+ }
120
+
121
+ if (inFlight) await inFlight;
122
+ },
123
+ trigger,
124
+ isRunning: () => inFlight !== null,
125
+ };
126
+ };
@@ -0,0 +1,73 @@
1
+ import type { CleanupRunSummary, ShareRecoveryRunSummary } from '../share-link-coordinator';
2
+ import type {
3
+ ShareLinkMaintenanceLogEvent,
4
+ ShareLinkMaintenanceLogger,
5
+ ShareLinkMaintenanceRunSummary,
6
+ } from './types';
7
+
8
+ /**
9
+ * Fixed, allowlisted aggregate counters. This is the ONLY surface a maintenance
10
+ * log may contain: no object ref, share id, owner id, namespace value, title,
11
+ * note, URL, fragment, key, ciphertext, recovery blob or exception object.
12
+ */
13
+ const recoveryCounts = (recovery: ShareRecoveryRunSummary | null): Record<string, number> => ({
14
+ recoveryDiscovered: recovery?.discovered ?? 0,
15
+ recoveryClaimed: recovery?.claimed ?? 0,
16
+ recoveryFinalized: recovery?.finalized ?? 0,
17
+ recoveryAbandoned: recovery?.abandoned ?? 0,
18
+ recoveryDeferred: recovery?.deferred ?? 0,
19
+ recoveryClaimLost: recovery?.claimLost ?? 0,
20
+ recoveryAnomalies: recovery?.anomalies ?? 0,
21
+ });
22
+
23
+ const cleanupCounts = (cleanup: CleanupRunSummary | null): Record<string, number> => ({
24
+ cleanupClaimed: cleanup?.claimed ?? 0,
25
+ cleanupCompleted: cleanup?.completed ?? 0,
26
+ cleanupRetried: cleanup?.retried ?? 0,
27
+ cleanupClaimLost: cleanup?.claimLost ?? 0,
28
+ cleanupSkipped: cleanup?.skipped ?? 0,
29
+ });
30
+
31
+ export const toMaintenanceLogEvent = (
32
+ summary: ShareLinkMaintenanceRunSummary
33
+ ): ShareLinkMaintenanceLogEvent => ({
34
+ event: 'share_link_maintenance',
35
+ status: summary.status,
36
+ durationMs: Math.max(0, Math.trunc(summary.durationMs)),
37
+ budgetMs: Math.max(0, Math.trunc(summary.budgetMs)),
38
+ counts: {
39
+ ...recoveryCounts(summary.recovery),
40
+ ...cleanupCounts(summary.cleanup),
41
+ receiptsPruned: summary.receiptPrune?.pruned ?? 0,
42
+ ...(summary.attachmentStages
43
+ ? { attachmentStagesQueued: summary.attachmentStages.queued }
44
+ : {}),
45
+ },
46
+ categories: {
47
+ ...(summary.recovery?.categories ?? {}),
48
+ ...(summary.cleanup?.categories ?? {}),
49
+ ...summary.categories,
50
+ },
51
+ });
52
+
53
+ /**
54
+ * Emit one allowlisted event. A throwing logger is swallowed: telemetry must
55
+ * never corrupt lifecycle state or turn a completed pass into a failure.
56
+ */
57
+ export const safeLogMaintenance = (
58
+ logger: ShareLinkMaintenanceLogger,
59
+ summary: ShareLinkMaintenanceRunSummary
60
+ ): void => {
61
+ try {
62
+ const event = toMaintenanceLogEvent(summary);
63
+ if (summary.status === 'failed') logger.error(event);
64
+ else logger.info(event);
65
+ } catch {
66
+ // Intentionally ignored: lifecycle state is authoritative.
67
+ }
68
+ };
69
+
70
+ export const createConsoleMaintenanceLogger = (): ShareLinkMaintenanceLogger => ({
71
+ info: event => console.log('[share-link-maintenance]', event),
72
+ error: event => console.error('[share-link-maintenance]', event),
73
+ });
@@ -0,0 +1,91 @@
1
+ import type { ShareRecoveryRunSummary, CleanupRunSummary } from '../share-link-coordinator';
2
+
3
+ /**
4
+ * LC-2187 share-link maintenance (scheduling + cleanup integration).
5
+ *
6
+ * This module owns the automatically configured, namespace-scoped maintenance pass. It
7
+ * is deliberately separate from the coordinator barrel so unit tests never load
8
+ * the Neo4j `@instance` or the LearnCard signing graph.
9
+ */
10
+
11
+ /** A fully validated, enabled maintenance configuration. */
12
+ export type ShareLinkMaintenanceConfig = {
13
+ namespace: string;
14
+ origin: string;
15
+ audience: string;
16
+ allowInsecureLoopback: boolean;
17
+ claimant: string;
18
+ recoveryLimit: number;
19
+ cleanupLimit: number;
20
+ receiptPruneLimit: number;
21
+ leaseMs: number;
22
+ claimMs: number;
23
+ budgetMs: number;
24
+ intervalMs: number;
25
+ requestTimeoutMs: number;
26
+ finalizeReserveMs: number;
27
+ graphTimeoutMs: number;
28
+ };
29
+
30
+ /**
31
+ * Fixed, non-sensitive configuration outcome. `invalid` never carries the bad
32
+ * value or an exception, so it is safe to log verbatim.
33
+ */
34
+ export type ShareLinkMaintenanceConfigResolution =
35
+ | { status: 'enabled'; config: ShareLinkMaintenanceConfig }
36
+ | { status: 'disabled' }
37
+ | { status: 'invalid'; category: 'share_link_maintenance_configuration_invalid' };
38
+
39
+ /** Fixed pass status. Never carries request/record data. */
40
+ export type ShareLinkMaintenanceStatus =
41
+ | 'disabled'
42
+ | 'invalid_configuration'
43
+ | 'initialization_failed'
44
+ | 'completed'
45
+ | 'deadline_exhausted'
46
+ | 'failed';
47
+
48
+ /** Fixed failure categories allowed in a maintenance summary/log. */
49
+ export type ShareLinkMaintenanceFailureCategory =
50
+ | 'share_link_maintenance_pass_failed'
51
+ | 'share_link_maintenance_recovery_failed'
52
+ | 'share_link_maintenance_cleanup_failed'
53
+ | 'share_link_maintenance_attachment_stage_failed'
54
+ | 'share_link_maintenance_receipt_prune_failed'
55
+ | 'share_link_maintenance_initialization_failed'
56
+ | 'share_link_maintenance_configuration_invalid';
57
+
58
+ /**
59
+ * Aggregate-only result of one maintenance invocation. It contains counts,
60
+ * durations and fixed categories; it never contains object/share/owner ids,
61
+ * URLs, keys, ciphertext, recovery, titles, notes or exception objects.
62
+ */
63
+ export type ShareLinkMaintenanceRunSummary = {
64
+ status: ShareLinkMaintenanceStatus;
65
+ durationMs: number;
66
+ budgetMs: number;
67
+ recovery: ShareRecoveryRunSummary | null;
68
+ cleanup: CleanupRunSummary | null;
69
+ /** Namespace-scoped expired/consumed receipt nodes deleted this pass. */
70
+ receiptPrune: { pruned: number } | null;
71
+ attachmentStages?: { queued: number } | null;
72
+ categories: Partial<Record<ShareLinkMaintenanceFailureCategory, number>>;
73
+ };
74
+
75
+ /**
76
+ * Allowlisted log event shape. The logger is called with this object only; a
77
+ * throwing logger must never corrupt lifecycle state.
78
+ */
79
+ export type ShareLinkMaintenanceLogEvent = {
80
+ event: 'share_link_maintenance';
81
+ status: ShareLinkMaintenanceStatus;
82
+ durationMs: number;
83
+ budgetMs: number;
84
+ counts: Record<string, number>;
85
+ categories: Record<string, number>;
86
+ };
87
+
88
+ export type ShareLinkMaintenanceLogger = {
89
+ info: (event: ShareLinkMaintenanceLogEvent) => void;
90
+ error: (event: ShareLinkMaintenanceLogEvent) => void;
91
+ };
@@ -0,0 +1,48 @@
1
+ import {
2
+ hasShareLinkServiceTransportConfig,
3
+ resolveShareLinkServiceTransportConfig,
4
+ } from '../share-link-maintenance/config';
5
+
6
+ /**
7
+ * Available automatically once trusted service configuration is complete.
8
+ * Namespace, origin and audience come only from server configuration. Missing
9
+ * wiring is inactive; partial or invalid wiring fails closed.
10
+ */
11
+
12
+ export const SHARE_LINK_OWNER_API_ENV = {
13
+ NAMESPACE: 'SHARE_LINK_OWNER_API_NAMESPACE',
14
+ } as const;
15
+
16
+ export type ShareLinkOwnerApiConfigResolution =
17
+ | { status: 'disabled' }
18
+ | { status: 'invalid' }
19
+ | {
20
+ status: 'enabled';
21
+ namespace: string;
22
+ origin: string;
23
+ audience: string;
24
+ allowInsecureLoopback: boolean;
25
+ };
26
+
27
+ export const resolveShareLinkOwnerApiConfig = (raw: unknown): ShareLinkOwnerApiConfigResolution => {
28
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return { status: 'invalid' };
29
+
30
+ const source = raw as Record<string, unknown>;
31
+ if (!hasShareLinkServiceTransportConfig(source)) return { status: 'disabled' };
32
+
33
+ const transport = resolveShareLinkServiceTransportConfig(source);
34
+ if (transport.status !== 'enabled') return { status: 'invalid' };
35
+
36
+ const ownerNamespace = source[SHARE_LINK_OWNER_API_ENV.NAMESPACE];
37
+ if (ownerNamespace !== undefined && ownerNamespace !== transport.config.namespace) {
38
+ return { status: 'invalid' };
39
+ }
40
+
41
+ return {
42
+ status: 'enabled',
43
+ namespace: transport.config.namespace,
44
+ origin: transport.config.origin,
45
+ audience: transport.config.audience,
46
+ allowInsecureLoopback: transport.config.allowInsecureLoopback,
47
+ };
48
+ };
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Bounded, sanitized diagnostics for share-link service composition.
3
+ *
4
+ * The owner and public APIs fail closed for two states that are otherwise
5
+ * indistinguishable to an operator:
6
+ *
7
+ * - intentionally absent configuration (`status: 'disabled'`) is silent and
8
+ * inert; it is a supported deployment;
9
+ * - malformed/partial service wiring (`status: 'invalid'`) or a failed
10
+ * dependency build emits one fixed category so a misconfiguration is
11
+ * distinguishable from an unconfigured deployment.
12
+ *
13
+ * The reporter is bounded per process: each category is emitted at most once,
14
+ * so a flood of requests against a misconfigured deployment cannot flood logs.
15
+ * The event carries only a fixed allowlisted category string: never an
16
+ * environment value, URL, request body, DID key, title, note or ciphertext.
17
+ */
18
+
19
+ export const SHARE_LINK_COMPOSITION_DIAGNOSTIC = {
20
+ OWNER_CONFIGURATION_INVALID: 'share_link_owner_configuration_invalid',
21
+ OWNER_INITIALIZATION_FAILED: 'share_link_owner_initialization_failed',
22
+ PUBLIC_CONFIGURATION_INVALID: 'share_link_public_configuration_invalid',
23
+ PUBLIC_INITIALIZATION_FAILED: 'share_link_public_initialization_failed',
24
+ } as const;
25
+
26
+ export type ShareLinkCompositionDiagnosticCategory =
27
+ (typeof SHARE_LINK_COMPOSITION_DIAGNOSTIC)[keyof typeof SHARE_LINK_COMPOSITION_DIAGNOSTIC];
28
+
29
+ export type ShareLinkDiagnosticSink = (category: ShareLinkCompositionDiagnosticCategory) => void;
30
+
31
+ /** Aggregate-only sink matching the existing route reporting convention. */
32
+ export const defaultShareLinkDiagnosticSink: ShareLinkDiagnosticSink = category => {
33
+ console.warn('share_link_composition_diagnostic', { category });
34
+ };
35
+
36
+ /** Wrap a sink so each category is emitted at most once per process. */
37
+ export const createBoundedShareLinkDiagnosticReporter = (
38
+ sink: ShareLinkDiagnosticSink = defaultShareLinkDiagnosticSink
39
+ ): ShareLinkDiagnosticSink => {
40
+ const reported = new Set<ShareLinkCompositionDiagnosticCategory>();
41
+
42
+ return category => {
43
+ if (reported.has(category)) return;
44
+ reported.add(category);
45
+ sink(category);
46
+ };
47
+ };
48
+
49
+ type ShareLinkConfigStatus = { status: 'disabled' } | { status: 'invalid' } | { status: 'enabled' };
50
+
51
+ /**
52
+ * Compose the lazy getter a share-link router uses around a pure config
53
+ * resolution and a retryable initializer.
54
+ *
55
+ * Disabled configuration returns `null` silently. Invalid configuration reports
56
+ * a single sanitized diagnostic and returns `null`, preserving the anonymous
57
+ * not_found behavior. An enabled but failing build reports a single sanitized
58
+ * diagnostic and rethrows so the caller maps it to a generic error while the
59
+ * retryable initializer clears the failed attempt cache.
60
+ */
61
+ export const createShareLinkDependencyResolver = <TDependencies>(options: {
62
+ readonly resolveConfig: () => ShareLinkConfigStatus;
63
+ readonly initializeDependencies: () => Promise<TDependencies>;
64
+ readonly reportDiagnostic: ShareLinkDiagnosticSink;
65
+ readonly configurationInvalidCategory: ShareLinkCompositionDiagnosticCategory;
66
+ readonly initializationFailedCategory: ShareLinkCompositionDiagnosticCategory;
67
+ }): (() => Promise<TDependencies | null>) => {
68
+ return async () => {
69
+ const config = options.resolveConfig();
70
+
71
+ // Intentionally absent configuration: inert and silent.
72
+ if (config.status === 'disabled') return null;
73
+
74
+ // Malformed/partial wiring: one bounded diagnostic, still fail closed.
75
+ if (config.status === 'invalid') {
76
+ options.reportDiagnostic(options.configurationInvalidCategory);
77
+ return null;
78
+ }
79
+
80
+ try {
81
+ return await options.initializeDependencies();
82
+ } catch (error) {
83
+ // The initializer has already cleared its failed attempt; report the
84
+ // category once and let the caller surface a sanitized error.
85
+ options.reportDiagnostic(options.initializationFailedCategory);
86
+ throw error;
87
+ }
88
+ };
89
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Retryable lazy initialization for the owner share-link runtime.
3
+ *
4
+ * Successful and in-flight builds are shared across concurrent callers. A
5
+ * *failed* build must never be cached: a transient constraint/graph/signer
6
+ * failure would otherwise poison the owner API until the process restarts
7
+ * (the same defect previously corrected for the C7 setup cache). Only the
8
+ * failed attempt is cleared, so a later request retries while any other
9
+ * in-flight attempt keeps its own shared promise.
10
+ */
11
+ export const createRetryableLazyInitializer = <T>(build: () => Promise<T>): (() => Promise<T>) => {
12
+ let cached: Promise<T> | null = null;
13
+
14
+ return () => {
15
+ if (cached) return cached;
16
+
17
+ const attempt = build();
18
+ cached = attempt;
19
+
20
+ // Preserve the rejection for the awaiting caller, but detach it from the
21
+ // cache so the next call rebuilds. `catch` also prevents an unhandled
22
+ // rejection when nobody is awaiting this particular attempt.
23
+ attempt.catch(() => {
24
+ if (cached === attempt) cached = null;
25
+ });
26
+
27
+ return attempt;
28
+ };
29
+ };
@@ -0,0 +1,54 @@
1
+ import type { ShareLink } from '@learncard/types';
2
+
3
+ import type { ShareLinkRecord } from '../models/ShareLink';
4
+
5
+ /**
6
+ * Explicit owner projection of a `ShareLinkRecord`.
7
+ *
8
+ * This is a deliberate field-by-field mapping, never a spread: the raw record
9
+ * carries internal object refs, operation ids, leases, hashes, byte counts and
10
+ * ciphertext bindings that must never reach a caller. `contentUrl` is
11
+ * intentionally omitted here — the public resolve/content routes own the guarded
12
+ * relative URL, and a LearnCloud object URL (or a fabricated decryption key)
13
+ * must never be exposed.
14
+ *
15
+ * `viewCountingEnabled` is the FRESHLY re-resolved authoritative eligibility for
16
+ * the owner (never the stale `minorPolicyViewCountingEnabled` snapshot alone and
17
+ * never a caller field). When it is not exactly `true`, `viewCount` is omitted
18
+ * and `lastViewedAt` is nulled so a formerly eligible owner's historical counts
19
+ * stop leaking through get/mutation/status/retry.
20
+ */
21
+ export const toOwnerShareLink = (
22
+ record: ShareLinkRecord,
23
+ viewCountingEnabled?: boolean
24
+ ): ShareLink => {
25
+ const countingEnabled = viewCountingEnabled === true;
26
+
27
+ return {
28
+ id: record.id,
29
+ title: record.title,
30
+ ...(record.note === null ? {} : { note: record.note }),
31
+ selectedCount: record.selectedCount,
32
+ version: record.version,
33
+ contentVersion: record.contentVersion,
34
+ attachmentId: record.attachmentId ?? null,
35
+ attachmentChunkCount: record.attachmentChunkCount ?? null,
36
+ status: record.status,
37
+ contentState: record.contentState,
38
+ createdAt: record.createdAt,
39
+ updatedAt: record.updatedAt,
40
+ expiresAt: record.expiresAt,
41
+ stoppedAt: record.stoppedAt,
42
+ ...(countingEnabled
43
+ ? { lastViewedAt: record.lastViewedAt, viewCount: record.viewCount }
44
+ : { lastViewedAt: null }),
45
+ passcodeProtected: record.passcodeHash != null,
46
+ notifyOnView: record.notifyOnView === true,
47
+ minorPolicy: {
48
+ isMinor: record.minorPolicyIsMinor,
49
+ policyResolved: record.minorPolicyResolved,
50
+ defaultExpiryDays: record.minorPolicyDefaultExpiryDays === 365 ? 365 : 30,
51
+ viewCountingEnabled: record.minorPolicyViewCountingEnabled,
52
+ },
53
+ };
54
+ };
@@ -0,0 +1,36 @@
1
+ import cache from '@cache';
2
+ import { environment } from '@environment';
3
+
4
+ // The short shared window limits distributed guessing while bounding the time
5
+ // an attacker can deny access to a legitimate recipient. Per-source limits
6
+ // preserve capacity for other recipients until the shared budget is exhausted.
7
+ const WINDOW_SECONDS = 60;
8
+ // A successful recipient view checks the passcode at resolve and content, so
9
+ // it spends two attempts. Keep that in mind before lowering these budgets.
10
+ const SHARE_ATTEMPT_LIMIT = 24;
11
+ const SOURCE_ATTEMPT_LIMIT = 6;
12
+
13
+ const keys = (namespace: string, shareId: string, sourceIp?: string): [string, string] => [
14
+ `share-link-passcode-attempt:${namespace}:${shareId}`,
15
+ `share-link-passcode-attempt-source:${namespace}:${shareId}:${sourceIp ?? 'unknown'}`,
16
+ ];
17
+
18
+ export const reserveSharePasscodeAttempt = async (
19
+ namespace: string,
20
+ shareId: string,
21
+ sourceIp?: string
22
+ ): Promise<boolean> => {
23
+ // Process-local fallback counters cannot stop rotated-IP guesses across
24
+ // production workers. Protected access requires the shared Redis counter.
25
+ if (environment.NODE_ENV === 'production' && !cache.redis) return false;
26
+ const [shareKey, sourceKey] = keys(namespace, shareId, sourceIp);
27
+ // Reserve before Argon2. Redis INCR is atomic across Lambda instances, so
28
+ // simultaneous guesses cannot all read a stale budget. Count successful
29
+ // attempts too; no cross-key refund race and no stored submitted value.
30
+ const sourceCount = await cache.incr(sourceKey, WINDOW_SECONDS);
31
+ if (sourceCount === undefined || sourceCount > SOURCE_ATTEMPT_LIMIT) return false;
32
+ // Rejected requests from this source never consume another recipient's
33
+ // share-wide budget. Both reservations still precede Argon2.
34
+ const shareCount = await cache.incr(shareKey, WINDOW_SECONDS);
35
+ return shareCount !== undefined && shareCount <= SHARE_ATTEMPT_LIMIT;
36
+ };
@@ -0,0 +1,47 @@
1
+ import { randomBytes } from 'node:crypto';
2
+
3
+ import { argon2id, argon2Verify } from 'hash-wasm';
4
+
5
+ const ARGON2_MEMORY_KIB = 19_456;
6
+ const ARGON2_ITERATIONS = 2;
7
+ const ARGON2_PARALLELISM = 1;
8
+ const ARGON2_HASH_BYTES = 32;
9
+ const MAX_CONCURRENT_VERIFICATIONS = 4;
10
+ let activeVerifications = 0;
11
+
12
+ export class SharePasscodeCapacityError extends Error {
13
+ constructor() {
14
+ super('share passcode verification capacity exhausted');
15
+ this.name = 'SharePasscodeCapacityError';
16
+ }
17
+ }
18
+
19
+ /** Hash a share passcode as an Argon2id PHC string with a fresh 128-bit salt. */
20
+ export const hashSharePasscode = async (passcode: string): Promise<string> =>
21
+ argon2id({
22
+ password: passcode,
23
+ salt: randomBytes(16),
24
+ iterations: ARGON2_ITERATIONS,
25
+ memorySize: ARGON2_MEMORY_KIB,
26
+ parallelism: ARGON2_PARALLELISM,
27
+ hashLength: ARGON2_HASH_BYTES,
28
+ outputType: 'encoded',
29
+ });
30
+
31
+ /** Verify without ever logging, returning, or persisting the plaintext passcode. */
32
+ export const verifySharePasscode = async (
33
+ passcodeHash: string,
34
+ passcode: string
35
+ ): Promise<boolean> => {
36
+ // Local defense for long-lived workers; Lambda's cross-instance budget is
37
+ // reserved in Redis before this function runs. Capacity is not a wrong code.
38
+ if (activeVerifications >= MAX_CONCURRENT_VERIFICATIONS) throw new SharePasscodeCapacityError();
39
+ activeVerifications += 1;
40
+ try {
41
+ return await argon2Verify({ password: passcode, hash: passcodeHash });
42
+ } catch {
43
+ return false;
44
+ } finally {
45
+ activeVerifications -= 1;
46
+ }
47
+ };
@@ -0,0 +1,7 @@
1
+ export * from './types';
2
+ export {
3
+ composeShareLinkPolicy,
4
+ createShareLinkPolicyResolver,
5
+ resolveShareLinkExpiry,
6
+ } from './resolver';
7
+ export { createProductionShareLinkPolicySource } from './production';