@learncard/network-brain-service 3.18.0 → 3.19.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.
- package/.env.example +38 -2
- package/CHANGELOG.md +118 -0
- package/README.md +42 -0
- package/compose.inbox.yml +7 -0
- package/dist/accesslayer/auth-grant/read.d.ts +1 -0
- package/dist/accesslayer/auth-grant/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
- package/dist/accesslayer/credential-activity/create.d.ts.map +1 -1
- package/dist/accesslayer/credential-refresh/email-delivery.d.ts +18 -0
- package/dist/accesslayer/credential-refresh/email-delivery.d.ts.map +1 -0
- package/dist/accesslayer/credential-refresh/index.d.ts +1 -0
- package/dist/accesslayer/credential-refresh/index.d.ts.map +1 -1
- 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-batch/store.d.ts +33 -0
- package/dist/accesslayer/inbox-batch/store.d.ts.map +1 -0
- package/dist/accesslayer/inbox-credential/create.d.ts +5 -0
- package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/refresh.d.ts +71 -0
- package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -0
- package/dist/accesslayer/inbox-credential/update.d.ts +1 -1
- package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
- package/dist/accesslayer/presentation/read.d.ts.map +1 -1
- package/dist/accesslayer/presentation/relationships/create.d.ts +2 -2
- package/dist/accesslayer/presentation/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/presentation/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/share-link/abandon.d.ts +3 -0
- package/dist/accesslayer/share-link/abandon.d.ts.map +1 -0
- package/dist/accesslayer/share-link/cleanup.d.ts +6 -0
- package/dist/accesslayer/share-link/cleanup.d.ts.map +1 -0
- package/dist/accesslayer/share-link/errors.d.ts +8 -0
- package/dist/accesslayer/share-link/errors.d.ts.map +1 -0
- package/dist/accesslayer/share-link/finalize.d.ts +3 -0
- package/dist/accesslayer/share-link/finalize.d.ts.map +1 -0
- package/dist/accesslayer/share-link/helpers.d.ts +56 -0
- package/dist/accesslayer/share-link/helpers.d.ts.map +1 -0
- package/dist/accesslayer/share-link/index.d.ts +12 -0
- package/dist/accesslayer/share-link/index.d.ts.map +1 -0
- package/dist/accesslayer/share-link/list.d.ts +5 -0
- package/dist/accesslayer/share-link/list.d.ts.map +1 -0
- package/dist/accesslayer/share-link/read.d.ts +5 -0
- package/dist/accesslayer/share-link/read.d.ts.map +1 -0
- package/dist/accesslayer/share-link/receipt.d.ts +11 -0
- package/dist/accesslayer/share-link/receipt.d.ts.map +1 -0
- package/dist/accesslayer/share-link/recovery.d.ts +6 -0
- package/dist/accesslayer/share-link/recovery.d.ts.map +1 -0
- package/dist/accesslayer/share-link/reserve.d.ts +4 -0
- package/dist/accesslayer/share-link/reserve.d.ts.map +1 -0
- package/dist/accesslayer/share-link/revoke.d.ts +3 -0
- package/dist/accesslayer/share-link/revoke.d.ts.map +1 -0
- package/dist/accesslayer/share-link/transaction-execution.d.ts +18 -0
- package/dist/accesslayer/share-link/transaction-execution.d.ts.map +1 -0
- package/dist/accesslayer/share-link/transaction.d.ts +15 -0
- package/dist/accesslayer/share-link/transaction.d.ts.map +1 -0
- package/dist/accesslayer/share-link/types.d.ts +370 -0
- package/dist/accesslayer/share-link/types.d.ts.map +1 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/cache/atomic-counter.test-helpers.d.ts +3 -0
- package/dist/cache/atomic-counter.test-helpers.d.ts.map +1 -0
- package/dist/cache/atomic-counter.test.d.ts +2 -0
- package/dist/cache/atomic-counter.test.d.ts.map +1 -0
- package/dist/cache/index.d.ts +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/config/environment.d.ts +55 -0
- package/dist/config/environment.d.ts.map +1 -1
- package/dist/constants/auth-grant.d.ts +2 -0
- package/dist/constants/auth-grant.d.ts.map +1 -1
- package/dist/credential-refresh.d.ts.map +1 -1
- package/dist/helpers/activity.helpers.d.ts +4 -0
- package/dist/helpers/activity.helpers.d.ts.map +1 -1
- package/dist/helpers/boost.helpers.d.ts.map +1 -1
- package/dist/helpers/contact-method.helpers.d.ts.map +1 -1
- package/dist/helpers/contact-method.helpers.test.d.ts +2 -0
- package/dist/helpers/contact-method.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email-content.helpers.d.ts +44 -0
- package/dist/helpers/credential-refresh-email-content.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-email.helpers.d.ts +17 -0
- package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-proof.helpers.d.ts +4 -0
- package/dist/helpers/credential-refresh-proof.helpers.d.ts.map +1 -0
- package/dist/helpers/credential-refresh-proof.helpers.test.d.ts +2 -0
- package/dist/helpers/credential-refresh-proof.helpers.test.d.ts.map +1 -0
- package/dist/helpers/credential-refresh.helpers.d.ts +45 -2
- package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
- package/dist/helpers/credential.helpers.d.ts +2 -1
- package/dist/helpers/credential.helpers.d.ts.map +1 -1
- package/dist/helpers/credentialSubject.helpers.d.ts +3 -0
- package/dist/helpers/credentialSubject.helpers.d.ts.map +1 -0
- package/dist/helpers/credentialSubject.helpers.test.d.ts +2 -0
- package/dist/helpers/credentialSubject.helpers.test.d.ts.map +1 -0
- package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/inbox-batch-http.helpers.d.ts +15 -0
- package/dist/helpers/inbox-batch-http.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch-http.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-batch-http.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-batch-status.helpers.d.ts +3 -0
- package/dist/helpers/inbox-batch-status.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch.helpers.d.ts +10 -0
- package/dist/helpers/inbox-batch.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-batch.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-batch.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-issuance-error.helpers.d.ts +13 -0
- package/dist/helpers/inbox-issuance-error.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-queue.helpers.d.ts +11 -0
- package/dist/helpers/inbox-queue.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox-queue.helpers.test.d.ts +2 -0
- package/dist/helpers/inbox-queue.helpers.test.d.ts.map +1 -0
- package/dist/helpers/inbox-refresh.helpers.d.ts +186 -0
- package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -0
- package/dist/helpers/inbox.helpers.d.ts +9 -1
- package/dist/helpers/inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/notificationMessages.d.ts +1 -1
- package/dist/helpers/notificationMessages.d.ts.map +1 -1
- package/dist/helpers/notifications.helpers.d.ts.map +1 -1
- package/dist/helpers/presentation-metadata.d.ts +4 -0
- package/dist/helpers/presentation-metadata.d.ts.map +1 -0
- package/dist/helpers/presentation.helpers.d.ts +1 -1
- package/dist/helpers/presentation.helpers.d.ts.map +1 -1
- package/dist/helpers/profile.helpers.d.ts +1 -0
- package/dist/helpers/profile.helpers.d.ts.map +1 -1
- package/dist/helpers/rateLimit.helpers.d.ts.map +1 -1
- package/dist/helpers/refresh-send-intent.helpers.d.ts +51 -0
- package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -0
- package/dist/helpers/share-content-client/adapters.d.ts +10 -0
- package/dist/helpers/share-content-client/adapters.d.ts.map +1 -0
- package/dist/helpers/share-content-client/canonical.d.ts +7 -0
- package/dist/helpers/share-content-client/canonical.d.ts.map +1 -0
- package/dist/helpers/share-content-client/client.d.ts +5 -0
- package/dist/helpers/share-content-client/client.d.ts.map +1 -0
- package/dist/helpers/share-content-client/config.d.ts +11 -0
- package/dist/helpers/share-content-client/config.d.ts.map +1 -0
- package/dist/helpers/share-content-client/index.d.ts +6 -0
- package/dist/helpers/share-content-client/index.d.ts.map +1 -0
- package/dist/helpers/share-content-client/responses.d.ts +16 -0
- package/dist/helpers/share-content-client/responses.d.ts.map +1 -0
- package/dist/helpers/share-content-client/types.d.ts +145 -0
- package/dist/helpers/share-content-client/types.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/budget-helpers.d.ts +5 -0
- package/dist/helpers/share-link-coordinator/budget-helpers.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts +3 -0
- package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/coordinator.d.ts +4 -0
- package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/index.d.ts +5 -0
- package/dist/helpers/share-link-coordinator/index.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/recovery-runner.d.ts +9 -0
- package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/runtime.d.ts +11 -0
- package/dist/helpers/share-link-coordinator/runtime.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/stat.d.ts +4 -0
- package/dist/helpers/share-link-coordinator/stat.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/types.d.ts +160 -0
- package/dist/helpers/share-link-coordinator/types.d.ts.map +1 -0
- package/dist/helpers/share-link-lifecycle/index.d.ts +5 -0
- package/dist/helpers/share-link-lifecycle/index.d.ts.map +1 -0
- package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts +4 -0
- package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts.map +1 -0
- package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts +21 -0
- package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts.map +1 -0
- package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts +9 -0
- package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts.map +1 -0
- package/dist/helpers/share-link-lifecycle/types.d.ts +14 -0
- package/dist/helpers/share-link-lifecycle/types.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/budget.d.ts +12 -0
- package/dist/helpers/share-link-maintenance/budget.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/config.d.ts +48 -0
- package/dist/helpers/share-link-maintenance/config.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/index.d.ts +8 -0
- package/dist/helpers/share-link-maintenance/index.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/runner.d.ts +19 -0
- package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/runtime.d.ts +36 -0
- package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/scheduler.d.ts +18 -0
- package/dist/helpers/share-link-maintenance/scheduler.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/telemetry.d.ts +5 -0
- package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/types.d.ts +53 -0
- package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -0
- package/dist/helpers/share-link-owner/config.d.ts +16 -0
- package/dist/helpers/share-link-owner/config.d.ts.map +1 -0
- package/dist/helpers/share-link-owner/diagnostics.d.ts +26 -0
- package/dist/helpers/share-link-owner/diagnostics.d.ts.map +1 -0
- package/dist/helpers/share-link-owner/lazy-initializer.d.ts +2 -0
- package/dist/helpers/share-link-owner/lazy-initializer.d.ts.map +1 -0
- package/dist/helpers/share-link-owner-projection.d.ts +4 -0
- package/dist/helpers/share-link-owner-projection.d.ts.map +1 -0
- package/dist/helpers/share-link-passcode-abuse.d.ts +2 -0
- package/dist/helpers/share-link-passcode-abuse.d.ts.map +1 -0
- package/dist/helpers/share-link-passcode.d.ts +6 -0
- package/dist/helpers/share-link-passcode.d.ts.map +1 -0
- package/dist/helpers/share-link-policy/index.d.ts +4 -0
- package/dist/helpers/share-link-policy/index.d.ts.map +1 -0
- package/dist/helpers/share-link-policy/production.d.ts +13 -0
- package/dist/helpers/share-link-policy/production.d.ts.map +1 -0
- package/dist/helpers/share-link-policy/resolver.d.ts +6 -0
- package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -0
- package/dist/helpers/share-link-policy/types.d.ts +16 -0
- package/dist/helpers/share-link-policy/types.d.ts.map +1 -0
- package/dist/helpers/share-link-public/config.d.ts +13 -0
- package/dist/helpers/share-link-public/config.d.ts.map +1 -0
- package/dist/helpers/share-link-view-notification.d.ts +2 -0
- package/dist/helpers/share-link-view-notification.d.ts.map +1 -0
- package/dist/helpers/shim.d.ts +1 -1
- package/dist/helpers/shim.d.ts.map +1 -1
- package/dist/inbox-queue-local.d.ts +2 -0
- package/dist/inbox-queue-local.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/models/AuthGrant.d.ts.map +1 -1
- package/dist/models/CredentialActivity.d.ts +1 -0
- package/dist/models/CredentialActivity.d.ts.map +1 -1
- package/dist/models/CredentialRefresh.d.ts +5 -2
- package/dist/models/CredentialRefresh.d.ts.map +1 -1
- package/dist/models/InboxCredential.d.ts +1 -0
- package/dist/models/InboxCredential.d.ts.map +1 -1
- package/dist/models/Presentation.d.ts +2 -0
- package/dist/models/Presentation.d.ts.map +1 -1
- package/dist/models/Profile.d.ts +2 -0
- package/dist/models/Profile.d.ts.map +1 -1
- package/dist/models/ShareLink.d.ts +44 -0
- package/dist/models/ShareLink.d.ts.map +1 -0
- package/dist/models/ShareViewReceipt.d.ts +22 -0
- package/dist/models/ShareViewReceipt.d.ts.map +1 -0
- package/dist/models/credential-refresh-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/models/share-link-constraints.d.ts +3 -0
- package/dist/models/share-link-constraints.d.ts.map +1 -0
- package/dist/openapi.d.ts.map +1 -1
- package/dist/public-share-link-http.d.ts +4 -0
- package/dist/public-share-link-http.d.ts.map +1 -0
- package/dist/routes/act-as.test.d.ts +2 -0
- package/dist/routes/act-as.test.d.ts.map +1 -0
- package/dist/routes/activity.d.ts +3 -0
- package/dist/routes/activity.d.ts.map +1 -1
- package/dist/routes/app-store.d.ts.map +1 -1
- package/dist/routes/auth-grants.d.ts +10 -0
- package/dist/routes/auth-grants.d.ts.map +1 -1
- package/dist/routes/boosts.d.ts +494 -0
- package/dist/routes/boosts.d.ts.map +1 -1
- package/dist/routes/contracts.d.ts +2 -2
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/credential-refreshes.d.ts.map +1 -1
- package/dist/routes/credentials.d.ts +1 -1
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/inbox.d.ts +953 -11
- package/dist/routes/inbox.d.ts.map +1 -1
- package/dist/routes/index.d.ts +154 -85
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/presentations.d.ts +1 -0
- package/dist/routes/presentations.d.ts.map +1 -1
- package/dist/routes/profile-manager.d.ts.map +1 -1
- package/dist/routes/profiles.d.ts.map +1 -1
- package/dist/routes/public-share-links.d.ts +230 -0
- package/dist/routes/public-share-links.d.ts.map +1 -0
- package/dist/routes/share-links.d.ts +844 -0
- package/dist/routes/share-links.d.ts.map +1 -0
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.test.d.ts +2 -0
- package/dist/services/delivery/adapters/postmark.adapter.test.d.ts.map +1 -0
- package/dist/services/registry/registry.factory.d.ts.map +1 -1
- package/dist/types/activity.d.ts +4 -0
- package/dist/types/activity.d.ts.map +1 -1
- package/dist/types/consentflowcontract.d.ts +1 -1
- package/dist/types/credential-refresh.d.ts +10 -1
- package/dist/types/credential-refresh.d.ts.map +1 -1
- package/dist/types/inbox-batch.d.ts +38 -0
- package/dist/types/inbox-batch.d.ts.map +1 -0
- package/inbox-e2e-setup.ts +53 -1
- package/inbox-elasticmq.conf +10 -0
- package/lambda.ts +64 -16
- package/package.json +24 -16
- package/scripts/seed-demo-persona.ts +425 -0
- package/scripts/setup-inbox-queues.ts +60 -0
- package/serverless-local.yml +2 -0
- package/serverless.yml +111 -0
- package/shareLinkMaintenanceLambda.ts +49 -0
- package/src/accesslayer/auth-grant/read.ts +2 -1
- package/src/accesslayer/consentflowcontract/relationships/create.ts +9 -16
- package/src/accesslayer/consentflowcontract/relationships/update.ts +15 -28
- package/src/accesslayer/credential-activity/create.ts +1 -0
- package/src/accesslayer/credential-refresh/email-delivery.ts +116 -0
- package/src/accesslayer/credential-refresh/index.ts +1 -0
- package/src/accesslayer/credential-refresh/read.ts +8 -2
- package/src/accesslayer/credential-refresh/update.ts +4 -0
- package/src/accesslayer/inbox-batch/store.ts +469 -0
- package/src/accesslayer/inbox-credential/create.ts +51 -19
- package/src/accesslayer/inbox-credential/refresh.ts +221 -0
- package/src/accesslayer/inbox-credential/update.ts +1 -1
- package/src/accesslayer/presentation/read.ts +57 -25
- package/src/accesslayer/presentation/relationships/create.ts +54 -11
- package/src/accesslayer/presentation/relationships/read.ts +21 -6
- package/src/accesslayer/share-link/abandon.ts +100 -0
- package/src/accesslayer/share-link/cleanup.ts +271 -0
- package/src/accesslayer/share-link/errors.ts +32 -0
- package/src/accesslayer/share-link/finalize.ts +265 -0
- package/src/accesslayer/share-link/helpers.ts +419 -0
- package/src/accesslayer/share-link/index.ts +11 -0
- package/src/accesslayer/share-link/list.ts +162 -0
- package/src/accesslayer/share-link/read.ts +107 -0
- package/src/accesslayer/share-link/receipt.ts +548 -0
- package/src/accesslayer/share-link/recovery.ts +545 -0
- package/src/accesslayer/share-link/reserve.ts +684 -0
- package/src/accesslayer/share-link/revoke.ts +197 -0
- package/src/accesslayer/share-link/transaction-execution.ts +72 -0
- package/src/accesslayer/share-link/transaction.ts +126 -0
- package/src/accesslayer/share-link/types.ts +601 -0
- package/src/app.ts +6 -0
- package/src/cache/atomic-counter.test-helpers.ts +27 -0
- package/src/cache/atomic-counter.test.ts +6 -0
- package/src/cache/index.ts +25 -13
- package/src/config/environment.ts +40 -0
- package/src/constants/auth-grant.ts +5 -0
- package/src/credential-refresh.ts +5 -1
- package/src/docker-entry.ts +65 -0
- package/src/helpers/activity.helpers.ts +4 -0
- package/src/helpers/app-counter.helpers.ts +1 -1
- package/src/helpers/boost.helpers.ts +4 -31
- package/src/helpers/contact-method.helpers.test.ts +43 -0
- package/src/helpers/contact-method.helpers.ts +3 -4
- package/src/helpers/credential-refresh-email-content.helpers.test.ts +222 -0
- package/src/helpers/credential-refresh-email-content.helpers.ts +155 -0
- package/src/helpers/credential-refresh-email.helpers.ts +201 -0
- package/src/helpers/credential-refresh-proof.helpers.test.ts +103 -0
- package/src/helpers/credential-refresh-proof.helpers.ts +43 -0
- package/src/helpers/credential-refresh.helpers.ts +375 -99
- package/src/helpers/credential.helpers.ts +4 -1
- package/src/helpers/credentialSubject.helpers.test.ts +114 -0
- package/src/helpers/credentialSubject.helpers.ts +40 -0
- package/src/helpers/finalize-inbox.helpers.ts +29 -14
- package/src/helpers/inbox-batch-http.helpers.test.ts +188 -0
- package/src/helpers/inbox-batch-http.helpers.ts +150 -0
- package/src/helpers/inbox-batch-status.helpers.ts +11 -0
- package/src/helpers/inbox-batch.helpers.test.ts +668 -0
- package/src/helpers/inbox-batch.helpers.ts +374 -0
- package/src/helpers/inbox-issuance-error.helpers.ts +25 -0
- package/src/helpers/inbox-queue.helpers.test.ts +253 -0
- package/src/helpers/inbox-queue.helpers.ts +372 -0
- package/src/helpers/inbox-refresh.helpers.ts +432 -0
- package/src/helpers/inbox.helpers.ts +275 -36
- package/src/helpers/notificationMessages.ts +18 -1
- package/src/helpers/notifications.helpers.ts +4 -2
- package/src/helpers/presentation-metadata.ts +41 -0
- package/src/helpers/presentation.helpers.ts +27 -20
- package/src/helpers/profile.helpers.ts +7 -0
- package/src/helpers/rateLimit.helpers.ts +2 -5
- package/src/helpers/refresh-send-intent.helpers.ts +273 -0
- package/src/helpers/share-content-client/adapters.ts +55 -0
- package/src/helpers/share-content-client/canonical.ts +25 -0
- package/src/helpers/share-content-client/client.ts +440 -0
- package/src/helpers/share-content-client/config.ts +189 -0
- package/src/helpers/share-content-client/index.ts +27 -0
- package/src/helpers/share-content-client/responses.ts +273 -0
- package/src/helpers/share-content-client/types.ts +253 -0
- package/src/helpers/share-link-coordinator/budget-helpers.ts +53 -0
- package/src/helpers/share-link-coordinator/cleanup-runner.ts +286 -0
- package/src/helpers/share-link-coordinator/coordinator.ts +714 -0
- package/src/helpers/share-link-coordinator/index.ts +8 -0
- package/src/helpers/share-link-coordinator/recovery-runner.ts +428 -0
- package/src/helpers/share-link-coordinator/runtime.ts +88 -0
- package/src/helpers/share-link-coordinator/stat.ts +30 -0
- package/src/helpers/share-link-coordinator/types.ts +353 -0
- package/src/helpers/share-link-lifecycle/index.ts +4 -0
- package/src/helpers/share-link-lifecycle/lease.helpers.ts +35 -0
- package/src/helpers/share-link-lifecycle/recovery.helpers.ts +173 -0
- package/src/helpers/share-link-lifecycle/request-hash.helpers.ts +130 -0
- package/src/helpers/share-link-lifecycle/types.ts +58 -0
- package/src/helpers/share-link-maintenance/budget.ts +63 -0
- package/src/helpers/share-link-maintenance/config.ts +312 -0
- package/src/helpers/share-link-maintenance/index.ts +24 -0
- package/src/helpers/share-link-maintenance/runner.ts +223 -0
- package/src/helpers/share-link-maintenance/runtime.ts +334 -0
- package/src/helpers/share-link-maintenance/scheduler.ts +126 -0
- package/src/helpers/share-link-maintenance/telemetry.ts +70 -0
- package/src/helpers/share-link-maintenance/types.ts +89 -0
- package/src/helpers/share-link-owner/config.ts +48 -0
- package/src/helpers/share-link-owner/diagnostics.ts +89 -0
- package/src/helpers/share-link-owner/lazy-initializer.ts +29 -0
- package/src/helpers/share-link-owner-projection.ts +52 -0
- package/src/helpers/share-link-passcode-abuse.ts +36 -0
- package/src/helpers/share-link-passcode.ts +47 -0
- package/src/helpers/share-link-policy/index.ts +7 -0
- package/src/helpers/share-link-policy/production.ts +88 -0
- package/src/helpers/share-link-policy/resolver.ts +108 -0
- package/src/helpers/share-link-policy/types.ts +48 -0
- package/src/helpers/share-link-public/config.ts +41 -0
- package/src/helpers/share-link-view-notification.ts +18 -0
- package/src/helpers/shim.ts +32 -31
- package/src/inbox-queue-local.ts +28 -0
- package/src/models/AuthGrant.ts +1 -0
- package/src/models/CredentialActivity.ts +3 -5
- package/src/models/CredentialRefresh.ts +4 -2
- package/src/models/InboxCredential.ts +2 -0
- package/src/models/Presentation.ts +2 -2
- package/src/models/Profile.ts +3 -2
- package/src/models/ShareLink.ts +130 -0
- package/src/models/ShareViewReceipt.ts +69 -0
- package/src/models/credential-refresh-constraints.test.ts +8 -3
- package/src/models/credential-refresh-constraints.ts +5 -0
- package/src/models/index.ts +2 -0
- package/src/models/share-link-constraints.ts +93 -0
- package/src/openapi.ts +8 -0
- package/src/public-share-link-http.ts +49 -0
- package/src/routes/act-as.test.ts +239 -0
- package/src/routes/app-store.ts +4 -13
- package/src/routes/auth-grants.ts +13 -11
- package/src/routes/boosts.ts +916 -103
- package/src/routes/contracts.ts +2 -11
- package/src/routes/credential-refreshes.test.ts +8 -2
- package/src/routes/credential-refreshes.ts +4 -1
- package/src/routes/credentials.ts +2 -1
- package/src/routes/inbox.ts +95 -49
- package/src/routes/index.ts +216 -46
- package/src/routes/presentations.ts +19 -3
- package/src/routes/profile-manager.ts +10 -2
- package/src/routes/profiles.ts +17 -6
- package/src/routes/public-share-links.ts +814 -0
- package/src/routes/share-links.ts +640 -0
- package/src/routes/skills.ts +1 -1
- package/src/routes/workflows.ts +43 -24
- package/src/services/delivery/adapters/postmark.adapter.test.ts +54 -0
- package/src/services/delivery/adapters/postmark.adapter.ts +16 -1
- package/src/services/registry/registry.factory.ts +2 -1
- package/src/types/activity.ts +2 -0
- package/src/types/credential-refresh.ts +21 -2
- package/src/types/inbox-batch.ts +46 -0
- package/test/atomic-counter.e2e.ts +12 -0
- package/test/auth-grant.spec.ts +21 -21
- package/test/credential-refresh-email.spec.ts +663 -0
- package/test/credential-refresh-publication.spec.ts +2 -0
- package/test/dids.spec.ts +10 -1
- package/test/guardian-gated-routes.spec.ts +13 -0
- package/test/helpers/inbox-e2e-environment.ts +6 -0
- package/test/helpers/share-link-fixtures.ts +94 -0
- package/test/inbox-batch.spec.ts +1338 -0
- package/test/inbox-refresh.spec.ts +534 -0
- package/test/managed-recipient-inbox.spec.ts +130 -0
- package/test/notificationMessages.spec.ts +13 -0
- package/test/openapi.spec.ts +61 -1
- package/test/presentations.spec.ts +54 -6
- package/test/profiles.spec.ts +67 -1
- package/test/refresh-send.spec.ts +1799 -0
- package/test/registry.spec.ts +56 -11
- package/test/share-content-client.contract.spec.ts +365 -0
- package/test/share-content-client.unit.spec.ts +462 -0
- package/test/share-link-abuse.unit.spec.ts +65 -0
- package/test/share-link-coordinator.neo4j.spec.ts +684 -0
- package/test/share-link-coordinator.unit.spec.ts +990 -0
- package/test/share-link-dependency-diagnostics.unit.spec.ts +127 -0
- package/test/share-link-lifecycle.neo4j.spec.ts +1268 -0
- package/test/share-link-lifecycle.unit.spec.ts +205 -0
- package/test/share-link-list-cursor.unit.spec.ts +86 -0
- package/test/share-link-list.neo4j.spec.ts +324 -0
- package/test/share-link-maintenance.neo4j.spec.ts +355 -0
- package/test/share-link-maintenance.unit.spec.ts +1113 -0
- package/test/share-link-owner-policy.unit.spec.ts +210 -0
- package/test/share-link-owner-route.unit.spec.ts +752 -0
- package/test/share-link-owner-runtime.unit.spec.ts +102 -0
- package/test/share-link-passcode.unit.spec.ts +29 -0
- package/test/share-link-policy.unit.spec.ts +135 -0
- package/test/share-link-presentation-metadata.unit.spec.ts +49 -0
- package/test/share-link-production-policy.unit.spec.ts +77 -0
- package/test/share-link-public-config.unit.spec.ts +64 -0
- package/test/share-link-public-http-adapter.unit.spec.ts +315 -0
- package/test/share-link-public-route.unit.spec.ts +800 -0
- package/test/share-link-receipt.neo4j.spec.ts +943 -0
- package/test/share-link-recovery.neo4j.spec.ts +935 -0
- package/test/share-link-recovery.unit.spec.ts +726 -0
- package/vitest.config.ts +10 -0
- package/vitest.inbox-e2e.config.ts +5 -1
- package/vitest.integration.config.ts +2 -0
- package/vitest.share-links-neo4j.config.ts +19 -0
- package/vitest.share-links-unit.config.ts +28 -0
package/src/routes/workflows.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { finalizeInboxRefresh } from '@helpers/inbox-refresh.helpers';
|
|
1
2
|
import { TRPCError } from '@trpc/server';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import base64url from 'base64url';
|
|
@@ -606,14 +607,28 @@ async function handleInboxClaimPresentation(
|
|
|
606
607
|
const credentialProcessingPromises = pendingCredentials.map(async inboxCredential => {
|
|
607
608
|
try {
|
|
608
609
|
let finalCredential: VC;
|
|
609
|
-
const credentialPayload =
|
|
610
|
-
|
|
611
|
-
|
|
610
|
+
const credentialPayload = inboxCredential.refreshId
|
|
611
|
+
? undefined
|
|
612
|
+
: await decryptInboxCredential(inboxCredential.credential);
|
|
613
|
+
|
|
614
|
+
if (inboxCredential.refreshId) {
|
|
615
|
+
// A contact token authorizes delivery to the VP's DID. Never attach an unrelated
|
|
616
|
+
// contact profile's decryption key to that holder's refresh aggregate.
|
|
617
|
+
const boundProfile = await getProfileByDid(holderDid);
|
|
618
|
+
finalCredential = (
|
|
619
|
+
await finalizeInboxRefresh({
|
|
620
|
+
inboxId: inboxCredential.id,
|
|
621
|
+
holderDid,
|
|
622
|
+
holderProfile: boundProfile,
|
|
623
|
+
domain: ctx.domain,
|
|
624
|
+
})
|
|
625
|
+
).credential;
|
|
626
|
+
} else if (inboxCredential.isSigned) {
|
|
612
627
|
// Credential is already signed
|
|
613
|
-
finalCredential = JSON.parse(credentialPayload) as VC;
|
|
628
|
+
finalCredential = JSON.parse(credentialPayload!) as VC;
|
|
614
629
|
} else {
|
|
615
630
|
// Need to sign the credential using signing authority
|
|
616
|
-
const unsignedCredential = JSON.parse(credentialPayload) as UnsignedVC;
|
|
631
|
+
const unsignedCredential = JSON.parse(credentialPayload!) as UnsignedVC;
|
|
617
632
|
const inboxCredentialSigningAuthorityEndpoint =
|
|
618
633
|
(inboxCredential.signingAuthority?.endpoint as string) ?? undefined;
|
|
619
634
|
const inboxCredentialSigningAuthorityName =
|
|
@@ -676,25 +691,29 @@ async function handleInboxClaimPresentation(
|
|
|
676
691
|
).credential as VC;
|
|
677
692
|
}
|
|
678
693
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
694
|
+
let encryptedDelivery: JWE | undefined;
|
|
695
|
+
if (!inboxCredential.refreshId) {
|
|
696
|
+
// Avoid the seeded encryption wrapper, which adds the service as a recipient.
|
|
697
|
+
const deliveryLearnCard = await getEmptyLearnCard();
|
|
698
|
+
|
|
699
|
+
try {
|
|
700
|
+
encryptedDelivery = await deliveryLearnCard.invoke.createDagJwe(
|
|
701
|
+
finalCredential,
|
|
702
|
+
[holderDid]
|
|
703
|
+
);
|
|
704
|
+
if (!encryptedDelivery.recipients?.length) {
|
|
705
|
+
throw new Error('No supported delivery encryption recipients');
|
|
706
|
+
}
|
|
707
|
+
} catch (error) {
|
|
708
|
+
deliveryEncryptionFailed = true;
|
|
709
|
+
throw error;
|
|
688
710
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
711
|
+
const finalized = await finalizeAndWipeInboxCredential(inboxCredential.id, {
|
|
712
|
+
recipientDid: holderDid,
|
|
713
|
+
credential: encryptedDelivery,
|
|
714
|
+
});
|
|
715
|
+
if (!finalized) throw new Error('Inbox credential is no longer pending');
|
|
692
716
|
}
|
|
693
|
-
const finalized = await finalizeAndWipeInboxCredential(inboxCredential.id, {
|
|
694
|
-
recipientDid: holderDid,
|
|
695
|
-
credential: encryptedDelivery,
|
|
696
|
-
});
|
|
697
|
-
if (!finalized) throw new Error('Inbox credential is no longer pending');
|
|
698
717
|
|
|
699
718
|
// Record the claim only after finalization succeeds so failed compare-and-swap
|
|
700
719
|
// attempts cannot leave a misleading audit relationship behind.
|
|
@@ -708,7 +727,7 @@ async function handleInboxClaimPresentation(
|
|
|
708
727
|
|
|
709
728
|
// Store credential and create boost relationship if this was a boost issuance
|
|
710
729
|
const boostUri = inboxCredential.boostUri;
|
|
711
|
-
if (holderProfile && boostUri) {
|
|
730
|
+
if (!inboxCredential.refreshId && holderProfile && boostUri) {
|
|
712
731
|
try {
|
|
713
732
|
const boost = await getBoostByUri(boostUri);
|
|
714
733
|
const issuerProfile = await getProfileByDid(inboxCredential.issuerDid);
|
|
@@ -717,7 +736,7 @@ async function handleInboxClaimPresentation(
|
|
|
717
736
|
// Store the credential in the database
|
|
718
737
|
const credentialInstance = await storeCredential({
|
|
719
738
|
kind: 'issued-credential',
|
|
720
|
-
credential: encryptedDelivery
|
|
739
|
+
credential: encryptedDelivery!,
|
|
721
740
|
statusEntries: getBitstringStatusListEntries(finalCredential),
|
|
722
741
|
});
|
|
723
742
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { Notification } from '../delivery.service';
|
|
3
|
+
|
|
4
|
+
const mocks = vi.hoisted(() => ({ send: vi.fn(), legacySend: vi.fn(), render: vi.fn() }));
|
|
5
|
+
vi.mock('@environment', () => ({ environment: {} }));
|
|
6
|
+
vi.mock('postmark', () => ({
|
|
7
|
+
ServerClient: class {
|
|
8
|
+
sendEmail = mocks.send;
|
|
9
|
+
sendEmailWithTemplate = mocks.legacySend;
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
vi.mock('@learncard/email-templates', () => ({
|
|
13
|
+
resolveBranding: () => ({}),
|
|
14
|
+
renderEmail: mocks.render,
|
|
15
|
+
}));
|
|
16
|
+
import { PostmarkAdapter } from './postmark.adapter';
|
|
17
|
+
|
|
18
|
+
const notification: Notification = {
|
|
19
|
+
contactMethod: { type: 'email', value: 'holder@example.com' },
|
|
20
|
+
templateId: 'credential-updated',
|
|
21
|
+
templateModel: { issuer: { name: 'School' }, credential: { name: 'Biology' } },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
mocks.render.mockResolvedValue({ html: '<p>Updated</p>', text: 'Updated', subject: 'Updated' });
|
|
27
|
+
mocks.send.mockResolvedValue({});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('update email provider attempts', () => {
|
|
31
|
+
it('sends the locally rendered update once', async () => {
|
|
32
|
+
await new PostmarkAdapter('test-key').send(notification);
|
|
33
|
+
expect(mocks.send).toHaveBeenCalledTimes(1);
|
|
34
|
+
expect(mocks.legacySend).not.toHaveBeenCalled();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('does not try a legacy template after an ambiguous provider failure', async () => {
|
|
38
|
+
mocks.send.mockRejectedValueOnce(new Error('Acknowledgement lost'));
|
|
39
|
+
await expect(new PostmarkAdapter('test-key').send(notification)).rejects.toThrow(
|
|
40
|
+
'Acknowledgement lost'
|
|
41
|
+
);
|
|
42
|
+
expect(mocks.send).toHaveBeenCalledTimes(1);
|
|
43
|
+
expect(mocks.legacySend).not.toHaveBeenCalled();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('fails without sending when the local update template cannot render', async () => {
|
|
47
|
+
mocks.render.mockRejectedValueOnce(new Error('Render failed'));
|
|
48
|
+
await expect(new PostmarkAdapter('test-key').send(notification)).rejects.toThrow(
|
|
49
|
+
'Render failed'
|
|
50
|
+
);
|
|
51
|
+
expect(mocks.send).not.toHaveBeenCalled();
|
|
52
|
+
expect(mocks.legacySend).not.toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -41,6 +41,7 @@ const LOCAL_TEMPLATE_MAP: Record<string, TemplateId> = {
|
|
|
41
41
|
'guardian-credential-approval': 'guardian-credential-approval',
|
|
42
42
|
'guardian-email-otp': 'guardian-email-otp',
|
|
43
43
|
'guardian-rejected-credential': 'guardian-rejected-credential',
|
|
44
|
+
'credential-updated': 'credential-updated',
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
export class PostmarkAdapter implements DeliveryService {
|
|
@@ -85,6 +86,8 @@ export class PostmarkAdapter implements DeliveryService {
|
|
|
85
86
|
`[PostmarkAdapter] Local render failed for "${notification.templateId}":`,
|
|
86
87
|
renderError
|
|
87
88
|
);
|
|
89
|
+
// This template is owned locally; there is no legacy provider alias.
|
|
90
|
+
if (notification.templateId === 'credential-updated') throw renderError;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
if (rendered) {
|
|
@@ -104,6 +107,9 @@ export class PostmarkAdapter implements DeliveryService {
|
|
|
104
107
|
`[PostmarkAdapter] sendEmail API failed for "${notification.templateId}":`,
|
|
105
108
|
sendError
|
|
106
109
|
);
|
|
110
|
+
// A lost provider acknowledgement may already have sent the email.
|
|
111
|
+
// Do not bypass the refresh delivery claim with a second attempt.
|
|
112
|
+
if (notification.templateId === 'credential-updated') throw sendError;
|
|
107
113
|
}
|
|
108
114
|
}
|
|
109
115
|
}
|
|
@@ -172,7 +178,10 @@ export class PostmarkAdapter implements DeliveryService {
|
|
|
172
178
|
};
|
|
173
179
|
|
|
174
180
|
case 'recovery-key':
|
|
175
|
-
return {
|
|
181
|
+
return {
|
|
182
|
+
recoveryKey: model.recoveryKey,
|
|
183
|
+
confirmationCode: model.confirmationCode,
|
|
184
|
+
};
|
|
176
185
|
|
|
177
186
|
case 'endorsement-request':
|
|
178
187
|
return {
|
|
@@ -217,6 +226,12 @@ export class PostmarkAdapter implements DeliveryService {
|
|
|
217
226
|
recipient: model.recipient,
|
|
218
227
|
};
|
|
219
228
|
|
|
229
|
+
case 'credential-updated':
|
|
230
|
+
return {
|
|
231
|
+
issuer: model.issuer,
|
|
232
|
+
credential: model.credential,
|
|
233
|
+
};
|
|
234
|
+
|
|
220
235
|
default:
|
|
221
236
|
return model as TemplateDataMap[TemplateId];
|
|
222
237
|
}
|
|
@@ -7,7 +7,8 @@ export const getRegistryService = (): RegistryService => {
|
|
|
7
7
|
if (
|
|
8
8
|
environment.NODE_ENV === 'test' ||
|
|
9
9
|
environment.TRUSTED_ISSUERS_WHITELIST ||
|
|
10
|
-
environment.IS_CI
|
|
10
|
+
environment.IS_CI ||
|
|
11
|
+
environment.IS_E2E_TEST
|
|
11
12
|
) {
|
|
12
13
|
return new WhitelistAdapter();
|
|
13
14
|
}
|
package/src/types/activity.ts
CHANGED
|
@@ -33,6 +33,7 @@ export const CredentialActivityValidator = z.object({
|
|
|
33
33
|
eventType: CredentialActivityEventTypeValidator,
|
|
34
34
|
timestamp: z.string(),
|
|
35
35
|
actorProfileId: z.string().optional(),
|
|
36
|
+
onBehalfOf: z.string().optional(),
|
|
36
37
|
recipientType: CredentialActivityRecipientTypeValidator,
|
|
37
38
|
recipientIdentifier: z.string(),
|
|
38
39
|
boostUri: z.string().optional(),
|
|
@@ -85,6 +86,7 @@ export const CredentialActivityStatsValidator = z.object({
|
|
|
85
86
|
export type CredentialActivityStats = z.infer<typeof CredentialActivityStatsValidator>;
|
|
86
87
|
|
|
87
88
|
export type LogCredentialActivityParams = {
|
|
89
|
+
onBehalfOf?: string;
|
|
88
90
|
actorProfileId?: string;
|
|
89
91
|
eventType: CredentialActivityEventType;
|
|
90
92
|
recipientType: CredentialActivityRecipientType;
|
|
@@ -12,17 +12,26 @@ import {
|
|
|
12
12
|
* immutable Credential nodes as holder-encrypted JWE JSON — never plaintext VC JSON.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
export const CredentialRefreshStateValidator = z.enum([
|
|
15
|
+
export const CredentialRefreshStateValidator = z.enum([
|
|
16
|
+
'pending_holder',
|
|
17
|
+
'awaiting_claim',
|
|
18
|
+
'active',
|
|
19
|
+
'revoked',
|
|
20
|
+
]);
|
|
16
21
|
export type CredentialRefreshState = z.infer<typeof CredentialRefreshStateValidator>;
|
|
17
22
|
|
|
18
23
|
/** The managed refresh aggregate node (metadata only — no credential bodies) */
|
|
19
24
|
export const CredentialRefreshRecordValidator = z.object({
|
|
25
|
+
/** Pending Universal Inbox delivery associated with this aggregate. */
|
|
26
|
+
inboxCredentialId: z.string().optional(),
|
|
27
|
+
/** Public status mechanism coordinates retained for the issuer allocation receipt. */
|
|
28
|
+
inboxCredentialStatus: z.string().optional(),
|
|
20
29
|
/** Cryptographically random, unguessable public route identifier */
|
|
21
30
|
refreshId: z.string().min(1),
|
|
22
31
|
issuerProfileId: z.string().min(1),
|
|
23
32
|
issuerDid: z.string().min(1),
|
|
24
33
|
holderProfileId: z.string().min(1).optional(),
|
|
25
|
-
holderDid: z.string().min(1),
|
|
34
|
+
holderDid: z.string().min(1).optional(),
|
|
26
35
|
/** Stable nonempty VC identifier shared by every version */
|
|
27
36
|
credentialId: z.string().min(1),
|
|
28
37
|
state: CredentialRefreshStateValidator,
|
|
@@ -45,6 +54,12 @@ export const CredentialRefreshRecordValidator = z.object({
|
|
|
45
54
|
idempotencyKey: z.string().optional(),
|
|
46
55
|
/** Issuer-authored, privacy-safe update summary */
|
|
47
56
|
updateSummary: z.string().optional(),
|
|
57
|
+
/**
|
|
58
|
+
* Bounded, normalized credential display title used only for the email update
|
|
59
|
+
* notice. Never rendered in in-app/push payloads. Sourced from the unsigned
|
|
60
|
+
* publish input or the original boost template; never from decrypted content.
|
|
61
|
+
*/
|
|
62
|
+
credentialDisplayName: z.string().optional(),
|
|
48
63
|
lastPublishedAt: z.string().optional(),
|
|
49
64
|
/** Notification delivery window key used to collapse repeat notifications */
|
|
50
65
|
notificationWindowKey: z.string().optional(),
|
|
@@ -93,6 +108,8 @@ export const CredentialRefreshVersionNodeValidator = z.object({
|
|
|
93
108
|
etag: z.string().optional(),
|
|
94
109
|
signingMode: CredentialRefreshSigningModeValidator,
|
|
95
110
|
updateSummary: z.string().optional(),
|
|
111
|
+
/** Bounded credential display title for the email update notice only. */
|
|
112
|
+
credentialDisplayName: z.string().optional(),
|
|
96
113
|
/** Caller-provided key for replaying this exact historical publication */
|
|
97
114
|
idempotencyKey: z.string().optional(),
|
|
98
115
|
/** Aggregate-scoped unique key used to enforce historical idempotency */
|
|
@@ -132,6 +149,8 @@ export const AdvanceCredentialRefreshHeadParamsValidator = z.object({
|
|
|
132
149
|
materialDigest: z.string().optional(),
|
|
133
150
|
signingMode: CredentialRefreshSigningModeValidator,
|
|
134
151
|
updateSummary: z.string().optional(),
|
|
152
|
+
/** Bounded credential display title persisted for the email update notice only. */
|
|
153
|
+
credentialDisplayName: z.string().optional(),
|
|
135
154
|
idempotencyKey: z.string().optional(),
|
|
136
155
|
/** Aggregate state observed by the caller; prevents claim/revocation races. */
|
|
137
156
|
expectedState: CredentialRefreshStateValidator.optional(),
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { InboxBatchStatus, IssueInboxCredentialBatch } from '@learncard/types';
|
|
2
|
+
import type { Context } from '@routes';
|
|
3
|
+
|
|
4
|
+
export type BatchItem = {
|
|
5
|
+
id: string;
|
|
6
|
+
batchId: string;
|
|
7
|
+
index: number;
|
|
8
|
+
state: InboxBatchStatus['items'][number]['state'];
|
|
9
|
+
phase?: 'PREPARING' | 'ISSUING';
|
|
10
|
+
owner?: string;
|
|
11
|
+
leaseUntil?: number;
|
|
12
|
+
attempts: number;
|
|
13
|
+
result?: string;
|
|
14
|
+
replayKey: string;
|
|
15
|
+
duplicate: boolean;
|
|
16
|
+
/** Encrypted client correlation fields retained after the batch payload is removed. */
|
|
17
|
+
correlation?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type InboxDispatchLease = { id: string; dispatchAt: number };
|
|
21
|
+
|
|
22
|
+
export type BatchJob = {
|
|
23
|
+
id: string;
|
|
24
|
+
issuer: string;
|
|
25
|
+
createdAt: number;
|
|
26
|
+
payload?: string;
|
|
27
|
+
completedAt?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type BatchJobPayload = {
|
|
31
|
+
batch: IssueInboxCredentialBatch;
|
|
32
|
+
context: Pick<Context, 'domain' | 'tenant'>;
|
|
33
|
+
/** Submitting caller's permissions, retained for deferred refresh authorization. */
|
|
34
|
+
scope?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type BatchReplayStore = {
|
|
38
|
+
get: (key: string) => Promise<string | null | undefined>;
|
|
39
|
+
setIfAbsent: (key: string, value: string, ttl: number) => Promise<'OK' | null | undefined>;
|
|
40
|
+
compareAndSet: (
|
|
41
|
+
key: string,
|
|
42
|
+
expected: string,
|
|
43
|
+
value: string | null,
|
|
44
|
+
ttl: number
|
|
45
|
+
) => Promise<boolean | undefined>;
|
|
46
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { afterAll, expect, it } from 'vitest';
|
|
2
|
+
import cache from '@cache';
|
|
3
|
+
import { testAtomicCacheCounter } from '../src/cache/atomic-counter.test-helpers';
|
|
4
|
+
|
|
5
|
+
// Fail if the isolated Redis container is missing; never silently test the mock in CI.
|
|
6
|
+
it('uses real Redis for the atomic counter contract', () => {
|
|
7
|
+
expect(cache.redis).toBeDefined();
|
|
8
|
+
});
|
|
9
|
+
testAtomicCacheCounter(cache);
|
|
10
|
+
afterAll(async () => {
|
|
11
|
+
await cache.redis?.quit();
|
|
12
|
+
});
|
package/test/auth-grant.spec.ts
CHANGED
|
@@ -200,7 +200,7 @@ describe('Auth Grants', () => {
|
|
|
200
200
|
|
|
201
201
|
const authGrants = await userA.clients.fullAuth.authGrants.getAuthGrants();
|
|
202
202
|
expect(authGrants).toHaveLength(1);
|
|
203
|
-
expect(
|
|
203
|
+
expect(authGrants[0]?.id).toEqual(authGrantId);
|
|
204
204
|
|
|
205
205
|
await userA.clients.fullAuth.authGrants.addAuthGrant({
|
|
206
206
|
name: 'test2',
|
|
@@ -209,8 +209,8 @@ describe('Auth Grants', () => {
|
|
|
209
209
|
const authGrants2 = await userA.clients.fullAuth.authGrants.getAuthGrants();
|
|
210
210
|
|
|
211
211
|
expect(authGrants2).toHaveLength(2);
|
|
212
|
-
expect(authGrants2[0]
|
|
213
|
-
expect(authGrants2[1]
|
|
212
|
+
expect(authGrants2[0]).toBeDefined();
|
|
213
|
+
expect(authGrants2[1]).toBeDefined();
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
it('should accept a limit', async () => {
|
|
@@ -247,7 +247,7 @@ describe('Auth Grants', () => {
|
|
|
247
247
|
});
|
|
248
248
|
|
|
249
249
|
expect(authGrants).toHaveLength(1);
|
|
250
|
-
expect(
|
|
250
|
+
expect(authGrants[0]?.id).toEqual(authGrantId);
|
|
251
251
|
});
|
|
252
252
|
});
|
|
253
253
|
|
|
@@ -278,17 +278,18 @@ describe('Auth Grants', () => {
|
|
|
278
278
|
id: authGrantId,
|
|
279
279
|
});
|
|
280
280
|
|
|
281
|
-
expect(
|
|
281
|
+
expect(updatedAuthGrant?.name).toEqual('test2');
|
|
282
282
|
});
|
|
283
283
|
|
|
284
|
-
it('should
|
|
284
|
+
it('should reject updates to the scopes, actAs, challenge, id, createdAt, expiresAt, status of an auth grant', async () => {
|
|
285
285
|
await userA.clients.fullAuth.profile.createProfile({ profileId: 'usera' });
|
|
286
286
|
const authGrantId = await userA.clients.fullAuth.authGrants.addAuthGrant({
|
|
287
287
|
name: 'test',
|
|
288
288
|
});
|
|
289
289
|
|
|
290
|
-
const invalidUpdates:
|
|
290
|
+
const invalidUpdates: Record<string, string> = {
|
|
291
291
|
scope: '*:*',
|
|
292
|
+
actAs: '*',
|
|
292
293
|
challenge: 'test',
|
|
293
294
|
id: 'test',
|
|
294
295
|
createdAt: 'test',
|
|
@@ -296,27 +297,26 @@ describe('Auth Grants', () => {
|
|
|
296
297
|
status: 'revoked',
|
|
297
298
|
};
|
|
298
299
|
|
|
299
|
-
for (const key
|
|
300
|
+
for (const [key, value] of Object.entries(invalidUpdates)) {
|
|
300
301
|
await expect(
|
|
301
302
|
userA.clients.fullAuth.authGrants.updateAuthGrant({
|
|
302
303
|
id: authGrantId,
|
|
303
|
-
updates: {
|
|
304
|
-
[key]: invalidUpdates[key],
|
|
305
|
-
},
|
|
304
|
+
updates: { [key]: value } as never,
|
|
306
305
|
})
|
|
307
|
-
).
|
|
306
|
+
).rejects.toMatchObject({ code: 'BAD_REQUEST' });
|
|
308
307
|
}
|
|
309
308
|
|
|
310
309
|
const updatedAuthGrant = await userA.clients.fullAuth.authGrants.getAuthGrant({
|
|
311
310
|
id: authGrantId,
|
|
312
311
|
});
|
|
313
312
|
|
|
314
|
-
expect(
|
|
315
|
-
expect(
|
|
316
|
-
expect(
|
|
317
|
-
expect(
|
|
318
|
-
expect(
|
|
319
|
-
expect(
|
|
313
|
+
expect(updatedAuthGrant).toMatchObject({ scope: AUTH_GRANT_READ_ONLY_SCOPE });
|
|
314
|
+
expect(updatedAuthGrant?.challenge).not.toEqual('test');
|
|
315
|
+
expect(updatedAuthGrant?.id).not.toEqual('test');
|
|
316
|
+
expect(updatedAuthGrant?.createdAt).not.toEqual('test');
|
|
317
|
+
expect(updatedAuthGrant?.expiresAt).not.toEqual('test');
|
|
318
|
+
expect(updatedAuthGrant?.status).not.toEqual('revoked');
|
|
319
|
+
expect(updatedAuthGrant?.actAs).toBeUndefined();
|
|
320
320
|
});
|
|
321
321
|
|
|
322
322
|
// it('should not allow you to update an auth grant for a scoped user profile', async () => {
|
|
@@ -366,7 +366,7 @@ describe('Auth Grants', () => {
|
|
|
366
366
|
id: authGrantId,
|
|
367
367
|
});
|
|
368
368
|
|
|
369
|
-
expect(
|
|
369
|
+
expect(updatedAuthGrant?.name).toEqual('test2');
|
|
370
370
|
});
|
|
371
371
|
|
|
372
372
|
it("should not allow you to update someone else's auth grant", async () => {
|
|
@@ -511,7 +511,7 @@ describe('Auth Grants', () => {
|
|
|
511
511
|
id: authGrantId,
|
|
512
512
|
});
|
|
513
513
|
|
|
514
|
-
expect(
|
|
514
|
+
expect(authGrant?.status).toEqual(AuthGrantStatusValidator.enum.revoked);
|
|
515
515
|
});
|
|
516
516
|
|
|
517
517
|
// it('should not allow you to revoke an auth grant for a scoped user profile', async () => {
|
|
@@ -552,7 +552,7 @@ describe('Auth Grants', () => {
|
|
|
552
552
|
id: authGrantId,
|
|
553
553
|
});
|
|
554
554
|
|
|
555
|
-
expect(
|
|
555
|
+
expect(authGrant?.status).toEqual(AuthGrantStatusValidator.enum.revoked);
|
|
556
556
|
});
|
|
557
557
|
|
|
558
558
|
it("should not allow you to revoke someone else's auth grant", async () => {
|