@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
package/lambdaApp.ts
CHANGED
|
@@ -13,9 +13,11 @@ import app from './src/openapi';
|
|
|
13
13
|
import skillsViewerApp from './src/skills-viewer';
|
|
14
14
|
import statusListsApp from './src/status-lists';
|
|
15
15
|
import credentialRefreshApp from './src/credential-refresh';
|
|
16
|
+
import campaignUnsubscribeApp from './src/campaign-unsubscribe';
|
|
16
17
|
import { appRouter, createContext } from './src/app';
|
|
17
18
|
import { publicShareLinkCacheControlHeaders } from './src/routes';
|
|
18
19
|
import { deliverQueuedNotification } from './src/helpers/notificationQueue.helpers';
|
|
20
|
+
import { processCampaignDispatchJob } from './src/helpers/campaign-queue.helpers';
|
|
19
21
|
import { startSkillEmbeddingBackfill } from './src/helpers/skill-embedding.helpers';
|
|
20
22
|
import { createOpenApiAwsLambdaHandler } from './src/helpers/shim';
|
|
21
23
|
import {
|
|
@@ -64,6 +66,17 @@ export const credentialRefreshHandler: typeof credentialRefreshProxy = async (ev
|
|
|
64
66
|
return credentialRefreshProxy(event, context);
|
|
65
67
|
};
|
|
66
68
|
|
|
69
|
+
const campaignUnsubscribeProxy = serverlessHttp(
|
|
70
|
+
toServerlessApplication(campaignUnsubscribeApp.server)
|
|
71
|
+
);
|
|
72
|
+
export const campaignUnsubscribeHandler: typeof campaignUnsubscribeProxy = async (
|
|
73
|
+
event,
|
|
74
|
+
context
|
|
75
|
+
) => {
|
|
76
|
+
await campaignUnsubscribeApp.ready();
|
|
77
|
+
return campaignUnsubscribeProxy(event, context);
|
|
78
|
+
};
|
|
79
|
+
|
|
67
80
|
export const _openApiHandler = createOpenApiAwsLambdaHandler({
|
|
68
81
|
router: appRouter,
|
|
69
82
|
responseMeta: meta => {
|
|
@@ -197,6 +210,13 @@ export const notificationsWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(asyn
|
|
|
197
210
|
} satisfies SQSBatchResponse;
|
|
198
211
|
});
|
|
199
212
|
|
|
213
|
+
export const campaignDispatchWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
214
|
+
// batchSize=1; throwing lets SQS apply the construct's retry/DLQ policy.
|
|
215
|
+
for (const record of event.Records) {
|
|
216
|
+
await processCampaignDispatchJob(JSON.parse(record.body));
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
200
220
|
export const inboxMaintenanceHandler = Sentry.AWSLambda.wrapHandler(async (): Promise<void> => {
|
|
201
221
|
const counts = await runInboxMaintenance();
|
|
202
222
|
console.log('Universal Inbox maintenance completed', counts);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/network-brain-service",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24.12 <25"
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"test:boost-recipients": "vitest run --config vitest.boost-recipients-neo4j.config.ts",
|
|
24
24
|
"seed:dev-app": "bun scripts/seed-dev-app.ts",
|
|
25
25
|
"seed:demo-persona": "bun --conditions=development scripts/seed-demo-persona.ts",
|
|
26
|
+
"migrate:consent-flow-versions": "bun --conditions=development scripts/migrate-consent-flow-versions.ts",
|
|
26
27
|
"skill-frameworks": "bun scripts/skill-frameworks.ts",
|
|
27
28
|
"skill-embeddings:backfill": "bun --conditions=development scripts/skill-embeddings-backfill.ts",
|
|
28
29
|
"backfill:profile-search": "bun scripts/backfill-profile-search.ts",
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
"start-p-4000": "PORT=4000 serverless offline --httpPort 4000 --websocketPort 4001 --lambdaPort 4002 --albPort 4003 --host 127.0.0.1 --config serverless-local.yml",
|
|
34
35
|
"dev": "bun --conditions=development --watch src/docker-entry.ts",
|
|
35
36
|
"start:docker": "bun --conditions=development src/docker-entry.ts",
|
|
36
|
-
"serverless-deploy": "node ../../../scripts/prepare-didkit-native-layer.cjs && serverless deploy --verbose"
|
|
37
|
+
"serverless-deploy": "node ../../../scripts/prepare-didkit-native-layer.cjs && node ../../../scripts/verify-node24-didkit-layer.cjs && serverless deploy --verbose"
|
|
37
38
|
},
|
|
38
39
|
"keywords": [],
|
|
39
40
|
"author": "Learning Economy Foundation (www.learningeconomy.io)",
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"serverless": "3.40.0",
|
|
55
56
|
"serverless-certificate-creator": "1.6.0",
|
|
56
57
|
"serverless-domain-manager": "6.3.1",
|
|
57
|
-
"serverless-esbuild": "1.
|
|
58
|
+
"serverless-esbuild": "1.57.2",
|
|
58
59
|
"serverless-http": "3.2.0",
|
|
59
60
|
"serverless-lift": "1.31.0",
|
|
60
61
|
"serverless-offline": "^14.4.0",
|
|
@@ -68,23 +69,23 @@
|
|
|
68
69
|
"@digitalcredentials/issuer-registry-client": "^4.1.0",
|
|
69
70
|
"@fastify/cors": "^11.2.0",
|
|
70
71
|
"@google/genai": "^1.27.0",
|
|
71
|
-
"@learncard/credential-library": "2.1.
|
|
72
|
-
"@learncard/core": "9.4.
|
|
73
|
-
"@learncard/crypto-plugin": "1.1.
|
|
74
|
-
"@learncard/did-web-plugin": "1.1.
|
|
75
|
-
"@learncard/didkey-plugin": "1.1.
|
|
76
|
-
"@learncard/didkit-plugin": "1.10.
|
|
77
|
-
"@learncard/didkit-plugin-node": "0.3.
|
|
78
|
-
"@learncard/dynamic-loader-plugin": "1.1.
|
|
79
|
-
"@learncard/encryption-plugin": "1.
|
|
80
|
-
"@learncard/expiration-plugin": "1.2.
|
|
81
|
-
"@learncard/email-templates": "1.
|
|
82
|
-
"@learncard/helpers": "1.
|
|
83
|
-
"@learncard/learn-card-plugin": "1.2.
|
|
72
|
+
"@learncard/credential-library": "2.1.7",
|
|
73
|
+
"@learncard/core": "9.4.44",
|
|
74
|
+
"@learncard/crypto-plugin": "1.1.44",
|
|
75
|
+
"@learncard/did-web-plugin": "1.1.44",
|
|
76
|
+
"@learncard/didkey-plugin": "1.1.44",
|
|
77
|
+
"@learncard/didkit-plugin": "1.10.10",
|
|
78
|
+
"@learncard/didkit-plugin-node": "0.3.10",
|
|
79
|
+
"@learncard/dynamic-loader-plugin": "1.1.44",
|
|
80
|
+
"@learncard/encryption-plugin": "1.2.0",
|
|
81
|
+
"@learncard/expiration-plugin": "1.2.44",
|
|
82
|
+
"@learncard/email-templates": "1.3.0",
|
|
83
|
+
"@learncard/helpers": "1.8.0",
|
|
84
|
+
"@learncard/learn-card-plugin": "1.2.44",
|
|
84
85
|
"@learncard/partner-connect-core": "0.2.0",
|
|
85
|
-
"@learncard/types": "
|
|
86
|
-
"@learncard/vc-plugin": "1.
|
|
87
|
-
"@learncard/vc-templates-plugin": "1.1.
|
|
86
|
+
"@learncard/types": "6.0.0",
|
|
87
|
+
"@learncard/vc-plugin": "1.7.0",
|
|
88
|
+
"@learncard/vc-templates-plugin": "1.1.44",
|
|
88
89
|
"@sentry/serverless": "7.61.0",
|
|
89
90
|
"@trpc/server": "11.8.0",
|
|
90
91
|
"@types/lodash": "^4.14.191",
|
package/project.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { migrateLegacyConsentFlowContracts } from '../src/helpers/consentflow-legacy-migration.helpers';
|
|
2
|
+
|
|
3
|
+
try {
|
|
4
|
+
await migrateLegacyConsentFlowContracts();
|
|
5
|
+
console.info('ConsentFlow contract version migration completed.');
|
|
6
|
+
} catch (error) {
|
|
7
|
+
console.error('ConsentFlow contract version migration failed.', error);
|
|
8
|
+
process.exitCode = 1;
|
|
9
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import neo4j from 'neo4j-driver';
|
|
2
|
+
|
|
3
|
+
/** Read-only, counts-only inventory. Pass the deployment timestamp as the sole argument. */
|
|
4
|
+
const main = async (): Promise<void> => {
|
|
5
|
+
const cutoff = process.argv[2];
|
|
6
|
+
if (!cutoff || !Number.isFinite(Date.parse(cutoff))) {
|
|
7
|
+
throw new Error('A valid deployment timestamp is required');
|
|
8
|
+
}
|
|
9
|
+
const { NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD } = process.env;
|
|
10
|
+
if (!NEO4J_URI || !NEO4J_USERNAME || !NEO4J_PASSWORD) {
|
|
11
|
+
throw new Error('Explicit Neo4j connection environment is required');
|
|
12
|
+
}
|
|
13
|
+
const driver = neo4j.driver(NEO4J_URI, neo4j.auth.basic(NEO4J_USERNAME, NEO4J_PASSWORD));
|
|
14
|
+
const session = driver.session({ defaultAccessMode: neo4j.session.READ });
|
|
15
|
+
try {
|
|
16
|
+
const result = await session.executeRead(tx =>
|
|
17
|
+
tx.run(
|
|
18
|
+
`MATCH (b:Boost)-[created:CREATED_BY]->()
|
|
19
|
+
WHERE created.date < $cutoff
|
|
20
|
+
WITH DISTINCT b
|
|
21
|
+
WITH b, EXISTS { MATCH (r:CredentialRefresh) WHERE r.boostId = b.id }
|
|
22
|
+
OR EXISTS { MATCH (i:RefreshSendIntent) WHERE i.boostUri ENDS WITH ':' + b.id } AS managed,
|
|
23
|
+
EXISTS { MATCH (a:CredentialActivity) WHERE a.source = 'send' AND a.boostUri ENDS WITH ':' + b.id } AS sent
|
|
24
|
+
RETURN count(CASE WHEN managed THEN b END) AS managed,
|
|
25
|
+
count(CASE WHEN sent AND NOT managed THEN b END) AS unclassifiedSendCandidates`,
|
|
26
|
+
{ cutoff: new Date(cutoff).toISOString() }
|
|
27
|
+
)
|
|
28
|
+
);
|
|
29
|
+
// Signed-only sends historically had no distinguishing marker. Candidates are
|
|
30
|
+
// an upper bound, not a claim that every counted Boost contains private data.
|
|
31
|
+
const row = result.records[0];
|
|
32
|
+
console.log(
|
|
33
|
+
JSON.stringify({
|
|
34
|
+
managed: Number(row?.get('managed') ?? 0),
|
|
35
|
+
unclassifiedSendCandidates: Number(row?.get('unclassifiedSendCandidates') ?? 0),
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
} finally {
|
|
39
|
+
await session.close();
|
|
40
|
+
await driver.close();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
main().catch(() => {
|
|
45
|
+
console.error('SEND_TEMPLATE_INVENTORY_FAILED');
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
});
|
|
@@ -21,6 +21,17 @@ configureOfflineSchemaEnvironment();
|
|
|
21
21
|
const { AllocateCredentialRefreshInputValidator, PublishCredentialRefreshInputValidator } =
|
|
22
22
|
await import('@learncard/types');
|
|
23
23
|
const { activityRouter } = await import('../src/routes/activity');
|
|
24
|
+
const { contractsRouter } = await import('../src/routes/contracts');
|
|
25
|
+
const { boostsRouter } = await import('../src/routes/boosts');
|
|
26
|
+
const consentListInput = contractsRouter._def.procedures.getConsentedContracts._def.inputs.at(
|
|
27
|
+
-1
|
|
28
|
+
) as ZodType;
|
|
29
|
+
const bodyInputs: Record<string, ZodType> = {
|
|
30
|
+
'/consent-flow-contract/update':
|
|
31
|
+
contractsRouter._def.procedures.updateConsentFlowContract._def.inputs.at(-1) as ZodType,
|
|
32
|
+
'/boost/skills/search':
|
|
33
|
+
boostsRouter._def.procedures.searchSkillsAvailableForBoost._def.inputs.at(-1) as ZodType,
|
|
34
|
+
};
|
|
24
35
|
|
|
25
36
|
const activityInputs: Record<string, ZodType> = {
|
|
26
37
|
'/activity/credentials': activityRouter._def.procedures.getMyActivities._def.inputs.at(
|
|
@@ -38,6 +49,10 @@ for (const record of records) {
|
|
|
38
49
|
valid = PublishCredentialRefreshInputValidator.safeParse(record.body).success;
|
|
39
50
|
} else if (url.pathname === '/credential-refresh/allocate') {
|
|
40
51
|
valid = AllocateCredentialRefreshInputValidator.safeParse(record.body).success;
|
|
52
|
+
} else if (url.pathname === '/consent-flow-contracts/consent') {
|
|
53
|
+
valid = consentListInput.safeParse(record.body ?? undefined).success;
|
|
54
|
+
} else if (bodyInputs[url.pathname]) {
|
|
55
|
+
valid = bodyInputs[url.pathname]!.safeParse(record.body).success;
|
|
41
56
|
} else {
|
|
42
57
|
const validator = activityInputs[url.pathname];
|
|
43
58
|
assert(validator, `No source validator for captured path ${url.pathname}`);
|
|
@@ -67,6 +82,6 @@ for (const validator of Object.values(activityInputs)) {
|
|
|
67
82
|
}
|
|
68
83
|
}
|
|
69
84
|
console.log(
|
|
70
|
-
`Verified ${records.length} captured publication/allocation/activity cases and Z-only boundaries against actual source validators`
|
|
85
|
+
`Verified ${records.length} captured publication/allocation/activity/consent/update/skill-search cases and Z-only boundaries against actual source validators`
|
|
71
86
|
);
|
|
72
87
|
process.exit(0);
|
package/serverless-local.yml
CHANGED
|
@@ -7,6 +7,7 @@ frameworkVersion: '3'
|
|
|
7
7
|
useDotenv: true
|
|
8
8
|
|
|
9
9
|
plugins:
|
|
10
|
+
- ../../../scripts/serverless-node24-runtime.cjs
|
|
10
11
|
- serverless-esbuild
|
|
11
12
|
- serverless-offline
|
|
12
13
|
- serverless-lift
|
|
@@ -29,7 +30,7 @@ custom:
|
|
|
29
30
|
minify: true
|
|
30
31
|
external: ['@learncard/didkit-plugin/dist/didkit_wasm_bg.wasm', 'p-limit']
|
|
31
32
|
sourcemap: external
|
|
32
|
-
target: '
|
|
33
|
+
target: 'node24'
|
|
33
34
|
watch:
|
|
34
35
|
pattern: ['src/**/*.ts', 'dids.ts']
|
|
35
36
|
|
|
@@ -41,7 +42,7 @@ package:
|
|
|
41
42
|
|
|
42
43
|
provider:
|
|
43
44
|
name: aws
|
|
44
|
-
runtime:
|
|
45
|
+
runtime: nodejs24.x
|
|
45
46
|
memorySize: 2048
|
|
46
47
|
timeout: 29
|
|
47
48
|
stage: ${opt:stage, "dev"}
|
|
@@ -124,6 +125,18 @@ functions:
|
|
|
124
125
|
- httpApi: GET /docs
|
|
125
126
|
- httpApi: GET /docs/{trpc+}
|
|
126
127
|
|
|
128
|
+
campaignUnsubscribe:
|
|
129
|
+
handler: lambda.campaignUnsubscribeHandler
|
|
130
|
+
vpc:
|
|
131
|
+
securityGroupIds:
|
|
132
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
133
|
+
subnetIds:
|
|
134
|
+
- Ref: PrivateSubnetA
|
|
135
|
+
events:
|
|
136
|
+
- httpApi: POST /campaigns/unsubscribe
|
|
137
|
+
- httpApi: GET /campaigns/unsubscribe/browser
|
|
138
|
+
- httpApi: POST /campaigns/unsubscribe/browser
|
|
139
|
+
|
|
127
140
|
skillsViewer:
|
|
128
141
|
handler: lambda.skillsViewerHandler
|
|
129
142
|
vpc:
|
|
@@ -24,9 +24,12 @@ const PROVIDER_SECRETS = [
|
|
|
24
24
|
'SMART_RESUME_ACCESS_KEY',
|
|
25
25
|
'SMART_RESUME_CONTRACT_URI',
|
|
26
26
|
'CREDENTIAL_REFRESH_DIGEST_SECRET',
|
|
27
|
+
'CREDENTIAL_REFRESH_DIGEST_KEY_VERSION',
|
|
28
|
+
'CREDENTIAL_REFRESH_DIGEST_PREVIOUS_KEYS',
|
|
27
29
|
'SHARE_LINK_REQUEST_HASH_SECRET',
|
|
28
30
|
'APP_STORE_ADMIN_PROFILE_IDS',
|
|
29
31
|
'CAMPAIGN_ADMIN_PROFILE_IDS',
|
|
32
|
+
'CAMPAIGN_UNSUBSCRIBE_SECRET',
|
|
30
33
|
'LOGIN_PROVIDER_DID',
|
|
31
34
|
'NOTIFICATIONS_SERVICE_WEBHOOK_URL',
|
|
32
35
|
];
|
|
@@ -46,6 +49,7 @@ exports.provider = ({ options = {}, env = process.env } = {}) => ({
|
|
|
46
49
|
REDIS_PORT: { 'Fn::GetAtt': ['ElasticCacheCluster', 'RedisEndpoint.Port'] },
|
|
47
50
|
NOTIFICATIONS_QUEUE_URL: '${construct:notifications-queue.queueUrl}',
|
|
48
51
|
INBOX_QUEUE_URL: '${construct:inbox-queue.queueUrl}',
|
|
52
|
+
CAMPAIGN_QUEUE_URL: '${construct:campaign-queue.queueUrl}',
|
|
49
53
|
// The deploy commit, emitted only when set so an empty string never counts toward the
|
|
50
54
|
// 4KB per-function environment limit.
|
|
51
55
|
...pickNonEmpty(['GIT_SHA'], env),
|
package/serverless.local.yml
CHANGED
|
@@ -119,6 +119,18 @@ functions:
|
|
|
119
119
|
- httpApi: GET /docs
|
|
120
120
|
- httpApi: GET /docs/{trpc+}
|
|
121
121
|
|
|
122
|
+
campaignUnsubscribe:
|
|
123
|
+
handler: lambda.campaignUnsubscribeHandler
|
|
124
|
+
vpc:
|
|
125
|
+
securityGroupIds:
|
|
126
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
127
|
+
subnetIds:
|
|
128
|
+
- Ref: PrivateSubnetA
|
|
129
|
+
events:
|
|
130
|
+
- httpApi: POST /campaigns/unsubscribe
|
|
131
|
+
- httpApi: GET /campaigns/unsubscribe/browser
|
|
132
|
+
- httpApi: POST /campaigns/unsubscribe/browser
|
|
133
|
+
|
|
122
134
|
skillsViewer:
|
|
123
135
|
handler: lambda.skillsViewerHandler
|
|
124
136
|
vpc:
|
package/serverless.yml
CHANGED
|
@@ -7,6 +7,7 @@ frameworkVersion: '3'
|
|
|
7
7
|
useDotenv: true
|
|
8
8
|
|
|
9
9
|
plugins:
|
|
10
|
+
- ../../../scripts/serverless-node24-runtime.cjs
|
|
10
11
|
- serverless-esbuild
|
|
11
12
|
- serverless-offline
|
|
12
13
|
- serverless-domain-manager
|
|
@@ -15,6 +16,18 @@ plugins:
|
|
|
15
16
|
- serverless-prune-plugin
|
|
16
17
|
|
|
17
18
|
constructs:
|
|
19
|
+
campaign-queue:
|
|
20
|
+
type: queue
|
|
21
|
+
batchSize: 1
|
|
22
|
+
maxRetries: 3
|
|
23
|
+
worker:
|
|
24
|
+
handler: lambda.campaignDispatchWorker
|
|
25
|
+
timeout: 300
|
|
26
|
+
vpc:
|
|
27
|
+
securityGroupIds:
|
|
28
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
29
|
+
subnetIds:
|
|
30
|
+
- Ref: PrivateSubnetA
|
|
18
31
|
inbox-queue:
|
|
19
32
|
type: queue
|
|
20
33
|
batchSize: 1
|
|
@@ -33,6 +46,14 @@ constructs:
|
|
|
33
46
|
type: queue
|
|
34
47
|
worker:
|
|
35
48
|
handler: lambda.notificationsWorker
|
|
49
|
+
timeout: 30
|
|
50
|
+
# Graph/cache bookkeeping requires the VPC. All webhook HTTP delivery
|
|
51
|
+
# must retain the connection-time public-address and no-redirect guard.
|
|
52
|
+
vpc:
|
|
53
|
+
securityGroupIds:
|
|
54
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
55
|
+
subnetIds:
|
|
56
|
+
- Ref: PrivateSubnetA
|
|
36
57
|
|
|
37
58
|
custom:
|
|
38
59
|
prune:
|
|
@@ -57,7 +78,7 @@ custom:
|
|
|
57
78
|
# serverless-esbuild default and must be preserved when overriding.
|
|
58
79
|
exclude: ['aws-sdk', '@learncard/didkit-plugin-node']
|
|
59
80
|
sourcemap: external
|
|
60
|
-
target: '
|
|
81
|
+
target: 'node24'
|
|
61
82
|
watch:
|
|
62
83
|
pattern: ['src/**/*.ts', 'dids.ts']
|
|
63
84
|
|
|
@@ -126,13 +147,13 @@ layers:
|
|
|
126
147
|
name: ${self:service}-${opt:stage, 'dev'}-didkit-native
|
|
127
148
|
description: Native DIDKit napi addon and runtime DIDKit dependency
|
|
128
149
|
compatibleRuntimes:
|
|
129
|
-
-
|
|
150
|
+
- nodejs24.x
|
|
130
151
|
compatibleArchitectures:
|
|
131
152
|
- x86_64
|
|
132
153
|
|
|
133
154
|
provider:
|
|
134
155
|
name: aws
|
|
135
|
-
runtime:
|
|
156
|
+
runtime: nodejs24.x
|
|
136
157
|
# Attach the native DIDKit layer to every function in this service.
|
|
137
158
|
layers:
|
|
138
159
|
- { Ref: DidkitNativeLambdaLayer }
|
|
@@ -229,6 +250,18 @@ functions:
|
|
|
229
250
|
events:
|
|
230
251
|
- schedule: rate(1 minute)
|
|
231
252
|
|
|
253
|
+
# Suspend consent updates whose response deadline has passed.
|
|
254
|
+
consentFlowDeadline:
|
|
255
|
+
handler: consentFlowDeadlineLambda.consentFlowDeadlineHandler
|
|
256
|
+
timeout: 30
|
|
257
|
+
reservedConcurrency: 1
|
|
258
|
+
vpc:
|
|
259
|
+
securityGroupIds:
|
|
260
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
261
|
+
subnetIds:
|
|
262
|
+
- Ref: PrivateSubnetA
|
|
263
|
+
events:
|
|
264
|
+
- schedule: rate(1 minute)
|
|
232
265
|
# LC-2187 share-link recovery + cleanup. Dedicated entrypoint (no lambda.ts
|
|
233
266
|
# import-time work). Runs automatically with valid service configuration;
|
|
234
267
|
# absent wiring remains inert and partial wiring fails closed. The cadence
|
|
@@ -291,6 +324,18 @@ functions:
|
|
|
291
324
|
- httpApi: GET /docs
|
|
292
325
|
- httpApi: GET /docs/{trpc+}
|
|
293
326
|
|
|
327
|
+
campaignUnsubscribe:
|
|
328
|
+
handler: lambda.campaignUnsubscribeHandler
|
|
329
|
+
vpc:
|
|
330
|
+
securityGroupIds:
|
|
331
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
332
|
+
subnetIds:
|
|
333
|
+
- Ref: PrivateSubnetA
|
|
334
|
+
events:
|
|
335
|
+
- httpApi: POST /campaigns/unsubscribe
|
|
336
|
+
- httpApi: GET /campaigns/unsubscribe/browser
|
|
337
|
+
- httpApi: POST /campaigns/unsubscribe/browser
|
|
338
|
+
|
|
294
339
|
skillsViewer:
|
|
295
340
|
handler: lambda.skillsViewerHandler
|
|
296
341
|
vpc:
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { QueryBuilder, BindParam } from 'neogma';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
UnsignedVC,
|
|
4
|
+
VC,
|
|
5
|
+
CredentialDisplayValidator,
|
|
6
|
+
type CredentialDisplay,
|
|
7
|
+
} from '@learncard/types';
|
|
8
|
+
import { DISPLAY_ONLY_ANCHOR_MARKER } from '@learncard/helpers';
|
|
3
9
|
import { v4 as uuid } from 'uuid';
|
|
4
10
|
|
|
5
11
|
import { Boost, BoostInstance, Profile, AppStoreListing } from '@models';
|
|
@@ -13,10 +19,11 @@ import { ProfileType } from 'types/profile';
|
|
|
13
19
|
import { AppStoreListingType } from 'types/app-store-listing';
|
|
14
20
|
|
|
15
21
|
export const createBoost = async (
|
|
16
|
-
credential: UnsignedVC | VC,
|
|
22
|
+
credential: UnsignedVC | VC | null,
|
|
17
23
|
creator: ProfileType,
|
|
18
24
|
metadata: Omit<BoostType, 'id' | 'boost'> = {},
|
|
19
|
-
domain: string
|
|
25
|
+
domain: string,
|
|
26
|
+
publicDisplay?: CredentialDisplay
|
|
20
27
|
): Promise<BoostInstance> => {
|
|
21
28
|
const id = uuid();
|
|
22
29
|
|
|
@@ -30,10 +37,20 @@ export const createBoost = async (
|
|
|
30
37
|
new BindParam({
|
|
31
38
|
params: {
|
|
32
39
|
id,
|
|
33
|
-
boost:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
boost: credential
|
|
41
|
+
? convertCredentialToBoostTemplateJSON(
|
|
42
|
+
credential,
|
|
43
|
+
getDidWeb(domain, creator.profileId)
|
|
44
|
+
)
|
|
45
|
+
: JSON.stringify({
|
|
46
|
+
'@context': ['https://www.w3.org/ns/credentials/v2'],
|
|
47
|
+
type: ['VerifiableCredential'],
|
|
48
|
+
issuer: getDidWeb(domain, creator.profileId),
|
|
49
|
+
credentialSubject: { id: 'did:example:123' },
|
|
50
|
+
name: publicDisplay?.name ?? 'Credential',
|
|
51
|
+
display: CredentialDisplayValidator.parse(publicDisplay ?? {}),
|
|
52
|
+
[DISPLAY_ONLY_ANCHOR_MARKER]: true,
|
|
53
|
+
}),
|
|
37
54
|
status,
|
|
38
55
|
...((flattenObject as any)(metadata) as any),
|
|
39
56
|
},
|
|
@@ -64,6 +81,44 @@ export const createBoost = async (
|
|
|
64
81
|
return boost;
|
|
65
82
|
};
|
|
66
83
|
|
|
84
|
+
/**
|
|
85
|
+
* `name`/`image` come only from the caller's explicit `display`. `category` may
|
|
86
|
+
* additionally fall back to the inline template's category: consent-flow write
|
|
87
|
+
* authorization (`resolveContractForSend` and the managed-send contract binding
|
|
88
|
+
* query) keys off a Boost's `category` and treats an absent category as
|
|
89
|
+
* unrestricted. Silently dropping the category — rather than the template's actual
|
|
90
|
+
* value — would let a category the recipient never consented to pass as an
|
|
91
|
+
* unrestricted write merely because the caller omitted `display`.
|
|
92
|
+
*/
|
|
93
|
+
export const normalizePublicDisplay = (
|
|
94
|
+
display: CredentialDisplay | undefined,
|
|
95
|
+
template?: { category?: string | null }
|
|
96
|
+
): CredentialDisplay => {
|
|
97
|
+
const category = display?.category ?? template?.category?.slice(0, 280);
|
|
98
|
+
return CredentialDisplayValidator.parse({
|
|
99
|
+
...(display ?? {}),
|
|
100
|
+
...(category ? { category } : {}),
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/** Display-only anchors never receive credential content or inline template metadata,
|
|
105
|
+
* except that `category` may be inherited from the template (see `normalizePublicDisplay`). */
|
|
106
|
+
export const createDisplayOnlyBoost = (
|
|
107
|
+
display: CredentialDisplay | undefined,
|
|
108
|
+
creator: ProfileType,
|
|
109
|
+
domain: string,
|
|
110
|
+
template?: { category?: string | null }
|
|
111
|
+
): Promise<BoostInstance> => {
|
|
112
|
+
const publicDisplay = normalizePublicDisplay(display, template);
|
|
113
|
+
return createBoost(
|
|
114
|
+
null,
|
|
115
|
+
creator,
|
|
116
|
+
{ ...publicDisplay, name: publicDisplay.name ?? 'Credential' },
|
|
117
|
+
domain,
|
|
118
|
+
publicDisplay
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
67
122
|
/**
|
|
68
123
|
* Creates a boost owned by an AppStoreListing (not a Profile).
|
|
69
124
|
* The boost is created with the app's DID as the issuer.
|