@learncard/network-brain-service 3.19.1 → 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 +44 -111
- package/AGENTS.md +52 -0
- package/CHANGELOG.md +109 -0
- package/README.md +9 -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/share-link/abandon.d.ts.map +1 -1
- 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/finalize.d.ts.map +1 -1
- package/dist/accesslayer/share-link/helpers.d.ts +3 -0
- package/dist/accesslayer/share-link/helpers.d.ts.map +1 -1
- package/dist/accesslayer/share-link/recovery.d.ts.map +1 -1
- package/dist/accesslayer/share-link/reserve.d.ts.map +1 -1
- package/dist/accesslayer/share-link/revoke.d.ts.map +1 -1
- package/dist/accesslayer/share-link/types.d.ts +8 -3
- package/dist/accesslayer/share-link/types.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 +5 -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/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/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/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/profile.helpers.d.ts +1 -0
- package/dist/helpers/profile.helpers.d.ts.map +1 -1
- 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/coordinator.d.ts.map +1 -1
- package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/runner.d.ts +1 -0
- package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/runtime.d.ts +2 -0
- package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/types.d.ts +4 -1
- package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -1
- package/dist/helpers/share-link-owner-projection.d.ts.map +1 -1
- package/dist/helpers/share-link-policy/production.d.ts +3 -5
- package/dist/helpers/share-link-policy/production.d.ts.map +1 -1
- package/dist/helpers/share-link-policy/resolver.d.ts +1 -1
- package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -1
- package/dist/helpers/share-link-policy/types.d.ts +8 -2
- package/dist/helpers/share-link-policy/types.d.ts.map +1 -1
- 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/ShareLink.d.ts +2 -0
- package/dist/models/ShareLink.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.map +1 -1
- package/dist/openapi.d.ts +1 -1
- package/dist/openapi.d.ts.map +1 -1
- 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 +146 -3
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/credentials.d.ts +34 -0
- 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.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 +55 -0
- package/dist/routes/public-share-links.d.ts.map +1 -1
- package/dist/routes/share-links.d.ts +175 -14
- package/dist/routes/share-links.d.ts.map +1 -1
- 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/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 +30 -3
- 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 -195
- package/lambdaApp.ts +203 -0
- package/package.json +20 -18
- package/scripts/export-openapi.ts +20 -0
- package/scripts/offline-schema-environment.ts +18 -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 +27 -74
- package/shareLinkMaintenanceLambda.ts +13 -44
- 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 +116 -93
- 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 +240 -189
- 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/share-link/abandon.ts +20 -0
- package/src/accesslayer/share-link/attachment.ts +360 -0
- package/src/accesslayer/share-link/finalize.ts +55 -7
- package/src/accesslayer/share-link/helpers.ts +33 -2
- package/src/accesslayer/share-link/recovery.ts +20 -0
- package/src/accesslayer/share-link/reserve.ts +85 -6
- package/src/accesslayer/share-link/revoke.ts +35 -0
- package/src/accesslayer/share-link/types.ts +9 -4
- 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 +1 -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/docker-entry.ts +24 -0
- package/src/helpers/app-manifest.helpers.ts +185 -0
- 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/finalize-inbox.helpers.ts +163 -44
- package/src/helpers/inbox.helpers.ts +51 -66
- package/src/helpers/inline-template.helpers.ts +560 -0
- package/src/helpers/notificationMessages.ts +59 -0
- package/src/helpers/notificationQueue.helpers.ts +23 -5
- package/src/helpers/notifications.helpers.ts +25 -1
- package/src/helpers/profile.helpers.ts +6 -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/coordinator.ts +15 -2
- package/src/helpers/share-link-coordinator/recovery-runner.ts +15 -0
- package/src/helpers/share-link-maintenance/runner.ts +28 -0
- package/src/helpers/share-link-maintenance/runtime.ts +6 -0
- package/src/helpers/share-link-maintenance/telemetry.ts +3 -0
- package/src/helpers/share-link-maintenance/types.ts +2 -0
- package/src/helpers/share-link-owner-projection.ts +2 -0
- package/src/helpers/share-link-policy/production.ts +37 -17
- package/src/helpers/share-link-policy/resolver.ts +16 -11
- package/src/helpers/share-link-policy/types.ts +15 -7
- 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/ShareLink.ts +4 -0
- package/src/models/index.ts +23 -0
- package/src/models/share-link-constraints.ts +5 -0
- package/src/openapi.ts +44 -8
- package/src/routes/app-store.ts +1054 -27
- package/src/routes/boosts.ts +12 -14
- package/src/routes/contracts.ts +382 -262
- package/src/routes/inbox.ts +4 -2
- package/src/routes/index.ts +4 -1
- package/src/routes/profile-manager.ts +19 -0
- package/src/routes/profiles.ts +12 -0
- package/src/routes/public-share-links.ts +169 -0
- package/src/routes/share-links.ts +69 -0
- package/src/routes/workflows.ts +3 -2
- 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/guardian-gated-routes.spec.ts +36 -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 +24 -2
- package/test/openapi.spec.ts +14 -73
- package/test/profiles.spec.ts +30 -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-lifecycle.neo4j.spec.ts +145 -2
- package/test/share-link-maintenance.unit.spec.ts +32 -5
- package/test/share-link-owner-route.unit.spec.ts +70 -2
- package/test/share-link-policy.unit.spec.ts +18 -4
- package/test/share-link-production-policy.unit.spec.ts +58 -9
- package/test/share-link-public-route.unit.spec.ts +279 -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 +13 -0
- package/vitest.inbox-e2e.config.ts +2 -0
- package/workerLambda.test.ts +100 -0
package/src/routes/inbox.ts
CHANGED
|
@@ -768,8 +768,10 @@ export const inboxRouter = t.router({
|
|
|
768
768
|
claimed: z.number(),
|
|
769
769
|
errors: z.number(),
|
|
770
770
|
guardianPending: z.number(),
|
|
771
|
-
verifiableCredentials: z.array(VCValidator),
|
|
772
|
-
deliveries: z.array(
|
|
771
|
+
verifiableCredentials: z.array(VCValidator.or(JWEValidator)),
|
|
772
|
+
deliveries: z.array(
|
|
773
|
+
z.object({ id: z.string(), credential: VCValidator.or(JWEValidator) })
|
|
774
|
+
),
|
|
773
775
|
})
|
|
774
776
|
)
|
|
775
777
|
.mutation(async ({ ctx }) => {
|
package/src/routes/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from '@accesslayer/profile/relationships/read';
|
|
24
24
|
import { getDidWeb } from '@helpers/did.helpers';
|
|
25
25
|
import { getEmptyLearnCard, isServersDidWebDID } from '@helpers/learnCard.helpers';
|
|
26
|
+
import { isServiceProfileExemptFromGuardianship } from '@helpers/profile.helpers';
|
|
26
27
|
import { invalidateChallengeForDid, isChallengeValidForDid } from '@cache/challenges';
|
|
27
28
|
import { ProfileType } from 'types/profile';
|
|
28
29
|
import { getProfileManagerById } from '@accesslayer/profile-manager/read';
|
|
@@ -555,7 +556,9 @@ export const guardianGatedRoute = profileRoute.use(async ({ ctx, next }) => {
|
|
|
555
556
|
const { profile } = ctx.user;
|
|
556
557
|
const guardianApprovalToken = ctx._guardianApprovalToken;
|
|
557
558
|
// Service profiles (orgs) are managed for admin purposes, not guardianship.
|
|
558
|
-
const isChildAccount =
|
|
559
|
+
const isChildAccount =
|
|
560
|
+
!isServiceProfileExemptFromGuardianship(profile.isServiceProfile, profile.type) &&
|
|
561
|
+
(await isProfileManaged(profile.profileId));
|
|
559
562
|
let guardianIdentity: { profileId: string; did: string } | undefined;
|
|
560
563
|
|
|
561
564
|
if (isChildAccount && guardianApprovalToken) {
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import { getProfilesThatManageAProfile } from '@accesslayer/profile/relationships/read';
|
|
30
30
|
import { updateProfileManager } from '@accesslayer/profile-manager/update';
|
|
31
31
|
import { getProfileManagerById } from '@accesslayer/profile-manager/read';
|
|
32
|
+
import { ProfileManager } from '@models';
|
|
32
33
|
|
|
33
34
|
export const profileManagersRouter = t.router({
|
|
34
35
|
createProfileManager: profileRoute
|
|
@@ -126,6 +127,24 @@ export const profileManagersRouter = t.router({
|
|
|
126
127
|
)
|
|
127
128
|
.output(z.string())
|
|
128
129
|
.mutation(async ({ input, ctx }) => {
|
|
130
|
+
// Org onboarding also uses this API. Only the persisted Family parent
|
|
131
|
+
// (or an explicit child type), not a client service flag, defines a child path.
|
|
132
|
+
if (input.isServiceProfile) {
|
|
133
|
+
const parents = await ProfileManager.findRelationships({
|
|
134
|
+
alias: 'childOf',
|
|
135
|
+
where: { source: { id: ctx.user.manager.id } },
|
|
136
|
+
});
|
|
137
|
+
if (
|
|
138
|
+
input.type === 'child' ||
|
|
139
|
+
parents.some(({ target }) => target.category === 'Family')
|
|
140
|
+
) {
|
|
141
|
+
throw new TRPCError({
|
|
142
|
+
code: 'BAD_REQUEST',
|
|
143
|
+
message: 'Child profiles cannot be service profiles.',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
129
148
|
const profileExists = await checkIfProfileExists(input);
|
|
130
149
|
|
|
131
150
|
if (profileExists) {
|
package/src/routes/profiles.ts
CHANGED
|
@@ -574,6 +574,18 @@ export const profilesRouter = t.router({
|
|
|
574
574
|
.output(z.boolean())
|
|
575
575
|
.mutation(async ({ input, ctx }) => {
|
|
576
576
|
const { profile } = ctx.user;
|
|
577
|
+
if (
|
|
578
|
+
profile.isServiceProfile === true &&
|
|
579
|
+
profile.type === 'child' &&
|
|
580
|
+
typeof input.type === 'string' &&
|
|
581
|
+
input.type !== 'child'
|
|
582
|
+
) {
|
|
583
|
+
throw new TRPCError({
|
|
584
|
+
code: 'BAD_REQUEST',
|
|
585
|
+
message:
|
|
586
|
+
'A service profile explicitly typed as a child cannot change profile type.',
|
|
587
|
+
});
|
|
588
|
+
}
|
|
577
589
|
|
|
578
590
|
const {
|
|
579
591
|
profileId,
|
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
ShareEnvelopeValidator,
|
|
11
11
|
ShareLinkPublicContentViewValidator,
|
|
12
12
|
ShareLinkPublicStateValidator,
|
|
13
|
+
GetShareLinkAttachmentChunkInputValidator,
|
|
14
|
+
ShareLinkAttachmentChunkOutputValidator,
|
|
15
|
+
ShareLinkAttachmentChunkEnvelopeValidator,
|
|
13
16
|
} from '@learncard/types';
|
|
14
17
|
import { LCNNotificationTypeEnumValidator } from '@learncard/types';
|
|
15
18
|
|
|
@@ -32,6 +35,11 @@ import type {
|
|
|
32
35
|
ShareViewEligibilitySource,
|
|
33
36
|
} from '@accesslayer/share-link';
|
|
34
37
|
import type { ShareLinkRecord } from '../models/ShareLink';
|
|
38
|
+
import { shareAttachmentTuple } from '@accesslayer/share-link/attachment';
|
|
39
|
+
import {
|
|
40
|
+
mintShareAttachmentAccessToken,
|
|
41
|
+
verifyShareAttachmentAccessToken,
|
|
42
|
+
} from '@helpers/share-link-attachment-access';
|
|
35
43
|
|
|
36
44
|
/**
|
|
37
45
|
* LC-2187 public (anonymous) share-link API: resolve, guarded content and
|
|
@@ -67,6 +75,11 @@ export type PublicShareLinkRouterDependencies = {
|
|
|
67
75
|
}) => Promise<
|
|
68
76
|
{ ok: true; value: ShareContentContentProjection } | { ok: false; error: string }
|
|
69
77
|
>;
|
|
78
|
+
fetchAttachmentChunk?: (
|
|
79
|
+
input: import('@helpers/share-content-client/types').ShareContentTuple
|
|
80
|
+
) => Promise<
|
|
81
|
+
{ ok: true; value: ShareContentContentProjection } | { ok: false; error: string }
|
|
82
|
+
>;
|
|
70
83
|
};
|
|
71
84
|
readonly receipts: {
|
|
72
85
|
persist: (input: PersistShareViewReceiptInput) => Promise<boolean>;
|
|
@@ -219,6 +232,161 @@ export const createPublicShareLinksRouter = (
|
|
|
219
232
|
};
|
|
220
233
|
|
|
221
234
|
return t.router({
|
|
235
|
+
attachmentChunk: openRouteWithoutInputCapture
|
|
236
|
+
.meta({
|
|
237
|
+
openapi: openapi(
|
|
238
|
+
'POST',
|
|
239
|
+
'/public/share-links/attachment-chunk',
|
|
240
|
+
'Fetch guarded encrypted attachment chunk'
|
|
241
|
+
),
|
|
242
|
+
})
|
|
243
|
+
.input(GetShareLinkAttachmentChunkInputValidator)
|
|
244
|
+
.output(ShareLinkAttachmentChunkOutputValidator)
|
|
245
|
+
.mutation(async ({ ctx, input }) => {
|
|
246
|
+
const dependencies = await resolve();
|
|
247
|
+
if (!dependencies.repository.fetchAttachmentChunk) notFound();
|
|
248
|
+
try {
|
|
249
|
+
await dependencies.enforceRateLimit({
|
|
250
|
+
key: rateLimitKey('attachment', dependencies.namespace, ctx.sourceIp),
|
|
251
|
+
limit: 240,
|
|
252
|
+
windowSeconds: 60,
|
|
253
|
+
description: 'public share-link attachment',
|
|
254
|
+
});
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (error instanceof TRPCError && error.code === 'TOO_MANY_REQUESTS')
|
|
257
|
+
throw new TRPCError({
|
|
258
|
+
code: 'TOO_MANY_REQUESTS',
|
|
259
|
+
message: 'try again later',
|
|
260
|
+
});
|
|
261
|
+
throw new TRPCError({
|
|
262
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
263
|
+
message: 'share-link content is unavailable',
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
const read = async (): Promise<ShareLinkRecord> => {
|
|
267
|
+
let record: ShareLinkRecord | null;
|
|
268
|
+
try {
|
|
269
|
+
record = await dependencies.repository.getShareLink({
|
|
270
|
+
shareId: input.id,
|
|
271
|
+
namespace: dependencies.namespace,
|
|
272
|
+
});
|
|
273
|
+
} catch {
|
|
274
|
+
throw new TRPCError({
|
|
275
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
276
|
+
message: 'share-link content is unavailable',
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
const state = classifyPublicShare(record, dependencies.now());
|
|
280
|
+
if (
|
|
281
|
+
state.state !== 'active' ||
|
|
282
|
+
state.record.namespace !== dependencies.namespace ||
|
|
283
|
+
state.record.contentVersion !== input.contentVersion ||
|
|
284
|
+
state.record.attachmentId !== input.attachmentId ||
|
|
285
|
+
!state.record.attachmentChunkCount ||
|
|
286
|
+
input.chunkIndex >= state.record.attachmentChunkCount
|
|
287
|
+
)
|
|
288
|
+
notFound();
|
|
289
|
+
return state.record;
|
|
290
|
+
};
|
|
291
|
+
const first = await read();
|
|
292
|
+
const grantAccepted =
|
|
293
|
+
input.accessToken &&
|
|
294
|
+
verifyShareAttachmentAccessToken(
|
|
295
|
+
input.accessToken,
|
|
296
|
+
first,
|
|
297
|
+
dependencies.namespace,
|
|
298
|
+
ctx.sourceIp,
|
|
299
|
+
dependencies.now()
|
|
300
|
+
);
|
|
301
|
+
const access = grantAccepted
|
|
302
|
+
? 'accepted'
|
|
303
|
+
: await passcodeAccepted(first, input.passcode, dependencies, ctx.sourceIp);
|
|
304
|
+
if (access === 'try_later')
|
|
305
|
+
throw new TRPCError({ code: 'TOO_MANY_REQUESTS', message: 'try again later' });
|
|
306
|
+
if (access !== 'accepted')
|
|
307
|
+
throw new TRPCError({
|
|
308
|
+
code: 'UNAUTHORIZED',
|
|
309
|
+
message: 'share-link passcode required',
|
|
310
|
+
});
|
|
311
|
+
const tuple = shareAttachmentTuple(
|
|
312
|
+
{
|
|
313
|
+
namespace: dependencies.namespace,
|
|
314
|
+
ownerProfileId: first.ownerProfileId,
|
|
315
|
+
shareId: first.id,
|
|
316
|
+
contentVersion: first.contentVersion,
|
|
317
|
+
attachmentId: input.attachmentId,
|
|
318
|
+
chunkCount: first.attachmentChunkCount!,
|
|
319
|
+
},
|
|
320
|
+
input.chunkIndex
|
|
321
|
+
);
|
|
322
|
+
let fetched;
|
|
323
|
+
try {
|
|
324
|
+
fetched = await dependencies.repository.fetchAttachmentChunk(tuple);
|
|
325
|
+
} catch {
|
|
326
|
+
throw new TRPCError({
|
|
327
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
328
|
+
message: 'share-link content is unavailable',
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (!fetched.ok) {
|
|
332
|
+
if (fetched.error === 'NOT_FOUND' || fetched.error === 'CONFLICT') notFound();
|
|
333
|
+
throw new TRPCError({
|
|
334
|
+
code: 'SERVICE_UNAVAILABLE',
|
|
335
|
+
message: 'share-link content is unavailable',
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
if (
|
|
339
|
+
Object.entries(tuple).some(
|
|
340
|
+
([key, value]) => fetched.value[key as keyof typeof fetched.value] !== value
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
notFound();
|
|
344
|
+
const final = await read();
|
|
345
|
+
if (
|
|
346
|
+
final.version !== first.version ||
|
|
347
|
+
final.ownerProfileId !== first.ownerProfileId ||
|
|
348
|
+
final.activeObjectRef !== first.activeObjectRef ||
|
|
349
|
+
final.passcodeHash !== first.passcodeHash
|
|
350
|
+
)
|
|
351
|
+
notFound();
|
|
352
|
+
if (
|
|
353
|
+
grantAccepted &&
|
|
354
|
+
!verifyShareAttachmentAccessToken(
|
|
355
|
+
input.accessToken!,
|
|
356
|
+
final,
|
|
357
|
+
dependencies.namespace,
|
|
358
|
+
ctx.sourceIp,
|
|
359
|
+
dependencies.now()
|
|
360
|
+
)
|
|
361
|
+
) {
|
|
362
|
+
throw new TRPCError({
|
|
363
|
+
code: 'UNAUTHORIZED',
|
|
364
|
+
message: 'share-link passcode required',
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const envelope = ShareLinkAttachmentChunkEnvelopeValidator.safeParse(
|
|
368
|
+
fetched.value.envelope
|
|
369
|
+
);
|
|
370
|
+
if (!envelope.success) notFound();
|
|
371
|
+
return {
|
|
372
|
+
id: input.id,
|
|
373
|
+
contentVersion: input.contentVersion,
|
|
374
|
+
attachmentId: input.attachmentId,
|
|
375
|
+
chunkIndex: input.chunkIndex,
|
|
376
|
+
envelope: envelope.data,
|
|
377
|
+
...(final.passcodeHash
|
|
378
|
+
? {
|
|
379
|
+
// Renew only after the chunk and its current policy pass every check.
|
|
380
|
+
accessToken: mintShareAttachmentAccessToken(
|
|
381
|
+
final,
|
|
382
|
+
dependencies.namespace,
|
|
383
|
+
ctx.sourceIp,
|
|
384
|
+
dependencies.now()
|
|
385
|
+
),
|
|
386
|
+
}
|
|
387
|
+
: {}),
|
|
388
|
+
};
|
|
389
|
+
}),
|
|
222
390
|
resolve: openRouteWithoutInputCapture
|
|
223
391
|
.meta({
|
|
224
392
|
openapi: openapi('POST', PUBLIC_RESOLVE_PATH, 'Resolve public share metadata'),
|
|
@@ -735,6 +903,7 @@ const buildProductionDependencies = async (
|
|
|
735
903
|
return {
|
|
736
904
|
namespace: config.namespace,
|
|
737
905
|
repository: {
|
|
906
|
+
fetchAttachmentChunk: input => client.get(input),
|
|
738
907
|
getShareLink: input => getShareLink(input),
|
|
739
908
|
fetchContent: async input => {
|
|
740
909
|
const result = await coordinator.fetchShareContent(input.shareId, {
|
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
ShareLinkOperationKeyInputValidator,
|
|
12
12
|
ShareOwnerRecoveryValidator,
|
|
13
13
|
UpdateShareLinkInputValidator,
|
|
14
|
+
PutShareLinkAttachmentChunkInputValidator,
|
|
15
|
+
PutShareLinkAttachmentChunkOutputValidator,
|
|
16
|
+
DeleteShareLinkAttachmentChunksInputValidator,
|
|
17
|
+
DeleteShareLinkAttachmentChunksOutputValidator,
|
|
14
18
|
} from '@learncard/types';
|
|
15
19
|
|
|
16
20
|
import { t, profileRouteWithoutInputCapture } from '@routes';
|
|
@@ -37,6 +41,7 @@ import type {
|
|
|
37
41
|
ShareLinkCoordinatorErrorCode,
|
|
38
42
|
} from '@helpers/share-link-coordinator';
|
|
39
43
|
import { decodeShareLinkListCursor } from '@accesslayer/share-link/list';
|
|
44
|
+
import type { ShareLinkAttachmentOwnerApi } from '@helpers/share-link-attachment';
|
|
40
45
|
import type {
|
|
41
46
|
ListShareLinksInput,
|
|
42
47
|
ListShareLinksResult,
|
|
@@ -57,6 +62,8 @@ export type ShareLinkRouterDependencies = {
|
|
|
57
62
|
readonly namespace: string;
|
|
58
63
|
readonly coordinator: ShareLinkCoordinator;
|
|
59
64
|
readonly recovery: RecoveryRunnerDependencies;
|
|
65
|
+
readonly attachments?: ShareLinkAttachmentOwnerApi;
|
|
66
|
+
readonly enforceAttachmentWriteRateLimit?: (ownerProfileId: string) => Promise<void>;
|
|
60
67
|
/**
|
|
61
68
|
* Bounded, namespace+owner-scoped keyset listing over the immutable
|
|
62
69
|
* `(createdAt, id)` key. Receives only trusted scope; the route decodes and
|
|
@@ -206,6 +213,56 @@ export const createShareLinksRouter = (
|
|
|
206
213
|
};
|
|
207
214
|
|
|
208
215
|
return t.router({
|
|
216
|
+
putAttachmentChunk: profileRouteWithoutInputCapture
|
|
217
|
+
.meta({
|
|
218
|
+
openapi: openapi(
|
|
219
|
+
'POST',
|
|
220
|
+
'/share-links/attachment-chunk/put',
|
|
221
|
+
'Stage encrypted share attachment chunk'
|
|
222
|
+
),
|
|
223
|
+
requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
|
|
224
|
+
})
|
|
225
|
+
.input(PutShareLinkAttachmentChunkInputValidator)
|
|
226
|
+
.output(PutShareLinkAttachmentChunkOutputValidator)
|
|
227
|
+
.mutation(async ({ ctx, input }) => {
|
|
228
|
+
const dependencies = await resolve();
|
|
229
|
+
if (!dependencies.attachments) notFound();
|
|
230
|
+
await (
|
|
231
|
+
dependencies.enforceAttachmentWriteRateLimit ??
|
|
232
|
+
dependencies.enforceOwnerWriteRateLimit
|
|
233
|
+
)(ctx.user.profile.profileId);
|
|
234
|
+
return runCoordinated(() =>
|
|
235
|
+
dependencies.attachments!.put(input, {
|
|
236
|
+
namespace: dependencies.namespace,
|
|
237
|
+
ownerProfileId: ctx.user.profile.profileId,
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
}),
|
|
241
|
+
deleteAttachmentChunks: profileRouteWithoutInputCapture
|
|
242
|
+
.meta({
|
|
243
|
+
openapi: openapi(
|
|
244
|
+
'POST',
|
|
245
|
+
'/share-links/attachment-chunks/delete',
|
|
246
|
+
'Delete unreferenced encrypted attachment'
|
|
247
|
+
),
|
|
248
|
+
requiredScope: AUTH_GRANT_SHARE_LINKS_WRITE_SCOPE,
|
|
249
|
+
})
|
|
250
|
+
.input(DeleteShareLinkAttachmentChunksInputValidator)
|
|
251
|
+
.output(DeleteShareLinkAttachmentChunksOutputValidator)
|
|
252
|
+
.mutation(async ({ ctx, input }) => {
|
|
253
|
+
const dependencies = await resolve();
|
|
254
|
+
if (!dependencies.attachments) notFound();
|
|
255
|
+
await (
|
|
256
|
+
dependencies.enforceAttachmentWriteRateLimit ??
|
|
257
|
+
dependencies.enforceOwnerWriteRateLimit
|
|
258
|
+
)(ctx.user.profile.profileId);
|
|
259
|
+
return runCoordinated(() =>
|
|
260
|
+
dependencies.attachments!.delete(input, {
|
|
261
|
+
namespace: dependencies.namespace,
|
|
262
|
+
ownerProfileId: ctx.user.profile.profileId,
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
}),
|
|
209
266
|
create: profileRouteWithoutInputCapture
|
|
210
267
|
.meta({
|
|
211
268
|
openapi: openapi('POST', '/share-links/create', 'Create an owner share link'),
|
|
@@ -629,6 +686,18 @@ const buildProductionDependencies = async (
|
|
|
629
686
|
namespace: config.namespace,
|
|
630
687
|
coordinator,
|
|
631
688
|
recovery,
|
|
689
|
+
attachments: (
|
|
690
|
+
await import('@helpers/share-link-attachment')
|
|
691
|
+
).createShareLinkAttachmentOwnerApi(client),
|
|
692
|
+
enforceAttachmentWriteRateLimit: ownerProfileId =>
|
|
693
|
+
enforceRateLimits([
|
|
694
|
+
{
|
|
695
|
+
key: `share-link-attachment-write:${config.namespace}:${ownerProfileId}`,
|
|
696
|
+
limit: 120,
|
|
697
|
+
windowSeconds: 60 * 60,
|
|
698
|
+
description: 'owner share-link attachment writes',
|
|
699
|
+
},
|
|
700
|
+
]),
|
|
632
701
|
listShareLinks: listRepository.listShareLinks,
|
|
633
702
|
enforceOwnerWriteRateLimit: ownerProfileId =>
|
|
634
703
|
enforceOwnerShareWriteRateLimit(config.namespace, ownerProfileId),
|
package/src/routes/workflows.ts
CHANGED
|
@@ -409,6 +409,7 @@ async function handlePresentationForClaim(
|
|
|
409
409
|
// Inject OBv3 skill alignments based on boost's framework/skills
|
|
410
410
|
await injectObv3AlignmentsIntoCredentialForBoost(boostCredential, boost, domain);
|
|
411
411
|
|
|
412
|
+
// encrypt=false on purpose: interoperable claim wallets may not decrypt LearnCard JWEs. See LC-2201.
|
|
412
413
|
const vc = (
|
|
413
414
|
await issueCredentialWithSigningAuthority(
|
|
414
415
|
{ type: 'profile', profile: saOwnerProfile },
|
|
@@ -679,14 +680,14 @@ async function handleInboxClaimPresentation(
|
|
|
679
680
|
// Set issuer from signing authority
|
|
680
681
|
unsignedCredential.issuer = signingAuthorityForUser.relationship.did;
|
|
681
682
|
|
|
682
|
-
//
|
|
683
|
+
// encrypt=false on purpose: interoperable inbox claim wallets may not decrypt LearnCard JWEs. See LC-2201.
|
|
683
684
|
finalCredential = (
|
|
684
685
|
await issueCredentialWithSigningAuthority(
|
|
685
686
|
{ type: 'profile', profile: issuerProfile },
|
|
686
687
|
unsignedCredential,
|
|
687
688
|
signingAuthorityForUser,
|
|
688
689
|
ctx.domain,
|
|
689
|
-
false
|
|
690
|
+
false
|
|
690
691
|
)
|
|
691
692
|
).credential as VC;
|
|
692
693
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AppManifestValidator,
|
|
5
|
+
AppManifestVersionValidator,
|
|
6
|
+
AppManifestDiffValidator,
|
|
7
|
+
} from '@learncard/types';
|
|
8
|
+
|
|
9
|
+
export const AppManifestTypeValidator = AppManifestValidator;
|
|
10
|
+
export type AppManifestType = z.infer<typeof AppManifestTypeValidator>;
|
|
11
|
+
|
|
12
|
+
export const AppManifestVersionTypeValidator = AppManifestVersionValidator;
|
|
13
|
+
export type AppManifestVersionType = z.infer<typeof AppManifestVersionTypeValidator>;
|
|
14
|
+
|
|
15
|
+
export const FlatAppManifestVersionValidator = z.object({
|
|
16
|
+
id: z.string(),
|
|
17
|
+
version: z.number().int().min(1),
|
|
18
|
+
manifestHash: z.string(),
|
|
19
|
+
manifestJson: z.string(),
|
|
20
|
+
status: z.enum(['draft', 'active', 'superseded']),
|
|
21
|
+
createdAt: z.string(),
|
|
22
|
+
activatedAt: z.string().optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export type FlatAppManifestVersionType = z.infer<typeof FlatAppManifestVersionValidator>;
|
|
26
|
+
|
|
27
|
+
export const AppManifestDiffTypeValidator = AppManifestDiffValidator;
|
|
28
|
+
export type AppManifestDiffType = z.infer<typeof AppManifestDiffTypeValidator>;
|
|
@@ -19,6 +19,16 @@ export type PromotionLevelEnum = z.infer<typeof PromotionLevel>;
|
|
|
19
19
|
export const AgeRating = z.enum(['4+', '9+', '12+', '17+']);
|
|
20
20
|
export type AgeRatingEnum = z.infer<typeof AgeRating>;
|
|
21
21
|
|
|
22
|
+
export const PendingListingUpdateStatus = z.enum(['DRAFT', 'PENDING_REVIEW']);
|
|
23
|
+
export type PendingListingUpdateStatusEnum = z.infer<typeof PendingListingUpdateStatus>;
|
|
24
|
+
|
|
25
|
+
/** Changes to a LISTED app held back until review, stored as listing storage fields. */
|
|
26
|
+
export const StoredPendingListingUpdateValidator = z.object({
|
|
27
|
+
changes: z.record(z.string(), z.unknown()).default({}),
|
|
28
|
+
manifestVersion: z.number().int().min(1).optional(),
|
|
29
|
+
});
|
|
30
|
+
export type StoredPendingListingUpdate = z.infer<typeof StoredPendingListingUpdateValidator>;
|
|
31
|
+
|
|
22
32
|
export const AppStoreListingValidator = z.object({
|
|
23
33
|
listing_id: z.string(),
|
|
24
34
|
slug: z.string().optional(),
|
|
@@ -43,6 +53,9 @@ export const AppStoreListingValidator = z.object({
|
|
|
43
53
|
age_rating: AgeRating.optional(),
|
|
44
54
|
submitted_at: z.string().optional(),
|
|
45
55
|
contact_email: z.string().email().optional(),
|
|
56
|
+
pending_update_json: z.string().optional(),
|
|
57
|
+
pending_update_status: PendingListingUpdateStatus.optional(),
|
|
58
|
+
pending_update_submitted_at: z.string().optional(),
|
|
46
59
|
});
|
|
47
60
|
export type AppStoreListingType = z.infer<typeof AppStoreListingValidator>;
|
|
48
61
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
ConsentFlowTermsStatusValidator,
|
|
5
5
|
ConsentFlowTransactionActionValidator,
|
|
6
6
|
ConsentFlowGuardianApprovalValidator,
|
|
7
|
+
ConsentFlowReferralValidator,
|
|
7
8
|
} from '@learncard/types';
|
|
8
9
|
import { z } from 'zod';
|
|
9
10
|
|
|
@@ -15,6 +16,10 @@ type FlatGuardianApproval = {
|
|
|
15
16
|
]?: string;
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
type FlatReferral = {
|
|
20
|
+
[Key in keyof z.infer<typeof ConsentFlowReferralValidator> as `referral.${Key}`]?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
18
23
|
type FlatConsentTerms = Partial<Record<`terms.${string}`, string | boolean | string[]>>;
|
|
19
24
|
|
|
20
25
|
export const DbContractValidator = z.object({
|
|
@@ -31,6 +36,8 @@ export const DbContractValidator = z.object({
|
|
|
31
36
|
createdAt: z.string(),
|
|
32
37
|
updatedAt: z.string(),
|
|
33
38
|
expiresAt: z.string().optional(),
|
|
39
|
+
audienceVersion: z.coerce.number().int().nonnegative().optional(),
|
|
40
|
+
hasConsented: z.boolean().optional(),
|
|
34
41
|
});
|
|
35
42
|
|
|
36
43
|
export type DbContractType = z.infer<typeof DbContractValidator>;
|
|
@@ -41,22 +48,35 @@ export const DbTransactionValidator = z.object({
|
|
|
41
48
|
action: ConsentFlowTransactionActionValidator,
|
|
42
49
|
terms: ConsentFlowTermsValidator,
|
|
43
50
|
guardianApproval: ConsentFlowGuardianApprovalValidator.optional(),
|
|
51
|
+
referral: ConsentFlowReferralValidator.optional(),
|
|
44
52
|
date: z.string(),
|
|
45
53
|
expiresAt: z.string().optional(),
|
|
46
54
|
oneTime: z.boolean().optional(),
|
|
47
55
|
});
|
|
48
56
|
|
|
49
57
|
export type DbTransactionType = z.infer<typeof DbTransactionValidator>;
|
|
50
|
-
export type FlatDbTransactionType = Omit<
|
|
58
|
+
export type FlatDbTransactionType = Omit<
|
|
59
|
+
DbTransactionType,
|
|
60
|
+
'guardianApproval' | 'terms' | 'referral'
|
|
61
|
+
> &
|
|
51
62
|
FlatConsentTerms &
|
|
52
|
-
FlatGuardianApproval
|
|
63
|
+
FlatGuardianApproval &
|
|
64
|
+
FlatReferral;
|
|
53
65
|
|
|
54
66
|
export const DbTermsValidator = z.object({
|
|
67
|
+
smartResumeFingerprint: z.string().optional(),
|
|
68
|
+
smartResumePublicationStatus: z.enum(['pending', 'sending', 'failed', 'succeeded']).optional(),
|
|
69
|
+
smartResumeLeaseId: z.string().optional(),
|
|
70
|
+
smartResumeLeaseUntil: z.coerce.number().optional(),
|
|
71
|
+
smartResumeMutationVersion: z.coerce.number().optional(),
|
|
72
|
+
smartResumeRedirectUrl: z.string().optional(),
|
|
73
|
+
mutationVersion: z.coerce.number().int().nonnegative().optional(),
|
|
55
74
|
id: z.string(),
|
|
56
75
|
status: ConsentFlowTermsStatusValidator,
|
|
57
76
|
// Neo4j has no properties to store for entirely empty permission objects.
|
|
58
77
|
terms: ConsentFlowTermsValidator.prefault({}),
|
|
59
78
|
guardianApproval: ConsentFlowGuardianApprovalValidator.optional(),
|
|
79
|
+
referral: ConsentFlowReferralValidator.optional(),
|
|
60
80
|
createdAt: z.string().optional(),
|
|
61
81
|
updatedAt: z.string().optional(),
|
|
62
82
|
expiresAt: z.string().optional(),
|
|
@@ -64,6 +84,7 @@ export const DbTermsValidator = z.object({
|
|
|
64
84
|
});
|
|
65
85
|
|
|
66
86
|
export type DbTermsType = z.infer<typeof DbTermsValidator>;
|
|
67
|
-
export type FlatDbTermsType = Omit<DbTermsType, 'guardianApproval' | 'terms'> &
|
|
87
|
+
export type FlatDbTermsType = Omit<DbTermsType, 'guardianApproval' | 'terms' | 'referral'> &
|
|
68
88
|
FlatConsentTerms &
|
|
69
|
-
FlatGuardianApproval
|
|
89
|
+
FlatGuardianApproval &
|
|
90
|
+
FlatReferral;
|