@lucern/graph-primitives 0.3.0-alpha.0 → 0.3.0-alpha.10

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 (141) hide show
  1. package/dist/{beliefDecay-Q_26RTc-.d.ts → beliefDecay-DZ6tkLYq.d.ts} +1 -1
  2. package/dist/beliefDecay.d.ts +1 -1
  3. package/dist/beliefDecay.js +188 -1144
  4. package/dist/beliefDecay.js.map +1 -1
  5. package/dist/{beliefEvidenceLinks-42FlR48t.d.ts → beliefEvidenceLinks-CWOXxxJg.d.ts} +1 -1
  6. package/dist/beliefEvidenceLinks.d.ts +1 -1
  7. package/dist/beliefEvidenceLinks.js +186 -871
  8. package/dist/beliefEvidenceLinks.js.map +1 -1
  9. package/dist/{beliefLifecycle-C-AehZgF.d.ts → beliefLifecycle-y8WLXqQj.d.ts} +1 -1
  10. package/dist/beliefLifecycle.d.ts +1 -1
  11. package/dist/confidencePropagationDispatch.d.ts +4 -4
  12. package/dist/confidencePropagationDispatch.js +31 -311
  13. package/dist/confidencePropagationDispatch.js.map +1 -1
  14. package/dist/{contradictions-Hdwl7zid.d.ts → contradictions-51VLsESq.d.ts} +1 -1
  15. package/dist/contradictions.d.ts +1 -1
  16. package/dist/contradictions.js +67 -800
  17. package/dist/contradictions.js.map +1 -1
  18. package/dist/debug.d.ts +4 -0
  19. package/dist/debug.js +34 -0
  20. package/dist/debug.js.map +1 -0
  21. package/dist/edges/contradicts.js +1 -122
  22. package/dist/edges/contradicts.js.map +1 -1
  23. package/dist/edges/dependsOn.js +14 -172
  24. package/dist/edges/dependsOn.js.map +1 -1
  25. package/dist/edges/elaborates.js +1 -49
  26. package/dist/edges/elaborates.js.map +1 -1
  27. package/dist/edges/index.js +15 -280
  28. package/dist/edges/index.js.map +1 -1
  29. package/dist/edges/informs.js +2 -65
  30. package/dist/edges/informs.js.map +1 -1
  31. package/dist/edges/propagationTypes.d.ts +2 -2
  32. package/dist/edges/propagationTypes.js.map +1 -1
  33. package/dist/edges/refutes.js +2 -65
  34. package/dist/edges/refutes.js.map +1 -1
  35. package/dist/edges/supports.js +1 -122
  36. package/dist/edges/supports.js.map +1 -1
  37. package/dist/edges/utils.d.ts +7 -7
  38. package/dist/edges/utils.js +2 -133
  39. package/dist/edges/utils.js.map +1 -1
  40. package/dist/embeddingTrigger.js +21 -1
  41. package/dist/embeddingTrigger.js.map +1 -1
  42. package/dist/entityBridge.js +3 -18
  43. package/dist/entityBridge.js.map +1 -1
  44. package/dist/{entityLifecycle-BkhRJ-XI.d.ts → entityLifecycle-CvgSK5FV.d.ts} +1 -1
  45. package/dist/entityLifecycle.d.ts +1 -1
  46. package/dist/entityLifecycle.js +193 -892
  47. package/dist/entityLifecycle.js.map +1 -1
  48. package/dist/{epistemicAnswers-DSP1slZ9.d.ts → epistemicAnswers-C5ib4z6_.d.ts} +1 -1
  49. package/dist/epistemicAnswers.d.ts +1 -1
  50. package/dist/epistemicAnswers.js +73 -810
  51. package/dist/epistemicAnswers.js.map +1 -1
  52. package/dist/{epistemicBeliefs-DtFVTp-k.d.ts → epistemicBeliefs-DzKjZAeC.d.ts} +3 -3
  53. package/dist/epistemicBeliefs.d.ts +2 -2
  54. package/dist/epistemicBeliefs.js +404 -1698
  55. package/dist/epistemicBeliefs.js.map +1 -1
  56. package/dist/epistemicContractHelpers.js +1 -318
  57. package/dist/epistemicContractHelpers.js.map +1 -1
  58. package/dist/epistemicContracts.d.ts +1 -1
  59. package/dist/epistemicContracts.js +417 -1980
  60. package/dist/epistemicContracts.js.map +1 -1
  61. package/dist/{epistemicEdges-DcA8ErUG.d.ts → epistemicEdges-CD5vxmlH.d.ts} +3 -3
  62. package/dist/epistemicEdges.d.ts +1 -1
  63. package/dist/epistemicEdges.js +248 -919
  64. package/dist/epistemicEdges.js.map +1 -1
  65. package/dist/{epistemicEvidence-Bo638XDP.d.ts → epistemicEvidence-xw6UUrwh.d.ts} +1 -1
  66. package/dist/epistemicEvidence.d.ts +1 -1
  67. package/dist/epistemicEvidence.js +229 -1087
  68. package/dist/epistemicEvidence.js.map +1 -1
  69. package/dist/{epistemicHelpers-Bd9xbaib.d.ts → epistemicHelpers-DevrYgPN.d.ts} +1 -1
  70. package/dist/epistemicHelpers.d.ts +1 -1
  71. package/dist/{epistemicLinking-CyeLOIzN.d.ts → epistemicLinking-CfE00tHJ.d.ts} +1 -1
  72. package/dist/epistemicLinking.d.ts +1 -1
  73. package/dist/epistemicLinking.js +3 -786
  74. package/dist/epistemicLinking.js.map +1 -1
  75. package/dist/{epistemicNodes-BpD6Koud.d.ts → epistemicNodes-NBrPW7fk.d.ts} +2 -2
  76. package/dist/epistemicNodes.d.ts +1 -1
  77. package/dist/epistemicNodes.js +172 -899
  78. package/dist/epistemicNodes.js.map +1 -1
  79. package/dist/{epistemicQuestions-CmEeY6zQ.d.ts → epistemicQuestions-B_nUclrH.d.ts} +1 -1
  80. package/dist/epistemicQuestions.d.ts +1 -1
  81. package/dist/epistemicQuestions.js +369 -1125
  82. package/dist/epistemicQuestions.js.map +1 -1
  83. package/dist/{epistemicSources-ZazxHOK1.d.ts → epistemicSources-dlKj58Jp.d.ts} +1 -1
  84. package/dist/epistemicSources.d.ts +1 -1
  85. package/dist/epistemicSources.js +86 -886
  86. package/dist/epistemicSources.js.map +1 -1
  87. package/dist/evaluators/index.js +417 -1980
  88. package/dist/evaluators/index.js.map +1 -1
  89. package/dist/evaluators/lintCheckerEvaluator.js.map +1 -1
  90. package/dist/evaluators/sentryCheckerEvaluator.js.map +1 -1
  91. package/dist/evaluators/shared.js +20 -1
  92. package/dist/evaluators/shared.js.map +1 -1
  93. package/dist/evaluators/testRunnerEvaluator.js +20 -1
  94. package/dist/evaluators/testRunnerEvaluator.js.map +1 -1
  95. package/dist/evaluators/tscCheckerEvaluator.js.map +1 -1
  96. package/dist/index.d.ts +20 -20
  97. package/dist/index.js +965 -3004
  98. package/dist/index.js.map +1 -1
  99. package/dist/{ontology-matching-Buhu23ss.d.ts → ontology-matching-C6rrz2VP.d.ts} +1 -1
  100. package/dist/ontology-matching.d.ts +1 -1
  101. package/dist/ontology-matching.js +1 -344
  102. package/dist/ontology-matching.js.map +1 -1
  103. package/dist/{ontologyApproval-Ba0Jjk1k.d.ts → ontologyApproval-CFYmqKmk.d.ts} +1 -1
  104. package/dist/ontologyApproval.d.ts +1 -1
  105. package/dist/ontologyApproval.js +1 -13
  106. package/dist/ontologyApproval.js.map +1 -1
  107. package/dist/ontologyDefinitions.js +6 -20
  108. package/dist/ontologyDefinitions.js.map +1 -1
  109. package/dist/ontologyHelpers.d.ts +1 -1
  110. package/dist/ontologyHelpers.js +4 -3
  111. package/dist/ontologyHelpers.js.map +1 -1
  112. package/dist/ontologyRegistry.js +2 -17
  113. package/dist/ontologyRegistry.js.map +1 -1
  114. package/dist/{projectionReconciliation-CxrXYGaB.d.ts → projectionReconciliation-jww2fBI0.d.ts} +1 -1
  115. package/dist/projectionReconciliation.d.ts +1 -1
  116. package/dist/projectionReconciliation.js +16 -37
  117. package/dist/projectionReconciliation.js.map +1 -1
  118. package/dist/{projectionStaleness-CAdpIsaW.d.ts → projectionStaleness-CmdbpjVK.d.ts} +1 -1
  119. package/dist/projectionStaleness.d.ts +1 -1
  120. package/dist/{questionEvidenceLinks-BdQD0TkM.d.ts → questionEvidenceLinks-DFlyPpAj.d.ts} +1 -1
  121. package/dist/questionEvidenceLinks.d.ts +1 -1
  122. package/dist/questionEvidenceLinks.js +199 -881
  123. package/dist/questionEvidenceLinks.js.map +1 -1
  124. package/dist/resolvers.js +86 -37
  125. package/dist/resolvers.js.map +1 -1
  126. package/dist/scopeResolverCompat.js +64 -7
  127. package/dist/scopeResolverCompat.js.map +1 -1
  128. package/dist/{text-matching-CMn2WnVD.d.ts → text-matching-DNg4M5Wd.d.ts} +1 -1
  129. package/dist/text-matching.d.ts +1 -1
  130. package/dist/text-matching.js +1 -244
  131. package/dist/text-matching.js.map +1 -1
  132. package/dist/topicProjectOverlay.js +56 -13
  133. package/dist/topicProjectOverlay.js.map +1 -1
  134. package/dist/topicScope.js +55 -6
  135. package/dist/topicScope.js.map +1 -1
  136. package/dist/workflowBridge.d.ts +27 -0
  137. package/dist/workflowBridge.js +352 -0
  138. package/dist/workflowBridge.js.map +1 -0
  139. package/dist/workspaceIsolation.js +56 -57
  140. package/dist/workspaceIsolation.js.map +1 -1
  141. package/package.json +6 -5
@@ -1,796 +1,27 @@
1
1
  import { v } from 'convex/values';
2
- import { componentsGeneric, mutationGeneric, queryGeneric, anyApi } from 'convex/server';
2
+ import { checkProjectAccess } from '@lucern/access-control/access';
3
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
+ import { componentsGeneric, mutationGeneric, anyApi, queryGeneric } from 'convex/server';
3
5
 
4
6
  // src/questionEvidenceLinks.ts
5
7
  var api = anyApi;
6
8
  componentsGeneric();
9
+ var internal = anyApi;
10
+ var mutation = mutationGeneric;
11
+ var query = queryGeneric;
7
12
 
8
- // ../access-control/src/topicProjectOverlay.ts
9
- var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
10
- function readNonEmptyString(value) {
11
- if (typeof value !== "string") {
12
- return;
13
- }
14
- const normalized = value.trim();
15
- return normalized.length > 0 ? normalized : void 0;
16
- }
17
- function readStringArray(value) {
18
- if (!Array.isArray(value)) {
19
- return [];
20
- }
21
- return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
22
- }
23
- function readMetadata(topic) {
24
- return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
25
- }
26
- function readLegacyProjectId(value) {
27
- if (!value) {
28
- return;
29
- }
30
- return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
31
- }
32
- function coerceVisibility(value) {
33
- return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
34
- }
35
- function coerceStatus(value) {
36
- return value === "active" || value === "archived" || value === "watching" ? value : void 0;
37
- }
38
- function mapProjectType(topic, metadata) {
39
- const explicit = readNonEmptyString(metadata.projectType);
40
- if (explicit) {
41
- return explicit;
42
- }
43
- if (topic.type === "theme") {
44
- return "thematic";
45
- }
46
- return readNonEmptyString(topic.type) || "general";
47
- }
48
- function isProjectLikeTopic(topic) {
49
- const metadata = readMetadata(topic);
50
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
51
- }
52
- async function resolveTopicDoc(ctx, scopeId) {
53
- if (ctx?.db && typeof ctx.db.get === "function") {
54
- try {
55
- const directTopic = await ctx.db.get(scopeId);
56
- if (directTopic) {
57
- return directTopic;
58
- }
59
- } catch {
60
- }
61
- }
62
- if (typeof ctx.runQuery !== "function") {
63
- return null;
64
- }
65
- try {
66
- const topic = await ctx.runQuery(api.topics.get, {
67
- id: String(scopeId)
68
- });
69
- if (topic?.name !== void 0 && topic?.type !== void 0) {
70
- return topic;
71
- }
72
- } catch {
73
- }
74
- try {
75
- const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
76
- projectId: String(scopeId)
77
- });
78
- if (topic?.name !== void 0 && topic?.type !== void 0) {
79
- return topic;
80
- }
81
- } catch {
82
- }
83
- return null;
84
- }
85
- function materializeTopicProjectOverlay(topic, idMode = "legacy") {
86
- const metadata = readMetadata(topic);
87
- const topicId = String(topic._id);
88
- const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
89
- const storageProjectId = legacyProjectId || topicId;
90
- const outwardId = idMode === "topic" ? topicId : storageProjectId;
91
- const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
92
- const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
93
- const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
94
- const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
95
- return {
96
- ...metadata,
97
- _id: outwardId,
98
- projectId: outwardId,
99
- topicId,
100
- storageProjectId,
101
- legacyProjectId,
102
- name: readNonEmptyString(topic.name) || "Untitled Theme",
103
- type: mapProjectType(topic, metadata),
104
- description: readNonEmptyString(topic.description),
105
- ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
106
- sharedWith: readStringArray(metadata.sharedWith),
107
- visibility,
108
- tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
109
- workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
110
- status,
111
- tags: readStringArray(metadata.tags),
112
- chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
113
- artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
114
- lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
115
- _creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
116
- createdAt,
117
- updatedAt
118
- };
119
- }
120
- async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
121
- const topic = await resolveTopicDoc(ctx, scopeId);
122
- if (!topic) {
123
- return null;
124
- }
125
- if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
126
- return null;
127
- }
128
- return materializeTopicProjectOverlay(topic, options.idMode);
129
- }
130
- async function listTopicProjectOverlays(ctx, options = {}) {
131
- let allTopics = [];
132
- if (ctx?.db?.query && typeof ctx.db.query === "function") {
133
- try {
134
- allTopics = await ctx.db.query("topics").collect();
135
- } catch {
136
- allTopics = [];
137
- }
138
- }
139
- if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
140
- allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
141
- }
142
- return allTopics.filter(
143
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
144
- ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
13
+ // src/debug.ts
14
+ function isGraphPrimitiveDebugEnabled() {
15
+ const env = globalThis.process?.env;
16
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
145
17
  }
146
-
147
- // ../access-control/src/projectGrantsBridge.ts
148
- var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
149
- function normalizeString(value) {
150
- if (typeof value !== "string") {
18
+ function debugGraphPrimitiveFallback(message, context) {
19
+ if (!isGraphPrimitiveDebugEnabled()) {
151
20
  return;
152
21
  }
153
- const trimmed = value.trim();
154
- return trimmed.length > 0 ? trimmed : void 0;
155
- }
156
- async function resolveGrantScopeIds(ctx, args) {
157
- const topicId = normalizeString(args.topicId);
158
- const projectId = normalizeString(args.projectId);
159
- for (const scopeId of [topicId, projectId]) {
160
- if (!scopeId) {
161
- continue;
162
- }
163
- try {
164
- const overlay = await resolveTopicProjectOverlay(ctx, scopeId, {
165
- idMode: "legacy",
166
- projectLikeOnly: false
167
- });
168
- if (overlay) {
169
- return {
170
- topicId: normalizeString(overlay.topicId) ?? topicId,
171
- projectId: normalizeString(overlay.projectId) ?? projectId ?? scopeId
172
- };
173
- }
174
- } catch {
175
- }
176
- }
177
- return { topicId, projectId };
178
- }
179
- async function normalizeProjectGrantRow(ctx, row) {
180
- const scope = await resolveGrantScopeIds(ctx, {
181
- topicId: row.topicId,
182
- projectId: row.projectId
183
- });
184
- return {
185
- ...row,
186
- ...scope.topicId ? { topicId: scope.topicId } : {},
187
- ...scope.projectId ?? scope.topicId ? { projectId: scope.projectId ?? scope.topicId } : {}
188
- };
189
- }
190
- async function normalizeProjectGrantRows(ctx, rows) {
191
- return await Promise.all(rows.map((row) => normalizeProjectGrantRow(ctx, row)));
192
- }
193
- async function listProjectGrantsByPrincipal(ctx, principalId) {
194
- const rows = await Promise.all(
195
- PROJECT_GRANT_STATUSES.map(
196
- (status) => ctx.db.query("projectGrants").withIndex(
197
- "by_principal_status",
198
- (q) => q.eq("principalId", principalId).eq("status", status)
199
- ).collect()
200
- )
201
- );
202
- return await normalizeProjectGrantRows(ctx, rows.flat());
203
- }
204
- async function listProjectGrantsByGroup(ctx, groupId) {
205
- const rows = await Promise.all(
206
- PROJECT_GRANT_STATUSES.map(
207
- (status) => ctx.db.query("projectGrants").withIndex(
208
- "by_group_status",
209
- (q) => q.eq("groupId", groupId).eq("status", status)
210
- ).collect()
211
- )
212
- );
213
- return await normalizeProjectGrantRows(ctx, rows.flat());
214
- }
215
- function buildScopeMatchers(inputScopeId, resolved) {
216
- return new Set(
217
- [inputScopeId, resolved.topicId, resolved.projectId].map((value) => normalizeString(value)).filter((value) => Boolean(value))
218
- );
219
- }
220
- function matchesResolvedScope(row, scopeIds) {
221
- const rowTopicId = normalizeString(row.topicId);
222
- const rowProjectId = normalizeString(row.projectId);
223
- return rowTopicId !== void 0 && scopeIds.has(rowTopicId) || rowProjectId !== void 0 && scopeIds.has(rowProjectId);
224
- }
225
- async function bridgeListProjectGrantsByTopicAndPrincipal(ctx, topicId, principalId) {
226
- const resolved = await resolveGrantScopeIds(ctx, { topicId });
227
- const scopeIds = buildScopeMatchers(topicId, resolved);
228
- const rows = await listProjectGrantsByPrincipal(ctx, principalId);
229
- return rows.filter((row) => matchesResolvedScope(row, scopeIds));
230
- }
231
- async function bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId) {
232
- const resolved = await resolveGrantScopeIds(ctx, { topicId });
233
- const scopeIds = buildScopeMatchers(topicId, resolved);
234
- const rows = await listProjectGrantsByGroup(ctx, groupId);
235
- return rows.filter((row) => matchesResolvedScope(row, scopeIds));
236
- }
237
- async function bridgeListProjectGrantsByPrincipalStatus(ctx, principalId, status) {
238
- const rows = await listProjectGrantsByPrincipal(ctx, principalId);
239
- return rows.filter((row) => row.status === status);
240
- }
241
- async function bridgeListProjectGrantsByGroupStatus(ctx, groupId, status) {
242
- const rows = await listProjectGrantsByGroup(ctx, groupId);
243
- return rows.filter((row) => row.status === status);
244
- }
245
- async function bridgeInsertProjectGrant(ctx, value) {
246
- const resolved = await resolveGrantScopeIds(ctx, value);
247
- return await ctx.db.insert("projectGrants", {
248
- ...value,
249
- ...resolved.topicId ? { topicId: resolved.topicId } : {},
250
- ...resolved.projectId ?? resolved.topicId ? { projectId: resolved.projectId ?? resolved.topicId } : {}
251
- });
252
- }
253
-
254
- // ../access-control/src/resolvers.ts
255
- async function findUserByClerkId(ctx, clerkId) {
256
- const normalizedClerkId = clerkId.trim();
257
- if (!normalizedClerkId) {
258
- return null;
259
- }
260
- if (typeof ctx.runQuery === "function") {
261
- try {
262
- const bridgedUser = await ctx.runQuery(api.users.getUserByClerkId, {
263
- clerkId: normalizedClerkId
264
- });
265
- if (bridgedUser) {
266
- return bridgedUser;
267
- }
268
- } catch {
269
- }
270
- }
271
- try {
272
- const users = await ctx.db.query("users").collect();
273
- return users.find((user) => String(user.clerkId ?? "") === normalizedClerkId) ?? null;
274
- } catch {
275
- return null;
276
- }
277
- }
278
- async function findUserByPrincipalId(ctx, principalId) {
279
- const normalizedPrincipalId = principalId.trim();
280
- if (!normalizedPrincipalId) {
281
- return null;
282
- }
283
- try {
284
- const users = await ctx.db.query("users").collect();
285
- return users.find(
286
- (user) => String(user.defaultPrincipalId ?? "") === normalizedPrincipalId
287
- ) ?? null;
288
- } catch {
289
- return null;
290
- }
291
- }
292
- async function findAgentByPrincipalId(ctx, principalId) {
293
- const normalizedPrincipalId = principalId.trim();
294
- if (!normalizedPrincipalId) {
295
- return null;
296
- }
297
- if (typeof ctx.runQuery === "function") {
298
- try {
299
- const bridgedAgent = await ctx.runQuery(
300
- api.agents.getAgentByPrincipalId,
301
- {
302
- principalId: normalizedPrincipalId
303
- }
304
- );
305
- if (bridgedAgent) {
306
- return bridgedAgent;
307
- }
308
- } catch {
309
- }
310
- }
311
- try {
312
- const agents = await ctx.db.query("agents").collect();
313
- return agents.find(
314
- (agent) => String(agent.principalId ?? "") === normalizedPrincipalId
315
- ) ?? null;
316
- } catch {
317
- return null;
318
- }
319
- }
320
- function defaultResolvers() {
321
- return {
322
- async getProject(ctx, topicId) {
323
- return await resolveTopicProjectOverlay(ctx, topicId, {
324
- idMode: "legacy",
325
- projectLikeOnly: false
326
- });
327
- },
328
- async listTopics(ctx) {
329
- return await listTopicProjectOverlays(ctx, { idMode: "legacy" });
330
- },
331
- async listTopicsByOwner(ctx, ownerId) {
332
- const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
333
- return topics.filter((topic) => topic.ownerId === ownerId);
334
- },
335
- async listTopicsByVisibility(ctx, visibility) {
336
- const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
337
- return topics.filter((topic) => topic.visibility === visibility);
338
- },
339
- async listProjectGrantsByProjectAndPrincipal(ctx, topicId, principalId) {
340
- return await bridgeListProjectGrantsByTopicAndPrincipal(
341
- ctx,
342
- topicId,
343
- principalId
344
- );
345
- },
346
- async listProjectGrantsByProjectAndGroup(ctx, topicId, groupId) {
347
- return await bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId);
348
- },
349
- async listProjectGrantsByPrincipalStatus(ctx, principalId, status) {
350
- return await bridgeListProjectGrantsByPrincipalStatus(
351
- ctx,
352
- principalId,
353
- status
354
- );
355
- },
356
- async listProjectGrantsByGroupStatus(ctx, groupId, status) {
357
- return await bridgeListProjectGrantsByGroupStatus(ctx, groupId, status);
358
- },
359
- async insertProjectGrant(ctx, value) {
360
- return await bridgeInsertProjectGrant(ctx, value);
361
- },
362
- async getAgentByPrincipalId(ctx, principalId) {
363
- return await findAgentByPrincipalId(ctx, principalId);
364
- },
365
- async getUserByClerkId(ctx, clerkId) {
366
- return await findUserByClerkId(ctx, clerkId);
367
- },
368
- async getUserByPrincipalId(ctx, principalId) {
369
- return await findUserByPrincipalId(ctx, principalId);
370
- }
371
- };
372
- }
373
- var resolverOverrides = {};
374
- function resolveAccessControlAppResolvers(_ctx) {
375
- return {
376
- ...defaultResolvers(),
377
- ...resolverOverrides
378
- };
22
+ console.debug(message, context ?? {});
379
23
  }
380
24
 
381
- // ../access-control/src/principalContext.ts
382
- function requireCanonicalResolvedUser(user, clerkId) {
383
- const resolved = user;
384
- if (!resolved) {
385
- throw new Error(
386
- `[AccessControl] Canonical user identity required for ${clerkId}. Sync users.upsertUser before user-bound access checks.`
387
- );
388
- }
389
- const { mcRole, defaultTenantId, defaultWorkspaceId, defaultPrincipalId } = resolved;
390
- if (mcRole !== "platform_admin" && mcRole !== "tenant_admin" && mcRole !== "workspace_admin" && mcRole !== "editor" && mcRole !== "viewer" && mcRole !== "auditor" && mcRole !== "service_agent") {
391
- throw new Error(
392
- `[AccessControl] Canonical MC role required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
393
- );
394
- }
395
- if (typeof defaultTenantId !== "string" || defaultTenantId.trim().length === 0) {
396
- throw new Error(
397
- `[AccessControl] Canonical home tenant required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
398
- );
399
- }
400
- if (typeof defaultWorkspaceId !== "string" || defaultWorkspaceId.trim().length === 0) {
401
- throw new Error(
402
- `[AccessControl] Canonical home workspace required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
403
- );
404
- }
405
- if (typeof defaultPrincipalId !== "string" || defaultPrincipalId.trim().length === 0) {
406
- throw new Error(
407
- `[AccessControl] Canonical federated principal required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
408
- );
409
- }
410
- return {
411
- mcRole,
412
- defaultTenantId: defaultTenantId.trim(),
413
- defaultWorkspaceId: defaultWorkspaceId.trim(),
414
- defaultPrincipalId: defaultPrincipalId.trim()
415
- };
416
- }
417
- function isPrincipalIdInput(value) {
418
- return value.startsWith("user:") || value.startsWith("group:") || value.startsWith("service:") || value.startsWith("agent:") || value.startsWith("external_viewer:");
419
- }
420
- async function resolveCanonicalUserRecord(ctx, actorId) {
421
- const normalizedActorId = actorId.trim();
422
- const clerkId = isPrincipalIdInput(normalizedActorId) && normalizedActorId.startsWith("user:") ? normalizedActorId.slice("user:".length) : normalizedActorId;
423
- const resolvers = resolveAccessControlAppResolvers();
424
- const resolvedByClerkId = await resolvers.getUserByClerkId(ctx, clerkId);
425
- if (resolvedByClerkId) {
426
- return {
427
- resolvedUser: resolvedByClerkId,
428
- clerkId,
429
- contextClerkId: clerkId
430
- };
431
- }
432
- const resolvedByPrincipalId = await resolvers.getUserByPrincipalId(
433
- ctx,
434
- normalizedActorId
435
- );
436
- return {
437
- resolvedUser: resolvedByPrincipalId ?? null,
438
- clerkId,
439
- contextClerkId: normalizedActorId.startsWith("user:") && clerkId.length > 0 ? clerkId : normalizedActorId
440
- };
441
- }
442
- function uniqRoles(roles) {
443
- const roleSet = /* @__PURE__ */ new Set();
444
- for (const role of roles) {
445
- if (role === "platform_admin" || role === "tenant_admin" || role === "workspace_admin" || role === "editor" || role === "viewer" || role === "auditor" || role === "service_agent") {
446
- roleSet.add(role);
447
- }
448
- }
449
- return [...roleSet];
450
- }
451
- function normalizeGroupIds(value) {
452
- if (!Array.isArray(value)) {
453
- return [];
454
- }
455
- return [...new Set(
456
- value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
457
- )];
458
- }
459
- function requireServiceAgentUser(user, actorId) {
460
- const canonicalUser = requireCanonicalResolvedUser(user, actorId);
461
- if (canonicalUser.mcRole !== "service_agent") {
462
- throw new Error(
463
- `[AccessControl] Canonical service_agent identity required for ${actorId}. Sync users.upsertUser before agent-bound access checks.`
464
- );
465
- }
466
- return canonicalUser;
467
- }
468
- function requireCanonicalResolvedAgent(agent, actorId) {
469
- const resolved = agent;
470
- if (!resolved) {
471
- throw new Error(
472
- `[AccessControl] Agent "${actorId}" not found in agents or users table.`
473
- );
474
- }
475
- if (typeof resolved.principalId !== "string" || resolved.principalId.trim().length === 0) {
476
- throw new Error(
477
- `[AccessControl] Canonical agent principalId required for ${actorId}.`
478
- );
479
- }
480
- if (typeof resolved.tenantId !== "string" || resolved.tenantId.trim().length === 0) {
481
- throw new Error(
482
- `[AccessControl] Canonical home tenant required for ${actorId}.`
483
- );
484
- }
485
- if (typeof resolved.workspaceId !== "string" || resolved.workspaceId.trim().length === 0) {
486
- throw new Error(
487
- `[AccessControl] Canonical home workspace required for ${actorId}.`
488
- );
489
- }
490
- return {
491
- principalId: resolved.principalId.trim(),
492
- tenantId: resolved.tenantId.trim(),
493
- workspaceId: resolved.workspaceId.trim(),
494
- roles: uniqRoles(Array.isArray(resolved.roles) ? resolved.roles : []) ?? ["service_agent"],
495
- groupIds: normalizeGroupIds(resolved.groupIds)
496
- };
497
- }
498
- async function resolvePrincipalContext(ctx, actorId) {
499
- if (actorId.startsWith("agent:")) {
500
- const resolvers = resolveAccessControlAppResolvers();
501
- const resolvedAgent = await resolvers.getAgentByPrincipalId(ctx, actorId);
502
- if (resolvedAgent) {
503
- const agent = requireCanonicalResolvedAgent(
504
- resolvedAgent,
505
- actorId
506
- );
507
- return {
508
- principalId: agent.principalId,
509
- principalType: "service",
510
- clerkId: actorId,
511
- tenantId: agent.tenantId,
512
- workspaceId: agent.workspaceId,
513
- roles: agent.roles.length > 0 ? agent.roles : ["service_agent"],
514
- groupIds: agent.groupIds,
515
- isPlatformAdmin: false,
516
- isTenantAdmin: false,
517
- isWorkspaceAdmin: false,
518
- isSystemFallback: false
519
- };
520
- }
521
- const resolvedUser2 = await resolvers.getUserByClerkId(
522
- ctx,
523
- actorId
524
- );
525
- if (!resolvedUser2) {
526
- throw new Error(
527
- `[AccessControl] Agent "${actorId}" not found in agents or users table.`
528
- );
529
- }
530
- const user2 = requireServiceAgentUser(
531
- resolvedUser2,
532
- actorId
533
- );
534
- console.warn(
535
- `[AccessControl] Deprecated legacy service-agent fallback for ${actorId}; migrate this principal into identity.agents.`
536
- );
537
- return {
538
- principalId: user2.defaultPrincipalId,
539
- principalType: "service",
540
- clerkId: actorId,
541
- tenantId: user2.defaultTenantId,
542
- workspaceId: user2.defaultWorkspaceId,
543
- roles: ["service_agent"],
544
- groupIds: normalizeGroupIds(resolvedUser2?.principalGroupIds),
545
- isPlatformAdmin: false,
546
- isTenantAdmin: false,
547
- isWorkspaceAdmin: false,
548
- isSystemFallback: false
549
- };
550
- }
551
- const {
552
- resolvedUser,
553
- contextClerkId
554
- } = await resolveCanonicalUserRecord(ctx, actorId);
555
- const user = requireCanonicalResolvedUser(
556
- resolvedUser,
557
- contextClerkId
558
- );
559
- if (!user.defaultPrincipalId) {
560
- throw new Error(
561
- `[AccessControl] Canonical federated principal required for ${contextClerkId}. Re-sync Master Control identity before user-bound access checks.`
562
- );
563
- }
564
- if (user.mcRole === "service_agent") {
565
- return {
566
- principalId: user.defaultPrincipalId,
567
- principalType: "service",
568
- clerkId: contextClerkId,
569
- tenantId: user.defaultTenantId,
570
- workspaceId: user.defaultWorkspaceId,
571
- roles: ["service_agent"],
572
- groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
573
- isPlatformAdmin: false,
574
- isTenantAdmin: false,
575
- isWorkspaceAdmin: false,
576
- isSystemFallback: false
577
- };
578
- }
579
- const principalId = user.defaultPrincipalId;
580
- const effectiveRole = user.mcRole;
581
- const roles = effectiveRole === "platform_admin" ? ["platform_admin", "tenant_admin"] : effectiveRole === "tenant_admin" ? ["tenant_admin"] : [effectiveRole];
582
- const tenantId = user.defaultTenantId;
583
- const workspaceId = user.defaultWorkspaceId;
584
- const isPlatformAdmin = effectiveRole === "platform_admin";
585
- return {
586
- principalId,
587
- principalType: "user",
588
- clerkId: contextClerkId,
589
- tenantId,
590
- workspaceId,
591
- roles: uniqRoles(roles),
592
- groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
593
- isPlatformAdmin,
594
- isTenantAdmin: isPlatformAdmin || effectiveRole === "tenant_admin",
595
- isWorkspaceAdmin: isPlatformAdmin || effectiveRole === "tenant_admin" || effectiveRole === "workspace_admin",
596
- isSystemFallback: false
597
- };
598
- }
599
-
600
- // ../access-control/src/access.ts
601
- function isTopicInPrincipalTenant(topic, principalTenantId) {
602
- if (!topic.tenantId) {
603
- return false;
604
- }
605
- if (!principalTenantId) {
606
- return false;
607
- }
608
- return String(topic.tenantId) === String(principalTenantId);
609
- }
610
- function isTopicInPrincipalWorkspace(topic, principalWorkspaceId) {
611
- if (!topic.workspaceId) {
612
- return false;
613
- }
614
- if (!principalWorkspaceId) {
615
- return false;
616
- }
617
- return String(topic.workspaceId) === String(principalWorkspaceId);
618
- }
619
- function isLegacyUnscopedTopic(topic) {
620
- return !topic.tenantId || !topic.workspaceId;
621
- }
622
- function isGrantScopeAlignedToTopic(topic, grant) {
623
- if (topic.tenantId && grant.tenantId && String(topic.tenantId) !== String(grant.tenantId)) {
624
- return false;
625
- }
626
- if (topic.workspaceId && grant.workspaceId && String(topic.workspaceId) !== String(grant.workspaceId)) {
627
- return false;
628
- }
629
- return true;
630
- }
631
- function isGrantSourceAllowedForVisibility(visibility, source) {
632
- if (source !== "external_share") {
633
- return true;
634
- }
635
- return visibility === "external" || visibility === "public";
636
- }
637
- function isGrantActive(grant) {
638
- if (grant.status !== "active") {
639
- return false;
640
- }
641
- if (grant.expiresAt !== void 0 && grant.expiresAt <= Date.now()) {
642
- return false;
643
- }
644
- return true;
645
- }
646
- async function hasPrincipalGrant(ctx, args) {
647
- const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndPrincipal(
648
- ctx,
649
- args.topic._id,
650
- args.principalId
651
- );
652
- if (grants.some(
653
- (grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
654
- args.topic.visibility,
655
- grant.source
656
- ) && (!args.principalIsExternal || args.topic.visibility === "public" || grant.source === "external_share")
657
- )) {
658
- return true;
659
- }
660
- return false;
661
- }
662
- async function hasGroupGrant(ctx, args) {
663
- if (args.groupIds.length === 0) {
664
- return false;
665
- }
666
- for (const groupId of args.groupIds) {
667
- const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndGroup(ctx, args.topic._id, groupId);
668
- if (grants.some(
669
- (grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
670
- args.topic.visibility,
671
- grant.source
672
- )
673
- )) {
674
- return true;
675
- }
676
- }
677
- return false;
678
- }
679
- function isExternalPrincipal(_ctx, _args) {
680
- return false;
681
- }
682
- async function evaluateTopicAccessDetailed(ctx, args) {
683
- if (args.legacyUserId) {
684
- return {
685
- hasAccess: true,
686
- isAdmin: false,
687
- isOwner: false,
688
- isShared: false,
689
- hasGrant: true,
690
- isFirmVisible: true,
691
- isExternalVisible: false,
692
- isPublicVisible: false,
693
- isTenantScopeMatch: true,
694
- isWorkspaceScopeMatch: true,
695
- isPrincipalExternal: false
696
- };
697
- }
698
- const topic = await resolveAccessControlAppResolvers().getProject(
699
- ctx,
700
- args.topicId
701
- );
702
- if (!topic) {
703
- return {
704
- hasAccess: false,
705
- isAdmin: false,
706
- isOwner: false,
707
- isShared: false,
708
- hasGrant: false,
709
- isFirmVisible: false,
710
- isExternalVisible: false,
711
- isPublicVisible: false,
712
- isTenantScopeMatch: false,
713
- isWorkspaceScopeMatch: false,
714
- isPrincipalExternal: false
715
- };
716
- }
717
- const { principalContext, legacyUserId } = args;
718
- const userIsAdmin = principalContext.isPlatformAdmin;
719
- const isOwner = topic.ownerId === legacyUserId;
720
- const isShared = (topic.sharedWith ?? []).includes(legacyUserId);
721
- const principalIsExternal = await isExternalPrincipal(ctx, {
722
- groupIds: principalContext.groupIds,
723
- topicTenantId: topic.tenantId,
724
- topicWorkspaceId: topic.workspaceId
725
- });
726
- const hasPrincipalGrantResult = await hasPrincipalGrant(ctx, {
727
- topic,
728
- principalId: principalContext.principalId,
729
- principalIsExternal
730
- });
731
- const hasGroupGrantResult = await hasGroupGrant(ctx, {
732
- topic,
733
- groupIds: principalContext.groupIds
734
- });
735
- const hasGrant = isShared || hasPrincipalGrantResult || hasGroupGrantResult;
736
- const legacyUnscoped = isLegacyUnscopedTopic(topic);
737
- const tenantScopeMatch = isTopicInPrincipalTenant(
738
- topic,
739
- principalContext.tenantId
740
- );
741
- const workspaceScopeMatch = isTopicInPrincipalWorkspace(
742
- topic,
743
- principalContext.workspaceId
744
- );
745
- const isPublicVisible = topic.visibility === "public";
746
- const isFirmVisible = topic.visibility === "firm" && !legacyUnscoped && tenantScopeMatch && workspaceScopeMatch && !principalIsExternal;
747
- const hasScopedGrant = hasGrant && (legacyUnscoped || tenantScopeMatch && workspaceScopeMatch);
748
- const isExternalVisible = topic.visibility === "external" && hasScopedGrant;
749
- const hasAccess = userIsAdmin || isOwner || hasScopedGrant || isPublicVisible || isFirmVisible;
750
- return {
751
- hasAccess,
752
- isAdmin: userIsAdmin,
753
- isOwner,
754
- isShared,
755
- hasGrant,
756
- isFirmVisible,
757
- isExternalVisible,
758
- isPublicVisible,
759
- isTenantScopeMatch: tenantScopeMatch,
760
- isWorkspaceScopeMatch: workspaceScopeMatch,
761
- isPrincipalExternal: principalIsExternal
762
- };
763
- }
764
- async function checkTopicAccessDetailed(ctx, topicId, userId) {
765
- const principalContext = await resolvePrincipalContext(ctx, userId);
766
- return evaluateTopicAccessDetailed(ctx, {
767
- topicId,
768
- legacyUserId: userId,
769
- principalContext
770
- });
771
- }
772
- async function checkTopicAccess(ctx, topicId, userId) {
773
- const result = await checkTopicAccessDetailed(ctx, topicId, userId);
774
- return result.hasAccess;
775
- }
776
- var checkProjectAccess = checkTopicAccess;
777
- var permissiveReturn = v.optional(v.any());
778
- var looseJsonObject = v.record(v.string(), v.any());
779
- var looseJsonArray = v.array(v.any());
780
- v.union(
781
- v.string(),
782
- v.number(),
783
- v.boolean(),
784
- v.null(),
785
- looseJsonObject,
786
- looseJsonArray
787
- );
788
- var api2 = anyApi;
789
- componentsGeneric();
790
- var internal = anyApi;
791
- var mutation = mutationGeneric;
792
- var query = queryGeneric;
793
-
794
25
  // src/matcherFeedbackUtils.ts
795
26
  function isOperationalLinkStatus(status) {
796
27
  return status !== "suggested" && status !== "dismissed";
@@ -824,96 +55,125 @@ function deriveMatcherReviewStatus(input) {
824
55
  }
825
56
 
826
57
  // src/topicProjectOverlay.ts
827
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
828
- function readNonEmptyString2(value) {
58
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
59
+ function readNonEmptyString(value) {
829
60
  if (typeof value !== "string") {
830
61
  return;
831
62
  }
832
63
  const normalized = value.trim();
833
64
  return normalized.length > 0 ? normalized : void 0;
834
65
  }
835
- function readStringArray2(value) {
66
+ function readStringArray(value) {
836
67
  if (!Array.isArray(value)) {
837
68
  return [];
838
69
  }
839
- return value.map((entry) => readNonEmptyString2(entry)).filter((entry) => Boolean(entry));
70
+ return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
840
71
  }
841
- function readMetadata2(topic) {
72
+ function readMetadata(topic) {
842
73
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
843
74
  }
844
- function readLegacyProjectId2(value) {
75
+ function readLegacyProjectId(value) {
845
76
  if (!value) {
846
77
  return;
847
78
  }
848
- return readNonEmptyString2(value[LEGACY_SCOPE_FIELD2]);
79
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
849
80
  }
850
- function coerceVisibility2(value) {
81
+ function coerceVisibility(value) {
851
82
  return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
852
83
  }
853
- function coerceStatus2(value) {
84
+ function coerceStatus(value) {
854
85
  return value === "active" || value === "archived" || value === "watching" ? value : void 0;
855
86
  }
856
- function mapProjectType2(topic, metadata) {
857
- const explicit = readNonEmptyString2(metadata.projectType);
87
+ function mapProjectType(topic, metadata) {
88
+ const explicit = readNonEmptyString(metadata.projectType);
858
89
  if (explicit) {
859
90
  return explicit;
860
91
  }
861
92
  if (topic.type === "theme") {
862
93
  return "thematic";
863
94
  }
864
- return readNonEmptyString2(topic.type) || "general";
95
+ return readNonEmptyString(topic.type) || "general";
865
96
  }
866
- function isProjectLikeTopic2(topic) {
867
- const metadata = readMetadata2(topic);
868
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId2(topic) !== void 0 || readNonEmptyString2(metadata.projectType) !== void 0;
97
+ function isProjectLikeTopic(topic) {
98
+ const metadata = readMetadata(topic);
99
+ return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
869
100
  }
870
101
  function isMissingLucernChildComponentError(error) {
871
- const message = error instanceof Error ? error.message : String(error);
102
+ const message = getErrorMessage(error);
872
103
  return message.includes(
873
104
  'Child component ComponentName(Identifier("lucern")) not found'
874
105
  ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
875
106
  }
876
- async function resolveTopicDoc2(ctx, scopeId) {
107
+ function getErrorMessage(error) {
108
+ if (error instanceof Error) {
109
+ return error.message;
110
+ }
111
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
112
+ return error.message;
113
+ }
114
+ return "unknown error";
115
+ }
116
+ async function resolveTopicDoc(ctx, scopeId) {
877
117
  if (ctx?.db && typeof ctx.db.get === "function") {
878
118
  try {
879
- const directTopic = await ctx.db.get(scopeId);
119
+ const directTopic = await ctx.db.get(
120
+ scopeId
121
+ );
880
122
  if (directTopic) {
881
123
  return directTopic;
882
124
  }
883
- } catch {
125
+ } catch (error) {
126
+ debugGraphPrimitiveFallback(
127
+ "[topicProjectOverlay] Failed to resolve topic by direct ID",
128
+ {
129
+ error,
130
+ scopeId
131
+ }
132
+ );
884
133
  }
885
134
  }
886
135
  if (typeof ctx.runQuery !== "function") {
887
136
  return null;
888
137
  }
889
138
  try {
890
- const topic = await ctx.runQuery(api2.topics.get, {
139
+ const topic = await ctx.runQuery(api.topics.get, {
891
140
  id: String(scopeId)
892
141
  });
893
142
  if (topic?.name !== void 0 && topic?.type !== void 0) {
894
143
  return topic;
895
144
  }
896
- } catch {
145
+ } catch (error) {
146
+ debugGraphPrimitiveFallback(
147
+ "[topicProjectOverlay] Failed to resolve topic by ID query",
148
+ {
149
+ error,
150
+ scopeId
151
+ }
152
+ );
897
153
  }
898
154
  try {
899
- const topic = await ctx.runQuery(api2.topics.getByLegacyScopeId, {
155
+ const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
900
156
  projectId: String(scopeId)
901
157
  });
902
158
  if (topic?.name !== void 0 && topic?.type !== void 0) {
903
159
  return topic;
904
160
  }
905
- } catch {
161
+ } catch (error) {
162
+ debugGraphPrimitiveFallback(
163
+ "[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
164
+ { error, scopeId }
165
+ );
906
166
  }
907
167
  return null;
908
168
  }
909
- function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
910
- const metadata = readMetadata2(topic);
169
+ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
170
+ const metadata = readMetadata(topic);
911
171
  const topicId = String(topic._id);
912
- const legacyProjectId = readLegacyProjectId2(topic) || readLegacyProjectId2(metadata) || readNonEmptyString2(metadata.legacyProjectId);
172
+ const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
913
173
  const storageProjectId = legacyProjectId || topicId;
914
174
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
915
- const visibility = coerceVisibility2(topic.visibility) || coerceVisibility2(metadata.visibility) || "private";
916
- const status = coerceStatus2(topic.status) || coerceStatus2(metadata.status) || "active";
175
+ const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
176
+ const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
917
177
  const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
918
178
  const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
919
179
  return {
@@ -923,16 +183,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
923
183
  topicId,
924
184
  storageProjectId,
925
185
  legacyProjectId,
926
- name: readNonEmptyString2(topic.name) || "Untitled Theme",
927
- type: mapProjectType2(topic, metadata),
928
- description: readNonEmptyString2(topic.description),
929
- ownerId: readNonEmptyString2(metadata.ownerId) || readNonEmptyString2(topic.createdBy) || "system",
930
- sharedWith: readStringArray2(metadata.sharedWith),
186
+ name: readNonEmptyString(topic.name) || "Untitled Theme",
187
+ type: mapProjectType(topic, metadata),
188
+ description: readNonEmptyString(topic.description),
189
+ ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
190
+ sharedWith: readStringArray(metadata.sharedWith),
931
191
  visibility,
932
- tenantId: readNonEmptyString2(topic.tenantId) || readNonEmptyString2(metadata.tenantId),
933
- workspaceId: readNonEmptyString2(topic.workspaceId) || readNonEmptyString2(metadata.workspaceId),
192
+ tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
193
+ workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
934
194
  status,
935
- tags: readStringArray2(metadata.tags),
195
+ tags: readStringArray(metadata.tags),
936
196
  chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
937
197
  artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
938
198
  lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
@@ -941,38 +201,42 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
941
201
  updatedAt
942
202
  };
943
203
  }
944
- async function resolveTopicProjectOverlay2(ctx, scopeId, options = {}) {
945
- const topic = await resolveTopicDoc2(ctx, scopeId);
204
+ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
205
+ const topic = await resolveTopicDoc(ctx, scopeId);
946
206
  if (!topic) {
947
207
  return null;
948
208
  }
949
- if (options.projectLikeOnly !== false && !isProjectLikeTopic2(topic)) {
209
+ if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
950
210
  return null;
951
211
  }
952
- return materializeTopicProjectOverlay2(topic, options.idMode);
212
+ return materializeTopicProjectOverlay(topic, options.idMode);
953
213
  }
954
- async function listTopicProjectOverlays2(ctx, options = {}) {
214
+ async function listTopicProjectOverlays(ctx, options = {}) {
955
215
  let allTopics = [];
956
216
  if (ctx?.db?.query && typeof ctx.db.query === "function") {
957
217
  try {
958
218
  allTopics = await ctx.db.query("topics").collect();
959
- } catch {
219
+ } catch (error) {
220
+ debugGraphPrimitiveFallback(
221
+ "[topicProjectOverlay] Failed to read topics table; falling back to API",
222
+ { error }
223
+ );
960
224
  allTopics = [];
961
225
  }
962
226
  }
963
227
  if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
964
- allTopics = (await ctx.runQuery(api2.topics.list, {}) ?? []) || [];
228
+ allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
965
229
  }
966
230
  return allTopics.filter(
967
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic2(topic)
968
- ).map((topic) => materializeTopicProjectOverlay2(topic, options.idMode));
231
+ (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
232
+ ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
969
233
  }
970
234
  async function patchTopicProjectOverlay(ctx, scopeId, value) {
971
- const topic = await resolveTopicDoc2(ctx, scopeId);
235
+ const topic = await resolveTopicDoc(ctx, scopeId);
972
236
  if (!topic) {
973
237
  return null;
974
238
  }
975
- const nextMetadata = { ...readMetadata2(topic) };
239
+ const nextMetadata = { ...readMetadata(topic) };
976
240
  const patch = {};
977
241
  const topicUpdateArgs = {
978
242
  id: String(topic._id)
@@ -997,7 +261,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
997
261
  `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
998
262
  );
999
263
  case "status": {
1000
- const status = coerceStatus2(rawValue);
264
+ const status = coerceStatus(rawValue);
1001
265
  if (status) {
1002
266
  patch.status = status;
1003
267
  topicUpdateArgs.status = status;
@@ -1005,7 +269,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1005
269
  break;
1006
270
  }
1007
271
  case "visibility": {
1008
- const visibility = coerceVisibility2(rawValue);
272
+ const visibility = coerceVisibility(rawValue);
1009
273
  if (visibility) {
1010
274
  patch.visibility = visibility;
1011
275
  topicUpdateArgs.visibility = visibility;
@@ -1013,7 +277,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1013
277
  break;
1014
278
  }
1015
279
  case "type": {
1016
- const projectType = readNonEmptyString2(rawValue);
280
+ const projectType = readNonEmptyString(rawValue);
1017
281
  if (projectType) {
1018
282
  nextMetadata.projectType = projectType;
1019
283
  } else {
@@ -1037,7 +301,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1037
301
  topicUpdateArgs.metadata = nextMetadata;
1038
302
  if (typeof ctx.runMutation === "function") {
1039
303
  try {
1040
- await ctx.runMutation(api2.topics.update, topicUpdateArgs);
304
+ await ctx.runMutation(api.topics.update, topicUpdateArgs);
1041
305
  } catch (error) {
1042
306
  if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
1043
307
  throw error;
@@ -1051,19 +315,28 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1051
315
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
1052
316
  );
1053
317
  }
1054
- return materializeTopicProjectOverlay2(
1055
- {
1056
- ...topic,
1057
- ...patch,
1058
- metadata: nextMetadata
1059
- }
1060
- );
318
+ return materializeTopicProjectOverlay({
319
+ ...topic,
320
+ ...patch,
321
+ metadata: nextMetadata
322
+ });
1061
323
  }
1062
324
 
1063
325
  // src/resolvers.ts
1064
326
  function isMissingLucernChildComponentError2(error) {
1065
- const message = error instanceof Error ? error.message : String(error);
1066
- return message.includes('Child component ComponentName(Identifier("lucern")) not found') || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
327
+ const message = getErrorMessage2(error);
328
+ return message.includes(
329
+ 'Child component ComponentName(Identifier("lucern")) not found'
330
+ ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
331
+ }
332
+ function getErrorMessage2(error) {
333
+ if (error instanceof Error) {
334
+ return error.message;
335
+ }
336
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
337
+ return error.message;
338
+ }
339
+ return "unknown error";
1067
340
  }
1068
341
  function isAdvisoryTopicPatch(value) {
1069
342
  const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
@@ -1077,52 +350,47 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
1077
350
  if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
1078
351
  throw error;
1079
352
  }
1080
- console.warn("[lucern graph-primitives] Non-fatal advisory topic patch failure", {
1081
- projectId,
1082
- keys: Object.keys(value),
1083
- error: error instanceof Error ? error.message : error
1084
- });
353
+ console.warn(
354
+ "[lucern graph-primitives] Non-fatal advisory topic patch failure",
355
+ {
356
+ projectId,
357
+ keys: Object.keys(value),
358
+ error: getErrorMessage2(error)
359
+ }
360
+ );
1085
361
  }
1086
362
  }
1087
- function defaultResolvers2() {
363
+ function defaultResolvers() {
1088
364
  return {
1089
- async getProject(ctx, projectId) {
1090
- return await resolveTopicProjectOverlay2(ctx, projectId, {
1091
- idMode: "legacy",
1092
- projectLikeOnly: false
1093
- });
1094
- },
1095
- async patchProject(ctx, projectId, value) {
1096
- await patchProjectWithTolerance(ctx, projectId, value);
1097
- },
1098
- async listTopics(ctx) {
1099
- return await listTopicProjectOverlays2(ctx, {
1100
- idMode: "legacy"
1101
- });
1102
- },
1103
- async getFinalArtifact(ctx, artifactId) {
1104
- return await ctx.db.get(artifactId);
1105
- }
365
+ getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
366
+ idMode: "legacy",
367
+ projectLikeOnly: false
368
+ }),
369
+ patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
370
+ listTopics: (ctx) => listTopicProjectOverlays(ctx, {
371
+ idMode: "legacy"
372
+ }),
373
+ getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
1106
374
  };
1107
375
  }
1108
- var resolverOverrides2 = {};
376
+ var resolverOverrides = {};
1109
377
  function resolveGraphPrimitivesAppResolvers(_ctx) {
1110
378
  return {
1111
- ...defaultResolvers2(),
1112
- ...resolverOverrides2
379
+ ...defaultResolvers(),
380
+ ...resolverOverrides
1113
381
  };
1114
382
  }
1115
- var LEGACY_SCOPE_FIELD3 = "graphScopeProjectId";
383
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
1116
384
  function asMappedProjectId(topic) {
1117
385
  if (!topic) {
1118
386
  return;
1119
387
  }
1120
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD3]);
388
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
1121
389
  if (directLegacyProjectId) {
1122
390
  return directLegacyProjectId;
1123
391
  }
1124
392
  const metadata = topic.metadata || {};
1125
- const candidate = metadata[LEGACY_SCOPE_FIELD3] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
393
+ const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
1126
394
  return candidate ? candidate : void 0;
1127
395
  }
1128
396
  function normalizeScopeValue(value) {
@@ -1151,9 +419,16 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
1151
419
  try {
1152
420
  return await ctx.db.query("topics").withIndex(
1153
421
  "by_graph_scope_project",
1154
- (q) => q.eq(LEGACY_SCOPE_FIELD3, scopeId)
422
+ (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
1155
423
  ).collect();
1156
- } catch {
424
+ } catch (error) {
425
+ debugGraphPrimitiveFallback(
426
+ "[topicScope] Failed to resolve scope alias via index",
427
+ {
428
+ error,
429
+ scopeId
430
+ }
431
+ );
1157
432
  const topics = await ctx.db.query("topics").collect();
1158
433
  return topics.filter((topic) => {
1159
434
  const normalizedGlobalId = normalizeScopeValue(topic.globalId);
@@ -1167,10 +442,17 @@ async function tryResolveHostTopicById(ctx, topicId) {
1167
442
  return null;
1168
443
  }
1169
444
  try {
1170
- return await ctx.runQuery(api2.topics.get, {
445
+ return await ctx.runQuery(api.topics.get, {
1171
446
  id: topicId
1172
447
  }) ?? null;
1173
- } catch {
448
+ } catch (error) {
449
+ debugGraphPrimitiveFallback(
450
+ "[topicScope] Failed to resolve topic by host query",
451
+ {
452
+ error,
453
+ topicId
454
+ }
455
+ );
1174
456
  return null;
1175
457
  }
1176
458
  }
@@ -1179,10 +461,17 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
1179
461
  return null;
1180
462
  }
1181
463
  try {
1182
- return await ctx.runQuery(api2.topics.getByLegacyScopeId, {
464
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
1183
465
  projectId: legacyScopeId
1184
466
  }) ?? null;
1185
- } catch {
467
+ } catch (error) {
468
+ debugGraphPrimitiveFallback(
469
+ "[topicScope] Failed to resolve topic by legacy scope",
470
+ {
471
+ error,
472
+ legacyScopeId
473
+ }
474
+ );
1186
475
  return null;
1187
476
  }
1188
477
  }
@@ -1211,8 +500,17 @@ async function resolveTopicProjectScope(ctx, args) {
1211
500
  if (args.topicId) {
1212
501
  let topic = null;
1213
502
  try {
1214
- topic = await ctx.db.get(args.topicId);
1215
- } catch {
503
+ topic = await ctx.db.get(
504
+ args.topicId
505
+ );
506
+ } catch (error) {
507
+ debugGraphPrimitiveFallback(
508
+ "[topicScope] Failed to load topic by direct id",
509
+ {
510
+ error,
511
+ topicId: args.topicId
512
+ }
513
+ );
1216
514
  }
1217
515
  if (!topic) {
1218
516
  topic = await tryResolveHostTopicById(ctx, String(args.topicId));
@@ -1249,7 +547,14 @@ async function resolveTopicProjectScope(ctx, args) {
1249
547
  directTopic = await ctx.db.get(
1250
548
  args.projectId
1251
549
  );
1252
- } catch {
550
+ } catch (error) {
551
+ debugGraphPrimitiveFallback(
552
+ "[topicScope] Failed to load direct project topic",
553
+ {
554
+ error,
555
+ projectId: args.projectId
556
+ }
557
+ );
1253
558
  }
1254
559
  if (directTopic) {
1255
560
  const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
@@ -1800,7 +1105,20 @@ var getByProject = query({
1800
1105
  if (!hasAccess) {
1801
1106
  return [];
1802
1107
  }
1803
- const scope = await resolveTopicProjectScope(ctx, args).catch(() => null);
1108
+ let scope;
1109
+ try {
1110
+ scope = await resolveTopicProjectScope(ctx, args);
1111
+ } catch (error) {
1112
+ debugGraphPrimitiveFallback(
1113
+ "[questionEvidenceLinks] Failed to resolve topic scope",
1114
+ {
1115
+ error,
1116
+ projectId: args.projectId,
1117
+ topicId: args.topicId
1118
+ }
1119
+ );
1120
+ scope = null;
1121
+ }
1804
1122
  if (!scope) {
1805
1123
  return [];
1806
1124
  }