@learncard/network-brain-service 4.1.2 → 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.
- package/.env.example +14 -0
- package/CHANGELOG.md +164 -0
- package/README.md +27 -1
- package/bootstrapLambda.test.ts +70 -32
- package/config/config.dev.json +1 -0
- package/config/config.example.json +2 -1
- package/config/config.production.json +1 -0
- package/config/config.scouts.dev.json +1 -0
- package/config/config.scouts.production.json +1 -0
- package/consentFlowDeadlineLambda.test.ts +108 -0
- package/consentFlowDeadlineLambda.ts +18 -0
- package/consentFlowDeadlineLambdaApp.ts +14 -0
- package/dist/accesslayer/boost/create.d.ts +8 -2
- package/dist/accesslayer/boost/create.d.ts.map +1 -1
- package/dist/accesslayer/campaign/audience.d.ts +4 -4
- package/dist/accesslayer/campaign/audience.d.ts.map +1 -1
- package/dist/accesslayer/campaign/create.d.ts +1 -1
- package/dist/accesslayer/campaign/create.d.ts.map +1 -1
- package/dist/accesslayer/campaign/delivery.d.ts +5 -0
- package/dist/accesslayer/campaign/delivery.d.ts.map +1 -0
- package/dist/accesslayer/campaign/dispatch.d.ts +5 -0
- package/dist/accesslayer/campaign/dispatch.d.ts.map +1 -0
- package/dist/accesslayer/campaign/dispatch.test.d.ts +2 -0
- package/dist/accesslayer/campaign/dispatch.test.d.ts.map +1 -0
- package/dist/accesslayer/campaign/email.d.ts +10 -0
- package/dist/accesslayer/campaign/email.d.ts.map +1 -0
- package/dist/accesslayer/campaign/email.test.d.ts +2 -0
- package/dist/accesslayer/campaign/email.test.d.ts.map +1 -0
- package/dist/accesslayer/campaign/preferences.d.ts +5 -0
- package/dist/accesslayer/campaign/preferences.d.ts.map +1 -0
- package/dist/accesslayer/campaign/read.d.ts.map +1 -1
- package/dist/accesslayer/campaign/test.d.ts +5 -0
- package/dist/accesslayer/campaign/test.d.ts.map +1 -0
- package/dist/accesslayer/campaign/test.test.d.ts +2 -0
- package/dist/accesslayer/campaign/test.test.d.ts.map +1 -0
- package/dist/accesslayer/campaign/update.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/delete.d.ts +1 -1
- package/dist/accesslayer/consentflowcontract/delete.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +2 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +19 -4
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +6 -1
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/versions.d.ts +68 -0
- package/dist/accesslayer/consentflowcontract/versions.d.ts.map +1 -0
- package/dist/accesslayer/credential/create.d.ts +3 -1
- package/dist/accesslayer/credential/create.d.ts.map +1 -1
- package/dist/accesslayer/credential/delete.d.ts +1 -0
- package/dist/accesslayer/credential/delete.d.ts.map +1 -1
- package/dist/accesslayer/credential/relationships/create.d.ts +15 -3
- package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/credential/relationships/read.d.ts +2 -0
- package/dist/accesslayer/credential/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/credential/relationships/update.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/read.d.ts +1 -0
- package/dist/accesslayer/credential-refresh/read.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/update.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/create.d.ts +1 -0
- package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/refresh.d.ts +7 -0
- package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -1
- package/dist/app.d.ts +11 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/cache/app-store.caches.d.ts +61 -1
- package/dist/cache/app-store.caches.d.ts.map +1 -1
- package/dist/campaign-unsubscribe.d.ts +7 -0
- package/dist/campaign-unsubscribe.d.ts.map +1 -0
- package/dist/campaign-unsubscribe.test.d.ts +2 -0
- package/dist/campaign-unsubscribe.test.d.ts.map +1 -0
- package/dist/config/environment.d.ts +42 -0
- package/dist/config/environment.d.ts.map +1 -1
- package/dist/config/stageConfig.d.ts +4 -0
- package/dist/config/stageConfig.d.ts.map +1 -1
- package/dist/credential-refresh.d.ts.map +1 -1
- package/dist/helpers/boost.helpers.d.ts +4 -1
- package/dist/helpers/boost.helpers.d.ts.map +1 -1
- package/dist/helpers/campaign-app-origin.helpers.d.ts +2 -0
- package/dist/helpers/campaign-app-origin.helpers.d.ts.map +1 -0
- package/dist/helpers/campaign-email.helpers.d.ts +13 -0
- package/dist/helpers/campaign-email.helpers.d.ts.map +1 -0
- package/dist/helpers/campaign-notifications.helpers.d.ts +7 -0
- package/dist/helpers/campaign-notifications.helpers.d.ts.map +1 -0
- package/dist/helpers/campaign-queue.helpers.d.ts +7 -0
- package/dist/helpers/campaign-queue.helpers.d.ts.map +1 -0
- package/dist/helpers/campaign-queue.helpers.test.d.ts +2 -0
- package/dist/helpers/campaign-queue.helpers.test.d.ts.map +1 -0
- package/dist/helpers/campaign-unsubscribe-page.d.ts +2 -0
- package/dist/helpers/campaign-unsubscribe-page.d.ts.map +1 -0
- package/dist/helpers/campaign-unsubscribe.helpers.d.ts +7 -0
- package/dist/helpers/campaign-unsubscribe.helpers.d.ts.map +1 -0
- package/dist/helpers/campaign-webhook.helpers.test.d.ts +2 -0
- package/dist/helpers/campaign-webhook.helpers.test.d.ts.map +1 -0
- package/dist/helpers/consentflow-deadline.helpers.d.ts +5 -0
- package/dist/helpers/consentflow-deadline.helpers.d.ts.map +1 -0
- package/dist/helpers/consentflow-legacy-migration.helpers.d.ts +2 -0
- package/dist/helpers/consentflow-legacy-migration.helpers.d.ts.map +1 -0
- package/dist/helpers/contract-events.helpers.d.ts.map +1 -1
- package/dist/helpers/contract.helpers.d.ts +1 -1
- package/dist/helpers/contract.helpers.d.ts.map +1 -1
- package/dist/helpers/credential-display.helpers.d.ts +5 -0
- package/dist/helpers/credential-display.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-display.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-display.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -1
- package/dist/helpers/credential-refresh-materiality.helpers.d.ts +4 -0
- package/dist/helpers/credential-refresh-materiality.helpers.d.ts.map +1 -1
- package/dist/helpers/credential-refresh.helpers.d.ts +30 -1
- package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
- package/dist/helpers/credential-status-mutation.helpers.d.ts +5 -0
- package/dist/helpers/credential-status-mutation.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-status-mutation.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-status-mutation.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential.helpers.d.ts +1 -1
- package/dist/helpers/credential.helpers.d.ts.map +1 -1
- package/dist/helpers/inbox-refresh.helpers.d.ts +4 -2
- package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -1
- package/dist/helpers/inbox.helpers.d.ts +1 -0
- package/dist/helpers/inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/learnCard.helpers.d.ts +1 -0
- package/dist/helpers/learnCard.helpers.d.ts.map +1 -1
- package/dist/helpers/managed-status.helpers.d.ts +7 -0
- package/dist/helpers/managed-status.helpers.d.ts.map +1 -0
- package/dist/helpers/managed-status.helpers.test.d.ts +2 -0
- package/dist/helpers/managed-status.helpers.test.d.ts.map +1 -0
- package/dist/helpers/notification-delivery-boundary.helpers.test.d.ts +2 -0
- package/dist/helpers/notification-delivery-boundary.helpers.test.d.ts.map +1 -0
- package/dist/helpers/notification-webhook-fetch.helpers.d.ts +13 -0
- package/dist/helpers/notification-webhook-fetch.helpers.d.ts.map +1 -0
- package/dist/helpers/notification-webhook-fetch.helpers.test.d.ts +2 -0
- package/dist/helpers/notification-webhook-fetch.helpers.test.d.ts.map +1 -0
- package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
- package/dist/helpers/notifications.helpers.d.ts.map +1 -1
- package/dist/helpers/refresh-send-intent.helpers.d.ts +13 -5
- package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -1
- package/dist/helpers/refresh-send-intent.helpers.test.d.ts +2 -0
- package/dist/helpers/refresh-send-intent.helpers.test.d.ts.map +1 -0
- package/dist/helpers/send-contract.helpers.d.ts +11 -0
- package/dist/helpers/send-contract.helpers.d.ts.map +1 -0
- package/dist/helpers/status-list.helpers.d.ts +12 -1
- package/dist/helpers/status-list.helpers.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/models/ConsentFlowContract.d.ts.map +1 -1
- package/dist/models/ConsentFlowContractVersion.d.ts +12 -0
- package/dist/models/ConsentFlowContractVersion.d.ts.map +1 -0
- package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
- package/dist/models/Credential.d.ts +2 -0
- package/dist/models/Credential.d.ts.map +1 -1
- package/dist/models/CredentialRefresh.d.ts +5 -0
- package/dist/models/CredentialRefresh.d.ts.map +1 -1
- package/dist/models/Integration.d.ts +1 -1
- package/dist/models/Profile.d.ts.map +1 -1
- package/dist/models/campaign-constraints.d.ts.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/publish-metafile.json +1 -1
- package/dist/routes/activity.d.ts +15 -5
- package/dist/routes/activity.d.ts.map +1 -1
- package/dist/routes/app-store.d.ts +15 -5
- package/dist/routes/app-store.d.ts.map +1 -1
- package/dist/routes/auth-grants.d.ts +12 -2
- package/dist/routes/auth-grants.d.ts.map +1 -1
- package/dist/routes/bench.d.ts +11 -1
- package/dist/routes/bench.d.ts.map +1 -1
- package/dist/routes/boosts.d.ts +70 -15
- package/dist/routes/boosts.d.ts.map +1 -1
- package/dist/routes/campaigns.d.ts +183 -51
- package/dist/routes/campaigns.d.ts.map +1 -1
- package/dist/routes/claim-hooks.d.ts +11 -1
- package/dist/routes/claim-hooks.d.ts.map +1 -1
- package/dist/routes/contact-methods.d.ts +11 -1
- package/dist/routes/contact-methods.d.ts.map +1 -1
- package/dist/routes/contracts.d.ts +374 -8
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/credential-input-privacy.test.d.ts +2 -0
- package/dist/routes/credential-input-privacy.test.d.ts.map +1 -0
- package/dist/routes/credential-refreshes.d.ts +210 -1
- package/dist/routes/credential-refreshes.d.ts.map +1 -1
- package/dist/routes/credential-share-previews.d.ts +11 -1
- package/dist/routes/credential-share-previews.d.ts.map +1 -1
- package/dist/routes/credentials.d.ts +132 -3
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/did-metadata.d.ts +11 -1
- package/dist/routes/did-metadata.d.ts.map +1 -1
- package/dist/routes/federation.d.ts +11 -1
- package/dist/routes/federation.d.ts.map +1 -1
- package/dist/routes/inbox.d.ts +54 -19
- package/dist/routes/inbox.d.ts.map +1 -1
- package/dist/routes/index.d.ts +100 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/integrations.d.ts +14 -4
- package/dist/routes/integrations.d.ts.map +1 -1
- package/dist/routes/presentations.d.ts +11 -1
- package/dist/routes/presentations.d.ts.map +1 -1
- package/dist/routes/profile-manager.d.ts +11 -1
- package/dist/routes/profile-manager.d.ts.map +1 -1
- package/dist/routes/profiles.d.ts +11 -1
- package/dist/routes/profiles.d.ts.map +1 -1
- package/dist/routes/public-share-links.d.ts +22 -2
- package/dist/routes/public-share-links.d.ts.map +1 -1
- package/dist/routes/share-links.d.ts +36 -16
- package/dist/routes/share-links.d.ts.map +1 -1
- package/dist/routes/skill-frameworks.d.ts +11 -1
- package/dist/routes/skill-frameworks.d.ts.map +1 -1
- package/dist/routes/skills.d.ts +11 -1
- package/dist/routes/skills.d.ts.map +1 -1
- package/dist/routes/storage.d.ts +37 -23
- package/dist/routes/storage.d.ts.map +1 -1
- package/dist/routes/test.d.ts +79 -3
- package/dist/routes/test.d.ts.map +1 -1
- package/dist/routes/utilities.d.ts +11 -1
- package/dist/routes/utilities.d.ts.map +1 -1
- package/dist/routes/workflows.d.ts +11 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.d.ts +3 -0
- package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
- package/dist/services/delivery/delivery.service.d.ts +10 -0
- package/dist/services/delivery/delivery.service.d.ts.map +1 -1
- package/dist/types/activity.d.ts +3 -3
- package/dist/types/app-manifest.d.ts +10 -10
- package/dist/types/campaign.d.ts +13 -1
- package/dist/types/campaign.d.ts.map +1 -1
- package/dist/types/consentflowcontract.d.ts +175 -7
- package/dist/types/consentflowcontract.d.ts.map +1 -1
- package/dist/types/credential-refresh.d.ts +27 -0
- package/dist/types/credential-refresh.d.ts.map +1 -1
- package/dist/types/credential.d.ts +5 -0
- package/dist/types/credential.d.ts.map +1 -1
- package/dist/types/integration.d.ts +3 -3
- package/lambda.ts +44 -14
- package/lambdaApp.ts +20 -0
- package/package.json +20 -19
- package/project.json +5 -0
- package/scripts/migrate-consent-flow-versions.ts +9 -0
- package/scripts/report-send-template-exposure.ts +47 -0
- package/scripts/verify-python-contracts.ts +16 -1
- package/serverless-local.yml +15 -2
- package/serverless.function-env.cjs +4 -0
- package/serverless.local.yml +12 -0
- package/serverless.yml +48 -3
- package/src/accesslayer/boost/create.ts +62 -7
- package/src/accesslayer/campaign/README.md +275 -6
- package/src/accesslayer/campaign/audience.ts +39 -7
- package/src/accesslayer/campaign/create.ts +4 -2
- package/src/accesslayer/campaign/delivery.ts +66 -0
- package/src/accesslayer/campaign/dispatch.test.ts +59 -0
- package/src/accesslayer/campaign/dispatch.ts +172 -0
- package/src/accesslayer/campaign/email.test.ts +73 -0
- package/src/accesslayer/campaign/email.ts +91 -0
- package/src/accesslayer/campaign/preferences.ts +53 -0
- package/src/accesslayer/campaign/read.ts +3 -1
- package/src/accesslayer/campaign/state.ts +1 -1
- package/src/accesslayer/campaign/test.test.ts +31 -0
- package/src/accesslayer/campaign/test.ts +75 -0
- package/src/accesslayer/campaign/update.ts +15 -4
- package/src/accesslayer/consentflowcontract/create.ts +20 -1
- package/src/accesslayer/consentflowcontract/delete.ts +121 -3
- package/src/accesslayer/consentflowcontract/read.ts +15 -9
- package/src/accesslayer/consentflowcontract/relationships/create.ts +70 -36
- package/src/accesslayer/consentflowcontract/relationships/read.ts +305 -73
- package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +1 -1
- package/src/accesslayer/consentflowcontract/relationships/update.ts +269 -98
- package/src/accesslayer/consentflowcontract/versions.ts +790 -0
- package/src/accesslayer/credential/create.ts +33 -9
- package/src/accesslayer/credential/delete.ts +45 -0
- package/src/accesslayer/credential/relationships/create.ts +102 -47
- package/src/accesslayer/credential/relationships/update.ts +8 -116
- package/src/accesslayer/credential-refresh/read.ts +26 -2
- package/src/accesslayer/credential-refresh/update.ts +6 -2
- package/src/accesslayer/inbox-credential/create.ts +7 -1
- package/src/accesslayer/inbox-credential/refresh.ts +14 -4
- package/src/campaign-unsubscribe.test.ts +158 -0
- package/src/campaign-unsubscribe.ts +86 -0
- package/src/config/environment.test.ts +52 -0
- package/src/config/environment.ts +58 -0
- package/src/config/serverlessFunctionEnv.test.ts +59 -0
- package/src/credential-refresh.ts +1 -0
- package/src/docker-entry.ts +26 -1
- package/src/helpers/boost-storage.helpers.test.ts +107 -41
- package/src/helpers/boost.helpers.ts +88 -40
- package/src/helpers/campaign-app-origin.helpers.ts +25 -0
- package/src/helpers/campaign-email.helpers.ts +113 -0
- package/src/helpers/campaign-notifications.helpers.ts +127 -0
- package/src/helpers/campaign-queue.helpers.test.ts +442 -0
- package/src/helpers/campaign-queue.helpers.ts +122 -0
- package/src/helpers/campaign-unsubscribe-page.ts +107 -0
- package/src/helpers/campaign-unsubscribe.helpers.ts +56 -0
- package/src/helpers/campaign-webhook.helpers.test.ts +137 -0
- package/src/helpers/consentflow-deadline.helpers.ts +22 -0
- package/src/helpers/consentflow-legacy-migration.helpers.ts +179 -0
- package/src/helpers/contract-events.helpers.ts +35 -3
- package/src/helpers/contract-requests.helpers.ts +1 -1
- package/src/helpers/contract.helpers.ts +5 -1
- package/src/helpers/credential-display.helpers.test.ts +68 -0
- package/src/helpers/credential-display.helpers.ts +15 -0
- package/src/helpers/credential-refresh-email.helpers.ts +5 -12
- package/src/helpers/credential-refresh-materiality.helpers.test.ts +138 -1
- package/src/helpers/credential-refresh-materiality.helpers.ts +62 -1
- package/src/helpers/credential-refresh-proof.helpers.test.ts +1 -1
- package/src/helpers/credential-refresh-proof.helpers.ts +2 -2
- package/src/helpers/credential-refresh.helpers.ts +1166 -376
- package/src/helpers/credential-status-mutation.helpers.test.ts +155 -0
- package/src/helpers/credential-status-mutation.helpers.ts +166 -0
- package/src/helpers/credential.helpers.ts +19 -11
- package/src/helpers/finalize-inbox.helpers.ts +1 -1
- package/src/helpers/inbox-refresh.helpers.ts +34 -8
- package/src/helpers/inbox.helpers.ts +2 -0
- package/src/helpers/learnCard.helpers.ts +23 -0
- package/src/helpers/managed-status.helpers.test.ts +101 -0
- package/src/helpers/managed-status.helpers.ts +147 -0
- package/src/helpers/notification-delivery-boundary.helpers.test.ts +114 -0
- package/src/helpers/notification-webhook-fetch.helpers.test.ts +228 -0
- package/src/helpers/notification-webhook-fetch.helpers.ts +166 -0
- package/src/helpers/notificationQueue.helpers.ts +5 -0
- package/src/helpers/notifications.helpers.ts +19 -12
- package/src/helpers/refresh-send-intent.helpers.test.ts +60 -0
- package/src/helpers/refresh-send-intent.helpers.ts +81 -18
- package/src/helpers/send-contract.helpers.ts +36 -0
- package/src/helpers/status-list.helpers.test.ts +29 -12
- package/src/helpers/status-list.helpers.ts +329 -20
- package/src/models/ConsentFlowContract.ts +3 -0
- package/src/models/ConsentFlowContractVersion.ts +50 -0
- package/src/models/ConsentFlowTerms.ts +4 -0
- package/src/models/Credential.ts +2 -0
- package/src/models/CredentialRefresh.ts +2 -0
- package/src/models/Profile.ts +2 -0
- package/src/models/campaign-constraints.ts +13 -8
- package/src/models/index.ts +2 -0
- package/src/openapi.ts +21 -0
- package/src/routes/app-store.ts +2 -1
- package/src/routes/boosts.ts +219 -137
- package/src/routes/campaigns.ts +98 -19
- package/src/routes/contracts.ts +433 -115
- package/src/routes/credential-input-privacy.test.ts +92 -0
- package/src/routes/credential-refreshes.ts +273 -2
- package/src/routes/federation.ts +7 -2
- package/src/routes/inbox.ts +6 -1
- package/src/routes/index.ts +31 -2
- package/src/routes/workflows.ts +61 -46
- package/src/services/delivery/adapters/postmark.adapter.test.ts +70 -1
- package/src/services/delivery/adapters/postmark.adapter.ts +84 -11
- package/src/services/delivery/delivery.service.ts +4 -0
- package/src/types/campaign.ts +14 -1
- package/src/types/consentflowcontract.ts +43 -2
- package/src/types/credential-refresh.ts +11 -1
- package/src/types/credential.ts +2 -0
- package/test/boost-status-cleanup.spec.ts +123 -0
- package/test/campaigns-email.unit.spec.ts +268 -0
- package/test/campaigns.spec.ts +804 -30
- package/test/consent-autoboost-idempotency.spec.ts +432 -0
- package/test/consent-data-boundaries.spec.ts +322 -26
- package/test/consent-legacy-migration.spec.ts +315 -0
- package/test/consent-recipients.spec.ts +7 -7
- package/test/consent-version-updates.spec.ts +761 -0
- package/test/consentflow.spec.ts +698 -226
- package/test/contract-requests.spec.ts +55 -9
- package/test/credential-refresh-allocation.spec.ts +212 -14
- package/test/credential-refresh-email.spec.ts +6 -10
- package/test/credential-refresh-endpoint.spec.ts +3 -0
- package/test/credential-refresh-publication.spec.ts +52 -15
- package/test/credential-storage.neo4j.spec.ts +33 -6
- package/test/helpers/legacy-autoboost.ts +48 -0
- package/test/holder-mode-send.spec.ts +710 -0
- package/test/inbox-refresh.spec.ts +105 -0
- package/test/inbox-security.e2e.ts +18 -22
- package/test/managed-status-revocation.spec.ts +970 -0
- package/test/notifications-worker.spec.ts +31 -25
- package/test/refresh-send.spec.ts +334 -5
- package/test/smart-resume-consent.spec.ts +1 -1
- package/test/verification/campaigns.ts +40 -0
- package/vitest.campaigns.config.ts +2 -0
- 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(
|
|
463
|
-
|
|
464
|
-
);
|
|
465
|
-
expect(
|
|
466
|
-
|
|
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', () => {
|