@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,798 +1,12 @@
1
1
  import { v } from 'convex/values';
2
- import { componentsGeneric, queryGeneric, mutationGeneric, anyApi } from 'convex/server';
2
+ import { checkProjectAccess } from '@lucern/access-control/access';
3
+ import { getCurrentUserId } from '@lucern/access-control/auth';
4
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
5
+ import { componentsGeneric, anyApi, queryGeneric, mutationGeneric } from 'convex/server';
3
6
 
4
7
  // src/entityLifecycle.ts
5
8
  var api = anyApi;
6
9
  componentsGeneric();
7
-
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));
145
- }
146
-
147
- // ../access-control/src/projectGrantsBridge.ts
148
- var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
149
- function normalizeString(value) {
150
- if (typeof value !== "string") {
151
- return;
152
- }
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
- };
379
- }
380
-
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
-
778
- // ../access-control/src/auth.ts
779
- async function getCurrentUserId(ctx) {
780
- const identity = await ctx.auth.getUserIdentity();
781
- return identity?.subject ?? null;
782
- }
783
- var permissiveReturn = v.optional(v.any());
784
- var looseJsonObject = v.record(v.string(), v.any());
785
- var looseJsonArray = v.array(v.any());
786
- v.union(
787
- v.string(),
788
- v.number(),
789
- v.boolean(),
790
- v.null(),
791
- looseJsonObject,
792
- looseJsonArray
793
- );
794
- var api2 = anyApi;
795
- componentsGeneric();
796
10
  var internal = anyApi;
797
11
  var mutation = mutationGeneric;
798
12
  var query = queryGeneric;
@@ -899,7 +113,7 @@ async function resolveTopicOntologyInternal(ctx, topicId) {
899
113
  "by_ontologyId",
900
114
  (q) => q.eq("ontologyId", current?.ontologyId)
901
115
  ).collect();
902
- const published = versions.filter((v4) => v4.status === "published").sort((a, b) => (b.publishedAt ?? 0) - (a.publishedAt ?? 0));
116
+ const published = versions.filter((v3) => v3.status === "published").sort((a, b) => (b.publishedAt ?? 0) - (a.publishedAt ?? 0));
903
117
  const latestPublished = published[0] ?? null;
904
118
  return {
905
119
  ontologyId: ontologyDef._id,
@@ -937,97 +151,138 @@ async function validateEntityTypeForTopic(ctx, topicId, nodeType) {
937
151
  };
938
152
  }
939
153
 
154
+ // src/debug.ts
155
+ function isGraphPrimitiveDebugEnabled() {
156
+ const env = globalThis.process?.env;
157
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
158
+ }
159
+ function debugGraphPrimitiveFallback(message, context) {
160
+ if (!isGraphPrimitiveDebugEnabled()) {
161
+ return;
162
+ }
163
+ console.debug(message, context ?? {});
164
+ }
165
+
940
166
  // src/topicProjectOverlay.ts
941
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
942
- function readNonEmptyString2(value) {
167
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
168
+ function readNonEmptyString(value) {
943
169
  if (typeof value !== "string") {
944
170
  return;
945
171
  }
946
172
  const normalized = value.trim();
947
173
  return normalized.length > 0 ? normalized : void 0;
948
174
  }
949
- function readStringArray2(value) {
175
+ function readStringArray(value) {
950
176
  if (!Array.isArray(value)) {
951
177
  return [];
952
178
  }
953
- return value.map((entry) => readNonEmptyString2(entry)).filter((entry) => Boolean(entry));
179
+ return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
954
180
  }
955
- function readMetadata2(topic) {
181
+ function readMetadata(topic) {
956
182
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
957
183
  }
958
- function readLegacyProjectId2(value) {
184
+ function readLegacyProjectId(value) {
959
185
  if (!value) {
960
186
  return;
961
187
  }
962
- return readNonEmptyString2(value[LEGACY_SCOPE_FIELD2]);
188
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
963
189
  }
964
- function coerceVisibility2(value) {
190
+ function coerceVisibility(value) {
965
191
  return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
966
192
  }
967
- function coerceStatus2(value) {
193
+ function coerceStatus(value) {
968
194
  return value === "active" || value === "archived" || value === "watching" ? value : void 0;
969
195
  }
970
- function mapProjectType2(topic, metadata) {
971
- const explicit = readNonEmptyString2(metadata.projectType);
196
+ function mapProjectType(topic, metadata) {
197
+ const explicit = readNonEmptyString(metadata.projectType);
972
198
  if (explicit) {
973
199
  return explicit;
974
200
  }
975
201
  if (topic.type === "theme") {
976
202
  return "thematic";
977
203
  }
978
- return readNonEmptyString2(topic.type) || "general";
204
+ return readNonEmptyString(topic.type) || "general";
979
205
  }
980
- function isProjectLikeTopic2(topic) {
981
- const metadata = readMetadata2(topic);
982
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId2(topic) !== void 0 || readNonEmptyString2(metadata.projectType) !== void 0;
206
+ function isProjectLikeTopic(topic) {
207
+ const metadata = readMetadata(topic);
208
+ return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
983
209
  }
984
210
  function isMissingLucernChildComponentError(error) {
985
- const message = error instanceof Error ? error.message : String(error);
211
+ const message = getErrorMessage(error);
986
212
  return message.includes(
987
213
  'Child component ComponentName(Identifier("lucern")) not found'
988
214
  ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
989
215
  }
990
- async function resolveTopicDoc2(ctx, scopeId) {
216
+ function getErrorMessage(error) {
217
+ if (error instanceof Error) {
218
+ return error.message;
219
+ }
220
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
221
+ return error.message;
222
+ }
223
+ return "unknown error";
224
+ }
225
+ async function resolveTopicDoc(ctx, scopeId) {
991
226
  if (ctx?.db && typeof ctx.db.get === "function") {
992
227
  try {
993
- const directTopic = await ctx.db.get(scopeId);
228
+ const directTopic = await ctx.db.get(
229
+ scopeId
230
+ );
994
231
  if (directTopic) {
995
232
  return directTopic;
996
233
  }
997
- } catch {
234
+ } catch (error) {
235
+ debugGraphPrimitiveFallback(
236
+ "[topicProjectOverlay] Failed to resolve topic by direct ID",
237
+ {
238
+ error,
239
+ scopeId
240
+ }
241
+ );
998
242
  }
999
243
  }
1000
244
  if (typeof ctx.runQuery !== "function") {
1001
245
  return null;
1002
246
  }
1003
247
  try {
1004
- const topic = await ctx.runQuery(api2.topics.get, {
248
+ const topic = await ctx.runQuery(api.topics.get, {
1005
249
  id: String(scopeId)
1006
250
  });
1007
251
  if (topic?.name !== void 0 && topic?.type !== void 0) {
1008
252
  return topic;
1009
253
  }
1010
- } catch {
254
+ } catch (error) {
255
+ debugGraphPrimitiveFallback(
256
+ "[topicProjectOverlay] Failed to resolve topic by ID query",
257
+ {
258
+ error,
259
+ scopeId
260
+ }
261
+ );
1011
262
  }
1012
263
  try {
1013
- const topic = await ctx.runQuery(api2.topics.getByLegacyScopeId, {
264
+ const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
1014
265
  projectId: String(scopeId)
1015
266
  });
1016
267
  if (topic?.name !== void 0 && topic?.type !== void 0) {
1017
268
  return topic;
1018
269
  }
1019
- } catch {
270
+ } catch (error) {
271
+ debugGraphPrimitiveFallback(
272
+ "[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
273
+ { error, scopeId }
274
+ );
1020
275
  }
1021
276
  return null;
1022
277
  }
1023
- function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1024
- const metadata = readMetadata2(topic);
278
+ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
279
+ const metadata = readMetadata(topic);
1025
280
  const topicId = String(topic._id);
1026
- const legacyProjectId = readLegacyProjectId2(topic) || readLegacyProjectId2(metadata) || readNonEmptyString2(metadata.legacyProjectId);
281
+ const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
1027
282
  const storageProjectId = legacyProjectId || topicId;
1028
283
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
1029
- const visibility = coerceVisibility2(topic.visibility) || coerceVisibility2(metadata.visibility) || "private";
1030
- const status = coerceStatus2(topic.status) || coerceStatus2(metadata.status) || "active";
284
+ const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
285
+ const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
1031
286
  const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
1032
287
  const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
1033
288
  return {
@@ -1037,16 +292,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1037
292
  topicId,
1038
293
  storageProjectId,
1039
294
  legacyProjectId,
1040
- name: readNonEmptyString2(topic.name) || "Untitled Theme",
1041
- type: mapProjectType2(topic, metadata),
1042
- description: readNonEmptyString2(topic.description),
1043
- ownerId: readNonEmptyString2(metadata.ownerId) || readNonEmptyString2(topic.createdBy) || "system",
1044
- sharedWith: readStringArray2(metadata.sharedWith),
295
+ name: readNonEmptyString(topic.name) || "Untitled Theme",
296
+ type: mapProjectType(topic, metadata),
297
+ description: readNonEmptyString(topic.description),
298
+ ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
299
+ sharedWith: readStringArray(metadata.sharedWith),
1045
300
  visibility,
1046
- tenantId: readNonEmptyString2(topic.tenantId) || readNonEmptyString2(metadata.tenantId),
1047
- workspaceId: readNonEmptyString2(topic.workspaceId) || readNonEmptyString2(metadata.workspaceId),
301
+ tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
302
+ workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
1048
303
  status,
1049
- tags: readStringArray2(metadata.tags),
304
+ tags: readStringArray(metadata.tags),
1050
305
  chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
1051
306
  artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
1052
307
  lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
@@ -1055,38 +310,42 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1055
310
  updatedAt
1056
311
  };
1057
312
  }
1058
- async function resolveTopicProjectOverlay2(ctx, scopeId, options = {}) {
1059
- const topic = await resolveTopicDoc2(ctx, scopeId);
313
+ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
314
+ const topic = await resolveTopicDoc(ctx, scopeId);
1060
315
  if (!topic) {
1061
316
  return null;
1062
317
  }
1063
- if (options.projectLikeOnly !== false && !isProjectLikeTopic2(topic)) {
318
+ if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
1064
319
  return null;
1065
320
  }
1066
- return materializeTopicProjectOverlay2(topic, options.idMode);
321
+ return materializeTopicProjectOverlay(topic, options.idMode);
1067
322
  }
1068
- async function listTopicProjectOverlays2(ctx, options = {}) {
323
+ async function listTopicProjectOverlays(ctx, options = {}) {
1069
324
  let allTopics = [];
1070
325
  if (ctx?.db?.query && typeof ctx.db.query === "function") {
1071
326
  try {
1072
327
  allTopics = await ctx.db.query("topics").collect();
1073
- } catch {
328
+ } catch (error) {
329
+ debugGraphPrimitiveFallback(
330
+ "[topicProjectOverlay] Failed to read topics table; falling back to API",
331
+ { error }
332
+ );
1074
333
  allTopics = [];
1075
334
  }
1076
335
  }
1077
336
  if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
1078
- allTopics = (await ctx.runQuery(api2.topics.list, {}) ?? []) || [];
337
+ allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
1079
338
  }
1080
339
  return allTopics.filter(
1081
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic2(topic)
1082
- ).map((topic) => materializeTopicProjectOverlay2(topic, options.idMode));
340
+ (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
341
+ ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
1083
342
  }
1084
343
  async function patchTopicProjectOverlay(ctx, scopeId, value) {
1085
- const topic = await resolveTopicDoc2(ctx, scopeId);
344
+ const topic = await resolveTopicDoc(ctx, scopeId);
1086
345
  if (!topic) {
1087
346
  return null;
1088
347
  }
1089
- const nextMetadata = { ...readMetadata2(topic) };
348
+ const nextMetadata = { ...readMetadata(topic) };
1090
349
  const patch = {};
1091
350
  const topicUpdateArgs = {
1092
351
  id: String(topic._id)
@@ -1111,7 +370,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1111
370
  `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
1112
371
  );
1113
372
  case "status": {
1114
- const status = coerceStatus2(rawValue);
373
+ const status = coerceStatus(rawValue);
1115
374
  if (status) {
1116
375
  patch.status = status;
1117
376
  topicUpdateArgs.status = status;
@@ -1119,7 +378,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1119
378
  break;
1120
379
  }
1121
380
  case "visibility": {
1122
- const visibility = coerceVisibility2(rawValue);
381
+ const visibility = coerceVisibility(rawValue);
1123
382
  if (visibility) {
1124
383
  patch.visibility = visibility;
1125
384
  topicUpdateArgs.visibility = visibility;
@@ -1127,7 +386,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1127
386
  break;
1128
387
  }
1129
388
  case "type": {
1130
- const projectType = readNonEmptyString2(rawValue);
389
+ const projectType = readNonEmptyString(rawValue);
1131
390
  if (projectType) {
1132
391
  nextMetadata.projectType = projectType;
1133
392
  } else {
@@ -1151,7 +410,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1151
410
  topicUpdateArgs.metadata = nextMetadata;
1152
411
  if (typeof ctx.runMutation === "function") {
1153
412
  try {
1154
- await ctx.runMutation(api2.topics.update, topicUpdateArgs);
413
+ await ctx.runMutation(api.topics.update, topicUpdateArgs);
1155
414
  } catch (error) {
1156
415
  if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
1157
416
  throw error;
@@ -1165,19 +424,28 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1165
424
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
1166
425
  );
1167
426
  }
1168
- return materializeTopicProjectOverlay2(
1169
- {
1170
- ...topic,
1171
- ...patch,
1172
- metadata: nextMetadata
1173
- }
1174
- );
427
+ return materializeTopicProjectOverlay({
428
+ ...topic,
429
+ ...patch,
430
+ metadata: nextMetadata
431
+ });
1175
432
  }
1176
433
 
1177
434
  // src/resolvers.ts
1178
435
  function isMissingLucernChildComponentError2(error) {
1179
- const message = error instanceof Error ? error.message : String(error);
1180
- return message.includes('Child component ComponentName(Identifier("lucern")) not found') || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
436
+ const message = getErrorMessage2(error);
437
+ return message.includes(
438
+ 'Child component ComponentName(Identifier("lucern")) not found'
439
+ ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
440
+ }
441
+ function getErrorMessage2(error) {
442
+ if (error instanceof Error) {
443
+ return error.message;
444
+ }
445
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
446
+ return error.message;
447
+ }
448
+ return "unknown error";
1181
449
  }
1182
450
  function isAdvisoryTopicPatch(value) {
1183
451
  const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
@@ -1191,52 +459,47 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
1191
459
  if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
1192
460
  throw error;
1193
461
  }
1194
- console.warn("[lucern graph-primitives] Non-fatal advisory topic patch failure", {
1195
- projectId,
1196
- keys: Object.keys(value),
1197
- error: error instanceof Error ? error.message : error
1198
- });
462
+ console.warn(
463
+ "[lucern graph-primitives] Non-fatal advisory topic patch failure",
464
+ {
465
+ projectId,
466
+ keys: Object.keys(value),
467
+ error: getErrorMessage2(error)
468
+ }
469
+ );
1199
470
  }
1200
471
  }
1201
- function defaultResolvers2() {
472
+ function defaultResolvers() {
1202
473
  return {
1203
- async getProject(ctx, projectId) {
1204
- return await resolveTopicProjectOverlay2(ctx, projectId, {
1205
- idMode: "legacy",
1206
- projectLikeOnly: false
1207
- });
1208
- },
1209
- async patchProject(ctx, projectId, value) {
1210
- await patchProjectWithTolerance(ctx, projectId, value);
1211
- },
1212
- async listTopics(ctx) {
1213
- return await listTopicProjectOverlays2(ctx, {
1214
- idMode: "legacy"
1215
- });
1216
- },
1217
- async getFinalArtifact(ctx, artifactId) {
1218
- return await ctx.db.get(artifactId);
1219
- }
474
+ getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
475
+ idMode: "legacy",
476
+ projectLikeOnly: false
477
+ }),
478
+ patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
479
+ listTopics: (ctx) => listTopicProjectOverlays(ctx, {
480
+ idMode: "legacy"
481
+ }),
482
+ getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
1220
483
  };
1221
484
  }
1222
- var resolverOverrides2 = {};
485
+ var resolverOverrides = {};
1223
486
  function resolveGraphPrimitivesAppResolvers(_ctx) {
1224
487
  return {
1225
- ...defaultResolvers2(),
1226
- ...resolverOverrides2
488
+ ...defaultResolvers(),
489
+ ...resolverOverrides
1227
490
  };
1228
491
  }
1229
- var LEGACY_SCOPE_FIELD3 = "graphScopeProjectId";
492
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
1230
493
  function asMappedProjectId(topic) {
1231
494
  if (!topic) {
1232
495
  return;
1233
496
  }
1234
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD3]);
497
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
1235
498
  if (directLegacyProjectId) {
1236
499
  return directLegacyProjectId;
1237
500
  }
1238
501
  const metadata = topic.metadata || {};
1239
- const candidate = metadata[LEGACY_SCOPE_FIELD3] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
502
+ const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
1240
503
  return candidate ? candidate : void 0;
1241
504
  }
1242
505
  function normalizeScopeValue(value) {
@@ -1265,9 +528,16 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
1265
528
  try {
1266
529
  return await ctx.db.query("topics").withIndex(
1267
530
  "by_graph_scope_project",
1268
- (q) => q.eq(LEGACY_SCOPE_FIELD3, scopeId)
531
+ (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
1269
532
  ).collect();
1270
- } catch {
533
+ } catch (error) {
534
+ debugGraphPrimitiveFallback(
535
+ "[topicScope] Failed to resolve scope alias via index",
536
+ {
537
+ error,
538
+ scopeId
539
+ }
540
+ );
1271
541
  const topics = await ctx.db.query("topics").collect();
1272
542
  return topics.filter((topic) => {
1273
543
  const normalizedGlobalId = normalizeScopeValue(topic.globalId);
@@ -1281,10 +551,17 @@ async function tryResolveHostTopicById(ctx, topicId) {
1281
551
  return null;
1282
552
  }
1283
553
  try {
1284
- return await ctx.runQuery(api2.topics.get, {
554
+ return await ctx.runQuery(api.topics.get, {
1285
555
  id: topicId
1286
556
  }) ?? null;
1287
- } catch {
557
+ } catch (error) {
558
+ debugGraphPrimitiveFallback(
559
+ "[topicScope] Failed to resolve topic by host query",
560
+ {
561
+ error,
562
+ topicId
563
+ }
564
+ );
1288
565
  return null;
1289
566
  }
1290
567
  }
@@ -1293,10 +570,17 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
1293
570
  return null;
1294
571
  }
1295
572
  try {
1296
- return await ctx.runQuery(api2.topics.getByLegacyScopeId, {
573
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
1297
574
  projectId: legacyScopeId
1298
575
  }) ?? null;
1299
- } catch {
576
+ } catch (error) {
577
+ debugGraphPrimitiveFallback(
578
+ "[topicScope] Failed to resolve topic by legacy scope",
579
+ {
580
+ error,
581
+ legacyScopeId
582
+ }
583
+ );
1300
584
  return null;
1301
585
  }
1302
586
  }
@@ -1325,8 +609,17 @@ async function resolveTopicProjectScope(ctx, args) {
1325
609
  if (args.topicId) {
1326
610
  let topic = null;
1327
611
  try {
1328
- topic = await ctx.db.get(args.topicId);
1329
- } catch {
612
+ topic = await ctx.db.get(
613
+ args.topicId
614
+ );
615
+ } catch (error) {
616
+ debugGraphPrimitiveFallback(
617
+ "[topicScope] Failed to load topic by direct id",
618
+ {
619
+ error,
620
+ topicId: args.topicId
621
+ }
622
+ );
1330
623
  }
1331
624
  if (!topic) {
1332
625
  topic = await tryResolveHostTopicById(ctx, String(args.topicId));
@@ -1363,7 +656,14 @@ async function resolveTopicProjectScope(ctx, args) {
1363
656
  directTopic = await ctx.db.get(
1364
657
  args.projectId
1365
658
  );
1366
- } catch {
659
+ } catch (error) {
660
+ debugGraphPrimitiveFallback(
661
+ "[topicScope] Failed to load direct project topic",
662
+ {
663
+ error,
664
+ projectId: args.projectId
665
+ }
666
+ );
1367
667
  }
1368
668
  if (directTopic) {
1369
669
  const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
@@ -1465,9 +765,10 @@ var ONTOLOGICAL_NODE_TYPES = [
1465
765
  "function",
1466
766
  "value_chain"
1467
767
  ];
1468
- function isOntologicalNodeType(nodeType) {
1469
- return ONTOLOGICAL_NODE_TYPES.includes(nodeType);
1470
- }
768
+ var ONTOLOGICAL_NODE_TYPE_SET = new Set(ONTOLOGICAL_NODE_TYPES);
769
+ var isOntologicalNodeType = ONTOLOGICAL_NODE_TYPE_SET.has.bind(
770
+ ONTOLOGICAL_NODE_TYPE_SET
771
+ );
1471
772
  function normalizeCanonicalEntityText(value) {
1472
773
  return value.trim().toLowerCase().replace(/\s+/g, " ");
1473
774
  }