@learncard/network-brain-service 3.19.1 → 4.0.1

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 (315) hide show
  1. package/.env.example +44 -111
  2. package/AGENTS.md +52 -0
  3. package/CHANGELOG.md +120 -0
  4. package/README.md +9 -2
  5. package/bootstrapLambda.test.ts +72 -0
  6. package/bundle.mjs +9 -1
  7. package/config/config.dev.json +29 -0
  8. package/config/config.example.json +29 -0
  9. package/config/config.json +1 -0
  10. package/config/config.production.json +29 -0
  11. package/config/config.scouts.dev.json +24 -0
  12. package/config/config.scouts.production.json +24 -0
  13. package/contractEventsLambda.ts +18 -0
  14. package/contractEventsLambdaApp.ts +19 -0
  15. package/didWebLambda.ts +13 -18
  16. package/didWebLambdaApp.ts +22 -0
  17. package/dist/accesslayer/app-manifest-version/create.d.ts +10 -0
  18. package/dist/accesslayer/app-manifest-version/create.d.ts.map +1 -0
  19. package/dist/accesslayer/app-manifest-version/read.d.ts +11 -0
  20. package/dist/accesslayer/app-manifest-version/read.d.ts.map +1 -0
  21. package/dist/accesslayer/app-manifest-version/update.d.ts +7 -0
  22. package/dist/accesslayer/app-manifest-version/update.d.ts.map +1 -0
  23. package/dist/accesslayer/app-store-listing/read.d.ts +2 -1
  24. package/dist/accesslayer/app-store-listing/read.d.ts.map +1 -1
  25. package/dist/accesslayer/app-store-listing/relationships/create.d.ts +1 -0
  26. package/dist/accesslayer/app-store-listing/relationships/create.d.ts.map +1 -1
  27. package/dist/accesslayer/app-store-listing/relationships/read.d.ts +2 -0
  28. package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
  29. package/dist/accesslayer/app-store-listing/update.d.ts +1 -0
  30. package/dist/accesslayer/app-store-listing/update.d.ts.map +1 -1
  31. package/dist/accesslayer/boost/relationships/create.d.ts +2 -1
  32. package/dist/accesslayer/boost/relationships/create.d.ts.map +1 -1
  33. package/dist/accesslayer/consentflowcontract/create.d.ts +9 -1
  34. package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
  35. package/dist/accesslayer/consentflowcontract/read.d.ts +34 -3
  36. package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
  37. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +4 -1
  38. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  39. package/dist/accesslayer/consentflowcontract/relationships/delete.d.ts.map +1 -1
  40. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +3 -2
  41. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  42. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts +12 -0
  43. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts.map +1 -0
  44. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts +12 -0
  45. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts.map +1 -0
  46. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +8 -5
  47. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  48. package/dist/accesslayer/credential/create.d.ts +2 -1
  49. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  50. package/dist/accesslayer/credential/relationships/create.d.ts +4 -3
  51. package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
  52. package/dist/accesslayer/inbox-credential/relationships/create.d.ts +2 -1
  53. package/dist/accesslayer/inbox-credential/relationships/create.d.ts.map +1 -1
  54. package/dist/accesslayer/inbox-credential/update.d.ts +2 -1
  55. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  56. package/dist/accesslayer/share-link/abandon.d.ts.map +1 -1
  57. package/dist/accesslayer/share-link/attachment.d.ts +37 -0
  58. package/dist/accesslayer/share-link/attachment.d.ts.map +1 -0
  59. package/dist/accesslayer/share-link/finalize.d.ts.map +1 -1
  60. package/dist/accesslayer/share-link/helpers.d.ts +3 -0
  61. package/dist/accesslayer/share-link/helpers.d.ts.map +1 -1
  62. package/dist/accesslayer/share-link/recovery.d.ts.map +1 -1
  63. package/dist/accesslayer/share-link/reserve.d.ts.map +1 -1
  64. package/dist/accesslayer/share-link/revoke.d.ts.map +1 -1
  65. package/dist/accesslayer/share-link/types.d.ts +8 -3
  66. package/dist/accesslayer/share-link/types.d.ts.map +1 -1
  67. package/dist/cache/app-store.caches.d.ts +49 -0
  68. package/dist/cache/app-store.caches.d.ts.map +1 -1
  69. package/dist/config/applyDockerStageConfig.d.ts +2 -0
  70. package/dist/config/applyDockerStageConfig.d.ts.map +1 -0
  71. package/dist/config/environment.d.ts +5 -0
  72. package/dist/config/environment.d.ts.map +1 -1
  73. package/dist/config/serverlessFunctionEnv.test.d.ts +2 -0
  74. package/dist/config/serverlessFunctionEnv.test.d.ts.map +1 -0
  75. package/dist/config/stageConfig.d.ts +112 -0
  76. package/dist/config/stageConfig.d.ts.map +1 -0
  77. package/dist/config/stageConfig.test.d.ts +2 -0
  78. package/dist/config/stageConfig.test.d.ts.map +1 -0
  79. package/dist/didkit_wasm_bg.wasm +0 -0
  80. package/dist/docker-entry.d.ts +1 -1
  81. package/dist/docker-entry.d.ts.map +1 -1
  82. package/dist/helpers/app-manifest.helpers.d.ts +5 -0
  83. package/dist/helpers/app-manifest.helpers.d.ts.map +1 -0
  84. package/dist/helpers/claim-hooks.helpers.d.ts +2 -1
  85. package/dist/helpers/claim-hooks.helpers.d.ts.map +1 -1
  86. package/dist/helpers/consent-referral.helpers.d.ts +2 -0
  87. package/dist/helpers/consent-referral.helpers.d.ts.map +1 -0
  88. package/dist/helpers/consent-scopes.helpers.d.ts +7 -0
  89. package/dist/helpers/consent-scopes.helpers.d.ts.map +1 -0
  90. package/dist/helpers/contract-event-maintenance.helpers.d.ts +3 -0
  91. package/dist/helpers/contract-event-maintenance.helpers.d.ts.map +1 -0
  92. package/dist/helpers/contract-events.helpers.d.ts +25 -0
  93. package/dist/helpers/contract-events.helpers.d.ts.map +1 -0
  94. package/dist/helpers/contract-requests.helpers.d.ts +19 -0
  95. package/dist/helpers/contract-requests.helpers.d.ts.map +1 -0
  96. package/dist/helpers/finalize-inbox.helpers.d.ts +3 -3
  97. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  98. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  99. package/dist/helpers/inline-template.helpers.d.ts +45 -0
  100. package/dist/helpers/inline-template.helpers.d.ts.map +1 -0
  101. package/dist/helpers/notificationMessages.d.ts +1 -1
  102. package/dist/helpers/notificationMessages.d.ts.map +1 -1
  103. package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
  104. package/dist/helpers/notifications.helpers.d.ts +6 -0
  105. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  106. package/dist/helpers/profile.helpers.d.ts +1 -0
  107. package/dist/helpers/profile.helpers.d.ts.map +1 -1
  108. package/dist/helpers/share-link-attachment-access.d.ts +4 -0
  109. package/dist/helpers/share-link-attachment-access.d.ts.map +1 -0
  110. package/dist/helpers/share-link-attachment.d.ts +18 -0
  111. package/dist/helpers/share-link-attachment.d.ts.map +1 -0
  112. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts +5 -0
  113. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts.map +1 -0
  114. package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -1
  115. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -1
  116. package/dist/helpers/share-link-maintenance/runner.d.ts +1 -0
  117. package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -1
  118. package/dist/helpers/share-link-maintenance/runtime.d.ts +2 -0
  119. package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -1
  120. package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -1
  121. package/dist/helpers/share-link-maintenance/types.d.ts +4 -1
  122. package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -1
  123. package/dist/helpers/share-link-owner-projection.d.ts.map +1 -1
  124. package/dist/helpers/share-link-policy/production.d.ts +3 -5
  125. package/dist/helpers/share-link-policy/production.d.ts.map +1 -1
  126. package/dist/helpers/share-link-policy/resolver.d.ts +1 -1
  127. package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -1
  128. package/dist/helpers/share-link-policy/types.d.ts +8 -2
  129. package/dist/helpers/share-link-policy/types.d.ts.map +1 -1
  130. package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
  131. package/dist/helpers/smartResume.helpers.d.ts +3 -0
  132. package/dist/helpers/smartResume.helpers.d.ts.map +1 -0
  133. package/dist/index.js +2 -2
  134. package/dist/models/AppManifestVersion.d.ts +20 -0
  135. package/dist/models/AppManifestVersion.d.ts.map +1 -0
  136. package/dist/models/AppStoreListing.d.ts +13 -0
  137. package/dist/models/AppStoreListing.d.ts.map +1 -1
  138. package/dist/models/ConsentFlowContract.d.ts +13 -2
  139. package/dist/models/ConsentFlowContract.d.ts.map +1 -1
  140. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  141. package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
  142. package/dist/models/Integration.d.ts +2 -0
  143. package/dist/models/Integration.d.ts.map +1 -1
  144. package/dist/models/ShareLink.d.ts +2 -0
  145. package/dist/models/ShareLink.d.ts.map +1 -1
  146. package/dist/models/index.d.ts +1 -0
  147. package/dist/models/index.d.ts.map +1 -1
  148. package/dist/models/share-link-constraints.d.ts.map +1 -1
  149. package/dist/openapi.d.ts +1 -1
  150. package/dist/openapi.d.ts.map +1 -1
  151. package/dist/publish-metafile.json +1 -0
  152. package/dist/routes/app-store.d.ts +619 -0
  153. package/dist/routes/app-store.d.ts.map +1 -1
  154. package/dist/routes/boosts.d.ts +2 -2
  155. package/dist/routes/boosts.d.ts.map +1 -1
  156. package/dist/routes/contracts.d.ts +146 -3
  157. package/dist/routes/contracts.d.ts.map +1 -1
  158. package/dist/routes/credentials.d.ts +34 -0
  159. package/dist/routes/credentials.d.ts.map +1 -1
  160. package/dist/routes/inbox.d.ts +52 -2
  161. package/dist/routes/inbox.d.ts.map +1 -1
  162. package/dist/routes/index.d.ts.map +1 -1
  163. package/dist/routes/profile-manager.d.ts.map +1 -1
  164. package/dist/routes/profiles.d.ts.map +1 -1
  165. package/dist/routes/public-share-links.d.ts +55 -0
  166. package/dist/routes/public-share-links.d.ts.map +1 -1
  167. package/dist/routes/share-links.d.ts +175 -14
  168. package/dist/routes/share-links.d.ts.map +1 -1
  169. package/dist/routes/test.d.ts +5 -0
  170. package/dist/routes/test.d.ts.map +1 -1
  171. package/dist/routes/workflows.d.ts.map +1 -1
  172. package/dist/types/app-manifest.d.ts +339 -0
  173. package/dist/types/app-manifest.d.ts.map +1 -0
  174. package/dist/types/app-store-listing.d.ts +34 -0
  175. package/dist/types/app-store-listing.d.ts.map +1 -1
  176. package/dist/types/consentflowcontract.d.ts +30 -3
  177. package/dist/types/consentflowcontract.d.ts.map +1 -1
  178. package/dist/types/credential-refresh.d.ts +1 -1
  179. package/esbuildPlugins.cjs +1 -0
  180. package/lambda.ts +49 -195
  181. package/lambdaApp.ts +203 -0
  182. package/package.json +20 -19
  183. package/project.json +8 -1
  184. package/scripts/export-openapi.ts +20 -0
  185. package/scripts/offline-schema-environment.ts +18 -0
  186. package/scripts/verify-python-contracts.ts +72 -0
  187. package/serverless-local.yml +3 -6
  188. package/serverless.function-env.cjs +50 -0
  189. package/serverless.local.yml +0 -1
  190. package/serverless.yml +29 -74
  191. package/shareLinkMaintenanceLambda.ts +13 -44
  192. package/shareLinkMaintenanceLambdaApp.ts +50 -0
  193. package/src/accesslayer/app-manifest-version/create.ts +57 -0
  194. package/src/accesslayer/app-manifest-version/read.ts +129 -0
  195. package/src/accesslayer/app-manifest-version/update.ts +61 -0
  196. package/src/accesslayer/app-store-listing/read.ts +6 -0
  197. package/src/accesslayer/app-store-listing/relationships/create.ts +20 -0
  198. package/src/accesslayer/app-store-listing/relationships/read.ts +24 -1
  199. package/src/accesslayer/app-store-listing/update.ts +15 -0
  200. package/src/accesslayer/boost/relationships/create.ts +8 -2
  201. package/src/accesslayer/consentflowcontract/create.ts +81 -8
  202. package/src/accesslayer/consentflowcontract/read.ts +84 -10
  203. package/src/accesslayer/consentflowcontract/relationships/create.ts +116 -93
  204. package/src/accesslayer/consentflowcontract/relationships/delete.ts +1 -0
  205. package/src/accesslayer/consentflowcontract/relationships/read.ts +184 -108
  206. package/src/accesslayer/consentflowcontract/relationships/recipients.ts +125 -0
  207. package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +125 -0
  208. package/src/accesslayer/consentflowcontract/relationships/update.ts +240 -189
  209. package/src/accesslayer/credential/create.test.ts +2 -115
  210. package/src/accesslayer/credential/create.ts +11 -6
  211. package/src/accesslayer/credential/relationships/create.ts +17 -10
  212. package/src/accesslayer/inbox-credential/relationships/create.ts +70 -48
  213. package/src/accesslayer/inbox-credential/update.ts +4 -2
  214. package/src/accesslayer/share-link/abandon.ts +20 -0
  215. package/src/accesslayer/share-link/attachment.ts +360 -0
  216. package/src/accesslayer/share-link/finalize.ts +55 -7
  217. package/src/accesslayer/share-link/helpers.ts +33 -2
  218. package/src/accesslayer/share-link/recovery.ts +20 -0
  219. package/src/accesslayer/share-link/reserve.ts +85 -6
  220. package/src/accesslayer/share-link/revoke.ts +35 -0
  221. package/src/accesslayer/share-link/types.ts +9 -4
  222. package/src/cache/app-store.caches.ts +35 -0
  223. package/src/config/applyDockerStageConfig.ts +6 -0
  224. package/src/config/environment.test.ts +17 -12
  225. package/src/config/environment.ts +1 -0
  226. package/src/config/serverlessFunctionEnv.test.ts +105 -0
  227. package/src/config/stageConfig.test.ts +25 -0
  228. package/src/config/stageConfig.ts +24 -0
  229. package/src/docker-entry.ts +24 -0
  230. package/src/helpers/app-manifest.helpers.ts +185 -0
  231. package/src/helpers/claim-hooks.helpers.ts +20 -7
  232. package/src/helpers/consent-referral.helpers.ts +6 -0
  233. package/src/helpers/consent-scopes.helpers.ts +58 -0
  234. package/src/helpers/contract-event-maintenance.helpers.ts +43 -0
  235. package/src/helpers/contract-events.helpers.ts +540 -0
  236. package/src/helpers/contract-requests.helpers.ts +211 -0
  237. package/src/helpers/finalize-inbox.helpers.ts +163 -44
  238. package/src/helpers/inbox.helpers.ts +51 -66
  239. package/src/helpers/inline-template.helpers.ts +560 -0
  240. package/src/helpers/notificationMessages.ts +59 -0
  241. package/src/helpers/notificationQueue.helpers.ts +23 -5
  242. package/src/helpers/notifications.helpers.ts +25 -1
  243. package/src/helpers/profile.helpers.ts +6 -0
  244. package/src/helpers/share-link-attachment-access.ts +87 -0
  245. package/src/helpers/share-link-attachment.ts +115 -0
  246. package/src/helpers/share-link-coordinator/attachment-stat.ts +41 -0
  247. package/src/helpers/share-link-coordinator/coordinator.ts +15 -2
  248. package/src/helpers/share-link-coordinator/recovery-runner.ts +15 -0
  249. package/src/helpers/share-link-maintenance/runner.ts +28 -0
  250. package/src/helpers/share-link-maintenance/runtime.ts +6 -0
  251. package/src/helpers/share-link-maintenance/telemetry.ts +3 -0
  252. package/src/helpers/share-link-maintenance/types.ts +2 -0
  253. package/src/helpers/share-link-owner-projection.ts +2 -0
  254. package/src/helpers/share-link-policy/production.ts +37 -17
  255. package/src/helpers/share-link-policy/resolver.ts +16 -11
  256. package/src/helpers/share-link-policy/types.ts +15 -7
  257. package/src/helpers/signingAuthority.helpers.ts +23 -14
  258. package/src/helpers/smartResume.helpers.ts +123 -0
  259. package/src/models/AppManifestVersion.ts +43 -0
  260. package/src/models/AppStoreListing.ts +32 -0
  261. package/src/models/ConsentFlowContract.ts +34 -3
  262. package/src/models/ConsentFlowTerms.ts +10 -0
  263. package/src/models/ConsentFlowTransaction.ts +3 -0
  264. package/src/models/Integration.ts +2 -0
  265. package/src/models/ShareLink.ts +4 -0
  266. package/src/models/index.ts +23 -0
  267. package/src/models/share-link-constraints.ts +5 -0
  268. package/src/openapi.ts +44 -8
  269. package/src/routes/app-store.ts +1054 -27
  270. package/src/routes/boosts.ts +12 -14
  271. package/src/routes/contracts.ts +382 -262
  272. package/src/routes/inbox.ts +4 -2
  273. package/src/routes/index.ts +4 -1
  274. package/src/routes/profile-manager.ts +19 -0
  275. package/src/routes/profiles.ts +12 -0
  276. package/src/routes/public-share-links.ts +169 -0
  277. package/src/routes/share-links.ts +69 -0
  278. package/src/routes/workflows.ts +3 -2
  279. package/src/types/app-manifest.ts +28 -0
  280. package/src/types/app-store-listing.ts +13 -0
  281. package/src/types/consentflowcontract.ts +25 -4
  282. package/test/app-manifest.helpers.spec.ts +170 -0
  283. package/test/app-manifest.routes.spec.ts +299 -0
  284. package/test/app-store-listing-updates.spec.ts +236 -0
  285. package/test/app-store-listing.spec.ts +65 -3
  286. package/test/consent-data-boundaries.spec.ts +352 -0
  287. package/test/consent-recipients.spec.ts +513 -0
  288. package/test/consent-scopes.helpers.spec.ts +67 -0
  289. package/test/consentflow.spec.ts +63 -2
  290. package/test/contract-requests.spec.ts +1588 -0
  291. package/test/credential-storage.neo4j.spec.ts +122 -0
  292. package/test/guardian-gated-routes.spec.ts +36 -0
  293. package/test/inbox-security.e2e.ts +26 -10
  294. package/test/inbox.spec.ts +184 -8
  295. package/test/inline-template.helpers.spec.ts +49 -0
  296. package/test/managed-recipient-inbox.spec.ts +24 -2
  297. package/test/openapi.spec.ts +14 -73
  298. package/test/profiles.spec.ts +30 -0
  299. package/test/share-link-attachment.neo4j.spec.ts +262 -0
  300. package/test/share-link-attachment.unit.spec.ts +244 -0
  301. package/test/share-link-lifecycle.neo4j.spec.ts +145 -2
  302. package/test/share-link-maintenance.unit.spec.ts +32 -5
  303. package/test/share-link-owner-route.unit.spec.ts +70 -2
  304. package/test/share-link-policy.unit.spec.ts +18 -4
  305. package/test/share-link-production-policy.unit.spec.ts +58 -9
  306. package/test/share-link-public-route.unit.spec.ts +279 -0
  307. package/test/share-link-service-policy.neo4j.spec.ts +403 -0
  308. package/test/signing-authority-encryption.spec.ts +75 -0
  309. package/test/smart-resume-consent.spec.ts +233 -0
  310. package/test/smart-resume-upload.spec.ts +65 -0
  311. package/tsconfig.build.json +1 -1
  312. package/tsconfig.json +1 -0
  313. package/vitest.config.ts +13 -0
  314. package/vitest.inbox-e2e.config.ts +2 -0
  315. package/workerLambda.test.ts +100 -0
@@ -1,10 +1,12 @@
1
+ import { termsReferralSnapshotCypher } from '@helpers/consent-referral.helpers';
2
+ import { appendConsentEvent, tryDispatchContractEvent } from '@helpers/contract-events.helpers';
1
3
  import { QueryBuilder, BindParam } from 'neogma';
4
+ import { TRPCError } from '@trpc/server';
2
5
  import { v4 as uuid } from 'uuid';
3
6
  import {
4
7
  ConsentFlowTerms as ConsentFlowTermsType,
5
8
  ConsentFlowTransaction as ConsentFlowTransactionType,
6
9
  ConsentFlowGuardianApproval,
7
- LCNNotificationTypeEnumValidator,
8
10
  LCNProfile,
9
11
  VC,
10
12
  UnsignedVC,
@@ -12,9 +14,6 @@ import {
12
14
  import { ConsentFlowTerms, ConsentFlowTransaction, ConsentFlowContract } from '@models';
13
15
  import { neogma } from '@instance';
14
16
  import { flattenObject } from '@helpers/objects.helpers';
15
- import { addNotificationToQueue } from '@helpers/notifications.helpers';
16
- import { getNotificationMessage } from '@helpers/notificationMessages';
17
- import { resolveRecipientLocale } from '@helpers/getRecipientLocale.helpers';
18
17
  import { DbContractType, DbTermsType } from 'types/consentflowcontract';
19
18
  import { getBoostUri, sendBoost } from '@helpers/boost.helpers';
20
19
  import { setCredentialSubjectIds } from '@helpers/credentialSubject.helpers';
@@ -24,6 +23,13 @@ import { issueCredentialWithSigningAuthority } from '@helpers/signingAuthority.h
24
23
  import { getProfileByProfileId } from '@accesslayer/profile/read';
25
24
  import { injectObv3AlignmentsIntoCredentialForBoost } from '@services/skills-provider/inject';
26
25
  import { removeRequestedForRelationship } from './delete';
26
+ import {
27
+ lockContractAudience,
28
+ audienceVersionWhere,
29
+ consentMutationWhere,
30
+ assertAudienceMutation,
31
+ runAudienceMutation,
32
+ } from './recipients';
27
33
 
28
34
  export const reconsentTerms = async (
29
35
  relationship: {
@@ -37,11 +43,17 @@ export const reconsentTerms = async (
37
43
  expiresAt,
38
44
  oneTime,
39
45
  guardianApproval,
46
+ audienceVersion,
47
+ expectedRequestId,
48
+ smartResumeFingerprint,
40
49
  }: {
41
50
  terms: ConsentFlowTermsType;
42
51
  expiresAt?: string;
43
52
  oneTime?: boolean;
44
53
  guardianApproval?: ConsentFlowGuardianApproval;
54
+ audienceVersion?: number;
55
+ expectedRequestId?: string;
56
+ smartResumeFingerprint?: string;
45
57
  },
46
58
  domain: string
47
59
  ): Promise<boolean> => {
@@ -64,38 +76,72 @@ export const reconsentTerms = async (
64
76
  .map(key => [key, null])
65
77
  );
66
78
 
67
- const result = await new QueryBuilder(
68
- new BindParam({
69
- params: {
70
- ...newFlat,
71
- ...removedProperties,
72
- updatedAt: transaction.date,
73
- status: oneTime ? 'stale' : 'live',
74
- ...(typeof expiresAt === 'string' ? { expiresAt } : {}),
75
- ...(typeof oneTime === 'boolean' ? { oneTime } : {}),
76
- },
77
- })
78
- )
79
- .match({
80
- model: ConsentFlowTerms,
81
- where: { id: relationship.terms.id },
82
- identifier: 'terms',
83
- })
84
- .set('terms += $params')
85
- .with('terms')
86
- .create({
87
- related: [
88
- {
89
- identifier: 'transaction',
90
- model: ConsentFlowTransaction,
91
- properties: transaction,
92
- },
93
- ConsentFlowTransaction.getRelationshipByAlias('isFor'),
94
- { identifier: 'terms' },
95
- ],
96
- })
97
- .set('transaction += $params')
98
- .run();
79
+ const result = await runAudienceMutation(
80
+ appendConsentEvent(
81
+ lockContractAudience(
82
+ new QueryBuilder(
83
+ new BindParam({
84
+ audienceVersion: audienceVersion ?? null,
85
+ expectedRequestId: expectedRequestId ?? null,
86
+ consenterProfileId: relationship.consenter.profileId,
87
+ termsMutationVersion: Number(relationship.terms.mutationVersion ?? 0),
88
+ params: {
89
+ ...newFlat,
90
+ ...removedProperties,
91
+ ...(smartResumeFingerprint
92
+ ? {
93
+ smartResumeFingerprint,
94
+ smartResumePublicationStatus: 'pending',
95
+ smartResumeMutationVersion:
96
+ Number(relationship.terms.mutationVersion ?? 0) + 1,
97
+ smartResumeLeaseId: null,
98
+ smartResumeLeaseUntil: null,
99
+ smartResumeRedirectUrl: null,
100
+ }
101
+ : {}),
102
+ updatedAt: transaction.date,
103
+ status: oneTime ? 'stale' : 'live',
104
+ ...(typeof expiresAt === 'string' ? { expiresAt } : {}),
105
+ ...(typeof oneTime === 'boolean' ? { oneTime } : {}),
106
+ },
107
+ })
108
+ ),
109
+ relationship.contract.id
110
+ )
111
+ .where(`${audienceVersionWhere} AND ${consentMutationWhere}`)
112
+ .set('contract.hasConsented = true')
113
+ .with('contract')
114
+ .match({
115
+ model: ConsentFlowTerms,
116
+ where: { id: relationship.terms.id },
117
+ identifier: 'terms',
118
+ })
119
+ .where('coalesce(terms.mutationVersion, 0) = $termsMutationVersion')
120
+ .set('terms += $params')
121
+ .set('terms.mutationVersion = coalesce(terms.mutationVersion, 0) + 1')
122
+ .with('terms, contract')
123
+ .create({
124
+ related: [
125
+ {
126
+ identifier: 'transaction',
127
+ model: ConsentFlowTransaction,
128
+ properties: transaction,
129
+ },
130
+ ConsentFlowTransaction.getRelationshipByAlias('isFor'),
131
+ { identifier: 'terms' },
132
+ ],
133
+ })
134
+ .set('transaction += $params')
135
+ .with('terms, contract, transaction'),
136
+ {
137
+ consenterProfileId: relationship.consenter.profileId,
138
+ transaction,
139
+ domain: domain,
140
+ messageKey: 'consentFlowTransactionReconsented',
141
+ }
142
+ ).return('terms.id AS id')
143
+ );
144
+ assertAudienceMutation(result.records.length);
99
145
 
100
146
  // Process Auto-Boosts
101
147
  const autoBoostsResult = await ConsentFlowContract.findRelationships({
@@ -207,6 +253,7 @@ export const reconsentTerms = async (
207
253
  { identifier: 'terms' },
208
254
  ],
209
255
  })
256
+ .raw(termsReferralSnapshotCypher('boostTransaction'))
210
257
  .run();
211
258
 
212
259
  // Send the boost to the consenter
@@ -227,20 +274,7 @@ export const reconsentTerms = async (
227
274
  );
228
275
  }
229
276
 
230
- await addNotificationToQueue({
231
- type: LCNNotificationTypeEnumValidator.enum.CONSENT_FLOW_TRANSACTION,
232
- from: relationship.consenter,
233
- to: relationship.contractOwner,
234
- message: getNotificationMessage(
235
- 'consentFlowTransactionReconsented',
236
- resolveRecipientLocale(relationship.contractOwner),
237
- {
238
- consenter: relationship.consenter.displayName,
239
- contractName: relationship.contract.name,
240
- }
241
- ),
242
- data: { transaction },
243
- });
277
+ await tryDispatchContractEvent(transaction.id);
244
278
 
245
279
  return result.summary.counters.containsUpdates();
246
280
  };
@@ -257,11 +291,13 @@ export const updateTerms = async (
257
291
  expiresAt,
258
292
  oneTime,
259
293
  guardianApproval,
294
+ audienceVersion,
260
295
  }: {
261
296
  terms: ConsentFlowTermsType;
262
297
  expiresAt?: string;
263
298
  oneTime?: boolean;
264
299
  guardianApproval?: ConsentFlowGuardianApproval;
300
+ audienceVersion?: number;
265
301
  },
266
302
  domain: string
267
303
  ): Promise<boolean> => {
@@ -300,27 +336,52 @@ export const updateTerms = async (
300
336
  ...Object.fromEntries(keysToRemove.map(k => [k, null as any])),
301
337
  };
302
338
 
303
- const result = await new QueryBuilder(new BindParam({ params: paramsForSet }))
304
- .match({
305
- model: ConsentFlowTerms,
306
- where: { id: relationship.terms.id },
307
- identifier: 'terms',
308
- })
309
- .set('terms += $params')
310
- .with('terms')
311
- .create({
312
- related: [
313
- {
314
- identifier: 'transaction',
315
- model: ConsentFlowTransaction,
316
- properties: transaction,
317
- },
318
- ConsentFlowTransaction.getRelationshipByAlias('isFor'),
319
- { identifier: 'terms' },
320
- ],
321
- })
322
- .set('transaction += $params')
323
- .run();
339
+ const result = await runAudienceMutation(
340
+ appendConsentEvent(
341
+ lockContractAudience(
342
+ new QueryBuilder(
343
+ new BindParam({
344
+ params: paramsForSet,
345
+ audienceVersion: audienceVersion ?? null,
346
+ termsMutationVersion: Number(relationship.terms.mutationVersion ?? 0),
347
+ })
348
+ ),
349
+ relationship.contract.id
350
+ )
351
+ .where(audienceVersionWhere)
352
+ .set('contract.hasConsented = true')
353
+ .with('contract')
354
+ .match({
355
+ model: ConsentFlowTerms,
356
+ where: { id: relationship.terms.id },
357
+ identifier: 'terms',
358
+ })
359
+ .where('coalesce(terms.mutationVersion, 0) = $termsMutationVersion')
360
+ .set('terms += $params')
361
+ .set('terms.mutationVersion = coalesce(terms.mutationVersion, 0) + 1')
362
+ .with('terms, contract')
363
+ .create({
364
+ related: [
365
+ {
366
+ identifier: 'transaction',
367
+ model: ConsentFlowTransaction,
368
+ properties: transaction,
369
+ },
370
+ ConsentFlowTransaction.getRelationshipByAlias('isFor'),
371
+ { identifier: 'terms' },
372
+ ],
373
+ })
374
+ .set('transaction += $params')
375
+ .with('terms, contract, transaction'),
376
+ {
377
+ consenterProfileId: relationship.consenter.profileId,
378
+ transaction,
379
+ domain: domain,
380
+ messageKey: 'consentFlowTransactionUpdatedTerms',
381
+ }
382
+ ).return('terms.id AS id')
383
+ );
384
+ assertAudienceMutation(result.records.length);
324
385
 
325
386
  // Process Auto-Boosts (for updates too)
326
387
  const autoBoosts = await ConsentFlowContract.findRelationships({
@@ -430,6 +491,7 @@ export const updateTerms = async (
430
491
  { identifier: 'terms' },
431
492
  ],
432
493
  })
494
+ .raw(termsReferralSnapshotCypher('boostTransaction'))
433
495
  .run();
434
496
 
435
497
  // Send the boost to the consenter
@@ -450,71 +512,59 @@ export const updateTerms = async (
450
512
  );
451
513
  }
452
514
 
453
- await addNotificationToQueue({
454
- type: LCNNotificationTypeEnumValidator.enum.CONSENT_FLOW_TRANSACTION,
455
- from: relationship.consenter,
456
- to: relationship.contractOwner,
457
- message: getNotificationMessage(
458
- 'consentFlowTransactionUpdatedTerms',
459
- resolveRecipientLocale(relationship.contractOwner),
460
- {
461
- consenter: relationship.consenter.displayName,
462
- contractName: relationship.contract.name,
463
- }
464
- ),
465
- data: { transaction },
466
- });
515
+ await tryDispatchContractEvent(transaction.id);
467
516
 
468
517
  return result.summary.counters.containsUpdates();
469
518
  };
470
519
 
471
- export const withdrawTerms = async (relationship: {
472
- terms: DbTermsType;
473
- consenter: LCNProfile;
474
- contract: DbContractType;
475
- contractOwner: LCNProfile;
476
- }): Promise<boolean> => {
520
+ export const withdrawTerms = async (
521
+ relationship: {
522
+ terms: DbTermsType;
523
+ consenter: LCNProfile;
524
+ contract: DbContractType;
525
+ contractOwner: LCNProfile;
526
+ },
527
+ domain?: string
528
+ ): Promise<boolean> => {
477
529
  const transaction = {
478
530
  id: uuid(),
479
531
  action: 'withdraw',
480
532
  date: new Date().toISOString(),
481
533
  } as const satisfies ConsentFlowTransactionType;
482
534
 
483
- const result = await new QueryBuilder()
484
- .match({
485
- model: ConsentFlowTerms,
486
- where: { id: relationship.terms.id },
487
- identifier: 'terms',
488
- })
489
- .set('terms.status = "withdrawn"')
490
- .with('terms')
491
- .create({
492
- related: [
493
- {
494
- identifier: 'transaction',
495
- model: ConsentFlowTransaction,
496
- properties: transaction,
497
- },
498
- ConsentFlowTransaction.getRelationshipByAlias('isFor'),
499
- { identifier: 'terms' },
500
- ],
501
- })
502
- .run();
503
-
504
- await addNotificationToQueue({
505
- type: LCNNotificationTypeEnumValidator.enum.CONSENT_FLOW_TRANSACTION,
506
- from: relationship.consenter,
507
- to: relationship.contractOwner,
508
- message: getNotificationMessage(
509
- 'consentFlowTransactionWithdrawn',
510
- resolveRecipientLocale(relationship.contractOwner),
535
+ const result = await runAudienceMutation(
536
+ appendConsentEvent(
537
+ lockContractAudience(new QueryBuilder(), relationship.contract.id)
538
+ .match({
539
+ model: ConsentFlowTerms,
540
+ where: { id: relationship.terms.id },
541
+ identifier: 'terms',
542
+ })
543
+ .set('terms.status = "withdrawn"')
544
+ .set('terms.mutationVersion = coalesce(terms.mutationVersion, 0) + 1')
545
+ .with('terms, contract')
546
+ .create({
547
+ related: [
548
+ {
549
+ identifier: 'transaction',
550
+ model: ConsentFlowTransaction,
551
+ properties: transaction,
552
+ },
553
+ ConsentFlowTransaction.getRelationshipByAlias('isFor'),
554
+ { identifier: 'terms' },
555
+ ],
556
+ })
557
+ .with('terms, contract, transaction'),
511
558
  {
512
- consenter: relationship.consenter.displayName,
513
- contractName: relationship.contract.name,
559
+ consenterProfileId: relationship.consenter.profileId,
560
+ transaction,
561
+ domain,
562
+ messageKey: 'consentFlowTransactionWithdrawn',
514
563
  }
515
- ),
516
- data: { transaction },
517
- });
564
+ ).return('terms.id AS id')
565
+ );
566
+
567
+ await tryDispatchContractEvent(transaction.id);
518
568
 
519
569
  try {
520
570
  await removeRequestedForRelationship(
@@ -535,7 +585,9 @@ export const syncCredentialsToContract = async (
535
585
  contract: DbContractType;
536
586
  contractOwner: LCNProfile;
537
587
  },
538
- categories: Record<string, string[]>
588
+ categories: Record<string, string[]>,
589
+ audienceVersion?: number,
590
+ domain?: string
539
591
  ): Promise<boolean> => {
540
592
  // First define the transaction with sync terms data
541
593
  // Create a structure that matches the terms format with multiple categories
@@ -598,58 +650,60 @@ export const syncCredentialsToContract = async (
598
650
  }
599
651
 
600
652
  // Use flattenObject for both the query params and transaction properties to handle Neo4j limitations
601
- const result = await new QueryBuilder(
602
- new BindParam({
603
- params: flattenObject({
604
- terms: updatedTerms,
605
- updatedAt: new Date().toISOString(),
606
- }),
607
- transactionParams: (flattenObject as any)(transaction),
608
- })
609
- )
610
- .match({
611
- model: ConsentFlowTerms,
612
- where: { id: relationship.terms.id },
613
- identifier: 'terms',
614
- })
615
- .set('terms += $params')
616
- .with('terms')
617
- .create({
618
- related: [
619
- { identifier: 'transaction', model: ConsentFlowTransaction },
620
- ConsentFlowTransaction.getRelationshipByAlias('isFor'),
621
- { identifier: 'terms' },
622
- ],
623
- })
624
- .set('transaction += $transactionParams')
625
- .run();
626
-
627
- // Calculate total number of credentials synced
628
- const totalCredentials = Object.values(categories).reduce(
629
- (total, uris) => total + uris.length,
630
- 0
631
- );
632
- const categoryCount = Object.keys(categories).length;
633
-
634
- // Send a notification to the contract owner
635
- await addNotificationToQueue({
636
- type: LCNNotificationTypeEnumValidator.enum.CONSENT_FLOW_TRANSACTION,
637
- from: relationship.consenter,
638
- to: relationship.contractOwner,
639
- message: getNotificationMessage(
640
- categoryCount === 1
641
- ? 'consentFlowTransactionSyncedSingle'
642
- : 'consentFlowTransactionSyncedPlural',
643
- resolveRecipientLocale(relationship.contractOwner),
653
+ const result = await runAudienceMutation(
654
+ appendConsentEvent(
655
+ lockContractAudience(
656
+ new QueryBuilder(
657
+ new BindParam({
658
+ audienceVersion: audienceVersion ?? null,
659
+ termsMutationVersion: Number(relationship.terms.mutationVersion ?? 0),
660
+ now: new Date().toISOString(),
661
+ params: flattenObject({
662
+ terms: updatedTerms,
663
+ updatedAt: new Date().toISOString(),
664
+ }),
665
+ transactionParams: (flattenObject as any)(transaction),
666
+ })
667
+ ),
668
+ relationship.contract.id
669
+ )
670
+ .where(audienceVersionWhere)
671
+ .match({
672
+ model: ConsentFlowTerms,
673
+ where: { id: relationship.terms.id },
674
+ identifier: 'terms',
675
+ })
676
+ .where(
677
+ `coalesce(terms.mutationVersion, 0) = $termsMutationVersion AND terms.status = 'live'
678
+ AND (CASE WHEN terms.expiresAt IS NULL OR trim(terms.expiresAt) = '' THEN true ELSE datetime(terms.expiresAt) > datetime($now) END)
679
+ AND (CASE WHEN contract.expiresAt IS NULL OR trim(contract.expiresAt) = '' THEN true ELSE datetime(contract.expiresAt) > datetime($now) END)`
680
+ )
681
+ .set('terms += $params')
682
+ .set('terms.mutationVersion = coalesce(terms.mutationVersion, 0) + 1')
683
+ .with('terms, contract')
684
+ .create({
685
+ related: [
686
+ { identifier: 'transaction', model: ConsentFlowTransaction },
687
+ ConsentFlowTransaction.getRelationshipByAlias('isFor'),
688
+ { identifier: 'terms' },
689
+ ],
690
+ })
691
+ .set('transaction += $transactionParams')
692
+ .with('terms, contract, transaction'),
644
693
  {
645
- consenter: relationship.consenter.displayName,
646
- totalCredentials: String(totalCredentials),
647
- categoryCount: String(categoryCount),
648
- contractName: relationship.contract.name,
694
+ consenterProfileId: relationship.consenter.profileId,
695
+ transaction,
696
+ domain,
697
+ messageKey:
698
+ Object.keys(categories).length === 1
699
+ ? 'consentFlowTransactionSyncedSingle'
700
+ : 'consentFlowTransactionSyncedPlural',
649
701
  }
650
- ),
651
- data: { transaction },
652
- });
702
+ ).return('terms.id AS id')
703
+ );
704
+ assertAudienceMutation(result.records.length);
705
+
706
+ await tryDispatchContractEvent(transaction.id);
653
707
 
654
708
  return result.summary.counters.containsUpdates();
655
709
  };
@@ -720,24 +774,12 @@ export const pruneDeletedUrisFromConsentTerms = async (
720
774
  ],
721
775
  })
722
776
  .set('transaction += $transactionParams')
777
+ .raw(termsReferralSnapshotCypher())
723
778
  .run();
724
779
 
725
780
  return result.summary.counters.containsUpdates() ? removedSharedUris : 0;
726
781
  };
727
782
 
728
- export const updateRequestedForStatusIfExists = async (
729
- id: string,
730
- profileId: string,
731
- status: 'pending' | 'accepted' | 'denied'
732
- ): Promise<void> => {
733
- const cypher = `
734
- MATCH (contract:ConsentFlowContract {id: $id})-[r:REQUESTED_FOR]->(profile:Profile {profileId: $profileId})
735
- SET r.status = $status
736
- `;
737
-
738
- await neogma.queryRunner.run(cypher, { id, profileId, status });
739
- };
740
-
741
783
  export const upsertRequestedForRelationship = async (
742
784
  id: string,
743
785
  profileId: string,
@@ -746,7 +788,11 @@ export const upsertRequestedForRelationship = async (
746
788
  ) => {
747
789
  const cypher = `
748
790
  MATCH (contract:ConsentFlowContract {id: $id})
791
+ SET contract.audienceLock = coalesce(contract.audienceLock, 0) + 1
792
+ WITH contract
749
793
  MATCH (profile:Profile {profileId: $profileId})
794
+ OPTIONAL MATCH (contract)-[existing:REQUESTED_FOR]->(profile)
795
+ WITH contract, profile, existing WHERE existing.requestId IS NULL
750
796
  MERGE (contract)-[r:REQUESTED_FOR]->(profile)
751
797
  ${status !== undefined ? 'SET r.status = $status' : ''}
752
798
  ${readStatus !== undefined ? 'SET r.readStatus = $readStatus' : ''}
@@ -757,5 +803,10 @@ export const upsertRequestedForRelationship = async (
757
803
  if (status !== undefined) params.status = status;
758
804
  if (readStatus !== undefined) params.readStatus = readStatus;
759
805
 
760
- await neogma.queryRunner.run(cypher, params);
806
+ const result = await neogma.queryRunner.run(cypher, params);
807
+ if (!result.records.length)
808
+ throw new TRPCError({
809
+ code: 'CONFLICT',
810
+ message: 'An attributed request already exists for this profile.',
811
+ });
761
812
  };