@lucern/graph-primitives 1.0.50 → 1.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/beliefDecay.js +34 -186
  2. package/dist/beliefEvidenceLinks.js +32 -187
  3. package/dist/contradictions.js +34 -187
  4. package/dist/entityLifecycle.js +88 -205
  5. package/dist/epistemicAnswers.js +35 -187
  6. package/dist/epistemicBeliefs.admin.js +34 -187
  7. package/dist/epistemicBeliefs.backfills.js +34 -188
  8. package/dist/epistemicBeliefs.confidence.d.ts +1 -1
  9. package/dist/epistemicBeliefs.confidence.js +32 -188
  10. package/dist/epistemicBeliefs.core.js +37 -187
  11. package/dist/epistemicBeliefs.d.ts +1 -1
  12. package/dist/epistemicBeliefs.helpers.d.ts +1 -1
  13. package/dist/epistemicBeliefs.helpers.js +34 -186
  14. package/dist/epistemicBeliefs.internal.js +37 -187
  15. package/dist/epistemicBeliefs.js +37 -187
  16. package/dist/epistemicBeliefs.lifecycle.js +32 -188
  17. package/dist/epistemicBeliefs.links.js +34 -188
  18. package/dist/epistemicContracts.evaluators.js +34 -188
  19. package/dist/epistemicContracts.handlers.js +34 -188
  20. package/dist/epistemicContracts.js +34 -188
  21. package/dist/epistemicEdges.d.ts +1 -1
  22. package/dist/epistemicEdges.helpers.d.ts +1 -1
  23. package/dist/epistemicEdges.js +32 -187
  24. package/dist/epistemicEdges.mutations.js +34 -186
  25. package/dist/epistemicEdges.queries.js +35 -188
  26. package/dist/epistemicEdges.types.d.ts +1 -1
  27. package/dist/epistemicEvidence.d.ts +1 -1
  28. package/dist/epistemicEvidence.js +37 -187
  29. package/dist/epistemicEvidenceHelpers.d.ts +1 -1
  30. package/dist/epistemicEvidenceHelpers.js +34 -186
  31. package/dist/epistemicEvidenceMutations.js +37 -187
  32. package/dist/epistemicEvidenceQueries.js +34 -186
  33. package/dist/epistemicHelpers.js +4 -1
  34. package/dist/epistemicInsert.js +4 -1
  35. package/dist/epistemicNodeCreation.js +4 -1
  36. package/dist/epistemicNodes.helpers.d.ts +1 -1
  37. package/dist/epistemicNodes.internal.js +35 -188
  38. package/dist/epistemicNodes.js +37 -188
  39. package/dist/epistemicNodes.mutations.js +35 -188
  40. package/dist/epistemicNodes.queries.js +35 -188
  41. package/dist/epistemicQuestions.conviction.js +34 -186
  42. package/dist/epistemicQuestions.create.js +37 -187
  43. package/dist/epistemicQuestions.d.ts +1 -1
  44. package/dist/epistemicQuestions.evidence.js +37 -187
  45. package/dist/epistemicQuestions.helpers.d.ts +1 -1
  46. package/dist/epistemicQuestions.helpers.js +34 -186
  47. package/dist/epistemicQuestions.js +37 -187
  48. package/dist/epistemicQuestions.lifecycle.js +34 -186
  49. package/dist/epistemicQuestions.queries.js +34 -186
  50. package/dist/epistemicQuestions.sprint.js +35 -188
  51. package/dist/epistemicQuestions.tail.js +37 -187
  52. package/dist/epistemicSources.js +35 -188
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +98 -213
  55. package/dist/proof-attestation.json +1 -1
  56. package/dist/questionEvidenceLinks.js +34 -187
  57. package/dist/scopeResolverCompat.d.ts +1 -1
  58. package/dist/scopeResolverCompat.js +32 -193
  59. package/dist/topicOntologyResolver.d.ts +3 -3
  60. package/dist/topicOntologyResolver.js +57 -18
  61. package/dist/{topicScope-DJVa0mLa.d.ts → topicScope-CL1IVOmv.d.ts} +2 -2
  62. package/dist/topicScope.d.ts +1 -1
  63. package/dist/topicScope.js +32 -193
  64. package/dist/workflowBridge.js +32 -193
  65. package/dist/workspaceIsolation.d.ts +1 -1
  66. package/dist/workspaceIsolation.js +32 -193
  67. package/package.json +4 -4
@@ -4,7 +4,7 @@ import { v } from 'convex/values';
4
4
  import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
5
  import { componentsGeneric, internalMutationGeneric, mutationGeneric } from 'convex/server';
6
6
  import { normalizeTupleContradictionPolicy, mkOpinion, conditionalDeduction, project, dampedDependencyCascade, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, confidenceFromSL, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, hasProjectedOpinionChanged } from '@lucern/confidence';
7
- import { generateUuidV7 } from '@lucern/contracts/ids';
7
+ import { generateUuidV7, isUuidV7 } from '@lucern/contracts/ids';
8
8
  import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
9
9
  import '@lucern/access-control/audience';
10
10
  import { getCurrentUserId } from '@lucern/access-control/auth';
@@ -14,7 +14,6 @@ import { throwStructuredMutationError } from '@lucern/access-control/structuredM
14
14
  var unsafeApi = unsafeConvexAnyApi(
15
15
  "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
16
16
  );
17
- var api = unsafeApi;
18
17
  componentsGeneric();
19
18
  var internal = unsafeApi;
20
19
  var internalMutation = internalMutationGeneric;
@@ -460,16 +459,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
460
459
  if (!node) {
461
460
  return null;
462
461
  }
463
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
462
+ const scopeKey = canonicalTopicGlobalId(node);
464
463
  if (!scopeKey) {
465
- return null;
464
+ throw new Error(
465
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
466
+ );
466
467
  }
468
+ const metadata = node.metadata ?? {};
467
469
  return {
468
470
  topicId: scopeKey,
469
471
  projectId: asMappedProjectId(node),
470
- source: "topic_node"
472
+ source: "topic_node",
473
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
474
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
471
475
  };
472
476
  }
477
+ function canonicalTopicGlobalId(node) {
478
+ const globalId = normalizeScopeValue(node.globalId);
479
+ if (globalId && isUuidV7(globalId)) {
480
+ return globalId;
481
+ }
482
+ const topicId = normalizeScopeValue(node.topicId);
483
+ return topicId && isUuidV7(topicId) ? topicId : null;
484
+ }
485
+ function requireUuidV7TopicScope(field, value) {
486
+ if (!isUuidV7(value)) {
487
+ throw new Error(
488
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
489
+ );
490
+ }
491
+ }
473
492
  function asMappedProjectId(topic) {
474
493
  if (!topic) {
475
494
  return;
@@ -491,200 +510,25 @@ function normalizeScopeValue(value) {
491
510
  const normalized = value.trim();
492
511
  return normalized.length > 0 ? normalized : void 0;
493
512
  }
494
- function pickPrimaryTopic(candidates) {
495
- return [...candidates].sort((a, b) => {
496
- const depthA = a.depth ?? 9999;
497
- const depthB = b.depth ?? 9999;
498
- if (depthA !== depthB) {
499
- return depthA - depthB;
500
- }
501
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
502
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
503
- if (createdA !== createdB) {
504
- return createdA - createdB;
505
- }
506
- return String(a.name || "").localeCompare(String(b.name || ""));
507
- })[0];
508
- }
509
- async function findTopicsByScopeAlias(ctx, scopeId) {
510
- const query = ctx.db.query("topics");
511
- try {
512
- return await query.withIndex(
513
- "by_graph_scope_project",
514
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
515
- ).collect();
516
- } catch (error) {
517
- debugGraphPrimitiveFallback(
518
- "[topicScope] Failed to resolve scope alias via index",
519
- {
520
- error,
521
- scopeId
522
- }
523
- );
524
- const topics = await query.collect();
525
- return topics.filter((topic) => {
526
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
527
- const mappedProjectId = asMappedProjectId(topic);
528
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
529
- });
530
- }
531
- }
532
- async function tryResolveHostTopicById(ctx, topicId) {
533
- if (typeof ctx.runQuery !== "function") {
534
- return null;
535
- }
536
- try {
537
- return await ctx.runQuery(api.topics.get, {
538
- id: topicId
539
- }) ?? null;
540
- } catch (error) {
541
- debugGraphPrimitiveFallback(
542
- "[topicScope] Failed to resolve topic by host query",
543
- {
544
- error,
545
- topicId
546
- }
547
- );
548
- return null;
549
- }
550
- }
551
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
552
- if (typeof ctx.runQuery !== "function") {
553
- return null;
554
- }
555
- try {
556
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
557
- projectId: legacyScopeId
558
- }) ?? null;
559
- } catch (error) {
560
- debugGraphPrimitiveFallback(
561
- "[topicScope] Failed to resolve topic by legacy scope",
562
- {
563
- error,
564
- legacyScopeId
565
- }
566
- );
567
- return null;
568
- }
569
- }
570
- async function resolveInheritedWorkspaceScope(ctx, topic) {
571
- const MAX_DEPTH = 10;
572
- let tenantId = normalizeScopeValue(topic.tenantId);
573
- let workspaceId = normalizeScopeValue(topic.workspaceId);
574
- if (tenantId && workspaceId) {
575
- return { tenantId, workspaceId };
576
- }
577
- let current = topic;
578
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
579
- current = await ctx.db.get(current.parentTopicId);
580
- if (!current) {
581
- break;
582
- }
583
- if (!tenantId) {
584
- tenantId = normalizeScopeValue(current.tenantId);
585
- }
586
- if (!workspaceId) {
587
- workspaceId = normalizeScopeValue(current.workspaceId);
588
- }
589
- if (tenantId && workspaceId) {
590
- break;
591
- }
592
- }
593
- return { tenantId, workspaceId };
594
- }
595
513
  async function resolveTopicProjectScope(ctx, args) {
596
514
  if (args.topicId) {
597
515
  return await resolveScopeFromTopicId(ctx, args.topicId);
598
516
  }
599
517
  if (args.projectId) {
600
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
518
+ throw new Error(
519
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
520
+ );
601
521
  }
602
- throw new Error(
603
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
604
- );
522
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
605
523
  }
606
524
  async function resolveScopeFromTopicId(ctx, topicId) {
607
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
608
- if (topic) {
609
- return await buildTopicScope(ctx, topic, "topic");
610
- }
611
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
525
+ const topicGlobalId = String(topicId);
526
+ requireUuidV7TopicScope("topicId", topicGlobalId);
527
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
612
528
  if (nodeScope) {
613
529
  return nodeScope;
614
530
  }
615
- throw new Error(`Topic not found: ${String(topicId)}`);
616
- }
617
- async function resolveTopicDocFromTopicId(ctx, topicId) {
618
- const direct = await tryReadTopicDoc(ctx, topicId, {
619
- failureLog: "[topicScope] Failed to load topic by direct id",
620
- idLogKey: "topicId"
621
- });
622
- if (direct) {
623
- return direct;
624
- }
625
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
626
- if (hostTopic) {
627
- return hostTopic;
628
- }
629
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
630
- }
631
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
632
- const directTopic = await resolveDirectLegacyProjectTopic(
633
- ctx,
634
- legacyProjectId
635
- );
636
- if (directTopic) {
637
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
638
- fallbackProjectId: legacyProjectId
639
- });
640
- }
641
- const primary = pickPrimaryTopic(
642
- await findTopicsByScopeAlias(ctx, legacyProjectId)
643
- );
644
- if (primary) {
645
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
646
- fallbackProjectId: legacyProjectId
647
- });
648
- }
649
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
650
- if (nodeScope) {
651
- return {
652
- ...nodeScope,
653
- projectId: nodeScope.projectId ?? legacyProjectId
654
- };
655
- }
656
- throw new Error(
657
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
658
- );
659
- }
660
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
661
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
662
- failureLog: "[topicScope] Failed to load direct project topic",
663
- idLogKey: "projectId"
664
- });
665
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
666
- }
667
- async function tryReadTopicDoc(ctx, id, log) {
668
- try {
669
- return await ctx.db.get(id);
670
- } catch (error) {
671
- debugGraphPrimitiveFallback(log.failureLog, {
672
- error,
673
- [log.idLogKey]: id
674
- });
675
- return null;
676
- }
677
- }
678
- async function buildTopicScope(ctx, topic, source, options = {}) {
679
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
680
- const mapped = asMappedProjectId(topic);
681
- return {
682
- topicId: topic._id,
683
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
684
- tenantId: inherited.tenantId,
685
- workspaceId: inherited.workspaceId,
686
- source
687
- };
531
+ throw new Error(`Topic not found: ${topicGlobalId}`);
688
532
  }
689
533
  ({
690
534
  projectId: v.optional(v.string()),
@@ -7,7 +7,7 @@ import '@lucern/access-control/audience';
7
7
  import { getCurrentUserId } from '@lucern/access-control/auth';
8
8
  import { throwStructuredMutationError } from '@lucern/access-control/structuredMutationError';
9
9
  import { normalizeTupleContradictionPolicy } from '@lucern/confidence';
10
- import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint } from '@lucern/contracts/ids';
10
+ import { generateGlobalId, assertUuidV7Identity, assertStorageEdgeVocabulary, assertUuidShapedEdgeEndpoint, isUuidV7 } from '@lucern/contracts/ids';
11
11
  import { assertEdgePolicyAllowed } from '@lucern/contracts/manifests/edge-policy-manifest';
12
12
  import { edgePolicyManifest } from '@lucern/contracts/manifests/edge-policy-manifest.data';
13
13
 
@@ -15,7 +15,6 @@ import { edgePolicyManifest } from '@lucern/contracts/manifests/edge-policy-mani
15
15
  var unsafeApi = unsafeConvexAnyApi(
16
16
  "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
17
17
  );
18
- var api = unsafeApi;
19
18
  componentsGeneric();
20
19
  var internal = unsafeApi;
21
20
  var mutation = mutationGeneric;
@@ -32,6 +31,8 @@ function debugGraphPrimitiveFallback(message, context) {
32
31
  }
33
32
  console.debug(message, context ?? {});
34
33
  }
34
+
35
+ // src/topicScope.ts
35
36
  var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
36
37
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
37
38
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -52,16 +53,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
52
53
  if (!node) {
53
54
  return null;
54
55
  }
55
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
56
+ const scopeKey = canonicalTopicGlobalId(node);
56
57
  if (!scopeKey) {
57
- return null;
58
+ throw new Error(
59
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
60
+ );
58
61
  }
62
+ const metadata = node.metadata ?? {};
59
63
  return {
60
64
  topicId: scopeKey,
61
65
  projectId: asMappedProjectId(node),
62
- source: "topic_node"
66
+ source: "topic_node",
67
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
68
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
63
69
  };
64
70
  }
71
+ function canonicalTopicGlobalId(node) {
72
+ const globalId = normalizeScopeValue(node.globalId);
73
+ if (globalId && isUuidV7(globalId)) {
74
+ return globalId;
75
+ }
76
+ const topicId = normalizeScopeValue(node.topicId);
77
+ return topicId && isUuidV7(topicId) ? topicId : null;
78
+ }
79
+ function requireUuidV7TopicScope(field, value) {
80
+ if (!isUuidV7(value)) {
81
+ throw new Error(
82
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
83
+ );
84
+ }
85
+ }
65
86
  function asMappedProjectId(topic) {
66
87
  if (!topic) {
67
88
  return;
@@ -83,200 +104,25 @@ function normalizeScopeValue(value) {
83
104
  const normalized = value.trim();
84
105
  return normalized.length > 0 ? normalized : void 0;
85
106
  }
86
- function pickPrimaryTopic(candidates) {
87
- return [...candidates].sort((a, b) => {
88
- const depthA = a.depth ?? 9999;
89
- const depthB = b.depth ?? 9999;
90
- if (depthA !== depthB) {
91
- return depthA - depthB;
92
- }
93
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
94
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
95
- if (createdA !== createdB) {
96
- return createdA - createdB;
97
- }
98
- return String(a.name || "").localeCompare(String(b.name || ""));
99
- })[0];
100
- }
101
- async function findTopicsByScopeAlias(ctx, scopeId) {
102
- const query2 = ctx.db.query("topics");
103
- try {
104
- return await query2.withIndex(
105
- "by_graph_scope_project",
106
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
107
- ).collect();
108
- } catch (error) {
109
- debugGraphPrimitiveFallback(
110
- "[topicScope] Failed to resolve scope alias via index",
111
- {
112
- error,
113
- scopeId
114
- }
115
- );
116
- const topics = await query2.collect();
117
- return topics.filter((topic) => {
118
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
119
- const mappedProjectId = asMappedProjectId(topic);
120
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
121
- });
122
- }
123
- }
124
- async function tryResolveHostTopicById(ctx, topicId) {
125
- if (typeof ctx.runQuery !== "function") {
126
- return null;
127
- }
128
- try {
129
- return await ctx.runQuery(api.topics.get, {
130
- id: topicId
131
- }) ?? null;
132
- } catch (error) {
133
- debugGraphPrimitiveFallback(
134
- "[topicScope] Failed to resolve topic by host query",
135
- {
136
- error,
137
- topicId
138
- }
139
- );
140
- return null;
141
- }
142
- }
143
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
144
- if (typeof ctx.runQuery !== "function") {
145
- return null;
146
- }
147
- try {
148
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
149
- projectId: legacyScopeId
150
- }) ?? null;
151
- } catch (error) {
152
- debugGraphPrimitiveFallback(
153
- "[topicScope] Failed to resolve topic by legacy scope",
154
- {
155
- error,
156
- legacyScopeId
157
- }
158
- );
159
- return null;
160
- }
161
- }
162
- async function resolveInheritedWorkspaceScope(ctx, topic) {
163
- const MAX_DEPTH = 10;
164
- let tenantId = normalizeScopeValue(topic.tenantId);
165
- let workspaceId = normalizeScopeValue(topic.workspaceId);
166
- if (tenantId && workspaceId) {
167
- return { tenantId, workspaceId };
168
- }
169
- let current = topic;
170
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
171
- current = await ctx.db.get(current.parentTopicId);
172
- if (!current) {
173
- break;
174
- }
175
- if (!tenantId) {
176
- tenantId = normalizeScopeValue(current.tenantId);
177
- }
178
- if (!workspaceId) {
179
- workspaceId = normalizeScopeValue(current.workspaceId);
180
- }
181
- if (tenantId && workspaceId) {
182
- break;
183
- }
184
- }
185
- return { tenantId, workspaceId };
186
- }
187
107
  async function resolveTopicProjectScope(ctx, args) {
188
108
  if (args.topicId) {
189
109
  return await resolveScopeFromTopicId(ctx, args.topicId);
190
110
  }
191
111
  if (args.projectId) {
192
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
112
+ throw new Error(
113
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
114
+ );
193
115
  }
194
- throw new Error(
195
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
196
- );
116
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
197
117
  }
198
118
  async function resolveScopeFromTopicId(ctx, topicId) {
199
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
200
- if (topic) {
201
- return await buildTopicScope(ctx, topic, "topic");
202
- }
203
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
119
+ const topicGlobalId = String(topicId);
120
+ requireUuidV7TopicScope("topicId", topicGlobalId);
121
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
204
122
  if (nodeScope) {
205
123
  return nodeScope;
206
124
  }
207
- throw new Error(`Topic not found: ${String(topicId)}`);
208
- }
209
- async function resolveTopicDocFromTopicId(ctx, topicId) {
210
- const direct = await tryReadTopicDoc(ctx, topicId, {
211
- failureLog: "[topicScope] Failed to load topic by direct id",
212
- idLogKey: "topicId"
213
- });
214
- if (direct) {
215
- return direct;
216
- }
217
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
218
- if (hostTopic) {
219
- return hostTopic;
220
- }
221
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
222
- }
223
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
224
- const directTopic = await resolveDirectLegacyProjectTopic(
225
- ctx,
226
- legacyProjectId
227
- );
228
- if (directTopic) {
229
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
230
- fallbackProjectId: legacyProjectId
231
- });
232
- }
233
- const primary = pickPrimaryTopic(
234
- await findTopicsByScopeAlias(ctx, legacyProjectId)
235
- );
236
- if (primary) {
237
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
238
- fallbackProjectId: legacyProjectId
239
- });
240
- }
241
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
242
- if (nodeScope) {
243
- return {
244
- ...nodeScope,
245
- projectId: nodeScope.projectId ?? legacyProjectId
246
- };
247
- }
248
- throw new Error(
249
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
250
- );
251
- }
252
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
253
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
254
- failureLog: "[topicScope] Failed to load direct project topic",
255
- idLogKey: "projectId"
256
- });
257
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
258
- }
259
- async function tryReadTopicDoc(ctx, id, log) {
260
- try {
261
- return await ctx.db.get(id);
262
- } catch (error) {
263
- debugGraphPrimitiveFallback(log.failureLog, {
264
- error,
265
- [log.idLogKey]: id
266
- });
267
- return null;
268
- }
269
- }
270
- async function buildTopicScope(ctx, topic, source, options = {}) {
271
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
272
- const mapped = asMappedProjectId(topic);
273
- return {
274
- topicId: topic._id,
275
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
276
- tenantId: inherited.tenantId,
277
- workspaceId: inherited.workspaceId,
278
- source
279
- };
125
+ throw new Error(`Topic not found: ${topicGlobalId}`);
280
126
  }
281
127
  var optionalScopeArgs = {
282
128
  projectId: v.optional(v.string()),