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