@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.
Files changed (315) hide show
  1. package/.env.example +44 -111
  2. package/AGENTS.md +52 -0
  3. package/CHANGELOG.md +120 -0
  4. package/README.md +9 -2
  5. package/bootstrapLambda.test.ts +72 -0
  6. package/bundle.mjs +9 -1
  7. package/config/config.dev.json +29 -0
  8. package/config/config.example.json +29 -0
  9. package/config/config.json +1 -0
  10. package/config/config.production.json +29 -0
  11. package/config/config.scouts.dev.json +24 -0
  12. package/config/config.scouts.production.json +24 -0
  13. package/contractEventsLambda.ts +18 -0
  14. package/contractEventsLambdaApp.ts +19 -0
  15. package/didWebLambda.ts +13 -18
  16. package/didWebLambdaApp.ts +22 -0
  17. package/dist/accesslayer/app-manifest-version/create.d.ts +10 -0
  18. package/dist/accesslayer/app-manifest-version/create.d.ts.map +1 -0
  19. package/dist/accesslayer/app-manifest-version/read.d.ts +11 -0
  20. package/dist/accesslayer/app-manifest-version/read.d.ts.map +1 -0
  21. package/dist/accesslayer/app-manifest-version/update.d.ts +7 -0
  22. package/dist/accesslayer/app-manifest-version/update.d.ts.map +1 -0
  23. package/dist/accesslayer/app-store-listing/read.d.ts +2 -1
  24. package/dist/accesslayer/app-store-listing/read.d.ts.map +1 -1
  25. package/dist/accesslayer/app-store-listing/relationships/create.d.ts +1 -0
  26. package/dist/accesslayer/app-store-listing/relationships/create.d.ts.map +1 -1
  27. package/dist/accesslayer/app-store-listing/relationships/read.d.ts +2 -0
  28. package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
  29. package/dist/accesslayer/app-store-listing/update.d.ts +1 -0
  30. package/dist/accesslayer/app-store-listing/update.d.ts.map +1 -1
  31. package/dist/accesslayer/boost/relationships/create.d.ts +2 -1
  32. package/dist/accesslayer/boost/relationships/create.d.ts.map +1 -1
  33. package/dist/accesslayer/consentflowcontract/create.d.ts +9 -1
  34. package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
  35. package/dist/accesslayer/consentflowcontract/read.d.ts +34 -3
  36. package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
  37. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +4 -1
  38. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  39. package/dist/accesslayer/consentflowcontract/relationships/delete.d.ts.map +1 -1
  40. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +3 -2
  41. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  42. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts +12 -0
  43. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts.map +1 -0
  44. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts +12 -0
  45. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts.map +1 -0
  46. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +8 -5
  47. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  48. package/dist/accesslayer/credential/create.d.ts +2 -1
  49. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  50. package/dist/accesslayer/credential/relationships/create.d.ts +4 -3
  51. package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
  52. package/dist/accesslayer/inbox-credential/relationships/create.d.ts +2 -1
  53. package/dist/accesslayer/inbox-credential/relationships/create.d.ts.map +1 -1
  54. package/dist/accesslayer/inbox-credential/update.d.ts +2 -1
  55. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  56. package/dist/accesslayer/share-link/abandon.d.ts.map +1 -1
  57. package/dist/accesslayer/share-link/attachment.d.ts +37 -0
  58. package/dist/accesslayer/share-link/attachment.d.ts.map +1 -0
  59. package/dist/accesslayer/share-link/finalize.d.ts.map +1 -1
  60. package/dist/accesslayer/share-link/helpers.d.ts +3 -0
  61. package/dist/accesslayer/share-link/helpers.d.ts.map +1 -1
  62. package/dist/accesslayer/share-link/recovery.d.ts.map +1 -1
  63. package/dist/accesslayer/share-link/reserve.d.ts.map +1 -1
  64. package/dist/accesslayer/share-link/revoke.d.ts.map +1 -1
  65. package/dist/accesslayer/share-link/types.d.ts +8 -3
  66. package/dist/accesslayer/share-link/types.d.ts.map +1 -1
  67. package/dist/cache/app-store.caches.d.ts +49 -0
  68. package/dist/cache/app-store.caches.d.ts.map +1 -1
  69. package/dist/config/applyDockerStageConfig.d.ts +2 -0
  70. package/dist/config/applyDockerStageConfig.d.ts.map +1 -0
  71. package/dist/config/environment.d.ts +5 -0
  72. package/dist/config/environment.d.ts.map +1 -1
  73. package/dist/config/serverlessFunctionEnv.test.d.ts +2 -0
  74. package/dist/config/serverlessFunctionEnv.test.d.ts.map +1 -0
  75. package/dist/config/stageConfig.d.ts +112 -0
  76. package/dist/config/stageConfig.d.ts.map +1 -0
  77. package/dist/config/stageConfig.test.d.ts +2 -0
  78. package/dist/config/stageConfig.test.d.ts.map +1 -0
  79. package/dist/didkit_wasm_bg.wasm +0 -0
  80. package/dist/docker-entry.d.ts +1 -1
  81. package/dist/docker-entry.d.ts.map +1 -1
  82. package/dist/helpers/app-manifest.helpers.d.ts +5 -0
  83. package/dist/helpers/app-manifest.helpers.d.ts.map +1 -0
  84. package/dist/helpers/claim-hooks.helpers.d.ts +2 -1
  85. package/dist/helpers/claim-hooks.helpers.d.ts.map +1 -1
  86. package/dist/helpers/consent-referral.helpers.d.ts +2 -0
  87. package/dist/helpers/consent-referral.helpers.d.ts.map +1 -0
  88. package/dist/helpers/consent-scopes.helpers.d.ts +7 -0
  89. package/dist/helpers/consent-scopes.helpers.d.ts.map +1 -0
  90. package/dist/helpers/contract-event-maintenance.helpers.d.ts +3 -0
  91. package/dist/helpers/contract-event-maintenance.helpers.d.ts.map +1 -0
  92. package/dist/helpers/contract-events.helpers.d.ts +25 -0
  93. package/dist/helpers/contract-events.helpers.d.ts.map +1 -0
  94. package/dist/helpers/contract-requests.helpers.d.ts +19 -0
  95. package/dist/helpers/contract-requests.helpers.d.ts.map +1 -0
  96. package/dist/helpers/finalize-inbox.helpers.d.ts +3 -3
  97. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  98. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  99. package/dist/helpers/inline-template.helpers.d.ts +45 -0
  100. package/dist/helpers/inline-template.helpers.d.ts.map +1 -0
  101. package/dist/helpers/notificationMessages.d.ts +1 -1
  102. package/dist/helpers/notificationMessages.d.ts.map +1 -1
  103. package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
  104. package/dist/helpers/notifications.helpers.d.ts +6 -0
  105. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  106. package/dist/helpers/profile.helpers.d.ts +1 -0
  107. package/dist/helpers/profile.helpers.d.ts.map +1 -1
  108. package/dist/helpers/share-link-attachment-access.d.ts +4 -0
  109. package/dist/helpers/share-link-attachment-access.d.ts.map +1 -0
  110. package/dist/helpers/share-link-attachment.d.ts +18 -0
  111. package/dist/helpers/share-link-attachment.d.ts.map +1 -0
  112. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts +5 -0
  113. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts.map +1 -0
  114. package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -1
  115. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -1
  116. package/dist/helpers/share-link-maintenance/runner.d.ts +1 -0
  117. package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -1
  118. package/dist/helpers/share-link-maintenance/runtime.d.ts +2 -0
  119. package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -1
  120. package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -1
  121. package/dist/helpers/share-link-maintenance/types.d.ts +4 -1
  122. package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -1
  123. package/dist/helpers/share-link-owner-projection.d.ts.map +1 -1
  124. package/dist/helpers/share-link-policy/production.d.ts +3 -5
  125. package/dist/helpers/share-link-policy/production.d.ts.map +1 -1
  126. package/dist/helpers/share-link-policy/resolver.d.ts +1 -1
  127. package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -1
  128. package/dist/helpers/share-link-policy/types.d.ts +8 -2
  129. package/dist/helpers/share-link-policy/types.d.ts.map +1 -1
  130. package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
  131. package/dist/helpers/smartResume.helpers.d.ts +3 -0
  132. package/dist/helpers/smartResume.helpers.d.ts.map +1 -0
  133. package/dist/index.js +2 -2
  134. package/dist/models/AppManifestVersion.d.ts +20 -0
  135. package/dist/models/AppManifestVersion.d.ts.map +1 -0
  136. package/dist/models/AppStoreListing.d.ts +13 -0
  137. package/dist/models/AppStoreListing.d.ts.map +1 -1
  138. package/dist/models/ConsentFlowContract.d.ts +13 -2
  139. package/dist/models/ConsentFlowContract.d.ts.map +1 -1
  140. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  141. package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
  142. package/dist/models/Integration.d.ts +2 -0
  143. package/dist/models/Integration.d.ts.map +1 -1
  144. package/dist/models/ShareLink.d.ts +2 -0
  145. package/dist/models/ShareLink.d.ts.map +1 -1
  146. package/dist/models/index.d.ts +1 -0
  147. package/dist/models/index.d.ts.map +1 -1
  148. package/dist/models/share-link-constraints.d.ts.map +1 -1
  149. package/dist/openapi.d.ts +1 -1
  150. package/dist/openapi.d.ts.map +1 -1
  151. package/dist/publish-metafile.json +1 -0
  152. package/dist/routes/app-store.d.ts +619 -0
  153. package/dist/routes/app-store.d.ts.map +1 -1
  154. package/dist/routes/boosts.d.ts +2 -2
  155. package/dist/routes/boosts.d.ts.map +1 -1
  156. package/dist/routes/contracts.d.ts +146 -3
  157. package/dist/routes/contracts.d.ts.map +1 -1
  158. package/dist/routes/credentials.d.ts +34 -0
  159. package/dist/routes/credentials.d.ts.map +1 -1
  160. package/dist/routes/inbox.d.ts +52 -2
  161. package/dist/routes/inbox.d.ts.map +1 -1
  162. package/dist/routes/index.d.ts.map +1 -1
  163. package/dist/routes/profile-manager.d.ts.map +1 -1
  164. package/dist/routes/profiles.d.ts.map +1 -1
  165. package/dist/routes/public-share-links.d.ts +55 -0
  166. package/dist/routes/public-share-links.d.ts.map +1 -1
  167. package/dist/routes/share-links.d.ts +175 -14
  168. package/dist/routes/share-links.d.ts.map +1 -1
  169. package/dist/routes/test.d.ts +5 -0
  170. package/dist/routes/test.d.ts.map +1 -1
  171. package/dist/routes/workflows.d.ts.map +1 -1
  172. package/dist/types/app-manifest.d.ts +339 -0
  173. package/dist/types/app-manifest.d.ts.map +1 -0
  174. package/dist/types/app-store-listing.d.ts +34 -0
  175. package/dist/types/app-store-listing.d.ts.map +1 -1
  176. package/dist/types/consentflowcontract.d.ts +30 -3
  177. package/dist/types/consentflowcontract.d.ts.map +1 -1
  178. package/dist/types/credential-refresh.d.ts +1 -1
  179. package/esbuildPlugins.cjs +1 -0
  180. package/lambda.ts +49 -195
  181. package/lambdaApp.ts +203 -0
  182. package/package.json +20 -19
  183. package/project.json +8 -1
  184. package/scripts/export-openapi.ts +20 -0
  185. package/scripts/offline-schema-environment.ts +18 -0
  186. package/scripts/verify-python-contracts.ts +72 -0
  187. package/serverless-local.yml +3 -6
  188. package/serverless.function-env.cjs +50 -0
  189. package/serverless.local.yml +0 -1
  190. package/serverless.yml +29 -74
  191. package/shareLinkMaintenanceLambda.ts +13 -44
  192. package/shareLinkMaintenanceLambdaApp.ts +50 -0
  193. package/src/accesslayer/app-manifest-version/create.ts +57 -0
  194. package/src/accesslayer/app-manifest-version/read.ts +129 -0
  195. package/src/accesslayer/app-manifest-version/update.ts +61 -0
  196. package/src/accesslayer/app-store-listing/read.ts +6 -0
  197. package/src/accesslayer/app-store-listing/relationships/create.ts +20 -0
  198. package/src/accesslayer/app-store-listing/relationships/read.ts +24 -1
  199. package/src/accesslayer/app-store-listing/update.ts +15 -0
  200. package/src/accesslayer/boost/relationships/create.ts +8 -2
  201. package/src/accesslayer/consentflowcontract/create.ts +81 -8
  202. package/src/accesslayer/consentflowcontract/read.ts +84 -10
  203. package/src/accesslayer/consentflowcontract/relationships/create.ts +116 -93
  204. package/src/accesslayer/consentflowcontract/relationships/delete.ts +1 -0
  205. package/src/accesslayer/consentflowcontract/relationships/read.ts +184 -108
  206. package/src/accesslayer/consentflowcontract/relationships/recipients.ts +125 -0
  207. package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +125 -0
  208. package/src/accesslayer/consentflowcontract/relationships/update.ts +240 -189
  209. package/src/accesslayer/credential/create.test.ts +2 -115
  210. package/src/accesslayer/credential/create.ts +11 -6
  211. package/src/accesslayer/credential/relationships/create.ts +17 -10
  212. package/src/accesslayer/inbox-credential/relationships/create.ts +70 -48
  213. package/src/accesslayer/inbox-credential/update.ts +4 -2
  214. package/src/accesslayer/share-link/abandon.ts +20 -0
  215. package/src/accesslayer/share-link/attachment.ts +360 -0
  216. package/src/accesslayer/share-link/finalize.ts +55 -7
  217. package/src/accesslayer/share-link/helpers.ts +33 -2
  218. package/src/accesslayer/share-link/recovery.ts +20 -0
  219. package/src/accesslayer/share-link/reserve.ts +85 -6
  220. package/src/accesslayer/share-link/revoke.ts +35 -0
  221. package/src/accesslayer/share-link/types.ts +9 -4
  222. package/src/cache/app-store.caches.ts +35 -0
  223. package/src/config/applyDockerStageConfig.ts +6 -0
  224. package/src/config/environment.test.ts +17 -12
  225. package/src/config/environment.ts +1 -0
  226. package/src/config/serverlessFunctionEnv.test.ts +105 -0
  227. package/src/config/stageConfig.test.ts +25 -0
  228. package/src/config/stageConfig.ts +24 -0
  229. package/src/docker-entry.ts +24 -0
  230. package/src/helpers/app-manifest.helpers.ts +185 -0
  231. package/src/helpers/claim-hooks.helpers.ts +20 -7
  232. package/src/helpers/consent-referral.helpers.ts +6 -0
  233. package/src/helpers/consent-scopes.helpers.ts +58 -0
  234. package/src/helpers/contract-event-maintenance.helpers.ts +43 -0
  235. package/src/helpers/contract-events.helpers.ts +540 -0
  236. package/src/helpers/contract-requests.helpers.ts +211 -0
  237. package/src/helpers/finalize-inbox.helpers.ts +163 -44
  238. package/src/helpers/inbox.helpers.ts +51 -66
  239. package/src/helpers/inline-template.helpers.ts +560 -0
  240. package/src/helpers/notificationMessages.ts +59 -0
  241. package/src/helpers/notificationQueue.helpers.ts +23 -5
  242. package/src/helpers/notifications.helpers.ts +25 -1
  243. package/src/helpers/profile.helpers.ts +6 -0
  244. package/src/helpers/share-link-attachment-access.ts +87 -0
  245. package/src/helpers/share-link-attachment.ts +115 -0
  246. package/src/helpers/share-link-coordinator/attachment-stat.ts +41 -0
  247. package/src/helpers/share-link-coordinator/coordinator.ts +15 -2
  248. package/src/helpers/share-link-coordinator/recovery-runner.ts +15 -0
  249. package/src/helpers/share-link-maintenance/runner.ts +28 -0
  250. package/src/helpers/share-link-maintenance/runtime.ts +6 -0
  251. package/src/helpers/share-link-maintenance/telemetry.ts +3 -0
  252. package/src/helpers/share-link-maintenance/types.ts +2 -0
  253. package/src/helpers/share-link-owner-projection.ts +2 -0
  254. package/src/helpers/share-link-policy/production.ts +37 -17
  255. package/src/helpers/share-link-policy/resolver.ts +16 -11
  256. package/src/helpers/share-link-policy/types.ts +15 -7
  257. package/src/helpers/signingAuthority.helpers.ts +23 -14
  258. package/src/helpers/smartResume.helpers.ts +123 -0
  259. package/src/models/AppManifestVersion.ts +43 -0
  260. package/src/models/AppStoreListing.ts +32 -0
  261. package/src/models/ConsentFlowContract.ts +34 -3
  262. package/src/models/ConsentFlowTerms.ts +10 -0
  263. package/src/models/ConsentFlowTransaction.ts +3 -0
  264. package/src/models/Integration.ts +2 -0
  265. package/src/models/ShareLink.ts +4 -0
  266. package/src/models/index.ts +23 -0
  267. package/src/models/share-link-constraints.ts +5 -0
  268. package/src/openapi.ts +44 -8
  269. package/src/routes/app-store.ts +1054 -27
  270. package/src/routes/boosts.ts +12 -14
  271. package/src/routes/contracts.ts +382 -262
  272. package/src/routes/inbox.ts +4 -2
  273. package/src/routes/index.ts +4 -1
  274. package/src/routes/profile-manager.ts +19 -0
  275. package/src/routes/profiles.ts +12 -0
  276. package/src/routes/public-share-links.ts +169 -0
  277. package/src/routes/share-links.ts +69 -0
  278. package/src/routes/workflows.ts +3 -2
  279. package/src/types/app-manifest.ts +28 -0
  280. package/src/types/app-store-listing.ts +13 -0
  281. package/src/types/consentflowcontract.ts +25 -4
  282. package/test/app-manifest.helpers.spec.ts +170 -0
  283. package/test/app-manifest.routes.spec.ts +299 -0
  284. package/test/app-store-listing-updates.spec.ts +236 -0
  285. package/test/app-store-listing.spec.ts +65 -3
  286. package/test/consent-data-boundaries.spec.ts +352 -0
  287. package/test/consent-recipients.spec.ts +513 -0
  288. package/test/consent-scopes.helpers.spec.ts +67 -0
  289. package/test/consentflow.spec.ts +63 -2
  290. package/test/contract-requests.spec.ts +1588 -0
  291. package/test/credential-storage.neo4j.spec.ts +122 -0
  292. package/test/guardian-gated-routes.spec.ts +36 -0
  293. package/test/inbox-security.e2e.ts +26 -10
  294. package/test/inbox.spec.ts +184 -8
  295. package/test/inline-template.helpers.spec.ts +49 -0
  296. package/test/managed-recipient-inbox.spec.ts +24 -2
  297. package/test/openapi.spec.ts +14 -73
  298. package/test/profiles.spec.ts +30 -0
  299. package/test/share-link-attachment.neo4j.spec.ts +262 -0
  300. package/test/share-link-attachment.unit.spec.ts +244 -0
  301. package/test/share-link-lifecycle.neo4j.spec.ts +145 -2
  302. package/test/share-link-maintenance.unit.spec.ts +32 -5
  303. package/test/share-link-owner-route.unit.spec.ts +70 -2
  304. package/test/share-link-policy.unit.spec.ts +18 -4
  305. package/test/share-link-production-policy.unit.spec.ts +58 -9
  306. package/test/share-link-public-route.unit.spec.ts +279 -0
  307. package/test/share-link-service-policy.neo4j.spec.ts +403 -0
  308. package/test/signing-authority-encryption.spec.ts +75 -0
  309. package/test/smart-resume-consent.spec.ts +233 -0
  310. package/test/smart-resume-upload.spec.ts +65 -0
  311. package/tsconfig.build.json +1 -1
  312. package/tsconfig.json +1 -0
  313. package/vitest.config.ts +13 -0
  314. package/vitest.inbox-e2e.config.ts +2 -0
  315. package/workerLambda.test.ts +100 -0
@@ -9,23 +9,31 @@ import type {
9
9
  /**
10
10
  * Pure policy decision table.
11
11
  *
12
- * - known minor, managed or unknown age => no views, 30 days
12
+ * - personal known minor, managed or unknown age => no views, 30 days
13
13
  * - known unmanaged adult => views enabled, 365 days
14
- *
14
+ * - service profile => views enabled without changing the age-derived expiry
15
15
  */
16
16
  export const composeShareLinkPolicy = (
17
17
  age: ShareLinkOwnerAge,
18
- isManaged: boolean
18
+ isManaged: boolean,
19
+ isServiceProfile = false
19
20
  ): ShareLinkPolicySnapshot => {
20
- const isMinor = age === 'minor' ? true : age === 'adult' ? false : null;
21
- const policyResolved = age !== 'unknown';
21
+ // Service age exemption does not establish that the owner is an adult.
22
+ const isMinor = isServiceProfile
23
+ ? false
24
+ : age === 'minor'
25
+ ? true
26
+ : age === 'adult'
27
+ ? false
28
+ : null;
29
+ const policyResolved = isServiceProfile || age !== 'unknown';
22
30
  const isUnmanagedAdult = age === 'adult' && !isManaged;
23
31
 
24
32
  return {
25
33
  isMinor,
26
34
  policyResolved,
27
35
  defaultExpiryDays: isUnmanagedAdult ? 365 : 30,
28
- viewCountingEnabled: isUnmanagedAdult,
36
+ viewCountingEnabled: isServiceProfile || isUnmanagedAdult,
29
37
  };
30
38
  };
31
39
 
@@ -38,12 +46,9 @@ export const createShareLinkPolicyResolver = (
38
46
  ): ShareLinkPolicyResolver => ({
39
47
  resolve: async (profileId: string): Promise<ShareLinkPolicySnapshot> => {
40
48
  try {
41
- const [age, isManaged] = await Promise.all([
42
- source.resolveOwnerAge(profileId),
43
- source.isManaged(profileId),
44
- ]);
49
+ const { age, isManaged, isServiceProfile } = await source.resolveOwner(profileId);
45
50
 
46
- return composeShareLinkPolicy(age, isManaged);
51
+ return composeShareLinkPolicy(age, isManaged, isServiceProfile);
47
52
  } catch {
48
53
  return DEFAULT_SHARE_LINK_POLICY;
49
54
  }
@@ -12,26 +12,34 @@ export type ShareLinkOwnerAge = 'adult' | 'minor' | 'unknown';
12
12
 
13
13
  /** Immutable policy snapshot persisted with the share under the write lock. */
14
14
  export type ShareLinkPolicySnapshot = {
15
- /** `true` known minor, `false` known adult, `null` unknown. */
15
+ /** `true` known minor, `false` known adult/service profile, `null` unknown. */
16
16
  isMinor: boolean | null;
17
- /** `true` only when the age source returned a known adult/minor value. */
17
+ /** `true` when age is known or a persisted service profile is age-exempt. */
18
18
  policyResolved: boolean;
19
19
  defaultExpiryDays: 30 | 365;
20
20
  viewCountingEnabled: boolean;
21
21
  };
22
22
 
23
+ /** Fresh graph-local classification; never persisted or inferred from an old snapshot. */
24
+ export type CurrentShareLinkPolicy = ShareLinkPolicySnapshot & {
25
+ isServiceProfile: boolean;
26
+ };
27
+
23
28
  /**
24
29
  * Authoritative server-side sources. Implementations must read persisted server
25
30
  * state only; no request-derived value may be used as authority.
26
31
  */
27
32
  export type ShareLinkPolicySource = {
28
33
  /**
29
- * Resolve the owner's age. `unknown` is required when no trustworthy source
30
- * exists; it must never be inferred from the absence of a manager.
34
+ * Read age, service classification and management from persisted state.
35
+ * Service profiles may bypass tracking age checks; explicitly typed children
36
+ * must never be classified as service profiles here.
31
37
  */
32
- resolveOwnerAge: (profileId: string) => Promise<ShareLinkOwnerAge>;
33
- /** Whether the profile is currently managed by another profile. */
34
- isManaged: (profileId: string) => Promise<boolean>;
38
+ resolveOwner: (profileId: string) => Promise<{
39
+ age: ShareLinkOwnerAge;
40
+ isManaged: boolean;
41
+ isServiceProfile: boolean;
42
+ }>;
35
43
  };
36
44
 
37
45
  /** Injectable server-side policy resolver used by the coordinator. */
@@ -1,6 +1,10 @@
1
1
  import { environment } from '@environment';
2
2
  import { Agent, fetch as undiciFetch, type Response as UndiciResponse } from 'undici';
3
- import { getDidWebLearnCard, getLearnCard } from '@helpers/learnCard.helpers';
3
+ import {
4
+ createDagJweForRecipients,
5
+ getDidWebLearnCard,
6
+ getLearnCard,
7
+ } from '@helpers/learnCard.helpers';
4
8
  import { getDidWeb } from '@helpers/did.helpers';
5
9
  import { VCValidator, JWEValidator } from '@learncard/types';
6
10
  import type { UnsignedVC, VC, JWE } from '@learncard/types';
@@ -113,9 +117,17 @@ const getSaAgent = (): Agent => {
113
117
  return _saAgent;
114
118
  };
115
119
 
116
- const _mockIssueCredentialWithSigningAuthority = async (credential: UnsignedVC): Promise<VC> => {
120
+ const _mockIssueCredentialWithSigningAuthority = async (
121
+ credential: UnsignedVC,
122
+ recipients?: string[]
123
+ ): Promise<VC | JWE> => {
117
124
  const learnCard = await getLearnCard();
118
- return learnCard.invoke.issueCredential({ ...credential, issuer: learnCard.id.did() });
125
+ const signedCredential = await learnCard.invoke.issueCredential({
126
+ ...credential,
127
+ issuer: learnCard.id.did(),
128
+ });
129
+
130
+ return recipients ? createDagJweForRecipients(signedCredential, recipients) : signedCredential;
119
131
  };
120
132
 
121
133
  export async function issueCredentialWithSigningAuthority(
@@ -151,6 +163,10 @@ export async function issueCredentialWithSigningAuthority(
151
163
  retryable: false,
152
164
  });
153
165
  }
166
+ // The validation above excludes undefined subject IDs on the encrypted path.
167
+ const encryptionRecipients = encrypt
168
+ ? [...new Set([...(subjectIds as string[]), ownerDid, ...additionalEncryptionRecipients])]
169
+ : undefined;
154
170
  const credentialToIssue = appendCredentialStatus
155
171
  ? await appendBitstringStatusListEntries(credential, ownerProfile.profileId, domain)
156
172
  : credential;
@@ -177,7 +193,8 @@ export async function issueCredentialWithSigningAuthority(
177
193
  try {
178
194
  if (IS_TEST_ENVIRONMENT) {
179
195
  return await _mockIssueCredentialWithSigningAuthority(
180
- JSON.parse(serializedCredential)
196
+ JSON.parse(serializedCredential),
197
+ encryptionRecipients
181
198
  );
182
199
  }
183
200
 
@@ -189,16 +206,8 @@ export async function issueCredentialWithSigningAuthority(
189
206
  perf.mark('initDid');
190
207
 
191
208
  // Brain authenticates the request but must not be able to decrypt the response.
192
- const encryption = encrypt
193
- ? {
194
- recipients: [
195
- ...new Set([
196
- ...subjectIds,
197
- ownerDid,
198
- ...additionalEncryptionRecipients,
199
- ]),
200
- ],
201
- }
209
+ const encryption = encryptionRecipients
210
+ ? { recipients: encryptionRecipients }
202
211
  : undefined;
203
212
 
204
213
  const didJwt = await traceCrypto('getDidAuthVp', () =>
@@ -0,0 +1,123 @@
1
+ import { environment } from '@environment';
2
+ import { ConsentFlowTermsValidator, type ConsentFlowTerms } from '@learncard/types';
3
+ import { resolveUri } from '@helpers/uri.helpers';
4
+
5
+ /** Publish only the terms returned by the locked consent/publication claim. */
6
+ export const uploadSmartResume = async (
7
+ terms: ConsentFlowTerms,
8
+ did: string,
9
+ recipientToken: string
10
+ ): Promise<string | undefined> => {
11
+ const isProduction = !environment.IS_OFFLINE;
12
+
13
+ const srUrl = isProduction ? 'https://my.smartresume.com/' : 'https://mystage.smartresume.com/';
14
+ const clientId = environment.SMART_RESUME_CLIENT_ID;
15
+ const accessKey = environment.SMART_RESUME_ACCESS_KEY;
16
+
17
+ const accessTokenResponse = (await fetch(`${srUrl}api/v1/token`, {
18
+ method: 'POST',
19
+ signal: AbortSignal.timeout(30_000),
20
+ headers: {
21
+ 'Content-Type': 'application/x-www-form-urlencoded',
22
+ Authorization: `Basic ${btoa(`${clientId}:${accessKey}`)}`,
23
+ },
24
+ body: new URLSearchParams({
25
+ grant_type: 'client_credentials',
26
+ scope: 'delete readonly replace',
27
+ }),
28
+ }).then(res => {
29
+ if (!res.ok) throw new Error(`SmartResume token request failed (${res.status})`);
30
+ return res.json();
31
+ })) as { access_token?: string };
32
+
33
+ const accessToken = accessTokenResponse.access_token;
34
+ if (!accessToken) throw new Error('Missing access_token for SmartResume');
35
+
36
+ const parsedTerms = ConsentFlowTermsValidator.parse(terms);
37
+
38
+ const categories = parsedTerms.read.credentials.categories;
39
+ const categoryValues = Object.values(categories) as Array<{ shared?: string[] }>;
40
+
41
+ const allSharedCredentialUris = [
42
+ // filter out duplicates
43
+ ...new Set(categoryValues.flatMap(({ shared }) => shared ?? [])),
44
+ ];
45
+
46
+ const resolvedCredentials = await Promise.all(
47
+ allSharedCredentialUris.map(async uri => {
48
+ let timer: ReturnType<typeof setTimeout> | undefined;
49
+ try {
50
+ return await Promise.race([
51
+ resolveUri(uri),
52
+ new Promise<never>((_, reject) => {
53
+ timer = setTimeout(
54
+ () => reject(new Error('Credential resolution timed out')),
55
+ 30_000
56
+ );
57
+ }),
58
+ ]);
59
+ } finally {
60
+ clearTimeout(timer);
61
+ }
62
+ })
63
+ );
64
+
65
+ type ResolvedCredential = {
66
+ issuer?: string | { id: string };
67
+ id?: string;
68
+ boostCredential?: Record<string, unknown>;
69
+ } & Record<string, unknown>;
70
+
71
+ const credentials = resolvedCredentials
72
+ .filter((cred): cred is ResolvedCredential => typeof cred === 'object' && cred !== null)
73
+ .map(cred =>
74
+ cred.boostCredential && typeof cred.boostCredential === 'object'
75
+ ? ({ ...cred.boostCredential, id: cred.id } as ResolvedCredential) // unwrap credential, preserve id
76
+ : cred
77
+ );
78
+
79
+ const transformedCredentials = credentials.map(cred => {
80
+ const issuer =
81
+ typeof cred.issuer === 'string' ? { id: cred.issuer } : cred.issuer || { id: '' };
82
+
83
+ return {
84
+ ...cred,
85
+ issuer,
86
+ };
87
+ });
88
+
89
+ const { name, email } = parsedTerms.read.personal;
90
+
91
+ const body = JSON.stringify({
92
+ '@context': [
93
+ 'https://www.w3.org/ns/credentials/v2',
94
+ 'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
95
+ 'https://w3id.org/security/suites/ed25519-2020/v1',
96
+ ],
97
+ recipienttoken: recipientToken,
98
+ recipient: {
99
+ id: did,
100
+ givenName: name && name !== 'Anonymous' ? name : '',
101
+ familyName: '', // this is necessary in order for givenName to be respected
102
+ email: email && email !== 'anonymous@hidden.com' ? email : '',
103
+ },
104
+ credentials: transformedCredentials,
105
+ });
106
+
107
+ const response = await fetch(`${srUrl}api/v1/credentials`, {
108
+ method: 'POST',
109
+ signal: AbortSignal.timeout(30_000),
110
+ headers: {
111
+ Authorization: `Bearer ${accessToken}`,
112
+ 'Content-Type': 'application/json',
113
+ },
114
+ body,
115
+ });
116
+
117
+ if (!response.ok) {
118
+ throw new Error(`SmartResume upload failed (${response.status})`);
119
+ }
120
+
121
+ const result = (await response.json()) as { redirect_url?: string };
122
+ return result.redirect_url;
123
+ };
@@ -0,0 +1,43 @@
1
+ import { ModelFactory, ModelRelatedNodesI, NeogmaInstance } from 'neogma';
2
+
3
+ import { neogma } from '@instance';
4
+
5
+ import { FlatAppManifestVersionType } from 'types/app-manifest';
6
+ import { Integration, IntegrationInstance } from './Integration';
7
+ import { AppStoreListing, AppStoreListingInstance } from './AppStoreListing';
8
+
9
+ export type AppManifestVersionRelationships = {
10
+ integration: ModelRelatedNodesI<typeof Integration, IntegrationInstance>;
11
+ listing: ModelRelatedNodesI<typeof AppStoreListing, AppStoreListingInstance>;
12
+ };
13
+
14
+ export type AppManifestVersionInstance = NeogmaInstance<
15
+ FlatAppManifestVersionType,
16
+ AppManifestVersionRelationships
17
+ >;
18
+
19
+ export const AppManifestVersion = ModelFactory<
20
+ FlatAppManifestVersionType,
21
+ AppManifestVersionRelationships
22
+ >(
23
+ {
24
+ label: 'AppManifestVersion',
25
+ schema: {
26
+ id: { type: 'string', required: true, uniqueItems: true },
27
+ version: { type: 'number', required: true },
28
+ manifestHash: { type: 'string', required: true },
29
+ manifestJson: { type: 'string', required: true },
30
+ status: { type: 'string', enum: ['draft', 'active', 'superseded'], required: true },
31
+ createdAt: { type: 'string', required: true },
32
+ activatedAt: { type: 'string', required: false },
33
+ },
34
+ relationships: {
35
+ integration: { model: Integration, direction: 'in', name: 'HAS_MANIFEST_VERSION' },
36
+ listing: { model: AppStoreListing, direction: 'in', name: 'USES_MANIFEST_VERSION' },
37
+ },
38
+ primaryKeyField: 'id',
39
+ },
40
+ neogma
41
+ );
42
+
43
+ export default AppManifestVersion;
@@ -6,13 +6,16 @@ import { Integration, IntegrationInstance } from './Integration';
6
6
  import { Profile, ProfileInstance } from './Profile';
7
7
  import { Boost, BoostInstance } from './Boost';
8
8
  import { Credential, CredentialInstance } from './Credential';
9
+ import { ConsentFlowContract, ConsentFlowInstance } from './ConsentFlowContract';
9
10
  import { SigningAuthority, SigningAuthorityInstance } from './SigningAuthority';
11
+ import type { AppManifestVersion, AppManifestVersionInstance } from './AppManifestVersion';
10
12
  import {
11
13
  FlatAppStoreListingType,
12
14
  AppListingStatus,
13
15
  LaunchType,
14
16
  PromotionLevel,
15
17
  AgeRating,
18
+ PendingListingUpdateStatus,
16
19
  } from 'types/app-store-listing';
17
20
 
18
21
  export type AppStoreListingRelationships = {
@@ -73,6 +76,13 @@ export type AppStoreListingRelationships = {
73
76
  { name: string; did: string; isPrimary?: boolean },
74
77
  { name: string; did: string; isPrimary?: boolean }
75
78
  >;
79
+ hasConsentContract: ModelRelatedNodesI<
80
+ typeof ConsentFlowContract,
81
+ ConsentFlowInstance,
82
+ { scopeHash: string; createdAt: string },
83
+ { scopeHash: string; createdAt: string }
84
+ >;
85
+ usesManifestVersion: ModelRelatedNodesI<typeof AppManifestVersion, AppManifestVersionInstance>;
76
86
  };
77
87
 
78
88
  export type AppStoreListingInstance = NeogmaInstance<
@@ -114,6 +124,13 @@ export const AppStoreListing = ModelFactory<FlatAppStoreListingType, AppStoreLis
114
124
  min_age: { type: 'number', required: false },
115
125
  age_rating: { type: 'string', enum: AgeRating.options, required: false },
116
126
  contact_email: { type: 'string', required: false },
127
+ pending_update_json: { type: 'string', required: false },
128
+ pending_update_status: {
129
+ type: 'string',
130
+ enum: PendingListingUpdateStatus.options,
131
+ required: false,
132
+ },
133
+ pending_update_submitted_at: { type: 'string', required: false },
117
134
  } as any,
118
135
  relationships: {
119
136
  publishedBy: { model: Integration, direction: 'in', name: 'PUBLISHES_LISTING' },
@@ -204,6 +221,21 @@ export const AppStoreListing = ModelFactory<FlatAppStoreListingType, AppStoreLis
204
221
  },
205
222
  },
206
223
  },
224
+ hasConsentContract: {
225
+ model: ConsentFlowContract,
226
+ direction: 'out',
227
+ name: 'HAS_CONSENT_CONTRACT',
228
+ properties: {
229
+ scopeHash: {
230
+ property: 'scopeHash',
231
+ schema: { type: 'string', required: true },
232
+ },
233
+ createdAt: {
234
+ property: 'createdAt',
235
+ schema: { type: 'string', required: true },
236
+ },
237
+ },
238
+ },
207
239
  },
208
240
  primaryKeyField: 'listing_id',
209
241
  },
@@ -14,16 +14,27 @@ export type ConsentFlowRelationships = {
14
14
  { signingAuthorityEndpoint: string; signingAuthorityName: string; issuer?: string }
15
15
  >;
16
16
  canWrite: ModelRelatedNodesI<typeof Profile, ProfileInstance>;
17
+ sharesDataWith: ModelRelatedNodesI<typeof Profile, ProfileInstance>;
17
18
  relatedTo: ModelRelatedNodesI<typeof Boost, BoostInstance>;
18
19
  requestedFor: ModelRelatedNodesI<
19
20
  typeof Profile,
20
21
  ProfileInstance,
21
22
  {
22
- status: 'pending' | 'accepted' | 'denied';
23
+ status: 'pending' | 'accepted' | 'denied' | 'cancelled';
24
+ requestId?: string;
25
+ requestedBy?: string;
26
+ externalReferenceId?: string;
27
+ message?: string;
28
+ requestedAt?: string;
23
29
  readStatus?: 'unseen' | 'seen' | null;
24
30
  },
25
31
  {
26
- status: 'pending' | 'accepted' | 'denied';
32
+ status: 'pending' | 'accepted' | 'denied' | 'cancelled';
33
+ requestId?: string;
34
+ requestedBy?: string;
35
+ externalReferenceId?: string;
36
+ message?: string;
37
+ requestedAt?: string;
27
38
  readStatus?: 'unseen' | 'seen' | null;
28
39
  }
29
40
  >;
@@ -47,6 +58,8 @@ export const ConsentFlowContract = ModelFactory<FlatDbContractType, ConsentFlowR
47
58
  createdAt: { type: 'string', required: true },
48
59
  updatedAt: { type: 'string', required: true },
49
60
  expiresAt: { type: 'string', required: false },
61
+ audienceVersion: { type: 'number', required: false },
62
+ hasConsented: { type: 'boolean', required: false },
50
63
  } as any,
51
64
  relationships: {
52
65
  createdBy: { model: Profile, direction: 'out', name: 'CREATED_BY' },
@@ -70,17 +83,35 @@ export const ConsentFlowContract = ModelFactory<FlatDbContractType, ConsentFlowR
70
83
  },
71
84
  },
72
85
  canWrite: { model: Profile, direction: 'out', name: 'CAN_WRITE' },
86
+ sharesDataWith: { model: Profile, direction: 'out', name: 'SHARES_DATA_WITH' },
73
87
  relatedTo: { model: Boost, direction: 'out', name: 'RELATED_TO' },
74
88
  requestedFor: {
75
89
  model: Profile,
76
90
  direction: 'out',
77
91
  name: 'REQUESTED_FOR',
78
92
  properties: {
93
+ requestId: {
94
+ property: 'requestId',
95
+ schema: { type: 'string', required: false },
96
+ },
97
+ requestedBy: {
98
+ property: 'requestedBy',
99
+ schema: { type: 'string', required: false },
100
+ },
101
+ externalReferenceId: {
102
+ property: 'externalReferenceId',
103
+ schema: { type: 'string', required: false },
104
+ },
105
+ message: { property: 'message', schema: { type: 'string', required: false } },
106
+ requestedAt: {
107
+ property: 'requestedAt',
108
+ schema: { type: 'string', required: false },
109
+ },
79
110
  status: {
80
111
  property: 'status',
81
112
  schema: {
82
113
  type: 'string',
83
- enum: ['pending', 'accepted', 'denied'],
114
+ enum: ['pending', 'accepted', 'denied', 'cancelled'],
84
115
  required: false,
85
116
  },
86
117
  },
@@ -19,10 +19,20 @@ export const ConsentFlowTerms = ModelFactory<FlatDbTermsType, ConsentFlowTermsRe
19
19
  {
20
20
  label: 'ConsentFlowTerms',
21
21
  schema: {
22
+ smartResumeFingerprint: { type: 'string', required: false },
23
+ smartResumePublicationStatus: { type: 'string', required: false },
24
+ smartResumeLeaseId: { type: 'string', required: false },
25
+ smartResumeLeaseUntil: { type: 'number', required: false },
26
+ smartResumeMutationVersion: { type: 'number', required: false },
27
+ smartResumeRedirectUrl: { type: 'string', required: false },
22
28
  id: { type: 'string', required: true },
29
+ 'referral.requestId': { type: 'string', required: false },
30
+ 'referral.requestedBy': { type: 'string', required: false },
31
+ 'referral.externalReferenceId': { type: 'string', required: false },
23
32
  status: { type: 'string', required: true },
24
33
  createdAt: { type: 'string', required: false },
25
34
  updatedAt: { type: 'string', required: false },
35
+ mutationVersion: { type: 'number', required: false },
26
36
  expiresAt: { type: 'string', required: false },
27
37
  oneTime: { type: 'boolean', required: false },
28
38
  deniedWriters: { type: 'string[]', required: false },
@@ -21,6 +21,9 @@ export const ConsentFlowTransaction = ModelFactory<
21
21
  label: 'ConsentFlowTransaction',
22
22
  schema: {
23
23
  id: { type: 'string', required: true },
24
+ 'referral.requestId': { type: 'string', required: false },
25
+ 'referral.requestedBy': { type: 'string', required: false },
26
+ 'referral.externalReferenceId': { type: 'string', required: false },
24
27
  status: { type: 'string', required: true },
25
28
  expiresAt: { type: 'string', required: false },
26
29
  oneTime: { type: 'boolean', required: false },
@@ -3,11 +3,13 @@ import { ModelFactory, ModelRelatedNodesI, NeogmaInstance } from 'neogma';
3
3
  import { neogma } from '@instance';
4
4
 
5
5
  import { Profile, ProfileInstance } from './Profile';
6
+ import type { AppManifestVersion, AppManifestVersionInstance } from './AppManifestVersion';
6
7
  import { FlatIntegrationType } from 'types/integration';
7
8
 
8
9
  export type IntegrationRelationships = {
9
10
  createdBy: ModelRelatedNodesI<typeof Profile, ProfileInstance>;
10
11
  publishesListing: ModelRelatedNodesI<any, any>;
12
+ hasManifestVersion: ModelRelatedNodesI<typeof AppManifestVersion, AppManifestVersionInstance>;
11
13
  };
12
14
 
13
15
  export type IntegrationInstance = NeogmaInstance<FlatIntegrationType, IntegrationRelationships>;
@@ -33,6 +33,8 @@ export type ShareLinkRecord = {
33
33
  ownerProfileId: string;
34
34
  version: number;
35
35
  contentVersion: number;
36
+ attachmentId?: string | null;
37
+ attachmentChunkCount?: number | null;
36
38
  /**
37
39
  * Monotonic fence. Bumped on every reservation and on revoke; a worker may
38
40
  * only finalize the reservation whose generation still equals this value.
@@ -90,6 +92,8 @@ export const ShareLink = ModelFactory<ShareLinkProperties, Record<string, never>
90
92
  ownerProfileId: { type: 'string', required: true },
91
93
  version: { type: 'number', required: true },
92
94
  contentVersion: { type: 'number', required: true },
95
+ attachmentId: { type: 'string', required: false },
96
+ attachmentChunkCount: { type: 'number', required: false },
93
97
  generation: { type: 'number', required: true },
94
98
  status: { type: 'string', required: true, enum: ['pending', 'active', 'stopped'] },
95
99
  contentState: {
@@ -1,5 +1,6 @@
1
1
  import { environment } from '@environment';
2
2
  import { neogma } from '@instance';
3
+ import { contractEventMaintenanceSchema } from '@helpers/contract-event-maintenance.helpers';
3
4
 
4
5
  import { Boost } from './Boost';
5
6
  import { Profile } from './Profile';
@@ -11,6 +12,7 @@ import { Skill } from './Skill';
11
12
  import { Tag } from './Tag';
12
13
  import { AppStoreListing } from './AppStoreListing';
13
14
  import { Integration } from './Integration';
15
+ import { AppManifestVersion } from './AppManifestVersion';
14
16
  import { CredentialActivity } from './CredentialActivity';
15
17
  import { CredentialRefresh } from './CredentialRefresh';
16
18
  import { StatusList } from './StatusList';
@@ -92,6 +94,19 @@ Integration.addRelationships({
92
94
  direction: 'out',
93
95
  name: 'PUBLISHES_LISTING',
94
96
  },
97
+ hasManifestVersion: {
98
+ model: AppManifestVersion,
99
+ direction: 'out',
100
+ name: 'HAS_MANIFEST_VERSION',
101
+ },
102
+ });
103
+
104
+ AppStoreListing.addRelationships({
105
+ usesManifestVersion: {
106
+ model: AppManifestVersion,
107
+ direction: 'out',
108
+ name: 'USES_MANIFEST_VERSION',
109
+ },
95
110
  });
96
111
 
97
112
  Profile.addRelationships({
@@ -128,6 +143,9 @@ const shouldCreateIndices =
128
143
  environment.NODE_ENV === 'production' || !environment.NEO4J_SKIP_INDICES;
129
144
 
130
145
  const indexQueries = [
146
+ 'CREATE CONSTRAINT consent_event_id IF NOT EXISTS FOR (e:ConsentFlowEvent) REQUIRE e.id IS UNIQUE',
147
+ 'CREATE CONSTRAINT consent_delivery_id IF NOT EXISTS FOR (d:ConsentFlowEventDelivery) REQUIRE d.id IS UNIQUE',
148
+ ...contractEventMaintenanceSchema,
131
149
  'CREATE INDEX profileId_idx IF NOT EXISTS FOR (p:Profile) ON (p.profileId)',
132
150
  'CREATE INDEX profile_did_idx IF NOT EXISTS FOR (p:Profile) ON (p.did)',
133
151
  'CREATE TEXT INDEX profileId_text_idx IF NOT EXISTS FOR (n:Profile) ON (n.profileId)',
@@ -152,6 +170,10 @@ const indexQueries = [
152
170
  'CREATE INDEX claim_hook_updated_idx IF NOT EXISTS FOR (c:ClaimHook) ON (c.updatedAt)',
153
171
  'CREATE INDEX integration_id_idx IF NOT EXISTS FOR (i:Integration) ON (i.id)',
154
172
  'CREATE INDEX integration_publishablekey_idx IF NOT EXISTS FOR (i:Integration) ON (i.publishableKey)',
173
+ 'CREATE INDEX app_manifest_version_id_idx IF NOT EXISTS FOR (m:AppManifestVersion) ON (m.id)',
174
+ 'CREATE INDEX app_manifest_version_version_idx IF NOT EXISTS FOR (m:AppManifestVersion) ON (m.version)',
175
+ 'CREATE INDEX app_manifest_version_hash_idx IF NOT EXISTS FOR (m:AppManifestVersion) ON (m.manifestHash)',
176
+ 'CREATE INDEX app_manifest_version_status_idx IF NOT EXISTS FOR (m:AppManifestVersion) ON (m.status)',
155
177
  'CREATE INDEX skill_framework_id_idx IF NOT EXISTS FOR (f:SkillFramework) ON (f.id)',
156
178
  'CREATE TEXT INDEX skill_framework_name_text_idx IF NOT EXISTS FOR (f:SkillFramework) ON (f.name)',
157
179
  'CREATE INDEX skill_id_idx IF NOT EXISTS FOR (s:Skill) ON (s.id)',
@@ -254,6 +276,7 @@ export * from './SkillFramework';
254
276
  export * from './Skill';
255
277
  export * from './Tag';
256
278
  export * from './Integration';
279
+ export * from './AppManifestVersion';
257
280
  export * from './AppStoreListing';
258
281
  export * from './CredentialActivity';
259
282
  export * from './CredentialRefresh';
@@ -9,6 +9,9 @@ import { neogma } from '@instance';
9
9
  * makes the resulting race fail closed instead of forking state.
10
10
  */
11
11
  const SHARE_LINK_CONSTRAINT_QUERIES = [
12
+ 'CREATE CONSTRAINT share_link_attachment_stage_key_unique IF NOT EXISTS FOR (a:ShareLinkAttachmentStage) REQUIRE (a.key) IS UNIQUE',
13
+ 'CREATE CONSTRAINT share_link_attachment_id_unique IF NOT EXISTS FOR (a:ShareLinkAttachmentStage) REQUIRE (a.attachmentId) IS UNIQUE',
14
+ 'CREATE CONSTRAINT share_attachment_owner_quota_key_unique IF NOT EXISTS FOR (q:ShareAttachmentOwnerQuota) REQUIRE (q.key) IS UNIQUE',
12
15
  // A share id is globally unique across namespaces.
13
16
  'CREATE CONSTRAINT share_link_id_unique IF NOT EXISTS FOR (s:ShareLink) REQUIRE (s.id) IS UNIQUE',
14
17
  // One idempotency record per (namespace, owner, operation kind, client request).
@@ -26,6 +29,8 @@ const SHARE_LINK_CONSTRAINT_QUERIES = [
26
29
  * idempotent and shares the same readiness gate as the constraints.
27
30
  */
28
31
  const SHARE_LINK_INDEX_QUERIES = [
32
+ 'CREATE INDEX share_attachment_stage_owner_idx IF NOT EXISTS FOR (a:ShareLinkAttachmentStage) ON (a.namespace, a.ownerProfileId, a.status)',
33
+ 'CREATE INDEX share_attachment_stage_expiry_idx IF NOT EXISTS FOR (a:ShareLinkAttachmentStage) ON (a.namespace, a.status, a.expiresAt)',
29
34
  'CREATE INDEX share_link_ns_owner_idx IF NOT EXISTS FOR (s:ShareLink) ON (s.namespace, s.ownerProfileId)',
30
35
  // Bounded owner-list keyset pagination: filters on the leading
31
36
  // (namespace, ownerProfileId) prefix and orders by the immutable