@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,13 +1,17 @@
|
|
|
1
|
-
import { v } from 'convex/values';
|
|
2
1
|
import { getCurrentUserId } from '@lucern/access-control/auth';
|
|
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 } from 'convex/server';
|
|
5
6
|
import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
6
7
|
|
|
7
8
|
// src/epistemicQuestions.lifecycle.ts
|
|
8
|
-
var
|
|
9
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
10
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
11
|
+
);
|
|
12
|
+
var api = unsafeApi;
|
|
9
13
|
componentsGeneric();
|
|
10
|
-
var internal =
|
|
14
|
+
var internal = unsafeApi;
|
|
11
15
|
var mutation = mutationGeneric;
|
|
12
16
|
|
|
13
17
|
// src/debug.ts
|
|
@@ -22,369 +26,8 @@ function debugGraphPrimitiveFallback(message, context) {
|
|
|
22
26
|
console.debug(message, context ?? {});
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
// src/topicScope.ts
|
|
26
|
-
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
27
|
-
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
28
|
-
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
let node = null;
|
|
32
|
-
try {
|
|
33
|
-
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
34
|
-
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
35
|
-
node = byGlobalId;
|
|
36
|
-
}
|
|
37
|
-
} catch (error) {
|
|
38
|
-
debugGraphPrimitiveFallback(
|
|
39
|
-
"[topicScope] topic-node scope lookup by globalId failed",
|
|
40
|
-
{ error, ref }
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
if (!node) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
|
|
47
|
-
if (!scopeKey) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
topicId: scopeKey,
|
|
52
|
-
projectId: asMappedProjectId(node),
|
|
53
|
-
source: "topic_node"
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function asMappedProjectId(topic) {
|
|
57
|
-
if (!topic) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
61
|
-
if (directLegacyProjectId) {
|
|
62
|
-
return directLegacyProjectId;
|
|
63
|
-
}
|
|
64
|
-
const metadata = topic.metadata || {};
|
|
65
|
-
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
66
|
-
return candidate ? candidate : void 0;
|
|
67
|
-
}
|
|
68
|
-
function normalizeScopeValue(value) {
|
|
69
|
-
if (typeof value !== "string") {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const normalized = value.trim();
|
|
73
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
74
|
-
}
|
|
75
|
-
function pickPrimaryTopic(candidates) {
|
|
76
|
-
return [...candidates].sort((a, b) => {
|
|
77
|
-
const depthA = a.depth ?? 9999;
|
|
78
|
-
const depthB = b.depth ?? 9999;
|
|
79
|
-
if (depthA !== depthB) {
|
|
80
|
-
return depthA - depthB;
|
|
81
|
-
}
|
|
82
|
-
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
83
|
-
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
84
|
-
if (createdA !== createdB) {
|
|
85
|
-
return createdA - createdB;
|
|
86
|
-
}
|
|
87
|
-
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
88
|
-
})[0];
|
|
89
|
-
}
|
|
90
|
-
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
91
|
-
try {
|
|
92
|
-
return await ctx.db.query("topics").withIndex(
|
|
93
|
-
"by_graph_scope_project",
|
|
94
|
-
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
95
|
-
).collect();
|
|
96
|
-
} catch (error) {
|
|
97
|
-
debugGraphPrimitiveFallback(
|
|
98
|
-
"[topicScope] Failed to resolve scope alias via index",
|
|
99
|
-
{
|
|
100
|
-
error,
|
|
101
|
-
scopeId
|
|
102
|
-
}
|
|
103
|
-
);
|
|
104
|
-
const topics = await ctx.db.query("topics").collect();
|
|
105
|
-
return topics.filter((topic) => {
|
|
106
|
-
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
107
|
-
const mappedProjectId = asMappedProjectId(topic);
|
|
108
|
-
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
async function tryResolveHostTopicById(ctx, topicId) {
|
|
113
|
-
if (typeof ctx.runQuery !== "function") {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
return await ctx.runQuery(api.topics.get, {
|
|
118
|
-
id: topicId
|
|
119
|
-
}) ?? null;
|
|
120
|
-
} catch (error) {
|
|
121
|
-
debugGraphPrimitiveFallback(
|
|
122
|
-
"[topicScope] Failed to resolve topic by host query",
|
|
123
|
-
{
|
|
124
|
-
error,
|
|
125
|
-
topicId
|
|
126
|
-
}
|
|
127
|
-
);
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
132
|
-
if (typeof ctx.runQuery !== "function") {
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
137
|
-
projectId: legacyScopeId
|
|
138
|
-
}) ?? null;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
debugGraphPrimitiveFallback(
|
|
141
|
-
"[topicScope] Failed to resolve topic by legacy scope",
|
|
142
|
-
{
|
|
143
|
-
error,
|
|
144
|
-
legacyScopeId
|
|
145
|
-
}
|
|
146
|
-
);
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
151
|
-
const MAX_DEPTH = 10;
|
|
152
|
-
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
153
|
-
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
154
|
-
if (tenantId && workspaceId) {
|
|
155
|
-
return { tenantId, workspaceId };
|
|
156
|
-
}
|
|
157
|
-
let current = topic;
|
|
158
|
-
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
159
|
-
current = await ctx.db.get(current.parentTopicId);
|
|
160
|
-
if (!current) break;
|
|
161
|
-
if (!tenantId) {
|
|
162
|
-
tenantId = normalizeScopeValue(current.tenantId);
|
|
163
|
-
}
|
|
164
|
-
if (!workspaceId) {
|
|
165
|
-
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
166
|
-
}
|
|
167
|
-
if (tenantId && workspaceId) break;
|
|
168
|
-
}
|
|
169
|
-
return { tenantId, workspaceId };
|
|
170
|
-
}
|
|
171
|
-
async function resolveTopicProjectScope(ctx, args) {
|
|
172
|
-
if (args.topicId) {
|
|
173
|
-
let topic = null;
|
|
174
|
-
try {
|
|
175
|
-
topic = await ctx.db.get(
|
|
176
|
-
args.topicId
|
|
177
|
-
);
|
|
178
|
-
} catch (error) {
|
|
179
|
-
debugGraphPrimitiveFallback(
|
|
180
|
-
"[topicScope] Failed to load topic by direct id",
|
|
181
|
-
{
|
|
182
|
-
error,
|
|
183
|
-
topicId: args.topicId
|
|
184
|
-
}
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
if (!topic) {
|
|
188
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
189
|
-
}
|
|
190
|
-
if (!topic) {
|
|
191
|
-
topic = pickPrimaryTopic(
|
|
192
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
193
|
-
) ?? null;
|
|
194
|
-
}
|
|
195
|
-
if (!topic) {
|
|
196
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
197
|
-
ctx,
|
|
198
|
-
String(args.topicId)
|
|
199
|
-
);
|
|
200
|
-
if (nodeScope) {
|
|
201
|
-
return nodeScope;
|
|
202
|
-
}
|
|
203
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
204
|
-
}
|
|
205
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
206
|
-
const mapped = asMappedProjectId(topic);
|
|
207
|
-
if (mapped) {
|
|
208
|
-
return {
|
|
209
|
-
topicId: topic._id,
|
|
210
|
-
projectId: mapped,
|
|
211
|
-
tenantId: inherited.tenantId,
|
|
212
|
-
workspaceId: inherited.workspaceId,
|
|
213
|
-
source: "topic"
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
return {
|
|
217
|
-
topicId: topic._id,
|
|
218
|
-
tenantId: inherited.tenantId,
|
|
219
|
-
workspaceId: inherited.workspaceId,
|
|
220
|
-
source: "topic"
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
if (args.projectId) {
|
|
224
|
-
let directTopic = null;
|
|
225
|
-
try {
|
|
226
|
-
directTopic = await ctx.db.get(
|
|
227
|
-
args.projectId
|
|
228
|
-
);
|
|
229
|
-
} catch (error) {
|
|
230
|
-
debugGraphPrimitiveFallback(
|
|
231
|
-
"[topicScope] Failed to load direct project topic",
|
|
232
|
-
{
|
|
233
|
-
error,
|
|
234
|
-
projectId: args.projectId
|
|
235
|
-
}
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
if (directTopic) {
|
|
239
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
240
|
-
const mapped = asMappedProjectId(directTopic);
|
|
241
|
-
return {
|
|
242
|
-
topicId: directTopic._id,
|
|
243
|
-
projectId: mapped ?? args.projectId,
|
|
244
|
-
tenantId: inherited.tenantId,
|
|
245
|
-
workspaceId: inherited.workspaceId,
|
|
246
|
-
source: "topic_inferred"
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
250
|
-
if (directTopic) {
|
|
251
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
252
|
-
const mapped = asMappedProjectId(directTopic);
|
|
253
|
-
return {
|
|
254
|
-
topicId: directTopic._id,
|
|
255
|
-
projectId: mapped ?? args.projectId,
|
|
256
|
-
tenantId: inherited.tenantId,
|
|
257
|
-
workspaceId: inherited.workspaceId,
|
|
258
|
-
source: "topic_inferred"
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
262
|
-
const primary = pickPrimaryTopic(topics);
|
|
263
|
-
if (primary) {
|
|
264
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
265
|
-
return {
|
|
266
|
-
topicId: primary._id,
|
|
267
|
-
projectId: args.projectId,
|
|
268
|
-
tenantId: inherited.tenantId,
|
|
269
|
-
workspaceId: inherited.workspaceId,
|
|
270
|
-
source: "project_mapped_topic"
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
274
|
-
ctx,
|
|
275
|
-
String(args.projectId)
|
|
276
|
-
);
|
|
277
|
-
if (nodeScope) {
|
|
278
|
-
return {
|
|
279
|
-
...nodeScope,
|
|
280
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
throw new Error(
|
|
284
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
throw new Error(
|
|
288
|
-
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
({
|
|
292
|
-
projectId: v.optional(v.string()),
|
|
293
|
-
topicId: v.optional(v.string())
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// src/workspaceIsolation.ts
|
|
297
|
-
function normalizeScopeValue2(value) {
|
|
298
|
-
if (typeof value !== "string") {
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
const normalized = value.trim();
|
|
302
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
303
|
-
}
|
|
304
|
-
function throwWorkspaceIsolationError(args) {
|
|
305
|
-
const error = new Error(args.message);
|
|
306
|
-
error.status = 409;
|
|
307
|
-
error.code = "INVARIANT_VIOLATION";
|
|
308
|
-
error.invariantCode = args.invariantCode;
|
|
309
|
-
error.suggestion = args.suggestion;
|
|
310
|
-
error.details = args.details;
|
|
311
|
-
throw error;
|
|
312
|
-
}
|
|
313
|
-
async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
|
|
314
|
-
const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
315
|
-
const resolved = {
|
|
316
|
-
tenantId: normalizeScopeValue2(node?.tenantId),
|
|
317
|
-
workspaceId: normalizeScopeValue2(node?.workspaceId),
|
|
318
|
-
epistemicLayer,
|
|
319
|
-
nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
|
|
320
|
-
};
|
|
321
|
-
if (!node) {
|
|
322
|
-
return resolved;
|
|
323
|
-
}
|
|
324
|
-
if (resolved.epistemicLayer === "ontological") {
|
|
325
|
-
return resolved;
|
|
326
|
-
}
|
|
327
|
-
if (resolved.tenantId || resolved.workspaceId) {
|
|
328
|
-
return resolved;
|
|
329
|
-
}
|
|
330
|
-
if (node.topicId) {
|
|
331
|
-
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
332
|
-
topicId: node.topicId
|
|
333
|
-
});
|
|
334
|
-
return {
|
|
335
|
-
...resolved,
|
|
336
|
-
tenantId: topicScope.tenantId,
|
|
337
|
-
workspaceId: topicScope.workspaceId
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
if (node.projectId) {
|
|
341
|
-
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
342
|
-
projectId: String(node.projectId)
|
|
343
|
-
});
|
|
344
|
-
return {
|
|
345
|
-
...resolved,
|
|
346
|
-
tenantId: topicScope.tenantId,
|
|
347
|
-
workspaceId: topicScope.workspaceId
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
return resolved;
|
|
351
|
-
}
|
|
352
|
-
function resolveRuntimePackMutationContext(args) {
|
|
353
|
-
if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
return {
|
|
357
|
-
toolName: args.runtimeToolName,
|
|
358
|
-
packKey: args.runtimePackKey,
|
|
359
|
-
packInstallScope: args.runtimePackInstallScope
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
363
|
-
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
367
|
-
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
368
|
-
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
throwWorkspaceIsolationError({
|
|
372
|
-
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
373
|
-
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
374
|
-
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
375
|
-
details: {
|
|
376
|
-
mutationName: args.mutationName,
|
|
377
|
-
toolName: args.runtime.toolName,
|
|
378
|
-
packKey: args.runtime.packKey,
|
|
379
|
-
targetWorkspaceId,
|
|
380
|
-
targetNodeType: args.target.nodeType,
|
|
381
|
-
targetLayer
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
|
|
386
29
|
// src/topicProjectOverlay.ts
|
|
387
|
-
var
|
|
30
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
388
31
|
function readNonEmptyString(value) {
|
|
389
32
|
if (typeof value !== "string") {
|
|
390
33
|
return;
|
|
@@ -401,11 +44,15 @@ function readStringArray(value) {
|
|
|
401
44
|
function readMetadata(topic) {
|
|
402
45
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
403
46
|
}
|
|
47
|
+
function omitMetadataKey(metadata, key) {
|
|
48
|
+
const { [key]: _omitted, ...rest } = metadata;
|
|
49
|
+
return rest;
|
|
50
|
+
}
|
|
404
51
|
function readLegacyProjectId(value) {
|
|
405
52
|
if (!value) {
|
|
406
53
|
return;
|
|
407
54
|
}
|
|
408
|
-
return readNonEmptyString(value[
|
|
55
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
409
56
|
}
|
|
410
57
|
function coerceVisibility(value) {
|
|
411
58
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
@@ -481,9 +128,12 @@ async function resolveTopicDoc(ctx, scopeId) {
|
|
|
481
128
|
);
|
|
482
129
|
}
|
|
483
130
|
try {
|
|
484
|
-
const topic = await ctx.runQuery(
|
|
485
|
-
|
|
486
|
-
|
|
131
|
+
const topic = await ctx.runQuery(
|
|
132
|
+
api.topics.getByLegacyScopeId,
|
|
133
|
+
{
|
|
134
|
+
projectId: String(scopeId)
|
|
135
|
+
}
|
|
136
|
+
);
|
|
487
137
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
488
138
|
return topic;
|
|
489
139
|
}
|
|
@@ -503,8 +153,18 @@ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
|
503
153
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
504
154
|
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
505
155
|
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
506
|
-
|
|
507
|
-
|
|
156
|
+
let createdAt = 0;
|
|
157
|
+
if (typeof topic.createdAt === "number") {
|
|
158
|
+
createdAt = topic.createdAt;
|
|
159
|
+
} else if (typeof topic._creationTime === "number") {
|
|
160
|
+
createdAt = topic._creationTime;
|
|
161
|
+
}
|
|
162
|
+
let updatedAt = createdAt;
|
|
163
|
+
if (typeof topic.updatedAt === "number") {
|
|
164
|
+
updatedAt = topic.updatedAt;
|
|
165
|
+
} else if (typeof metadata.updatedAt === "number") {
|
|
166
|
+
updatedAt = metadata.updatedAt;
|
|
167
|
+
}
|
|
508
168
|
return {
|
|
509
169
|
...metadata,
|
|
510
170
|
_id: outwardId,
|
|
@@ -546,183 +206,457 @@ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
|
546
206
|
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
547
207
|
return null;
|
|
548
208
|
}
|
|
549
|
-
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
209
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
210
|
+
}
|
|
211
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
212
|
+
let allTopics = [];
|
|
213
|
+
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
214
|
+
try {
|
|
215
|
+
allTopics = await ctx.db.query("topics").collect();
|
|
216
|
+
} catch (error) {
|
|
217
|
+
debugGraphPrimitiveFallback(
|
|
218
|
+
"[topicProjectOverlay] Failed to read topics table; falling back to API",
|
|
219
|
+
{ error }
|
|
220
|
+
);
|
|
221
|
+
allTopics = [];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
225
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
226
|
+
}
|
|
227
|
+
return allTopics.filter(
|
|
228
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
229
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
230
|
+
}
|
|
231
|
+
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
232
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
233
|
+
if (!topic) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const plan = buildTopicProjectOverlayPatchPlan(topic, value);
|
|
237
|
+
await applyTopicProjectOverlayPatch(ctx, topic, plan);
|
|
238
|
+
return materializeTopicProjectOverlay({
|
|
239
|
+
...topic,
|
|
240
|
+
...plan.patch,
|
|
241
|
+
metadata: plan.nextMetadata
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function buildTopicProjectOverlayPatchPlan(topic, value) {
|
|
245
|
+
const plan = {
|
|
246
|
+
nextMetadata: { ...readMetadata(topic) },
|
|
247
|
+
patch: {},
|
|
248
|
+
topicUpdateArgs: {
|
|
249
|
+
id: String(topic._id)
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
253
|
+
applyTopicProjectOverlayPatchEntry(plan, key, rawValue);
|
|
254
|
+
}
|
|
255
|
+
plan.patch.updatedAt = Date.now();
|
|
256
|
+
plan.patch.metadata = plan.nextMetadata;
|
|
257
|
+
plan.topicUpdateArgs.metadata = plan.nextMetadata;
|
|
258
|
+
return plan;
|
|
259
|
+
}
|
|
260
|
+
function applyTopicProjectOverlayPatchEntry(plan, key, rawValue) {
|
|
261
|
+
switch (key) {
|
|
262
|
+
case "_id":
|
|
263
|
+
case "projectId":
|
|
264
|
+
case "topicId":
|
|
265
|
+
case "legacyProjectId":
|
|
266
|
+
case "storageProjectId":
|
|
267
|
+
case "updatedAt":
|
|
268
|
+
case "createdAt":
|
|
269
|
+
return;
|
|
270
|
+
case "name":
|
|
271
|
+
case "description":
|
|
272
|
+
plan.patch[key] = rawValue;
|
|
273
|
+
plan.topicUpdateArgs[key] = rawValue;
|
|
274
|
+
return;
|
|
275
|
+
case "tenantId":
|
|
276
|
+
case "workspaceId":
|
|
277
|
+
case "ownerId":
|
|
278
|
+
throw new Error(
|
|
279
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
280
|
+
);
|
|
281
|
+
case "status":
|
|
282
|
+
applyTopicStatusPatch(plan, rawValue);
|
|
283
|
+
return;
|
|
284
|
+
case "visibility":
|
|
285
|
+
applyTopicVisibilityPatch(plan, rawValue);
|
|
286
|
+
return;
|
|
287
|
+
case "type":
|
|
288
|
+
applyTopicProjectTypePatch(plan, rawValue);
|
|
289
|
+
return;
|
|
290
|
+
default:
|
|
291
|
+
applyTopicMetadataPatch(plan, key, rawValue);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function applyTopicStatusPatch(plan, rawValue) {
|
|
295
|
+
const status = coerceStatus(rawValue);
|
|
296
|
+
if (status) {
|
|
297
|
+
plan.patch.status = status;
|
|
298
|
+
plan.topicUpdateArgs.status = status;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function applyTopicVisibilityPatch(plan, rawValue) {
|
|
302
|
+
const visibility = coerceVisibility(rawValue);
|
|
303
|
+
if (visibility) {
|
|
304
|
+
plan.patch.visibility = visibility;
|
|
305
|
+
plan.topicUpdateArgs.visibility = visibility;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function applyTopicProjectTypePatch(plan, rawValue) {
|
|
309
|
+
const projectType = readNonEmptyString(rawValue);
|
|
310
|
+
if (projectType) {
|
|
311
|
+
plan.nextMetadata.projectType = projectType;
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, "projectType");
|
|
315
|
+
}
|
|
316
|
+
function applyTopicMetadataPatch(plan, key, rawValue) {
|
|
317
|
+
if (rawValue === void 0) {
|
|
318
|
+
plan.nextMetadata = omitMetadataKey(plan.nextMetadata, key);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
plan.nextMetadata[key] = rawValue;
|
|
322
|
+
}
|
|
323
|
+
async function applyTopicProjectOverlayPatch(ctx, topic, plan) {
|
|
324
|
+
if (typeof ctx.runMutation === "function") {
|
|
325
|
+
try {
|
|
326
|
+
await ctx.runMutation(api.topics.update, plan.topicUpdateArgs);
|
|
327
|
+
} catch (error) {
|
|
328
|
+
if (!canPatchTopicViaLocalDb(ctx, error)) {
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
332
|
+
}
|
|
333
|
+
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
334
|
+
await ctx.db.patch(topic._id, plan.patch);
|
|
335
|
+
} else {
|
|
336
|
+
throw new Error(
|
|
337
|
+
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function canPatchTopicViaLocalDb(ctx, error) {
|
|
342
|
+
return isMissingLucernChildComponentError(error) && Boolean(ctx?.db) && typeof ctx.db?.patch === "function";
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/resolvers.ts
|
|
346
|
+
function isMissingLucernChildComponentError2(error) {
|
|
347
|
+
const message = getErrorMessage2(error);
|
|
348
|
+
return message.includes(
|
|
349
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
350
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
351
|
+
}
|
|
352
|
+
function getErrorMessage2(error) {
|
|
353
|
+
if (error instanceof Error) {
|
|
354
|
+
return error.message;
|
|
355
|
+
}
|
|
356
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
357
|
+
return error.message;
|
|
358
|
+
}
|
|
359
|
+
return "unknown error";
|
|
360
|
+
}
|
|
361
|
+
function isAdvisoryTopicPatch(value) {
|
|
362
|
+
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
363
|
+
const keys = Object.keys(value);
|
|
364
|
+
return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
|
|
365
|
+
}
|
|
366
|
+
async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
367
|
+
try {
|
|
368
|
+
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
if (!(isAdvisoryTopicPatch(value) && isMissingLucernChildComponentError2(error))) {
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
console.warn(
|
|
374
|
+
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
375
|
+
{
|
|
376
|
+
projectId,
|
|
377
|
+
keys: Object.keys(value),
|
|
378
|
+
error: getErrorMessage2(error)
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function defaultResolvers() {
|
|
384
|
+
return {
|
|
385
|
+
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
386
|
+
idMode: "legacy",
|
|
387
|
+
projectLikeOnly: false
|
|
388
|
+
}),
|
|
389
|
+
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
390
|
+
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
391
|
+
idMode: "legacy"
|
|
392
|
+
}),
|
|
393
|
+
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
var resolverOverrides = {};
|
|
397
|
+
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
398
|
+
return {
|
|
399
|
+
...defaultResolvers(),
|
|
400
|
+
...resolverOverrides
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
404
|
+
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
405
|
+
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
let node = null;
|
|
409
|
+
try {
|
|
410
|
+
const byGlobalId = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", ref)).first();
|
|
411
|
+
if (byGlobalId && byGlobalId.nodeType === "topic") {
|
|
412
|
+
node = byGlobalId;
|
|
413
|
+
}
|
|
414
|
+
} catch (error) {
|
|
415
|
+
debugGraphPrimitiveFallback(
|
|
416
|
+
"[topicScope] topic-node scope lookup by globalId failed",
|
|
417
|
+
{ error, ref }
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
if (!node) {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
|
|
424
|
+
if (!scopeKey) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
topicId: scopeKey,
|
|
429
|
+
projectId: asMappedProjectId(node),
|
|
430
|
+
source: "topic_node"
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function asMappedProjectId(topic) {
|
|
434
|
+
if (!topic) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
438
|
+
topic[LEGACY_SCOPE_FIELD2]
|
|
439
|
+
);
|
|
440
|
+
if (directLegacyProjectId) {
|
|
441
|
+
return directLegacyProjectId;
|
|
442
|
+
}
|
|
443
|
+
const metadata = topic.metadata || {};
|
|
444
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
445
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
446
|
+
}
|
|
447
|
+
function normalizeScopeValue(value) {
|
|
448
|
+
if (typeof value !== "string") {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const normalized = value.trim();
|
|
452
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
453
|
+
}
|
|
454
|
+
function pickPrimaryTopic(candidates) {
|
|
455
|
+
return [...candidates].sort((a, b) => {
|
|
456
|
+
const depthA = a.depth ?? 9999;
|
|
457
|
+
const depthB = b.depth ?? 9999;
|
|
458
|
+
if (depthA !== depthB) {
|
|
459
|
+
return depthA - depthB;
|
|
460
|
+
}
|
|
461
|
+
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
462
|
+
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
463
|
+
if (createdA !== createdB) {
|
|
464
|
+
return createdA - createdB;
|
|
465
|
+
}
|
|
466
|
+
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
467
|
+
})[0];
|
|
468
|
+
}
|
|
469
|
+
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
470
|
+
const query2 = ctx.db.query("topics");
|
|
471
|
+
try {
|
|
472
|
+
return await query2.withIndex(
|
|
473
|
+
"by_graph_scope_project",
|
|
474
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
|
|
475
|
+
).collect();
|
|
476
|
+
} catch (error) {
|
|
477
|
+
debugGraphPrimitiveFallback(
|
|
478
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
479
|
+
{
|
|
480
|
+
error,
|
|
481
|
+
scopeId
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
const topics = await query2.collect();
|
|
485
|
+
return topics.filter((topic) => {
|
|
486
|
+
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
487
|
+
const mappedProjectId = asMappedProjectId(topic);
|
|
488
|
+
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async function tryResolveHostTopicById(ctx, topicId) {
|
|
493
|
+
if (typeof ctx.runQuery !== "function") {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
return await ctx.runQuery(api.topics.get, {
|
|
498
|
+
id: topicId
|
|
499
|
+
}) ?? null;
|
|
500
|
+
} catch (error) {
|
|
501
|
+
debugGraphPrimitiveFallback(
|
|
502
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
503
|
+
{
|
|
504
|
+
error,
|
|
505
|
+
topicId
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
512
|
+
if (typeof ctx.runQuery !== "function") {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
try {
|
|
516
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
517
|
+
projectId: legacyScopeId
|
|
518
|
+
}) ?? null;
|
|
519
|
+
} catch (error) {
|
|
520
|
+
debugGraphPrimitiveFallback(
|
|
521
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
522
|
+
{
|
|
523
|
+
error,
|
|
524
|
+
legacyScopeId
|
|
525
|
+
}
|
|
526
|
+
);
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
550
529
|
}
|
|
551
|
-
async function
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
debugGraphPrimitiveFallback(
|
|
558
|
-
"[topicProjectOverlay] Failed to read topics table; falling back to API",
|
|
559
|
-
{ error }
|
|
560
|
-
);
|
|
561
|
-
allTopics = [];
|
|
562
|
-
}
|
|
530
|
+
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
531
|
+
const MAX_DEPTH = 10;
|
|
532
|
+
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
533
|
+
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
534
|
+
if (tenantId && workspaceId) {
|
|
535
|
+
return { tenantId, workspaceId };
|
|
563
536
|
}
|
|
564
|
-
|
|
565
|
-
|
|
537
|
+
let current = topic;
|
|
538
|
+
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
539
|
+
current = await ctx.db.get(current.parentTopicId);
|
|
540
|
+
if (!current) {
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
if (!tenantId) {
|
|
544
|
+
tenantId = normalizeScopeValue(current.tenantId);
|
|
545
|
+
}
|
|
546
|
+
if (!workspaceId) {
|
|
547
|
+
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
548
|
+
}
|
|
549
|
+
if (tenantId && workspaceId) {
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
566
552
|
}
|
|
567
|
-
return
|
|
568
|
-
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
569
|
-
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
553
|
+
return { tenantId, workspaceId };
|
|
570
554
|
}
|
|
571
|
-
async function
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
return null;
|
|
555
|
+
async function resolveTopicProjectScope(ctx, args) {
|
|
556
|
+
if (args.topicId) {
|
|
557
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
575
558
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const topicUpdateArgs = {
|
|
579
|
-
id: String(topic._id)
|
|
580
|
-
};
|
|
581
|
-
for (const [key, rawValue] of Object.entries(value)) {
|
|
582
|
-
switch (key) {
|
|
583
|
-
case "_id":
|
|
584
|
-
case "projectId":
|
|
585
|
-
case "topicId":
|
|
586
|
-
case "legacyProjectId":
|
|
587
|
-
case "storageProjectId":
|
|
588
|
-
break;
|
|
589
|
-
case "name":
|
|
590
|
-
case "description":
|
|
591
|
-
patch[key] = rawValue;
|
|
592
|
-
topicUpdateArgs[key] = rawValue;
|
|
593
|
-
break;
|
|
594
|
-
case "tenantId":
|
|
595
|
-
case "workspaceId":
|
|
596
|
-
case "ownerId":
|
|
597
|
-
throw new Error(
|
|
598
|
-
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
599
|
-
);
|
|
600
|
-
case "status": {
|
|
601
|
-
const status = coerceStatus(rawValue);
|
|
602
|
-
if (status) {
|
|
603
|
-
patch.status = status;
|
|
604
|
-
topicUpdateArgs.status = status;
|
|
605
|
-
}
|
|
606
|
-
break;
|
|
607
|
-
}
|
|
608
|
-
case "visibility": {
|
|
609
|
-
const visibility = coerceVisibility(rawValue);
|
|
610
|
-
if (visibility) {
|
|
611
|
-
patch.visibility = visibility;
|
|
612
|
-
topicUpdateArgs.visibility = visibility;
|
|
613
|
-
}
|
|
614
|
-
break;
|
|
615
|
-
}
|
|
616
|
-
case "type": {
|
|
617
|
-
const projectType = readNonEmptyString(rawValue);
|
|
618
|
-
if (projectType) {
|
|
619
|
-
nextMetadata.projectType = projectType;
|
|
620
|
-
} else {
|
|
621
|
-
delete nextMetadata.projectType;
|
|
622
|
-
}
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
case "updatedAt":
|
|
626
|
-
case "createdAt":
|
|
627
|
-
break;
|
|
628
|
-
default:
|
|
629
|
-
if (rawValue === void 0) {
|
|
630
|
-
delete nextMetadata[key];
|
|
631
|
-
} else {
|
|
632
|
-
nextMetadata[key] = rawValue;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
559
|
+
if (args.projectId) {
|
|
560
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
635
561
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
throw error;
|
|
645
|
-
}
|
|
646
|
-
await ctx.db.patch(String(topic._id), patch);
|
|
647
|
-
}
|
|
648
|
-
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
649
|
-
await ctx.db.patch(String(topic._id), patch);
|
|
650
|
-
} else {
|
|
651
|
-
throw new Error(
|
|
652
|
-
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
653
|
-
);
|
|
562
|
+
throw new Error(
|
|
563
|
+
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
567
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
568
|
+
if (topic) {
|
|
569
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
654
570
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
});
|
|
571
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
572
|
+
if (nodeScope) {
|
|
573
|
+
return nodeScope;
|
|
574
|
+
}
|
|
575
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
660
576
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
577
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
578
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
579
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
580
|
+
idLogKey: "topicId"
|
|
581
|
+
});
|
|
582
|
+
if (direct) {
|
|
583
|
+
return direct;
|
|
584
|
+
}
|
|
585
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
586
|
+
if (hostTopic) {
|
|
587
|
+
return hostTopic;
|
|
588
|
+
}
|
|
589
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
668
590
|
}
|
|
669
|
-
function
|
|
670
|
-
|
|
671
|
-
|
|
591
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
592
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
593
|
+
ctx,
|
|
594
|
+
legacyProjectId
|
|
595
|
+
);
|
|
596
|
+
if (directTopic) {
|
|
597
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
598
|
+
fallbackProjectId: legacyProjectId
|
|
599
|
+
});
|
|
672
600
|
}
|
|
673
|
-
|
|
674
|
-
|
|
601
|
+
const primary = pickPrimaryTopic(
|
|
602
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
603
|
+
);
|
|
604
|
+
if (primary) {
|
|
605
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
606
|
+
fallbackProjectId: legacyProjectId
|
|
607
|
+
});
|
|
675
608
|
}
|
|
676
|
-
|
|
609
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
610
|
+
if (nodeScope) {
|
|
611
|
+
return {
|
|
612
|
+
...nodeScope,
|
|
613
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
throw new Error(
|
|
617
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
618
|
+
);
|
|
677
619
|
}
|
|
678
|
-
function
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
620
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
621
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
622
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
623
|
+
idLogKey: "projectId"
|
|
624
|
+
});
|
|
625
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
682
626
|
}
|
|
683
|
-
async function
|
|
627
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
684
628
|
try {
|
|
685
|
-
await
|
|
629
|
+
return await ctx.db.get(id);
|
|
686
630
|
} catch (error) {
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
{
|
|
693
|
-
projectId,
|
|
694
|
-
keys: Object.keys(value),
|
|
695
|
-
error: getErrorMessage2(error)
|
|
696
|
-
}
|
|
697
|
-
);
|
|
631
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
632
|
+
error,
|
|
633
|
+
[log.idLogKey]: id
|
|
634
|
+
});
|
|
635
|
+
return null;
|
|
698
636
|
}
|
|
699
637
|
}
|
|
700
|
-
function
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
idMode: "legacy",
|
|
704
|
-
projectLikeOnly: false
|
|
705
|
-
}),
|
|
706
|
-
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
707
|
-
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
708
|
-
idMode: "legacy"
|
|
709
|
-
}),
|
|
710
|
-
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
var resolverOverrides = {};
|
|
714
|
-
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
638
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
639
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
640
|
+
const mapped = asMappedProjectId(topic);
|
|
715
641
|
return {
|
|
716
|
-
|
|
717
|
-
...
|
|
642
|
+
topicId: topic._id,
|
|
643
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
644
|
+
tenantId: inherited.tenantId,
|
|
645
|
+
workspaceId: inherited.workspaceId,
|
|
646
|
+
source
|
|
718
647
|
};
|
|
719
648
|
}
|
|
649
|
+
({
|
|
650
|
+
projectId: v.optional(v.string()),
|
|
651
|
+
topicId: v.optional(v.string())
|
|
652
|
+
});
|
|
720
653
|
|
|
721
654
|
// src/epistemicQuestions.helpers.ts
|
|
722
655
|
async function markProjectGraphDirty(ctx, projectId, topicId) {
|
|
656
|
+
const markCacheStaleByTopic = internal.graphAnalysisCache.markCacheStaleByTopic;
|
|
723
657
|
const normalizedProjectId = typeof projectId === "string" && projectId.trim().length > 0 ? projectId : void 0;
|
|
724
658
|
const normalizedTopicId = typeof topicId === "string" && topicId.trim().length > 0 ? topicId : void 0;
|
|
725
|
-
if (!normalizedProjectId
|
|
659
|
+
if (!(normalizedProjectId || normalizedTopicId)) {
|
|
726
660
|
return;
|
|
727
661
|
}
|
|
728
662
|
if (normalizedProjectId) {
|
|
@@ -735,17 +669,17 @@ async function markProjectGraphDirty(ctx, projectId, topicId) {
|
|
|
735
669
|
);
|
|
736
670
|
}
|
|
737
671
|
if (normalizedTopicId) {
|
|
738
|
-
await ctx.scheduler.runAfter(
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
672
|
+
await ctx.scheduler.runAfter(0, markCacheStaleByTopic, {
|
|
673
|
+
topicId: normalizedTopicId
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
const resolvedProjectId = normalizedTopicId ?? normalizedProjectId;
|
|
677
|
+
if (!resolvedProjectId) {
|
|
678
|
+
return;
|
|
745
679
|
}
|
|
746
680
|
await resolveGraphPrimitivesAppResolvers().patchProject(
|
|
747
681
|
ctx,
|
|
748
|
-
|
|
682
|
+
resolvedProjectId,
|
|
749
683
|
{
|
|
750
684
|
lastActivityAt: Date.now()
|
|
751
685
|
}
|
|
@@ -754,8 +688,144 @@ async function markProjectGraphDirty(ctx, projectId, topicId) {
|
|
|
754
688
|
function normalizeQuestionTopicId(topicId) {
|
|
755
689
|
return typeof topicId === "string" && topicId.trim().length > 0 ? topicId : void 0;
|
|
756
690
|
}
|
|
691
|
+
function normalizeScopeValue2(value) {
|
|
692
|
+
if (typeof value !== "string") {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const normalized = value.trim();
|
|
696
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
697
|
+
}
|
|
698
|
+
function throwWorkspaceIsolationError(args) {
|
|
699
|
+
const error = new Error(args.message);
|
|
700
|
+
error.status = 409;
|
|
701
|
+
error.code = "INVARIANT_VIOLATION";
|
|
702
|
+
error.invariantCode = args.invariantCode;
|
|
703
|
+
error.suggestion = args.suggestion;
|
|
704
|
+
error.details = args.details;
|
|
705
|
+
throw error;
|
|
706
|
+
}
|
|
707
|
+
async function resolveNodeScopeForWorkspaceIsolation(ctx, node) {
|
|
708
|
+
const epistemicLayer = typeof node?.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
709
|
+
const resolved = {
|
|
710
|
+
tenantId: normalizeScopeValue2(node?.tenantId),
|
|
711
|
+
workspaceId: normalizeScopeValue2(node?.workspaceId),
|
|
712
|
+
epistemicLayer,
|
|
713
|
+
nodeType: typeof node?.nodeType === "string" ? node.nodeType : void 0
|
|
714
|
+
};
|
|
715
|
+
if (!node) {
|
|
716
|
+
return resolved;
|
|
717
|
+
}
|
|
718
|
+
if (resolved.epistemicLayer === "ontological") {
|
|
719
|
+
return resolved;
|
|
720
|
+
}
|
|
721
|
+
if (resolved.tenantId || resolved.workspaceId) {
|
|
722
|
+
return resolved;
|
|
723
|
+
}
|
|
724
|
+
const topicId = normalizeScopeValue2(node.topicId);
|
|
725
|
+
if (topicId) {
|
|
726
|
+
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
727
|
+
topicId
|
|
728
|
+
});
|
|
729
|
+
return {
|
|
730
|
+
...resolved,
|
|
731
|
+
tenantId: topicScope.tenantId,
|
|
732
|
+
workspaceId: topicScope.workspaceId
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
if (node.projectId) {
|
|
736
|
+
const topicScope = await resolveTopicProjectScope(ctx, {
|
|
737
|
+
projectId: String(node.projectId)
|
|
738
|
+
});
|
|
739
|
+
return {
|
|
740
|
+
...resolved,
|
|
741
|
+
tenantId: topicScope.tenantId,
|
|
742
|
+
workspaceId: topicScope.workspaceId
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
return resolved;
|
|
746
|
+
}
|
|
747
|
+
function resolveRuntimePackMutationContext(args) {
|
|
748
|
+
if (!(args.runtimeToolName || args.runtimePackKey || args.runtimePackInstallScope)) {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
return {
|
|
752
|
+
toolName: args.runtimeToolName,
|
|
753
|
+
packKey: args.runtimePackKey,
|
|
754
|
+
packInstallScope: args.runtimePackInstallScope
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
758
|
+
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
762
|
+
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
763
|
+
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
throwWorkspaceIsolationError({
|
|
767
|
+
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
768
|
+
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
769
|
+
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
770
|
+
details: {
|
|
771
|
+
mutationName: args.mutationName,
|
|
772
|
+
toolName: args.runtime.toolName,
|
|
773
|
+
packKey: args.runtime.packKey,
|
|
774
|
+
targetWorkspaceId,
|
|
775
|
+
targetNodeType: args.target.nodeType,
|
|
776
|
+
targetLayer
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
}
|
|
757
780
|
|
|
758
781
|
// src/epistemicQuestions.lifecycle.ts
|
|
782
|
+
function readOptionalString(value) {
|
|
783
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
784
|
+
}
|
|
785
|
+
function readConvexId(value) {
|
|
786
|
+
const normalized = readOptionalString(value);
|
|
787
|
+
return normalized ? normalized : null;
|
|
788
|
+
}
|
|
789
|
+
function readRecord(value) {
|
|
790
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
791
|
+
}
|
|
792
|
+
function readQuestionLifecycleNode(value) {
|
|
793
|
+
const record = readRecord(value);
|
|
794
|
+
if (!record) {
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
const nodeType = readOptionalString(record.nodeType);
|
|
798
|
+
if (!nodeType) {
|
|
799
|
+
return null;
|
|
800
|
+
}
|
|
801
|
+
const node = {
|
|
802
|
+
metadata: readRecord(record.metadata) ?? {},
|
|
803
|
+
nodeType
|
|
804
|
+
};
|
|
805
|
+
const projectId = readOptionalString(record.projectId);
|
|
806
|
+
if (projectId !== void 0) {
|
|
807
|
+
node.projectId = projectId;
|
|
808
|
+
}
|
|
809
|
+
const topicId = readOptionalString(record.topicId);
|
|
810
|
+
if (topicId !== void 0) {
|
|
811
|
+
node.topicId = topicId;
|
|
812
|
+
}
|
|
813
|
+
return node;
|
|
814
|
+
}
|
|
815
|
+
function requireQuestionLifecycleNode(value) {
|
|
816
|
+
const node = readQuestionLifecycleNode(value);
|
|
817
|
+
if (node?.nodeType !== "question") {
|
|
818
|
+
throw new Error("Question not found");
|
|
819
|
+
}
|
|
820
|
+
return node;
|
|
821
|
+
}
|
|
822
|
+
function resolveLifecycleNodeId(args) {
|
|
823
|
+
const nodeId = readConvexId(args.nodeId) ?? readConvexId(args.questionId);
|
|
824
|
+
if (!nodeId) {
|
|
825
|
+
throw new Error("Missing nodeId/questionId");
|
|
826
|
+
}
|
|
827
|
+
return nodeId;
|
|
828
|
+
}
|
|
759
829
|
var updateStatus = mutation({
|
|
760
830
|
args: {
|
|
761
831
|
nodeId: v.optional(v.id("epistemicNodes")),
|
|
@@ -785,25 +855,19 @@ var updateStatus = mutation({
|
|
|
785
855
|
},
|
|
786
856
|
returns: permissiveReturn,
|
|
787
857
|
handler: async (ctx, args) => {
|
|
788
|
-
const resolvedNodeId = args
|
|
789
|
-
if (!resolvedNodeId) {
|
|
790
|
-
throw new Error("Missing nodeId/questionId");
|
|
791
|
-
}
|
|
858
|
+
const resolvedNodeId = resolveLifecycleNodeId(args);
|
|
792
859
|
const resolvedUserId = args.userId || await getCurrentUserId(ctx);
|
|
793
860
|
if (!resolvedUserId) {
|
|
794
861
|
throw new Error("Not authenticated");
|
|
795
862
|
}
|
|
796
|
-
const node = await ctx.db.get(resolvedNodeId);
|
|
797
|
-
if (!node || node.nodeType !== "question") {
|
|
798
|
-
throw new Error("Question not found");
|
|
799
|
-
}
|
|
863
|
+
const node = requireQuestionLifecycleNode(await ctx.db.get(resolvedNodeId));
|
|
800
864
|
assertTenantPackWorkspaceMutationAllowed({
|
|
801
865
|
runtime: resolveRuntimePackMutationContext(args),
|
|
802
866
|
target: await resolveNodeScopeForWorkspaceIsolation(ctx, node),
|
|
803
867
|
mutationName: "epistemicQuestions.updateStatus"
|
|
804
868
|
});
|
|
805
869
|
const now = Date.now();
|
|
806
|
-
const metadata = node.metadata
|
|
870
|
+
const metadata = node.metadata;
|
|
807
871
|
const previousStatus = metadata.questionStatus || "open";
|
|
808
872
|
await ctx.db.patch(resolvedNodeId, {
|
|
809
873
|
updatedAt: now,
|
|
@@ -857,12 +921,9 @@ var updatePriority = mutation({
|
|
|
857
921
|
},
|
|
858
922
|
returns: permissiveReturn,
|
|
859
923
|
handler: async (ctx, args) => {
|
|
860
|
-
const node = await ctx.db.get(args.nodeId);
|
|
861
|
-
if (!node || node.nodeType !== "question") {
|
|
862
|
-
throw new Error("Question not found");
|
|
863
|
-
}
|
|
924
|
+
const node = requireQuestionLifecycleNode(await ctx.db.get(args.nodeId));
|
|
864
925
|
const now = Date.now();
|
|
865
|
-
const metadata = node.metadata
|
|
926
|
+
const metadata = node.metadata;
|
|
866
927
|
const previousPriority = metadata.priority || "medium";
|
|
867
928
|
await ctx.db.patch(args.nodeId, {
|
|
868
929
|
updatedAt: now,
|