@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.
- package/dist/{beliefDecay-DZ6tkLYq.d.ts → beliefDecay-BmkEk5OJ.d.ts} +3 -3
- package/dist/beliefDecay.d.ts +1 -1
- package/dist/beliefDecay.js +448 -314
- package/dist/beliefDecay.js.map +1 -1
- package/dist/{beliefEvidenceLinks-CWOXxxJg.d.ts → beliefEvidenceLinks-BzfjON_6.d.ts} +13 -13
- package/dist/beliefEvidenceLinks.d.ts +1 -1
- package/dist/beliefEvidenceLinks.js +843 -624
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/beliefEvidenceLinks.operational.d.ts +7 -5
- package/dist/beliefEvidenceLinks.operational.js +91 -18
- package/dist/beliefEvidenceLinks.operational.js.map +1 -1
- package/dist/beliefLifecycle.js.map +1 -1
- package/dist/confidencePropagationDispatch.d.ts +28 -27
- package/dist/confidencePropagationDispatch.js +157 -99
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/{contradictions-51VLsESq.d.ts → contradictions-BATPuZTL.d.ts} +10 -10
- package/dist/contradictions.d.ts +1 -1
- package/dist/contradictions.js +398 -228
- package/dist/contradictions.js.map +1 -1
- package/dist/convex.d.ts +65 -30
- package/dist/convex.js +7 -3
- package/dist/convex.js.map +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/edgeValidation.js +293 -85
- package/dist/edgeValidation.js.map +1 -1
- package/dist/edges/contains.d.ts +1 -1
- package/dist/edges/contains.js.map +1 -1
- package/dist/edges/contradicts.d.ts +1 -1
- package/dist/edges/contradicts.js.map +1 -1
- package/dist/edges/{dependsOn.d.ts → depends-on.d.ts} +1 -1
- package/dist/edges/{dependsOn.js → depends-on.js} +4 -4
- package/dist/edges/depends-on.js.map +1 -0
- package/dist/edges/{derivedFrom.d.ts → derived-from.d.ts} +1 -1
- package/dist/edges/{derivedFrom.js → derived-from.js} +3 -3
- package/dist/edges/derived-from.js.map +1 -0
- package/dist/edges/elaborates.d.ts +1 -1
- package/dist/edges/elaborates.js.map +1 -1
- package/dist/edges/index.d.ts +7 -3
- package/dist/edges/index.js +7 -4
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.d.ts +1 -1
- package/dist/edges/informs.js.map +1 -1
- package/dist/edges/{propagationTypes.d.ts → propagation-types.d.ts} +14 -14
- package/dist/edges/{propagationTypes.js → propagation-types.js} +3 -3
- package/dist/edges/propagation-types.js.map +1 -0
- package/dist/edges/refutes.d.ts +1 -1
- package/dist/edges/refutes.js.map +1 -1
- package/dist/edges/supports.d.ts +1 -1
- package/dist/edges/supports.js.map +1 -1
- package/dist/edges/tests.d.ts +1 -1
- package/dist/edges/tests.js.map +1 -1
- package/dist/edges/utils.d.ts +1 -1
- package/dist/edges/utils.js.map +1 -1
- package/dist/embeddingTrigger.d.ts +14 -6
- package/dist/embeddingTrigger.js +11 -14
- package/dist/embeddingTrigger.js.map +1 -1
- package/dist/{entityBridge-DMaKooYn.d.ts → entityBridge-BhVDM3pc.d.ts} +5 -5
- package/dist/entityBridge.d.ts +1 -1
- package/dist/entityBridge.js +602 -225
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityCanonicalMatch.d.ts +14 -12
- package/dist/entityCanonicalMatch.js.map +1 -1
- package/dist/{entityLifecycle-CvgSK5FV.d.ts → entityLifecycle-BsfCz9pS.d.ts} +5 -9
- package/dist/entityLifecycle.d.ts +1 -1
- package/dist/entityLifecycle.js +857 -515
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/{entityValidation-KLZ_Xl2D.d.ts → entityValidation-B1yNEHJx.d.ts} +7 -6
- package/dist/entityValidation.d.ts +3 -1
- package/dist/entityValidation.js +60 -8
- package/dist/entityValidation.js.map +1 -1
- package/dist/{epistemicAnswers-C5ib4z6_.d.ts → epistemicAnswers-f47YMu9U.d.ts} +6 -6
- package/dist/epistemicAnswers.d.ts +1 -1
- package/dist/epistemicAnswers.js +587 -545
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.admin.d.ts +8 -8
- package/dist/epistemicBeliefs.admin.js +366 -203
- package/dist/epistemicBeliefs.admin.js.map +1 -1
- package/dist/epistemicBeliefs.backfills.d.ts +8 -8
- package/dist/epistemicBeliefs.backfills.js +655 -308
- package/dist/epistemicBeliefs.backfills.js.map +1 -1
- package/dist/epistemicBeliefs.confidence.d.ts +19 -14
- package/dist/epistemicBeliefs.confidence.js +634 -423
- package/dist/epistemicBeliefs.confidence.js.map +1 -1
- package/dist/epistemicBeliefs.core.d.ts +6 -6
- package/dist/epistemicBeliefs.core.js +719 -411
- package/dist/epistemicBeliefs.core.js.map +1 -1
- package/dist/epistemicBeliefs.d.ts +11 -8
- package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
- package/dist/epistemicBeliefs.forkEvidence.js +8 -28
- package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
- package/dist/epistemicBeliefs.helpers.d.ts +69 -74
- package/dist/epistemicBeliefs.helpers.js +359 -248
- package/dist/epistemicBeliefs.helpers.js.map +1 -1
- package/dist/epistemicBeliefs.internal.d.ts +5 -5
- package/dist/epistemicBeliefs.internal.js +1246 -1044
- package/dist/epistemicBeliefs.internal.js.map +1 -1
- package/dist/epistemicBeliefs.js +4922 -3608
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
- package/dist/epistemicBeliefs.lifecycle.js +1137 -818
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
- package/dist/epistemicBeliefs.links.d.ts +7 -7
- package/dist/epistemicBeliefs.links.js +408 -307
- package/dist/epistemicBeliefs.links.js.map +1 -1
- package/dist/epistemicBeliefs.queries.d.ts +4 -4
- package/dist/epistemicBeliefs.queries.js +175 -20
- package/dist/epistemicBeliefs.queries.js.map +1 -1
- package/dist/epistemicBeliefs.topicAnchor.d.ts +6 -4
- package/dist/epistemicBeliefs.topicAnchor.js +12 -5
- package/dist/epistemicBeliefs.topicAnchor.js.map +1 -1
- package/dist/epistemicContracts.d.ts +28 -3
- package/dist/epistemicContracts.evaluators.d.ts +2 -0
- package/dist/epistemicContracts.evaluators.js +1063 -613
- package/dist/epistemicContracts.evaluators.js.map +1 -1
- package/dist/epistemicContracts.handlers.d.ts +15 -32
- package/dist/epistemicContracts.handlers.js +2086 -1644
- package/dist/epistemicContracts.handlers.js.map +1 -1
- package/dist/epistemicContracts.js +1131 -672
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicContracts.metrics.d.ts +2 -0
- package/dist/epistemicContracts.metrics.js +375 -158
- package/dist/epistemicContracts.metrics.js.map +1 -1
- package/dist/epistemicContracts.types.d.ts +87 -81
- package/dist/epistemicEdgeCreation.d.ts +2 -0
- package/dist/epistemicEdgeCreation.js +87 -16
- package/dist/epistemicEdgeCreation.js.map +1 -1
- package/dist/{epistemicEdges-BF-cn4i3.d.ts → epistemicEdges-BGBh0QSP.d.ts} +4 -7
- package/dist/epistemicEdges.d.ts +6 -5
- package/dist/epistemicEdges.handlers.d.ts +3 -3
- package/dist/epistemicEdges.handlers.js +129 -24
- package/dist/epistemicEdges.handlers.js.map +1 -1
- package/dist/epistemicEdges.helpers.d.ts +6 -4
- package/dist/epistemicEdges.helpers.js +37 -2
- package/dist/epistemicEdges.helpers.js.map +1 -1
- package/dist/epistemicEdges.js +1969 -1205
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +7 -7
- package/dist/epistemicEdges.mutations.js +960 -583
- package/dist/epistemicEdges.mutations.js.map +1 -1
- package/dist/epistemicEdges.queries.d.ts +16 -16
- package/dist/epistemicEdges.queries.js +639 -367
- package/dist/epistemicEdges.queries.js.map +1 -1
- package/dist/epistemicEdges.types.d.ts +10 -8
- package/dist/epistemicEvidence.d.ts +4 -1
- package/dist/epistemicEvidence.js +937 -536
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicEvidenceHelpers.d.ts +26 -10
- package/dist/epistemicEvidenceHelpers.js +239 -200
- package/dist/epistemicEvidenceHelpers.js.map +1 -1
- package/dist/epistemicEvidenceMutations.d.ts +8 -8
- package/dist/epistemicEvidenceMutations.js +844 -696
- package/dist/epistemicEvidenceMutations.js.map +1 -1
- package/dist/epistemicEvidenceQueries.d.ts +8 -8
- package/dist/epistemicEvidenceQueries.js +514 -238
- package/dist/epistemicEvidenceQueries.js.map +1 -1
- package/dist/epistemicHelpers.d.ts +4 -2
- package/dist/epistemicHelpers.js +308 -134
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicInsert.d.ts +16 -4
- package/dist/epistemicInsert.js +6 -3
- package/dist/epistemicInsert.js.map +1 -1
- package/dist/epistemicLayerRules.d.ts +10 -8
- package/dist/epistemicLayerRules.js +1 -5
- package/dist/epistemicLayerRules.js.map +1 -1
- package/dist/{epistemicLinking-CfE00tHJ.d.ts → epistemicLinking-CsCDv2cN.d.ts} +3 -3
- package/dist/epistemicLinking.d.ts +1 -1
- package/dist/epistemicLinking.js +177 -100
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodeCreation.d.ts +2 -0
- package/dist/epistemicNodeCreation.js +203 -40
- package/dist/epistemicNodeCreation.js.map +1 -1
- package/dist/{epistemicNodes-BCQxpYx_.d.ts → epistemicNodes-CokAgBHg.d.ts} +3 -3
- package/dist/epistemicNodes.d.ts +3 -3
- package/dist/epistemicNodes.helpers.d.ts +24 -15
- package/dist/epistemicNodes.helpers.js.map +1 -1
- package/dist/epistemicNodes.internal.d.ts +6 -6
- package/dist/epistemicNodes.internal.js +389 -319
- package/dist/epistemicNodes.internal.js.map +1 -1
- package/dist/epistemicNodes.js +704 -508
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +6 -6
- package/dist/epistemicNodes.mutations.js +564 -467
- package/dist/epistemicNodes.mutations.js.map +1 -1
- package/dist/epistemicNodes.queries.d.ts +8 -8
- package/dist/epistemicNodes.queries.js +311 -314
- package/dist/epistemicNodes.queries.js.map +1 -1
- package/dist/epistemicNodes.validators.d.ts +2 -2
- package/dist/epistemicNodes.validators.js.map +1 -1
- package/dist/epistemicQuestions.conviction.d.ts +8 -8
- package/dist/epistemicQuestions.conviction.js +665 -484
- package/dist/epistemicQuestions.conviction.js.map +1 -1
- package/dist/epistemicQuestions.create.d.ts +4 -4
- package/dist/epistemicQuestions.create.js +640 -612
- package/dist/epistemicQuestions.create.js.map +1 -1
- package/dist/epistemicQuestions.d.ts +8 -5
- package/dist/epistemicQuestions.evidence.d.ts +2 -2
- package/dist/epistemicQuestions.evidence.js +475 -383
- package/dist/epistemicQuestions.evidence.js.map +1 -1
- package/dist/epistemicQuestions.helpers.d.ts +125 -24
- package/dist/epistemicQuestions.helpers.js +240 -209
- package/dist/epistemicQuestions.helpers.js.map +1 -1
- package/dist/epistemicQuestions.js +3474 -2823
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicQuestions.lifecycle.d.ts +2 -2
- package/dist/epistemicQuestions.lifecycle.js +607 -546
- package/dist/epistemicQuestions.lifecycle.js.map +1 -1
- package/dist/epistemicQuestions.queries.d.ts +12 -7
- package/dist/epistemicQuestions.queries.js +305 -244
- package/dist/epistemicQuestions.queries.js.map +1 -1
- package/dist/epistemicQuestions.sprint.d.ts +2 -2
- package/dist/epistemicQuestions.sprint.js +600 -394
- package/dist/epistemicQuestions.sprint.js.map +1 -1
- package/dist/epistemicQuestions.tail.d.ts +6 -6
- package/dist/epistemicQuestions.tail.js +572 -433
- package/dist/epistemicQuestions.tail.js.map +1 -1
- package/dist/{epistemicSources-dlKj58Jp.d.ts → epistemicSources-DQtaEkWs.d.ts} +4 -4
- package/dist/epistemicSources.d.ts +1 -1
- package/dist/epistemicSources.js +352 -312
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.d.ts +8 -6
- package/dist/evaluators/index.js +399 -167
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lint-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{lintCheckerEvaluator.js → lint-checker-evaluator.js} +10 -5
- package/dist/evaluators/lint-checker-evaluator.js.map +1 -0
- package/dist/evaluators/{sentryCheckerEvaluator.d.ts → sentry-checker-evaluator.d.ts} +7 -2
- package/dist/evaluators/{sentryCheckerEvaluator.js → sentry-checker-evaluator.js} +3 -3
- package/dist/evaluators/sentry-checker-evaluator.js.map +1 -0
- package/dist/evaluators/shared.d.ts +2 -2
- package/dist/evaluators/shared.js +3 -1
- package/dist/evaluators/shared.js.map +1 -1
- package/dist/evaluators/{testRunnerEvaluator.d.ts → test-runner-evaluator.d.ts} +6 -1
- package/dist/evaluators/{testRunnerEvaluator.js → test-runner-evaluator.js} +6 -4
- package/dist/evaluators/test-runner-evaluator.js.map +1 -0
- package/dist/evaluators/tsc-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{tscCheckerEvaluator.js → tsc-checker-evaluator.js} +10 -5
- package/dist/evaluators/tsc-checker-evaluator.js.map +1 -0
- package/dist/graphTypes.js +6 -2
- package/dist/graphTypes.js.map +1 -1
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.js +313 -93
- package/dist/helpers.js.map +1 -1
- package/dist/{index-C-Kyd7hD.d.ts → index-DZxyC9Pb.d.ts} +7 -6
- package/dist/index.d.ts +87 -83
- package/dist/index.js +15677 -10594
- package/dist/index.js.map +1 -1
- package/dist/invariantEnforcement.d.ts +3 -3
- package/dist/invariantEnforcement.js.map +1 -1
- package/dist/logicalRoleInference.d.ts +2 -0
- package/dist/logicalRoleInference.js +1 -1
- package/dist/logicalRoleInference.js.map +1 -1
- package/dist/matcherFeedbackUtils.d.ts +2 -2
- package/dist/matcherFeedbackUtils.js.map +1 -1
- package/dist/{ontology-matching-C6rrz2VP.d.ts → ontology-matching-C-mYFrir.d.ts} +16 -16
- package/dist/ontology-matching.d.ts +1 -1
- package/dist/{ontologyApproval-CFYmqKmk.d.ts → ontologyApproval-BVt0feJi.d.ts} +10 -10
- package/dist/ontologyApproval.d.ts +1 -1
- package/dist/ontologyApproval.js +7 -1
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.d.ts +14 -24
- package/dist/ontologyDefinitions.js +269 -34
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyHelpers.d.ts +13 -13
- package/dist/ontologyHelpers.js.map +1 -1
- package/dist/{ontologyRegistry-B67rPJ16.d.ts → ontologyRegistry-CljS-ENv.d.ts} +2 -2
- package/dist/ontologyRegistry.d.ts +1 -1
- package/dist/ontologyRegistry.js +34 -6
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/{projectionReconciliation-jww2fBI0.d.ts → projectionReconciliation-DnrSgHSQ.d.ts} +4 -4
- package/dist/projectionReconciliation.d.ts +1 -1
- package/dist/projectionReconciliation.js +57 -10
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/{projectionStaleness-CmdbpjVK.d.ts → projectionStaleness-C8ImQ2zP.d.ts} +17 -17
- package/dist/projectionStaleness.d.ts +1 -1
- package/dist/projectionStaleness.js +8 -2
- package/dist/projectionStaleness.js.map +1 -1
- package/dist/proof-attestation.json +1 -1
- package/dist/{questionEvidenceLinks-DFlyPpAj.d.ts → questionEvidenceLinks-_nPRa-LY.d.ts} +10 -10
- package/dist/questionEvidenceLinks.d.ts +1 -1
- package/dist/questionEvidenceLinks.js +564 -347
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/{resolverTypes-CC8Ea2E2.d.ts → resolverTypes-BOXPxLET.d.ts} +8 -7
- package/dist/resolverTypes.d.ts +4 -2
- package/dist/{resolvers-Br1a6eLV.d.ts → resolvers-B1TIBmRO.d.ts} +3 -1
- package/dist/resolvers.d.ts +5 -3
- package/dist/resolvers.js +121 -77
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.d.ts +10 -7
- package/dist/scopeResolverCompat.js +106 -123
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/{text-matching-DNg4M5Wd.d.ts → text-matching-DzFooju6.d.ts} +7 -7
- package/dist/text-matching.d.ts +1 -1
- package/dist/topicOntologyResolver.d.ts +22 -21
- package/dist/topicOntologyResolver.js +54 -32
- package/dist/topicOntologyResolver.js.map +1 -1
- package/dist/topicProjectOverlay.d.ts +30 -20
- package/dist/topicProjectOverlay.js +120 -76
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/{topicScope-7zhyeGl7.d.ts → topicScope-DJVa0mLa.d.ts} +22 -7
- package/dist/topicScope.d.ts +3 -1
- package/dist/topicScope.js +104 -119
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.d.ts +26 -15
- package/dist/workflowBridge.js +140 -144
- package/dist/workflowBridge.js.map +1 -1
- package/dist/workspaceIsolation.d.ts +14 -12
- package/dist/workspaceIsolation.js +108 -122
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +4 -4
- package/dist/edges/dependsOn.js.map +0 -1
- package/dist/edges/derivedFrom.js.map +0 -1
- package/dist/edges/propagationTypes.js.map +0 -1
- package/dist/evaluators/lintCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/lintCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/sentryCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/testRunnerEvaluator.js.map +0 -1
- package/dist/evaluators/tscCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/tscCheckerEvaluator.js.map +0 -1
- package/dist/{epistemicQuestions-bwHd2FWE.d.ts → epistemicQuestions-Do1fhYm5.d.ts} +4 -4
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import { v } from 'convex/values';
|
|
2
1
|
import { checkProjectAccess } from '@lucern/access-control/access';
|
|
3
2
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
4
|
-
import {
|
|
3
|
+
import { v } from 'convex/values';
|
|
4
|
+
import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
|
|
5
|
+
import { componentsGeneric, mutationGeneric, queryGeneric } from 'convex/server';
|
|
5
6
|
|
|
6
7
|
// src/questionEvidenceLinks.ts
|
|
7
|
-
var
|
|
8
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
9
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
10
|
+
);
|
|
11
|
+
var api = unsafeApi;
|
|
8
12
|
componentsGeneric();
|
|
9
|
-
var internal =
|
|
13
|
+
var internal = unsafeApi;
|
|
10
14
|
var mutation = mutationGeneric;
|
|
11
15
|
var query = queryGeneric;
|
|
12
16
|
|
|
@@ -72,6 +76,10 @@ function readStringArray(value) {
|
|
|
72
76
|
function readMetadata(topic) {
|
|
73
77
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
74
78
|
}
|
|
79
|
+
function omitMetadataKey(metadata, key) {
|
|
80
|
+
const { [key]: _omitted, ...rest } = metadata;
|
|
81
|
+
return rest;
|
|
82
|
+
}
|
|
75
83
|
function readLegacyProjectId(value) {
|
|
76
84
|
if (!value) {
|
|
77
85
|
return;
|
|
@@ -152,9 +160,12 @@ async function resolveTopicDoc(ctx, scopeId) {
|
|
|
152
160
|
);
|
|
153
161
|
}
|
|
154
162
|
try {
|
|
155
|
-
const topic = await ctx.runQuery(
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
const topic = await ctx.runQuery(
|
|
164
|
+
api.topics.getByLegacyScopeId,
|
|
165
|
+
{
|
|
166
|
+
projectId: String(scopeId)
|
|
167
|
+
}
|
|
168
|
+
);
|
|
158
169
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
159
170
|
return topic;
|
|
160
171
|
}
|
|
@@ -174,8 +185,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
|
174
185
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
175
186
|
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
176
187
|
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
177
|
-
|
|
178
|
-
|
|
188
|
+
let createdAt = 0;
|
|
189
|
+
if (typeof topic.createdAt === "number") {
|
|
190
|
+
createdAt = topic.createdAt;
|
|
191
|
+
} else if (typeof topic._creationTime === "number") {
|
|
192
|
+
createdAt = topic._creationTime;
|
|
193
|
+
}
|
|
194
|
+
let updatedAt = createdAt;
|
|
195
|
+
if (typeof topic.updatedAt === "number") {
|
|
196
|
+
updatedAt = topic.updatedAt;
|
|
197
|
+
} else if (typeof metadata.updatedAt === "number") {
|
|
198
|
+
updatedAt = metadata.updatedAt;
|
|
199
|
+
}
|
|
179
200
|
return {
|
|
180
201
|
...metadata,
|
|
181
202
|
_id: outwardId,
|
|
@@ -244,90 +265,113 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
244
265
|
if (!topic) {
|
|
245
266
|
return null;
|
|
246
267
|
}
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
268
|
+
const plan = buildTopicProjectOverlayPatchPlan(topic, value);
|
|
269
|
+
await applyTopicProjectOverlayPatch(ctx, topic, plan);
|
|
270
|
+
return materializeTopicProjectOverlay({
|
|
271
|
+
...topic,
|
|
272
|
+
...plan.patch,
|
|
273
|
+
metadata: plan.nextMetadata
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function buildTopicProjectOverlayPatchPlan(topic, value) {
|
|
277
|
+
const plan = {
|
|
278
|
+
nextMetadata: { ...readMetadata(topic) },
|
|
279
|
+
patch: {},
|
|
280
|
+
topicUpdateArgs: {
|
|
281
|
+
id: String(topic._id)
|
|
282
|
+
}
|
|
251
283
|
};
|
|
252
284
|
for (const [key, rawValue] of Object.entries(value)) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
delete nextMetadata.projectType;
|
|
293
|
-
}
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
296
|
-
case "updatedAt":
|
|
297
|
-
case "createdAt":
|
|
298
|
-
break;
|
|
299
|
-
default:
|
|
300
|
-
if (rawValue === void 0) {
|
|
301
|
-
delete nextMetadata[key];
|
|
302
|
-
} else {
|
|
303
|
-
nextMetadata[key] = rawValue;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
285
|
+
applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
|
|
286
|
+
}
|
|
287
|
+
plan.patch.updatedAt = Date.now();
|
|
288
|
+
plan.patch.metadata = plan.nextMetadata;
|
|
289
|
+
plan.topicUpdateArgs.metadata = plan.nextMetadata;
|
|
290
|
+
return plan;
|
|
291
|
+
}
|
|
292
|
+
function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
|
|
293
|
+
switch (key) {
|
|
294
|
+
case "_id":
|
|
295
|
+
case "projectId":
|
|
296
|
+
case "topicId":
|
|
297
|
+
case "legacyProjectId":
|
|
298
|
+
case "storageProjectId":
|
|
299
|
+
case "updatedAt":
|
|
300
|
+
case "createdAt":
|
|
301
|
+
return;
|
|
302
|
+
case "name":
|
|
303
|
+
case "description":
|
|
304
|
+
plan.patch[key] = rawValue;
|
|
305
|
+
plan.topicUpdateArgs[key] = rawValue;
|
|
306
|
+
return;
|
|
307
|
+
case "tenantId":
|
|
308
|
+
case "workspaceId":
|
|
309
|
+
case "ownerId":
|
|
310
|
+
throw new Error(
|
|
311
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
312
|
+
);
|
|
313
|
+
case "status":
|
|
314
|
+
applyTopicStatusPatch(plan, rawValue);
|
|
315
|
+
return;
|
|
316
|
+
case "visibility":
|
|
317
|
+
applyTopicVisibilityPatch(plan, rawValue);
|
|
318
|
+
return;
|
|
319
|
+
case "type":
|
|
320
|
+
applyTopicProjectTypePatch(plan, rawValue);
|
|
321
|
+
return;
|
|
322
|
+
default:
|
|
323
|
+
applyTopicMetadataPatch(plan, key, rawValue);
|
|
306
324
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
325
|
+
}
|
|
326
|
+
function applyTopicStatusPatch(plan, rawValue) {
|
|
327
|
+
const status = coerceStatus(rawValue);
|
|
328
|
+
if (status) {
|
|
329
|
+
plan.patch.status = status;
|
|
330
|
+
plan.topicUpdateArgs.status = status;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function applyTopicVisibilityPatch(plan, rawValue) {
|
|
334
|
+
const visibility = coerceVisibility(rawValue);
|
|
335
|
+
if (visibility) {
|
|
336
|
+
plan.patch.visibility = visibility;
|
|
337
|
+
plan.topicUpdateArgs.visibility = visibility;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function applyTopicProjectTypePatch(plan, rawValue) {
|
|
341
|
+
const projectType = readNonEmptyString(rawValue);
|
|
342
|
+
if (projectType) {
|
|
343
|
+
plan.nextMetadata.projectType = projectType;
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
|
|
347
|
+
}
|
|
348
|
+
function applyTopicMetadataPatch(plan, key, rawValue) {
|
|
349
|
+
if (rawValue === void 0) {
|
|
350
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
plan.nextMetadata[key] = rawValue;
|
|
354
|
+
}
|
|
355
|
+
async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
|
|
310
356
|
if (typeof ctx.runMutation === "function") {
|
|
311
357
|
try {
|
|
312
|
-
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
358
|
+
await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
|
|
313
359
|
} catch (error) {
|
|
314
|
-
if (!
|
|
360
|
+
if (!canPatchTopicViaLocalDb(ctx, error)) {
|
|
315
361
|
throw error;
|
|
316
362
|
}
|
|
317
|
-
await ctx.db.patch(
|
|
363
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
318
364
|
}
|
|
319
365
|
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
320
|
-
await ctx.db.patch(
|
|
366
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
321
367
|
} else {
|
|
322
368
|
throw new Error(
|
|
323
369
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
324
370
|
);
|
|
325
371
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
metadata: nextMetadata
|
|
330
|
-
});
|
|
372
|
+
}
|
|
373
|
+
function canPatchTopicViaLocalDb(ctx, error) {
|
|
374
|
+
return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
|
|
331
375
|
}
|
|
332
376
|
|
|
333
377
|
// src/resolvers.ts
|
|
@@ -355,7 +399,7 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
355
399
|
try {
|
|
356
400
|
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
357
401
|
} catch (error) {
|
|
358
|
-
if (!isAdvisoryTopicPatch(value)
|
|
402
|
+
if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
|
|
359
403
|
throw error;
|
|
360
404
|
}
|
|
361
405
|
console.warn(
|
|
@@ -422,13 +466,15 @@ function asMappedProjectId(topic) {
|
|
|
422
466
|
if (!topic) {
|
|
423
467
|
return;
|
|
424
468
|
}
|
|
425
|
-
const directLegacyProjectId = normalizeScopeValue(
|
|
469
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
470
|
+
topic[LEGACY_SCOPE_FIELD2]
|
|
471
|
+
);
|
|
426
472
|
if (directLegacyProjectId) {
|
|
427
473
|
return directLegacyProjectId;
|
|
428
474
|
}
|
|
429
475
|
const metadata = topic.metadata || {};
|
|
430
476
|
const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
431
|
-
return candidate ? candidate : void 0;
|
|
477
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
432
478
|
}
|
|
433
479
|
function normalizeScopeValue(value) {
|
|
434
480
|
if (typeof value !== "string") {
|
|
@@ -453,8 +499,9 @@ function pickPrimaryTopic(candidates) {
|
|
|
453
499
|
})[0];
|
|
454
500
|
}
|
|
455
501
|
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
502
|
+
const query2 = ctx.db.query("topics");
|
|
456
503
|
try {
|
|
457
|
-
return await
|
|
504
|
+
return await query2.withIndex(
|
|
458
505
|
"by_graph_scope_project",
|
|
459
506
|
(q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
|
|
460
507
|
).collect();
|
|
@@ -466,7 +513,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
466
513
|
scopeId
|
|
467
514
|
}
|
|
468
515
|
);
|
|
469
|
-
const topics = await
|
|
516
|
+
const topics = await query2.collect();
|
|
470
517
|
return topics.filter((topic) => {
|
|
471
518
|
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
472
519
|
const mappedProjectId = asMappedProjectId(topic);
|
|
@@ -522,137 +569,115 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
|
522
569
|
let current = topic;
|
|
523
570
|
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
524
571
|
current = await ctx.db.get(current.parentTopicId);
|
|
525
|
-
if (!current)
|
|
572
|
+
if (!current) {
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
526
575
|
if (!tenantId) {
|
|
527
576
|
tenantId = normalizeScopeValue(current.tenantId);
|
|
528
577
|
}
|
|
529
578
|
if (!workspaceId) {
|
|
530
579
|
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
531
580
|
}
|
|
532
|
-
if (tenantId && workspaceId)
|
|
581
|
+
if (tenantId && workspaceId) {
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
533
584
|
}
|
|
534
585
|
return { tenantId, workspaceId };
|
|
535
586
|
}
|
|
536
587
|
async function resolveTopicProjectScope(ctx, args) {
|
|
537
588
|
if (args.topicId) {
|
|
538
|
-
|
|
539
|
-
try {
|
|
540
|
-
topic = await ctx.db.get(
|
|
541
|
-
args.topicId
|
|
542
|
-
);
|
|
543
|
-
} catch (error) {
|
|
544
|
-
debugGraphPrimitiveFallback(
|
|
545
|
-
"[topicScope] Failed to load topic by direct id",
|
|
546
|
-
{
|
|
547
|
-
error,
|
|
548
|
-
topicId: args.topicId
|
|
549
|
-
}
|
|
550
|
-
);
|
|
551
|
-
}
|
|
552
|
-
if (!topic) {
|
|
553
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
554
|
-
}
|
|
555
|
-
if (!topic) {
|
|
556
|
-
topic = pickPrimaryTopic(
|
|
557
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
558
|
-
) ?? null;
|
|
559
|
-
}
|
|
560
|
-
if (!topic) {
|
|
561
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
562
|
-
ctx,
|
|
563
|
-
String(args.topicId)
|
|
564
|
-
);
|
|
565
|
-
if (nodeScope) {
|
|
566
|
-
return nodeScope;
|
|
567
|
-
}
|
|
568
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
569
|
-
}
|
|
570
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
571
|
-
const mapped = asMappedProjectId(topic);
|
|
572
|
-
if (mapped) {
|
|
573
|
-
return {
|
|
574
|
-
topicId: topic._id,
|
|
575
|
-
projectId: mapped,
|
|
576
|
-
tenantId: inherited.tenantId,
|
|
577
|
-
workspaceId: inherited.workspaceId,
|
|
578
|
-
source: "topic"
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
return {
|
|
582
|
-
topicId: topic._id,
|
|
583
|
-
tenantId: inherited.tenantId,
|
|
584
|
-
workspaceId: inherited.workspaceId,
|
|
585
|
-
source: "topic"
|
|
586
|
-
};
|
|
589
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
587
590
|
}
|
|
588
591
|
if (args.projectId) {
|
|
589
|
-
|
|
590
|
-
try {
|
|
591
|
-
directTopic = await ctx.db.get(
|
|
592
|
-
args.projectId
|
|
593
|
-
);
|
|
594
|
-
} catch (error) {
|
|
595
|
-
debugGraphPrimitiveFallback(
|
|
596
|
-
"[topicScope] Failed to load direct project topic",
|
|
597
|
-
{
|
|
598
|
-
error,
|
|
599
|
-
projectId: args.projectId
|
|
600
|
-
}
|
|
601
|
-
);
|
|
602
|
-
}
|
|
603
|
-
if (directTopic) {
|
|
604
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
605
|
-
const mapped = asMappedProjectId(directTopic);
|
|
606
|
-
return {
|
|
607
|
-
topicId: directTopic._id,
|
|
608
|
-
projectId: mapped ?? args.projectId,
|
|
609
|
-
tenantId: inherited.tenantId,
|
|
610
|
-
workspaceId: inherited.workspaceId,
|
|
611
|
-
source: "topic_inferred"
|
|
612
|
-
};
|
|
613
|
-
}
|
|
614
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
615
|
-
if (directTopic) {
|
|
616
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
617
|
-
const mapped = asMappedProjectId(directTopic);
|
|
618
|
-
return {
|
|
619
|
-
topicId: directTopic._id,
|
|
620
|
-
projectId: mapped ?? args.projectId,
|
|
621
|
-
tenantId: inherited.tenantId,
|
|
622
|
-
workspaceId: inherited.workspaceId,
|
|
623
|
-
source: "topic_inferred"
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
627
|
-
const primary = pickPrimaryTopic(topics);
|
|
628
|
-
if (primary) {
|
|
629
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
630
|
-
return {
|
|
631
|
-
topicId: primary._id,
|
|
632
|
-
projectId: args.projectId,
|
|
633
|
-
tenantId: inherited.tenantId,
|
|
634
|
-
workspaceId: inherited.workspaceId,
|
|
635
|
-
source: "project_mapped_topic"
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
639
|
-
ctx,
|
|
640
|
-
String(args.projectId)
|
|
641
|
-
);
|
|
642
|
-
if (nodeScope) {
|
|
643
|
-
return {
|
|
644
|
-
...nodeScope,
|
|
645
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
throw new Error(
|
|
649
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
650
|
-
);
|
|
592
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
651
593
|
}
|
|
652
594
|
throw new Error(
|
|
653
595
|
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
654
596
|
);
|
|
655
597
|
}
|
|
598
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
599
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
600
|
+
if (topic) {
|
|
601
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
602
|
+
}
|
|
603
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
604
|
+
if (nodeScope) {
|
|
605
|
+
return nodeScope;
|
|
606
|
+
}
|
|
607
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
608
|
+
}
|
|
609
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
610
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
611
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
612
|
+
idLogKey: "topicId"
|
|
613
|
+
});
|
|
614
|
+
if (direct) {
|
|
615
|
+
return direct;
|
|
616
|
+
}
|
|
617
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
618
|
+
if (hostTopic) {
|
|
619
|
+
return hostTopic;
|
|
620
|
+
}
|
|
621
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
622
|
+
}
|
|
623
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
624
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
625
|
+
ctx,
|
|
626
|
+
legacyProjectId
|
|
627
|
+
);
|
|
628
|
+
if (directTopic) {
|
|
629
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
630
|
+
fallbackProjectId: legacyProjectId
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
const primary = pickPrimaryTopic(
|
|
634
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
635
|
+
);
|
|
636
|
+
if (primary) {
|
|
637
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
638
|
+
fallbackProjectId: legacyProjectId
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
642
|
+
if (nodeScope) {
|
|
643
|
+
return {
|
|
644
|
+
...nodeScope,
|
|
645
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
throw new Error(
|
|
649
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
653
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
654
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
655
|
+
idLogKey: "projectId"
|
|
656
|
+
});
|
|
657
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
658
|
+
}
|
|
659
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
660
|
+
try {
|
|
661
|
+
return await ctx.db.get(id);
|
|
662
|
+
} catch (error) {
|
|
663
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
664
|
+
error,
|
|
665
|
+
[log.idLogKey]: id
|
|
666
|
+
});
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
671
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
672
|
+
const mapped = asMappedProjectId(topic);
|
|
673
|
+
return {
|
|
674
|
+
topicId: topic._id,
|
|
675
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
676
|
+
tenantId: inherited.tenantId,
|
|
677
|
+
workspaceId: inherited.workspaceId,
|
|
678
|
+
source
|
|
679
|
+
};
|
|
680
|
+
}
|
|
656
681
|
var optionalScopeArgs = {
|
|
657
682
|
projectId: v.optional(v.string()),
|
|
658
683
|
topicId: v.optional(v.string())
|
|
@@ -684,6 +709,251 @@ var matcherMetadataValidator = v.object({
|
|
|
684
709
|
signalSnapshot: v.optional(v.any()),
|
|
685
710
|
outcomeMetadata: v.optional(v.any())
|
|
686
711
|
});
|
|
712
|
+
function isRecord(value) {
|
|
713
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
714
|
+
}
|
|
715
|
+
function readConvexId(value) {
|
|
716
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
717
|
+
}
|
|
718
|
+
function readOptionalNumber(value) {
|
|
719
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
720
|
+
}
|
|
721
|
+
function readRequiredNumber(value, fieldName) {
|
|
722
|
+
const numberValue = readOptionalNumber(value);
|
|
723
|
+
if (numberValue === void 0) {
|
|
724
|
+
throw new Error(`questionEvidenceLinks row has invalid ${fieldName}.`);
|
|
725
|
+
}
|
|
726
|
+
return numberValue;
|
|
727
|
+
}
|
|
728
|
+
function readOptionalString(value) {
|
|
729
|
+
return typeof value === "string" ? value : void 0;
|
|
730
|
+
}
|
|
731
|
+
function readStringArray2(value) {
|
|
732
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
733
|
+
}
|
|
734
|
+
function readLinkSuggestionStatus(value) {
|
|
735
|
+
if (value === void 0) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (value === "suggested" || value === "approved" || value === "dismissed") {
|
|
739
|
+
return value;
|
|
740
|
+
}
|
|
741
|
+
throw new Error("questionEvidenceLinks row has invalid status.");
|
|
742
|
+
}
|
|
743
|
+
function readEvidenceImpact(value) {
|
|
744
|
+
if (value === void 0) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
if (value === "supports" || value === "contradicts" || value === "neutral") {
|
|
748
|
+
return value;
|
|
749
|
+
}
|
|
750
|
+
throw new Error("questionEvidenceLinks row has invalid impact.");
|
|
751
|
+
}
|
|
752
|
+
function readMatcherReviewStatus(value) {
|
|
753
|
+
if (value === void 0) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
if (value === "pending" || value === "accepted" || value === "rejected" || value === "auto_accepted" || value === "superseded") {
|
|
757
|
+
return value;
|
|
758
|
+
}
|
|
759
|
+
throw new Error("matcher metadata has invalid reviewStatus.");
|
|
760
|
+
}
|
|
761
|
+
function readMatcherMetadata(value) {
|
|
762
|
+
if (value === void 0) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (!isRecord(value) || typeof value.surface !== "string") {
|
|
766
|
+
throw new Error("matcher metadata must include a string surface.");
|
|
767
|
+
}
|
|
768
|
+
return {
|
|
769
|
+
configSnapshot: value.configSnapshot,
|
|
770
|
+
matcherFamily: readOptionalString(value.matcherFamily),
|
|
771
|
+
matcherKey: readOptionalString(value.matcherKey),
|
|
772
|
+
matcherVersion: readOptionalString(value.matcherVersion),
|
|
773
|
+
outcomeMetadata: value.outcomeMetadata,
|
|
774
|
+
reviewStatus: readMatcherReviewStatus(value.reviewStatus),
|
|
775
|
+
signalSnapshot: value.signalSnapshot,
|
|
776
|
+
surface: value.surface
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function readMetadata2(value) {
|
|
780
|
+
return isRecord(value) ? value : void 0;
|
|
781
|
+
}
|
|
782
|
+
function readEpistemicNode(value) {
|
|
783
|
+
if (!isRecord(value)) {
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
const id = readConvexId(value._id);
|
|
787
|
+
if (!id) {
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
return {
|
|
791
|
+
_creationTime: readOptionalNumber(value._creationTime),
|
|
792
|
+
_id: id,
|
|
793
|
+
canonicalText: readOptionalString(value.canonicalText),
|
|
794
|
+
createdAt: readOptionalNumber(value.createdAt),
|
|
795
|
+
globalId: readOptionalString(value.globalId),
|
|
796
|
+
metadata: readMetadata2(value.metadata),
|
|
797
|
+
nodeType: readOptionalString(value.nodeType),
|
|
798
|
+
projectId: readOptionalString(value.projectId),
|
|
799
|
+
status: readOptionalString(value.status),
|
|
800
|
+
title: readOptionalString(value.title),
|
|
801
|
+
topicId: readOptionalString(value.topicId),
|
|
802
|
+
verificationStatus: readOptionalString(value.verificationStatus)
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function requireEpistemicNode(value, message) {
|
|
806
|
+
const node = readEpistemicNode(value);
|
|
807
|
+
if (!node) {
|
|
808
|
+
throw new Error(message);
|
|
809
|
+
}
|
|
810
|
+
return node;
|
|
811
|
+
}
|
|
812
|
+
function readQuestionEvidenceLink(value, context = "questionEvidenceLinks row") {
|
|
813
|
+
if (!isRecord(value)) {
|
|
814
|
+
throw new Error(`${context} must be an object.`);
|
|
815
|
+
}
|
|
816
|
+
const id = readConvexId(value._id);
|
|
817
|
+
const questionId = readConvexId(value.questionId);
|
|
818
|
+
const insightId = readConvexId(value.insightId);
|
|
819
|
+
const createdBy = readOptionalString(value.createdBy);
|
|
820
|
+
if (!(id && questionId && insightId && createdBy)) {
|
|
821
|
+
throw new Error(`${context} has invalid required identifiers.`);
|
|
822
|
+
}
|
|
823
|
+
if (typeof value.helpsAnswer !== "boolean") {
|
|
824
|
+
throw new Error(`${context} has invalid helpsAnswer.`);
|
|
825
|
+
}
|
|
826
|
+
return {
|
|
827
|
+
_id: id,
|
|
828
|
+
createdAt: readRequiredNumber(value.createdAt, "createdAt"),
|
|
829
|
+
createdBy,
|
|
830
|
+
helpsAnswer: value.helpsAnswer,
|
|
831
|
+
impact: readEvidenceImpact(value.impact),
|
|
832
|
+
impactScore: readOptionalNumber(value.impactScore),
|
|
833
|
+
insightId,
|
|
834
|
+
questionId,
|
|
835
|
+
rationale: readOptionalString(value.rationale),
|
|
836
|
+
relevance: readOptionalNumber(value.relevance),
|
|
837
|
+
status: readLinkSuggestionStatus(value.status)
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
function readQuestionEvidenceLinks(values) {
|
|
841
|
+
return values.map(
|
|
842
|
+
(value, index) => readQuestionEvidenceLink(value, `questionEvidenceLinks[${index}]`)
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
function readEpistemicEdge(value) {
|
|
846
|
+
if (!isRecord(value)) {
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
const id = readConvexId(value._id);
|
|
850
|
+
const createdAt = readOptionalNumber(value.createdAt);
|
|
851
|
+
if (!(id && createdAt !== void 0)) {
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
return {
|
|
855
|
+
_id: id,
|
|
856
|
+
context: readOptionalString(value.context),
|
|
857
|
+
createdAt,
|
|
858
|
+
createdBy: readOptionalString(value.createdBy),
|
|
859
|
+
fromGlobalId: readOptionalString(value.fromGlobalId),
|
|
860
|
+
fromNodeId: readConvexId(value.fromNodeId) ?? void 0,
|
|
861
|
+
globalId: readOptionalString(value.globalId),
|
|
862
|
+
weight: readOptionalNumber(value.weight)
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
async function fetchOperationalQuestionEvidenceLinks(ctx, questionId) {
|
|
866
|
+
const links = readQuestionEvidenceLinks(
|
|
867
|
+
await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", questionId)).collect()
|
|
868
|
+
);
|
|
869
|
+
return await Promise.all(
|
|
870
|
+
links.filter((link) => isOperationalLinkStatus(link.status)).map(async (link) => {
|
|
871
|
+
const insight = readEpistemicNode(await ctx.db.get(link.insightId));
|
|
872
|
+
return {
|
|
873
|
+
...link,
|
|
874
|
+
insight
|
|
875
|
+
};
|
|
876
|
+
})
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
function calculateEdgeImpact(edgeWeight) {
|
|
880
|
+
return edgeWeight == null ? 0 : Math.round(edgeWeight * 10);
|
|
881
|
+
}
|
|
882
|
+
async function buildEvidenceDetailFromEdge(ctx, edge, questionId, seenEvidenceIds) {
|
|
883
|
+
if (!edge.fromNodeId) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
const fromNodeId = String(edge.fromNodeId);
|
|
887
|
+
if (seenEvidenceIds.has(fromNodeId)) {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
seenEvidenceIds.add(fromNodeId);
|
|
891
|
+
const evidenceNode = readEpistemicNode(await ctx.db.get(edge.fromNodeId));
|
|
892
|
+
if (evidenceNode?.nodeType !== "evidence" || evidenceNode.status !== "active") {
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
const meta = evidenceNode.metadata || {};
|
|
896
|
+
const impactScore = calculateEdgeImpact(edge.weight);
|
|
897
|
+
return {
|
|
898
|
+
_id: edge._id,
|
|
899
|
+
_creationTime: edge.createdAt,
|
|
900
|
+
questionId,
|
|
901
|
+
insightId: edge.fromNodeId,
|
|
902
|
+
impact: deriveImpact(impactScore, void 0),
|
|
903
|
+
impactScore,
|
|
904
|
+
rationale: edge.context ?? null,
|
|
905
|
+
userId: edge.createdBy,
|
|
906
|
+
createdAt: edge.createdAt,
|
|
907
|
+
insight: {
|
|
908
|
+
_id: evidenceNode._id,
|
|
909
|
+
_creationTime: evidenceNode.createdAt,
|
|
910
|
+
insight: evidenceNode.canonicalText || evidenceNode.title || readOptionalString(meta.snippet) || null,
|
|
911
|
+
text: evidenceNode.canonicalText || evidenceNode.title || readOptionalString(meta.snippet) || null,
|
|
912
|
+
title: evidenceNode.title ?? null,
|
|
913
|
+
canonicalText: evidenceNode.canonicalText ?? null,
|
|
914
|
+
source: readOptionalString(meta.sourceTitle) ?? null,
|
|
915
|
+
sourceUrl: readOptionalString(meta.sourceUrl) ?? null,
|
|
916
|
+
verificationStatus: evidenceNode.verificationStatus ?? "unverified"
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
async function fetchDerivedFromEdgesWithDetails(ctx, questionId, seenEvidenceIds) {
|
|
921
|
+
const edges = (await ctx.db.query("epistemicEdges").withIndex(
|
|
922
|
+
"by_to_type",
|
|
923
|
+
(q) => q.eq("toNodeId", questionId).eq("edgeType", "derived_from")
|
|
924
|
+
).collect()).flatMap((edge) => {
|
|
925
|
+
const edgeRow = readEpistemicEdge(edge);
|
|
926
|
+
return edgeRow ? [edgeRow] : [];
|
|
927
|
+
});
|
|
928
|
+
const rows = [];
|
|
929
|
+
for (const edge of edges) {
|
|
930
|
+
const edgeRow = await buildEvidenceDetailFromEdge(
|
|
931
|
+
ctx,
|
|
932
|
+
edge,
|
|
933
|
+
questionId,
|
|
934
|
+
seenEvidenceIds
|
|
935
|
+
);
|
|
936
|
+
if (edgeRow) {
|
|
937
|
+
rows.push(edgeRow);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return rows;
|
|
941
|
+
}
|
|
942
|
+
function deriveImpact(impactScore, fallback) {
|
|
943
|
+
if (impactScore === void 0) {
|
|
944
|
+
return fallback;
|
|
945
|
+
}
|
|
946
|
+
if (impactScore > 0) {
|
|
947
|
+
return "supports";
|
|
948
|
+
}
|
|
949
|
+
if (impactScore < 0) {
|
|
950
|
+
return "contradicts";
|
|
951
|
+
}
|
|
952
|
+
return "neutral";
|
|
953
|
+
}
|
|
954
|
+
function readEpistemicMetadata(node) {
|
|
955
|
+
return node?.metadata ?? {};
|
|
956
|
+
}
|
|
687
957
|
async function markProjectGraphDirty(ctx, projectId) {
|
|
688
958
|
if (!projectId) {
|
|
689
959
|
return;
|
|
@@ -724,8 +994,8 @@ async function recordMatcherDecision(ctx, args) {
|
|
|
724
994
|
});
|
|
725
995
|
}
|
|
726
996
|
async function applyOperationalLinkEffects(ctx, args) {
|
|
727
|
-
const meta = args.questionNode
|
|
728
|
-
const currentRelated = meta.relatedInsightIds
|
|
997
|
+
const meta = readEpistemicMetadata(args.questionNode);
|
|
998
|
+
const currentRelated = readStringArray2(meta.relatedInsightIds);
|
|
729
999
|
if (!currentRelated.includes(args.insightId)) {
|
|
730
1000
|
await ctx.db.patch(args.questionId, {
|
|
731
1001
|
updatedAt: Date.now(),
|
|
@@ -736,8 +1006,8 @@ async function applyOperationalLinkEffects(ctx, args) {
|
|
|
736
1006
|
});
|
|
737
1007
|
}
|
|
738
1008
|
try {
|
|
739
|
-
const questionNode = await ctx.db.get(args.questionId);
|
|
740
|
-
const evidenceNode = await ctx.db.get(args.insightId);
|
|
1009
|
+
const questionNode = readEpistemicNode(await ctx.db.get(args.questionId));
|
|
1010
|
+
const evidenceNode = readEpistemicNode(await ctx.db.get(args.insightId));
|
|
741
1011
|
if (questionNode && questionNode.nodeType === "question" && evidenceNode && evidenceNode.nodeType === "evidence" && questionNode.globalId && evidenceNode.globalId) {
|
|
742
1012
|
const existingEdge = await ctx.db.query("epistemicEdges").withIndex(
|
|
743
1013
|
"by_from_to",
|
|
@@ -751,7 +1021,7 @@ async function applyOperationalLinkEffects(ctx, args) {
|
|
|
751
1021
|
toGlobalId: questionNode.globalId,
|
|
752
1022
|
edgeType: "derived_from",
|
|
753
1023
|
weight: args.relevance ?? 0.5,
|
|
754
|
-
context: args.rationale
|
|
1024
|
+
context: args.rationale ?? "Linked from question evidence links",
|
|
755
1025
|
projectId: args.questionNode.projectId ? String(args.questionNode.projectId) : void 0,
|
|
756
1026
|
createdBy: args.createdBy,
|
|
757
1027
|
fromNodeType: "evidence",
|
|
@@ -765,47 +1035,42 @@ async function applyOperationalLinkEffects(ctx, args) {
|
|
|
765
1035
|
console.error("[EpistemicSpine] Failed to create derived_from edge:", e);
|
|
766
1036
|
}
|
|
767
1037
|
if (args.questionNode.projectId) {
|
|
768
|
-
await ctx.scheduler.runAfter(
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
projectId: args.questionNode.projectId,
|
|
776
|
-
userId: args.createdBy
|
|
777
|
-
}
|
|
778
|
-
);
|
|
1038
|
+
await ctx.scheduler.runAfter(0, "verificationActions:deepVerifyEvidence", {
|
|
1039
|
+
insightId: args.insightId,
|
|
1040
|
+
targetType: "question",
|
|
1041
|
+
targetId: args.questionId,
|
|
1042
|
+
projectId: args.questionNode.projectId,
|
|
1043
|
+
userId: args.createdBy
|
|
1044
|
+
});
|
|
779
1045
|
}
|
|
780
1046
|
}
|
|
781
1047
|
async function removeOperationalLinkEffects(ctx, args) {
|
|
782
|
-
const question = await ctx.db.get(args.questionId);
|
|
1048
|
+
const question = readEpistemicNode(await ctx.db.get(args.questionId));
|
|
783
1049
|
if (question) {
|
|
784
|
-
const meta = question
|
|
785
|
-
const currentRelated = meta.relatedInsightIds
|
|
1050
|
+
const meta = readEpistemicMetadata(question);
|
|
1051
|
+
const currentRelated = readStringArray2(meta.relatedInsightIds);
|
|
786
1052
|
await ctx.db.patch(args.questionId, {
|
|
787
1053
|
updatedAt: Date.now(),
|
|
788
1054
|
metadata: {
|
|
789
1055
|
...meta,
|
|
790
|
-
relatedInsightIds: currentRelated.filter(
|
|
791
|
-
(id) => id !== args.insightId
|
|
792
|
-
)
|
|
1056
|
+
relatedInsightIds: currentRelated.filter((id) => id !== args.insightId)
|
|
793
1057
|
}
|
|
794
1058
|
});
|
|
795
1059
|
}
|
|
796
1060
|
try {
|
|
797
|
-
const questionNode = await ctx.db.get(args.questionId);
|
|
798
|
-
const evidenceNode = await ctx.db.get(args.insightId);
|
|
1061
|
+
const questionNode = readEpistemicNode(await ctx.db.get(args.questionId));
|
|
1062
|
+
const evidenceNode = readEpistemicNode(await ctx.db.get(args.insightId));
|
|
799
1063
|
if (questionNode && questionNode.nodeType === "question" && evidenceNode && evidenceNode.nodeType === "evidence") {
|
|
800
1064
|
const edge = await ctx.db.query("epistemicEdges").withIndex(
|
|
801
1065
|
"by_from_to",
|
|
802
1066
|
(q) => q.eq("fromNodeId", evidenceNode._id).eq("toNodeId", questionNode._id)
|
|
803
1067
|
).first();
|
|
804
|
-
|
|
1068
|
+
const edgeRow = readEpistemicEdge(edge);
|
|
1069
|
+
if (edgeRow?.globalId) {
|
|
805
1070
|
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.deleteEdge, {
|
|
806
|
-
globalId:
|
|
1071
|
+
globalId: edgeRow.globalId
|
|
807
1072
|
});
|
|
808
|
-
await ctx.db.delete(
|
|
1073
|
+
await ctx.db.delete(edgeRow._id);
|
|
809
1074
|
}
|
|
810
1075
|
}
|
|
811
1076
|
} catch (e) {
|
|
@@ -833,23 +1098,29 @@ var create = mutation({
|
|
|
833
1098
|
},
|
|
834
1099
|
returns: permissiveReturn,
|
|
835
1100
|
handler: async (ctx, args) => {
|
|
836
|
-
const
|
|
837
|
-
|
|
1101
|
+
const matcherMetadata = readMatcherMetadata(args.matcherMetadata);
|
|
1102
|
+
const question = requireEpistemicNode(
|
|
1103
|
+
await ctx.db.get(args.questionId),
|
|
1104
|
+
"Question not found"
|
|
1105
|
+
);
|
|
1106
|
+
if (question.nodeType !== "question") {
|
|
838
1107
|
throw new Error("Question not found");
|
|
839
1108
|
}
|
|
840
|
-
const insight =
|
|
841
|
-
|
|
1109
|
+
const insight = requireEpistemicNode(
|
|
1110
|
+
await ctx.db.get(args.insightId),
|
|
1111
|
+
"Insight not found"
|
|
1112
|
+
);
|
|
1113
|
+
if (insight.nodeType !== "evidence") {
|
|
842
1114
|
throw new Error("Insight not found");
|
|
843
1115
|
}
|
|
844
|
-
const existingLinks =
|
|
845
|
-
"by_questionId",
|
|
846
|
-
|
|
847
|
-
).collect();
|
|
1116
|
+
const existingLinks = readQuestionEvidenceLinks(
|
|
1117
|
+
await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", args.questionId)).collect()
|
|
1118
|
+
);
|
|
848
1119
|
const duplicate = existingLinks.find(
|
|
849
1120
|
(link) => link.insightId === args.insightId
|
|
850
1121
|
);
|
|
851
1122
|
const impactScore = args.impactScore;
|
|
852
|
-
const derivedImpact = impactScore
|
|
1123
|
+
const derivedImpact = deriveImpact(impactScore, args.impact);
|
|
853
1124
|
if (duplicate) {
|
|
854
1125
|
const nextStatus = mergeLinkSuggestionStatus(
|
|
855
1126
|
duplicate.status,
|
|
@@ -863,9 +1134,7 @@ var create = mutation({
|
|
|
863
1134
|
impact: derivedImpact,
|
|
864
1135
|
status: nextStatus
|
|
865
1136
|
});
|
|
866
|
-
if (isOperationalLinkStatus(nextStatus) && !isOperationalLinkStatus(
|
|
867
|
-
duplicate.status
|
|
868
|
-
)) {
|
|
1137
|
+
if (isOperationalLinkStatus(nextStatus) && !isOperationalLinkStatus(duplicate.status)) {
|
|
869
1138
|
await applyOperationalLinkEffects(ctx, {
|
|
870
1139
|
questionNode: question,
|
|
871
1140
|
questionId: args.questionId,
|
|
@@ -881,7 +1150,7 @@ var create = mutation({
|
|
|
881
1150
|
insightId: args.insightId,
|
|
882
1151
|
linkId: duplicate._id,
|
|
883
1152
|
linkStatus: nextStatus,
|
|
884
|
-
matcherMetadata
|
|
1153
|
+
matcherMetadata
|
|
885
1154
|
});
|
|
886
1155
|
await markProjectGraphDirty(ctx, question.projectId);
|
|
887
1156
|
return duplicate._id;
|
|
@@ -904,7 +1173,7 @@ var create = mutation({
|
|
|
904
1173
|
insightId: args.insightId,
|
|
905
1174
|
linkId,
|
|
906
1175
|
linkStatus: args.status,
|
|
907
|
-
matcherMetadata
|
|
1176
|
+
matcherMetadata
|
|
908
1177
|
});
|
|
909
1178
|
if (isOperationalLinkStatus(args.status)) {
|
|
910
1179
|
await applyOperationalLinkEffects(ctx, {
|
|
@@ -935,12 +1204,13 @@ var updateImpact = mutation({
|
|
|
935
1204
|
},
|
|
936
1205
|
returns: permissiveReturn,
|
|
937
1206
|
handler: async (ctx, args) => {
|
|
938
|
-
const
|
|
939
|
-
if (!
|
|
1207
|
+
const rawLink = await ctx.db.get(args.linkId);
|
|
1208
|
+
if (!rawLink) {
|
|
940
1209
|
throw new Error("Link not found");
|
|
941
1210
|
}
|
|
1211
|
+
const link = readQuestionEvidenceLink(rawLink, "updateImpact.link");
|
|
942
1212
|
const newImpactScore = args.impactScore;
|
|
943
|
-
const newImpact = newImpactScore
|
|
1213
|
+
const newImpact = deriveImpact(newImpactScore, args.impact);
|
|
944
1214
|
const updateData = {};
|
|
945
1215
|
if (newImpact) {
|
|
946
1216
|
updateData.impact = newImpact;
|
|
@@ -949,7 +1219,7 @@ var updateImpact = mutation({
|
|
|
949
1219
|
updateData.impactScore = newImpactScore;
|
|
950
1220
|
}
|
|
951
1221
|
await ctx.db.patch(args.linkId, updateData);
|
|
952
|
-
const question = await ctx.db.get(link.questionId);
|
|
1222
|
+
const question = readEpistemicNode(await ctx.db.get(link.questionId));
|
|
953
1223
|
await markProjectGraphDirty(ctx, question?.projectId);
|
|
954
1224
|
return { success: true, newImpact, newImpactScore };
|
|
955
1225
|
}
|
|
@@ -963,11 +1233,12 @@ var reviewSuggestion = mutation({
|
|
|
963
1233
|
},
|
|
964
1234
|
returns: permissiveReturn,
|
|
965
1235
|
handler: async (ctx, args) => {
|
|
966
|
-
const
|
|
967
|
-
if (!
|
|
1236
|
+
const rawLink = await ctx.db.get(args.linkId);
|
|
1237
|
+
if (!rawLink) {
|
|
968
1238
|
throw new Error("Link not found");
|
|
969
1239
|
}
|
|
970
|
-
const
|
|
1240
|
+
const link = readQuestionEvidenceLink(rawLink, "reviewSuggestion.link");
|
|
1241
|
+
const question = readEpistemicNode(await ctx.db.get(link.questionId));
|
|
971
1242
|
if (!question) {
|
|
972
1243
|
throw new Error("Question not found");
|
|
973
1244
|
}
|
|
@@ -1024,14 +1295,15 @@ var remove = mutation({
|
|
|
1024
1295
|
},
|
|
1025
1296
|
returns: permissiveReturn,
|
|
1026
1297
|
handler: async (ctx, args) => {
|
|
1027
|
-
const
|
|
1028
|
-
if (!
|
|
1298
|
+
const rawLink = await ctx.db.get(args.linkId);
|
|
1299
|
+
if (!rawLink) {
|
|
1029
1300
|
throw new Error("Link not found");
|
|
1030
1301
|
}
|
|
1302
|
+
const link = readQuestionEvidenceLink(rawLink, "remove.link");
|
|
1031
1303
|
if (link.createdBy !== args.userId) {
|
|
1032
1304
|
throw new Error("Only the creator can remove this link");
|
|
1033
1305
|
}
|
|
1034
|
-
const question = await ctx.db.get(link.questionId);
|
|
1306
|
+
const question = readEpistemicNode(await ctx.db.get(link.questionId));
|
|
1035
1307
|
if (isOperationalLinkStatus(link.status)) {
|
|
1036
1308
|
await removeOperationalLinkEffects(ctx, {
|
|
1037
1309
|
questionId: link.questionId,
|
|
@@ -1048,13 +1320,10 @@ var getByQuestion = query({
|
|
|
1048
1320
|
},
|
|
1049
1321
|
returns: permissiveReturn,
|
|
1050
1322
|
handler: async (ctx, args) => {
|
|
1051
|
-
const links =
|
|
1052
|
-
"by_questionId",
|
|
1053
|
-
(q) => q.eq("questionId", args.questionId)
|
|
1054
|
-
).collect();
|
|
1055
|
-
return links.filter(
|
|
1056
|
-
(link) => isOperationalLinkStatus(link.status)
|
|
1323
|
+
const links = readQuestionEvidenceLinks(
|
|
1324
|
+
await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", args.questionId)).collect()
|
|
1057
1325
|
);
|
|
1326
|
+
return links.filter((link) => isOperationalLinkStatus(link.status));
|
|
1058
1327
|
}
|
|
1059
1328
|
});
|
|
1060
1329
|
var getPendingSuggestions = query({
|
|
@@ -1063,10 +1332,9 @@ var getPendingSuggestions = query({
|
|
|
1063
1332
|
},
|
|
1064
1333
|
returns: permissiveReturn,
|
|
1065
1334
|
handler: async (ctx, args) => {
|
|
1066
|
-
const links =
|
|
1067
|
-
"by_questionId",
|
|
1068
|
-
|
|
1069
|
-
).collect();
|
|
1335
|
+
const links = readQuestionEvidenceLinks(
|
|
1336
|
+
await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", args.questionId)).collect()
|
|
1337
|
+
);
|
|
1070
1338
|
return links.filter((link) => link.status === "suggested");
|
|
1071
1339
|
}
|
|
1072
1340
|
});
|
|
@@ -1076,13 +1344,10 @@ var getByInsight = query({
|
|
|
1076
1344
|
},
|
|
1077
1345
|
returns: permissiveReturn,
|
|
1078
1346
|
handler: async (ctx, args) => {
|
|
1079
|
-
const links =
|
|
1080
|
-
"by_insightId",
|
|
1081
|
-
(q) => q.eq("insightId", args.insightId)
|
|
1082
|
-
).collect();
|
|
1083
|
-
return links.filter(
|
|
1084
|
-
(link) => isOperationalLinkStatus(link.status)
|
|
1347
|
+
const links = readQuestionEvidenceLinks(
|
|
1348
|
+
await ctx.db.query("questionEvidenceLinks").withIndex("by_insightId", (q) => q.eq("insightId", args.insightId)).collect()
|
|
1085
1349
|
);
|
|
1350
|
+
return links.filter((link) => isOperationalLinkStatus(link.status));
|
|
1086
1351
|
}
|
|
1087
1352
|
});
|
|
1088
1353
|
var getEvidenceWithDetails = query({
|
|
@@ -1091,68 +1356,27 @@ var getEvidenceWithDetails = query({
|
|
|
1091
1356
|
},
|
|
1092
1357
|
returns: permissiveReturn,
|
|
1093
1358
|
handler: async (ctx, args) => {
|
|
1094
|
-
const questionNode = await ctx.db.get(args.questionId);
|
|
1095
|
-
if (
|
|
1359
|
+
const questionNode = readEpistemicNode(await ctx.db.get(args.questionId));
|
|
1360
|
+
if (questionNode?.nodeType !== "question") {
|
|
1096
1361
|
return [];
|
|
1097
1362
|
}
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
).collect();
|
|
1102
|
-
const linksWithDetails = await Promise.all(
|
|
1103
|
-
links.filter(
|
|
1104
|
-
(link) => isOperationalLinkStatus(
|
|
1105
|
-
link.status
|
|
1106
|
-
)
|
|
1107
|
-
).map(async (link) => {
|
|
1108
|
-
const insight = await ctx.db.get(link.insightId);
|
|
1109
|
-
return {
|
|
1110
|
-
...link,
|
|
1111
|
-
insight
|
|
1112
|
-
};
|
|
1113
|
-
})
|
|
1363
|
+
const linksWithInsight = await fetchOperationalQuestionEvidenceLinks(
|
|
1364
|
+
ctx,
|
|
1365
|
+
args.questionId
|
|
1114
1366
|
);
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
if (!evidenceNode || evidenceNode.nodeType !== "evidence" || evidenceNode.status !== "active") {
|
|
1129
|
-
continue;
|
|
1130
|
-
}
|
|
1131
|
-
const meta = evidenceNode.metadata || {};
|
|
1132
|
-
epistemicEdgeResults.push({
|
|
1133
|
-
_id: edge._id,
|
|
1134
|
-
_creationTime: edge.createdAt,
|
|
1135
|
-
questionId: args.questionId,
|
|
1136
|
-
insightId: edge.fromNodeId,
|
|
1137
|
-
impact: (edge.weight ?? 0) > 0 ? "supports" : (edge.weight ?? 0) < 0 ? "contradicts" : "neutral",
|
|
1138
|
-
impactScore: edge.weight != null ? Math.round(edge.weight * 10) : 0,
|
|
1139
|
-
rationale: edge.context || null,
|
|
1140
|
-
userId: edge.createdBy,
|
|
1141
|
-
createdAt: edge.createdAt,
|
|
1142
|
-
insight: {
|
|
1143
|
-
_id: evidenceNode._id,
|
|
1144
|
-
_creationTime: evidenceNode.createdAt,
|
|
1145
|
-
insight: evidenceNode.canonicalText || evidenceNode.title || meta.snippet || null,
|
|
1146
|
-
text: evidenceNode.canonicalText || evidenceNode.title || meta.snippet || null,
|
|
1147
|
-
title: evidenceNode.title || null,
|
|
1148
|
-
canonicalText: evidenceNode.canonicalText || null,
|
|
1149
|
-
source: meta.sourceTitle || null,
|
|
1150
|
-
sourceUrl: meta.sourceUrl || null,
|
|
1151
|
-
verificationStatus: evidenceNode.verificationStatus || "unverified"
|
|
1152
|
-
}
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
return [...filtered, ...epistemicEdgeResults];
|
|
1367
|
+
const filteredLinks = linksWithInsight.filter(
|
|
1368
|
+
(link) => link.insight !== null
|
|
1369
|
+
);
|
|
1370
|
+
const seenEvidenceIds = new Set(
|
|
1371
|
+
filteredLinks.map((link) => String(link.insightId))
|
|
1372
|
+
);
|
|
1373
|
+
const edgeRows = await fetchDerivedFromEdgesWithDetails(
|
|
1374
|
+
ctx,
|
|
1375
|
+
args.questionId,
|
|
1376
|
+
seenEvidenceIds
|
|
1377
|
+
);
|
|
1378
|
+
const evidenceRows = [...filteredLinks, ...edgeRows];
|
|
1379
|
+
return evidenceRows;
|
|
1156
1380
|
}
|
|
1157
1381
|
});
|
|
1158
1382
|
var getByProject = query({
|
|
@@ -1193,32 +1417,30 @@ var getByProject = query({
|
|
|
1193
1417
|
}
|
|
1194
1418
|
const pageSize = Math.max(1, Math.min(Math.floor(args.limit ?? 300), 1e3));
|
|
1195
1419
|
const questionScanLimit = Math.min(pageSize * 2, 1e3);
|
|
1196
|
-
const questions = await ctx.db.query("epistemicNodes").withIndex(
|
|
1420
|
+
const questions = (await ctx.db.query("epistemicNodes").withIndex(
|
|
1197
1421
|
"by_topic_type",
|
|
1198
1422
|
(q) => q.eq("topicId", scope.topicId).eq("nodeType", "question")
|
|
1199
|
-
).order("desc").take(questionScanLimit)
|
|
1423
|
+
).order("desc").take(questionScanLimit)).flatMap((question) => {
|
|
1424
|
+
const row = readEpistemicNode(question);
|
|
1425
|
+
return row?.nodeType === "question" ? [row] : [];
|
|
1426
|
+
});
|
|
1200
1427
|
const questionIds = questions.slice(0, pageSize).map((q) => q._id);
|
|
1201
1428
|
const allLinks = await Promise.all(
|
|
1202
1429
|
questionIds.map(async (questionId) => {
|
|
1203
|
-
const links =
|
|
1204
|
-
"by_questionId",
|
|
1205
|
-
(q) => q.eq("questionId", questionId)
|
|
1206
|
-
).collect();
|
|
1207
|
-
return links.filter(
|
|
1208
|
-
(link) => isOperationalLinkStatus(
|
|
1209
|
-
link.status
|
|
1210
|
-
)
|
|
1430
|
+
const links = readQuestionEvidenceLinks(
|
|
1431
|
+
await ctx.db.query("questionEvidenceLinks").withIndex("by_questionId", (q) => q.eq("questionId", questionId)).collect()
|
|
1211
1432
|
);
|
|
1433
|
+
return links.filter((link) => isOperationalLinkStatus(link.status));
|
|
1212
1434
|
})
|
|
1213
1435
|
);
|
|
1214
1436
|
const flattenedLinks = allLinks.flat().slice(0, pageSize * 5);
|
|
1215
1437
|
const questionDetails = {};
|
|
1216
1438
|
for (const question of questions) {
|
|
1217
1439
|
const meta = question.metadata || {};
|
|
1218
|
-
questionDetails[question._id] = {
|
|
1219
|
-
_id: question._id,
|
|
1220
|
-
question: question.canonicalText
|
|
1221
|
-
status: meta.status
|
|
1440
|
+
questionDetails[String(question._id)] = {
|
|
1441
|
+
_id: String(question._id),
|
|
1442
|
+
question: question.canonicalText ?? question.title ?? "",
|
|
1443
|
+
status: readOptionalString(meta.status) ?? question.status ?? "active"
|
|
1222
1444
|
};
|
|
1223
1445
|
}
|
|
1224
1446
|
return {
|
|
@@ -1233,17 +1455,12 @@ var getLinkedQuestionsForInsight = query({
|
|
|
1233
1455
|
},
|
|
1234
1456
|
returns: permissiveReturn,
|
|
1235
1457
|
handler: async (ctx, args) => {
|
|
1236
|
-
const links =
|
|
1237
|
-
"by_insightId",
|
|
1238
|
-
|
|
1239
|
-
).collect();
|
|
1458
|
+
const links = readQuestionEvidenceLinks(
|
|
1459
|
+
await ctx.db.query("questionEvidenceLinks").withIndex("by_insightId", (q) => q.eq("insightId", args.insightId)).collect()
|
|
1460
|
+
);
|
|
1240
1461
|
const linkedQuestions = await Promise.all(
|
|
1241
|
-
links.filter(
|
|
1242
|
-
(link)
|
|
1243
|
-
link.status
|
|
1244
|
-
)
|
|
1245
|
-
).map(async (link) => {
|
|
1246
|
-
const question = await ctx.db.get(link.questionId);
|
|
1462
|
+
links.filter((link) => isOperationalLinkStatus(link.status)).map(async (link) => {
|
|
1463
|
+
const question = readEpistemicNode(await ctx.db.get(link.questionId));
|
|
1247
1464
|
return {
|
|
1248
1465
|
linkId: link._id,
|
|
1249
1466
|
questionId: link.questionId,
|
|
@@ -1254,7 +1471,7 @@ var getLinkedQuestionsForInsight = query({
|
|
|
1254
1471
|
createdAt: link.createdAt,
|
|
1255
1472
|
question: question ? {
|
|
1256
1473
|
_id: question._id,
|
|
1257
|
-
question: question.
|
|
1474
|
+
question: question.canonicalText || question.title || "",
|
|
1258
1475
|
status: question.status
|
|
1259
1476
|
} : null
|
|
1260
1477
|
};
|