@lucern/graph-primitives 1.0.48 → 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 +2 -2
  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 } from 'convex/server';
6
- import { assertUuidV7Identity } from '@lucern/contracts/ids';
6
+ import { 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';
@@ -12,7 +12,6 @@ import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helper
12
12
  var unsafeApi = unsafeConvexAnyApi(
13
13
  "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
14
14
  );
15
- var api = unsafeApi;
16
15
  componentsGeneric();
17
16
  var internal = unsafeApi;
18
17
  var mutation = mutationGeneric;
@@ -30,6 +29,9 @@ function debugGraphPrimitiveFallback(message, context) {
30
29
  }
31
30
  function insertEpistemicNode(ctx, doc) {
32
31
  assertUuidV7Identity("epistemicNodes", doc.globalId);
32
+ if (doc.topicId !== void 0 && doc.topicId !== null) {
33
+ assertUuidV7Reference("epistemicNodes.topicId", doc.topicId);
34
+ }
33
35
  return ctx.db.insert("epistemicNodes", doc);
34
36
  }
35
37
 
@@ -400,16 +402,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
400
402
  if (!node) {
401
403
  return null;
402
404
  }
403
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
405
+ const scopeKey = canonicalTopicGlobalId(node);
404
406
  if (!scopeKey) {
405
- return null;
407
+ throw new Error(
408
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
409
+ );
406
410
  }
411
+ const metadata = node.metadata ?? {};
407
412
  return {
408
413
  topicId: scopeKey,
409
414
  projectId: asMappedProjectId(node),
410
- source: "topic_node"
415
+ source: "topic_node",
416
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
417
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
411
418
  };
412
419
  }
420
+ function canonicalTopicGlobalId(node) {
421
+ const globalId = normalizeScopeValue(node.globalId);
422
+ if (globalId && isUuidV7(globalId)) {
423
+ return globalId;
424
+ }
425
+ const topicId = normalizeScopeValue(node.topicId);
426
+ return topicId && isUuidV7(topicId) ? topicId : null;
427
+ }
428
+ function requireUuidV7TopicScope(field, value) {
429
+ if (!isUuidV7(value)) {
430
+ throw new Error(
431
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
432
+ );
433
+ }
434
+ }
413
435
  function asMappedProjectId(topic) {
414
436
  if (!topic) {
415
437
  return;
@@ -431,200 +453,25 @@ function normalizeScopeValue(value) {
431
453
  const normalized = value.trim();
432
454
  return normalized.length > 0 ? normalized : void 0;
433
455
  }
434
- function pickPrimaryTopic(candidates) {
435
- return [...candidates].sort((a, b) => {
436
- const depthA = a.depth ?? 9999;
437
- const depthB = b.depth ?? 9999;
438
- if (depthA !== depthB) {
439
- return depthA - depthB;
440
- }
441
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
442
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
443
- if (createdA !== createdB) {
444
- return createdA - createdB;
445
- }
446
- return String(a.name || "").localeCompare(String(b.name || ""));
447
- })[0];
448
- }
449
- async function findTopicsByScopeAlias(ctx, scopeId) {
450
- const query = ctx.db.query("topics");
451
- try {
452
- return await query.withIndex(
453
- "by_graph_scope_project",
454
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
455
- ).collect();
456
- } catch (error) {
457
- debugGraphPrimitiveFallback(
458
- "[topicScope] Failed to resolve scope alias via index",
459
- {
460
- error,
461
- scopeId
462
- }
463
- );
464
- const topics = await query.collect();
465
- return topics.filter((topic) => {
466
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
467
- const mappedProjectId = asMappedProjectId(topic);
468
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
469
- });
470
- }
471
- }
472
- async function tryResolveHostTopicById(ctx, topicId) {
473
- if (typeof ctx.runQuery !== "function") {
474
- return null;
475
- }
476
- try {
477
- return await ctx.runQuery(api.topics.get, {
478
- id: topicId
479
- }) ?? null;
480
- } catch (error) {
481
- debugGraphPrimitiveFallback(
482
- "[topicScope] Failed to resolve topic by host query",
483
- {
484
- error,
485
- topicId
486
- }
487
- );
488
- return null;
489
- }
490
- }
491
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
492
- if (typeof ctx.runQuery !== "function") {
493
- return null;
494
- }
495
- try {
496
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
497
- projectId: legacyScopeId
498
- }) ?? null;
499
- } catch (error) {
500
- debugGraphPrimitiveFallback(
501
- "[topicScope] Failed to resolve topic by legacy scope",
502
- {
503
- error,
504
- legacyScopeId
505
- }
506
- );
507
- return null;
508
- }
509
- }
510
- async function resolveInheritedWorkspaceScope(ctx, topic) {
511
- const MAX_DEPTH = 10;
512
- let tenantId = normalizeScopeValue(topic.tenantId);
513
- let workspaceId = normalizeScopeValue(topic.workspaceId);
514
- if (tenantId && workspaceId) {
515
- return { tenantId, workspaceId };
516
- }
517
- let current = topic;
518
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
519
- current = await ctx.db.get(current.parentTopicId);
520
- if (!current) {
521
- break;
522
- }
523
- if (!tenantId) {
524
- tenantId = normalizeScopeValue(current.tenantId);
525
- }
526
- if (!workspaceId) {
527
- workspaceId = normalizeScopeValue(current.workspaceId);
528
- }
529
- if (tenantId && workspaceId) {
530
- break;
531
- }
532
- }
533
- return { tenantId, workspaceId };
534
- }
535
456
  async function resolveTopicProjectScope(ctx, args) {
536
457
  if (args.topicId) {
537
458
  return await resolveScopeFromTopicId(ctx, args.topicId);
538
459
  }
539
460
  if (args.projectId) {
540
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
461
+ throw new Error(
462
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
463
+ );
541
464
  }
542
- throw new Error(
543
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
544
- );
465
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
545
466
  }
546
467
  async function resolveScopeFromTopicId(ctx, topicId) {
547
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
548
- if (topic) {
549
- return await buildTopicScope(ctx, topic, "topic");
550
- }
551
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
468
+ const topicGlobalId = String(topicId);
469
+ requireUuidV7TopicScope("topicId", topicGlobalId);
470
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
552
471
  if (nodeScope) {
553
472
  return nodeScope;
554
473
  }
555
- throw new Error(`Topic not found: ${String(topicId)}`);
556
- }
557
- async function resolveTopicDocFromTopicId(ctx, topicId) {
558
- const direct = await tryReadTopicDoc(ctx, topicId, {
559
- failureLog: "[topicScope] Failed to load topic by direct id",
560
- idLogKey: "topicId"
561
- });
562
- if (direct) {
563
- return direct;
564
- }
565
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
566
- if (hostTopic) {
567
- return hostTopic;
568
- }
569
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
570
- }
571
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
572
- const directTopic = await resolveDirectLegacyProjectTopic(
573
- ctx,
574
- legacyProjectId
575
- );
576
- if (directTopic) {
577
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
578
- fallbackProjectId: legacyProjectId
579
- });
580
- }
581
- const primary = pickPrimaryTopic(
582
- await findTopicsByScopeAlias(ctx, legacyProjectId)
583
- );
584
- if (primary) {
585
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
586
- fallbackProjectId: legacyProjectId
587
- });
588
- }
589
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
590
- if (nodeScope) {
591
- return {
592
- ...nodeScope,
593
- projectId: nodeScope.projectId ?? legacyProjectId
594
- };
595
- }
596
- throw new Error(
597
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
598
- );
599
- }
600
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
601
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
602
- failureLog: "[topicScope] Failed to load direct project topic",
603
- idLogKey: "projectId"
604
- });
605
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
606
- }
607
- async function tryReadTopicDoc(ctx, id, log) {
608
- try {
609
- return await ctx.db.get(id);
610
- } catch (error) {
611
- debugGraphPrimitiveFallback(log.failureLog, {
612
- error,
613
- [log.idLogKey]: id
614
- });
615
- return null;
616
- }
617
- }
618
- async function buildTopicScope(ctx, topic, source, options = {}) {
619
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
620
- const mapped = asMappedProjectId(topic);
621
- return {
622
- topicId: topic._id,
623
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
624
- tenantId: inherited.tenantId,
625
- workspaceId: inherited.workspaceId,
626
- source
627
- };
474
+ throw new Error(`Topic not found: ${topicGlobalId}`);
628
475
  }
629
476
  var optionalScopeArgs = {
630
477
  projectId: v.optional(v.string()),
@@ -3,12 +3,12 @@ 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, queryGeneric } from 'convex/server';
6
+ import { isUuidV7 } from '@lucern/contracts/ids';
6
7
 
7
8
  // src/epistemicNodes.queries.ts
8
- var unsafeApi = unsafeConvexAnyApi(
9
+ unsafeConvexAnyApi(
9
10
  "graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
10
11
  );
11
- var api = unsafeApi;
12
12
  componentsGeneric();
13
13
  var query = queryGeneric;
14
14
 
@@ -170,6 +170,8 @@ v.union(
170
170
  v.literal("contradicted"),
171
171
  v.literal("outdated")
172
172
  );
173
+
174
+ // src/topicScope.ts
173
175
  var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
174
176
  async function resolveTopicNodeScopeOrNull(ctx, ref) {
175
177
  if (!ctx?.db || typeof ctx.db.query !== "function") {
@@ -190,16 +192,36 @@ async function resolveTopicNodeScopeOrNull(ctx, ref) {
190
192
  if (!node) {
191
193
  return null;
192
194
  }
193
- const scopeKey = normalizeScopeValue(node.topicId) ?? normalizeScopeValue(node.globalId);
195
+ const scopeKey = canonicalTopicGlobalId(node);
194
196
  if (!scopeKey) {
195
- return null;
197
+ throw new Error(
198
+ `topic.uuidv7_identity_required: topic node ${ref} is missing a UUIDv7 globalId.`
199
+ );
196
200
  }
201
+ const metadata = node.metadata ?? {};
197
202
  return {
198
203
  topicId: scopeKey,
199
204
  projectId: asMappedProjectId(node),
200
- source: "topic_node"
205
+ source: "topic_node",
206
+ tenantId: normalizeScopeValue(node.tenantId) ?? normalizeScopeValue(metadata.tenantId),
207
+ workspaceId: normalizeScopeValue(node.workspaceId) ?? normalizeScopeValue(metadata.workspaceId)
201
208
  };
202
209
  }
210
+ function canonicalTopicGlobalId(node) {
211
+ const globalId = normalizeScopeValue(node.globalId);
212
+ if (globalId && isUuidV7(globalId)) {
213
+ return globalId;
214
+ }
215
+ const topicId = normalizeScopeValue(node.topicId);
216
+ return topicId && isUuidV7(topicId) ? topicId : null;
217
+ }
218
+ function requireUuidV7TopicScope(field, value) {
219
+ if (!isUuidV7(value)) {
220
+ throw new Error(
221
+ `topic.uuidv7_required: ${field} must be a UUIDv7 public topic identifier.`
222
+ );
223
+ }
224
+ }
203
225
  function asMappedProjectId(topic) {
204
226
  if (!topic) {
205
227
  return;
@@ -221,200 +243,25 @@ function normalizeScopeValue(value) {
221
243
  const normalized = value.trim();
222
244
  return normalized.length > 0 ? normalized : void 0;
223
245
  }
224
- function pickPrimaryTopic(candidates) {
225
- return [...candidates].sort((a, b) => {
226
- const depthA = a.depth ?? 9999;
227
- const depthB = b.depth ?? 9999;
228
- if (depthA !== depthB) {
229
- return depthA - depthB;
230
- }
231
- const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
232
- const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
233
- if (createdA !== createdB) {
234
- return createdA - createdB;
235
- }
236
- return String(a.name || "").localeCompare(String(b.name || ""));
237
- })[0];
238
- }
239
- async function findTopicsByScopeAlias(ctx, scopeId) {
240
- const query2 = ctx.db.query("topics");
241
- try {
242
- return await query2.withIndex(
243
- "by_graph_scope_project",
244
- (q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
245
- ).collect();
246
- } catch (error) {
247
- debugGraphPrimitiveFallback(
248
- "[topicScope] Failed to resolve scope alias via index",
249
- {
250
- error,
251
- scopeId
252
- }
253
- );
254
- const topics = await query2.collect();
255
- return topics.filter((topic) => {
256
- const normalizedGlobalId = normalizeScopeValue(topic.globalId);
257
- const mappedProjectId = asMappedProjectId(topic);
258
- return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
259
- });
260
- }
261
- }
262
- async function tryResolveHostTopicById(ctx, topicId) {
263
- if (typeof ctx.runQuery !== "function") {
264
- return null;
265
- }
266
- try {
267
- return await ctx.runQuery(api.topics.get, {
268
- id: topicId
269
- }) ?? null;
270
- } catch (error) {
271
- debugGraphPrimitiveFallback(
272
- "[topicScope] Failed to resolve topic by host query",
273
- {
274
- error,
275
- topicId
276
- }
277
- );
278
- return null;
279
- }
280
- }
281
- async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
282
- if (typeof ctx.runQuery !== "function") {
283
- return null;
284
- }
285
- try {
286
- return await ctx.runQuery(api.topics.getByLegacyScopeId, {
287
- projectId: legacyScopeId
288
- }) ?? null;
289
- } catch (error) {
290
- debugGraphPrimitiveFallback(
291
- "[topicScope] Failed to resolve topic by legacy scope",
292
- {
293
- error,
294
- legacyScopeId
295
- }
296
- );
297
- return null;
298
- }
299
- }
300
- async function resolveInheritedWorkspaceScope(ctx, topic) {
301
- const MAX_DEPTH = 10;
302
- let tenantId = normalizeScopeValue(topic.tenantId);
303
- let workspaceId = normalizeScopeValue(topic.workspaceId);
304
- if (tenantId && workspaceId) {
305
- return { tenantId, workspaceId };
306
- }
307
- let current = topic;
308
- for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
309
- current = await ctx.db.get(current.parentTopicId);
310
- if (!current) {
311
- break;
312
- }
313
- if (!tenantId) {
314
- tenantId = normalizeScopeValue(current.tenantId);
315
- }
316
- if (!workspaceId) {
317
- workspaceId = normalizeScopeValue(current.workspaceId);
318
- }
319
- if (tenantId && workspaceId) {
320
- break;
321
- }
322
- }
323
- return { tenantId, workspaceId };
324
- }
325
246
  async function resolveTopicProjectScope(ctx, args) {
326
247
  if (args.topicId) {
327
248
  return await resolveScopeFromTopicId(ctx, args.topicId);
328
249
  }
329
250
  if (args.projectId) {
330
- return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
251
+ throw new Error(
252
+ "topic.uuidv7_required: projectId scope aliases are retired; pass topicId as the UUIDv7 topic globalId."
253
+ );
331
254
  }
332
- throw new Error(
333
- "Missing scope: provide topicId (preferred) or legacy projectId alias."
334
- );
255
+ throw new Error("topic.uuidv7_required: Missing scope: provide topicId.");
335
256
  }
336
257
  async function resolveScopeFromTopicId(ctx, topicId) {
337
- const topic = await resolveTopicDocFromTopicId(ctx, topicId);
338
- if (topic) {
339
- return await buildTopicScope(ctx, topic, "topic");
340
- }
341
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
258
+ const topicGlobalId = String(topicId);
259
+ requireUuidV7TopicScope("topicId", topicGlobalId);
260
+ const nodeScope = await resolveTopicNodeScopeOrNull(ctx, topicGlobalId);
342
261
  if (nodeScope) {
343
262
  return nodeScope;
344
263
  }
345
- throw new Error(`Topic not found: ${String(topicId)}`);
346
- }
347
- async function resolveTopicDocFromTopicId(ctx, topicId) {
348
- const direct = await tryReadTopicDoc(ctx, topicId, {
349
- failureLog: "[topicScope] Failed to load topic by direct id",
350
- idLogKey: "topicId"
351
- });
352
- if (direct) {
353
- return direct;
354
- }
355
- const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
356
- if (hostTopic) {
357
- return hostTopic;
358
- }
359
- return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
360
- }
361
- async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
362
- const directTopic = await resolveDirectLegacyProjectTopic(
363
- ctx,
364
- legacyProjectId
365
- );
366
- if (directTopic) {
367
- return await buildTopicScope(ctx, directTopic, "topic_inferred", {
368
- fallbackProjectId: legacyProjectId
369
- });
370
- }
371
- const primary = pickPrimaryTopic(
372
- await findTopicsByScopeAlias(ctx, legacyProjectId)
373
- );
374
- if (primary) {
375
- return await buildTopicScope(ctx, primary, "project_mapped_topic", {
376
- fallbackProjectId: legacyProjectId
377
- });
378
- }
379
- const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
380
- if (nodeScope) {
381
- return {
382
- ...nodeScope,
383
- projectId: nodeScope.projectId ?? legacyProjectId
384
- };
385
- }
386
- throw new Error(
387
- `Legacy project scope ${legacyProjectId} has no mapped topic.`
388
- );
389
- }
390
- async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
391
- const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
392
- failureLog: "[topicScope] Failed to load direct project topic",
393
- idLogKey: "projectId"
394
- });
395
- return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
396
- }
397
- async function tryReadTopicDoc(ctx, id, log) {
398
- try {
399
- return await ctx.db.get(id);
400
- } catch (error) {
401
- debugGraphPrimitiveFallback(log.failureLog, {
402
- error,
403
- [log.idLogKey]: id
404
- });
405
- return null;
406
- }
407
- }
408
- async function buildTopicScope(ctx, topic, source, options = {}) {
409
- const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
410
- const mapped = asMappedProjectId(topic);
411
- return {
412
- topicId: topic._id,
413
- ...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
414
- tenantId: inherited.tenantId,
415
- workspaceId: inherited.workspaceId,
416
- source
417
- };
264
+ throw new Error(`Topic not found: ${topicGlobalId}`);
418
265
  }
419
266
  var optionalScopeArgs = {
420
267
  projectId: v.optional(v.string()),