@lucern/graph-primitives 1.0.29 → 1.0.30

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 (319) hide show
  1. package/dist/{beliefDecay-DZ6tkLYq.d.ts → beliefDecay-BmkEk5OJ.d.ts} +3 -3
  2. package/dist/beliefDecay.d.ts +1 -1
  3. package/dist/beliefDecay.js +448 -314
  4. package/dist/beliefDecay.js.map +1 -1
  5. package/dist/{beliefEvidenceLinks-CWOXxxJg.d.ts → beliefEvidenceLinks-BzfjON_6.d.ts} +13 -13
  6. package/dist/beliefEvidenceLinks.d.ts +1 -1
  7. package/dist/beliefEvidenceLinks.js +843 -624
  8. package/dist/beliefEvidenceLinks.js.map +1 -1
  9. package/dist/beliefEvidenceLinks.operational.d.ts +7 -5
  10. package/dist/beliefEvidenceLinks.operational.js +91 -18
  11. package/dist/beliefEvidenceLinks.operational.js.map +1 -1
  12. package/dist/beliefLifecycle.js.map +1 -1
  13. package/dist/confidencePropagationDispatch.d.ts +28 -27
  14. package/dist/confidencePropagationDispatch.js +157 -99
  15. package/dist/confidencePropagationDispatch.js.map +1 -1
  16. package/dist/{contradictions-51VLsESq.d.ts → contradictions-BATPuZTL.d.ts} +10 -10
  17. package/dist/contradictions.d.ts +1 -1
  18. package/dist/contradictions.js +395 -225
  19. package/dist/contradictions.js.map +1 -1
  20. package/dist/convex.d.ts +65 -30
  21. package/dist/convex.js +7 -3
  22. package/dist/convex.js.map +1 -1
  23. package/dist/debug.js.map +1 -1
  24. package/dist/edgeValidation.js +293 -85
  25. package/dist/edgeValidation.js.map +1 -1
  26. package/dist/edges/contains.d.ts +1 -1
  27. package/dist/edges/contains.js.map +1 -1
  28. package/dist/edges/contradicts.d.ts +1 -1
  29. package/dist/edges/contradicts.js.map +1 -1
  30. package/dist/edges/{dependsOn.d.ts → depends-on.d.ts} +1 -1
  31. package/dist/edges/{dependsOn.js → depends-on.js} +4 -4
  32. package/dist/edges/depends-on.js.map +1 -0
  33. package/dist/edges/{derivedFrom.d.ts → derived-from.d.ts} +1 -1
  34. package/dist/edges/{derivedFrom.js → derived-from.js} +3 -3
  35. package/dist/edges/derived-from.js.map +1 -0
  36. package/dist/edges/elaborates.d.ts +1 -1
  37. package/dist/edges/elaborates.js.map +1 -1
  38. package/dist/edges/index.d.ts +7 -3
  39. package/dist/edges/index.js +7 -4
  40. package/dist/edges/index.js.map +1 -1
  41. package/dist/edges/informs.d.ts +1 -1
  42. package/dist/edges/informs.js.map +1 -1
  43. package/dist/edges/{propagationTypes.d.ts → propagation-types.d.ts} +14 -14
  44. package/dist/edges/{propagationTypes.js → propagation-types.js} +3 -3
  45. package/dist/edges/propagation-types.js.map +1 -0
  46. package/dist/edges/refutes.d.ts +1 -1
  47. package/dist/edges/refutes.js.map +1 -1
  48. package/dist/edges/supports.d.ts +1 -1
  49. package/dist/edges/supports.js.map +1 -1
  50. package/dist/edges/tests.d.ts +1 -1
  51. package/dist/edges/tests.js.map +1 -1
  52. package/dist/edges/utils.d.ts +1 -1
  53. package/dist/edges/utils.js.map +1 -1
  54. package/dist/embeddingTrigger.d.ts +14 -6
  55. package/dist/embeddingTrigger.js +11 -14
  56. package/dist/embeddingTrigger.js.map +1 -1
  57. package/dist/{entityBridge-DMaKooYn.d.ts → entityBridge-BhVDM3pc.d.ts} +5 -5
  58. package/dist/entityBridge.d.ts +1 -1
  59. package/dist/entityBridge.js +602 -225
  60. package/dist/entityBridge.js.map +1 -1
  61. package/dist/entityCanonicalMatch.d.ts +14 -12
  62. package/dist/entityCanonicalMatch.js.map +1 -1
  63. package/dist/{entityLifecycle-CvgSK5FV.d.ts → entityLifecycle-BsfCz9pS.d.ts} +5 -9
  64. package/dist/entityLifecycle.d.ts +1 -1
  65. package/dist/entityLifecycle.js +854 -480
  66. package/dist/entityLifecycle.js.map +1 -1
  67. package/dist/{entityValidation-KLZ_Xl2D.d.ts → entityValidation-B1yNEHJx.d.ts} +7 -6
  68. package/dist/entityValidation.d.ts +3 -1
  69. package/dist/entityValidation.js +60 -8
  70. package/dist/entityValidation.js.map +1 -1
  71. package/dist/{epistemicAnswers-C5ib4z6_.d.ts → epistemicAnswers-f47YMu9U.d.ts} +6 -6
  72. package/dist/epistemicAnswers.d.ts +1 -1
  73. package/dist/epistemicAnswers.js +587 -545
  74. package/dist/epistemicAnswers.js.map +1 -1
  75. package/dist/epistemicBeliefs.admin.d.ts +8 -8
  76. package/dist/epistemicBeliefs.admin.js +365 -166
  77. package/dist/epistemicBeliefs.admin.js.map +1 -1
  78. package/dist/epistemicBeliefs.backfills.d.ts +8 -8
  79. package/dist/epistemicBeliefs.backfills.js +655 -289
  80. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  81. package/dist/epistemicBeliefs.confidence.d.ts +19 -15
  82. package/dist/epistemicBeliefs.confidence.js +633 -386
  83. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  84. package/dist/epistemicBeliefs.core.d.ts +6 -6
  85. package/dist/epistemicBeliefs.core.js +717 -371
  86. package/dist/epistemicBeliefs.core.js.map +1 -1
  87. package/dist/epistemicBeliefs.d.ts +11 -9
  88. package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
  89. package/dist/epistemicBeliefs.forkEvidence.js +8 -8
  90. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
  91. package/dist/epistemicBeliefs.helpers.d.ts +68 -49
  92. package/dist/epistemicBeliefs.helpers.js +358 -211
  93. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  94. package/dist/epistemicBeliefs.internal.d.ts +5 -5
  95. package/dist/epistemicBeliefs.internal.js +1248 -1026
  96. package/dist/epistemicBeliefs.internal.js.map +1 -1
  97. package/dist/epistemicBeliefs.js +4942 -3590
  98. package/dist/epistemicBeliefs.js.map +1 -1
  99. package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
  100. package/dist/epistemicBeliefs.lifecycle.js +1138 -781
  101. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  102. package/dist/epistemicBeliefs.links.d.ts +7 -7
  103. package/dist/epistemicBeliefs.links.js +404 -267
  104. package/dist/epistemicBeliefs.links.js.map +1 -1
  105. package/dist/epistemicBeliefs.queries.d.ts +4 -4
  106. package/dist/epistemicBeliefs.queries.js +175 -20
  107. package/dist/epistemicBeliefs.queries.js.map +1 -1
  108. package/dist/epistemicBeliefs.topicAnchor.d.ts +6 -4
  109. package/dist/epistemicBeliefs.topicAnchor.js +12 -5
  110. package/dist/epistemicBeliefs.topicAnchor.js.map +1 -1
  111. package/dist/epistemicContracts.d.ts +28 -3
  112. package/dist/epistemicContracts.evaluators.d.ts +2 -0
  113. package/dist/epistemicContracts.evaluators.js +1062 -576
  114. package/dist/epistemicContracts.evaluators.js.map +1 -1
  115. package/dist/epistemicContracts.handlers.d.ts +15 -32
  116. package/dist/epistemicContracts.handlers.js +1829 -1351
  117. package/dist/epistemicContracts.handlers.js.map +1 -1
  118. package/dist/epistemicContracts.js +1131 -636
  119. package/dist/epistemicContracts.js.map +1 -1
  120. package/dist/epistemicContracts.metrics.d.ts +2 -0
  121. package/dist/epistemicContracts.metrics.js +375 -158
  122. package/dist/epistemicContracts.metrics.js.map +1 -1
  123. package/dist/epistemicContracts.types.d.ts +87 -81
  124. package/dist/epistemicEdgeCreation.d.ts +2 -0
  125. package/dist/epistemicEdgeCreation.js +87 -16
  126. package/dist/epistemicEdgeCreation.js.map +1 -1
  127. package/dist/{epistemicEdges-BF-cn4i3.d.ts → epistemicEdges-BGBh0QSP.d.ts} +4 -7
  128. package/dist/epistemicEdges.d.ts +6 -5
  129. package/dist/epistemicEdges.handlers.d.ts +3 -3
  130. package/dist/epistemicEdges.handlers.js +129 -24
  131. package/dist/epistemicEdges.handlers.js.map +1 -1
  132. package/dist/epistemicEdges.helpers.d.ts +6 -4
  133. package/dist/epistemicEdges.helpers.js +37 -2
  134. package/dist/epistemicEdges.helpers.js.map +1 -1
  135. package/dist/epistemicEdges.js +1966 -1202
  136. package/dist/epistemicEdges.js.map +1 -1
  137. package/dist/epistemicEdges.mutations.d.ts +7 -7
  138. package/dist/epistemicEdges.mutations.js +956 -579
  139. package/dist/epistemicEdges.mutations.js.map +1 -1
  140. package/dist/epistemicEdges.queries.d.ts +16 -16
  141. package/dist/epistemicEdges.queries.js +639 -367
  142. package/dist/epistemicEdges.queries.js.map +1 -1
  143. package/dist/epistemicEdges.types.d.ts +10 -8
  144. package/dist/epistemicEvidence.d.ts +4 -1
  145. package/dist/epistemicEvidence.js +933 -532
  146. package/dist/epistemicEvidence.js.map +1 -1
  147. package/dist/epistemicEvidenceHelpers.d.ts +26 -10
  148. package/dist/epistemicEvidenceHelpers.js +239 -200
  149. package/dist/epistemicEvidenceHelpers.js.map +1 -1
  150. package/dist/epistemicEvidenceMutations.d.ts +8 -8
  151. package/dist/epistemicEvidenceMutations.js +840 -692
  152. package/dist/epistemicEvidenceMutations.js.map +1 -1
  153. package/dist/epistemicEvidenceQueries.d.ts +8 -8
  154. package/dist/epistemicEvidenceQueries.js +514 -238
  155. package/dist/epistemicEvidenceQueries.js.map +1 -1
  156. package/dist/epistemicHelpers.d.ts +4 -2
  157. package/dist/epistemicHelpers.js +308 -134
  158. package/dist/epistemicHelpers.js.map +1 -1
  159. package/dist/epistemicInsert.d.ts +16 -4
  160. package/dist/epistemicInsert.js +6 -3
  161. package/dist/epistemicInsert.js.map +1 -1
  162. package/dist/epistemicLayerRules.d.ts +10 -8
  163. package/dist/epistemicLayerRules.js +1 -5
  164. package/dist/epistemicLayerRules.js.map +1 -1
  165. package/dist/{epistemicLinking-CfE00tHJ.d.ts → epistemicLinking-CsCDv2cN.d.ts} +3 -3
  166. package/dist/epistemicLinking.d.ts +1 -1
  167. package/dist/epistemicLinking.js +177 -100
  168. package/dist/epistemicLinking.js.map +1 -1
  169. package/dist/epistemicNodeCreation.d.ts +2 -0
  170. package/dist/epistemicNodeCreation.js +203 -40
  171. package/dist/epistemicNodeCreation.js.map +1 -1
  172. package/dist/{epistemicNodes-BCQxpYx_.d.ts → epistemicNodes-CokAgBHg.d.ts} +3 -3
  173. package/dist/epistemicNodes.d.ts +3 -3
  174. package/dist/epistemicNodes.helpers.d.ts +24 -15
  175. package/dist/epistemicNodes.helpers.js.map +1 -1
  176. package/dist/epistemicNodes.internal.d.ts +6 -6
  177. package/dist/epistemicNodes.internal.js +389 -319
  178. package/dist/epistemicNodes.internal.js.map +1 -1
  179. package/dist/epistemicNodes.js +700 -504
  180. package/dist/epistemicNodes.js.map +1 -1
  181. package/dist/epistemicNodes.mutations.d.ts +6 -6
  182. package/dist/epistemicNodes.mutations.js +560 -463
  183. package/dist/epistemicNodes.mutations.js.map +1 -1
  184. package/dist/epistemicNodes.queries.d.ts +8 -8
  185. package/dist/epistemicNodes.queries.js +311 -314
  186. package/dist/epistemicNodes.queries.js.map +1 -1
  187. package/dist/epistemicNodes.validators.d.ts +2 -2
  188. package/dist/epistemicNodes.validators.js.map +1 -1
  189. package/dist/epistemicQuestions.conviction.d.ts +8 -8
  190. package/dist/epistemicQuestions.conviction.js +665 -484
  191. package/dist/epistemicQuestions.conviction.js.map +1 -1
  192. package/dist/epistemicQuestions.create.d.ts +4 -4
  193. package/dist/epistemicQuestions.create.js +640 -612
  194. package/dist/epistemicQuestions.create.js.map +1 -1
  195. package/dist/epistemicQuestions.d.ts +8 -5
  196. package/dist/epistemicQuestions.evidence.d.ts +2 -2
  197. package/dist/epistemicQuestions.evidence.js +475 -383
  198. package/dist/epistemicQuestions.evidence.js.map +1 -1
  199. package/dist/epistemicQuestions.helpers.d.ts +125 -24
  200. package/dist/epistemicQuestions.helpers.js +240 -209
  201. package/dist/epistemicQuestions.helpers.js.map +1 -1
  202. package/dist/epistemicQuestions.js +3474 -2823
  203. package/dist/epistemicQuestions.js.map +1 -1
  204. package/dist/epistemicQuestions.lifecycle.d.ts +2 -2
  205. package/dist/epistemicQuestions.lifecycle.js +607 -546
  206. package/dist/epistemicQuestions.lifecycle.js.map +1 -1
  207. package/dist/epistemicQuestions.queries.d.ts +12 -7
  208. package/dist/epistemicQuestions.queries.js +305 -244
  209. package/dist/epistemicQuestions.queries.js.map +1 -1
  210. package/dist/epistemicQuestions.sprint.d.ts +2 -2
  211. package/dist/epistemicQuestions.sprint.js +600 -394
  212. package/dist/epistemicQuestions.sprint.js.map +1 -1
  213. package/dist/epistemicQuestions.tail.d.ts +6 -6
  214. package/dist/epistemicQuestions.tail.js +572 -433
  215. package/dist/epistemicQuestions.tail.js.map +1 -1
  216. package/dist/{epistemicSources-dlKj58Jp.d.ts → epistemicSources-DQtaEkWs.d.ts} +4 -4
  217. package/dist/epistemicSources.d.ts +1 -1
  218. package/dist/epistemicSources.js +351 -311
  219. package/dist/epistemicSources.js.map +1 -1
  220. package/dist/evaluators/index.d.ts +8 -6
  221. package/dist/evaluators/index.js +399 -167
  222. package/dist/evaluators/index.js.map +1 -1
  223. package/dist/evaluators/lint-checker-evaluator.d.ts +16 -0
  224. package/dist/evaluators/{lintCheckerEvaluator.js → lint-checker-evaluator.js} +10 -5
  225. package/dist/evaluators/lint-checker-evaluator.js.map +1 -0
  226. package/dist/evaluators/{sentryCheckerEvaluator.d.ts → sentry-checker-evaluator.d.ts} +7 -2
  227. package/dist/evaluators/{sentryCheckerEvaluator.js → sentry-checker-evaluator.js} +3 -3
  228. package/dist/evaluators/sentry-checker-evaluator.js.map +1 -0
  229. package/dist/evaluators/shared.d.ts +2 -2
  230. package/dist/evaluators/shared.js +3 -1
  231. package/dist/evaluators/shared.js.map +1 -1
  232. package/dist/evaluators/{testRunnerEvaluator.d.ts → test-runner-evaluator.d.ts} +6 -1
  233. package/dist/evaluators/{testRunnerEvaluator.js → test-runner-evaluator.js} +6 -4
  234. package/dist/evaluators/test-runner-evaluator.js.map +1 -0
  235. package/dist/evaluators/tsc-checker-evaluator.d.ts +16 -0
  236. package/dist/evaluators/{tscCheckerEvaluator.js → tsc-checker-evaluator.js} +10 -5
  237. package/dist/evaluators/tsc-checker-evaluator.js.map +1 -0
  238. package/dist/graphTypes.js +6 -2
  239. package/dist/graphTypes.js.map +1 -1
  240. package/dist/helpers.d.ts +2 -0
  241. package/dist/helpers.js +313 -93
  242. package/dist/helpers.js.map +1 -1
  243. package/dist/{index-C-Kyd7hD.d.ts → index-DZxyC9Pb.d.ts} +7 -6
  244. package/dist/index.d.ts +86 -83
  245. package/dist/index.js +16914 -11760
  246. package/dist/index.js.map +1 -1
  247. package/dist/invariantEnforcement.d.ts +3 -3
  248. package/dist/invariantEnforcement.js.map +1 -1
  249. package/dist/logicalRoleInference.d.ts +2 -0
  250. package/dist/logicalRoleInference.js +1 -1
  251. package/dist/logicalRoleInference.js.map +1 -1
  252. package/dist/matcherFeedbackUtils.d.ts +2 -2
  253. package/dist/matcherFeedbackUtils.js.map +1 -1
  254. package/dist/{ontology-matching-C6rrz2VP.d.ts → ontology-matching-C-mYFrir.d.ts} +16 -16
  255. package/dist/ontology-matching.d.ts +1 -1
  256. package/dist/{ontologyApproval-CFYmqKmk.d.ts → ontologyApproval-BVt0feJi.d.ts} +10 -10
  257. package/dist/ontologyApproval.d.ts +1 -1
  258. package/dist/ontologyApproval.js +7 -1
  259. package/dist/ontologyApproval.js.map +1 -1
  260. package/dist/ontologyDefinitions.d.ts +14 -24
  261. package/dist/ontologyDefinitions.js +269 -34
  262. package/dist/ontologyDefinitions.js.map +1 -1
  263. package/dist/ontologyHelpers.d.ts +13 -13
  264. package/dist/ontologyHelpers.js.map +1 -1
  265. package/dist/{ontologyRegistry-B67rPJ16.d.ts → ontologyRegistry-CljS-ENv.d.ts} +2 -2
  266. package/dist/ontologyRegistry.d.ts +1 -1
  267. package/dist/ontologyRegistry.js +34 -6
  268. package/dist/ontologyRegistry.js.map +1 -1
  269. package/dist/{projectionReconciliation-jww2fBI0.d.ts → projectionReconciliation-DnrSgHSQ.d.ts} +4 -4
  270. package/dist/projectionReconciliation.d.ts +1 -1
  271. package/dist/projectionReconciliation.js +57 -10
  272. package/dist/projectionReconciliation.js.map +1 -1
  273. package/dist/{projectionStaleness-CmdbpjVK.d.ts → projectionStaleness-C8ImQ2zP.d.ts} +17 -17
  274. package/dist/projectionStaleness.d.ts +1 -1
  275. package/dist/projectionStaleness.js +8 -2
  276. package/dist/projectionStaleness.js.map +1 -1
  277. package/dist/proof-attestation.json +1 -1
  278. package/dist/{questionEvidenceLinks-DFlyPpAj.d.ts → questionEvidenceLinks-_nPRa-LY.d.ts} +10 -10
  279. package/dist/questionEvidenceLinks.d.ts +1 -1
  280. package/dist/questionEvidenceLinks.js +564 -347
  281. package/dist/questionEvidenceLinks.js.map +1 -1
  282. package/dist/{resolverTypes-CC8Ea2E2.d.ts → resolverTypes-BOXPxLET.d.ts} +8 -7
  283. package/dist/resolverTypes.d.ts +4 -2
  284. package/dist/{resolvers-Br1a6eLV.d.ts → resolvers-B1TIBmRO.d.ts} +3 -1
  285. package/dist/resolvers.d.ts +5 -3
  286. package/dist/resolvers.js +121 -77
  287. package/dist/resolvers.js.map +1 -1
  288. package/dist/scopeResolverCompat.d.ts +10 -7
  289. package/dist/scopeResolverCompat.js +106 -123
  290. package/dist/scopeResolverCompat.js.map +1 -1
  291. package/dist/{text-matching-DNg4M5Wd.d.ts → text-matching-DzFooju6.d.ts} +7 -7
  292. package/dist/text-matching.d.ts +1 -1
  293. package/dist/topicOntologyResolver.d.ts +22 -21
  294. package/dist/topicOntologyResolver.js +54 -32
  295. package/dist/topicOntologyResolver.js.map +1 -1
  296. package/dist/topicProjectOverlay.d.ts +30 -20
  297. package/dist/topicProjectOverlay.js +120 -76
  298. package/dist/topicProjectOverlay.js.map +1 -1
  299. package/dist/{topicScope-7zhyeGl7.d.ts → topicScope-DJVa0mLa.d.ts} +22 -7
  300. package/dist/topicScope.d.ts +3 -1
  301. package/dist/topicScope.js +104 -119
  302. package/dist/topicScope.js.map +1 -1
  303. package/dist/workflowBridge.d.ts +26 -15
  304. package/dist/workflowBridge.js +140 -144
  305. package/dist/workflowBridge.js.map +1 -1
  306. package/dist/workspaceIsolation.d.ts +14 -12
  307. package/dist/workspaceIsolation.js +108 -122
  308. package/dist/workspaceIsolation.js.map +1 -1
  309. package/package.json +4 -4
  310. package/dist/edges/dependsOn.js.map +0 -1
  311. package/dist/edges/derivedFrom.js.map +0 -1
  312. package/dist/edges/propagationTypes.js.map +0 -1
  313. package/dist/evaluators/lintCheckerEvaluator.d.ts +0 -11
  314. package/dist/evaluators/lintCheckerEvaluator.js.map +0 -1
  315. package/dist/evaluators/sentryCheckerEvaluator.js.map +0 -1
  316. package/dist/evaluators/testRunnerEvaluator.js.map +0 -1
  317. package/dist/evaluators/tscCheckerEvaluator.d.ts +0 -11
  318. package/dist/evaluators/tscCheckerEvaluator.js.map +0 -1
  319. package/dist/{epistemicQuestions-bwHd2FWE.d.ts → epistemicQuestions-Do1fhYm5.d.ts} +4 -4
@@ -1,39 +1,125 @@
1
- import { v } from 'convex/values';
2
1
  import { requireScopeWriteAccess } from '@lucern/access-control/access';
3
- import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
4
2
  import { getCurrentUserId } from '@lucern/access-control/auth';
3
+ import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
5
4
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
6
- import { componentsGeneric, anyApi, queryGeneric, mutationGeneric } from 'convex/server';
7
- import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
5
+ import { v } from 'convex/values';
6
+ import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
7
+ import { componentsGeneric, queryGeneric, mutationGeneric } from 'convex/server';
8
8
  import { assertEdgePolicyAllowed, edgePolicyManifest } from '@lucern/contracts';
9
+ import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
9
10
 
10
11
  // src/entityLifecycle.ts
11
- var api = anyApi;
12
+ var unsafeApi = unsafeConvexAnyApi(
13
+ "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
14
+ );
15
+ var api = unsafeApi;
12
16
  componentsGeneric();
13
- var internal = anyApi;
17
+ var internal = unsafeApi;
14
18
  var mutation = mutationGeneric;
15
19
  var query = queryGeneric;
16
20
 
21
+ // src/entityCanonicalMatch.ts
22
+ function normalizeCanonicalEntityText(value) {
23
+ return value.trim().toLowerCase().replace(/\s+/g, " ");
24
+ }
25
+ function buildEntityTitle(canonicalText) {
26
+ return canonicalText.slice(0, 100) + (canonicalText.length > 100 ? "..." : "");
27
+ }
28
+ function matchesCanonicalEntityRecord(node, args) {
29
+ if (node.epistemicLayer !== "ontological") {
30
+ return false;
31
+ }
32
+ if (node.workspaceId !== void 0) {
33
+ return false;
34
+ }
35
+ if (args.nodeType && node.nodeType !== args.nodeType) {
36
+ return false;
37
+ }
38
+ if (args.contentHash && node.contentHash !== args.contentHash) {
39
+ return false;
40
+ }
41
+ const rowTenantId = typeof node.tenantId === "string" && node.tenantId.trim().length > 0 ? node.tenantId.trim() : void 0;
42
+ if (rowTenantId !== args.tenantId) {
43
+ return false;
44
+ }
45
+ if (args.includeArchived) {
46
+ return node.status !== "deleted";
47
+ }
48
+ return node.status === "active";
49
+ }
50
+
17
51
  // src/entityValidation.ts
52
+ function isObjectLike(value) {
53
+ return value !== null && typeof value === "object";
54
+ }
55
+ function isEntitySchemaConfigField(value) {
56
+ if (!isObjectLike(value)) {
57
+ return false;
58
+ }
59
+ const required = value.required;
60
+ const type = value.type;
61
+ return typeof required === "boolean" && (type === "string" || type === "number" || type === "boolean");
62
+ }
63
+ function readEntitySchema(value) {
64
+ if (!isObjectLike(value)) {
65
+ return {};
66
+ }
67
+ const schema = {};
68
+ for (const [fieldName, fieldValue] of Object.entries(value)) {
69
+ if (!isEntitySchemaConfigField(fieldValue)) {
70
+ continue;
71
+ }
72
+ schema[fieldName] = fieldValue;
73
+ }
74
+ return schema;
75
+ }
76
+ function readSchemaEnumConfigRow(row) {
77
+ if (!isObjectLike(row)) {
78
+ return null;
79
+ }
80
+ const value = row.value;
81
+ if (typeof value !== "string") {
82
+ return null;
83
+ }
84
+ return {
85
+ value,
86
+ status: typeof row.status === "string" ? row.status : void 0,
87
+ metadata: isObjectLike(row.metadata) ? row.metadata : void 0
88
+ };
89
+ }
90
+ function readEntitySchemaFromConfig(row) {
91
+ const parsed = readSchemaEnumConfigRow(row);
92
+ if (parsed?.status !== "active" || !parsed?.metadata) {
93
+ return null;
94
+ }
95
+ const schema = parsed.metadata.schema;
96
+ if (!schema) {
97
+ return null;
98
+ }
99
+ return readEntitySchema(schema);
100
+ }
18
101
  async function getEntityTypeSchema(ctx, nodeType, tenantId) {
19
102
  if (tenantId) {
20
- const tenantEntry = await ctx.db.query("schemaEnumConfig").withIndex(
103
+ const tenantEntries = await ctx.db.query("schemaEnumConfig").withIndex(
21
104
  "by_tenant_category",
22
105
  (q) => q.eq("tenantId", tenantId).eq("category", "entity_type")
23
106
  ).collect();
24
- const tenantMatch = tenantEntry.find(
25
- (e) => e.value === nodeType && e.status === "active"
26
- );
27
- if (tenantMatch?.metadata?.schema) {
28
- return tenantMatch.metadata.schema;
107
+ const tenantMatch = tenantEntries.find((entry) => {
108
+ const parsed = readSchemaEnumConfigRow(entry);
109
+ return parsed !== null && parsed.value === nodeType && parsed.status === "active";
110
+ });
111
+ const tenantSchema = readEntitySchemaFromConfig(tenantMatch);
112
+ if (tenantSchema !== null) {
113
+ return tenantSchema;
29
114
  }
30
115
  }
31
116
  const platformEntry = await ctx.db.query("schemaEnumConfig").withIndex(
32
117
  "by_category_value",
33
118
  (q) => q.eq("category", "entity_type").eq("value", nodeType)
34
119
  ).first();
35
- if (platformEntry?.metadata?.schema && platformEntry.status === "active") {
36
- return platformEntry.metadata.schema;
120
+ const platformSchema = readEntitySchemaFromConfig(platformEntry);
121
+ if (platformSchema !== null) {
122
+ return platformSchema;
37
123
  }
38
124
  return null;
39
125
  }
@@ -77,99 +163,54 @@ async function validateEntityMetadata(ctx, nodeType, metadata, tenantId) {
77
163
  errors
78
164
  };
79
165
  }
80
-
81
- // src/entityCanonicalMatch.ts
82
- function normalizeCanonicalEntityText(value) {
83
- return value.trim().toLowerCase().replace(/\s+/g, " ");
84
- }
85
- function buildEntityTitle(canonicalText) {
86
- return canonicalText.slice(0, 100) + (canonicalText.length > 100 ? "..." : "");
87
- }
88
- function matchesCanonicalEntityRecord(node, args) {
89
- if (node.epistemicLayer !== "ontological") {
90
- return false;
91
- }
92
- if (node.workspaceId !== void 0) {
93
- return false;
94
- }
95
- if (args.nodeType && node.nodeType !== args.nodeType) {
96
- return false;
97
- }
98
- if (args.contentHash && node.contentHash !== args.contentHash) {
99
- return false;
100
- }
101
- const rowTenantId = typeof node.tenantId === "string" && node.tenantId.trim().length > 0 ? node.tenantId.trim() : void 0;
102
- if (rowTenantId !== args.tenantId) {
103
- return false;
104
- }
105
- if (args.includeArchived) {
106
- return node.status !== "deleted";
107
- }
108
- return node.status === "active";
166
+ function insertEpistemicNode(ctx, doc) {
167
+ assertUuidV7Identity("epistemicNodes", doc.globalId);
168
+ return ctx.db.insert("epistemicNodes", doc);
109
169
  }
110
-
111
- // src/topicOntologyResolver.ts
112
- var MAX_RESOLUTION_DEPTH = 10;
113
- async function resolveTopicOntologyInternal(ctx, topicId) {
114
- let current = await ctx.db.get(topicId);
115
- if (!current) {
116
- return null;
117
- }
118
- const startTopicId = topicId;
119
- for (let i = 0; i < MAX_RESOLUTION_DEPTH && current; i++) {
120
- if (current.ontologyId) {
121
- const ontologyDef = await ctx.db.get(
122
- current.ontologyId
123
- );
124
- if (!ontologyDef || ontologyDef.status === "archived") {
125
- if (current.parentTopicId) {
126
- current = await ctx.db.get(
127
- current.parentTopicId
128
- );
129
- continue;
130
- }
131
- return null;
132
- }
133
- const versions = await ctx.db.query("ontologyVersions").withIndex(
134
- "by_ontologyId",
135
- (q) => q.eq("ontologyId", current?.ontologyId)
136
- ).collect();
137
- const published = versions.filter((v3) => v3.status === "published").sort((a, b) => (b.publishedAt ?? 0) - (a.publishedAt ?? 0));
138
- const latestPublished = published[0] ?? null;
139
- return {
140
- ontologyId: ontologyDef._id,
141
- ontologyKey: ontologyDef.ontologyKey,
142
- ontologyName: ontologyDef.name,
143
- tier: ontologyDef.tier,
144
- source: current._id === startTopicId ? "direct" : "inherited",
145
- sourceTopicId: current._id,
146
- validEntityTypes: latestPublished ? latestPublished.entityTypes.map((et) => et.value) : [],
147
- validEdgeTypes: latestPublished ? latestPublished.edgeTypes.map((et) => et.value) : [],
148
- publishedVersion: latestPublished
149
- };
150
- }
151
- if (!current.parentTopicId) {
152
- break;
153
- }
154
- current = await ctx.db.get(current.parentTopicId);
170
+ async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
171
+ assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
172
+ const node = await ctx.db.query("epistemicNodes").withIndex(
173
+ "by_globalId",
174
+ (q) => q.eq("globalId", endpoint)
175
+ ).first();
176
+ if (!node) {
177
+ throw new Error(
178
+ `edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
179
+ );
155
180
  }
156
- return null;
157
181
  }
158
- async function validateEntityTypeForTopic(ctx, topicId, nodeType) {
159
- const resolved = await resolveTopicOntologyInternal(ctx, topicId);
160
- if (!resolved) {
161
- return { valid: true };
182
+ async function insertEpistemicEdge(ctx, doc) {
183
+ assertUuidV7Identity("epistemicEdges", doc.globalId);
184
+ assertStorageEdgeVocabulary(doc.edgeType);
185
+ if (!doc.fromNodeId || typeof doc.fromNodeId !== "string") {
186
+ throw new Error(
187
+ "edge_endpoint_missing: epistemicEdges insert requires a non-empty fromNodeId"
188
+ );
162
189
  }
163
- if (resolved.validEntityTypes.length === 0) {
164
- return { valid: true };
190
+ if (!doc.toNodeId || typeof doc.toNodeId !== "string") {
191
+ throw new Error(
192
+ "edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId"
193
+ );
165
194
  }
166
- if (resolved.validEntityTypes.includes(nodeType)) {
167
- return { valid: true };
195
+ await assertExistingNodeEndpoint(ctx, "fromNodeId", doc.fromNodeId);
196
+ await assertExistingNodeEndpoint(ctx, "toNodeId", doc.toNodeId);
197
+ if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== "extracted_from") {
198
+ assertEdgePolicyAllowed(
199
+ edgePolicyManifest,
200
+ doc.edgeType,
201
+ {
202
+ kind: "epistemic_node",
203
+ nodeId: doc.fromNodeId,
204
+ nodeType: doc.fromNodeType
205
+ },
206
+ {
207
+ kind: "epistemic_node",
208
+ nodeId: doc.toNodeId,
209
+ nodeType: doc.toNodeType
210
+ }
211
+ );
168
212
  }
169
- return {
170
- valid: false,
171
- error: `Entity type "${nodeType}" is not defined in the ontology "${resolved.ontologyKey}" (${resolved.ontologyName}). Valid entity types: ${resolved.validEntityTypes.join(", ")}. Source: ${resolved.source} from topic ${resolved.sourceTopicId}.`
172
- };
213
+ return ctx.db.insert("epistemicEdges", doc);
173
214
  }
174
215
 
175
216
  // src/debug.ts
@@ -202,6 +243,10 @@ function readStringArray(value) {
202
243
  function readMetadata(topic) {
203
244
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
204
245
  }
246
+ function omitMetadataKey(metadata, key) {
247
+ const { [key]: _omitted, ...rest } = metadata;
248
+ return rest;
249
+ }
205
250
  function readLegacyProjectId(value) {
206
251
  if (!value) {
207
252
  return;
@@ -282,9 +327,12 @@ async function resolveTopicDoc(ctx, scopeId) {
282
327
  );
283
328
  }
284
329
  try {
285
- const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
286
- projectId: String(scopeId)
287
- });
330
+ const topic = await ctx.runQuery(
331
+ api.topics.getByLegacyScopeId,
332
+ {
333
+ projectId: String(scopeId)
334
+ }
335
+ );
288
336
  if (topic?.name !== void 0 && topic?.type !== void 0) {
289
337
  return topic;
290
338
  }
@@ -304,8 +352,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
304
352
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
305
353
  const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
306
354
  const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
307
- const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
308
- const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
355
+ let createdAt = 0;
356
+ if (typeof topic.createdAt === "number") {
357
+ createdAt = topic.createdAt;
358
+ } else if (typeof topic._creationTime === "number") {
359
+ createdAt = topic._creationTime;
360
+ }
361
+ let updatedAt = createdAt;
362
+ if (typeof topic.updatedAt === "number") {
363
+ updatedAt = topic.updatedAt;
364
+ } else if (typeof metadata.updatedAt === "number") {
365
+ updatedAt = metadata.updatedAt;
366
+ }
309
367
  return {
310
368
  ...metadata,
311
369
  _id: outwardId,
@@ -374,90 +432,113 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
374
432
  if (!topic) {
375
433
  return null;
376
434
  }
377
- const nextMetadata = { ...readMetadata(topic) };
378
- const patch = {};
379
- const topicUpdateArgs = {
380
- id: String(topic._id)
435
+ const plan = buildTopicProjectOverlayPatchPlan(topic, value);
436
+ await applyTopicProjectOverlayPatch(ctx, topic, plan);
437
+ return materializeTopicProjectOverlay({
438
+ ...topic,
439
+ ...plan.patch,
440
+ metadata: plan.nextMetadata
441
+ });
442
+ }
443
+ function buildTopicProjectOverlayPatchPlan(topic, value) {
444
+ const plan = {
445
+ nextMetadata: { ...readMetadata(topic) },
446
+ patch: {},
447
+ topicUpdateArgs: {
448
+ id: String(topic._id)
449
+ }
381
450
  };
382
451
  for (const [key, rawValue] of Object.entries(value)) {
383
- switch (key) {
384
- case "_id":
385
- case "projectId":
386
- case "topicId":
387
- case "legacyProjectId":
388
- case "storageProjectId":
389
- break;
390
- case "name":
391
- case "description":
392
- patch[key] = rawValue;
393
- topicUpdateArgs[key] = rawValue;
394
- break;
395
- case "tenantId":
396
- case "workspaceId":
397
- case "ownerId":
398
- throw new Error(
399
- `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
400
- );
401
- case "status": {
402
- const status = coerceStatus(rawValue);
403
- if (status) {
404
- patch.status = status;
405
- topicUpdateArgs.status = status;
406
- }
407
- break;
408
- }
409
- case "visibility": {
410
- const visibility = coerceVisibility(rawValue);
411
- if (visibility) {
412
- patch.visibility = visibility;
413
- topicUpdateArgs.visibility = visibility;
414
- }
415
- break;
416
- }
417
- case "type": {
418
- const projectType = readNonEmptyString(rawValue);
419
- if (projectType) {
420
- nextMetadata.projectType = projectType;
421
- } else {
422
- delete nextMetadata.projectType;
423
- }
424
- break;
425
- }
426
- case "updatedAt":
427
- case "createdAt":
428
- break;
429
- default:
430
- if (rawValue === void 0) {
431
- delete nextMetadata[key];
432
- } else {
433
- nextMetadata[key] = rawValue;
434
- }
435
- }
452
+ applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
453
+ }
454
+ plan.patch.updatedAt = Date.now();
455
+ plan.patch.metadata = plan.nextMetadata;
456
+ plan.topicUpdateArgs.metadata = plan.nextMetadata;
457
+ return plan;
458
+ }
459
+ function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
460
+ switch (key) {
461
+ case "_id":
462
+ case "projectId":
463
+ case "topicId":
464
+ case "legacyProjectId":
465
+ case "storageProjectId":
466
+ case "updatedAt":
467
+ case "createdAt":
468
+ return;
469
+ case "name":
470
+ case "description":
471
+ plan.patch[key] = rawValue;
472
+ plan.topicUpdateArgs[key] = rawValue;
473
+ return;
474
+ case "tenantId":
475
+ case "workspaceId":
476
+ case "ownerId":
477
+ throw new Error(
478
+ `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
479
+ );
480
+ case "status":
481
+ applyTopicStatusPatch(plan, rawValue);
482
+ return;
483
+ case "visibility":
484
+ applyTopicVisibilityPatch(plan, rawValue);
485
+ return;
486
+ case "type":
487
+ applyTopicProjectTypePatch(plan, rawValue);
488
+ return;
489
+ default:
490
+ applyTopicMetadataPatch(plan, key, rawValue);
491
+ }
492
+ }
493
+ function applyTopicStatusPatch(plan, rawValue) {
494
+ const status = coerceStatus(rawValue);
495
+ if (status) {
496
+ plan.patch.status = status;
497
+ plan.topicUpdateArgs.status = status;
498
+ }
499
+ }
500
+ function applyTopicVisibilityPatch(plan, rawValue) {
501
+ const visibility = coerceVisibility(rawValue);
502
+ if (visibility) {
503
+ plan.patch.visibility = visibility;
504
+ plan.topicUpdateArgs.visibility = visibility;
505
+ }
506
+ }
507
+ function applyTopicProjectTypePatch(plan, rawValue) {
508
+ const projectType = readNonEmptyString(rawValue);
509
+ if (projectType) {
510
+ plan.nextMetadata.projectType = projectType;
511
+ return;
512
+ }
513
+ plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
514
+ }
515
+ function applyTopicMetadataPatch(plan, key, rawValue) {
516
+ if (rawValue === void 0) {
517
+ plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
518
+ return;
436
519
  }
437
- patch.updatedAt = Date.now();
438
- patch.metadata = nextMetadata;
439
- topicUpdateArgs.metadata = nextMetadata;
520
+ plan.nextMetadata[key] = rawValue;
521
+ }
522
+ async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
440
523
  if (typeof ctx.runMutation === "function") {
441
524
  try {
442
- await ctx.runMutation(api.topics.update, topicUpdateArgs);
525
+ await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
443
526
  } catch (error) {
444
- if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
527
+ if (!canPatchTopicViaLocalDb(ctx, error)) {
445
528
  throw error;
446
529
  }
447
- await ctx.db.patch(String(topic._id), patch);
530
+ await ctx.db.patch(topic._id, plan.patch);
448
531
  }
449
532
  } else if (ctx?.db && typeof ctx.db.patch === "function") {
450
- await ctx.db.patch(String(topic._id), patch);
533
+ await ctx.db.patch(topic._id, plan.patch);
451
534
  } else {
452
535
  throw new Error(
453
536
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
454
537
  );
455
538
  }
456
- return materializeTopicProjectOverlay({
457
- ...topic,
458
- ...patch,
459
- metadata: nextMetadata
460
- });
539
+ }
540
+ function canPatchTopicViaLocalDb(ctx, error) {
541
+ return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
461
542
  }
462
543
 
463
544
  // src/resolvers.ts
@@ -485,7 +566,7 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
485
566
  try {
486
567
  await patchTopicProjectOverlay(ctx, projectId, value);
487
568
  } catch (error) {
488
- if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
569
+ if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
489
570
  throw error;
490
571
  }
491
572
  console.warn(
@@ -518,6 +599,92 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
518
599
  ...resolverOverrides
519
600
  };
520
601
  }
602
+
603
+ // src/topicOntologyResolver.ts
604
+ var MAX_RESOLUTION_DEPTH = 10;
605
+ async function loadTopic(ctx, topicId) {
606
+ return await ctx.db.get(topicId);
607
+ }
608
+ async function loadOntologyDefinition(ctx, ontologyId) {
609
+ return await ctx.db.get(ontologyId);
610
+ }
611
+ async function loadPublishedOntologyVersions(ctx, ontologyId) {
612
+ const versions = await ctx.db.query("ontologyVersions").withIndex("by_ontologyId", (q) => q.eq("ontologyId", ontologyId)).collect();
613
+ return versions.filter((version) => version.status === "published").sort((a, b) => (b.publishedAt ?? 0) - (a.publishedAt ?? 0));
614
+ }
615
+ function resolvedTopicOntology(args) {
616
+ return {
617
+ ontologyId: args.ontologyDef._id,
618
+ ontologyKey: args.ontologyDef.ontologyKey,
619
+ ontologyName: args.ontologyDef.name,
620
+ publishedVersion: args.latestPublished,
621
+ source: args.source,
622
+ sourceTopicId: args.sourceTopicId,
623
+ tier: args.ontologyDef.tier,
624
+ validEdgeTypes: args.latestPublished?.edgeTypes.map((edgeType) => edgeType.value) ?? [],
625
+ validEntityTypes: args.latestPublished?.entityTypes.map((entityType) => entityType.value) ?? []
626
+ };
627
+ }
628
+ async function resolveCurrentTopicOntology(args) {
629
+ if (!args.current.ontologyId) {
630
+ return "continue";
631
+ }
632
+ const ontologyDef = await loadOntologyDefinition(
633
+ args.ctx,
634
+ args.current.ontologyId
635
+ );
636
+ if (!ontologyDef || ontologyDef.status === "archived") {
637
+ return args.current.parentTopicId ? "continue" : null;
638
+ }
639
+ const published = await loadPublishedOntologyVersions(
640
+ args.ctx,
641
+ args.current.ontologyId
642
+ );
643
+ return resolvedTopicOntology({
644
+ latestPublished: published[0] ?? null,
645
+ ontologyDef,
646
+ source: args.current._id === args.startTopicId ? "direct" : "inherited",
647
+ sourceTopicId: args.current._id
648
+ });
649
+ }
650
+ async function resolveTopicOntologyInternal(ctx, topicId) {
651
+ let current = await loadTopic(ctx, topicId);
652
+ if (!current) {
653
+ return null;
654
+ }
655
+ const startTopicId = topicId;
656
+ for (let i = 0; i < MAX_RESOLUTION_DEPTH && current; i++) {
657
+ const resolved = await resolveCurrentTopicOntology({
658
+ ctx,
659
+ current,
660
+ startTopicId
661
+ });
662
+ if (resolved !== "continue") {
663
+ return resolved;
664
+ }
665
+ if (!current.parentTopicId) {
666
+ break;
667
+ }
668
+ current = await loadTopic(ctx, current.parentTopicId);
669
+ }
670
+ return null;
671
+ }
672
+ async function validateEntityTypeForTopic(ctx, topicId, nodeType) {
673
+ const resolved = await resolveTopicOntologyInternal(ctx, topicId);
674
+ if (!resolved) {
675
+ return { valid: true };
676
+ }
677
+ if (resolved.validEntityTypes.length === 0) {
678
+ return { valid: true };
679
+ }
680
+ if (resolved.validEntityTypes.includes(nodeType)) {
681
+ return { valid: true };
682
+ }
683
+ return {
684
+ valid: false,
685
+ error: `Entity type "${nodeType}" is not defined in the ontology "${resolved.ontologyKey}" (${resolved.ontologyName}). Valid entity types: ${resolved.validEntityTypes.join(", ")}. Source: ${resolved.source} from topic ${resolved.sourceTopicId}.`
686
+ };
687
+ }
521
688
  var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
522
689
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
523
690
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -552,13 +719,15 @@ function asMappedProjectId(topic) {
552
719
  if (!topic) {
553
720
  return;
554
721
  }
555
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
722
+ const directLegacyProjectId = normalizeScopeValue(
723
+ topic[LEGACY_SCOPE_FIELD2]
724
+ );
556
725
  if (directLegacyProjectId) {
557
726
  return directLegacyProjectId;
558
727
  }
559
728
  const metadata = topic.metadata || {};
560
729
  const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
561
- return candidate ? candidate : void 0;
730
+ return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
562
731
  }
563
732
  function normalizeScopeValue(value) {
564
733
  if (typeof value !== "string") {
@@ -583,8 +752,9 @@ function pickPrimaryTopic(candidates) {
583
752
  })[0];
584
753
  }
585
754
  async function findTopicsByScopeAlias(ctx, scopeId) {
755
+ const query2 = ctx.db.query("topics");
586
756
  try {
587
- return await ctx.db.query("topics").withIndex(
757
+ return await query2.withIndex(
588
758
  "by_graph_scope_project",
589
759
  (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
590
760
  ).collect();
@@ -596,7 +766,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
596
766
  scopeId
597
767
  }
598
768
  );
599
- const topics = await ctx.db.query("topics").collect();
769
+ const topics = await query2.collect();
600
770
  return topics.filter((topic) => {
601
771
  const normalizedGlobalId = normalizeScopeValue(topic.globalId);
602
772
  const mappedProjectId = asMappedProjectId(topic);
@@ -652,187 +822,119 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
652
822
  let current = topic;
653
823
  for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
654
824
  current = await ctx.db.get(current.parentTopicId);
655
- if (!current) break;
825
+ if (!current) {
826
+ break;
827
+ }
656
828
  if (!tenantId) {
657
829
  tenantId = normalizeScopeValue(current.tenantId);
658
830
  }
659
831
  if (!workspaceId) {
660
832
  workspaceId = normalizeScopeValue(current.workspaceId);
661
833
  }
662
- if (tenantId && workspaceId) break;
834
+ if (tenantId && workspaceId) {
835
+ break;
836
+ }
663
837
  }
664
838
  return { tenantId, workspaceId };
665
839
  }
666
840
  async function resolveTopicProjectScope(ctx, args) {
667
841
  if (args.topicId) {
668
- let topic = null;
669
- try {
670
- topic = await ctx.db.get(
671
- args.topicId
672
- );
673
- } catch (error) {
674
- debugGraphPrimitiveFallback(
675
- "[topicScope] Failed to load topic by direct id",
676
- {
677
- error,
678
- topicId: args.topicId
679
- }
680
- );
681
- }
682
- if (!topic) {
683
- topic = await tryResolveHostTopicById(ctx, String(args.topicId));
684
- }
685
- if (!topic) {
686
- topic = pickPrimaryTopic(
687
- await findTopicsByScopeAlias(ctx, String(args.topicId))
688
- ) ?? null;
689
- }
690
- if (!topic) {
691
- const nodeScope = await resolveTopicNodeScopeOrNull(
692
- ctx,
693
- String(args.topicId)
694
- );
695
- if (nodeScope) {
696
- return nodeScope;
697
- }
698
- throw new Error(`Topic not found: ${String(args.topicId)}`);
699
- }
700
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
701
- const mapped = asMappedProjectId(topic);
702
- if (mapped) {
703
- return {
704
- topicId: topic._id,
705
- projectId: mapped,
706
- tenantId: inherited.tenantId,
707
- workspaceId: inherited.workspaceId,
708
- source: "topic"
709
- };
710
- }
711
- return {
712
- topicId: topic._id,
713
- tenantId: inherited.tenantId,
714
- workspaceId: inherited.workspaceId,
715
- source: "topic"
716
- };
842
+ return await resolveScopeFromTopicId(ctx, args.topicId);
717
843
  }
718
844
  if (args.projectId) {
719
- let directTopic = null;
720
- try {
721
- directTopic = await ctx.db.get(
722
- args.projectId
723
- );
724
- } catch (error) {
725
- debugGraphPrimitiveFallback(
726
- "[topicScope] Failed to load direct project topic",
727
- {
728
- error,
729
- projectId: args.projectId
730
- }
731
- );
732
- }
733
- if (directTopic) {
734
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
735
- const mapped = asMappedProjectId(directTopic);
736
- return {
737
- topicId: directTopic._id,
738
- projectId: mapped ?? args.projectId,
739
- tenantId: inherited.tenantId,
740
- workspaceId: inherited.workspaceId,
741
- source: "topic_inferred"
742
- };
743
- }
744
- directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
745
- if (directTopic) {
746
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
747
- const mapped = asMappedProjectId(directTopic);
748
- return {
749
- topicId: directTopic._id,
750
- projectId: mapped ?? args.projectId,
751
- tenantId: inherited.tenantId,
752
- workspaceId: inherited.workspaceId,
753
- source: "topic_inferred"
754
- };
755
- }
756
- const topics = await findTopicsByScopeAlias(ctx, args.projectId);
757
- const primary = pickPrimaryTopic(topics);
758
- if (primary) {
759
- const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
760
- return {
761
- topicId: primary._id,
762
- projectId: args.projectId,
763
- tenantId: inherited.tenantId,
764
- workspaceId: inherited.workspaceId,
765
- source: "project_mapped_topic"
766
- };
767
- }
768
- const nodeScope = await resolveTopicNodeScopeOrNull(
769
- ctx,
770
- String(args.projectId)
771
- );
772
- if (nodeScope) {
773
- return {
774
- ...nodeScope,
775
- projectId: nodeScope.projectId ?? String(args.projectId)
776
- };
777
- }
778
- throw new Error(
779
- `Legacy project scope ${String(args.projectId)} has no mapped topic.`
780
- );
845
+ return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
781
846
  }
782
847
  throw new Error(
783
848
  "Missing scope: provide topicId (preferred) or legacy projectId alias."
784
849
  );
785
850
  }
786
- var optionalScopeArgs = {
787
- projectId: v.optional(v.string()),
788
- topicId: v.optional(v.string())
789
- };
790
- async function insertEpistemicNode(ctx, doc) {
791
- assertUuidV7Identity("epistemicNodes", doc.globalId);
792
- return ctx.db.insert("epistemicNodes", doc);
851
+ async function resolveScopeFromTopicId(ctx, topicId) {
852
+ const topic = await resolveTopicDocFromTopicId(ctx, topicId);
853
+ if (topic) {
854
+ return await buildTopicScope(ctx, topic, "topic");
855
+ }
856
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
857
+ if (nodeScope) {
858
+ return nodeScope;
859
+ }
860
+ throw new Error(`Topic not found: ${String(topicId)}`);
793
861
  }
794
- async function assertExistingNodeEndpoint(ctx, endpointRole, endpoint) {
795
- assertUuidShapedEdgeEndpoint(endpointRole, endpoint);
796
- const node = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", endpoint)).first();
797
- if (!node) {
798
- throw new Error(
799
- `edge_endpoint_not_canonical: epistemicEdges insert requires ${endpointRole} to be the globalId of an existing epistemicNodes row, received ${endpoint} (no node with that globalId)`
800
- );
862
+ async function resolveTopicDocFromTopicId(ctx, topicId) {
863
+ const direct = await tryReadTopicDoc(ctx, topicId, {
864
+ failureLog: "[topicScope] Failed to load topic by direct id",
865
+ idLogKey: "topicId"
866
+ });
867
+ if (direct) {
868
+ return direct;
869
+ }
870
+ const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
871
+ if (hostTopic) {
872
+ return hostTopic;
801
873
  }
874
+ return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
802
875
  }
803
- async function insertEpistemicEdge(ctx, doc) {
804
- assertUuidV7Identity("epistemicEdges", doc.globalId);
805
- assertStorageEdgeVocabulary(doc.edgeType);
806
- if (!doc.fromNodeId || typeof doc.fromNodeId !== "string") {
807
- throw new Error(
808
- "edge_endpoint_missing: epistemicEdges insert requires a non-empty fromNodeId"
809
- );
876
+ async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
877
+ const directTopic = await resolveDirectLegacyProjectTopic(
878
+ ctx,
879
+ legacyProjectId
880
+ );
881
+ if (directTopic) {
882
+ return await buildTopicScope(ctx, directTopic, "topic_inferred", {
883
+ fallbackProjectId: legacyProjectId
884
+ });
810
885
  }
811
- if (!doc.toNodeId || typeof doc.toNodeId !== "string") {
812
- throw new Error(
813
- "edge_endpoint_missing: epistemicEdges insert requires a non-empty toNodeId"
814
- );
886
+ const primary = pickPrimaryTopic(
887
+ await findTopicsByScopeAlias(ctx, legacyProjectId)
888
+ );
889
+ if (primary) {
890
+ return await buildTopicScope(ctx, primary, "project_mapped_topic", {
891
+ fallbackProjectId: legacyProjectId
892
+ });
815
893
  }
816
- await assertExistingNodeEndpoint(ctx, "fromNodeId", doc.fromNodeId);
817
- await assertExistingNodeEndpoint(ctx, "toNodeId", doc.toNodeId);
818
- if (doc.fromNodeType && doc.toNodeType && doc.edgeType !== "extracted_from") {
819
- assertEdgePolicyAllowed(
820
- edgePolicyManifest,
821
- doc.edgeType,
822
- {
823
- kind: "epistemic_node",
824
- nodeId: doc.fromNodeId,
825
- nodeType: doc.fromNodeType
826
- },
827
- {
828
- kind: "epistemic_node",
829
- nodeId: doc.toNodeId,
830
- nodeType: doc.toNodeType
831
- }
832
- );
894
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
895
+ if (nodeScope) {
896
+ return {
897
+ ...nodeScope,
898
+ projectId: nodeScope.projectId ?? legacyProjectId
899
+ };
900
+ }
901
+ throw new Error(
902
+ `Legacy project scope ${legacyProjectId} has no mapped topic.`
903
+ );
904
+ }
905
+ async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
906
+ const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
907
+ failureLog: "[topicScope] Failed to load direct project topic",
908
+ idLogKey: "projectId"
909
+ });
910
+ return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
911
+ }
912
+ async function tryReadTopicDoc(ctx, id, log) {
913
+ try {
914
+ return await ctx.db.get(id);
915
+ } catch (error) {
916
+ debugGraphPrimitiveFallback(log.failureLog, {
917
+ error,
918
+ [log.idLogKey]: id
919
+ });
920
+ return null;
833
921
  }
834
- return ctx.db.insert("epistemicEdges", doc);
835
922
  }
923
+ async function buildTopicScope(ctx, topic, source, options = {}) {
924
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
925
+ const mapped = asMappedProjectId(topic);
926
+ return {
927
+ topicId: topic._id,
928
+ ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
929
+ tenantId: inherited.tenantId,
930
+ workspaceId: inherited.workspaceId,
931
+ source
932
+ };
933
+ }
934
+ var optionalScopeArgs = {
935
+ projectId: v.optional(v.string()),
936
+ topicId: v.optional(v.string())
937
+ };
836
938
 
837
939
  // src/entityLifecycle.ts
838
940
  async function requireAuthenticatedUserId(ctx) {
@@ -868,14 +970,227 @@ var ONTOLOGICAL_NODE_TYPE_SET = new Set(ONTOLOGICAL_NODE_TYPES);
868
970
  var isOntologicalNodeType = ONTOLOGICAL_NODE_TYPE_SET.has.bind(
869
971
  ONTOLOGICAL_NODE_TYPE_SET
870
972
  );
973
+ function isRecord(value) {
974
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
975
+ }
976
+ function readConvexId(value) {
977
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
978
+ }
979
+ function readOptionalString(value) {
980
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
981
+ }
982
+ function readRecord(value) {
983
+ return isRecord(value) ? value : void 0;
984
+ }
985
+ function readStringArray2(value) {
986
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : void 0;
987
+ }
988
+ function readEntityUpdateMetadata(value) {
989
+ if (!isRecord(value)) {
990
+ return;
991
+ }
992
+ const metadata = {};
993
+ const crunchbase = readOptionalString(value.crunchbase);
994
+ const linkedin = readOptionalString(value.linkedin);
995
+ const pitchbook = readOptionalString(value.pitchbook);
996
+ const twitter = readOptionalString(value.twitter);
997
+ const website = readOptionalString(value.website);
998
+ if (crunchbase !== void 0) {
999
+ metadata.crunchbase = crunchbase;
1000
+ }
1001
+ if (linkedin !== void 0) {
1002
+ metadata.linkedin = linkedin;
1003
+ }
1004
+ if (pitchbook !== void 0) {
1005
+ metadata.pitchbook = pitchbook;
1006
+ }
1007
+ if (twitter !== void 0) {
1008
+ metadata.twitter = twitter;
1009
+ }
1010
+ if (website !== void 0) {
1011
+ metadata.website = website;
1012
+ }
1013
+ return metadata;
1014
+ }
1015
+ function readCanonicalEntityRecord(value) {
1016
+ if (!isRecord(value)) {
1017
+ return null;
1018
+ }
1019
+ const id = readConvexId(value._id);
1020
+ if (!id) {
1021
+ return null;
1022
+ }
1023
+ const record = { _id: id };
1024
+ const canonicalText = readOptionalString(value.canonicalText);
1025
+ const contentHash = readOptionalString(value.contentHash);
1026
+ const domain = readOptionalString(value.domain);
1027
+ const epistemicLayer = readOptionalString(value.epistemicLayer);
1028
+ const externalIds = readRecord(value.externalIds);
1029
+ const globalId = readOptionalString(value.globalId);
1030
+ const metadata = readRecord(value.metadata);
1031
+ const nodeType = readOptionalString(value.nodeType);
1032
+ const projectId = readOptionalString(value.projectId);
1033
+ const status = readOptionalString(value.status);
1034
+ const subtype = readOptionalString(value.subtype);
1035
+ const tags = readStringArray2(value.tags);
1036
+ const tenantId = readOptionalString(value.tenantId);
1037
+ const title = readOptionalString(value.title);
1038
+ const verificationStatus = readOptionalString(value.verificationStatus);
1039
+ const workspaceId = readOptionalString(value.workspaceId);
1040
+ if (canonicalText !== void 0) {
1041
+ record.canonicalText = canonicalText;
1042
+ }
1043
+ if (contentHash !== void 0) {
1044
+ record.contentHash = contentHash;
1045
+ }
1046
+ if (domain !== void 0) {
1047
+ record.domain = domain;
1048
+ }
1049
+ if (epistemicLayer !== void 0) {
1050
+ record.epistemicLayer = epistemicLayer;
1051
+ }
1052
+ if (externalIds !== void 0) {
1053
+ record.externalIds = externalIds;
1054
+ }
1055
+ if (globalId !== void 0) {
1056
+ record.globalId = globalId;
1057
+ }
1058
+ if (metadata !== void 0) {
1059
+ record.metadata = metadata;
1060
+ }
1061
+ if (nodeType !== void 0) {
1062
+ record.nodeType = nodeType;
1063
+ }
1064
+ if (projectId !== void 0) {
1065
+ record.projectId = projectId;
1066
+ }
1067
+ if (status !== void 0) {
1068
+ record.status = status;
1069
+ }
1070
+ if (subtype !== void 0) {
1071
+ record.subtype = subtype;
1072
+ }
1073
+ if (tags !== void 0) {
1074
+ record.tags = tags;
1075
+ }
1076
+ if (tenantId !== void 0) {
1077
+ record.tenantId = tenantId;
1078
+ }
1079
+ if (title !== void 0) {
1080
+ record.title = title;
1081
+ }
1082
+ if (verificationStatus !== void 0) {
1083
+ record.verificationStatus = verificationStatus;
1084
+ }
1085
+ if (workspaceId !== void 0) {
1086
+ record.workspaceId = workspaceId;
1087
+ }
1088
+ return record;
1089
+ }
1090
+ function readEntityEdgeRecord(value) {
1091
+ if (!isRecord(value)) {
1092
+ return null;
1093
+ }
1094
+ const id = readConvexId(value._id);
1095
+ if (!id) {
1096
+ return null;
1097
+ }
1098
+ const edge = { _id: id };
1099
+ const fromNodeId = readConvexId(value.fromNodeId);
1100
+ const toNodeId = readConvexId(value.toNodeId);
1101
+ if (fromNodeId !== void 0) {
1102
+ edge.fromNodeId = fromNodeId;
1103
+ }
1104
+ if (toNodeId !== void 0) {
1105
+ edge.toNodeId = toNodeId;
1106
+ }
1107
+ return edge;
1108
+ }
1109
+ function readRowList(values, reader) {
1110
+ return values.flatMap((value) => {
1111
+ const row = reader(value);
1112
+ return row ? [row] : [];
1113
+ });
1114
+ }
1115
+ function readScopeTopicDoc(value) {
1116
+ if (!isRecord(value)) {
1117
+ return null;
1118
+ }
1119
+ const id = readConvexId(value._id);
1120
+ if (!id) {
1121
+ return null;
1122
+ }
1123
+ const topic = { _id: id };
1124
+ const tenantId = readOptionalString(value.tenantId);
1125
+ const workspaceId = readOptionalString(value.workspaceId);
1126
+ if (tenantId !== void 0) {
1127
+ topic.tenantId = tenantId;
1128
+ }
1129
+ if (workspaceId !== void 0) {
1130
+ topic.workspaceId = workspaceId;
1131
+ }
1132
+ return topic;
1133
+ }
1134
+ function readScopeProjectDoc(value) {
1135
+ if (!isRecord(value)) {
1136
+ return null;
1137
+ }
1138
+ const id = readOptionalString(value._id);
1139
+ if (!id) {
1140
+ return null;
1141
+ }
1142
+ const project = { _id: id };
1143
+ const tenantId = readOptionalString(value.tenantId);
1144
+ const workspaceId = readOptionalString(value.workspaceId);
1145
+ if (tenantId !== void 0) {
1146
+ project.tenantId = tenantId;
1147
+ }
1148
+ if (workspaceId !== void 0) {
1149
+ project.workspaceId = workspaceId;
1150
+ }
1151
+ return project;
1152
+ }
1153
+ function readEntityUpdateNode(value) {
1154
+ const node = readCanonicalEntityRecord(value);
1155
+ const nodeType = node?.nodeType;
1156
+ if (!(node?.canonicalText && nodeType && isOntologicalNodeType(nodeType))) {
1157
+ return null;
1158
+ }
1159
+ return {
1160
+ _id: node._id,
1161
+ canonicalText: node.canonicalText,
1162
+ domain: node.domain,
1163
+ externalIds: readEntityUpdateMetadata(node.externalIds),
1164
+ globalId: node.globalId,
1165
+ metadata: node.metadata,
1166
+ nodeType,
1167
+ projectId: node.projectId,
1168
+ status: node.status,
1169
+ subtype: node.subtype,
1170
+ tags: node.tags,
1171
+ tenantId: node.tenantId,
1172
+ title: node.title,
1173
+ verificationStatus: node.verificationStatus
1174
+ };
1175
+ }
871
1176
  async function resolveCanonicalEntityScope(ctx, args) {
872
1177
  const scope = await resolveTopicProjectScope(ctx, args);
873
- const topic = scope.topicId ? await ctx.db.get(scope.topicId) ?? null : null;
874
- const project = scope.projectId !== void 0 ? await resolveGraphPrimitivesAppResolvers().getProject(
875
- ctx,
876
- scope.projectId
877
- ) ?? null : null;
878
- const tenantId = typeof topic?.tenantId === "string" && topic.tenantId.trim().length > 0 ? topic.tenantId.trim() : typeof project?.tenantId === "string" && project.tenantId.trim().length > 0 ? project.tenantId.trim() : void 0;
1178
+ const topic = scope.topicId ? readScopeTopicDoc(await ctx.db.get(scope.topicId)) : null;
1179
+ const project = scope.projectId === void 0 ? null : readScopeProjectDoc(
1180
+ await resolveGraphPrimitivesAppResolvers().getProject(
1181
+ ctx,
1182
+ scope.projectId
1183
+ )
1184
+ );
1185
+ let tenantId;
1186
+ if (typeof topic?.tenantId === "string") {
1187
+ const trimmedTopicTenantId = topic.tenantId.trim();
1188
+ tenantId = trimmedTopicTenantId.length > 0 ? trimmedTopicTenantId : void 0;
1189
+ }
1190
+ if (tenantId === void 0 && typeof project?.tenantId === "string") {
1191
+ const trimmedProjectTenantId = project.tenantId.trim();
1192
+ tenantId = trimmedProjectTenantId.length > 0 ? trimmedProjectTenantId : void 0;
1193
+ }
879
1194
  return {
880
1195
  ...scope,
881
1196
  tenantId,
@@ -885,10 +1200,13 @@ async function resolveCanonicalEntityScope(ctx, args) {
885
1200
  }
886
1201
  async function findExistingCanonicalEntity(ctx, args) {
887
1202
  const contentHash = generateContentHash(args.nodeType, args.canonicalText);
888
- const candidates = await ctx.db.query("epistemicNodes").withIndex(
889
- "by_contentHash",
890
- (q) => q.eq("contentHash", contentHash)
891
- ).collect();
1203
+ const candidates = readRowList(
1204
+ await ctx.db.query("epistemicNodes").withIndex(
1205
+ "by_contentHash",
1206
+ (q) => q.eq("contentHash", contentHash)
1207
+ ).collect(),
1208
+ readCanonicalEntityRecord
1209
+ );
892
1210
  return candidates.find(
893
1211
  (node) => matchesCanonicalEntityRecord(node, {
894
1212
  nodeType: args.nodeType,
@@ -898,13 +1216,29 @@ async function findExistingCanonicalEntity(ctx, args) {
898
1216
  ) ?? null;
899
1217
  }
900
1218
  async function listCanonicalEntitiesForScope(ctx, args) {
901
- const baseRows = args.tenantId !== void 0 ? await ctx.db.query("epistemicNodes").withIndex(
902
- "by_tenantId",
903
- (q) => q.eq("tenantId", args.tenantId)
904
- ).collect() : args.nodeType ? await ctx.db.query("epistemicNodes").withIndex(
905
- "by_nodeType",
906
- (q) => q.eq("nodeType", args.nodeType)
907
- ).collect() : await ctx.db.query("epistemicNodes").collect();
1219
+ let baseRows = readRowList(
1220
+ await ctx.db.query("epistemicNodes").collect(),
1221
+ readCanonicalEntityRecord
1222
+ );
1223
+ if (args.tenantId === void 0) {
1224
+ if (args.nodeType !== void 0) {
1225
+ baseRows = readRowList(
1226
+ await ctx.db.query("epistemicNodes").withIndex(
1227
+ "by_nodeType",
1228
+ (q) => q.eq("nodeType", args.nodeType)
1229
+ ).collect(),
1230
+ readCanonicalEntityRecord
1231
+ );
1232
+ }
1233
+ } else {
1234
+ baseRows = readRowList(
1235
+ await ctx.db.query("epistemicNodes").withIndex(
1236
+ "by_tenantId",
1237
+ (q) => q.eq("tenantId", args.tenantId)
1238
+ ).collect(),
1239
+ readCanonicalEntityRecord
1240
+ );
1241
+ }
908
1242
  const normalizedSearch = typeof args.searchTerm === "string" && args.searchTerm.trim().length > 0 ? normalizeCanonicalEntityText(args.searchTerm) : void 0;
909
1243
  return baseRows.filter(
910
1244
  (node) => matchesCanonicalEntityRecord(node, {
@@ -995,11 +1329,7 @@ var createEntity = mutation({
995
1329
  projectId: args.projectId
996
1330
  });
997
1331
  if (scope.projectId) {
998
- await requireScopeWriteAccess(
999
- ctx,
1000
- scope.projectId,
1001
- authenticatedUserId
1002
- );
1332
+ await requireScopeWriteAccess(ctx, scope.projectId, authenticatedUserId);
1003
1333
  }
1004
1334
  if (scope.topicId) {
1005
1335
  const ontologyValidation = await validateEntityTypeForTopic(
@@ -1018,7 +1348,7 @@ var createEntity = mutation({
1018
1348
  });
1019
1349
  }
1020
1350
  }
1021
- const metadata = args.metadata || {};
1351
+ const metadata = readRecord(args.metadata) ?? {};
1022
1352
  const validation = await validateEntityMetadata(
1023
1353
  ctx,
1024
1354
  args.nodeType,
@@ -1093,6 +1423,100 @@ var createEntity = mutation({
1093
1423
  return { nodeId, globalId: entityGlobalId, isDuplicate: false };
1094
1424
  }
1095
1425
  });
1426
+ function normalizeEntityMetadataPatch(metadata) {
1427
+ return metadata === null || metadata === void 0 ? {} : metadata;
1428
+ }
1429
+ function resolveScopeProjectId(node) {
1430
+ return typeof node.projectId === "string" && node.projectId.trim().length > 0 ? node.projectId : void 0;
1431
+ }
1432
+ function prepareEntityAttributePlan(node, args) {
1433
+ const existingMetadata = node.metadata ?? {};
1434
+ const mergedMetadata = {
1435
+ ...existingMetadata,
1436
+ ...normalizeEntityMetadataPatch(args.metadata)
1437
+ };
1438
+ if (args.subtype !== void 0) {
1439
+ mergedMetadata.subtype = args.subtype;
1440
+ }
1441
+ const nextCanonicalText = args.canonicalText === void 0 ? node.canonicalText : args.canonicalText.trim();
1442
+ if (nextCanonicalText.length === 0) {
1443
+ throwStructuredMutationError({
1444
+ message: "canonicalText cannot be empty.",
1445
+ status: 400,
1446
+ code: "INVALID_REQUEST",
1447
+ invariantCode: "entity.update_requires_entity_lifecycle"
1448
+ });
1449
+ }
1450
+ return {
1451
+ nextCanonicalText,
1452
+ existingMetadata,
1453
+ mergedMetadata
1454
+ };
1455
+ }
1456
+ async function loadEntityForUpdate(ctx, nodeId) {
1457
+ const node = readEntityUpdateNode(await ctx.db.get(nodeId));
1458
+ if (!node) {
1459
+ throwStructuredMutationError({
1460
+ message: "Entity not found.",
1461
+ status: 404,
1462
+ code: "NOT_FOUND",
1463
+ details: { nodeId }
1464
+ });
1465
+ }
1466
+ return node;
1467
+ }
1468
+ async function assertCanonicalEntityUnique(ctx, node, nodeId, canonicalText) {
1469
+ const duplicate = await findExistingCanonicalEntity(ctx, {
1470
+ nodeType: node.nodeType,
1471
+ canonicalText,
1472
+ tenantId: typeof node.tenantId === "string" ? node.tenantId : void 0
1473
+ });
1474
+ if (duplicate && String(duplicate._id) !== String(nodeId)) {
1475
+ throwStructuredMutationError({
1476
+ message: "A canonical entity with this name already exists in the tenant scope.",
1477
+ status: 409,
1478
+ code: "CONFLICT",
1479
+ invariantCode: "entity.canonical_text_unique_per_tenant",
1480
+ suggestion: "Merge the duplicate entity instead of renaming this node onto an existing canonical record.",
1481
+ details: {
1482
+ nodeId,
1483
+ duplicateNodeId: duplicate._id,
1484
+ canonicalText
1485
+ }
1486
+ });
1487
+ }
1488
+ }
1489
+ function buildEntityAttributeUpdates(node, args, plan) {
1490
+ const updates = {
1491
+ metadata: plan.mergedMetadata
1492
+ };
1493
+ if (args.canonicalText !== void 0) {
1494
+ updates.canonicalText = plan.nextCanonicalText;
1495
+ updates.contentHash = generateContentHash(
1496
+ node.nodeType,
1497
+ plan.nextCanonicalText
1498
+ );
1499
+ updates.title = args.title === void 0 ? buildEntityTitle(plan.nextCanonicalText) : args.title;
1500
+ } else if (args.title !== void 0) {
1501
+ updates.title = args.title;
1502
+ }
1503
+ if (args.subtype !== void 0) {
1504
+ updates.subtype = args.subtype;
1505
+ }
1506
+ if (args.domain !== void 0) {
1507
+ updates.domain = args.domain;
1508
+ }
1509
+ if (args.tags !== void 0) {
1510
+ updates.tags = args.tags;
1511
+ }
1512
+ if (args.verificationStatus !== void 0) {
1513
+ updates.verificationStatus = args.verificationStatus;
1514
+ }
1515
+ if (args.externalIds !== void 0) {
1516
+ updates.externalIds = args.externalIds;
1517
+ }
1518
+ return updates;
1519
+ }
1096
1520
  var updateEntityAttributes = mutation({
1097
1521
  args: {
1098
1522
  nodeId: v.id("epistemicNodes"),
@@ -1118,68 +1542,33 @@ var updateEntityAttributes = mutation({
1118
1542
  handler: async (ctx, args) => {
1119
1543
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
1120
1544
  const now = Date.now();
1121
- const node = await ctx.db.get(args.nodeId);
1122
- if (!node) {
1123
- throwStructuredMutationError({
1124
- message: "Entity not found.",
1125
- status: 404,
1126
- code: "NOT_FOUND",
1127
- details: { nodeId: args.nodeId }
1128
- });
1129
- }
1130
- if (!isOntologicalNodeType(node.nodeType)) {
1131
- throwStructuredMutationError({
1132
- message: `Node "${args.nodeId}" is a ${node.nodeType}, not an entity. Use belief/question APIs for epistemic nodes.`,
1133
- status: 400,
1134
- code: "LIFECYCLE_VIOLATION",
1135
- invariantCode: "entity.type_check",
1136
- suggestion: "Only ontological entities can be updated via updateEntityAttributes."
1137
- });
1138
- }
1139
- const scopeProjectId = typeof node.projectId === "string" && node.projectId.trim().length > 0 ? node.projectId : void 0;
1140
- if (scopeProjectId) {
1545
+ const node = await loadEntityForUpdate(ctx, args.nodeId);
1546
+ const scopeProjectId = resolveScopeProjectId(node);
1547
+ if (scopeProjectId !== void 0) {
1141
1548
  await requireScopeWriteAccess(ctx, scopeProjectId, authenticatedUserId);
1142
1549
  }
1143
- const existingMetadata = node.metadata || {};
1144
- const newMetadataFields = args.metadata !== void 0 ? args.metadata || {} : {};
1145
- const mergedMetadata = { ...existingMetadata, ...newMetadataFields };
1146
- if (args.subtype !== void 0) {
1147
- mergedMetadata.subtype = args.subtype;
1148
- }
1149
- if (args.canonicalText !== void 0 && args.canonicalText.trim().length === 0) {
1150
- throwStructuredMutationError({
1151
- message: "canonicalText cannot be empty.",
1152
- status: 400,
1153
- code: "INVALID_REQUEST",
1154
- invariantCode: "entity.update_requires_entity_lifecycle"
1155
- });
1156
- }
1157
- const nextCanonicalText = typeof args.canonicalText === "string" && args.canonicalText.trim().length > 0 ? args.canonicalText.trim() : node.canonicalText;
1550
+ const plan = prepareEntityAttributePlan(node, {
1551
+ canonicalText: args.canonicalText,
1552
+ metadata: args.metadata,
1553
+ subtype: args.subtype,
1554
+ domain: args.domain,
1555
+ title: args.title,
1556
+ tags: args.tags,
1557
+ verificationStatus: args.verificationStatus,
1558
+ externalIds: args.externalIds
1559
+ });
1158
1560
  if (args.canonicalText !== void 0) {
1159
- const duplicate = await findExistingCanonicalEntity(ctx, {
1160
- nodeType: node.nodeType,
1161
- canonicalText: nextCanonicalText,
1162
- tenantId: typeof node.tenantId === "string" ? node.tenantId : void 0
1163
- });
1164
- if (duplicate && String(duplicate._id) !== String(args.nodeId)) {
1165
- throwStructuredMutationError({
1166
- message: "A canonical entity with this name already exists in the tenant scope.",
1167
- status: 409,
1168
- code: "CONFLICT",
1169
- invariantCode: "entity.canonical_text_unique_per_tenant",
1170
- suggestion: "Merge the duplicate entity instead of renaming this node onto an existing canonical record.",
1171
- details: {
1172
- nodeId: args.nodeId,
1173
- duplicateNodeId: duplicate._id,
1174
- canonicalText: nextCanonicalText
1175
- }
1176
- });
1177
- }
1561
+ await assertCanonicalEntityUnique(
1562
+ ctx,
1563
+ node,
1564
+ args.nodeId,
1565
+ plan.nextCanonicalText
1566
+ );
1178
1567
  }
1179
1568
  const validation = await validateEntityMetadata(
1180
1569
  ctx,
1181
1570
  node.nodeType,
1182
- mergedMetadata,
1571
+ plan.mergedMetadata,
1183
1572
  typeof node.tenantId === "string" ? node.tenantId : void 0
1184
1573
  );
1185
1574
  if (!validation.valid) {
@@ -1192,31 +1581,22 @@ var updateEntityAttributes = mutation({
1192
1581
  });
1193
1582
  }
1194
1583
  const updates = {
1195
- metadata: mergedMetadata,
1584
+ ...buildEntityAttributeUpdates(
1585
+ node,
1586
+ {
1587
+ canonicalText: args.canonicalText,
1588
+ title: args.title,
1589
+ metadata: args.metadata,
1590
+ subtype: args.subtype,
1591
+ domain: args.domain,
1592
+ tags: args.tags,
1593
+ verificationStatus: args.verificationStatus,
1594
+ externalIds: args.externalIds
1595
+ },
1596
+ plan
1597
+ ),
1196
1598
  updatedAt: now
1197
1599
  };
1198
- if (args.canonicalText !== void 0) {
1199
- updates.canonicalText = nextCanonicalText;
1200
- updates.contentHash = generateContentHash(node.nodeType, nextCanonicalText);
1201
- updates.title = args.title !== void 0 ? args.title : buildEntityTitle(nextCanonicalText);
1202
- } else if (args.title !== void 0) {
1203
- updates.title = args.title;
1204
- }
1205
- if (args.subtype !== void 0) {
1206
- updates.subtype = args.subtype;
1207
- }
1208
- if (args.domain !== void 0) {
1209
- updates.domain = args.domain;
1210
- }
1211
- if (args.tags !== void 0) {
1212
- updates.tags = args.tags;
1213
- }
1214
- if (args.verificationStatus !== void 0) {
1215
- updates.verificationStatus = args.verificationStatus;
1216
- }
1217
- if (args.externalIds !== void 0) {
1218
- updates.externalIds = args.externalIds;
1219
- }
1220
1600
  await ctx.db.patch(args.nodeId, updates);
1221
1601
  await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
1222
1602
  nodeId: args.nodeId,
@@ -1237,7 +1617,7 @@ var updateEntityAttributes = mutation({
1237
1617
  tags: node.tags,
1238
1618
  verificationStatus: node.verificationStatus,
1239
1619
  externalIds: node.externalIds,
1240
- metadata: existingMetadata
1620
+ metadata: plan.existingMetadata
1241
1621
  },
1242
1622
  newState: updates,
1243
1623
  projectId: scopeProjectId
@@ -1254,9 +1634,13 @@ var mergeEntities = mutation({
1254
1634
  handler: async (ctx, args) => {
1255
1635
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
1256
1636
  const now = Date.now();
1257
- const canonical = await ctx.db.get(args.canonicalNodeId);
1258
- const duplicate = await ctx.db.get(args.duplicateNodeId);
1259
- if (!canonical || !duplicate) {
1637
+ const canonical = readEntityUpdateNode(
1638
+ await ctx.db.get(args.canonicalNodeId)
1639
+ );
1640
+ const duplicate = readEntityUpdateNode(
1641
+ await ctx.db.get(args.duplicateNodeId)
1642
+ );
1643
+ if (!(canonical && duplicate)) {
1260
1644
  throwStructuredMutationError({
1261
1645
  message: "One or both entity nodes not found.",
1262
1646
  status: 404,
@@ -1267,14 +1651,6 @@ var mergeEntities = mutation({
1267
1651
  }
1268
1652
  });
1269
1653
  }
1270
- if (!isOntologicalNodeType(canonical.nodeType) || !isOntologicalNodeType(duplicate.nodeType)) {
1271
- throwStructuredMutationError({
1272
- message: "Both nodes must be ontological entities to merge.",
1273
- status: 400,
1274
- code: "LIFECYCLE_VIOLATION",
1275
- invariantCode: "entity.type_check"
1276
- });
1277
- }
1278
1654
  if (args.canonicalNodeId === args.duplicateNodeId) {
1279
1655
  throwStructuredMutationError({
1280
1656
  message: "Cannot merge an entity with itself.",
@@ -1285,6 +1661,18 @@ var mergeEntities = mutation({
1285
1661
  details: { canonicalNodeId: args.canonicalNodeId }
1286
1662
  });
1287
1663
  }
1664
+ if (!(canonical.globalId && duplicate.globalId)) {
1665
+ throwStructuredMutationError({
1666
+ message: "Both entity nodes must have globalId values to merge.",
1667
+ status: 400,
1668
+ code: "LIFECYCLE_VIOLATION",
1669
+ invariantCode: "entity.global_id_required",
1670
+ details: {
1671
+ canonicalNodeId: args.canonicalNodeId,
1672
+ duplicateNodeId: args.duplicateNodeId
1673
+ }
1674
+ });
1675
+ }
1288
1676
  if (canonical.nodeType !== duplicate.nodeType) {
1289
1677
  throwStructuredMutationError({
1290
1678
  message: `Cannot merge different entity types: ${canonical.nodeType} vs ${duplicate.nodeType}.`,
@@ -1314,10 +1702,13 @@ var mergeEntities = mutation({
1314
1702
  createdAt: now,
1315
1703
  updatedAt: now
1316
1704
  });
1317
- const outgoingEdges = await ctx.db.query("epistemicEdges").withIndex(
1318
- "by_from",
1319
- (q) => q.eq("fromNodeId", args.duplicateNodeId)
1320
- ).collect();
1705
+ const outgoingEdges = readRowList(
1706
+ await ctx.db.query("epistemicEdges").withIndex(
1707
+ "by_from",
1708
+ (q) => q.eq("fromNodeId", args.duplicateNodeId)
1709
+ ).collect(),
1710
+ readEntityEdgeRecord
1711
+ );
1321
1712
  for (const edge of outgoingEdges) {
1322
1713
  if (edge.toNodeId === args.canonicalNodeId) {
1323
1714
  continue;
@@ -1328,10 +1719,13 @@ var mergeEntities = mutation({
1328
1719
  updatedAt: now
1329
1720
  });
1330
1721
  }
1331
- const incomingEdges = await ctx.db.query("epistemicEdges").withIndex(
1332
- "by_to",
1333
- (q) => q.eq("toNodeId", args.duplicateNodeId)
1334
- ).collect();
1722
+ const incomingEdges = readRowList(
1723
+ await ctx.db.query("epistemicEdges").withIndex(
1724
+ "by_to",
1725
+ (q) => q.eq("toNodeId", args.duplicateNodeId)
1726
+ ).collect(),
1727
+ readEntityEdgeRecord
1728
+ );
1335
1729
  for (const edge of incomingEdges) {
1336
1730
  if (edge.fromNodeId === args.canonicalNodeId) {
1337
1731
  continue;
@@ -1342,7 +1736,7 @@ var mergeEntities = mutation({
1342
1736
  updatedAt: now
1343
1737
  });
1344
1738
  }
1345
- const duplicateMetadata = duplicate.metadata || {};
1739
+ const duplicateMetadata = duplicate.metadata ?? {};
1346
1740
  await ctx.db.patch(args.duplicateNodeId, {
1347
1741
  status: "superseded",
1348
1742
  metadata: {
@@ -1407,29 +1801,9 @@ var archiveEntity = mutation({
1407
1801
  handler: async (ctx, args) => {
1408
1802
  const authenticatedUserId = await requireAuthenticatedUserId(ctx);
1409
1803
  const now = Date.now();
1410
- const node = await ctx.db.get(args.nodeId);
1411
- if (!node) {
1412
- throwStructuredMutationError({
1413
- message: "Entity not found.",
1414
- status: 404,
1415
- code: "NOT_FOUND",
1416
- details: { nodeId: args.nodeId }
1417
- });
1418
- }
1419
- if (!isOntologicalNodeType(node.nodeType)) {
1420
- throwStructuredMutationError({
1421
- message: `Node "${args.nodeId}" is a ${node.nodeType}, not an entity. Use belief archive APIs for epistemic nodes.`,
1422
- status: 400,
1423
- code: "LIFECYCLE_VIOLATION",
1424
- invariantCode: "entity.type_check"
1425
- });
1426
- }
1804
+ const node = await loadEntityForUpdate(ctx, args.nodeId);
1427
1805
  if (node.projectId) {
1428
- await requireScopeWriteAccess(
1429
- ctx,
1430
- node.projectId,
1431
- authenticatedUserId
1432
- );
1806
+ await requireScopeWriteAccess(ctx, node.projectId, authenticatedUserId);
1433
1807
  }
1434
1808
  await ctx.db.patch(args.nodeId, {
1435
1809
  status: "archived",