@lucern/graph-primitives 1.0.28 → 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 +398 -228
  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 +857 -515
  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 +366 -203
  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 -308
  80. package/dist/epistemicBeliefs.backfills.js.map +1 -1
  81. package/dist/epistemicBeliefs.confidence.d.ts +19 -14
  82. package/dist/epistemicBeliefs.confidence.js +634 -423
  83. package/dist/epistemicBeliefs.confidence.js.map +1 -1
  84. package/dist/epistemicBeliefs.core.d.ts +6 -6
  85. package/dist/epistemicBeliefs.core.js +719 -411
  86. package/dist/epistemicBeliefs.core.js.map +1 -1
  87. package/dist/epistemicBeliefs.d.ts +11 -8
  88. package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
  89. package/dist/epistemicBeliefs.forkEvidence.js +8 -28
  90. package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
  91. package/dist/epistemicBeliefs.helpers.d.ts +69 -74
  92. package/dist/epistemicBeliefs.helpers.js +359 -248
  93. package/dist/epistemicBeliefs.helpers.js.map +1 -1
  94. package/dist/epistemicBeliefs.internal.d.ts +5 -5
  95. package/dist/epistemicBeliefs.internal.js +1246 -1044
  96. package/dist/epistemicBeliefs.internal.js.map +1 -1
  97. package/dist/epistemicBeliefs.js +4922 -3608
  98. package/dist/epistemicBeliefs.js.map +1 -1
  99. package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
  100. package/dist/epistemicBeliefs.lifecycle.js +1137 -818
  101. package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
  102. package/dist/epistemicBeliefs.links.d.ts +7 -7
  103. package/dist/epistemicBeliefs.links.js +408 -307
  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 +1063 -613
  114. package/dist/epistemicContracts.evaluators.js.map +1 -1
  115. package/dist/epistemicContracts.handlers.d.ts +15 -32
  116. package/dist/epistemicContracts.handlers.js +2086 -1644
  117. package/dist/epistemicContracts.handlers.js.map +1 -1
  118. package/dist/epistemicContracts.js +1131 -672
  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 +1969 -1205
  136. package/dist/epistemicEdges.js.map +1 -1
  137. package/dist/epistemicEdges.mutations.d.ts +7 -7
  138. package/dist/epistemicEdges.mutations.js +960 -583
  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 +937 -536
  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 +844 -696
  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 +704 -508
  180. package/dist/epistemicNodes.js.map +1 -1
  181. package/dist/epistemicNodes.mutations.d.ts +6 -6
  182. package/dist/epistemicNodes.mutations.js +564 -467
  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 +352 -312
  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 +87 -83
  245. package/dist/index.js +15677 -10594
  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,15 +1,19 @@
1
- import { v } from 'convex/values';
2
- import { checkScopeAccess, requireProjectAccess } from '@lucern/access-control/access';
1
+ import { checkScopeAccess, requireScopeWriteAccess } from '@lucern/access-control/access';
3
2
  import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
- import { componentsGeneric, anyApi, mutationGeneric, queryGeneric } from 'convex/server';
3
+ import { v } from 'convex/values';
4
+ import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
+ import { componentsGeneric, mutationGeneric, queryGeneric } from 'convex/server';
6
+ import '@lucern/contracts';
5
7
  import { generateGlobalId, assertUuidV7Identity } from '@lucern/contracts/ids';
6
8
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
7
- import '@lucern/contracts';
8
9
 
9
10
  // src/epistemicSources.ts
10
- var api = anyApi;
11
+ var unsafeApi = unsafeConvexAnyApi(
12
+ "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
13
+ );
14
+ var api = unsafeApi;
11
15
  componentsGeneric();
12
- var internal = anyApi;
16
+ var internal = unsafeApi;
13
17
  var mutation = mutationGeneric;
14
18
  var query = queryGeneric;
15
19
 
@@ -26,22 +30,19 @@ function debugGraphPrimitiveFallback(message, context) {
26
30
  }
27
31
 
28
32
  // src/embeddingTrigger.ts
33
+ var embeddingActionRef = "embeddingActions:generateEpistemicNodeEmbedding";
29
34
  async function scheduleEmbeddingGeneration(args) {
30
35
  try {
31
- await args.ctx.scheduler.runAfter(
32
- 0,
33
- "embeddingActions:generateEpistemicNodeEmbedding",
34
- {
35
- nodeId: args.nodeId,
36
- projectId: args.projectId ? String(args.projectId) : void 0,
37
- topicId: args.topicId ? String(args.topicId) : void 0,
38
- createdBy: args.createdBy,
39
- nodeType: args.nodeType,
40
- text: args.text.slice(0, 2e4),
41
- hasAnswer: args.hasAnswer,
42
- confidence: args.confidence
43
- }
44
- );
36
+ await args.ctx.scheduler.runAfter(0, embeddingActionRef, {
37
+ nodeId: args.nodeId,
38
+ projectId: args.projectId ? String(args.projectId) : void 0,
39
+ topicId: args.topicId ? String(args.topicId) : void 0,
40
+ createdBy: args.createdBy,
41
+ nodeType: args.nodeType,
42
+ text: args.text.slice(0, 2e4),
43
+ hasAnswer: args.hasAnswer,
44
+ confidence: args.confidence
45
+ });
45
46
  } catch (error) {
46
47
  debugGraphPrimitiveFallback(
47
48
  "[embeddingTrigger] Failed to schedule embedding generation",
@@ -53,6 +54,10 @@ async function scheduleEmbeddingGeneration(args) {
53
54
  );
54
55
  }
55
56
  }
57
+ function insertEpistemicNode(ctx, doc) {
58
+ assertUuidV7Identity("epistemicNodes", doc.globalId);
59
+ return ctx.db.insert("epistemicNodes", doc);
60
+ }
56
61
  var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
57
62
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
58
63
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -87,13 +92,15 @@ function asMappedProjectId(topic) {
87
92
  if (!topic) {
88
93
  return;
89
94
  }
90
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
95
+ const directLegacyProjectId = normalizeScopeValue(
96
+ topic[LEGACY_SCOPE_FIELD]
97
+ );
91
98
  if (directLegacyProjectId) {
92
99
  return directLegacyProjectId;
93
100
  }
94
101
  const metadata = topic.metadata || {};
95
102
  const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
96
- return candidate ? candidate : void 0;
103
+ return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
97
104
  }
98
105
  function normalizeScopeValue(value) {
99
106
  if (typeof value !== "string") {
@@ -118,8 +125,9 @@ function pickPrimaryTopic(candidates) {
118
125
  })[0];
119
126
  }
120
127
  async function findTopicsByScopeAlias(ctx, scopeId) {
128
+ const query2 = ctx.db.query("topics");
121
129
  try {
122
- return await ctx.db.query("topics").withIndex(
130
+ return await query2.withIndex(
123
131
  "by_graph_scope_project",
124
132
  (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
125
133
  ).collect();
@@ -131,7 +139,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
131
139
  scopeId
132
140
  }
133
141
  );
134
- const topics = await ctx.db.query("topics").collect();
142
+ const topics = await query2.collect();
135
143
  return topics.filter((topic) => {
136
144
  const normalizedGlobalId = normalizeScopeValue(topic.globalId);
137
145
  const mappedProjectId = asMappedProjectId(topic);
@@ -187,137 +195,115 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
187
195
  let current = topic;
188
196
  for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
189
197
  current = await ctx.db.get(current.parentTopicId);
190
- if (!current) break;
198
+ if (!current) {
199
+ break;
200
+ }
191
201
  if (!tenantId) {
192
202
  tenantId = normalizeScopeValue(current.tenantId);
193
203
  }
194
204
  if (!workspaceId) {
195
205
  workspaceId = normalizeScopeValue(current.workspaceId);
196
206
  }
197
- if (tenantId && workspaceId) break;
207
+ if (tenantId && workspaceId) {
208
+ break;
209
+ }
198
210
  }
199
211
  return { tenantId, workspaceId };
200
212
  }
201
213
  async function resolveTopicProjectScope(ctx, args) {
202
214
  if (args.topicId) {
203
- let topic = null;
204
- try {
205
- topic = await ctx.db.get(
206
- args.topicId
207
- );
208
- } catch (error) {
209
- debugGraphPrimitiveFallback(
210
- "[topicScope] Failed to load topic by direct id",
211
- {
212
- error,
213
- topicId: args.topicId
214
- }
215
- );
216
- }
217
- if (!topic) {
218
- topic = await tryResolveHostTopicById(ctx, String(args.topicId));
219
- }
220
- if (!topic) {
221
- topic = pickPrimaryTopic(
222
- await findTopicsByScopeAlias(ctx, String(args.topicId))
223
- ) ?? null;
224
- }
225
- if (!topic) {
226
- const nodeScope = await resolveTopicNodeScopeOrNull(
227
- ctx,
228
- String(args.topicId)
229
- );
230
- if (nodeScope) {
231
- return nodeScope;
232
- }
233
- throw new Error(`Topic not found: ${String(args.topicId)}`);
234
- }
235
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
236
- const mapped = asMappedProjectId(topic);
237
- if (mapped) {
238
- return {
239
- topicId: topic._id,
240
- projectId: mapped,
241
- tenantId: inherited.tenantId,
242
- workspaceId: inherited.workspaceId,
243
- source: "topic"
244
- };
245
- }
246
- return {
247
- topicId: topic._id,
248
- tenantId: inherited.tenantId,
249
- workspaceId: inherited.workspaceId,
250
- source: "topic"
251
- };
215
+ return await resolveScopeFromTopicId(ctx, args.topicId);
252
216
  }
253
217
  if (args.projectId) {
254
- let directTopic = null;
255
- try {
256
- directTopic = await ctx.db.get(
257
- args.projectId
258
- );
259
- } catch (error) {
260
- debugGraphPrimitiveFallback(
261
- "[topicScope] Failed to load direct project topic",
262
- {
263
- error,
264
- projectId: args.projectId
265
- }
266
- );
267
- }
268
- if (directTopic) {
269
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
270
- const mapped = asMappedProjectId(directTopic);
271
- return {
272
- topicId: directTopic._id,
273
- projectId: mapped ?? args.projectId,
274
- tenantId: inherited.tenantId,
275
- workspaceId: inherited.workspaceId,
276
- source: "topic_inferred"
277
- };
278
- }
279
- directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
280
- if (directTopic) {
281
- const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
282
- const mapped = asMappedProjectId(directTopic);
283
- return {
284
- topicId: directTopic._id,
285
- projectId: mapped ?? args.projectId,
286
- tenantId: inherited.tenantId,
287
- workspaceId: inherited.workspaceId,
288
- source: "topic_inferred"
289
- };
290
- }
291
- const topics = await findTopicsByScopeAlias(ctx, args.projectId);
292
- const primary = pickPrimaryTopic(topics);
293
- if (primary) {
294
- const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
295
- return {
296
- topicId: primary._id,
297
- projectId: args.projectId,
298
- tenantId: inherited.tenantId,
299
- workspaceId: inherited.workspaceId,
300
- source: "project_mapped_topic"
301
- };
302
- }
303
- const nodeScope = await resolveTopicNodeScopeOrNull(
304
- ctx,
305
- String(args.projectId)
306
- );
307
- if (nodeScope) {
308
- return {
309
- ...nodeScope,
310
- projectId: nodeScope.projectId ?? String(args.projectId)
311
- };
312
- }
313
- throw new Error(
314
- `Legacy project scope ${String(args.projectId)} has no mapped topic.`
315
- );
218
+ return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
316
219
  }
317
220
  throw new Error(
318
221
  "Missing scope: provide topicId (preferred) or legacy projectId alias."
319
222
  );
320
223
  }
224
+ async function resolveScopeFromTopicId(ctx, topicId) {
225
+ const topic = await resolveTopicDocFromTopicId(ctx, topicId);
226
+ if (topic) {
227
+ return await buildTopicScope(ctx, topic, "topic");
228
+ }
229
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
230
+ if (nodeScope) {
231
+ return nodeScope;
232
+ }
233
+ throw new Error(`Topic not found: ${String(topicId)}`);
234
+ }
235
+ async function resolveTopicDocFromTopicId(ctx, topicId) {
236
+ const direct = await tryReadTopicDoc(ctx, topicId, {
237
+ failureLog: "[topicScope] Failed to load topic by direct id",
238
+ idLogKey: "topicId"
239
+ });
240
+ if (direct) {
241
+ return direct;
242
+ }
243
+ const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
244
+ if (hostTopic) {
245
+ return hostTopic;
246
+ }
247
+ return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
248
+ }
249
+ async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
250
+ const directTopic = await resolveDirectLegacyProjectTopic(
251
+ ctx,
252
+ legacyProjectId
253
+ );
254
+ if (directTopic) {
255
+ return await buildTopicScope(ctx, directTopic, "topic_inferred", {
256
+ fallbackProjectId: legacyProjectId
257
+ });
258
+ }
259
+ const primary = pickPrimaryTopic(
260
+ await findTopicsByScopeAlias(ctx, legacyProjectId)
261
+ );
262
+ if (primary) {
263
+ return await buildTopicScope(ctx, primary, "project_mapped_topic", {
264
+ fallbackProjectId: legacyProjectId
265
+ });
266
+ }
267
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
268
+ if (nodeScope) {
269
+ return {
270
+ ...nodeScope,
271
+ projectId: nodeScope.projectId ?? legacyProjectId
272
+ };
273
+ }
274
+ throw new Error(
275
+ `Legacy project scope ${legacyProjectId} has no mapped topic.`
276
+ );
277
+ }
278
+ async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
279
+ const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
280
+ failureLog: "[topicScope] Failed to load direct project topic",
281
+ idLogKey: "projectId"
282
+ });
283
+ return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
284
+ }
285
+ async function tryReadTopicDoc(ctx, id, log) {
286
+ try {
287
+ return await ctx.db.get(id);
288
+ } catch (error) {
289
+ debugGraphPrimitiveFallback(log.failureLog, {
290
+ error,
291
+ [log.idLogKey]: id
292
+ });
293
+ return null;
294
+ }
295
+ }
296
+ async function buildTopicScope(ctx, topic, source, options = {}) {
297
+ const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
298
+ const mapped = asMappedProjectId(topic);
299
+ return {
300
+ topicId: topic._id,
301
+ ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
302
+ tenantId: inherited.tenantId,
303
+ workspaceId: inherited.workspaceId,
304
+ source
305
+ };
306
+ }
321
307
  ({
322
308
  projectId: v.optional(v.string()),
323
309
  topicId: v.optional(v.string())
@@ -359,12 +345,11 @@ function assertWorkspaceScopedEpistemicNodeScope(args) {
359
345
  }
360
346
  });
361
347
  }
362
- async function insertEpistemicNode(ctx, doc) {
363
- assertUuidV7Identity("epistemicNodes", doc.globalId);
364
- return ctx.db.insert("epistemicNodes", doc);
365
- }
366
348
 
367
349
  // src/epistemicSources.ts
350
+ var TRAILING_SLASHES_PATTERN = /\/+$/;
351
+ var HASH_MODULUS = 2 ** 32;
352
+ var HASH_SIGN_BOUNDARY = 2 ** 31;
368
353
  function throwStructuredSourceError(args) {
369
354
  const error = new Error(args.message);
370
355
  error.status = args.status;
@@ -376,28 +361,39 @@ function throwStructuredSourceError(args) {
376
361
  }
377
362
  function normalizeString(value) {
378
363
  if (typeof value !== "string") {
379
- return void 0;
364
+ return;
380
365
  }
381
366
  const normalized = value.trim();
382
367
  return normalized.length > 0 ? normalized : void 0;
383
368
  }
369
+ function readConvexId(value) {
370
+ const normalized = normalizeString(value);
371
+ return normalized ? normalized : null;
372
+ }
384
373
  function normalizeNumber(value) {
385
374
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
386
375
  }
387
376
  function asRecord(value) {
388
377
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
389
378
  }
379
+ function isSourceNodeDoc(value) {
380
+ const record = asRecord(value);
381
+ return readConvexId(record._id) !== null && normalizeString(record.nodeType) === "source";
382
+ }
390
383
  function normalizeMetadata(metadata) {
391
384
  const record = asRecord(metadata);
392
385
  const { url: _ignoredUrl, contentSha: _ignoredSha, ...rest } = record;
393
386
  return rest;
394
387
  }
388
+ function toSignedInt32(value) {
389
+ const normalized = (value % HASH_MODULUS + HASH_MODULUS) % HASH_MODULUS;
390
+ return normalized >= HASH_SIGN_BOUNDARY ? normalized - HASH_MODULUS : normalized;
391
+ }
395
392
  function generateSourceContentHash(identity) {
396
393
  const content = `source:${identity.trim().toLowerCase()}`;
397
394
  let hash = 5381;
398
395
  for (let index = 0; index < content.length; index += 1) {
399
- hash = (hash << 5) + hash + content.charCodeAt(index);
400
- hash &= hash;
396
+ hash = toSignedInt32(hash * 33 + content.charCodeAt(index));
401
397
  }
402
398
  return Math.abs(hash).toString(16).padStart(8, "0");
403
399
  }
@@ -428,7 +424,7 @@ function normalizeSourceUrl(url) {
428
424
  const protocol = parsed.protocol.toLowerCase();
429
425
  const hostname = parsed.hostname.toLowerCase();
430
426
  const port = parsed.port ? `:${parsed.port}` : "";
431
- const pathname = parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/+$/, "");
427
+ const pathname = parsed.pathname === "/" ? "" : parsed.pathname.replace(TRAILING_SLASHES_PATTERN, "");
432
428
  const sortedEntries = [...parsed.searchParams.entries()].sort(
433
429
  ([aKey, aValue], [bKey, bValue]) => aKey === bKey ? aValue.localeCompare(bValue) : aKey.localeCompare(bKey)
434
430
  );
@@ -440,13 +436,13 @@ function normalizeSourceUrl(url) {
440
436
  return `${protocol}//${hostname}${port}${pathname}${search.length > 0 ? `?${search}` : ""}`;
441
437
  }
442
438
  async function findSourceByIdentity(ctx, args) {
443
- const sourceNodes = await ctx.db.query("epistemicNodes").withIndex(
444
- "by_nodeType",
445
- (q) => q.eq("nodeType", "source")
446
- ).collect();
439
+ const sourceNodes = await ctx.db.query("epistemicNodes").withIndex("by_nodeType", (q) => q.eq("nodeType", "source")).collect();
447
440
  let urlMatch = null;
448
441
  let shaMatch = null;
449
442
  for (const node of sourceNodes) {
443
+ if (!isSourceNodeDoc(node)) {
444
+ continue;
445
+ }
450
446
  const metadata = asRecord(node.metadata);
451
447
  if (args.normalizedUrl && normalizeString(metadata.url) === args.normalizedUrl && !urlMatch) {
452
448
  urlMatch = node;
@@ -483,18 +479,198 @@ function buildSourceMetadata(args) {
483
479
  if (args.capturedAt !== void 0) {
484
480
  nextMetadata.capturedAt = args.capturedAt;
485
481
  }
486
- if (!Object.prototype.hasOwnProperty.call(existingMetadata, "url") && args.normalizedUrl) {
482
+ if (!Object.hasOwn(existingMetadata, "url") && args.normalizedUrl) {
487
483
  nextMetadata.url = args.normalizedUrl;
488
- } else if (Object.prototype.hasOwnProperty.call(existingMetadata, "url")) {
484
+ } else if (Object.hasOwn(existingMetadata, "url")) {
489
485
  nextMetadata.url = existingMetadata.url;
490
486
  }
491
- if (!Object.prototype.hasOwnProperty.call(existingMetadata, "contentSha") && args.sha) {
487
+ if (!Object.hasOwn(existingMetadata, "contentSha") && args.sha) {
492
488
  nextMetadata.contentSha = args.sha;
493
- } else if (Object.prototype.hasOwnProperty.call(existingMetadata, "contentSha")) {
489
+ } else if (Object.hasOwn(existingMetadata, "contentSha")) {
494
490
  nextMetadata.contentSha = existingMetadata.contentSha;
495
491
  }
496
492
  return nextMetadata;
497
493
  }
494
+ function readSourceIdentity(args) {
495
+ const sourceUrl = normalizeString(args.url);
496
+ const normalizedUrl = sourceUrl ? normalizeSourceUrl(sourceUrl) : void 0;
497
+ const sha = normalizeString(args.sha);
498
+ if (!(normalizedUrl || sha)) {
499
+ throwStructuredSourceError({
500
+ message: "Source identity requires a URL or content SHA.",
501
+ status: 400,
502
+ code: "INVALID_REQUEST",
503
+ invariantCode: "source.identity.required",
504
+ suggestion: "Provide url or sha."
505
+ });
506
+ }
507
+ return { normalizedUrl, sha };
508
+ }
509
+ async function syncSourceNodeToNeo4j(args) {
510
+ await args.ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
511
+ nodeId: args.nodeId,
512
+ operation: "upsert"
513
+ });
514
+ }
515
+ async function insertSourceAudit(ctx, audit) {
516
+ await ctx.db.insert("epistemicAudit", { ...audit });
517
+ }
518
+ async function updateExistingSource(args) {
519
+ if (args.existing.topicId && !await checkScopeAccess(
520
+ args.ctx,
521
+ String(args.existing.topicId),
522
+ args.userId
523
+ )) {
524
+ throwStructuredSourceError({
525
+ message: "Access denied to existing source scope.",
526
+ status: 403,
527
+ code: "FORBIDDEN",
528
+ invariantCode: "policy.scope_required",
529
+ suggestion: "Request access to the source topic scope and retry.",
530
+ details: { topicId: args.existing.topicId }
531
+ });
532
+ }
533
+ const existingMetadata = asRecord(args.existing.metadata);
534
+ const existingSha = normalizeString(existingMetadata.contentSha);
535
+ if (args.sha && existingSha && existingSha !== args.sha) {
536
+ throwStructuredSourceError({
537
+ message: "Same URL cannot be reused with a different content hash.",
538
+ status: 409,
539
+ code: "CONTENT_SHA_MISMATCH",
540
+ invariantCode: "source.content_hash_immutable",
541
+ suggestion: "Same URL, different content. Create a new source with a distinct URL or use fork semantics.",
542
+ details: {
543
+ sourceId: String(args.existing._id),
544
+ url: existingMetadata.url,
545
+ existingSha,
546
+ receivedSha: args.sha
547
+ }
548
+ });
549
+ }
550
+ const nextMetadata = buildSourceMetadata({
551
+ existingMetadata,
552
+ kind: args.kind,
553
+ capturedAt: normalizeNumber(args.capturedAt),
554
+ metadata: args.metadataPatch
555
+ });
556
+ const patch = {
557
+ metadata: nextMetadata,
558
+ updatedAt: args.now
559
+ };
560
+ const title = normalizeString(args.title);
561
+ if (title) {
562
+ patch.title = title;
563
+ patch.canonicalText = title;
564
+ }
565
+ await args.ctx.db.patch(args.existing._id, patch);
566
+ await syncSourceNodeToNeo4j({
567
+ ctx: args.ctx,
568
+ nodeId: args.existing._id
569
+ });
570
+ await insertSourceAudit(args.ctx, {
571
+ entityType: "source",
572
+ entityId: String(args.existing._id),
573
+ changeType: "source_metadata_updated",
574
+ changedAt: args.now,
575
+ changedBy: args.userId,
576
+ isAgent: false,
577
+ topicId: args.existing.topicId ? String(args.existing.topicId) : void 0,
578
+ previousState: {
579
+ title: args.existing.title,
580
+ metadata: existingMetadata
581
+ },
582
+ newState: {
583
+ title: title ?? args.existing.title,
584
+ metadata: nextMetadata
585
+ }
586
+ });
587
+ const updated = await args.ctx.db.get(args.existing._id);
588
+ return isSourceNodeDoc(updated) ? updated : args.existing;
589
+ }
590
+ async function createSource(args) {
591
+ if (!normalizeString(args.topicId)) {
592
+ throwStructuredSourceError({
593
+ message: "topicId is required when creating a new source.",
594
+ status: 400,
595
+ code: "INVALID_REQUEST",
596
+ invariantCode: "source.context_required",
597
+ suggestion: "Provide topicId when creating a new source node."
598
+ });
599
+ }
600
+ const scope = await resolveTopicProjectScope(args.ctx, {
601
+ topicId: args.topicId
602
+ });
603
+ assertWorkspaceScopedEpistemicNodeScope({
604
+ scope,
605
+ nodeType: "source",
606
+ mutationName: "epistemicSources.upsertSource"
607
+ });
608
+ if (scope.projectId) {
609
+ await requireScopeWriteAccess(args.ctx, scope.projectId, args.userId);
610
+ }
611
+ const globalId = generateGlobalId();
612
+ const title = normalizeString(args.title);
613
+ const nextMetadata = buildSourceMetadata({
614
+ normalizedUrl: args.normalizedUrl,
615
+ sha: args.sha,
616
+ kind: args.kind,
617
+ capturedAt: normalizeNumber(args.capturedAt),
618
+ metadata: args.metadataPatch
619
+ });
620
+ const nodeId = await insertEpistemicNode(args.ctx, {
621
+ globalId,
622
+ topicId: scope.topicId,
623
+ projectId: scope.projectId,
624
+ tenantId: scope.tenantId,
625
+ workspaceId: scope.workspaceId,
626
+ nodeType: "source",
627
+ epistemicLayer: "L1",
628
+ canonicalText: title ?? args.normalizedUrl ?? "source",
629
+ contentHash: args.sha ?? generateSourceContentHash(args.normalizedUrl ?? title ?? "source"),
630
+ ...title ? { title } : {},
631
+ status: "active",
632
+ createdAt: args.now,
633
+ updatedAt: args.now,
634
+ createdBy: args.userId,
635
+ metadata: nextMetadata
636
+ });
637
+ await syncSourceNodeToNeo4j({
638
+ ctx: args.ctx,
639
+ nodeId
640
+ });
641
+ await scheduleEmbeddingGeneration({
642
+ ctx: args.ctx,
643
+ nodeId,
644
+ projectId: scope.projectId,
645
+ topicId: scope.topicId,
646
+ createdBy: args.userId,
647
+ nodeType: "source",
648
+ text: sourceEmbeddingText({
649
+ title,
650
+ url: args.normalizedUrl,
651
+ kind: args.kind,
652
+ metadata: nextMetadata
653
+ })
654
+ });
655
+ await insertSourceAudit(args.ctx, {
656
+ entityType: "source",
657
+ entityId: String(nodeId),
658
+ changeType: "source_created",
659
+ changedAt: args.now,
660
+ changedBy: args.userId,
661
+ isAgent: false,
662
+ topicId: scope.topicId ? String(scope.topicId) : void 0,
663
+ previousState: null,
664
+ newState: {
665
+ title,
666
+ url: args.normalizedUrl,
667
+ contentSha: args.sha,
668
+ kind: args.kind
669
+ }
670
+ });
671
+ const created = await args.ctx.db.get(nodeId);
672
+ return isSourceNodeDoc(created) ? created : null;
673
+ }
498
674
  function sourceEmbeddingText(args) {
499
675
  const lines = [
500
676
  normalizeString(args.title),
@@ -517,169 +693,35 @@ var upsertSource = mutation({
517
693
  },
518
694
  returns: permissiveReturn,
519
695
  handler: async (ctx, args) => {
520
- const sourceUrl = normalizeString(args.url);
521
- const normalizedUrl = sourceUrl ? normalizeSourceUrl(sourceUrl) : void 0;
522
- const sha = normalizeString(args.sha);
523
- if (!normalizedUrl && !sha) {
524
- throwStructuredSourceError({
525
- message: "Source identity requires a URL or content SHA.",
526
- status: 400,
527
- code: "INVALID_REQUEST",
528
- invariantCode: "source.identity.required",
529
- suggestion: "Provide url or sha."
530
- });
531
- }
696
+ const { normalizedUrl, sha } = readSourceIdentity(args);
532
697
  const now = Date.now();
533
698
  const metadataPatch = normalizeMetadata(args.metadata);
534
699
  const existing = await findSourceByIdentity(ctx, { normalizedUrl, sha });
535
700
  if (existing) {
536
- if (existing.topicId && !await checkScopeAccess(ctx, String(existing.topicId), args.userId)) {
537
- throwStructuredSourceError({
538
- message: "Access denied to existing source scope.",
539
- status: 403,
540
- code: "FORBIDDEN",
541
- invariantCode: "policy.scope_required",
542
- suggestion: "Request access to the source topic scope and retry.",
543
- details: { topicId: existing.topicId }
544
- });
545
- }
546
- const existingMetadata = asRecord(existing.metadata);
547
- const existingSha = normalizeString(existingMetadata.contentSha);
548
- if (sha && existingSha && existingSha !== sha) {
549
- throwStructuredSourceError({
550
- message: "Same URL cannot be reused with a different content hash.",
551
- status: 409,
552
- code: "CONTENT_SHA_MISMATCH",
553
- invariantCode: "source.content_hash_immutable",
554
- suggestion: "Same URL, different content. Create a new source with a distinct URL or use fork semantics.",
555
- details: {
556
- sourceId: String(existing._id),
557
- url: existingMetadata.url,
558
- existingSha,
559
- receivedSha: sha
560
- }
561
- });
562
- }
563
- const nextMetadata2 = buildSourceMetadata({
564
- existingMetadata,
701
+ return await updateExistingSource({
702
+ ctx,
703
+ existing,
565
704
  kind: args.kind,
566
- capturedAt: normalizeNumber(args.capturedAt),
567
- metadata: metadataPatch
705
+ capturedAt: args.capturedAt,
706
+ metadataPatch,
707
+ now,
708
+ sha,
709
+ title: args.title,
710
+ userId: args.userId
568
711
  });
569
- const patch = {
570
- metadata: nextMetadata2,
571
- updatedAt: now
572
- };
573
- const title2 = normalizeString(args.title);
574
- if (title2) {
575
- patch.title = title2;
576
- patch.canonicalText = title2;
577
- }
578
- await ctx.db.patch(existing._id, patch);
579
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
580
- nodeId: existing._id,
581
- operation: "upsert"
582
- });
583
- await ctx.db.insert("epistemicAudit", {
584
- entityType: "source",
585
- entityId: existing._id,
586
- changeType: "source_metadata_updated",
587
- changedAt: now,
588
- changedBy: args.userId,
589
- isAgent: false,
590
- topicId: existing.topicId ? String(existing.topicId) : void 0,
591
- previousState: {
592
- title: existing.title,
593
- metadata: existingMetadata
594
- },
595
- newState: {
596
- title: title2 ?? existing.title,
597
- metadata: nextMetadata2
598
- }
599
- });
600
- return await ctx.db.get(existing._id) ?? existing;
601
712
  }
602
- if (!normalizeString(args.topicId)) {
603
- throwStructuredSourceError({
604
- message: "topicId is required when creating a new source.",
605
- status: 400,
606
- code: "INVALID_REQUEST",
607
- invariantCode: "source.context_required",
608
- suggestion: "Provide topicId when creating a new source node."
609
- });
610
- }
611
- const scope = await resolveTopicProjectScope(ctx, {
612
- topicId: args.topicId
613
- });
614
- assertWorkspaceScopedEpistemicNodeScope({
615
- scope,
616
- nodeType: "source",
617
- mutationName: "epistemicSources.upsertSource"
618
- });
619
- if (scope.projectId) {
620
- await requireProjectAccess(ctx, scope.projectId, args.userId);
621
- }
622
- const globalId = generateGlobalId();
623
- const title = normalizeString(args.title);
624
- const nextMetadata = buildSourceMetadata({
713
+ return await createSource({
714
+ ctx,
625
715
  normalizedUrl,
626
716
  sha,
627
717
  kind: args.kind,
628
- capturedAt: normalizeNumber(args.capturedAt),
629
- metadata: metadataPatch
718
+ capturedAt: args.capturedAt,
719
+ metadataPatch,
720
+ now,
721
+ title: args.title,
722
+ topicId: args.topicId,
723
+ userId: args.userId
630
724
  });
631
- const nodeId = await insertEpistemicNode(ctx, {
632
- globalId,
633
- topicId: scope.topicId,
634
- projectId: scope.projectId,
635
- tenantId: scope.tenantId,
636
- workspaceId: scope.workspaceId,
637
- nodeType: "source",
638
- epistemicLayer: "L1",
639
- canonicalText: title ?? normalizedUrl ?? "source",
640
- contentHash: sha ?? generateSourceContentHash(normalizedUrl ?? title ?? "source"),
641
- ...title ? { title } : {},
642
- status: "active",
643
- createdAt: now,
644
- updatedAt: now,
645
- createdBy: args.userId,
646
- metadata: nextMetadata
647
- });
648
- await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
649
- nodeId,
650
- operation: "upsert"
651
- });
652
- await scheduleEmbeddingGeneration({
653
- ctx,
654
- nodeId,
655
- projectId: scope.projectId,
656
- topicId: scope.topicId,
657
- createdBy: args.userId,
658
- nodeType: "source",
659
- text: sourceEmbeddingText({
660
- title,
661
- url: normalizedUrl,
662
- kind: args.kind,
663
- metadata: nextMetadata
664
- })
665
- });
666
- await ctx.db.insert("epistemicAudit", {
667
- entityType: "source",
668
- entityId: nodeId,
669
- changeType: "source_created",
670
- changedAt: now,
671
- changedBy: args.userId,
672
- isAgent: false,
673
- topicId: scope.topicId ? String(scope.topicId) : void 0,
674
- previousState: null,
675
- newState: {
676
- title,
677
- url: normalizedUrl,
678
- contentSha: sha,
679
- kind: args.kind
680
- }
681
- });
682
- return await ctx.db.get(nodeId);
683
725
  }
684
726
  });
685
727
  var getById = query({
@@ -689,14 +731,12 @@ var getById = query({
689
731
  },
690
732
  returns: permissiveReturn,
691
733
  handler: async (ctx, args) => {
692
- const id = args.nodeId ?? args.sourceId;
734
+ const id = args.nodeId ?? readConvexId(args.sourceId);
693
735
  if (!id) {
694
736
  return null;
695
737
  }
696
- const node = await ctx.db.get(
697
- id
698
- );
699
- if (!node || node.nodeType !== "source") {
738
+ const node = await ctx.db.get(id);
739
+ if (!isSourceNodeDoc(node)) {
700
740
  return null;
701
741
  }
702
742
  return node;
@@ -710,7 +750,7 @@ var findByUrl = query({
710
750
  handler: async (ctx, args) => {
711
751
  const normalizedUrl = normalizeSourceUrl(args.url);
712
752
  const node = await findSourceByIdentity(ctx, { normalizedUrl });
713
- return node && node.nodeType === "source" ? node : null;
753
+ return node?.nodeType === "source" ? node : null;
714
754
  }
715
755
  });
716
756
  var findBySha = query({
@@ -724,7 +764,7 @@ var findBySha = query({
724
764
  return null;
725
765
  }
726
766
  const node = await findSourceByIdentity(ctx, { sha });
727
- return node && node.nodeType === "source" ? node : null;
767
+ return node?.nodeType === "source" ? node : null;
728
768
  }
729
769
  });
730
770