@learncard/network-brain-service 4.1.3 → 5.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 (375) hide show
  1. package/.env.example +14 -0
  2. package/CHANGELOG.md +143 -0
  3. package/README.md +27 -1
  4. package/bootstrapLambda.test.ts +70 -32
  5. package/config/config.dev.json +1 -0
  6. package/config/config.example.json +2 -1
  7. package/config/config.production.json +1 -0
  8. package/config/config.scouts.dev.json +1 -0
  9. package/config/config.scouts.production.json +1 -0
  10. package/consentFlowDeadlineLambda.test.ts +108 -0
  11. package/consentFlowDeadlineLambda.ts +18 -0
  12. package/consentFlowDeadlineLambdaApp.ts +14 -0
  13. package/dist/accesslayer/boost/create.d.ts +8 -2
  14. package/dist/accesslayer/boost/create.d.ts.map +1 -1
  15. package/dist/accesslayer/campaign/audience.d.ts +4 -4
  16. package/dist/accesslayer/campaign/audience.d.ts.map +1 -1
  17. package/dist/accesslayer/campaign/create.d.ts +1 -1
  18. package/dist/accesslayer/campaign/create.d.ts.map +1 -1
  19. package/dist/accesslayer/campaign/delivery.d.ts +5 -0
  20. package/dist/accesslayer/campaign/delivery.d.ts.map +1 -0
  21. package/dist/accesslayer/campaign/dispatch.d.ts +5 -0
  22. package/dist/accesslayer/campaign/dispatch.d.ts.map +1 -0
  23. package/dist/accesslayer/campaign/dispatch.test.d.ts +2 -0
  24. package/dist/accesslayer/campaign/dispatch.test.d.ts.map +1 -0
  25. package/dist/accesslayer/campaign/email.d.ts +10 -0
  26. package/dist/accesslayer/campaign/email.d.ts.map +1 -0
  27. package/dist/accesslayer/campaign/email.test.d.ts +2 -0
  28. package/dist/accesslayer/campaign/email.test.d.ts.map +1 -0
  29. package/dist/accesslayer/campaign/preferences.d.ts +5 -0
  30. package/dist/accesslayer/campaign/preferences.d.ts.map +1 -0
  31. package/dist/accesslayer/campaign/read.d.ts.map +1 -1
  32. package/dist/accesslayer/campaign/test.d.ts +5 -0
  33. package/dist/accesslayer/campaign/test.d.ts.map +1 -0
  34. package/dist/accesslayer/campaign/test.test.d.ts +2 -0
  35. package/dist/accesslayer/campaign/test.test.d.ts.map +1 -0
  36. package/dist/accesslayer/campaign/update.d.ts.map +1 -1
  37. package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
  38. package/dist/accesslayer/consentflowcontract/delete.d.ts +1 -1
  39. package/dist/accesslayer/consentflowcontract/delete.d.ts.map +1 -1
  40. package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
  41. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +2 -1
  42. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  43. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +19 -4
  44. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  45. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +6 -1
  46. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  47. package/dist/accesslayer/consentflowcontract/versions.d.ts +68 -0
  48. package/dist/accesslayer/consentflowcontract/versions.d.ts.map +1 -0
  49. package/dist/accesslayer/credential/create.d.ts +3 -1
  50. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  51. package/dist/accesslayer/credential/delete.d.ts +1 -0
  52. package/dist/accesslayer/credential/delete.d.ts.map +1 -1
  53. package/dist/accesslayer/credential/relationships/create.d.ts +15 -3
  54. package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
  55. package/dist/accesslayer/credential/relationships/read.d.ts +2 -0
  56. package/dist/accesslayer/credential/relationships/read.d.ts.map +1 -1
  57. package/dist/accesslayer/credential/relationships/update.d.ts.map +1 -1
  58. package/dist/accesslayer/credential-refresh/read.d.ts +1 -0
  59. package/dist/accesslayer/credential-refresh/read.d.ts.map +1 -1
  60. package/dist/accesslayer/credential-refresh/update.d.ts.map +1 -1
  61. package/dist/accesslayer/inbox-credential/create.d.ts +1 -0
  62. package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
  63. package/dist/accesslayer/inbox-credential/refresh.d.ts +7 -0
  64. package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -1
  65. package/dist/app.d.ts +11 -1
  66. package/dist/app.d.ts.map +1 -1
  67. package/dist/cache/app-store.caches.d.ts +61 -1
  68. package/dist/cache/app-store.caches.d.ts.map +1 -1
  69. package/dist/campaign-unsubscribe.d.ts +7 -0
  70. package/dist/campaign-unsubscribe.d.ts.map +1 -0
  71. package/dist/campaign-unsubscribe.test.d.ts +2 -0
  72. package/dist/campaign-unsubscribe.test.d.ts.map +1 -0
  73. package/dist/config/environment.d.ts +42 -0
  74. package/dist/config/environment.d.ts.map +1 -1
  75. package/dist/config/stageConfig.d.ts +4 -0
  76. package/dist/config/stageConfig.d.ts.map +1 -1
  77. package/dist/credential-refresh.d.ts.map +1 -1
  78. package/dist/helpers/boost.helpers.d.ts +4 -1
  79. package/dist/helpers/boost.helpers.d.ts.map +1 -1
  80. package/dist/helpers/campaign-app-origin.helpers.d.ts +2 -0
  81. package/dist/helpers/campaign-app-origin.helpers.d.ts.map +1 -0
  82. package/dist/helpers/campaign-email.helpers.d.ts +13 -0
  83. package/dist/helpers/campaign-email.helpers.d.ts.map +1 -0
  84. package/dist/helpers/campaign-notifications.helpers.d.ts +7 -0
  85. package/dist/helpers/campaign-notifications.helpers.d.ts.map +1 -0
  86. package/dist/helpers/campaign-queue.helpers.d.ts +7 -0
  87. package/dist/helpers/campaign-queue.helpers.d.ts.map +1 -0
  88. package/dist/helpers/campaign-queue.helpers.test.d.ts +2 -0
  89. package/dist/helpers/campaign-queue.helpers.test.d.ts.map +1 -0
  90. package/dist/helpers/campaign-unsubscribe-page.d.ts +2 -0
  91. package/dist/helpers/campaign-unsubscribe-page.d.ts.map +1 -0
  92. package/dist/helpers/campaign-unsubscribe.helpers.d.ts +7 -0
  93. package/dist/helpers/campaign-unsubscribe.helpers.d.ts.map +1 -0
  94. package/dist/helpers/campaign-webhook.helpers.test.d.ts +2 -0
  95. package/dist/helpers/campaign-webhook.helpers.test.d.ts.map +1 -0
  96. package/dist/helpers/consentflow-deadline.helpers.d.ts +5 -0
  97. package/dist/helpers/consentflow-deadline.helpers.d.ts.map +1 -0
  98. package/dist/helpers/consentflow-legacy-migration.helpers.d.ts +2 -0
  99. package/dist/helpers/consentflow-legacy-migration.helpers.d.ts.map +1 -0
  100. package/dist/helpers/contract-events.helpers.d.ts.map +1 -1
  101. package/dist/helpers/contract.helpers.d.ts +1 -1
  102. package/dist/helpers/contract.helpers.d.ts.map +1 -1
  103. package/dist/helpers/credential-display.helpers.d.ts +5 -0
  104. package/dist/helpers/credential-display.helpers.d.ts.map +1 -0
  105. package/dist/helpers/credential-display.helpers.test.d.ts +2 -0
  106. package/dist/helpers/credential-display.helpers.test.d.ts.map +1 -0
  107. package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -1
  108. package/dist/helpers/credential-refresh-materiality.helpers.d.ts +4 -0
  109. package/dist/helpers/credential-refresh-materiality.helpers.d.ts.map +1 -1
  110. package/dist/helpers/credential-refresh.helpers.d.ts +30 -1
  111. package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
  112. package/dist/helpers/credential-status-mutation.helpers.d.ts +5 -0
  113. package/dist/helpers/credential-status-mutation.helpers.d.ts.map +1 -0
  114. package/dist/helpers/credential-status-mutation.helpers.test.d.ts +2 -0
  115. package/dist/helpers/credential-status-mutation.helpers.test.d.ts.map +1 -0
  116. package/dist/helpers/credential.helpers.d.ts +1 -1
  117. package/dist/helpers/credential.helpers.d.ts.map +1 -1
  118. package/dist/helpers/inbox-refresh.helpers.d.ts +4 -2
  119. package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -1
  120. package/dist/helpers/inbox.helpers.d.ts +1 -0
  121. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  122. package/dist/helpers/learnCard.helpers.d.ts +1 -0
  123. package/dist/helpers/learnCard.helpers.d.ts.map +1 -1
  124. package/dist/helpers/managed-status.helpers.d.ts +7 -0
  125. package/dist/helpers/managed-status.helpers.d.ts.map +1 -0
  126. package/dist/helpers/managed-status.helpers.test.d.ts +2 -0
  127. package/dist/helpers/managed-status.helpers.test.d.ts.map +1 -0
  128. package/dist/helpers/notification-delivery-boundary.helpers.test.d.ts +2 -0
  129. package/dist/helpers/notification-delivery-boundary.helpers.test.d.ts.map +1 -0
  130. package/dist/helpers/notification-webhook-fetch.helpers.d.ts +13 -0
  131. package/dist/helpers/notification-webhook-fetch.helpers.d.ts.map +1 -0
  132. package/dist/helpers/notification-webhook-fetch.helpers.test.d.ts +2 -0
  133. package/dist/helpers/notification-webhook-fetch.helpers.test.d.ts.map +1 -0
  134. package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
  135. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  136. package/dist/helpers/refresh-send-intent.helpers.d.ts +13 -5
  137. package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -1
  138. package/dist/helpers/refresh-send-intent.helpers.test.d.ts +2 -0
  139. package/dist/helpers/refresh-send-intent.helpers.test.d.ts.map +1 -0
  140. package/dist/helpers/send-contract.helpers.d.ts +11 -0
  141. package/dist/helpers/send-contract.helpers.d.ts.map +1 -0
  142. package/dist/helpers/status-list.helpers.d.ts +12 -1
  143. package/dist/helpers/status-list.helpers.d.ts.map +1 -1
  144. package/dist/index.js +2 -2
  145. package/dist/models/ConsentFlowContract.d.ts.map +1 -1
  146. package/dist/models/ConsentFlowContractVersion.d.ts +12 -0
  147. package/dist/models/ConsentFlowContractVersion.d.ts.map +1 -0
  148. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  149. package/dist/models/Credential.d.ts +2 -0
  150. package/dist/models/Credential.d.ts.map +1 -1
  151. package/dist/models/CredentialRefresh.d.ts +5 -0
  152. package/dist/models/CredentialRefresh.d.ts.map +1 -1
  153. package/dist/models/Integration.d.ts +1 -1
  154. package/dist/models/Profile.d.ts.map +1 -1
  155. package/dist/models/campaign-constraints.d.ts.map +1 -1
  156. package/dist/models/index.d.ts +1 -0
  157. package/dist/models/index.d.ts.map +1 -1
  158. package/dist/openapi.d.ts.map +1 -1
  159. package/dist/publish-metafile.json +1 -1
  160. package/dist/routes/activity.d.ts +15 -5
  161. package/dist/routes/activity.d.ts.map +1 -1
  162. package/dist/routes/app-store.d.ts +15 -5
  163. package/dist/routes/app-store.d.ts.map +1 -1
  164. package/dist/routes/auth-grants.d.ts +12 -2
  165. package/dist/routes/auth-grants.d.ts.map +1 -1
  166. package/dist/routes/bench.d.ts +11 -1
  167. package/dist/routes/bench.d.ts.map +1 -1
  168. package/dist/routes/boosts.d.ts +70 -15
  169. package/dist/routes/boosts.d.ts.map +1 -1
  170. package/dist/routes/campaigns.d.ts +183 -51
  171. package/dist/routes/campaigns.d.ts.map +1 -1
  172. package/dist/routes/claim-hooks.d.ts +11 -1
  173. package/dist/routes/claim-hooks.d.ts.map +1 -1
  174. package/dist/routes/contact-methods.d.ts +11 -1
  175. package/dist/routes/contact-methods.d.ts.map +1 -1
  176. package/dist/routes/contracts.d.ts +374 -8
  177. package/dist/routes/contracts.d.ts.map +1 -1
  178. package/dist/routes/credential-input-privacy.test.d.ts +2 -0
  179. package/dist/routes/credential-input-privacy.test.d.ts.map +1 -0
  180. package/dist/routes/credential-refreshes.d.ts +210 -1
  181. package/dist/routes/credential-refreshes.d.ts.map +1 -1
  182. package/dist/routes/credential-share-previews.d.ts +11 -1
  183. package/dist/routes/credential-share-previews.d.ts.map +1 -1
  184. package/dist/routes/credentials.d.ts +132 -3
  185. package/dist/routes/credentials.d.ts.map +1 -1
  186. package/dist/routes/did-metadata.d.ts +11 -1
  187. package/dist/routes/did-metadata.d.ts.map +1 -1
  188. package/dist/routes/federation.d.ts +11 -1
  189. package/dist/routes/federation.d.ts.map +1 -1
  190. package/dist/routes/inbox.d.ts +54 -19
  191. package/dist/routes/inbox.d.ts.map +1 -1
  192. package/dist/routes/index.d.ts +100 -1
  193. package/dist/routes/index.d.ts.map +1 -1
  194. package/dist/routes/integrations.d.ts +14 -4
  195. package/dist/routes/integrations.d.ts.map +1 -1
  196. package/dist/routes/presentations.d.ts +11 -1
  197. package/dist/routes/presentations.d.ts.map +1 -1
  198. package/dist/routes/profile-manager.d.ts +11 -1
  199. package/dist/routes/profile-manager.d.ts.map +1 -1
  200. package/dist/routes/profiles.d.ts +11 -1
  201. package/dist/routes/profiles.d.ts.map +1 -1
  202. package/dist/routes/public-share-links.d.ts +22 -2
  203. package/dist/routes/public-share-links.d.ts.map +1 -1
  204. package/dist/routes/share-links.d.ts +36 -16
  205. package/dist/routes/share-links.d.ts.map +1 -1
  206. package/dist/routes/skill-frameworks.d.ts +11 -1
  207. package/dist/routes/skill-frameworks.d.ts.map +1 -1
  208. package/dist/routes/skills.d.ts +11 -1
  209. package/dist/routes/skills.d.ts.map +1 -1
  210. package/dist/routes/storage.d.ts +37 -23
  211. package/dist/routes/storage.d.ts.map +1 -1
  212. package/dist/routes/test.d.ts +79 -3
  213. package/dist/routes/test.d.ts.map +1 -1
  214. package/dist/routes/utilities.d.ts +11 -1
  215. package/dist/routes/utilities.d.ts.map +1 -1
  216. package/dist/routes/workflows.d.ts +11 -1
  217. package/dist/routes/workflows.d.ts.map +1 -1
  218. package/dist/services/delivery/adapters/postmark.adapter.d.ts +3 -0
  219. package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
  220. package/dist/services/delivery/delivery.service.d.ts +10 -0
  221. package/dist/services/delivery/delivery.service.d.ts.map +1 -1
  222. package/dist/types/activity.d.ts +3 -3
  223. package/dist/types/app-manifest.d.ts +10 -10
  224. package/dist/types/campaign.d.ts +13 -1
  225. package/dist/types/campaign.d.ts.map +1 -1
  226. package/dist/types/consentflowcontract.d.ts +175 -7
  227. package/dist/types/consentflowcontract.d.ts.map +1 -1
  228. package/dist/types/credential-refresh.d.ts +27 -0
  229. package/dist/types/credential-refresh.d.ts.map +1 -1
  230. package/dist/types/credential.d.ts +5 -0
  231. package/dist/types/credential.d.ts.map +1 -1
  232. package/dist/types/integration.d.ts +3 -3
  233. package/lambda.ts +44 -14
  234. package/lambdaApp.ts +20 -0
  235. package/package.json +20 -19
  236. package/project.json +5 -0
  237. package/scripts/migrate-consent-flow-versions.ts +9 -0
  238. package/scripts/report-send-template-exposure.ts +47 -0
  239. package/scripts/verify-python-contracts.ts +16 -1
  240. package/serverless-local.yml +15 -2
  241. package/serverless.function-env.cjs +4 -0
  242. package/serverless.local.yml +12 -0
  243. package/serverless.yml +48 -3
  244. package/src/accesslayer/boost/create.ts +62 -7
  245. package/src/accesslayer/campaign/README.md +275 -6
  246. package/src/accesslayer/campaign/audience.ts +39 -7
  247. package/src/accesslayer/campaign/create.ts +4 -2
  248. package/src/accesslayer/campaign/delivery.ts +66 -0
  249. package/src/accesslayer/campaign/dispatch.test.ts +59 -0
  250. package/src/accesslayer/campaign/dispatch.ts +172 -0
  251. package/src/accesslayer/campaign/email.test.ts +73 -0
  252. package/src/accesslayer/campaign/email.ts +91 -0
  253. package/src/accesslayer/campaign/preferences.ts +53 -0
  254. package/src/accesslayer/campaign/read.ts +3 -1
  255. package/src/accesslayer/campaign/state.ts +1 -1
  256. package/src/accesslayer/campaign/test.test.ts +31 -0
  257. package/src/accesslayer/campaign/test.ts +75 -0
  258. package/src/accesslayer/campaign/update.ts +15 -4
  259. package/src/accesslayer/consentflowcontract/create.ts +20 -1
  260. package/src/accesslayer/consentflowcontract/delete.ts +121 -3
  261. package/src/accesslayer/consentflowcontract/read.ts +15 -9
  262. package/src/accesslayer/consentflowcontract/relationships/create.ts +70 -36
  263. package/src/accesslayer/consentflowcontract/relationships/read.ts +305 -73
  264. package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +1 -1
  265. package/src/accesslayer/consentflowcontract/relationships/update.ts +269 -98
  266. package/src/accesslayer/consentflowcontract/versions.ts +790 -0
  267. package/src/accesslayer/credential/create.ts +33 -9
  268. package/src/accesslayer/credential/delete.ts +45 -0
  269. package/src/accesslayer/credential/relationships/create.ts +102 -47
  270. package/src/accesslayer/credential/relationships/update.ts +8 -116
  271. package/src/accesslayer/credential-refresh/read.ts +26 -2
  272. package/src/accesslayer/credential-refresh/update.ts +6 -2
  273. package/src/accesslayer/inbox-credential/create.ts +7 -1
  274. package/src/accesslayer/inbox-credential/refresh.ts +14 -4
  275. package/src/campaign-unsubscribe.test.ts +158 -0
  276. package/src/campaign-unsubscribe.ts +86 -0
  277. package/src/config/environment.test.ts +52 -0
  278. package/src/config/environment.ts +58 -0
  279. package/src/config/serverlessFunctionEnv.test.ts +59 -0
  280. package/src/credential-refresh.ts +1 -0
  281. package/src/docker-entry.ts +26 -1
  282. package/src/helpers/boost-storage.helpers.test.ts +107 -41
  283. package/src/helpers/boost.helpers.ts +88 -40
  284. package/src/helpers/campaign-app-origin.helpers.ts +25 -0
  285. package/src/helpers/campaign-email.helpers.ts +113 -0
  286. package/src/helpers/campaign-notifications.helpers.ts +127 -0
  287. package/src/helpers/campaign-queue.helpers.test.ts +442 -0
  288. package/src/helpers/campaign-queue.helpers.ts +122 -0
  289. package/src/helpers/campaign-unsubscribe-page.ts +107 -0
  290. package/src/helpers/campaign-unsubscribe.helpers.ts +56 -0
  291. package/src/helpers/campaign-webhook.helpers.test.ts +137 -0
  292. package/src/helpers/consentflow-deadline.helpers.ts +22 -0
  293. package/src/helpers/consentflow-legacy-migration.helpers.ts +179 -0
  294. package/src/helpers/contract-events.helpers.ts +35 -3
  295. package/src/helpers/contract-requests.helpers.ts +1 -1
  296. package/src/helpers/contract.helpers.ts +5 -1
  297. package/src/helpers/credential-display.helpers.test.ts +68 -0
  298. package/src/helpers/credential-display.helpers.ts +15 -0
  299. package/src/helpers/credential-refresh-email.helpers.ts +5 -12
  300. package/src/helpers/credential-refresh-materiality.helpers.test.ts +138 -1
  301. package/src/helpers/credential-refresh-materiality.helpers.ts +62 -1
  302. package/src/helpers/credential-refresh-proof.helpers.test.ts +1 -1
  303. package/src/helpers/credential-refresh-proof.helpers.ts +2 -2
  304. package/src/helpers/credential-refresh.helpers.ts +1166 -376
  305. package/src/helpers/credential-status-mutation.helpers.test.ts +155 -0
  306. package/src/helpers/credential-status-mutation.helpers.ts +166 -0
  307. package/src/helpers/credential.helpers.ts +19 -11
  308. package/src/helpers/finalize-inbox.helpers.ts +1 -1
  309. package/src/helpers/inbox-refresh.helpers.ts +34 -8
  310. package/src/helpers/inbox.helpers.ts +2 -0
  311. package/src/helpers/learnCard.helpers.ts +23 -0
  312. package/src/helpers/managed-status.helpers.test.ts +101 -0
  313. package/src/helpers/managed-status.helpers.ts +147 -0
  314. package/src/helpers/notification-delivery-boundary.helpers.test.ts +114 -0
  315. package/src/helpers/notification-webhook-fetch.helpers.test.ts +228 -0
  316. package/src/helpers/notification-webhook-fetch.helpers.ts +166 -0
  317. package/src/helpers/notificationQueue.helpers.ts +5 -0
  318. package/src/helpers/notifications.helpers.ts +19 -12
  319. package/src/helpers/refresh-send-intent.helpers.test.ts +60 -0
  320. package/src/helpers/refresh-send-intent.helpers.ts +81 -18
  321. package/src/helpers/send-contract.helpers.ts +36 -0
  322. package/src/helpers/status-list.helpers.test.ts +29 -12
  323. package/src/helpers/status-list.helpers.ts +329 -20
  324. package/src/models/ConsentFlowContract.ts +3 -0
  325. package/src/models/ConsentFlowContractVersion.ts +50 -0
  326. package/src/models/ConsentFlowTerms.ts +4 -0
  327. package/src/models/Credential.ts +2 -0
  328. package/src/models/CredentialRefresh.ts +2 -0
  329. package/src/models/Profile.ts +2 -0
  330. package/src/models/campaign-constraints.ts +13 -8
  331. package/src/models/index.ts +2 -0
  332. package/src/openapi.ts +21 -0
  333. package/src/routes/app-store.ts +2 -1
  334. package/src/routes/boosts.ts +219 -137
  335. package/src/routes/campaigns.ts +98 -19
  336. package/src/routes/contracts.ts +433 -115
  337. package/src/routes/credential-input-privacy.test.ts +92 -0
  338. package/src/routes/credential-refreshes.ts +273 -2
  339. package/src/routes/federation.ts +7 -2
  340. package/src/routes/inbox.ts +6 -1
  341. package/src/routes/index.ts +31 -2
  342. package/src/routes/workflows.ts +61 -46
  343. package/src/services/delivery/adapters/postmark.adapter.test.ts +70 -1
  344. package/src/services/delivery/adapters/postmark.adapter.ts +84 -11
  345. package/src/services/delivery/delivery.service.ts +4 -0
  346. package/src/types/campaign.ts +14 -1
  347. package/src/types/consentflowcontract.ts +43 -2
  348. package/src/types/credential-refresh.ts +11 -1
  349. package/src/types/credential.ts +2 -0
  350. package/test/boost-status-cleanup.spec.ts +123 -0
  351. package/test/campaigns-email.unit.spec.ts +268 -0
  352. package/test/campaigns.spec.ts +804 -30
  353. package/test/consent-autoboost-idempotency.spec.ts +432 -0
  354. package/test/consent-data-boundaries.spec.ts +322 -26
  355. package/test/consent-legacy-migration.spec.ts +315 -0
  356. package/test/consent-recipients.spec.ts +7 -7
  357. package/test/consent-version-updates.spec.ts +761 -0
  358. package/test/consentflow.spec.ts +698 -226
  359. package/test/contract-requests.spec.ts +55 -9
  360. package/test/credential-refresh-allocation.spec.ts +212 -14
  361. package/test/credential-refresh-email.spec.ts +6 -10
  362. package/test/credential-refresh-endpoint.spec.ts +3 -0
  363. package/test/credential-refresh-publication.spec.ts +52 -15
  364. package/test/credential-storage.neo4j.spec.ts +33 -6
  365. package/test/helpers/legacy-autoboost.ts +48 -0
  366. package/test/holder-mode-send.spec.ts +710 -0
  367. package/test/inbox-refresh.spec.ts +105 -0
  368. package/test/inbox-security.e2e.ts +18 -22
  369. package/test/managed-status-revocation.spec.ts +970 -0
  370. package/test/notifications-worker.spec.ts +31 -25
  371. package/test/refresh-send.spec.ts +334 -5
  372. package/test/smart-resume-consent.spec.ts +1 -1
  373. package/test/verification/campaigns.ts +40 -0
  374. package/vitest.campaigns.config.ts +2 -0
  375. package/vitest.config.ts +14 -0
@@ -0,0 +1,315 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { neogma } from '@instance';
4
+ import { getIdFromUri } from '@helpers/uri.helpers';
5
+ import { migrateLegacyConsentFlowContracts } from '@helpers/consentflow-legacy-migration.helpers';
6
+ import { getConsentFlowContractVersionRecord } from '@accesslayer/consentflowcontract/versions';
7
+ import { getUser } from './helpers/getClient';
8
+ import { normalContract, normalFullTerms } from './helpers/contract';
9
+
10
+ const recoveryFixture = async () => {
11
+ const suffix = randomBytes(5).toString('hex');
12
+ const owner = await getUser(randomBytes(32).toString('hex'));
13
+ const learner = await getUser(randomBytes(32).toString('hex'));
14
+ await owner.clients.fullAuth.profile.createProfile({ profileId: `repair-owner-${suffix}` });
15
+ await learner.clients.fullAuth.profile.createProfile({ profileId: `repair-user-${suffix}` });
16
+ const uri = await owner.clients.fullAuth.contracts.createConsentFlowContract({
17
+ name: 'Post-marker legacy contract',
18
+ contract: normalContract,
19
+ });
20
+ const untouchedUri = await owner.clients.fullAuth.contracts.createConsentFlowContract({
21
+ name: 'Unrelated legacy contract',
22
+ contract: normalContract,
23
+ });
24
+ const { termsUri } = await learner.clients.fullAuth.contracts.consentToContract({
25
+ contractUri: uri,
26
+ terms: normalFullTerms,
27
+ });
28
+ const { termsUri: untouchedTermsUri } =
29
+ await learner.clients.fullAuth.contracts.consentToContract({
30
+ contractUri: untouchedUri,
31
+ terms: normalFullTerms,
32
+ });
33
+ const id = getIdFromUri(uri);
34
+ const untouchedId = getIdFromUri(untouchedUri);
35
+ const markerAt = '2026-01-01T00:00:00.000Z';
36
+ await neogma.queryRunner.run(
37
+ `MATCH (contract:ConsentFlowContract)-[:HAS_VERSION]->(version:ConsentFlowContractVersion)
38
+ WHERE contract.id IN $ids
39
+ DETACH DELETE version
40
+ REMOVE contract.currentVersion
41
+ WITH contract
42
+ MATCH (terms:ConsentFlowTerms)-[:CONSENTS_TO]->(contract)
43
+ REMOVE terms.consentedVersion
44
+ FOREACH (_ IN CASE WHEN contract.id=$untouchedId THEN [1] ELSE [] END |
45
+ SET terms.status='stale', terms.oneTime=true)`,
46
+ { ids: [id, untouchedId], untouchedId }
47
+ );
48
+ await neogma.queryRunner.run(
49
+ `MERGE (migration:ConsentFlowContractMigration {id:'legacy-v1'})
50
+ SET migration.completedAt=$markerAt`,
51
+ { markerAt }
52
+ );
53
+ const assertUnrelatedUnchanged = async () => {
54
+ const result = await neogma.queryRunner.run(
55
+ `MATCH (contract:ConsentFlowContract {id:$id})<-[:CONSENTS_TO]-(terms:ConsentFlowTerms)
56
+ MATCH (migration:ConsentFlowContractMigration {id:'legacy-v1'})
57
+ RETURN contract.currentVersion AS version, terms.consentedVersion AS accepted,
58
+ terms.status AS status, migration.completedAt AS marker,
59
+ COUNT { (contract)-[:HAS_VERSION]->() } AS snapshots`,
60
+ { id: untouchedId }
61
+ );
62
+ expect(result.records[0].get('version')).toBeNull();
63
+ expect(result.records[0].get('accepted')).toBeNull();
64
+ expect(result.records[0].get('status')).toBe('stale');
65
+ expect(Number(result.records[0].get('snapshots'))).toBe(0);
66
+ expect(result.records[0].get('marker')).toBe(markerAt);
67
+ };
68
+ const cleanup = async () => {
69
+ await neogma.queryRunner.run(
70
+ `MATCH (contract:ConsentFlowContract) WHERE contract.id IN $ids
71
+ OPTIONAL MATCH (contract)-[:HAS_VERSION]->(version:ConsentFlowContractVersion)
72
+ OPTIONAL MATCH (terms:ConsentFlowTerms)-[:CONSENTS_TO]->(contract)
73
+ DETACH DELETE version, terms, contract`,
74
+ { ids: [id, untouchedId] }
75
+ );
76
+ await neogma.queryRunner.run(
77
+ `MATCH (migration:ConsentFlowContractMigration {id:'legacy-v1'}) DELETE migration`
78
+ );
79
+ };
80
+ return { owner, uri, id, termsUri, untouchedTermsUri, assertUnrelatedUnchanged, cleanup };
81
+ };
82
+
83
+ describe('legacy contract startup migration', () => {
84
+ it('dates the captured legacy revision from its last modification, not its original creation', async () => {
85
+ const fixture = await recoveryFixture();
86
+ const updatedAt = '2026-02-01T00:00:00.000Z';
87
+ try {
88
+ await neogma.queryRunner.run(
89
+ `MATCH (contract:ConsentFlowContract {id:$id})
90
+ SET contract.createdAt='2020-01-01T00:00:00.000Z', contract.updatedAt=$updatedAt`,
91
+ { id: fixture.id, updatedAt }
92
+ );
93
+ const snapshot = await getConsentFlowContractVersionRecord(fixture.id, 1);
94
+ expect(snapshot?.createdAt).toBe(updatedAt);
95
+ expect(snapshot?.contract).toEqual(normalContract);
96
+ await fixture.assertUnrelatedUnchanged();
97
+ } finally {
98
+ await fixture.cleanup();
99
+ }
100
+ });
101
+
102
+ it('backfills healthy data once while isolating malformed historical contracts', async () => {
103
+ const suffix = randomBytes(5).toString('hex');
104
+ const owner = await getUser(randomBytes(32).toString('hex'));
105
+ const learner = await getUser(randomBytes(32).toString('hex'));
106
+ await owner.clients.fullAuth.profile.createProfile({
107
+ profileId: `migration-owner-${suffix}`,
108
+ });
109
+ await learner.clients.fullAuth.profile.createProfile({
110
+ profileId: `migration-user-${suffix}`,
111
+ });
112
+ const uri = await owner.clients.fullAuth.contracts.createConsentFlowContract({
113
+ name: 'Migration QA',
114
+ contract: normalContract,
115
+ });
116
+ const malformedUri = await owner.clients.fullAuth.contracts.createConsentFlowContract({
117
+ name: 'Malformed historical request',
118
+ contract: normalContract,
119
+ });
120
+ const { termsUri } = await learner.clients.fullAuth.contracts.consentToContract({
121
+ contractUri: uri,
122
+ terms: normalFullTerms,
123
+ });
124
+ const id = getIdFromUri(uri);
125
+ const malformedId = getIdFromUri(malformedUri);
126
+ await neogma.queryRunner.run(
127
+ `MATCH (contract:ConsentFlowContract)-[relationship:HAS_VERSION]->(version:ConsentFlowContractVersion)
128
+ WHERE contract.id IN $ids
129
+ DELETE relationship, version
130
+ SET contract.currentVersion=null
131
+ WITH contract
132
+ FOREACH (_ IN CASE WHEN contract.id=$malformedId THEN [1] ELSE [] END |
133
+ SET contract['contract.read.credentials.categories.Achievement.required']='invalid')`,
134
+ { ids: [id, malformedId], malformedId }
135
+ );
136
+ await neogma.queryRunner.run(
137
+ `MATCH (migration:ConsentFlowContractMigration {id:'legacy-v1'}) DELETE migration`
138
+ );
139
+ await neogma.queryRunner.run(
140
+ `MATCH (terms:ConsentFlowTerms {id:$id}) SET terms.consentedVersion=null`,
141
+ { id: getIdFromUri(termsUri) }
142
+ );
143
+ try {
144
+ await migrateLegacyConsentFlowContracts();
145
+ const incompleteMarker = await neogma.queryRunner.run(
146
+ `MATCH (migration:ConsentFlowContractMigration {id:'legacy-v1'})
147
+ RETURN migration.completedAt AS completedAt`
148
+ );
149
+ expect(incompleteMarker.records).toHaveLength(0);
150
+ await neogma.queryRunner.run(
151
+ `MATCH (contract:ConsentFlowContract {id:$id}) DETACH DELETE contract`,
152
+ { id: malformedId }
153
+ );
154
+ await migrateLegacyConsentFlowContracts();
155
+ const completeMarker = await neogma.queryRunner.run(
156
+ `MATCH (migration:ConsentFlowContractMigration {id:'legacy-v1'})
157
+ RETURN migration.completedAt AS completedAt`
158
+ );
159
+ expect(completeMarker.records).toHaveLength(1);
160
+ await migrateLegacyConsentFlowContracts();
161
+ expect((await getConsentFlowContractVersionRecord(id, 1))?.contract).toEqual(
162
+ normalContract
163
+ );
164
+ expect(await getConsentFlowContractVersionRecord(malformedId, 1)).toBeNull();
165
+ const versions = await owner.clients.fullAuth.contracts.getContractVersions({ uri });
166
+ expect(versions.map(version => version.version)).toEqual([1]);
167
+ const consent = await learner.clients.fullAuth.contracts.getConsentStatus({
168
+ uri,
169
+ profileId: `migration-user-${suffix}`,
170
+ });
171
+ expect(consent).toMatchObject({
172
+ status: 'live',
173
+ consentedVersion: 1,
174
+ currentVersion: 1,
175
+ needsReconsent: false,
176
+ });
177
+ const data = await owner.clients.fullAuth.contracts.getConsentedDataForContract({
178
+ uri,
179
+ });
180
+ expect(data.records.map(record => record.personal)).toEqual([
181
+ normalFullTerms.read.personal,
182
+ ]);
183
+ } finally {
184
+ await neogma.queryRunner.run(
185
+ `MATCH (contract:ConsentFlowContract {id:$id}) DETACH DELETE contract`,
186
+ { id: malformedId }
187
+ );
188
+ await neogma.queryRunner.run(
189
+ `MATCH (migration:ConsentFlowContractMigration {id:'legacy-v1'}) DELETE migration`
190
+ );
191
+ }
192
+ });
193
+ it('repairs concurrent post-marker reads once and normalizes only that connection', async () => {
194
+ const fixture = await recoveryFixture();
195
+ try {
196
+ await neogma.queryRunner.run(
197
+ `MATCH (terms:ConsentFlowTerms {id:$id}) SET terms.status='stale', terms.oneTime=true`,
198
+ { id: getIdFromUri(fixture.termsUri) }
199
+ );
200
+ const snapshots = await Promise.all(
201
+ Array.from({ length: 4 }, () => getConsentFlowContractVersionRecord(fixture.id, 1))
202
+ );
203
+ expect(snapshots.map(snapshot => snapshot?.contract)).toEqual(
204
+ Array.from({ length: 4 }, () => normalContract)
205
+ );
206
+ const versions = await fixture.owner.clients.fullAuth.contracts.getContractVersions({
207
+ uri: fixture.uri,
208
+ });
209
+ expect(versions.map(version => version.version)).toEqual([1]);
210
+ const terms = await neogma.queryRunner.run(
211
+ `MATCH (terms:ConsentFlowTerms {id:$id})
212
+ RETURN terms.consentedVersion AS accepted, terms.status AS status`,
213
+ { id: getIdFromUri(fixture.termsUri) }
214
+ );
215
+ expect(Number(terms.records[0].get('accepted'))).toBe(1);
216
+ expect(terms.records[0].get('status')).toBe('consumed');
217
+ await fixture.assertUnrelatedUnchanged();
218
+ } finally {
219
+ await fixture.cleanup();
220
+ }
221
+ });
222
+
223
+ it('publishes an update after scoped legacy recovery while preserving the original snapshot', async () => {
224
+ const fixture = await recoveryFixture();
225
+ try {
226
+ const updated =
227
+ await fixture.owner.clients.fullAuth.contracts.updateConsentFlowContract({
228
+ uri: fixture.uri,
229
+ changes: { subtitle: 'Updated after recovery' },
230
+ });
231
+ expect(updated).toMatchObject({ kind: 'relaxed', version: 2 });
232
+ expect((await getConsentFlowContractVersionRecord(fixture.id, 1))?.contract).toEqual(
233
+ normalContract
234
+ );
235
+ const versions = await fixture.owner.clients.fullAuth.contracts.getContractVersions({
236
+ uri: fixture.uri,
237
+ });
238
+ expect(versions.map(version => version.version).sort((a, b) => a - b)).toEqual([1, 2]);
239
+ await fixture.assertUnrelatedUnchanged();
240
+ } finally {
241
+ await fixture.cleanup();
242
+ }
243
+ });
244
+
245
+ it('retires a post-marker legacy contract and withdraws its existing connection', async () => {
246
+ const fixture = await recoveryFixture();
247
+ try {
248
+ await fixture.owner.clients.fullAuth.contracts.deleteConsentFlowContract({
249
+ uri: fixture.uri,
250
+ });
251
+ const result = await neogma.queryRunner.run(
252
+ `MATCH (contract:ConsentFlowContract {id:$id})<-[:CONSENTS_TO]-(terms:ConsentFlowTerms)
253
+ RETURN contract.status AS contractStatus, terms.status AS termsStatus,
254
+ contract.currentVersion AS version`,
255
+ { id: fixture.id }
256
+ );
257
+ expect(result.records[0].get('contractStatus')).toBe('retired');
258
+ expect(result.records[0].get('termsStatus')).toBe('withdrawn');
259
+ expect(Number(result.records[0].get('version'))).toBe(2);
260
+ expect((await getConsentFlowContractVersionRecord(fixture.id, 1))?.contract).toEqual(
261
+ normalContract
262
+ );
263
+ await fixture.assertUnrelatedUnchanged();
264
+ } finally {
265
+ await fixture.cleanup();
266
+ }
267
+ });
268
+
269
+ it('rejects update and retirement when versioned history is missing', async () => {
270
+ const fixture = await recoveryFixture();
271
+ try {
272
+ await neogma.queryRunner.run(
273
+ `MATCH (contract:ConsentFlowContract {id:$id}) SET contract.currentVersion=1`,
274
+ { id: fixture.id }
275
+ );
276
+ await expect(
277
+ fixture.owner.clients.fullAuth.contracts.updateConsentFlowContract({
278
+ uri: fixture.uri,
279
+ changes: { subtitle: 'Must not publish' },
280
+ })
281
+ ).rejects.toMatchObject({ code: 'PRECONDITION_FAILED' });
282
+ await expect(
283
+ fixture.owner.clients.fullAuth.contracts.deleteConsentFlowContract({
284
+ uri: fixture.uri,
285
+ })
286
+ ).rejects.toMatchObject({ code: 'PRECONDITION_FAILED' });
287
+ expect(await getConsentFlowContractVersionRecord(fixture.id, 1)).toBeNull();
288
+ await fixture.assertUnrelatedUnchanged();
289
+ } finally {
290
+ await fixture.cleanup();
291
+ }
292
+ });
293
+
294
+ it('does not record an accepted version for a malformed legacy request', async () => {
295
+ const fixture = await recoveryFixture();
296
+ try {
297
+ await neogma.queryRunner.run(
298
+ `MATCH (contract:ConsentFlowContract {id:$id})
299
+ SET contract['contract.read.credentials.categories.Achievement.required']='invalid'`,
300
+ { id: fixture.id }
301
+ );
302
+ expect(await getConsentFlowContractVersionRecord(fixture.id, 1)).toBeNull();
303
+ const result = await neogma.queryRunner.run(
304
+ `MATCH (contract:ConsentFlowContract {id:$id})<-[:CONSENTS_TO]-(terms:ConsentFlowTerms)
305
+ RETURN contract.currentVersion AS version, terms.consentedVersion AS accepted`,
306
+ { id: fixture.id }
307
+ );
308
+ expect(result.records[0].get('version')).toBeNull();
309
+ expect(result.records[0].get('accepted')).toBeNull();
310
+ await fixture.assertUnrelatedUnchanged();
311
+ } finally {
312
+ await fixture.cleanup();
313
+ }
314
+ });
315
+ });
@@ -453,18 +453,18 @@ describe('contract data recipients', () => {
453
453
  await accept(uri, 1);
454
454
  const id = uri.split(':').at(-1)!;
455
455
  expect(
456
- await getConsentedDataForContract(id, recipient.profileId, { limit: 10 })
456
+ (await getConsentedDataForContract(id, recipient.profileId, { limit: 10 })).records
457
457
  ).toHaveLength(1);
458
458
  await owner.clients.fullAuth.contracts.removeContractRecipient({
459
459
  contractUri: uri,
460
460
  recipient: recipient.profileId,
461
461
  });
462
- expect(await getConsentedDataForContract(id, recipient.profileId, { limit: 10 })).toEqual(
463
- []
464
- );
465
- expect(await getConsentedDataForContract(id, owner.profileId, { limit: 10 })).toHaveLength(
466
- 1
467
- );
462
+ expect(
463
+ (await getConsentedDataForContract(id, recipient.profileId, { limit: 10 })).records
464
+ ).toEqual([]);
465
+ expect(
466
+ (await getConsentedDataForContract(id, owner.profileId, { limit: 10 })).records
467
+ ).toHaveLength(1);
468
468
  });
469
469
 
470
470
  it('includes recipient endpoints and audience acknowledgements in generated OpenAPI', () => {