@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.
Files changed (312) hide show
  1. package/.env.example +44 -111
  2. package/AGENTS.md +52 -0
  3. package/CHANGELOG.md +109 -0
  4. package/README.md +9 -2
  5. package/bootstrapLambda.test.ts +72 -0
  6. package/config/config.dev.json +29 -0
  7. package/config/config.example.json +29 -0
  8. package/config/config.json +1 -0
  9. package/config/config.production.json +29 -0
  10. package/config/config.scouts.dev.json +24 -0
  11. package/config/config.scouts.production.json +24 -0
  12. package/contractEventsLambda.ts +18 -0
  13. package/contractEventsLambdaApp.ts +19 -0
  14. package/didWebLambda.ts +13 -18
  15. package/didWebLambdaApp.ts +22 -0
  16. package/dist/accesslayer/app-manifest-version/create.d.ts +10 -0
  17. package/dist/accesslayer/app-manifest-version/create.d.ts.map +1 -0
  18. package/dist/accesslayer/app-manifest-version/read.d.ts +11 -0
  19. package/dist/accesslayer/app-manifest-version/read.d.ts.map +1 -0
  20. package/dist/accesslayer/app-manifest-version/update.d.ts +7 -0
  21. package/dist/accesslayer/app-manifest-version/update.d.ts.map +1 -0
  22. package/dist/accesslayer/app-store-listing/read.d.ts +2 -1
  23. package/dist/accesslayer/app-store-listing/read.d.ts.map +1 -1
  24. package/dist/accesslayer/app-store-listing/relationships/create.d.ts +1 -0
  25. package/dist/accesslayer/app-store-listing/relationships/create.d.ts.map +1 -1
  26. package/dist/accesslayer/app-store-listing/relationships/read.d.ts +2 -0
  27. package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
  28. package/dist/accesslayer/app-store-listing/update.d.ts +1 -0
  29. package/dist/accesslayer/app-store-listing/update.d.ts.map +1 -1
  30. package/dist/accesslayer/boost/relationships/create.d.ts +2 -1
  31. package/dist/accesslayer/boost/relationships/create.d.ts.map +1 -1
  32. package/dist/accesslayer/consentflowcontract/create.d.ts +9 -1
  33. package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
  34. package/dist/accesslayer/consentflowcontract/read.d.ts +34 -3
  35. package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
  36. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +4 -1
  37. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  38. package/dist/accesslayer/consentflowcontract/relationships/delete.d.ts.map +1 -1
  39. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +3 -2
  40. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  41. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts +12 -0
  42. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts.map +1 -0
  43. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts +12 -0
  44. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts.map +1 -0
  45. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +8 -5
  46. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  47. package/dist/accesslayer/credential/create.d.ts +2 -1
  48. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  49. package/dist/accesslayer/credential/relationships/create.d.ts +4 -3
  50. package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
  51. package/dist/accesslayer/inbox-credential/relationships/create.d.ts +2 -1
  52. package/dist/accesslayer/inbox-credential/relationships/create.d.ts.map +1 -1
  53. package/dist/accesslayer/inbox-credential/update.d.ts +2 -1
  54. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  55. package/dist/accesslayer/share-link/abandon.d.ts.map +1 -1
  56. package/dist/accesslayer/share-link/attachment.d.ts +37 -0
  57. package/dist/accesslayer/share-link/attachment.d.ts.map +1 -0
  58. package/dist/accesslayer/share-link/finalize.d.ts.map +1 -1
  59. package/dist/accesslayer/share-link/helpers.d.ts +3 -0
  60. package/dist/accesslayer/share-link/helpers.d.ts.map +1 -1
  61. package/dist/accesslayer/share-link/recovery.d.ts.map +1 -1
  62. package/dist/accesslayer/share-link/reserve.d.ts.map +1 -1
  63. package/dist/accesslayer/share-link/revoke.d.ts.map +1 -1
  64. package/dist/accesslayer/share-link/types.d.ts +8 -3
  65. package/dist/accesslayer/share-link/types.d.ts.map +1 -1
  66. package/dist/cache/app-store.caches.d.ts +49 -0
  67. package/dist/cache/app-store.caches.d.ts.map +1 -1
  68. package/dist/config/applyDockerStageConfig.d.ts +2 -0
  69. package/dist/config/applyDockerStageConfig.d.ts.map +1 -0
  70. package/dist/config/environment.d.ts +5 -0
  71. package/dist/config/environment.d.ts.map +1 -1
  72. package/dist/config/serverlessFunctionEnv.test.d.ts +2 -0
  73. package/dist/config/serverlessFunctionEnv.test.d.ts.map +1 -0
  74. package/dist/config/stageConfig.d.ts +112 -0
  75. package/dist/config/stageConfig.d.ts.map +1 -0
  76. package/dist/config/stageConfig.test.d.ts +2 -0
  77. package/dist/config/stageConfig.test.d.ts.map +1 -0
  78. package/dist/didkit_wasm_bg.wasm +0 -0
  79. package/dist/docker-entry.d.ts +1 -1
  80. package/dist/docker-entry.d.ts.map +1 -1
  81. package/dist/helpers/app-manifest.helpers.d.ts +5 -0
  82. package/dist/helpers/app-manifest.helpers.d.ts.map +1 -0
  83. package/dist/helpers/claim-hooks.helpers.d.ts +2 -1
  84. package/dist/helpers/claim-hooks.helpers.d.ts.map +1 -1
  85. package/dist/helpers/consent-referral.helpers.d.ts +2 -0
  86. package/dist/helpers/consent-referral.helpers.d.ts.map +1 -0
  87. package/dist/helpers/consent-scopes.helpers.d.ts +7 -0
  88. package/dist/helpers/consent-scopes.helpers.d.ts.map +1 -0
  89. package/dist/helpers/contract-event-maintenance.helpers.d.ts +3 -0
  90. package/dist/helpers/contract-event-maintenance.helpers.d.ts.map +1 -0
  91. package/dist/helpers/contract-events.helpers.d.ts +25 -0
  92. package/dist/helpers/contract-events.helpers.d.ts.map +1 -0
  93. package/dist/helpers/contract-requests.helpers.d.ts +19 -0
  94. package/dist/helpers/contract-requests.helpers.d.ts.map +1 -0
  95. package/dist/helpers/finalize-inbox.helpers.d.ts +3 -3
  96. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  97. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  98. package/dist/helpers/inline-template.helpers.d.ts +45 -0
  99. package/dist/helpers/inline-template.helpers.d.ts.map +1 -0
  100. package/dist/helpers/notificationMessages.d.ts +1 -1
  101. package/dist/helpers/notificationMessages.d.ts.map +1 -1
  102. package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
  103. package/dist/helpers/notifications.helpers.d.ts +6 -0
  104. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  105. package/dist/helpers/profile.helpers.d.ts +1 -0
  106. package/dist/helpers/profile.helpers.d.ts.map +1 -1
  107. package/dist/helpers/share-link-attachment-access.d.ts +4 -0
  108. package/dist/helpers/share-link-attachment-access.d.ts.map +1 -0
  109. package/dist/helpers/share-link-attachment.d.ts +18 -0
  110. package/dist/helpers/share-link-attachment.d.ts.map +1 -0
  111. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts +5 -0
  112. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts.map +1 -0
  113. package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -1
  114. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -1
  115. package/dist/helpers/share-link-maintenance/runner.d.ts +1 -0
  116. package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -1
  117. package/dist/helpers/share-link-maintenance/runtime.d.ts +2 -0
  118. package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -1
  119. package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -1
  120. package/dist/helpers/share-link-maintenance/types.d.ts +4 -1
  121. package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -1
  122. package/dist/helpers/share-link-owner-projection.d.ts.map +1 -1
  123. package/dist/helpers/share-link-policy/production.d.ts +3 -5
  124. package/dist/helpers/share-link-policy/production.d.ts.map +1 -1
  125. package/dist/helpers/share-link-policy/resolver.d.ts +1 -1
  126. package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -1
  127. package/dist/helpers/share-link-policy/types.d.ts +8 -2
  128. package/dist/helpers/share-link-policy/types.d.ts.map +1 -1
  129. package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
  130. package/dist/helpers/smartResume.helpers.d.ts +3 -0
  131. package/dist/helpers/smartResume.helpers.d.ts.map +1 -0
  132. package/dist/index.js +2 -2
  133. package/dist/models/AppManifestVersion.d.ts +20 -0
  134. package/dist/models/AppManifestVersion.d.ts.map +1 -0
  135. package/dist/models/AppStoreListing.d.ts +13 -0
  136. package/dist/models/AppStoreListing.d.ts.map +1 -1
  137. package/dist/models/ConsentFlowContract.d.ts +13 -2
  138. package/dist/models/ConsentFlowContract.d.ts.map +1 -1
  139. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  140. package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
  141. package/dist/models/Integration.d.ts +2 -0
  142. package/dist/models/Integration.d.ts.map +1 -1
  143. package/dist/models/ShareLink.d.ts +2 -0
  144. package/dist/models/ShareLink.d.ts.map +1 -1
  145. package/dist/models/index.d.ts +1 -0
  146. package/dist/models/index.d.ts.map +1 -1
  147. package/dist/models/share-link-constraints.d.ts.map +1 -1
  148. package/dist/openapi.d.ts +1 -1
  149. package/dist/openapi.d.ts.map +1 -1
  150. package/dist/routes/app-store.d.ts +619 -0
  151. package/dist/routes/app-store.d.ts.map +1 -1
  152. package/dist/routes/boosts.d.ts +2 -2
  153. package/dist/routes/boosts.d.ts.map +1 -1
  154. package/dist/routes/contracts.d.ts +146 -3
  155. package/dist/routes/contracts.d.ts.map +1 -1
  156. package/dist/routes/credentials.d.ts +34 -0
  157. package/dist/routes/credentials.d.ts.map +1 -1
  158. package/dist/routes/inbox.d.ts +52 -2
  159. package/dist/routes/inbox.d.ts.map +1 -1
  160. package/dist/routes/index.d.ts.map +1 -1
  161. package/dist/routes/profile-manager.d.ts.map +1 -1
  162. package/dist/routes/profiles.d.ts.map +1 -1
  163. package/dist/routes/public-share-links.d.ts +55 -0
  164. package/dist/routes/public-share-links.d.ts.map +1 -1
  165. package/dist/routes/share-links.d.ts +175 -14
  166. package/dist/routes/share-links.d.ts.map +1 -1
  167. package/dist/routes/test.d.ts +5 -0
  168. package/dist/routes/test.d.ts.map +1 -1
  169. package/dist/routes/workflows.d.ts.map +1 -1
  170. package/dist/types/app-manifest.d.ts +339 -0
  171. package/dist/types/app-manifest.d.ts.map +1 -0
  172. package/dist/types/app-store-listing.d.ts +34 -0
  173. package/dist/types/app-store-listing.d.ts.map +1 -1
  174. package/dist/types/consentflowcontract.d.ts +30 -3
  175. package/dist/types/consentflowcontract.d.ts.map +1 -1
  176. package/dist/types/credential-refresh.d.ts +1 -1
  177. package/esbuildPlugins.cjs +1 -0
  178. package/lambda.ts +49 -195
  179. package/lambdaApp.ts +203 -0
  180. package/package.json +20 -18
  181. package/scripts/export-openapi.ts +20 -0
  182. package/scripts/offline-schema-environment.ts +18 -0
  183. package/scripts/verify-python-contracts.ts +72 -0
  184. package/serverless-local.yml +3 -6
  185. package/serverless.function-env.cjs +50 -0
  186. package/serverless.local.yml +0 -1
  187. package/serverless.yml +27 -74
  188. package/shareLinkMaintenanceLambda.ts +13 -44
  189. package/shareLinkMaintenanceLambdaApp.ts +50 -0
  190. package/src/accesslayer/app-manifest-version/create.ts +57 -0
  191. package/src/accesslayer/app-manifest-version/read.ts +129 -0
  192. package/src/accesslayer/app-manifest-version/update.ts +61 -0
  193. package/src/accesslayer/app-store-listing/read.ts +6 -0
  194. package/src/accesslayer/app-store-listing/relationships/create.ts +20 -0
  195. package/src/accesslayer/app-store-listing/relationships/read.ts +24 -1
  196. package/src/accesslayer/app-store-listing/update.ts +15 -0
  197. package/src/accesslayer/boost/relationships/create.ts +8 -2
  198. package/src/accesslayer/consentflowcontract/create.ts +81 -8
  199. package/src/accesslayer/consentflowcontract/read.ts +84 -10
  200. package/src/accesslayer/consentflowcontract/relationships/create.ts +116 -93
  201. package/src/accesslayer/consentflowcontract/relationships/delete.ts +1 -0
  202. package/src/accesslayer/consentflowcontract/relationships/read.ts +184 -108
  203. package/src/accesslayer/consentflowcontract/relationships/recipients.ts +125 -0
  204. package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +125 -0
  205. package/src/accesslayer/consentflowcontract/relationships/update.ts +240 -189
  206. package/src/accesslayer/credential/create.test.ts +2 -115
  207. package/src/accesslayer/credential/create.ts +11 -6
  208. package/src/accesslayer/credential/relationships/create.ts +17 -10
  209. package/src/accesslayer/inbox-credential/relationships/create.ts +70 -48
  210. package/src/accesslayer/inbox-credential/update.ts +4 -2
  211. package/src/accesslayer/share-link/abandon.ts +20 -0
  212. package/src/accesslayer/share-link/attachment.ts +360 -0
  213. package/src/accesslayer/share-link/finalize.ts +55 -7
  214. package/src/accesslayer/share-link/helpers.ts +33 -2
  215. package/src/accesslayer/share-link/recovery.ts +20 -0
  216. package/src/accesslayer/share-link/reserve.ts +85 -6
  217. package/src/accesslayer/share-link/revoke.ts +35 -0
  218. package/src/accesslayer/share-link/types.ts +9 -4
  219. package/src/cache/app-store.caches.ts +35 -0
  220. package/src/config/applyDockerStageConfig.ts +6 -0
  221. package/src/config/environment.test.ts +17 -12
  222. package/src/config/environment.ts +1 -0
  223. package/src/config/serverlessFunctionEnv.test.ts +105 -0
  224. package/src/config/stageConfig.test.ts +25 -0
  225. package/src/config/stageConfig.ts +24 -0
  226. package/src/docker-entry.ts +24 -0
  227. package/src/helpers/app-manifest.helpers.ts +185 -0
  228. package/src/helpers/claim-hooks.helpers.ts +20 -7
  229. package/src/helpers/consent-referral.helpers.ts +6 -0
  230. package/src/helpers/consent-scopes.helpers.ts +58 -0
  231. package/src/helpers/contract-event-maintenance.helpers.ts +43 -0
  232. package/src/helpers/contract-events.helpers.ts +540 -0
  233. package/src/helpers/contract-requests.helpers.ts +211 -0
  234. package/src/helpers/finalize-inbox.helpers.ts +163 -44
  235. package/src/helpers/inbox.helpers.ts +51 -66
  236. package/src/helpers/inline-template.helpers.ts +560 -0
  237. package/src/helpers/notificationMessages.ts +59 -0
  238. package/src/helpers/notificationQueue.helpers.ts +23 -5
  239. package/src/helpers/notifications.helpers.ts +25 -1
  240. package/src/helpers/profile.helpers.ts +6 -0
  241. package/src/helpers/share-link-attachment-access.ts +87 -0
  242. package/src/helpers/share-link-attachment.ts +115 -0
  243. package/src/helpers/share-link-coordinator/attachment-stat.ts +41 -0
  244. package/src/helpers/share-link-coordinator/coordinator.ts +15 -2
  245. package/src/helpers/share-link-coordinator/recovery-runner.ts +15 -0
  246. package/src/helpers/share-link-maintenance/runner.ts +28 -0
  247. package/src/helpers/share-link-maintenance/runtime.ts +6 -0
  248. package/src/helpers/share-link-maintenance/telemetry.ts +3 -0
  249. package/src/helpers/share-link-maintenance/types.ts +2 -0
  250. package/src/helpers/share-link-owner-projection.ts +2 -0
  251. package/src/helpers/share-link-policy/production.ts +37 -17
  252. package/src/helpers/share-link-policy/resolver.ts +16 -11
  253. package/src/helpers/share-link-policy/types.ts +15 -7
  254. package/src/helpers/signingAuthority.helpers.ts +23 -14
  255. package/src/helpers/smartResume.helpers.ts +123 -0
  256. package/src/models/AppManifestVersion.ts +43 -0
  257. package/src/models/AppStoreListing.ts +32 -0
  258. package/src/models/ConsentFlowContract.ts +34 -3
  259. package/src/models/ConsentFlowTerms.ts +10 -0
  260. package/src/models/ConsentFlowTransaction.ts +3 -0
  261. package/src/models/Integration.ts +2 -0
  262. package/src/models/ShareLink.ts +4 -0
  263. package/src/models/index.ts +23 -0
  264. package/src/models/share-link-constraints.ts +5 -0
  265. package/src/openapi.ts +44 -8
  266. package/src/routes/app-store.ts +1054 -27
  267. package/src/routes/boosts.ts +12 -14
  268. package/src/routes/contracts.ts +382 -262
  269. package/src/routes/inbox.ts +4 -2
  270. package/src/routes/index.ts +4 -1
  271. package/src/routes/profile-manager.ts +19 -0
  272. package/src/routes/profiles.ts +12 -0
  273. package/src/routes/public-share-links.ts +169 -0
  274. package/src/routes/share-links.ts +69 -0
  275. package/src/routes/workflows.ts +3 -2
  276. package/src/types/app-manifest.ts +28 -0
  277. package/src/types/app-store-listing.ts +13 -0
  278. package/src/types/consentflowcontract.ts +25 -4
  279. package/test/app-manifest.helpers.spec.ts +170 -0
  280. package/test/app-manifest.routes.spec.ts +299 -0
  281. package/test/app-store-listing-updates.spec.ts +236 -0
  282. package/test/app-store-listing.spec.ts +65 -3
  283. package/test/consent-data-boundaries.spec.ts +352 -0
  284. package/test/consent-recipients.spec.ts +513 -0
  285. package/test/consent-scopes.helpers.spec.ts +67 -0
  286. package/test/consentflow.spec.ts +63 -2
  287. package/test/contract-requests.spec.ts +1588 -0
  288. package/test/credential-storage.neo4j.spec.ts +122 -0
  289. package/test/guardian-gated-routes.spec.ts +36 -0
  290. package/test/inbox-security.e2e.ts +26 -10
  291. package/test/inbox.spec.ts +184 -8
  292. package/test/inline-template.helpers.spec.ts +49 -0
  293. package/test/managed-recipient-inbox.spec.ts +24 -2
  294. package/test/openapi.spec.ts +14 -73
  295. package/test/profiles.spec.ts +30 -0
  296. package/test/share-link-attachment.neo4j.spec.ts +262 -0
  297. package/test/share-link-attachment.unit.spec.ts +244 -0
  298. package/test/share-link-lifecycle.neo4j.spec.ts +145 -2
  299. package/test/share-link-maintenance.unit.spec.ts +32 -5
  300. package/test/share-link-owner-route.unit.spec.ts +70 -2
  301. package/test/share-link-policy.unit.spec.ts +18 -4
  302. package/test/share-link-production-policy.unit.spec.ts +58 -9
  303. package/test/share-link-public-route.unit.spec.ts +279 -0
  304. package/test/share-link-service-policy.neo4j.spec.ts +403 -0
  305. package/test/signing-authority-encryption.spec.ts +75 -0
  306. package/test/smart-resume-consent.spec.ts +233 -0
  307. package/test/smart-resume-upload.spec.ts +65 -0
  308. package/tsconfig.build.json +1 -1
  309. package/tsconfig.json +1 -0
  310. package/vitest.config.ts +13 -0
  311. package/vitest.inbox-e2e.config.ts +2 -0
  312. package/workerLambda.test.ts +100 -0
package/lambda.ts CHANGED
@@ -1,203 +1,57 @@
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
- });
1
+ import { bootstrapLambda } from '@learncard/service-config';
46
2
 
47
- startSkillEmbeddingBackfill().catch(err =>
48
- console.error('Skill embedding backfill startup error:', err)
49
- );
3
+ import { base, stages } from './src/config/stageConfig';
50
4
 
51
- export const swaggerUiHandler = serverlessHttp(toServerlessApplication(app), {
52
- basePath: '/docs',
53
- });
5
+ type LambdaApp = typeof import('./lambdaApp');
54
6
 
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,
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 _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
- });
16
+ export const trpcHandler = async (...args: Parameters<LambdaApp['trpcHandler']>) =>
17
+ (await getApplication()).trpcHandler(...args);
151
18
 
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
- });
19
+ export const openApiHandler = async (...args: Parameters<LambdaApp['openApiHandler']>) =>
20
+ (await getApplication()).openApiHandler(...args);
174
21
 
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
- });
22
+ export const _trpcHandler = async (...args: Parameters<LambdaApp['_trpcHandler']>) =>
23
+ (await getApplication())._trpcHandler(...args);
199
24
 
200
- export const inboxMaintenanceHandler = Sentry.AWSLambda.wrapHandler(async (): Promise<void> => {
201
- const counts = await runInboxMaintenance();
202
- console.log('Universal Inbox maintenance completed', counts);
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.19.1",
3
+ "version": "4.0.0",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=24.12 <25"
7
7
  },
8
- "packageManager": "bun@1.3.14",
8
+ "packageManager": "bun@1.4.2",
9
9
  "main": "dist/index.js",
10
10
  "types": "dist/index.d.ts",
11
11
  "scripts": {
@@ -62,22 +62,24 @@
62
62
  "@digitalcredentials/issuer-registry-client": "^3.2.0-beta.5",
63
63
  "@fastify/cors": "^11.2.0",
64
64
  "@google/genai": "^1.27.0",
65
- "@learncard/credential-library": "2.0.5",
66
- "@learncard/core": "9.4.37",
67
- "@learncard/crypto-plugin": "1.1.37",
68
- "@learncard/did-web-plugin": "1.1.37",
69
- "@learncard/didkey-plugin": "1.1.37",
70
- "@learncard/didkit-plugin": "1.10.2",
71
- "@learncard/didkit-plugin-node": "0.3.2",
72
- "@learncard/dynamic-loader-plugin": "1.1.37",
73
- "@learncard/encryption-plugin": "1.1.37",
74
- "@learncard/expiration-plugin": "1.2.37",
75
- "@learncard/email-templates": "1.2.0",
76
- "@learncard/helpers": "1.6.1",
77
- "@learncard/learn-card-plugin": "1.2.37",
78
- "@learncard/types": "5.22.0",
79
- "@learncard/vc-plugin": "1.6.3",
80
- "@learncard/vc-templates-plugin": "1.1.37",
65
+ "@learncard/credential-library": "2.1.0",
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/service-config": "0.1.1",
80
+ "@learncard/types": "5.23.0",
81
+ "@learncard/vc-plugin": "1.6.4",
82
+ "@learncard/vc-templates-plugin": "1.1.38",
81
83
  "@sentry/serverless": "7.61.0",
82
84
  "@trpc/server": "11.8.0",
83
85
  "@types/lodash": "^4.14.191",
@@ -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);
@@ -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
- LAMBDA_STAGE: ${opt:stage, "dev"}
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