@learncard/network-brain-service 3.19.1 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +44 -111
- package/AGENTS.md +52 -0
- package/CHANGELOG.md +120 -0
- package/README.md +9 -2
- package/bootstrapLambda.test.ts +72 -0
- package/bundle.mjs +9 -1
- 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/publish-metafile.json +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 +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 -19
- package/project.json +8 -1
- 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 +29 -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/lambda.ts
CHANGED
|
@@ -1,203 +1,57 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
Context,
|
|
4
|
-
APIGatewayProxyResultV2,
|
|
5
|
-
APIGatewayProxyEventV2,
|
|
6
|
-
SQSBatchResponse,
|
|
7
|
-
SQSHandler,
|
|
8
|
-
} from 'aws-lambda';
|
|
9
|
-
import { awsLambdaRequestHandler } from '@trpc/server/adapters/aws-lambda';
|
|
10
|
-
import * as Sentry from '@sentry/serverless';
|
|
11
|
-
|
|
12
|
-
import app from './src/openapi';
|
|
13
|
-
import skillsViewerApp from './src/skills-viewer';
|
|
14
|
-
import statusListsApp from './src/status-lists';
|
|
15
|
-
import credentialRefreshApp from './src/credential-refresh';
|
|
16
|
-
import { appRouter, createContext } from './src/app';
|
|
17
|
-
import { publicShareLinkCacheControlHeaders } from './src/routes';
|
|
18
|
-
import { deliverQueuedNotification } from './src/helpers/notificationQueue.helpers';
|
|
19
|
-
import { startSkillEmbeddingBackfill } from './src/helpers/skill-embedding.helpers';
|
|
20
|
-
import { createOpenApiAwsLambdaHandler } from './src/helpers/shim';
|
|
21
|
-
import {
|
|
22
|
-
handleTrpcError,
|
|
23
|
-
sentryBeforeSend,
|
|
24
|
-
getTracesSampleRate,
|
|
25
|
-
} from './src/helpers/sentry.helpers';
|
|
26
|
-
import { environment } from './src/config/environment';
|
|
27
|
-
import { toServerlessApplication } from './src/helpers/serverlessApplication';
|
|
28
|
-
import { runInboxMaintenance } from './src/helpers/inbox-maintenance.helpers';
|
|
29
|
-
import {
|
|
30
|
-
inboxBatchResponseMeta,
|
|
31
|
-
withInboxBatchBodyLimit,
|
|
32
|
-
} from './src/helpers/inbox-batch-http.helpers';
|
|
33
|
-
|
|
34
|
-
Sentry.AWSLambda.init({
|
|
35
|
-
dsn: environment.SENTRY_DSN,
|
|
36
|
-
environment: environment.SENTRY_ENV,
|
|
37
|
-
enabled: Boolean(environment.SENTRY_DSN),
|
|
38
|
-
tracesSampleRate: getTracesSampleRate(),
|
|
39
|
-
beforeSend: sentryBeforeSend,
|
|
40
|
-
integrations: [
|
|
41
|
-
new Sentry.Integrations.Console(),
|
|
42
|
-
new Sentry.Integrations.Http({ tracing: true }),
|
|
43
|
-
new Sentry.Integrations.ContextLines(),
|
|
44
|
-
],
|
|
45
|
-
});
|
|
1
|
+
import { bootstrapLambda } from '@learncard/service-config';
|
|
46
2
|
|
|
47
|
-
|
|
48
|
-
console.error('Skill embedding backfill startup error:', err)
|
|
49
|
-
);
|
|
3
|
+
import { base, stages } from './src/config/stageConfig';
|
|
50
4
|
|
|
51
|
-
|
|
52
|
-
basePath: '/docs',
|
|
53
|
-
});
|
|
5
|
+
type LambdaApp = typeof import('./lambdaApp');
|
|
54
6
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
export const credentialRefreshHandler: typeof credentialRefreshProxy = async (event, context) => {
|
|
63
|
-
await credentialRefreshApp.ready();
|
|
64
|
-
return credentialRefreshProxy(event, context);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export const _openApiHandler = createOpenApiAwsLambdaHandler({
|
|
68
|
-
router: appRouter,
|
|
69
|
-
responseMeta: meta => {
|
|
70
|
-
return {
|
|
71
|
-
...inboxBatchResponseMeta(meta),
|
|
72
|
-
headers: {
|
|
73
|
-
'Access-Control-Allow-Origin': '*',
|
|
74
|
-
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
75
|
-
'Access-Control-Allow-Headers': 'Authorization, Content-Type',
|
|
76
|
-
...publicShareLinkCacheControlHeaders(meta.paths),
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
},
|
|
80
|
-
createContext,
|
|
81
|
-
onError: handleTrpcError,
|
|
7
|
+
const getApplication = bootstrapLambda<LambdaApp>({
|
|
8
|
+
base,
|
|
9
|
+
stages,
|
|
10
|
+
stage: process.env.AWS_LAMBDA_FUNCTION_NAME
|
|
11
|
+
? process.env.LAMBDA_STAGE
|
|
12
|
+
: process.env.CONFIG_STAGE,
|
|
13
|
+
importApp: () => import('./lambdaApp'),
|
|
82
14
|
});
|
|
83
15
|
|
|
84
|
-
export const
|
|
85
|
-
|
|
86
|
-
allowMethodOverride: true,
|
|
87
|
-
router: appRouter,
|
|
88
|
-
createContext,
|
|
89
|
-
onError: handleTrpcError,
|
|
90
|
-
responseMeta: ({ paths }) => {
|
|
91
|
-
return {
|
|
92
|
-
headers: {
|
|
93
|
-
'Access-Control-Allow-Origin': '*',
|
|
94
|
-
'Access-Control-Allow-Methods': '*',
|
|
95
|
-
'Access-Control-Allow-Headers': 'authorization',
|
|
96
|
-
...publicShareLinkCacheControlHeaders(paths),
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
},
|
|
100
|
-
}),
|
|
101
|
-
'trpc'
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
export const openApiHandler = Sentry.AWSLambda.wrapHandler(
|
|
105
|
-
async (event: APIGatewayProxyEventV2, context: Context): Promise<APIGatewayProxyResultV2> => {
|
|
106
|
-
if (event.requestContext.http.method === 'OPTIONS') {
|
|
107
|
-
return {
|
|
108
|
-
statusCode: 204,
|
|
109
|
-
headers: {
|
|
110
|
-
'Access-Control-Allow-Origin': '*',
|
|
111
|
-
'Access-Control-Allow-Headers': 'Authorization, Content-Type',
|
|
112
|
-
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return _openApiHandler(event, context);
|
|
118
|
-
}
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
export const trpcHandler = Sentry.AWSLambda.wrapHandler(
|
|
122
|
-
async (event: APIGatewayProxyEventV2, context: Context): Promise<APIGatewayProxyResultV2> => {
|
|
123
|
-
if (event.requestContext.http.method === 'OPTIONS') {
|
|
124
|
-
return {
|
|
125
|
-
statusCode: 200,
|
|
126
|
-
headers: {
|
|
127
|
-
'Access-Control-Allow-Origin': '*',
|
|
128
|
-
'Access-Control-Allow-Headers': '*',
|
|
129
|
-
'Access-Control-Allow-Methods': '*',
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return _trpcHandler(event, context);
|
|
135
|
-
}
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
export const inboxQueueWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
139
|
-
const { processInboxQueueMessage } = await import('./src/helpers/inbox-queue.helpers');
|
|
140
|
-
const batchItemFailures = [];
|
|
141
|
-
for (const record of event.Records) {
|
|
142
|
-
try {
|
|
143
|
-
await processInboxQueueMessage(record.body);
|
|
144
|
-
} catch {
|
|
145
|
-
console.error('Inbox worker message failed', { messageId: record.messageId });
|
|
146
|
-
batchItemFailures.push({ itemIdentifier: record.messageId });
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return { batchItemFailures } satisfies SQSBatchResponse;
|
|
150
|
-
});
|
|
16
|
+
export const trpcHandler = async (...args: Parameters<LambdaApp['trpcHandler']>) =>
|
|
17
|
+
(await getApplication()).trpcHandler(...args);
|
|
151
18
|
|
|
152
|
-
export const
|
|
153
|
-
|
|
154
|
-
const { dispatchInboxJobs } = await import('./src/helpers/inbox-queue.helpers');
|
|
155
|
-
// Share one clock across publication and recovery, retaining five seconds for shutdown.
|
|
156
|
-
const deadline = Date.now() + Math.max(0, context.getRemainingTimeInMillis() - 5_000);
|
|
157
|
-
await dispatchInboxJobs(deadline);
|
|
158
|
-
}
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
export const inboxDeadLetterWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
162
|
-
const { processInboxDeadLetter } = await import('./src/helpers/inbox-queue.helpers');
|
|
163
|
-
const batchItemFailures = [];
|
|
164
|
-
for (const record of event.Records) {
|
|
165
|
-
try {
|
|
166
|
-
await processInboxDeadLetter(record.body);
|
|
167
|
-
} catch {
|
|
168
|
-
console.error('Inbox dead-letter processing failed', { messageId: record.messageId });
|
|
169
|
-
batchItemFailures.push({ itemIdentifier: record.messageId });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return { batchItemFailures } satisfies SQSBatchResponse;
|
|
173
|
-
});
|
|
19
|
+
export const openApiHandler = async (...args: Parameters<LambdaApp['openApiHandler']>) =>
|
|
20
|
+
(await getApplication()).openApiHandler(...args);
|
|
174
21
|
|
|
175
|
-
export const
|
|
176
|
-
|
|
177
|
-
event.Records.map(async record => {
|
|
178
|
-
try {
|
|
179
|
-
await deliverQueuedNotification(record.body);
|
|
180
|
-
|
|
181
|
-
return undefined;
|
|
182
|
-
} catch (error) {
|
|
183
|
-
console.error('Notification queue record failed', {
|
|
184
|
-
messageId: record.messageId,
|
|
185
|
-
error,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
return { itemIdentifier: record.messageId };
|
|
189
|
-
}
|
|
190
|
-
})
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
batchItemFailures: batchItemFailures.filter(
|
|
195
|
-
(failure): failure is { itemIdentifier: string } => failure !== undefined
|
|
196
|
-
),
|
|
197
|
-
} satisfies SQSBatchResponse;
|
|
198
|
-
});
|
|
22
|
+
export const _trpcHandler = async (...args: Parameters<LambdaApp['_trpcHandler']>) =>
|
|
23
|
+
(await getApplication())._trpcHandler(...args);
|
|
199
24
|
|
|
200
|
-
export const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
25
|
+
export const _openApiHandler = async (...args: Parameters<LambdaApp['_openApiHandler']>) =>
|
|
26
|
+
(await getApplication())._openApiHandler(...args);
|
|
27
|
+
|
|
28
|
+
export const swaggerUiHandler = async (...args: Parameters<LambdaApp['swaggerUiHandler']>) =>
|
|
29
|
+
(await getApplication()).swaggerUiHandler(...args);
|
|
30
|
+
|
|
31
|
+
export const skillsViewerHandler = async (...args: Parameters<LambdaApp['skillsViewerHandler']>) =>
|
|
32
|
+
(await getApplication()).skillsViewerHandler(...args);
|
|
33
|
+
|
|
34
|
+
export const statusListsHandler = async (...args: Parameters<LambdaApp['statusListsHandler']>) =>
|
|
35
|
+
(await getApplication()).statusListsHandler(...args);
|
|
36
|
+
|
|
37
|
+
export const credentialRefreshHandler = async (
|
|
38
|
+
...args: Parameters<LambdaApp['credentialRefreshHandler']>
|
|
39
|
+
) => (await getApplication()).credentialRefreshHandler(...args);
|
|
40
|
+
|
|
41
|
+
export const inboxQueueWorker = async (...args: Parameters<LambdaApp['inboxQueueWorker']>) =>
|
|
42
|
+
(await getApplication()).inboxQueueWorker(...args);
|
|
43
|
+
|
|
44
|
+
export const inboxQueueDispatcher = async (
|
|
45
|
+
...args: Parameters<LambdaApp['inboxQueueDispatcher']>
|
|
46
|
+
) => (await getApplication()).inboxQueueDispatcher(...args);
|
|
47
|
+
|
|
48
|
+
export const inboxDeadLetterWorker = async (
|
|
49
|
+
...args: Parameters<LambdaApp['inboxDeadLetterWorker']>
|
|
50
|
+
) => (await getApplication()).inboxDeadLetterWorker(...args);
|
|
51
|
+
|
|
52
|
+
export const notificationsWorker = async (...args: Parameters<LambdaApp['notificationsWorker']>) =>
|
|
53
|
+
(await getApplication()).notificationsWorker(...args);
|
|
54
|
+
|
|
55
|
+
export const inboxMaintenanceHandler = async (
|
|
56
|
+
...args: Parameters<LambdaApp['inboxMaintenanceHandler']>
|
|
57
|
+
) => (await getApplication()).inboxMaintenanceHandler(...args);
|
package/lambdaApp.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import serverlessHttp from 'serverless-http';
|
|
2
|
+
import type {
|
|
3
|
+
Context,
|
|
4
|
+
APIGatewayProxyResultV2,
|
|
5
|
+
APIGatewayProxyEventV2,
|
|
6
|
+
SQSBatchResponse,
|
|
7
|
+
SQSHandler,
|
|
8
|
+
} from 'aws-lambda';
|
|
9
|
+
import { awsLambdaRequestHandler } from '@trpc/server/adapters/aws-lambda';
|
|
10
|
+
import * as Sentry from '@sentry/serverless';
|
|
11
|
+
|
|
12
|
+
import app from './src/openapi';
|
|
13
|
+
import skillsViewerApp from './src/skills-viewer';
|
|
14
|
+
import statusListsApp from './src/status-lists';
|
|
15
|
+
import credentialRefreshApp from './src/credential-refresh';
|
|
16
|
+
import { appRouter, createContext } from './src/app';
|
|
17
|
+
import { publicShareLinkCacheControlHeaders } from './src/routes';
|
|
18
|
+
import { deliverQueuedNotification } from './src/helpers/notificationQueue.helpers';
|
|
19
|
+
import { startSkillEmbeddingBackfill } from './src/helpers/skill-embedding.helpers';
|
|
20
|
+
import { createOpenApiAwsLambdaHandler } from './src/helpers/shim';
|
|
21
|
+
import {
|
|
22
|
+
handleTrpcError,
|
|
23
|
+
sentryBeforeSend,
|
|
24
|
+
getTracesSampleRate,
|
|
25
|
+
} from './src/helpers/sentry.helpers';
|
|
26
|
+
import { environment } from './src/config/environment';
|
|
27
|
+
import { toServerlessApplication } from './src/helpers/serverlessApplication';
|
|
28
|
+
import { runInboxMaintenance } from './src/helpers/inbox-maintenance.helpers';
|
|
29
|
+
import {
|
|
30
|
+
inboxBatchResponseMeta,
|
|
31
|
+
withInboxBatchBodyLimit,
|
|
32
|
+
} from './src/helpers/inbox-batch-http.helpers';
|
|
33
|
+
|
|
34
|
+
Sentry.AWSLambda.init({
|
|
35
|
+
dsn: environment.SENTRY_DSN,
|
|
36
|
+
environment: environment.SENTRY_ENV,
|
|
37
|
+
enabled: Boolean(environment.SENTRY_DSN),
|
|
38
|
+
tracesSampleRate: getTracesSampleRate(),
|
|
39
|
+
beforeSend: sentryBeforeSend,
|
|
40
|
+
integrations: [
|
|
41
|
+
new Sentry.Integrations.Console(),
|
|
42
|
+
new Sentry.Integrations.Http({ tracing: true }),
|
|
43
|
+
new Sentry.Integrations.ContextLines(),
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
startSkillEmbeddingBackfill().catch(err =>
|
|
48
|
+
console.error('Skill embedding backfill startup error:', err)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
export const swaggerUiHandler = serverlessHttp(toServerlessApplication(app), {
|
|
52
|
+
basePath: '/docs',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const skillsViewerHandler = serverlessHttp(toServerlessApplication(skillsViewerApp));
|
|
56
|
+
|
|
57
|
+
export const statusListsHandler = serverlessHttp(toServerlessApplication(statusListsApp));
|
|
58
|
+
|
|
59
|
+
// Passing the Fastify instance selects serverless-http's inject adapter, which
|
|
60
|
+
// drops the API Gateway source address. The HTTP server path preserves it.
|
|
61
|
+
const credentialRefreshProxy = serverlessHttp(toServerlessApplication(credentialRefreshApp.server));
|
|
62
|
+
export const credentialRefreshHandler: typeof credentialRefreshProxy = async (event, context) => {
|
|
63
|
+
await credentialRefreshApp.ready();
|
|
64
|
+
return credentialRefreshProxy(event, context);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const _openApiHandler = createOpenApiAwsLambdaHandler({
|
|
68
|
+
router: appRouter,
|
|
69
|
+
responseMeta: meta => {
|
|
70
|
+
return {
|
|
71
|
+
...inboxBatchResponseMeta(meta),
|
|
72
|
+
headers: {
|
|
73
|
+
'Access-Control-Allow-Origin': '*',
|
|
74
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
75
|
+
'Access-Control-Allow-Headers': 'Authorization, Content-Type',
|
|
76
|
+
...publicShareLinkCacheControlHeaders(meta.paths),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
createContext,
|
|
81
|
+
onError: handleTrpcError,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export const _trpcHandler = withInboxBatchBodyLimit(
|
|
85
|
+
awsLambdaRequestHandler({
|
|
86
|
+
allowMethodOverride: true,
|
|
87
|
+
router: appRouter,
|
|
88
|
+
createContext,
|
|
89
|
+
onError: handleTrpcError,
|
|
90
|
+
responseMeta: ({ paths }) => {
|
|
91
|
+
return {
|
|
92
|
+
headers: {
|
|
93
|
+
'Access-Control-Allow-Origin': '*',
|
|
94
|
+
'Access-Control-Allow-Methods': '*',
|
|
95
|
+
'Access-Control-Allow-Headers': 'authorization',
|
|
96
|
+
...publicShareLinkCacheControlHeaders(paths),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
'trpc'
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
export const openApiHandler = Sentry.AWSLambda.wrapHandler(
|
|
105
|
+
async (event: APIGatewayProxyEventV2, context: Context): Promise<APIGatewayProxyResultV2> => {
|
|
106
|
+
if (event.requestContext.http.method === 'OPTIONS') {
|
|
107
|
+
return {
|
|
108
|
+
statusCode: 204,
|
|
109
|
+
headers: {
|
|
110
|
+
'Access-Control-Allow-Origin': '*',
|
|
111
|
+
'Access-Control-Allow-Headers': 'Authorization, Content-Type',
|
|
112
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return _openApiHandler(event, context);
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
export const trpcHandler = Sentry.AWSLambda.wrapHandler(
|
|
122
|
+
async (event: APIGatewayProxyEventV2, context: Context): Promise<APIGatewayProxyResultV2> => {
|
|
123
|
+
if (event.requestContext.http.method === 'OPTIONS') {
|
|
124
|
+
return {
|
|
125
|
+
statusCode: 200,
|
|
126
|
+
headers: {
|
|
127
|
+
'Access-Control-Allow-Origin': '*',
|
|
128
|
+
'Access-Control-Allow-Headers': '*',
|
|
129
|
+
'Access-Control-Allow-Methods': '*',
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return _trpcHandler(event, context);
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
export const inboxQueueWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
139
|
+
const { processInboxQueueMessage } = await import('./src/helpers/inbox-queue.helpers');
|
|
140
|
+
const batchItemFailures = [];
|
|
141
|
+
for (const record of event.Records) {
|
|
142
|
+
try {
|
|
143
|
+
await processInboxQueueMessage(record.body);
|
|
144
|
+
} catch {
|
|
145
|
+
console.error('Inbox worker message failed', { messageId: record.messageId });
|
|
146
|
+
batchItemFailures.push({ itemIdentifier: record.messageId });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { batchItemFailures } satisfies SQSBatchResponse;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
export const inboxQueueDispatcher = Sentry.AWSLambda.wrapHandler(
|
|
153
|
+
async (_event: unknown, context: Context): Promise<void> => {
|
|
154
|
+
const { dispatchInboxJobs } = await import('./src/helpers/inbox-queue.helpers');
|
|
155
|
+
// Share one clock across publication and recovery, retaining five seconds for shutdown.
|
|
156
|
+
const deadline = Date.now() + Math.max(0, context.getRemainingTimeInMillis() - 5_000);
|
|
157
|
+
await dispatchInboxJobs(deadline);
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
export const inboxDeadLetterWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
162
|
+
const { processInboxDeadLetter } = await import('./src/helpers/inbox-queue.helpers');
|
|
163
|
+
const batchItemFailures = [];
|
|
164
|
+
for (const record of event.Records) {
|
|
165
|
+
try {
|
|
166
|
+
await processInboxDeadLetter(record.body);
|
|
167
|
+
} catch {
|
|
168
|
+
console.error('Inbox dead-letter processing failed', { messageId: record.messageId });
|
|
169
|
+
batchItemFailures.push({ itemIdentifier: record.messageId });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { batchItemFailures } satisfies SQSBatchResponse;
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
export const notificationsWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
|
|
176
|
+
const batchItemFailures = await Promise.all(
|
|
177
|
+
event.Records.map(async record => {
|
|
178
|
+
try {
|
|
179
|
+
await deliverQueuedNotification(record.body);
|
|
180
|
+
|
|
181
|
+
return undefined;
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error('Notification queue record failed', {
|
|
184
|
+
messageId: record.messageId,
|
|
185
|
+
error,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return { itemIdentifier: record.messageId };
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
batchItemFailures: batchItemFailures.filter(
|
|
195
|
+
(failure): failure is { itemIdentifier: string } => failure !== undefined
|
|
196
|
+
),
|
|
197
|
+
} satisfies SQSBatchResponse;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
export const inboxMaintenanceHandler = Sentry.AWSLambda.wrapHandler(async (): Promise<void> => {
|
|
201
|
+
const counts = await runInboxMaintenance();
|
|
202
|
+
console.log('Universal Inbox maintenance completed', counts);
|
|
203
|
+
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/network-brain-service",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24.12 <25"
|
|
7
7
|
},
|
|
8
|
-
"packageManager": "bun@1.
|
|
8
|
+
"packageManager": "bun@1.4.2",
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
11
|
"scripts": {
|
|
@@ -59,25 +59,26 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@aws-sdk/client-sqs": "^3.379.1",
|
|
62
|
-
"@digitalcredentials/issuer-registry-client": "^
|
|
62
|
+
"@digitalcredentials/issuer-registry-client": "^4.1.0",
|
|
63
63
|
"@fastify/cors": "^11.2.0",
|
|
64
64
|
"@google/genai": "^1.27.0",
|
|
65
|
-
"@learncard/credential-library": "2.
|
|
66
|
-
"@learncard/core": "9.4.
|
|
67
|
-
"@learncard/crypto-plugin": "1.1.
|
|
68
|
-
"@learncard/did-web-plugin": "1.1.
|
|
69
|
-
"@learncard/didkey-plugin": "1.1.
|
|
70
|
-
"@learncard/didkit-plugin": "1.10.
|
|
71
|
-
"@learncard/didkit-plugin-node": "0.3.
|
|
72
|
-
"@learncard/dynamic-loader-plugin": "1.1.
|
|
73
|
-
"@learncard/encryption-plugin": "1.1.
|
|
74
|
-
"@learncard/expiration-plugin": "1.2.
|
|
75
|
-
"@learncard/email-templates": "1.2.
|
|
76
|
-
"@learncard/helpers": "1.6.
|
|
77
|
-
"@learncard/learn-card-plugin": "1.2.
|
|
78
|
-
"@learncard/
|
|
79
|
-
"@learncard/
|
|
80
|
-
"@learncard/vc-
|
|
65
|
+
"@learncard/credential-library": "2.1.1",
|
|
66
|
+
"@learncard/core": "9.4.38",
|
|
67
|
+
"@learncard/crypto-plugin": "1.1.38",
|
|
68
|
+
"@learncard/did-web-plugin": "1.1.38",
|
|
69
|
+
"@learncard/didkey-plugin": "1.1.38",
|
|
70
|
+
"@learncard/didkit-plugin": "1.10.3",
|
|
71
|
+
"@learncard/didkit-plugin-node": "0.3.3",
|
|
72
|
+
"@learncard/dynamic-loader-plugin": "1.1.38",
|
|
73
|
+
"@learncard/encryption-plugin": "1.1.38",
|
|
74
|
+
"@learncard/expiration-plugin": "1.2.38",
|
|
75
|
+
"@learncard/email-templates": "1.2.1",
|
|
76
|
+
"@learncard/helpers": "1.6.2",
|
|
77
|
+
"@learncard/learn-card-plugin": "1.2.38",
|
|
78
|
+
"@learncard/partner-connect-core": "0.2.0",
|
|
79
|
+
"@learncard/types": "5.23.0",
|
|
80
|
+
"@learncard/vc-plugin": "1.6.4",
|
|
81
|
+
"@learncard/vc-templates-plugin": "1.1.38",
|
|
81
82
|
"@sentry/serverless": "7.61.0",
|
|
82
83
|
"@trpc/server": "11.8.0",
|
|
83
84
|
"@types/lodash": "^4.14.191",
|
package/project.json
CHANGED
|
@@ -35,7 +35,14 @@
|
|
|
35
35
|
},
|
|
36
36
|
"build": {
|
|
37
37
|
"executor": "nx:run-script",
|
|
38
|
-
"inputs": [
|
|
38
|
+
"inputs": [
|
|
39
|
+
"source",
|
|
40
|
+
"{projectRoot}/bundle.mjs",
|
|
41
|
+
"{projectRoot}/esbuildPlugins.cjs",
|
|
42
|
+
"{projectRoot}/package.json",
|
|
43
|
+
"{projectRoot}/tsconfig*.json",
|
|
44
|
+
"{workspaceRoot}/packages/service-config/src/**/*"
|
|
45
|
+
],
|
|
39
46
|
"options": {
|
|
40
47
|
"script": "build"
|
|
41
48
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { configureOfflineSchemaEnvironment } from './offline-schema-environment';
|
|
3
|
+
|
|
4
|
+
const output = process.argv[2];
|
|
5
|
+
if (!output)
|
|
6
|
+
throw new Error('Usage: bun --conditions=development scripts/export-openapi.ts OUTPUT');
|
|
7
|
+
|
|
8
|
+
// Schema export registers routes, but never calls them or loads local credentials.
|
|
9
|
+
configureOfflineSchemaEnvironment();
|
|
10
|
+
|
|
11
|
+
// Static imports would initialize the router before service credentials are removed.
|
|
12
|
+
const { openApiDocument } = await import('../src/openapi');
|
|
13
|
+
|
|
14
|
+
await Bun.write(
|
|
15
|
+
resolve(output),
|
|
16
|
+
`${JSON.stringify(openApiDocument, null, 2)}
|
|
17
|
+
`
|
|
18
|
+
);
|
|
19
|
+
console.log(`Exported ${Object.keys(openApiDocument.paths ?? {}).length} local OpenAPI paths`);
|
|
20
|
+
process.exit(0);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// These tools only import validators/routes; they must never inherit service credentials.
|
|
2
|
+
export function configureOfflineSchemaEnvironment(): void {
|
|
3
|
+
for (const name of Object.keys(process.env)) {
|
|
4
|
+
if (!['PATH', 'HOME', 'TMPDIR'].includes(name)) delete process.env[name];
|
|
5
|
+
}
|
|
6
|
+
Object.assign(process.env, {
|
|
7
|
+
NODE_ENV: 'test',
|
|
8
|
+
DOTENV_CONFIG_PATH: '/dev/null',
|
|
9
|
+
SEED: 'a'.repeat(64),
|
|
10
|
+
NEO4J_URI: 'bolt://127.0.0.1:1',
|
|
11
|
+
NEO4J_USERNAME: 'schema-export',
|
|
12
|
+
NEO4J_PASSWORD: 'synthetic-schema-export',
|
|
13
|
+
NEO4J_SKIP_INDICES: 'true',
|
|
14
|
+
SKIP_DIDKIT_NAPI: 'true',
|
|
15
|
+
SKIP_SKILL_FRAMEWORK_SEED: 'true',
|
|
16
|
+
TRACE_CONSOLE: 'false',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import type { ZodType } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { configureOfflineSchemaEnvironment } from './offline-schema-environment';
|
|
5
|
+
|
|
6
|
+
type CapturedRequest = {
|
|
7
|
+
method: string;
|
|
8
|
+
path: string;
|
|
9
|
+
body: unknown;
|
|
10
|
+
serverValid: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const capture = process.argv[2];
|
|
14
|
+
if (!capture)
|
|
15
|
+
throw new Error(
|
|
16
|
+
'Usage: bun --conditions=development scripts/verify-python-contracts.ts CAPTURE'
|
|
17
|
+
);
|
|
18
|
+
configureOfflineSchemaEnvironment();
|
|
19
|
+
|
|
20
|
+
// Static imports would initialize router dependencies before credentials are removed.
|
|
21
|
+
const { AllocateCredentialRefreshInputValidator, PublishCredentialRefreshInputValidator } =
|
|
22
|
+
await import('@learncard/types');
|
|
23
|
+
const { activityRouter } = await import('../src/routes/activity');
|
|
24
|
+
|
|
25
|
+
const activityInputs: Record<string, ZodType> = {
|
|
26
|
+
'/activity/credentials': activityRouter._def.procedures.getMyActivities._def.inputs.at(
|
|
27
|
+
-1
|
|
28
|
+
) as ZodType,
|
|
29
|
+
'/activity/credentials/stats': activityRouter._def.procedures.getActivityStats._def.inputs.at(
|
|
30
|
+
-1
|
|
31
|
+
) as ZodType,
|
|
32
|
+
};
|
|
33
|
+
const records: CapturedRequest[] = await Bun.file(capture).json();
|
|
34
|
+
for (const record of records) {
|
|
35
|
+
const url = new URL(record.path, 'http://offline.invalid');
|
|
36
|
+
let valid: boolean;
|
|
37
|
+
if (url.pathname === '/credential-refresh/publish') {
|
|
38
|
+
valid = PublishCredentialRefreshInputValidator.safeParse(record.body).success;
|
|
39
|
+
} else if (url.pathname === '/credential-refresh/allocate') {
|
|
40
|
+
valid = AllocateCredentialRefreshInputValidator.safeParse(record.body).success;
|
|
41
|
+
} else {
|
|
42
|
+
const validator = activityInputs[url.pathname];
|
|
43
|
+
assert(validator, `No source validator for captured path ${url.pathname}`);
|
|
44
|
+
valid = validator.safeParse(Object.fromEntries(url.searchParams)).success;
|
|
45
|
+
}
|
|
46
|
+
assert.equal(
|
|
47
|
+
valid,
|
|
48
|
+
record.serverValid,
|
|
49
|
+
`Source contract rejected/accepted unexpected input: ${record.method} ${record.path}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The historical client format and offset suffixes must remain invalid server input.
|
|
54
|
+
for (const validator of Object.values(activityInputs)) {
|
|
55
|
+
for (const date of [
|
|
56
|
+
'2026-01-02T03:04:05.000000+0000',
|
|
57
|
+
'2026-01-02T03:04:05+00:00',
|
|
58
|
+
'2026-01-02T03:04:05',
|
|
59
|
+
]) {
|
|
60
|
+
for (const field of ['startDate', 'endDate']) {
|
|
61
|
+
assert.equal(
|
|
62
|
+
validator.safeParse({ [field]: date }).success,
|
|
63
|
+
false,
|
|
64
|
+
`Server unexpectedly accepts ${field}=${date}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
console.log(
|
|
70
|
+
`Verified ${records.length} captured publication/allocation/activity cases and Z-only boundaries against actual source validators`
|
|
71
|
+
);
|
|
72
|
+
process.exit(0);
|
package/serverless-local.yml
CHANGED
|
@@ -50,7 +50,9 @@ provider:
|
|
|
50
50
|
INBOX_QUEUE_URL: ${env:INBOX_QUEUE_URL, ''}
|
|
51
51
|
INBOX_QUEUE_ENDPOINT: ${env:INBOX_QUEUE_ENDPOINT, ''}
|
|
52
52
|
PORT: ${env:PORT, 3000}
|
|
53
|
-
|
|
53
|
+
CONFIG_STAGE: ${env:CONFIG_STAGE, ''}
|
|
54
|
+
# serverless-offline sets AWS_LAMBDA_FUNCTION_NAME, so bootstrap reads LAMBDA_STAGE.
|
|
55
|
+
LAMBDA_STAGE: ${env:CONFIG_STAGE, ''}
|
|
54
56
|
SEED: ${env:SEED}
|
|
55
57
|
NEO4J_URI: ${env:NEO4J_URI}
|
|
56
58
|
NEO4J_USERNAME: ${env:NEO4J_USERNAME}
|
|
@@ -61,17 +63,12 @@ provider:
|
|
|
61
63
|
'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Port]
|
|
62
64
|
NOTIFICATIONS_QUEUE_URL: ${construct:notifications-queue.queueUrl}
|
|
63
65
|
SKILL_EMBEDDING_GOOGLE_API_KEY: ${env:SKILL_EMBEDDING_GOOGLE_API_KEY, ""}
|
|
64
|
-
SKILL_EMBEDDING_GOOGLE_MODEL: ${env:SKILL_EMBEDDING_GOOGLE_MODEL, "gemini-embedding-001"}
|
|
65
66
|
SKILL_EMBEDDING_BATCH_SIZE: ${env:SKILL_EMBEDDING_BATCH_SIZE, "25"}
|
|
66
67
|
SKILL_EMBEDDING_BACKFILL_ON_STARTUP: ${env:SKILL_EMBEDDING_BACKFILL_ON_STARTUP, "false"}
|
|
67
68
|
SKILL_EMBEDDING_BACKFILL_LOCK_TTL_SECONDS: ${env:SKILL_EMBEDDING_BACKFILL_LOCK_TTL_SECONDS, "900"}
|
|
68
69
|
SKILL_EMBEDDING_BACKFILL_PAGE_SIZE: ${env:SKILL_EMBEDDING_BACKFILL_PAGE_SIZE, "100"}
|
|
69
70
|
SKILL_EMBEDDING_BACKFILL_BATCH_SIZE: ${env:SKILL_EMBEDDING_BACKFILL_BATCH_SIZE, "50"}
|
|
70
|
-
OIDC_TRUSTED_ISSUERS: ${env:OIDC_TRUSTED_ISSUERS, ""}
|
|
71
|
-
OIDC_EXPECTED_AUDIENCE: ${env:OIDC_EXPECTED_AUDIENCE, ""}
|
|
72
|
-
CREDENTIAL_REFRESH_ENABLED: ${env:CREDENTIAL_REFRESH_ENABLED, "false"}
|
|
73
71
|
CREDENTIAL_REFRESH_DIGEST_SECRET: ${env:CREDENTIAL_REFRESH_DIGEST_SECRET, ""}
|
|
74
|
-
CREDENTIAL_REFRESH_NOTIFICATION_WINDOW_HOURS: ${env:CREDENTIAL_REFRESH_NOTIFICATION_WINDOW_HOURS, "24"}
|
|
75
72
|
tracing:
|
|
76
73
|
apiGateway: true
|
|
77
74
|
lambda: true
|