@lucern/graph-primitives 1.0.50 → 1.0.53

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
@@ -3,7 +3,7 @@ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
3
3
  import { v } from 'convex/values';
4
4
  import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
5
  import { componentsGeneric, mutationGeneric, queryGeneric, internalMutationGeneric, internalQueryGeneric } from 'convex/server';
6
- import { generateGlobalId, assertUuidV7Identity } from '@lucern/contracts/ids';
6
+ import { generateGlobalId, assertUuidV7Identity, assertUuidV7Reference, isUuidV7 } from '@lucern/contracts/ids';
7
7
  import '@lucern/contracts/manifests/edge-policy-manifest';
8
8
  import '@lucern/contracts/manifests/edge-policy-manifest.data';
9
9
  import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
@@ -428,6 +428,8 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
428
428
  ...resolverOverrides
429
429
  };
430
430
  }
431
+
432
+ // src/topicScope.ts
431
433
  var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
432
434
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
433
435
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -448,16 +450,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
448
450
  if (!node) {
449
451
  return null;
450
452
  }
451
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
453
+ const scopeKey = canonicalTopicGlobalId(node);
452
454
  if (!scopeKey) {
453
- return null;
455
+ throw new Error(
456
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
457
+ );
454
458
  }
459
+ const metadata = node.metadata ?? {};
455
460
  return {
456
461
  topicId: scopeKey,
457
462
  projectId: asMappedProjectId(node),
458
- source: "topic_node"
463
+ source: "topic_node",
464
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
465
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
459
466
  };
460
467
  }
468
+ function canonicalTopicGlobalId(node) {
469
+ const globalId = normalizeScopeValue(node.globalId);
470
+ if (globalId && isUuidV7(globalId)) {
471
+ return globalId;
472
+ }
473
+ const topicId = normalizeScopeValue(node.topicId);
474
+ return topicId && isUuidV7(topicId) ? topicId : null;
475
+ }
476
+ function requireUuidV7TopicScope(field, value) {
477
+ if (!isUuidV7(value)) {
478
+ throw new Error(
479
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
480
+ );
481
+ }
482
+ }
461
483
  function asMappedProjectId(topic) {
462
484
  if (!topic) {
463
485
  return;
@@ -479,200 +501,25 @@ function normalizeScopeValue(value) {
479
501
  const normalized = value.trim();
480
502
  return normalized.length > 0 ? normalized : void 0;
481
503
  }
482
- function pickPrimaryTopic(candidates) {
483
- return [...candidates].sort((a, b) => {
484
- const depthA = a.depth ?? 9999;
485
- const depthB = b.depth ?? 9999;
486
- if (depthA !== depthB) {
487
- return depthA - depthB;
488
- }
489
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
490
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
491
- if (createdA !== createdB) {
492
- return createdA - createdB;
493
- }
494
- return String(a.name || "").localeCompare(String(b.name || ""));
495
- })[0];
496
- }
497
- async function findTopicsByScopeAlias(ctx, scopeId) {
498
- const query2 = ctx.db.query("topics");
499
- try {
500
- return await query2.withIndex(
501
- "by_graph_scope_project",
502
- (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
503
- ).collect();
504
- } catch (error) {
505
- debugGraphPrimitiveFallback(
506
- "[topicScope] Failed to resolve scope alias via index",
507
- {
508
- error,
509
- scopeId
510
- }
511
- );
512
- const topics = await query2.collect();
513
- return topics.filter((topic) => {
514
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
515
- const mappedProjectId = asMappedProjectId(topic);
516
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
517
- });
518
- }
519
- }
520
- async function tryResolveHostTopicById(ctx, topicId) {
521
- if (typeof ctx.runQuery !== "function") {
522
- return null;
523
- }
524
- try {
525
- return await ctx.runQuery(api.topics.get, {
526
- id: topicId
527
- }) ?? null;
528
- } catch (error) {
529
- debugGraphPrimitiveFallback(
530
- "[topicScope] Failed to resolve topic by host query",
531
- {
532
- error,
533
- topicId
534
- }
535
- );
536
- return null;
537
- }
538
- }
539
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
540
- if (typeof ctx.runQuery !== "function") {
541
- return null;
542
- }
543
- try {
544
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
545
- projectId: legacyScopeId
546
- }) ?? null;
547
- } catch (error) {
548
- debugGraphPrimitiveFallback(
549
- "[topicScope] Failed to resolve topic by legacy scope",
550
- {
551
- error,
552
- legacyScopeId
553
- }
554
- );
555
- return null;
556
- }
557
- }
558
- async function resolveInheritedWorkspaceScope(ctx, topic) {
559
- const MAX_DEPTH = 10;
560
- let tenantId = normalizeScopeValue(topic.tenantId);
561
- let workspaceId = normalizeScopeValue(topic.workspaceId);
562
- if (tenantId && workspaceId) {
563
- return { tenantId, workspaceId };
564
- }
565
- let current = topic;
566
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
567
- current = await ctx.db.get(current.parentTopicId);
568
- if (!current) {
569
- break;
570
- }
571
- if (!tenantId) {
572
- tenantId = normalizeScopeValue(current.tenantId);
573
- }
574
- if (!workspaceId) {
575
- workspaceId = normalizeScopeValue(current.workspaceId);
576
- }
577
- if (tenantId && workspaceId) {
578
- break;
579
- }
580
- }
581
- return { tenantId, workspaceId };
582
- }
583
504
  async function resolveTopicProjectScope(ctx, args) {
584
505
  if (args.topicId) {
585
506
  return await resolveScopeFromTopicId(ctx, args.topicId);
586
507
  }
587
508
  if (args.projectId) {
588
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
509
+ throw new Error(
510
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
511
+ );
589
512
  }
590
- throw new Error(
591
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
592
- );
513
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
593
514
  }
594
515
  async function resolveScopeFromTopicId(ctx, topicId) {
595
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
596
- if (topic) {
597
- return await buildTopicScope(ctx, topic, "topic");
598
- }
599
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
516
+ const topicGlobalId = String(topicId);
517
+ requireUuidV7TopicScope("topicId", topicGlobalId);
518
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
600
519
  if (nodeScope) {
601
520
  return nodeScope;
602
521
  }
603
- throw new Error(`Topic not found: ${String(topicId)}`);
604
- }
605
- async function resolveTopicDocFromTopicId(ctx, topicId) {
606
- const direct = await tryReadTopicDoc(ctx, topicId, {
607
- failureLog: "[topicScope] Failed to load topic by direct id",
608
- idLogKey: "topicId"
609
- });
610
- if (direct) {
611
- return direct;
612
- }
613
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
614
- if (hostTopic) {
615
- return hostTopic;
616
- }
617
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
618
- }
619
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
620
- const directTopic = await resolveDirectLegacyProjectTopic(
621
- ctx,
622
- legacyProjectId
623
- );
624
- if (directTopic) {
625
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
626
- fallbackProjectId: legacyProjectId
627
- });
628
- }
629
- const primary = pickPrimaryTopic(
630
- await findTopicsByScopeAlias(ctx, legacyProjectId)
631
- );
632
- if (primary) {
633
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
634
- fallbackProjectId: legacyProjectId
635
- });
636
- }
637
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
638
- if (nodeScope) {
639
- return {
640
- ...nodeScope,
641
- projectId: nodeScope.projectId ?? legacyProjectId
642
- };
643
- }
644
- throw new Error(
645
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
646
- );
647
- }
648
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
649
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
650
- failureLog: "[topicScope] Failed to load direct project topic",
651
- idLogKey: "projectId"
652
- });
653
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
654
- }
655
- async function tryReadTopicDoc(ctx, id, log) {
656
- try {
657
- return await ctx.db.get(id);
658
- } catch (error) {
659
- debugGraphPrimitiveFallback(log.failureLog, {
660
- error,
661
- [log.idLogKey]: id
662
- });
663
- return null;
664
- }
665
- }
666
- async function buildTopicScope(ctx, topic, source, options = {}) {
667
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
668
- const mapped = asMappedProjectId(topic);
669
- return {
670
- topicId: topic._id,
671
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
672
- tenantId: inherited.tenantId,
673
- workspaceId: inherited.workspaceId,
674
- source
675
- };
522
+ throw new Error(`Topic not found: ${topicGlobalId}`);
676
523
  }
677
524
  var optionalScopeArgs = {
678
525
  projectId: v.optional(v.string()),
@@ -1770,6 +1617,9 @@ async function scheduleEmbeddingGeneration(args) {
1770
1617
  }
1771
1618
  function insertEpistemicNode(ctx, doc) {
1772
1619
  assertUuidV7Identity("epistemicNodes", doc.globalId);
1620
+ if (doc.topicId !== void 0 && doc.topicId !== null) {
1621
+ assertUuidV7Reference("epistemicNodes.topicId", doc.topicId);
1622
+ }
1773
1623
  return ctx.db.insert("epistemicNodes", doc);
1774
1624
  }
1775
1625
  function normalizeScopeValue2(value) {
@@ -3,6 +3,7 @@ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
3
3
  import { v } from 'convex/values';
4
4
  import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
5
5
  import { componentsGeneric, mutationGeneric } from 'convex/server';
6
+ import { isUuidV7 } from '@lucern/contracts/ids';
6
7
  import '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
7
8
 
8
9
  // src/epistemicQuestions.lifecycle.ts
@@ -400,6 +401,8 @@ function resolveGraphPrimitivesAppResolvers(_ctx) {
400
401
  ...resolverOverrides
401
402
  };
402
403
  }
404
+
405
+ // src/topicScope.ts
403
406
  var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
404
407
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
405
408
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -420,16 +423,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
420
423
  if (!node) {
421
424
  return null;
422
425
  }
423
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
426
+ const scopeKey = canonicalTopicGlobalId(node);
424
427
  if (!scopeKey) {
425
- return null;
428
+ throw new Error(
429
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
430
+ );
426
431
  }
432
+ const metadata = node.metadata ?? {};
427
433
  return {
428
434
  topicId: scopeKey,
429
435
  projectId: asMappedProjectId(node),
430
- source: "topic_node"
436
+ source: "topic_node",
437
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
438
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
431
439
  };
432
440
  }
441
+ function canonicalTopicGlobalId(node) {
442
+ const globalId = normalizeScopeValue(node.globalId);
443
+ if (globalId && isUuidV7(globalId)) {
444
+ return globalId;
445
+ }
446
+ const topicId = normalizeScopeValue(node.topicId);
447
+ return topicId && isUuidV7(topicId) ? topicId : null;
448
+ }
449
+ function requireUuidV7TopicScope(field, value) {
450
+ if (!isUuidV7(value)) {
451
+ throw new Error(
452
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
453
+ );
454
+ }
455
+ }
433
456
  function asMappedProjectId(topic) {
434
457
  if (!topic) {
435
458
  return;
@@ -451,200 +474,25 @@ function normalizeScopeValue(value) {
451
474
  const normalized = value.trim();
452
475
  return normalized.length > 0 ? normalized : void 0;
453
476
  }
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
- }
529
- }
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 };
536
- }
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
- }
552
- }
553
- return { tenantId, workspaceId };
554
- }
555
477
  async function resolveTopicProjectScope(ctx, args) {
556
478
  if (args.topicId) {
557
479
  return await resolveScopeFromTopicId(ctx, args.topicId);
558
480
  }
559
481
  if (args.projectId) {
560
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
482
+ throw new Error(
483
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
484
+ );
561
485
  }
562
- throw new Error(
563
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
564
- );
486
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
565
487
  }
566
488
  async function resolveScopeFromTopicId(ctx, topicId) {
567
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
568
- if (topic) {
569
- return await buildTopicScope(ctx, topic, "topic");
570
- }
571
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
489
+ const topicGlobalId = String(topicId);
490
+ requireUuidV7TopicScope("topicId", topicGlobalId);
491
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
572
492
  if (nodeScope) {
573
493
  return nodeScope;
574
494
  }
575
- throw new Error(`Topic not found: ${String(topicId)}`);
576
- }
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;
590
- }
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
- });
600
- }
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
- });
608
- }
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
- );
619
- }
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);
626
- }
627
- async function tryReadTopicDoc(ctx, id, log) {
628
- try {
629
- return await ctx.db.get(id);
630
- } catch (error) {
631
- debugGraphPrimitiveFallback(log.failureLog, {
632
- error,
633
- [log.idLogKey]: id
634
- });
635
- return null;
636
- }
637
- }
638
- async function buildTopicScope(ctx, topic, source, options = {}) {
639
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
640
- const mapped = asMappedProjectId(topic);
641
- return {
642
- topicId: topic._id,
643
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
644
- tenantId: inherited.tenantId,
645
- workspaceId: inherited.workspaceId,
646
- source
647
- };
495
+ throw new Error(`Topic not found: ${topicGlobalId}`);
648
496
  }
649
497
  ({
650
498
  projectId: v.optional(v.string()),