@learncard/network-brain-service 3.19.0 → 4.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 +47 -82
- package/AGENTS.md +52 -0
- package/CHANGELOG.md +147 -0
- package/README.md +51 -2
- package/bootstrapLambda.test.ts +72 -0
- package/config/config.dev.json +29 -0
- package/config/config.example.json +29 -0
- package/config/config.json +1 -0
- package/config/config.production.json +29 -0
- package/config/config.scouts.dev.json +24 -0
- package/config/config.scouts.production.json +24 -0
- package/contractEventsLambda.ts +18 -0
- package/contractEventsLambdaApp.ts +19 -0
- package/didWebLambda.ts +13 -18
- package/didWebLambdaApp.ts +22 -0
- package/dist/accesslayer/app-manifest-version/create.d.ts +10 -0
- package/dist/accesslayer/app-manifest-version/create.d.ts.map +1 -0
- package/dist/accesslayer/app-manifest-version/read.d.ts +11 -0
- package/dist/accesslayer/app-manifest-version/read.d.ts.map +1 -0
- package/dist/accesslayer/app-manifest-version/update.d.ts +7 -0
- package/dist/accesslayer/app-manifest-version/update.d.ts.map +1 -0
- package/dist/accesslayer/app-store-listing/read.d.ts +2 -1
- package/dist/accesslayer/app-store-listing/read.d.ts.map +1 -1
- package/dist/accesslayer/app-store-listing/relationships/create.d.ts +1 -0
- package/dist/accesslayer/app-store-listing/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/app-store-listing/relationships/read.d.ts +2 -0
- package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/app-store-listing/update.d.ts +1 -0
- package/dist/accesslayer/app-store-listing/update.d.ts.map +1 -1
- package/dist/accesslayer/boost/relationships/create.d.ts +2 -1
- package/dist/accesslayer/boost/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/create.d.ts +9 -1
- package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/read.d.ts +34 -3
- package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +4 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/delete.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +3 -2
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts +12 -0
- package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts.map +1 -0
- package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts +12 -0
- package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts.map +1 -0
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +8 -5
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
- package/dist/accesslayer/credential/create.d.ts +2 -1
- package/dist/accesslayer/credential/create.d.ts.map +1 -1
- package/dist/accesslayer/credential/relationships/create.d.ts +4 -3
- package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/relationships/create.d.ts +2 -1
- package/dist/accesslayer/inbox-credential/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/update.d.ts +2 -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/attachment.d.ts +37 -0
- package/dist/accesslayer/share-link/attachment.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 +59 -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 +375 -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/app-store.caches.d.ts +49 -0
- package/dist/cache/app-store.caches.d.ts.map +1 -1
- package/dist/config/applyDockerStageConfig.d.ts +2 -0
- package/dist/config/applyDockerStageConfig.d.ts.map +1 -0
- package/dist/config/environment.d.ts +14 -0
- package/dist/config/environment.d.ts.map +1 -1
- package/dist/config/serverlessFunctionEnv.test.d.ts +2 -0
- package/dist/config/serverlessFunctionEnv.test.d.ts.map +1 -0
- package/dist/config/stageConfig.d.ts +112 -0
- package/dist/config/stageConfig.d.ts.map +1 -0
- package/dist/config/stageConfig.test.d.ts +2 -0
- package/dist/config/stageConfig.test.d.ts.map +1 -0
- package/dist/constants/auth-grant.d.ts +2 -0
- package/dist/constants/auth-grant.d.ts.map +1 -1
- package/dist/didkit_wasm_bg.wasm +0 -0
- package/dist/docker-entry.d.ts +1 -1
- package/dist/docker-entry.d.ts.map +1 -1
- package/dist/helpers/app-manifest.helpers.d.ts +5 -0
- package/dist/helpers/app-manifest.helpers.d.ts.map +1 -0
- package/dist/helpers/boost.helpers.d.ts.map +1 -1
- package/dist/helpers/claim-hooks.helpers.d.ts +2 -1
- package/dist/helpers/claim-hooks.helpers.d.ts.map +1 -1
- package/dist/helpers/consent-referral.helpers.d.ts +2 -0
- package/dist/helpers/consent-referral.helpers.d.ts.map +1 -0
- package/dist/helpers/consent-scopes.helpers.d.ts +7 -0
- package/dist/helpers/consent-scopes.helpers.d.ts.map +1 -0
- package/dist/helpers/contract-event-maintenance.helpers.d.ts +3 -0
- package/dist/helpers/contract-event-maintenance.helpers.d.ts.map +1 -0
- package/dist/helpers/contract-events.helpers.d.ts +25 -0
- package/dist/helpers/contract-events.helpers.d.ts.map +1 -0
- package/dist/helpers/contract-requests.helpers.d.ts +19 -0
- package/dist/helpers/contract-requests.helpers.d.ts.map +1 -0
- 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 +3 -3
- package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/inline-template.helpers.d.ts +45 -0
- package/dist/helpers/inline-template.helpers.d.ts.map +1 -0
- package/dist/helpers/notificationMessages.d.ts +1 -1
- package/dist/helpers/notificationMessages.d.ts.map +1 -1
- package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
- package/dist/helpers/notifications.helpers.d.ts +6 -0
- 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 +2 -0
- package/dist/helpers/profile.helpers.d.ts.map +1 -1
- 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-attachment-access.d.ts +4 -0
- package/dist/helpers/share-link-attachment-access.d.ts.map +1 -0
- package/dist/helpers/share-link-attachment.d.ts +18 -0
- package/dist/helpers/share-link-attachment.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/attachment-stat.d.ts +5 -0
- package/dist/helpers/share-link-coordinator/attachment-stat.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 +20 -0
- package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -0
- package/dist/helpers/share-link-maintenance/runtime.d.ts +38 -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 +56 -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 +11 -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 +22 -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/signingAuthority.helpers.d.ts.map +1 -1
- package/dist/helpers/smartResume.helpers.d.ts +3 -0
- package/dist/helpers/smartResume.helpers.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/models/AppManifestVersion.d.ts +20 -0
- package/dist/models/AppManifestVersion.d.ts.map +1 -0
- package/dist/models/AppStoreListing.d.ts +13 -0
- package/dist/models/AppStoreListing.d.ts.map +1 -1
- package/dist/models/ConsentFlowContract.d.ts +13 -2
- package/dist/models/ConsentFlowContract.d.ts.map +1 -1
- package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
- package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
- package/dist/models/Integration.d.ts +2 -0
- package/dist/models/Integration.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 +46 -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/index.d.ts +2 -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 +1 -1
- 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/app-store.d.ts +619 -0
- package/dist/routes/app-store.d.ts.map +1 -1
- package/dist/routes/boosts.d.ts +2 -2
- package/dist/routes/boosts.d.ts.map +1 -1
- package/dist/routes/contracts.d.ts +148 -5
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/credentials.d.ts +35 -1
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/inbox.d.ts +52 -2
- package/dist/routes/inbox.d.ts.map +1 -1
- package/dist/routes/index.d.ts +153 -0
- 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 +285 -0
- package/dist/routes/public-share-links.d.ts.map +1 -0
- package/dist/routes/share-links.d.ts +1005 -0
- package/dist/routes/share-links.d.ts.map +1 -0
- package/dist/routes/test.d.ts +5 -0
- package/dist/routes/test.d.ts.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
- package/dist/types/app-manifest.d.ts +339 -0
- package/dist/types/app-manifest.d.ts.map +1 -0
- package/dist/types/app-store-listing.d.ts +34 -0
- package/dist/types/app-store-listing.d.ts.map +1 -1
- package/dist/types/consentflowcontract.d.ts +31 -4
- package/dist/types/consentflowcontract.d.ts.map +1 -1
- package/dist/types/credential-refresh.d.ts +1 -1
- package/esbuildPlugins.cjs +1 -0
- package/lambda.ts +49 -192
- package/lambdaApp.ts +203 -0
- package/package.json +25 -17
- package/scripts/export-openapi.ts +20 -0
- package/scripts/offline-schema-environment.ts +18 -0
- package/scripts/seed-demo-persona.ts +425 -0
- package/scripts/verify-python-contracts.ts +72 -0
- package/serverless-local.yml +3 -6
- package/serverless.function-env.cjs +50 -0
- package/serverless.local.yml +0 -1
- package/serverless.yml +44 -55
- package/shareLinkMaintenanceLambda.ts +18 -0
- package/shareLinkMaintenanceLambdaApp.ts +50 -0
- package/src/accesslayer/app-manifest-version/create.ts +57 -0
- package/src/accesslayer/app-manifest-version/read.ts +129 -0
- package/src/accesslayer/app-manifest-version/update.ts +61 -0
- package/src/accesslayer/app-store-listing/read.ts +6 -0
- package/src/accesslayer/app-store-listing/relationships/create.ts +20 -0
- package/src/accesslayer/app-store-listing/relationships/read.ts +24 -1
- package/src/accesslayer/app-store-listing/update.ts +15 -0
- package/src/accesslayer/boost/relationships/create.ts +8 -2
- package/src/accesslayer/consentflowcontract/create.ts +81 -8
- package/src/accesslayer/consentflowcontract/read.ts +84 -10
- package/src/accesslayer/consentflowcontract/relationships/create.ts +125 -109
- package/src/accesslayer/consentflowcontract/relationships/delete.ts +1 -0
- package/src/accesslayer/consentflowcontract/relationships/read.ts +184 -108
- package/src/accesslayer/consentflowcontract/relationships/recipients.ts +125 -0
- package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +125 -0
- package/src/accesslayer/consentflowcontract/relationships/update.ts +255 -217
- package/src/accesslayer/credential/create.test.ts +2 -115
- package/src/accesslayer/credential/create.ts +11 -6
- package/src/accesslayer/credential/relationships/create.ts +17 -10
- package/src/accesslayer/inbox-credential/relationships/create.ts +70 -48
- package/src/accesslayer/inbox-credential/update.ts +4 -2
- 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 +120 -0
- package/src/accesslayer/share-link/attachment.ts +360 -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 +313 -0
- package/src/accesslayer/share-link/helpers.ts +450 -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 +565 -0
- package/src/accesslayer/share-link/reserve.ts +763 -0
- package/src/accesslayer/share-link/revoke.ts +232 -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 +606 -0
- package/src/app.ts +6 -0
- package/src/cache/app-store.caches.ts +35 -0
- package/src/config/applyDockerStageConfig.ts +6 -0
- package/src/config/environment.test.ts +17 -12
- package/src/config/environment.ts +15 -0
- package/src/config/serverlessFunctionEnv.test.ts +105 -0
- package/src/config/stageConfig.test.ts +25 -0
- package/src/config/stageConfig.ts +24 -0
- package/src/constants/auth-grant.ts +5 -0
- package/src/docker-entry.ts +81 -0
- package/src/helpers/app-manifest.helpers.ts +185 -0
- package/src/helpers/boost.helpers.ts +4 -31
- package/src/helpers/claim-hooks.helpers.ts +20 -7
- package/src/helpers/consent-referral.helpers.ts +6 -0
- package/src/helpers/consent-scopes.helpers.ts +58 -0
- package/src/helpers/contract-event-maintenance.helpers.ts +43 -0
- package/src/helpers/contract-events.helpers.ts +540 -0
- package/src/helpers/contract-requests.helpers.ts +211 -0
- package/src/helpers/credentialSubject.helpers.test.ts +114 -0
- package/src/helpers/credentialSubject.helpers.ts +40 -0
- package/src/helpers/finalize-inbox.helpers.ts +163 -44
- package/src/helpers/inbox.helpers.ts +58 -70
- package/src/helpers/inline-template.helpers.ts +560 -0
- package/src/helpers/notificationMessages.ts +77 -1
- package/src/helpers/notificationQueue.helpers.ts +23 -5
- package/src/helpers/notifications.helpers.ts +25 -1
- package/src/helpers/presentation-metadata.ts +41 -0
- package/src/helpers/presentation.helpers.ts +27 -20
- package/src/helpers/profile.helpers.ts +13 -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-attachment-access.ts +87 -0
- package/src/helpers/share-link-attachment.ts +115 -0
- package/src/helpers/share-link-coordinator/attachment-stat.ts +41 -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 +727 -0
- package/src/helpers/share-link-coordinator/index.ts +8 -0
- package/src/helpers/share-link-coordinator/recovery-runner.ts +443 -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 +251 -0
- package/src/helpers/share-link-maintenance/runtime.ts +340 -0
- package/src/helpers/share-link-maintenance/scheduler.ts +126 -0
- package/src/helpers/share-link-maintenance/telemetry.ts +73 -0
- package/src/helpers/share-link-maintenance/types.ts +91 -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 +54 -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 +108 -0
- package/src/helpers/share-link-policy/resolver.ts +113 -0
- package/src/helpers/share-link-policy/types.ts +56 -0
- package/src/helpers/share-link-public/config.ts +41 -0
- package/src/helpers/share-link-view-notification.ts +18 -0
- package/src/helpers/signingAuthority.helpers.ts +23 -14
- package/src/helpers/smartResume.helpers.ts +123 -0
- package/src/models/AppManifestVersion.ts +43 -0
- package/src/models/AppStoreListing.ts +32 -0
- package/src/models/ConsentFlowContract.ts +34 -3
- package/src/models/ConsentFlowTerms.ts +10 -0
- package/src/models/ConsentFlowTransaction.ts +3 -0
- package/src/models/Integration.ts +2 -0
- package/src/models/Presentation.ts +2 -2
- package/src/models/Profile.ts +3 -2
- package/src/models/ShareLink.ts +134 -0
- package/src/models/ShareViewReceipt.ts +69 -0
- package/src/models/index.ts +25 -0
- package/src/models/share-link-constraints.ts +98 -0
- package/src/openapi.ts +44 -8
- package/src/public-share-link-http.ts +49 -0
- package/src/routes/app-store.ts +1056 -38
- package/src/routes/boosts.ts +14 -25
- package/src/routes/contracts.ts +384 -273
- package/src/routes/inbox.ts +4 -2
- package/src/routes/index.ts +142 -44
- package/src/routes/presentations.ts +19 -3
- package/src/routes/profile-manager.ts +29 -2
- package/src/routes/profiles.ts +29 -6
- package/src/routes/public-share-links.ts +983 -0
- package/src/routes/share-links.ts +709 -0
- package/src/routes/workflows.ts +3 -2
- package/src/services/delivery/adapters/postmark.adapter.ts +4 -1
- package/src/types/app-manifest.ts +28 -0
- package/src/types/app-store-listing.ts +13 -0
- package/src/types/consentflowcontract.ts +25 -4
- package/test/app-manifest.helpers.spec.ts +170 -0
- package/test/app-manifest.routes.spec.ts +299 -0
- package/test/app-store-listing-updates.spec.ts +236 -0
- package/test/app-store-listing.spec.ts +65 -3
- package/test/consent-data-boundaries.spec.ts +352 -0
- package/test/consent-recipients.spec.ts +513 -0
- package/test/consent-scopes.helpers.spec.ts +67 -0
- package/test/consentflow.spec.ts +63 -2
- package/test/contract-requests.spec.ts +1588 -0
- package/test/credential-storage.neo4j.spec.ts +122 -0
- package/test/dids.spec.ts +10 -1
- package/test/guardian-gated-routes.spec.ts +49 -0
- package/test/helpers/share-link-fixtures.ts +94 -0
- package/test/inbox-security.e2e.ts +26 -10
- package/test/inbox.spec.ts +184 -8
- package/test/inline-template.helpers.spec.ts +49 -0
- package/test/managed-recipient-inbox.spec.ts +152 -0
- package/test/notificationMessages.spec.ts +13 -0
- package/test/openapi.spec.ts +14 -53
- package/test/presentations.spec.ts +54 -6
- package/test/profiles.spec.ts +97 -1
- 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-attachment.neo4j.spec.ts +262 -0
- package/test/share-link-attachment.unit.spec.ts +244 -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 +1411 -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 +1140 -0
- package/test/share-link-owner-policy.unit.spec.ts +210 -0
- package/test/share-link-owner-route.unit.spec.ts +820 -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 +149 -0
- package/test/share-link-presentation-metadata.unit.spec.ts +49 -0
- package/test/share-link-production-policy.unit.spec.ts +126 -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 +1079 -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/test/share-link-service-policy.neo4j.spec.ts +403 -0
- package/test/signing-authority-encryption.spec.ts +75 -0
- package/test/smart-resume-consent.spec.ts +233 -0
- package/test/smart-resume-upload.spec.ts +65 -0
- package/tsconfig.build.json +1 -1
- package/tsconfig.json +1 -0
- package/vitest.config.ts +14 -0
- package/vitest.inbox-e2e.config.ts +2 -0
- package/vitest.share-links-neo4j.config.ts +19 -0
- package/vitest.share-links-unit.config.ts +28 -0
- package/workerLambda.test.ts +100 -0
package/src/routes/contracts.ts
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
import { SendContractRequestValidator, ContractRequestFieldsValidator } from '@learncard/types';
|
|
2
|
+
import {
|
|
3
|
+
sendGenericContractRequest,
|
|
4
|
+
decideGenericContractRequest,
|
|
5
|
+
markGenericContractRequestSeen,
|
|
6
|
+
assertGenericRequestWriteScope,
|
|
7
|
+
canManageContractRequests,
|
|
8
|
+
resolveContractRequest,
|
|
9
|
+
} from '@helpers/contract-requests.helpers';
|
|
1
10
|
import { environment } from '@environment';
|
|
11
|
+
import {
|
|
12
|
+
smartResumeFingerprint,
|
|
13
|
+
publishSmartResume,
|
|
14
|
+
} from '@accesslayer/consentflowcontract/relationships/smartResume';
|
|
15
|
+
import { uploadSmartResume } from '@helpers/smartResume.helpers';
|
|
2
16
|
import { TRPCError } from '@trpc/server';
|
|
3
17
|
import { z } from 'zod';
|
|
4
18
|
|
|
5
19
|
import { t, profileRoute, openRoute, guardianGatedRoute } from '@routes';
|
|
6
|
-
import { ConsentFlowContract } from '@models';
|
|
7
20
|
|
|
8
21
|
import {
|
|
9
22
|
ConsentFlowContractValidator,
|
|
@@ -47,10 +60,11 @@ import {
|
|
|
47
60
|
isProfileConsentFlowContractAdmin,
|
|
48
61
|
getWritersForContract,
|
|
49
62
|
} from '@accesslayer/consentflowcontract/relationships/read';
|
|
50
|
-
import {
|
|
63
|
+
import { inflateObject } from '@helpers/objects.helpers';
|
|
64
|
+
import { constructUri, getIdFromUri } from '@helpers/uri.helpers';
|
|
51
65
|
import {
|
|
52
66
|
getContractByUri,
|
|
53
|
-
|
|
67
|
+
getStoredContractRequest,
|
|
54
68
|
getRequestedForList,
|
|
55
69
|
getRequestedForForUser,
|
|
56
70
|
getContractById,
|
|
@@ -64,19 +78,15 @@ import { areTermsValid } from '@helpers/contract.helpers';
|
|
|
64
78
|
import { updateDidForProfile, getProfileIdFromString } from '@helpers/did.helpers';
|
|
65
79
|
import {
|
|
66
80
|
syncCredentialsToContract,
|
|
67
|
-
updateRequestedForStatusIfExists,
|
|
68
81
|
updateTerms,
|
|
69
82
|
pruneDeletedUrisFromConsentTerms,
|
|
70
83
|
upsertRequestedForRelationship,
|
|
71
84
|
withdrawTerms,
|
|
72
85
|
} from '@accesslayer/consentflowcontract/relationships/update';
|
|
73
|
-
import {
|
|
74
|
-
consentToContract,
|
|
75
|
-
setAutoBoostForContract,
|
|
76
|
-
setCreatorForContract,
|
|
77
|
-
} from '@accesslayer/consentflowcontract/relationships/create';
|
|
86
|
+
import { consentToContract } from '@accesslayer/consentflowcontract/relationships/create';
|
|
78
87
|
import { getProfileByDid, getProfileByProfileId } from '@accesslayer/profile/read';
|
|
79
88
|
import { sendBoost, isDraftBoost } from '@helpers/boost.helpers';
|
|
89
|
+
import { setCredentialSubjectIds } from '@helpers/credentialSubject.helpers';
|
|
80
90
|
import { isRelationshipBlocked } from '@helpers/connection.helpers';
|
|
81
91
|
import { getBoostByUri } from '@accesslayer/boost/read';
|
|
82
92
|
import { canProfileIssueBoost } from '@accesslayer/boost/relationships/read';
|
|
@@ -104,6 +114,24 @@ import { addNotificationToQueue } from '@helpers/notifications.helpers';
|
|
|
104
114
|
import { getNotificationMessage } from '@helpers/notificationMessages';
|
|
105
115
|
import { resolveRecipientLocale } from '@helpers/getRecipientLocale.helpers';
|
|
106
116
|
import { ProfileType } from 'types/profile';
|
|
117
|
+
import {
|
|
118
|
+
getRecipientsForContract,
|
|
119
|
+
addRecipientToContract,
|
|
120
|
+
removeRecipientFromContract,
|
|
121
|
+
canReadContractData,
|
|
122
|
+
} from '@accesslayer/consentflowcontract/relationships/recipients';
|
|
123
|
+
|
|
124
|
+
const publicContractRecipient = (domain: string, recipient: ProfileType) => {
|
|
125
|
+
const profile = updateDidForProfile(domain, recipient);
|
|
126
|
+
return { ...sanitizeProfileForTier(profile, 'unauthenticated'), did: profile.did };
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const resolveRecipientProfileId = async (identifier: string, domain: string): Promise<string> => {
|
|
130
|
+
const profileId = await getProfileIdFromString(identifier, domain);
|
|
131
|
+
if (!profileId)
|
|
132
|
+
throw new TRPCError({ code: 'BAD_REQUEST', message: 'Invalid recipient identifier' });
|
|
133
|
+
return profileId;
|
|
134
|
+
};
|
|
107
135
|
|
|
108
136
|
export const contractsRouter = t.router({
|
|
109
137
|
createConsentFlowContract: profileRoute
|
|
@@ -132,6 +160,7 @@ export const contractsRouter = t.router({
|
|
|
132
160
|
expiresAt: z.string().optional(),
|
|
133
161
|
autoboosts: z.array(AutoBoostConfigValidator).optional(),
|
|
134
162
|
writers: z.array(z.string()).optional(),
|
|
163
|
+
recipients: z.array(z.string()).max(50).optional(),
|
|
135
164
|
})
|
|
136
165
|
)
|
|
137
166
|
.output(z.string())
|
|
@@ -149,26 +178,31 @@ export const contractsRouter = t.router({
|
|
|
149
178
|
expiresAt,
|
|
150
179
|
autoboosts,
|
|
151
180
|
writers,
|
|
181
|
+
recipients,
|
|
152
182
|
} = input;
|
|
153
183
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
184
|
+
const recipientIds = [
|
|
185
|
+
...new Set(
|
|
186
|
+
await Promise.all(
|
|
187
|
+
(recipients ?? []).map(id => resolveRecipientProfileId(id, ctx.domain))
|
|
188
|
+
)
|
|
189
|
+
),
|
|
190
|
+
].filter(id => id !== ctx.user.profile.profileId);
|
|
191
|
+
const recipientProfiles = await getProfilesByProfileIds(recipientIds);
|
|
192
|
+
if (recipientProfiles.length !== recipientIds.length) {
|
|
193
|
+
throw new TRPCError({
|
|
194
|
+
code: 'BAD_REQUEST',
|
|
195
|
+
message: 'Could not find all recipient profiles',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
167
198
|
|
|
168
|
-
|
|
169
|
-
|
|
199
|
+
const preparedAutoBoosts: {
|
|
200
|
+
id: string;
|
|
201
|
+
signingAuthorityEndpoint: string;
|
|
202
|
+
signingAuthorityName: string;
|
|
203
|
+
}[] = [];
|
|
170
204
|
|
|
171
|
-
//
|
|
205
|
+
// Validate specified writers before creating the contract.
|
|
172
206
|
if (writers && writers.length > 0) {
|
|
173
207
|
const writerProfiles = await getProfilesByProfileIds(writers);
|
|
174
208
|
|
|
@@ -183,17 +217,6 @@ export const contractsRouter = t.router({
|
|
|
183
217
|
)}`,
|
|
184
218
|
});
|
|
185
219
|
}
|
|
186
|
-
|
|
187
|
-
// Create CAN_WRITE relationship for each writer using static relateTo
|
|
188
|
-
for (const writerProfile of writerProfiles) {
|
|
189
|
-
await ConsentFlowContract.relateTo({
|
|
190
|
-
alias: 'canWrite',
|
|
191
|
-
where: {
|
|
192
|
-
source: { id: createdContract.id }, // Specify source contract by id
|
|
193
|
-
target: { profileId: writerProfile.profileId }, // Specify target profile by profileId
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
220
|
}
|
|
198
221
|
|
|
199
222
|
if (autoboosts && autoboosts.length > 0) {
|
|
@@ -242,18 +265,94 @@ export const contractsRouter = t.router({
|
|
|
242
265
|
});
|
|
243
266
|
}
|
|
244
267
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
normalizedSigningAuthority,
|
|
249
|
-
|
|
250
|
-
);
|
|
268
|
+
preparedAutoBoosts.push({
|
|
269
|
+
id: boost.id,
|
|
270
|
+
signingAuthorityEndpoint: normalizedSigningAuthority.endpoint,
|
|
271
|
+
signingAuthorityName: normalizedSigningAuthority.name,
|
|
272
|
+
});
|
|
251
273
|
}
|
|
252
274
|
}
|
|
253
275
|
|
|
276
|
+
// All validation precedes the single atomic creation statement.
|
|
277
|
+
const createdContract = await createConsentFlowContract({
|
|
278
|
+
contract,
|
|
279
|
+
ownerProfileId: ctx.user.profile.profileId,
|
|
280
|
+
recipientIds,
|
|
281
|
+
writerIds: writers,
|
|
282
|
+
autoboosts: preparedAutoBoosts,
|
|
283
|
+
name,
|
|
284
|
+
subtitle,
|
|
285
|
+
description,
|
|
286
|
+
reasonForAccessing,
|
|
287
|
+
needsGuardianConsent,
|
|
288
|
+
redirectUrl,
|
|
289
|
+
frontDoorBoostUri,
|
|
290
|
+
image,
|
|
291
|
+
expiresAt,
|
|
292
|
+
});
|
|
293
|
+
|
|
254
294
|
return constructUri('contract', createdContract.id, ctx.domain);
|
|
255
295
|
}),
|
|
256
296
|
|
|
297
|
+
addContractRecipient: profileRoute
|
|
298
|
+
.meta({
|
|
299
|
+
requiredScope: 'contracts:write',
|
|
300
|
+
openapi: {
|
|
301
|
+
protect: true,
|
|
302
|
+
method: 'POST',
|
|
303
|
+
path: '/consent-flow-contract/recipient/add',
|
|
304
|
+
tags: ['Contracts'],
|
|
305
|
+
summary: 'Add a data recipient before first consent',
|
|
306
|
+
},
|
|
307
|
+
})
|
|
308
|
+
.input(z.object({ contractUri: z.string(), recipient: z.string() }))
|
|
309
|
+
.output(z.boolean())
|
|
310
|
+
.mutation(async ({ ctx, input }) => {
|
|
311
|
+
const contract = await getContractByUri(input.contractUri);
|
|
312
|
+
if (!contract)
|
|
313
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Could not find contract' });
|
|
314
|
+
if (!(await isProfileConsentFlowContractAdmin(ctx.user.profile, contract)))
|
|
315
|
+
throw new TRPCError({
|
|
316
|
+
code: 'UNAUTHORIZED',
|
|
317
|
+
message: 'Profile does not own contract',
|
|
318
|
+
});
|
|
319
|
+
const profileId = await resolveRecipientProfileId(input.recipient, ctx.domain);
|
|
320
|
+
if (!(await getProfileByProfileId(profileId)))
|
|
321
|
+
throw new TRPCError({
|
|
322
|
+
code: 'BAD_REQUEST',
|
|
323
|
+
message: 'Could not find recipient profile',
|
|
324
|
+
});
|
|
325
|
+
return addRecipientToContract(contract.id, profileId);
|
|
326
|
+
}),
|
|
327
|
+
|
|
328
|
+
removeContractRecipient: profileRoute
|
|
329
|
+
.meta({
|
|
330
|
+
requiredScope: 'contracts:write',
|
|
331
|
+
openapi: {
|
|
332
|
+
protect: true,
|
|
333
|
+
method: 'POST',
|
|
334
|
+
path: '/consent-flow-contract/recipient/remove',
|
|
335
|
+
tags: ['Contracts'],
|
|
336
|
+
summary: 'Remove a data recipient',
|
|
337
|
+
},
|
|
338
|
+
})
|
|
339
|
+
.input(z.object({ contractUri: z.string(), recipient: z.string() }))
|
|
340
|
+
.output(z.boolean())
|
|
341
|
+
.mutation(async ({ ctx, input }) => {
|
|
342
|
+
const contract = await getContractByUri(input.contractUri);
|
|
343
|
+
if (!contract)
|
|
344
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Could not find contract' });
|
|
345
|
+
if (!(await isProfileConsentFlowContractAdmin(ctx.user.profile, contract)))
|
|
346
|
+
throw new TRPCError({
|
|
347
|
+
code: 'UNAUTHORIZED',
|
|
348
|
+
message: 'Profile does not own contract',
|
|
349
|
+
});
|
|
350
|
+
return removeRecipientFromContract(
|
|
351
|
+
contract.id,
|
|
352
|
+
await resolveRecipientProfileId(input.recipient, ctx.domain)
|
|
353
|
+
);
|
|
354
|
+
}),
|
|
355
|
+
|
|
257
356
|
getConsentFlowContract: openRoute
|
|
258
357
|
.meta({
|
|
259
358
|
openapi: {
|
|
@@ -279,6 +378,7 @@ export const contractsRouter = t.router({
|
|
|
279
378
|
}
|
|
280
379
|
|
|
281
380
|
const autoboosts = await getAutoBoostsForContract(result.contract.id, ctx.domain);
|
|
381
|
+
const recipients = await getRecipientsForContract(result.contract.id);
|
|
282
382
|
|
|
283
383
|
return {
|
|
284
384
|
owner: updateDidForProfile(ctx.domain, result.contractOwner),
|
|
@@ -296,6 +396,10 @@ export const contractsRouter = t.router({
|
|
|
296
396
|
uri: constructUri('contract', result.contract.id, ctx.domain),
|
|
297
397
|
...(result.contract.expiresAt ? { expiresAt: result.contract.expiresAt } : {}),
|
|
298
398
|
autoBoosts: autoboosts,
|
|
399
|
+
recipients: recipients.map(recipient =>
|
|
400
|
+
publicContractRecipient(ctx.domain, recipient)
|
|
401
|
+
),
|
|
402
|
+
audienceVersion: Number(result.contract.audienceVersion ?? 0),
|
|
299
403
|
};
|
|
300
404
|
}),
|
|
301
405
|
|
|
@@ -425,16 +529,16 @@ export const contractsRouter = t.router({
|
|
|
425
529
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'Could not find contract' });
|
|
426
530
|
}
|
|
427
531
|
|
|
428
|
-
if (!(await
|
|
532
|
+
if (!(await canReadContractData(contract.id, profile.profileId))) {
|
|
429
533
|
throw new TRPCError({
|
|
430
534
|
code: 'UNAUTHORIZED',
|
|
431
|
-
message: 'Profile
|
|
535
|
+
message: 'Profile is not in the contract data audience',
|
|
432
536
|
});
|
|
433
537
|
}
|
|
434
538
|
|
|
435
539
|
const contractId = getIdFromUri(uri);
|
|
436
540
|
|
|
437
|
-
const results = await getConsentedDataForContract(contractId, {
|
|
541
|
+
const results = await getConsentedDataForContract(contractId, profile.profileId, {
|
|
438
542
|
query,
|
|
439
543
|
limit: limit + 1,
|
|
440
544
|
cursor,
|
|
@@ -818,17 +922,7 @@ export const contractsRouter = t.router({
|
|
|
818
922
|
unsignedVc.issuanceDate = new Date().toISOString();
|
|
819
923
|
}
|
|
820
924
|
unsignedVc.issuer = { id: getDidWeb(ctx.domain, profile.profileId) };
|
|
821
|
-
|
|
822
|
-
unsignedVc.credentialSubject = unsignedVc.credentialSubject.map(subject => ({
|
|
823
|
-
...subject,
|
|
824
|
-
id: getDidWeb(ctx.domain, otherProfile.profileId),
|
|
825
|
-
}));
|
|
826
|
-
} else {
|
|
827
|
-
unsignedVc.credentialSubject = {
|
|
828
|
-
...unsignedVc.credentialSubject,
|
|
829
|
-
id: getDidWeb(ctx.domain, otherProfile.profileId),
|
|
830
|
-
};
|
|
831
|
-
}
|
|
925
|
+
setCredentialSubjectIds(unsignedVc, getDidWeb(ctx.domain, otherProfile.profileId));
|
|
832
926
|
if (unsignedVc?.type?.includes('BoostCredential')) unsignedVc.boostId = boostUri;
|
|
833
927
|
// Inject OBv3 skill alignments based on boost's framework/skills
|
|
834
928
|
await injectObv3AlignmentsIntoCredentialForBoost(unsignedVc, boost, ctx.domain);
|
|
@@ -901,6 +995,8 @@ export const contractsRouter = t.router({
|
|
|
901
995
|
contractUri: z.string(),
|
|
902
996
|
expiresAt: z.string().optional(),
|
|
903
997
|
oneTime: z.boolean().optional(),
|
|
998
|
+
audienceVersion: z.number().int().nonnegative().optional(),
|
|
999
|
+
expectedRequestId: z.string().min(1).optional(),
|
|
904
1000
|
recipientToken: z.string().optional(), // SmartResume recipientToken needed for API call
|
|
905
1001
|
})
|
|
906
1002
|
)
|
|
@@ -916,7 +1012,15 @@ export const contractsRouter = t.router({
|
|
|
916
1012
|
});
|
|
917
1013
|
}
|
|
918
1014
|
|
|
919
|
-
const {
|
|
1015
|
+
const {
|
|
1016
|
+
terms,
|
|
1017
|
+
contractUri,
|
|
1018
|
+
expiresAt,
|
|
1019
|
+
oneTime,
|
|
1020
|
+
recipientToken,
|
|
1021
|
+
audienceVersion,
|
|
1022
|
+
expectedRequestId,
|
|
1023
|
+
} = input;
|
|
920
1024
|
|
|
921
1025
|
const contractDetails = await getContractDetailsByUri(contractUri);
|
|
922
1026
|
|
|
@@ -924,7 +1028,37 @@ export const contractsRouter = t.router({
|
|
|
924
1028
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'Could not find contract' });
|
|
925
1029
|
}
|
|
926
1030
|
|
|
927
|
-
if (
|
|
1031
|
+
if (
|
|
1032
|
+
contractDetails.contract.expiresAt?.trim() &&
|
|
1033
|
+
(!Number.isFinite(Date.parse(contractDetails.contract.expiresAt)) ||
|
|
1034
|
+
Date.parse(contractDetails.contract.expiresAt) <= Date.now())
|
|
1035
|
+
) {
|
|
1036
|
+
throw new TRPCError({ code: 'CONFLICT', message: 'This contract has expired.' });
|
|
1037
|
+
}
|
|
1038
|
+
const isSmartResume =
|
|
1039
|
+
contractUri === environment.SMART_RESUME_CONTRACT_URI ||
|
|
1040
|
+
contractUri ===
|
|
1041
|
+
'lc:network:network.learncard.com/trpc:contract:55b738f0-49f4-4b33-b6c1-afa99b605cd6';
|
|
1042
|
+
if (isSmartResume && !recipientToken) {
|
|
1043
|
+
throw new TRPCError({
|
|
1044
|
+
code: 'BAD_REQUEST',
|
|
1045
|
+
message: 'Missing recipientToken for SmartResume',
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
const previousTerms = await getContractTermsForProfile(
|
|
1049
|
+
profile,
|
|
1050
|
+
contractDetails.contract
|
|
1051
|
+
);
|
|
1052
|
+
const retrySmartResume =
|
|
1053
|
+
isSmartResume &&
|
|
1054
|
+
Boolean(previousTerms?.smartResumeFingerprint) &&
|
|
1055
|
+
(previousTerms?.status === 'live' ||
|
|
1056
|
+
(previousTerms?.status === 'stale' && previousTerms.oneTime));
|
|
1057
|
+
|
|
1058
|
+
if (
|
|
1059
|
+
!retrySmartResume &&
|
|
1060
|
+
(await hasProfileConsentedToContract(profile, contractDetails.contract))
|
|
1061
|
+
) {
|
|
928
1062
|
try {
|
|
929
1063
|
await upsertRequestedForRelationship(
|
|
930
1064
|
contractDetails.contract.id,
|
|
@@ -939,10 +1073,6 @@ export const contractsRouter = t.router({
|
|
|
939
1073
|
}
|
|
940
1074
|
|
|
941
1075
|
if (!guardianIdentity) {
|
|
942
|
-
const previousTerms = await getContractTermsForProfile(
|
|
943
|
-
profile,
|
|
944
|
-
contractDetails.contract
|
|
945
|
-
);
|
|
946
1076
|
if (previousTerms && (await hasGuardianApprovalHistory(previousTerms))) {
|
|
947
1077
|
throw new TRPCError({
|
|
948
1078
|
code: 'FORBIDDEN',
|
|
@@ -963,156 +1093,46 @@ export const contractsRouter = t.router({
|
|
|
963
1093
|
);
|
|
964
1094
|
}
|
|
965
1095
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
const categoryValues = Object.values(categories) as Array<{ shared?: string[] }>;
|
|
1004
|
-
|
|
1005
|
-
const allSharedCredentialUris = [
|
|
1006
|
-
// filter out duplicates
|
|
1007
|
-
...new Set(categoryValues.flatMap(({ shared }) => shared ?? [])),
|
|
1008
|
-
];
|
|
1009
|
-
|
|
1010
|
-
const resolvedCredentials = await Promise.all(
|
|
1011
|
-
allSharedCredentialUris.map(async uri => {
|
|
1012
|
-
try {
|
|
1013
|
-
return await resolveUri(uri);
|
|
1014
|
-
} catch (error) {
|
|
1015
|
-
console.error(`Error resolving URI ${uri}:`, error);
|
|
1016
|
-
return undefined;
|
|
1017
|
-
}
|
|
1018
|
-
})
|
|
1019
|
-
);
|
|
1020
|
-
|
|
1021
|
-
type ResolvedCredential = {
|
|
1022
|
-
issuer?: string | { id: string };
|
|
1023
|
-
id?: string;
|
|
1024
|
-
boostCredential?: Record<string, unknown>;
|
|
1025
|
-
} & Record<string, unknown>;
|
|
1026
|
-
|
|
1027
|
-
const credentials = resolvedCredentials
|
|
1028
|
-
.filter(
|
|
1029
|
-
(cred): cred is ResolvedCredential =>
|
|
1030
|
-
typeof cred === 'object' && cred !== null
|
|
1031
|
-
)
|
|
1032
|
-
.map(cred =>
|
|
1033
|
-
cred.boostCredential && typeof cred.boostCredential === 'object'
|
|
1034
|
-
? ({ ...cred.boostCredential, id: cred.id } as ResolvedCredential) // unwrap credential, preserve id
|
|
1035
|
-
: cred
|
|
1036
|
-
);
|
|
1037
|
-
|
|
1038
|
-
const transformedCredentials = credentials.map(cred => {
|
|
1039
|
-
const issuer =
|
|
1040
|
-
typeof cred.issuer === 'string'
|
|
1041
|
-
? { id: cred.issuer }
|
|
1042
|
-
: cred.issuer || { id: '' };
|
|
1043
|
-
|
|
1044
|
-
return {
|
|
1045
|
-
...cred,
|
|
1046
|
-
issuer,
|
|
1047
|
-
};
|
|
1048
|
-
});
|
|
1049
|
-
|
|
1050
|
-
const { name, email } = parsedTerms.read.personal;
|
|
1051
|
-
|
|
1052
|
-
const body = JSON.stringify({
|
|
1053
|
-
'@context': [
|
|
1054
|
-
'https://www.w3.org/ns/credentials/v2',
|
|
1055
|
-
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
|
|
1056
|
-
'https://w3id.org/security/suites/ed25519-2020/v1',
|
|
1057
|
-
],
|
|
1058
|
-
recipienttoken: recipientToken,
|
|
1059
|
-
recipient: {
|
|
1060
|
-
id: ctx.user.did,
|
|
1061
|
-
givenName: name && name !== 'Anonymous' ? name : '',
|
|
1062
|
-
familyName: '', // this is neecessary in order for givenName to be respected
|
|
1063
|
-
email: email && email !== 'anonymous@hidden.com' ? email : '',
|
|
1096
|
+
const fingerprint = isSmartResume
|
|
1097
|
+
? smartResumeFingerprint({
|
|
1098
|
+
terms,
|
|
1099
|
+
expiresAt,
|
|
1100
|
+
oneTime,
|
|
1101
|
+
audienceVersion,
|
|
1102
|
+
recipientToken,
|
|
1103
|
+
expectedRequestId,
|
|
1104
|
+
})
|
|
1105
|
+
: undefined;
|
|
1106
|
+
// A completed one-time snapshot can be followed by a new reviewed decision.
|
|
1107
|
+
// Identical submissions still replay the completed upload instead.
|
|
1108
|
+
const newDecisionAfterOneTime =
|
|
1109
|
+
retrySmartResume &&
|
|
1110
|
+
previousTerms?.status === 'stale' &&
|
|
1111
|
+
previousTerms.oneTime &&
|
|
1112
|
+
previousTerms.smartResumePublicationStatus === 'succeeded' &&
|
|
1113
|
+
fingerprint !== previousTerms.smartResumeFingerprint;
|
|
1114
|
+
if (!retrySmartResume || newDecisionAfterOneTime)
|
|
1115
|
+
await consentToContract(
|
|
1116
|
+
profile,
|
|
1117
|
+
contractDetails,
|
|
1118
|
+
{
|
|
1119
|
+
terms,
|
|
1120
|
+
expiresAt,
|
|
1121
|
+
oneTime,
|
|
1122
|
+
audienceVersion,
|
|
1123
|
+
expectedRequestId,
|
|
1124
|
+
smartResumeFingerprint: fingerprint,
|
|
1125
|
+
guardianApproval: guardianIdentity
|
|
1126
|
+
? {
|
|
1127
|
+
guardianProfileId: guardianIdentity.profileId,
|
|
1128
|
+
guardianDid: guardianIdentity.did,
|
|
1129
|
+
approvedAt: new Date().toISOString(),
|
|
1130
|
+
contractUpdatedAt: contractDetails.contract.updatedAt,
|
|
1131
|
+
}
|
|
1132
|
+
: undefined,
|
|
1064
1133
|
},
|
|
1065
|
-
|
|
1066
|
-
});
|
|
1067
|
-
|
|
1068
|
-
try {
|
|
1069
|
-
const response = await fetch(`${srUrl}api/v1/credentials`, {
|
|
1070
|
-
method: 'POST',
|
|
1071
|
-
headers: {
|
|
1072
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1073
|
-
'Content-Type': 'application/json',
|
|
1074
|
-
},
|
|
1075
|
-
body,
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
|
-
if (!response.ok) {
|
|
1079
|
-
throw new Error(`Error (${response.status}): ${await response.text()}`);
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
const result = (await response.json()) as { redirect_url?: string };
|
|
1083
|
-
redirectUrl = result.redirect_url;
|
|
1084
|
-
} catch (error) {
|
|
1085
|
-
console.error('Error uploading credentials to SmartResume:', error);
|
|
1086
|
-
throw error;
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
await consentToContract(
|
|
1091
|
-
profile,
|
|
1092
|
-
contractDetails,
|
|
1093
|
-
{
|
|
1094
|
-
terms,
|
|
1095
|
-
expiresAt,
|
|
1096
|
-
oneTime,
|
|
1097
|
-
guardianApproval: guardianIdentity
|
|
1098
|
-
? {
|
|
1099
|
-
guardianProfileId: guardianIdentity.profileId,
|
|
1100
|
-
guardianDid: guardianIdentity.did,
|
|
1101
|
-
approvedAt: new Date().toISOString(),
|
|
1102
|
-
contractUpdatedAt: contractDetails.contract.updatedAt,
|
|
1103
|
-
}
|
|
1104
|
-
: undefined,
|
|
1105
|
-
},
|
|
1106
|
-
ctx.domain
|
|
1107
|
-
);
|
|
1108
|
-
|
|
1109
|
-
try {
|
|
1110
|
-
await updateRequestedForStatusIfExists(
|
|
1111
|
-
contractDetails.contract.id,
|
|
1112
|
-
profile.profileId,
|
|
1113
|
-
'accepted'
|
|
1134
|
+
ctx.domain
|
|
1114
1135
|
);
|
|
1115
|
-
} catch {}
|
|
1116
1136
|
|
|
1117
1137
|
const relationship = await getContractTermsForProfile(
|
|
1118
1138
|
profile,
|
|
@@ -1126,6 +1146,18 @@ export const contractsRouter = t.router({
|
|
|
1126
1146
|
});
|
|
1127
1147
|
}
|
|
1128
1148
|
|
|
1149
|
+
const redirectUrl = isSmartResume
|
|
1150
|
+
? await publishSmartResume({
|
|
1151
|
+
contractId: contractDetails.contract.id,
|
|
1152
|
+
profileId: profile.profileId,
|
|
1153
|
+
termsId: relationship.id,
|
|
1154
|
+
fingerprint: fingerprint!,
|
|
1155
|
+
audienceVersion,
|
|
1156
|
+
expectedRequestId,
|
|
1157
|
+
upload: acceptedTerms =>
|
|
1158
|
+
uploadSmartResume(acceptedTerms, ctx.user.did, recipientToken!),
|
|
1159
|
+
})
|
|
1160
|
+
: undefined;
|
|
1129
1161
|
return { termsUri: constructUri('terms', relationship.id, ctx.domain), redirectUrl };
|
|
1130
1162
|
}),
|
|
1131
1163
|
|
|
@@ -1187,9 +1219,14 @@ export const contractsRouter = t.router({
|
|
|
1187
1219
|
? { expiresAt: record.contract.expiresAt }
|
|
1188
1220
|
: {}),
|
|
1189
1221
|
autoBoosts: record.autoBoosts,
|
|
1222
|
+
audienceVersion: Number(record.contract.audienceVersion ?? 0),
|
|
1223
|
+
recipients: record.recipients.map(recipient =>
|
|
1224
|
+
publicContractRecipient(ctx.domain, recipient)
|
|
1225
|
+
),
|
|
1190
1226
|
},
|
|
1191
1227
|
uri: constructUri('terms', record.terms.id, ctx.domain),
|
|
1192
1228
|
terms: record.terms.terms,
|
|
1229
|
+
...(record.terms.referral ? { referral: record.terms.referral } : {}),
|
|
1193
1230
|
...(record.terms.expiresAt ? { expiresAt: record.terms.expiresAt } : {}),
|
|
1194
1231
|
...(record.terms.oneTime ? { oneTime: record.terms.oneTime } : {}),
|
|
1195
1232
|
consenter: updateDidForProfile(ctx.domain, profile),
|
|
@@ -1216,6 +1253,7 @@ export const contractsRouter = t.router({
|
|
|
1216
1253
|
terms: ConsentFlowTermsValidator,
|
|
1217
1254
|
expiresAt: z.string().optional(),
|
|
1218
1255
|
oneTime: z.boolean().optional(),
|
|
1256
|
+
audienceVersion: z.number().int().nonnegative().optional(),
|
|
1219
1257
|
})
|
|
1220
1258
|
)
|
|
1221
1259
|
.output(z.boolean())
|
|
@@ -1231,7 +1269,7 @@ export const contractsRouter = t.router({
|
|
|
1231
1269
|
});
|
|
1232
1270
|
}
|
|
1233
1271
|
|
|
1234
|
-
const { uri, terms, expiresAt, oneTime } = input;
|
|
1272
|
+
const { uri, terms, expiresAt, oneTime, audienceVersion } = input;
|
|
1235
1273
|
|
|
1236
1274
|
const decodedUri = decodeURIComponent(uri);
|
|
1237
1275
|
const relationship = await getContractTermsByUri(decodedUri);
|
|
@@ -1280,6 +1318,7 @@ export const contractsRouter = t.router({
|
|
|
1280
1318
|
terms,
|
|
1281
1319
|
expiresAt,
|
|
1282
1320
|
oneTime,
|
|
1321
|
+
audienceVersion,
|
|
1283
1322
|
guardianApproval: guardianIdentity
|
|
1284
1323
|
? {
|
|
1285
1324
|
guardianProfileId: guardianIdentity.profileId,
|
|
@@ -1405,7 +1444,7 @@ export const contractsRouter = t.router({
|
|
|
1405
1444
|
});
|
|
1406
1445
|
}
|
|
1407
1446
|
|
|
1408
|
-
await Promise.all([withdrawTerms(relationship), deleteStorageForUri(uri)]);
|
|
1447
|
+
await Promise.all([withdrawTerms(relationship, ctx.domain), deleteStorageForUri(uri)]);
|
|
1409
1448
|
|
|
1410
1449
|
return true;
|
|
1411
1450
|
}),
|
|
@@ -1537,13 +1576,14 @@ export const contractsRouter = t.router({
|
|
|
1537
1576
|
.input(
|
|
1538
1577
|
z.object({
|
|
1539
1578
|
termsUri: z.string(),
|
|
1579
|
+
audienceVersion: z.number().int().nonnegative().optional(),
|
|
1540
1580
|
categories: z.record(z.string(), z.string().array()),
|
|
1541
1581
|
})
|
|
1542
1582
|
)
|
|
1543
1583
|
.output(z.boolean())
|
|
1544
1584
|
.mutation(async ({ ctx, input }) => {
|
|
1545
1585
|
const { profile } = ctx.user;
|
|
1546
|
-
const { termsUri, categories } = input;
|
|
1586
|
+
const { termsUri, categories, audienceVersion } = input;
|
|
1547
1587
|
|
|
1548
1588
|
// Verify the terms exist and belong to this profile
|
|
1549
1589
|
const relationship = await getContractTermsByUri(termsUri);
|
|
@@ -1613,7 +1653,12 @@ export const contractsRouter = t.router({
|
|
|
1613
1653
|
}
|
|
1614
1654
|
|
|
1615
1655
|
// Sync the credentials
|
|
1616
|
-
const result = await syncCredentialsToContract(
|
|
1656
|
+
const result = await syncCredentialsToContract(
|
|
1657
|
+
relationship,
|
|
1658
|
+
categories,
|
|
1659
|
+
audienceVersion,
|
|
1660
|
+
ctx.domain
|
|
1661
|
+
);
|
|
1617
1662
|
|
|
1618
1663
|
return result;
|
|
1619
1664
|
}),
|
|
@@ -1833,6 +1878,49 @@ export const contractsRouter = t.router({
|
|
|
1833
1878
|
return true;
|
|
1834
1879
|
}),
|
|
1835
1880
|
|
|
1881
|
+
sendContractRequest: profileRoute
|
|
1882
|
+
.meta({
|
|
1883
|
+
requiredScope: 'contracts:write',
|
|
1884
|
+
openapi: {
|
|
1885
|
+
protect: true,
|
|
1886
|
+
method: 'POST',
|
|
1887
|
+
path: '/consent-flow-contracts/request',
|
|
1888
|
+
tags: ['Contracts'],
|
|
1889
|
+
summary: 'Send an attributed contract request',
|
|
1890
|
+
},
|
|
1891
|
+
})
|
|
1892
|
+
.input(SendContractRequestValidator)
|
|
1893
|
+
.output(z.boolean())
|
|
1894
|
+
.mutation(async ({ ctx, input }) =>
|
|
1895
|
+
sendGenericContractRequest(input, ctx.user.profile, ctx.domain)
|
|
1896
|
+
),
|
|
1897
|
+
|
|
1898
|
+
denyContractRequest: profileRoute
|
|
1899
|
+
.meta({
|
|
1900
|
+
requiredScope: 'contracts:write',
|
|
1901
|
+
openapi: {
|
|
1902
|
+
protect: true,
|
|
1903
|
+
method: 'POST',
|
|
1904
|
+
path: '/consent-flow-contracts/deny-request',
|
|
1905
|
+
tags: ['Contracts'],
|
|
1906
|
+
summary: 'Deny a pending contract request as its target',
|
|
1907
|
+
},
|
|
1908
|
+
})
|
|
1909
|
+
.input(z.object({ contractUri: z.string() }))
|
|
1910
|
+
.output(z.boolean())
|
|
1911
|
+
.mutation(async ({ ctx, input }) => {
|
|
1912
|
+
const contract = await getContractByUri(input.contractUri);
|
|
1913
|
+
if (!contract)
|
|
1914
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Contract not found' });
|
|
1915
|
+
return decideGenericContractRequest({
|
|
1916
|
+
contract,
|
|
1917
|
+
targetProfileId: ctx.user.profile.profileId,
|
|
1918
|
+
actorProfileId: ctx.user.profile.profileId,
|
|
1919
|
+
status: 'denied',
|
|
1920
|
+
domain: ctx.domain,
|
|
1921
|
+
});
|
|
1922
|
+
}),
|
|
1923
|
+
|
|
1836
1924
|
sendAiInsightsContractRequest: profileRoute
|
|
1837
1925
|
.meta({
|
|
1838
1926
|
openapi: {
|
|
@@ -1857,43 +1945,14 @@ export const contractsRouter = t.router({
|
|
|
1857
1945
|
const { profile } = ctx.user;
|
|
1858
1946
|
const { contractUri, targetProfileId, shareLink } = input;
|
|
1859
1947
|
|
|
1860
|
-
const
|
|
1948
|
+
const { contract: contractByUri, target: targetProfile } = await resolveContractRequest(
|
|
1949
|
+
contractUri,
|
|
1861
1950
|
targetProfileId,
|
|
1862
|
-
|
|
1951
|
+
profile,
|
|
1952
|
+
ctx.domain,
|
|
1953
|
+
false
|
|
1863
1954
|
);
|
|
1864
|
-
|
|
1865
|
-
throw new TRPCError({ code: 'NOT_FOUND', message: 'Profile not found' });
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
const contractByUri = await getContractByUri(contractUri);
|
|
1869
|
-
|
|
1870
|
-
if (!contractByUri)
|
|
1871
|
-
throw new TRPCError({ code: 'NOT_FOUND', message: 'Contract not found' });
|
|
1872
|
-
|
|
1873
|
-
const contract = await getConsentFlowContractById(contractByUri.id); // model instance
|
|
1874
|
-
|
|
1875
|
-
if (!contract) {
|
|
1876
|
-
throw new TRPCError({ code: 'NOT_FOUND', message: 'Contract not found' });
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
const writers = await getWritersForContract(contractByUri);
|
|
1880
|
-
const isAuthorized = writers.some(writer => writer.profileId === profile.profileId);
|
|
1881
|
-
|
|
1882
|
-
if (!isAuthorized) {
|
|
1883
|
-
throw new TRPCError({
|
|
1884
|
-
code: 'UNAUTHORIZED',
|
|
1885
|
-
message: 'You do not have permission to send requests for this contract.',
|
|
1886
|
-
});
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
const targetProfile = await getProfileByProfileId(resolvedTargetProfileId);
|
|
1890
|
-
|
|
1891
|
-
if (!targetProfile) {
|
|
1892
|
-
throw new TRPCError({
|
|
1893
|
-
code: 'NOT_FOUND',
|
|
1894
|
-
message: 'Target profile not found.',
|
|
1895
|
-
});
|
|
1896
|
-
}
|
|
1955
|
+
const resolvedTargetProfileId = targetProfile.profileId;
|
|
1897
1956
|
|
|
1898
1957
|
try {
|
|
1899
1958
|
await upsertRequestedForRelationship(
|
|
@@ -1903,6 +1962,7 @@ export const contractsRouter = t.router({
|
|
|
1903
1962
|
null
|
|
1904
1963
|
);
|
|
1905
1964
|
} catch (error) {
|
|
1965
|
+
if (error instanceof TRPCError && error.code === 'CONFLICT') throw error;
|
|
1906
1966
|
throw new TRPCError({
|
|
1907
1967
|
code: 'BAD_REQUEST',
|
|
1908
1968
|
message: 'Unable to send request',
|
|
@@ -2035,9 +2095,9 @@ export const contractsRouter = t.router({
|
|
|
2035
2095
|
)
|
|
2036
2096
|
.output(
|
|
2037
2097
|
z.array(
|
|
2038
|
-
|
|
2098
|
+
ContractRequestFieldsValidator.extend({
|
|
2039
2099
|
profile: LCNProfileValidator,
|
|
2040
|
-
status: z.enum(['pending', 'accepted', 'denied']).nullable(),
|
|
2100
|
+
status: z.enum(['pending', 'accepted', 'denied', 'cancelled']).nullable(),
|
|
2041
2101
|
readStatus: z.enum(['unseen', 'seen']).nullable().optional(),
|
|
2042
2102
|
})
|
|
2043
2103
|
)
|
|
@@ -2050,17 +2110,19 @@ export const contractsRouter = t.router({
|
|
|
2050
2110
|
|
|
2051
2111
|
if (!contractByUri)
|
|
2052
2112
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'Contract not found' });
|
|
2053
|
-
const
|
|
2054
|
-
const isAuthorized = writers.some(w => w.profileId === profile.profileId);
|
|
2113
|
+
const isAuthorized = await canManageContractRequests(contractByUri, profile.profileId);
|
|
2055
2114
|
|
|
2056
|
-
if (
|
|
2115
|
+
if (
|
|
2116
|
+
!isAuthorized &&
|
|
2117
|
+
!(await canReadContractData(contractByUri.id, profile.profileId))
|
|
2118
|
+
) {
|
|
2057
2119
|
throw new TRPCError({
|
|
2058
2120
|
code: 'UNAUTHORIZED',
|
|
2059
2121
|
message: 'You do not have permissions to view requests made for this contract.',
|
|
2060
2122
|
});
|
|
2061
2123
|
}
|
|
2062
2124
|
|
|
2063
|
-
const requests = await getRequestedForList(contractByUri.id);
|
|
2125
|
+
const requests = await getRequestedForList(contractByUri.id, profile.profileId);
|
|
2064
2126
|
|
|
2065
2127
|
return Promise.all(
|
|
2066
2128
|
requests.map(async request => {
|
|
@@ -2095,13 +2157,11 @@ export const contractsRouter = t.router({
|
|
|
2095
2157
|
})
|
|
2096
2158
|
)
|
|
2097
2159
|
.output(
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
})
|
|
2104
|
-
.nullable()
|
|
2160
|
+
ContractRequestFieldsValidator.extend({
|
|
2161
|
+
profile: LCNProfileValidator,
|
|
2162
|
+
status: z.enum(['pending', 'accepted', 'denied', 'cancelled']).nullable(),
|
|
2163
|
+
readStatus: z.enum(['unseen', 'seen']).nullable().optional(),
|
|
2164
|
+
}).nullable()
|
|
2105
2165
|
)
|
|
2106
2166
|
.query(async ({ ctx, input }) => {
|
|
2107
2167
|
const { profile } = ctx.user;
|
|
@@ -2128,10 +2188,9 @@ export const contractsRouter = t.router({
|
|
|
2128
2188
|
const isCheckingOwnStatus = profile.profileId === resolvedTargetProfileId;
|
|
2129
2189
|
|
|
2130
2190
|
if (!isCheckingOwnStatus) {
|
|
2131
|
-
const
|
|
2132
|
-
const isAuthorized = writers.some(w => w.profileId === profile.profileId);
|
|
2191
|
+
const isAuthorized = await canManageContractRequests(contract, profile.profileId);
|
|
2133
2192
|
|
|
2134
|
-
if (!isAuthorized) {
|
|
2193
|
+
if (!isAuthorized && !(await canReadContractData(contract.id, profile.profileId))) {
|
|
2135
2194
|
throw new TRPCError({
|
|
2136
2195
|
code: 'UNAUTHORIZED',
|
|
2137
2196
|
message:
|
|
@@ -2140,7 +2199,11 @@ export const contractsRouter = t.router({
|
|
|
2140
2199
|
}
|
|
2141
2200
|
}
|
|
2142
2201
|
|
|
2143
|
-
const requests = await getRequestedForForUser(
|
|
2202
|
+
const requests = await getRequestedForForUser(
|
|
2203
|
+
contract.id,
|
|
2204
|
+
resolvedTargetProfileId,
|
|
2205
|
+
profile.profileId
|
|
2206
|
+
);
|
|
2144
2207
|
|
|
2145
2208
|
if (!requests?.[0]) return null;
|
|
2146
2209
|
|
|
@@ -2163,7 +2226,7 @@ export const contractsRouter = t.router({
|
|
|
2163
2226
|
tags: ['Contracts'],
|
|
2164
2227
|
summary: 'Marks a contract request as seen',
|
|
2165
2228
|
description:
|
|
2166
|
-
'Updates the read status of a contract request to "seen" for the specified target profile.
|
|
2229
|
+
'Updates the read status of a contract request to "seen" for the specified target profile. Targets can mark their existing requests seen. Legacy writer behavior is retained for legacy requests.',
|
|
2167
2230
|
},
|
|
2168
2231
|
})
|
|
2169
2232
|
.input(
|
|
@@ -2190,6 +2253,31 @@ export const contractsRouter = t.router({
|
|
|
2190
2253
|
if (!contractByUri)
|
|
2191
2254
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'Contract not found' });
|
|
2192
2255
|
|
|
2256
|
+
const request = await getStoredContractRequest(
|
|
2257
|
+
contractByUri.id,
|
|
2258
|
+
resolvedTargetProfileId
|
|
2259
|
+
);
|
|
2260
|
+
if (request?.requestId) {
|
|
2261
|
+
assertGenericRequestWriteScope(ctx.user.scope);
|
|
2262
|
+
if (profile.profileId !== resolvedTargetProfileId)
|
|
2263
|
+
throw new TRPCError({
|
|
2264
|
+
code: 'UNAUTHORIZED',
|
|
2265
|
+
message: 'Only the target can mark this request seen',
|
|
2266
|
+
});
|
|
2267
|
+
return markGenericContractRequestSeen(contractByUri.id, resolvedTargetProfileId);
|
|
2268
|
+
}
|
|
2269
|
+
if (profile.profileId === resolvedTargetProfileId) {
|
|
2270
|
+
if (!request)
|
|
2271
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Request not found' });
|
|
2272
|
+
await upsertRequestedForRelationship(
|
|
2273
|
+
contractByUri.id,
|
|
2274
|
+
resolvedTargetProfileId,
|
|
2275
|
+
undefined,
|
|
2276
|
+
'seen'
|
|
2277
|
+
);
|
|
2278
|
+
return true;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2193
2281
|
const writers = await getWritersForContract(contractByUri);
|
|
2194
2282
|
const isAuthorized = writers.some(w => w.profileId === profile.profileId);
|
|
2195
2283
|
|
|
@@ -2226,7 +2314,7 @@ export const contractsRouter = t.router({
|
|
|
2226
2314
|
tags: ['Contracts'],
|
|
2227
2315
|
summary: 'Cancels/removes a contract request',
|
|
2228
2316
|
description:
|
|
2229
|
-
'
|
|
2317
|
+
'Cancels a pending generic request while retaining its history. Owner/writers, the requesting recipient, or the target may cancel. Legacy requests retain deletion behavior.',
|
|
2230
2318
|
},
|
|
2231
2319
|
})
|
|
2232
2320
|
.input(
|
|
@@ -2253,6 +2341,21 @@ export const contractsRouter = t.router({
|
|
|
2253
2341
|
if (!contractByUri)
|
|
2254
2342
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'Contract not found' });
|
|
2255
2343
|
|
|
2344
|
+
const request = await getStoredContractRequest(
|
|
2345
|
+
contractByUri.id,
|
|
2346
|
+
resolvedTargetProfileId
|
|
2347
|
+
);
|
|
2348
|
+
if (request?.requestId) {
|
|
2349
|
+
assertGenericRequestWriteScope(ctx.user.scope);
|
|
2350
|
+
return decideGenericContractRequest({
|
|
2351
|
+
contract: contractByUri,
|
|
2352
|
+
targetProfileId: resolvedTargetProfileId,
|
|
2353
|
+
actorProfileId: profile.profileId,
|
|
2354
|
+
status: 'cancelled',
|
|
2355
|
+
domain: ctx.domain,
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2256
2359
|
const isDenyingRequest = profile.profileId === resolvedTargetProfileId;
|
|
2257
2360
|
|
|
2258
2361
|
if (!isDenyingRequest) {
|
|
@@ -2311,10 +2414,15 @@ export const contractsRouter = t.router({
|
|
|
2311
2414
|
)
|
|
2312
2415
|
.output(
|
|
2313
2416
|
z.array(
|
|
2314
|
-
|
|
2315
|
-
contract: ConsentFlowContractValidator.extend({
|
|
2417
|
+
ContractRequestFieldsValidator.extend({
|
|
2418
|
+
contract: ConsentFlowContractValidator.extend({
|
|
2419
|
+
uri: z.string(),
|
|
2420
|
+
name: z.string().optional(),
|
|
2421
|
+
image: z.string().optional(),
|
|
2422
|
+
description: z.string().optional(),
|
|
2423
|
+
}),
|
|
2316
2424
|
profile: LCNProfileValidator,
|
|
2317
|
-
status: z.enum(['pending', 'accepted', 'denied']).nullable(),
|
|
2425
|
+
status: z.enum(['pending', 'accepted', 'denied', 'cancelled']).nullable(),
|
|
2318
2426
|
readStatus: z.enum(['unseen', 'seen']).nullable().optional(),
|
|
2319
2427
|
})
|
|
2320
2428
|
)
|
|
@@ -2354,7 +2462,10 @@ export const contractsRouter = t.router({
|
|
|
2354
2462
|
sanitizeProfileForTier(updatedProfile, 'authenticated')
|
|
2355
2463
|
),
|
|
2356
2464
|
contract: {
|
|
2357
|
-
...request.contract,
|
|
2465
|
+
...inflateObject(request.contract).contract,
|
|
2466
|
+
name: request.contract.name,
|
|
2467
|
+
image: request.contract.image,
|
|
2468
|
+
description: request.contract.description,
|
|
2358
2469
|
uri: constructUri('contract', request.contract.id, ctx.domain),
|
|
2359
2470
|
},
|
|
2360
2471
|
};
|
|
@@ -2381,7 +2492,7 @@ export const contractsRouter = t.router({
|
|
|
2381
2492
|
)
|
|
2382
2493
|
.output(
|
|
2383
2494
|
z.array(
|
|
2384
|
-
|
|
2495
|
+
ContractRequestFieldsValidator.extend({
|
|
2385
2496
|
profile: LCNProfileValidator,
|
|
2386
2497
|
status: z.enum(['pending', 'accepted', 'denied']).nullable(),
|
|
2387
2498
|
readStatus: z.enum(['unseen', 'seen']).nullable().optional(),
|