@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,818 +1,27 @@
1
1
  import { v } from 'convex/values';
2
- import { componentsGeneric, queryGeneric, mutationGeneric, anyApi, internalMutationGeneric } from 'convex/server';
2
+ import { checkScopeAccess, requireProjectAccess } from '@lucern/access-control/access';
3
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
+ import { componentsGeneric, anyApi, queryGeneric, mutationGeneric, internalMutationGeneric } from 'convex/server';
3
5
 
4
6
  // src/epistemicEdges.ts
5
7
  var api = anyApi;
6
8
  componentsGeneric();
9
+ var internal = anyApi;
10
+ var internalMutation = internalMutationGeneric;
11
+ var mutation = mutationGeneric;
12
+ var query = queryGeneric;
7
13
 
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));
14
+ // src/debug.ts
15
+ function isGraphPrimitiveDebugEnabled() {
16
+ const env = globalThis.process?.env;
17
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
145
18
  }
146
-
147
- // ../access-control/src/projectGrantsBridge.ts
148
- var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
149
- function normalizeString(value) {
150
- if (typeof value !== "string") {
19
+ function debugGraphPrimitiveFallback(message, context) {
20
+ if (!isGraphPrimitiveDebugEnabled()) {
151
21
  return;
152
22
  }
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];
23
+ console.debug(message, context ?? {});
450
24
  }
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
- async function checkScopeAccess(ctx, scopeId, userId) {
777
- try {
778
- const topic = await ctx.db.get(scopeId);
779
- if (topic && topic.name !== void 0 && topic.type !== void 0) {
780
- return true;
781
- }
782
- } catch {
783
- }
784
- try {
785
- return await checkTopicAccess(ctx, scopeId, userId);
786
- } catch {
787
- return false;
788
- }
789
- }
790
- async function requireTopicAccess(ctx, topicId, userId) {
791
- const hasAccess = await checkTopicAccess(ctx, topicId, userId);
792
- if (!hasAccess) {
793
- throw new Error(
794
- "Access denied: You don't have permission to access this topic"
795
- );
796
- }
797
- }
798
- var requireProjectAccess = requireTopicAccess;
799
- var permissiveReturn = v.optional(v.any());
800
- var looseJsonObject = v.record(v.string(), v.any());
801
- var looseJsonArray = v.array(v.any());
802
- v.union(
803
- v.string(),
804
- v.number(),
805
- v.boolean(),
806
- v.null(),
807
- looseJsonObject,
808
- looseJsonArray
809
- );
810
- var api2 = anyApi;
811
- componentsGeneric();
812
- var internal = anyApi;
813
- var internalMutation = internalMutationGeneric;
814
- var mutation = mutationGeneric;
815
- var query = queryGeneric;
816
25
 
817
26
  // src/graphTypes.ts
818
27
  var EDGE_TYPE_TO_REL = {
@@ -1153,96 +362,125 @@ function validateEdgeLayers(edgeType, fromLayer, toLayer) {
1153
362
  }
1154
363
 
1155
364
  // src/topicProjectOverlay.ts
1156
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
1157
- function readNonEmptyString2(value) {
365
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
366
+ function readNonEmptyString(value) {
1158
367
  if (typeof value !== "string") {
1159
368
  return;
1160
369
  }
1161
370
  const normalized = value.trim();
1162
371
  return normalized.length > 0 ? normalized : void 0;
1163
372
  }
1164
- function readStringArray2(value) {
373
+ function readStringArray(value) {
1165
374
  if (!Array.isArray(value)) {
1166
375
  return [];
1167
376
  }
1168
- return value.map((entry) => readNonEmptyString2(entry)).filter((entry) => Boolean(entry));
377
+ return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
1169
378
  }
1170
- function readMetadata2(topic) {
379
+ function readMetadata(topic) {
1171
380
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
1172
381
  }
1173
- function readLegacyProjectId2(value) {
382
+ function readLegacyProjectId(value) {
1174
383
  if (!value) {
1175
384
  return;
1176
385
  }
1177
- return readNonEmptyString2(value[LEGACY_SCOPE_FIELD2]);
386
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
1178
387
  }
1179
- function coerceVisibility2(value) {
388
+ function coerceVisibility(value) {
1180
389
  return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
1181
390
  }
1182
- function coerceStatus2(value) {
391
+ function coerceStatus(value) {
1183
392
  return value === "active" || value === "archived" || value === "watching" ? value : void 0;
1184
393
  }
1185
- function mapProjectType2(topic, metadata) {
1186
- const explicit = readNonEmptyString2(metadata.projectType);
394
+ function mapProjectType(topic, metadata) {
395
+ const explicit = readNonEmptyString(metadata.projectType);
1187
396
  if (explicit) {
1188
397
  return explicit;
1189
398
  }
1190
399
  if (topic.type === "theme") {
1191
400
  return "thematic";
1192
401
  }
1193
- return readNonEmptyString2(topic.type) || "general";
402
+ return readNonEmptyString(topic.type) || "general";
1194
403
  }
1195
- function isProjectLikeTopic2(topic) {
1196
- const metadata = readMetadata2(topic);
1197
- return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId2(topic) !== void 0 || readNonEmptyString2(metadata.projectType) !== void 0;
404
+ function isProjectLikeTopic(topic) {
405
+ const metadata = readMetadata(topic);
406
+ return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
1198
407
  }
1199
408
  function isMissingLucernChildComponentError(error) {
1200
- const message = error instanceof Error ? error.message : String(error);
409
+ const message = getErrorMessage(error);
1201
410
  return message.includes(
1202
411
  'Child component ComponentName(Identifier("lucern")) not found'
1203
412
  ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
1204
413
  }
1205
- async function resolveTopicDoc2(ctx, scopeId) {
414
+ function getErrorMessage(error) {
415
+ if (error instanceof Error) {
416
+ return error.message;
417
+ }
418
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
419
+ return error.message;
420
+ }
421
+ return "unknown error";
422
+ }
423
+ async function resolveTopicDoc(ctx, scopeId) {
1206
424
  if (ctx?.db && typeof ctx.db.get === "function") {
1207
425
  try {
1208
- const directTopic = await ctx.db.get(scopeId);
426
+ const directTopic = await ctx.db.get(
427
+ scopeId
428
+ );
1209
429
  if (directTopic) {
1210
430
  return directTopic;
1211
431
  }
1212
- } catch {
432
+ } catch (error) {
433
+ debugGraphPrimitiveFallback(
434
+ "[topicProjectOverlay] Failed to resolve topic by direct ID",
435
+ {
436
+ error,
437
+ scopeId
438
+ }
439
+ );
1213
440
  }
1214
441
  }
1215
442
  if (typeof ctx.runQuery !== "function") {
1216
443
  return null;
1217
444
  }
1218
445
  try {
1219
- const topic = await ctx.runQuery(api2.topics.get, {
446
+ const topic = await ctx.runQuery(api.topics.get, {
1220
447
  id: String(scopeId)
1221
448
  });
1222
449
  if (topic?.name !== void 0 && topic?.type !== void 0) {
1223
450
  return topic;
1224
451
  }
1225
- } catch {
452
+ } catch (error) {
453
+ debugGraphPrimitiveFallback(
454
+ "[topicProjectOverlay] Failed to resolve topic by ID query",
455
+ {
456
+ error,
457
+ scopeId
458
+ }
459
+ );
1226
460
  }
1227
461
  try {
1228
- const topic = await ctx.runQuery(api2.topics.getByLegacyScopeId, {
462
+ const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
1229
463
  projectId: String(scopeId)
1230
464
  });
1231
465
  if (topic?.name !== void 0 && topic?.type !== void 0) {
1232
466
  return topic;
1233
467
  }
1234
- } catch {
468
+ } catch (error) {
469
+ debugGraphPrimitiveFallback(
470
+ "[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
471
+ { error, scopeId }
472
+ );
1235
473
  }
1236
474
  return null;
1237
475
  }
1238
- function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1239
- const metadata = readMetadata2(topic);
476
+ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
477
+ const metadata = readMetadata(topic);
1240
478
  const topicId = String(topic._id);
1241
- const legacyProjectId = readLegacyProjectId2(topic) || readLegacyProjectId2(metadata) || readNonEmptyString2(metadata.legacyProjectId);
479
+ const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
1242
480
  const storageProjectId = legacyProjectId || topicId;
1243
481
  const outwardId = idMode === "topic" ? topicId : storageProjectId;
1244
- const visibility = coerceVisibility2(topic.visibility) || coerceVisibility2(metadata.visibility) || "private";
1245
- const status = coerceStatus2(topic.status) || coerceStatus2(metadata.status) || "active";
482
+ const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
483
+ const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
1246
484
  const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
1247
485
  const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
1248
486
  return {
@@ -1252,16 +490,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1252
490
  topicId,
1253
491
  storageProjectId,
1254
492
  legacyProjectId,
1255
- name: readNonEmptyString2(topic.name) || "Untitled Theme",
1256
- type: mapProjectType2(topic, metadata),
1257
- description: readNonEmptyString2(topic.description),
1258
- ownerId: readNonEmptyString2(metadata.ownerId) || readNonEmptyString2(topic.createdBy) || "system",
1259
- sharedWith: readStringArray2(metadata.sharedWith),
493
+ name: readNonEmptyString(topic.name) || "Untitled Theme",
494
+ type: mapProjectType(topic, metadata),
495
+ description: readNonEmptyString(topic.description),
496
+ ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
497
+ sharedWith: readStringArray(metadata.sharedWith),
1260
498
  visibility,
1261
- tenantId: readNonEmptyString2(topic.tenantId) || readNonEmptyString2(metadata.tenantId),
1262
- workspaceId: readNonEmptyString2(topic.workspaceId) || readNonEmptyString2(metadata.workspaceId),
499
+ tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
500
+ workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
1263
501
  status,
1264
- tags: readStringArray2(metadata.tags),
502
+ tags: readStringArray(metadata.tags),
1265
503
  chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
1266
504
  artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
1267
505
  lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
@@ -1270,38 +508,42 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1270
508
  updatedAt
1271
509
  };
1272
510
  }
1273
- async function resolveTopicProjectOverlay2(ctx, scopeId, options = {}) {
1274
- const topic = await resolveTopicDoc2(ctx, scopeId);
511
+ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
512
+ const topic = await resolveTopicDoc(ctx, scopeId);
1275
513
  if (!topic) {
1276
514
  return null;
1277
515
  }
1278
- if (options.projectLikeOnly !== false && !isProjectLikeTopic2(topic)) {
516
+ if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
1279
517
  return null;
1280
518
  }
1281
- return materializeTopicProjectOverlay2(topic, options.idMode);
519
+ return materializeTopicProjectOverlay(topic, options.idMode);
1282
520
  }
1283
- async function listTopicProjectOverlays2(ctx, options = {}) {
521
+ async function listTopicProjectOverlays(ctx, options = {}) {
1284
522
  let allTopics = [];
1285
523
  if (ctx?.db?.query && typeof ctx.db.query === "function") {
1286
524
  try {
1287
525
  allTopics = await ctx.db.query("topics").collect();
1288
- } catch {
526
+ } catch (error) {
527
+ debugGraphPrimitiveFallback(
528
+ "[topicProjectOverlay] Failed to read topics table; falling back to API",
529
+ { error }
530
+ );
1289
531
  allTopics = [];
1290
532
  }
1291
533
  }
1292
534
  if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
1293
- allTopics = (await ctx.runQuery(api2.topics.list, {}) ?? []) || [];
535
+ allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
1294
536
  }
1295
537
  return allTopics.filter(
1296
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic2(topic)
1297
- ).map((topic) => materializeTopicProjectOverlay2(topic, options.idMode));
538
+ (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
539
+ ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
1298
540
  }
1299
541
  async function patchTopicProjectOverlay(ctx, scopeId, value) {
1300
- const topic = await resolveTopicDoc2(ctx, scopeId);
542
+ const topic = await resolveTopicDoc(ctx, scopeId);
1301
543
  if (!topic) {
1302
544
  return null;
1303
545
  }
1304
- const nextMetadata = { ...readMetadata2(topic) };
546
+ const nextMetadata = { ...readMetadata(topic) };
1305
547
  const patch = {};
1306
548
  const topicUpdateArgs = {
1307
549
  id: String(topic._id)
@@ -1326,7 +568,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1326
568
  `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
1327
569
  );
1328
570
  case "status": {
1329
- const status = coerceStatus2(rawValue);
571
+ const status = coerceStatus(rawValue);
1330
572
  if (status) {
1331
573
  patch.status = status;
1332
574
  topicUpdateArgs.status = status;
@@ -1334,7 +576,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1334
576
  break;
1335
577
  }
1336
578
  case "visibility": {
1337
- const visibility = coerceVisibility2(rawValue);
579
+ const visibility = coerceVisibility(rawValue);
1338
580
  if (visibility) {
1339
581
  patch.visibility = visibility;
1340
582
  topicUpdateArgs.visibility = visibility;
@@ -1342,7 +584,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1342
584
  break;
1343
585
  }
1344
586
  case "type": {
1345
- const projectType = readNonEmptyString2(rawValue);
587
+ const projectType = readNonEmptyString(rawValue);
1346
588
  if (projectType) {
1347
589
  nextMetadata.projectType = projectType;
1348
590
  } else {
@@ -1366,7 +608,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1366
608
  topicUpdateArgs.metadata = nextMetadata;
1367
609
  if (typeof ctx.runMutation === "function") {
1368
610
  try {
1369
- await ctx.runMutation(api2.topics.update, topicUpdateArgs);
611
+ await ctx.runMutation(api.topics.update, topicUpdateArgs);
1370
612
  } catch (error) {
1371
613
  if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
1372
614
  throw error;
@@ -1380,19 +622,28 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1380
622
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
1381
623
  );
1382
624
  }
1383
- return materializeTopicProjectOverlay2(
1384
- {
1385
- ...topic,
1386
- ...patch,
1387
- metadata: nextMetadata
1388
- }
1389
- );
625
+ return materializeTopicProjectOverlay({
626
+ ...topic,
627
+ ...patch,
628
+ metadata: nextMetadata
629
+ });
1390
630
  }
1391
631
 
1392
632
  // src/resolvers.ts
1393
633
  function isMissingLucernChildComponentError2(error) {
1394
- const message = error instanceof Error ? error.message : String(error);
1395
- return message.includes('Child component ComponentName(Identifier("lucern")) not found') || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
634
+ const message = getErrorMessage2(error);
635
+ return message.includes(
636
+ 'Child component ComponentName(Identifier("lucern")) not found'
637
+ ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
638
+ }
639
+ function getErrorMessage2(error) {
640
+ if (error instanceof Error) {
641
+ return error.message;
642
+ }
643
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
644
+ return error.message;
645
+ }
646
+ return "unknown error";
1396
647
  }
1397
648
  function isAdvisoryTopicPatch(value) {
1398
649
  const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
@@ -1406,52 +657,47 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
1406
657
  if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
1407
658
  throw error;
1408
659
  }
1409
- console.warn("[lucern graph-primitives] Non-fatal advisory topic patch failure", {
1410
- projectId,
1411
- keys: Object.keys(value),
1412
- error: error instanceof Error ? error.message : error
1413
- });
660
+ console.warn(
661
+ "[lucern graph-primitives] Non-fatal advisory topic patch failure",
662
+ {
663
+ projectId,
664
+ keys: Object.keys(value),
665
+ error: getErrorMessage2(error)
666
+ }
667
+ );
1414
668
  }
1415
669
  }
1416
- function defaultResolvers2() {
670
+ function defaultResolvers() {
1417
671
  return {
1418
- async getProject(ctx, projectId) {
1419
- return await resolveTopicProjectOverlay2(ctx, projectId, {
1420
- idMode: "legacy",
1421
- projectLikeOnly: false
1422
- });
1423
- },
1424
- async patchProject(ctx, projectId, value) {
1425
- await patchProjectWithTolerance(ctx, projectId, value);
1426
- },
1427
- async listTopics(ctx) {
1428
- return await listTopicProjectOverlays2(ctx, {
1429
- idMode: "legacy"
1430
- });
1431
- },
1432
- async getFinalArtifact(ctx, artifactId) {
1433
- return await ctx.db.get(artifactId);
1434
- }
672
+ getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
673
+ idMode: "legacy",
674
+ projectLikeOnly: false
675
+ }),
676
+ patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
677
+ listTopics: (ctx) => listTopicProjectOverlays(ctx, {
678
+ idMode: "legacy"
679
+ }),
680
+ getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
1435
681
  };
1436
682
  }
1437
- var resolverOverrides2 = {};
683
+ var resolverOverrides = {};
1438
684
  function resolveGraphPrimitivesAppResolvers(_ctx) {
1439
685
  return {
1440
- ...defaultResolvers2(),
1441
- ...resolverOverrides2
686
+ ...defaultResolvers(),
687
+ ...resolverOverrides
1442
688
  };
1443
689
  }
1444
- var LEGACY_SCOPE_FIELD3 = "graphScopeProjectId";
690
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
1445
691
  function asMappedProjectId(topic) {
1446
692
  if (!topic) {
1447
693
  return;
1448
694
  }
1449
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD3]);
695
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
1450
696
  if (directLegacyProjectId) {
1451
697
  return directLegacyProjectId;
1452
698
  }
1453
699
  const metadata = topic.metadata || {};
1454
- const candidate = metadata[LEGACY_SCOPE_FIELD3] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
700
+ const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
1455
701
  return candidate ? candidate : void 0;
1456
702
  }
1457
703
  function normalizeScopeValue(value) {
@@ -1480,9 +726,16 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
1480
726
  try {
1481
727
  return await ctx.db.query("topics").withIndex(
1482
728
  "by_graph_scope_project",
1483
- (q) => q.eq(LEGACY_SCOPE_FIELD3, scopeId)
729
+ (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
1484
730
  ).collect();
1485
- } catch {
731
+ } catch (error) {
732
+ debugGraphPrimitiveFallback(
733
+ "[topicScope] Failed to resolve scope alias via index",
734
+ {
735
+ error,
736
+ scopeId
737
+ }
738
+ );
1486
739
  const topics = await ctx.db.query("topics").collect();
1487
740
  return topics.filter((topic) => {
1488
741
  const normalizedGlobalId = normalizeScopeValue(topic.globalId);
@@ -1496,10 +749,17 @@ async function tryResolveHostTopicById(ctx, topicId) {
1496
749
  return null;
1497
750
  }
1498
751
  try {
1499
- return await ctx.runQuery(api2.topics.get, {
752
+ return await ctx.runQuery(api.topics.get, {
1500
753
  id: topicId
1501
754
  }) ?? null;
1502
- } catch {
755
+ } catch (error) {
756
+ debugGraphPrimitiveFallback(
757
+ "[topicScope] Failed to resolve topic by host query",
758
+ {
759
+ error,
760
+ topicId
761
+ }
762
+ );
1503
763
  return null;
1504
764
  }
1505
765
  }
@@ -1508,10 +768,17 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
1508
768
  return null;
1509
769
  }
1510
770
  try {
1511
- return await ctx.runQuery(api2.topics.getByLegacyScopeId, {
771
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
1512
772
  projectId: legacyScopeId
1513
773
  }) ?? null;
1514
- } catch {
774
+ } catch (error) {
775
+ debugGraphPrimitiveFallback(
776
+ "[topicScope] Failed to resolve topic by legacy scope",
777
+ {
778
+ error,
779
+ legacyScopeId
780
+ }
781
+ );
1515
782
  return null;
1516
783
  }
1517
784
  }
@@ -1540,8 +807,17 @@ async function resolveTopicProjectScope(ctx, args) {
1540
807
  if (args.topicId) {
1541
808
  let topic = null;
1542
809
  try {
1543
- topic = await ctx.db.get(args.topicId);
1544
- } catch {
810
+ topic = await ctx.db.get(
811
+ args.topicId
812
+ );
813
+ } catch (error) {
814
+ debugGraphPrimitiveFallback(
815
+ "[topicScope] Failed to load topic by direct id",
816
+ {
817
+ error,
818
+ topicId: args.topicId
819
+ }
820
+ );
1545
821
  }
1546
822
  if (!topic) {
1547
823
  topic = await tryResolveHostTopicById(ctx, String(args.topicId));
@@ -1578,7 +854,14 @@ async function resolveTopicProjectScope(ctx, args) {
1578
854
  directTopic = await ctx.db.get(
1579
855
  args.projectId
1580
856
  );
1581
- } catch {
857
+ } catch (error) {
858
+ debugGraphPrimitiveFallback(
859
+ "[topicScope] Failed to load direct project topic",
860
+ {
861
+ error,
862
+ projectId: args.projectId
863
+ }
864
+ );
1582
865
  }
1583
866
  if (directTopic) {
1584
867
  const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
@@ -1785,6 +1068,34 @@ var edgeTypeValidator = v.union(
1785
1068
  v.literal("competes_with")
1786
1069
  // Company → Company (ontological → ontological)
1787
1070
  );
1071
+ function buildEdgeStatusSuccessResult() {
1072
+ return { success: true };
1073
+ }
1074
+ function buildEdgeNotFoundResult() {
1075
+ const result = {};
1076
+ result.success = false;
1077
+ result.error = "Edge not found";
1078
+ return result;
1079
+ }
1080
+ function buildEdgeMirrorSkippedResult() {
1081
+ return {
1082
+ success: false,
1083
+ reason: "source_not_in_convex"
1084
+ };
1085
+ }
1086
+ function buildEdgeMirrorMissingResult() {
1087
+ return {
1088
+ success: false,
1089
+ reason: "not_found"
1090
+ };
1091
+ }
1092
+ function buildEdgeMirrorWriteResult(edgeId, existed) {
1093
+ return {
1094
+ success: true,
1095
+ edgeId,
1096
+ existed
1097
+ };
1098
+ }
1788
1099
  var get = query({
1789
1100
  args: { edgeId: v.id("epistemicEdges") },
1790
1101
  returns: permissiveReturn,
@@ -1936,7 +1247,15 @@ var getByProjectAndType = query({
1936
1247
  projectId: args.projectId,
1937
1248
  topicId: args.topicId
1938
1249
  });
1939
- } catch {
1250
+ } catch (error) {
1251
+ debugGraphPrimitiveFallback(
1252
+ "[epistemicEdges] Failed to resolve getAll scope",
1253
+ {
1254
+ error,
1255
+ projectId: args.projectId,
1256
+ topicId: args.topicId
1257
+ }
1258
+ );
1940
1259
  return [];
1941
1260
  }
1942
1261
  const projectEdges = await collectScopedEdges(ctx, scope, 5e3);
@@ -1962,7 +1281,15 @@ var getByProject = query({
1962
1281
  projectId: args.projectId,
1963
1282
  topicId: args.topicId
1964
1283
  });
1965
- } catch {
1284
+ } catch (error) {
1285
+ debugGraphPrimitiveFallback(
1286
+ "[epistemicEdges] Failed to resolve getByProject scope",
1287
+ {
1288
+ error,
1289
+ projectId: args.projectId,
1290
+ topicId: args.topicId
1291
+ }
1292
+ );
1966
1293
  return [];
1967
1294
  }
1968
1295
  if (args.userId) {
@@ -1976,7 +1303,11 @@ var getByProject = query({
1976
1303
  }
1977
1304
  }
1978
1305
  const pageSize = Math.max(1, Math.min(Math.floor(args.limit ?? 500), 2e3));
1979
- const edges = await collectScopedEdges(ctx, scope, Math.min(pageSize * 3, 6e3));
1306
+ const edges = await collectScopedEdges(
1307
+ ctx,
1308
+ scope,
1309
+ Math.min(pageSize * 3, 6e3)
1310
+ );
1980
1311
  return edges.filter((edge) => edgeMatchesWorkspaceReasoningScope(edge, scope)).slice(0, pageSize);
1981
1312
  }
1982
1313
  });
@@ -2210,7 +1541,7 @@ var update = mutation({
2210
1541
  projectId: edge.projectId
2211
1542
  });
2212
1543
  }
2213
- return { success: true };
1544
+ return buildEdgeStatusSuccessResult();
2214
1545
  }
2215
1546
  });
2216
1547
  var remove = mutation({
@@ -2222,7 +1553,7 @@ var remove = mutation({
2222
1553
  handler: async (ctx, args) => {
2223
1554
  const edge = await ctx.db.get(args.edgeId);
2224
1555
  if (!edge) {
2225
- return { success: false, error: "Edge not found" };
1556
+ return buildEdgeNotFoundResult();
2226
1557
  }
2227
1558
  if (edge.projectId && args.userId) {
2228
1559
  await requireProjectAccess(ctx, edge.projectId, args.userId);
@@ -2257,7 +1588,7 @@ var remove = mutation({
2257
1588
  globalId: edge.globalId
2258
1589
  });
2259
1590
  await ctx.db.delete(args.edgeId);
2260
- return { success: true };
1591
+ return buildEdgeStatusSuccessResult();
2261
1592
  }
2262
1593
  });
2263
1594
  var removeBetween = mutation({
@@ -2417,9 +1748,7 @@ var getLineage = query({
2417
1748
  }
2418
1749
  visited.add(nodeIdStr);
2419
1750
  const edges = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", current.nodeId)).collect();
2420
- const lineageEdges = edges.filter(
2421
- (e) => e.edgeType === "derived_from"
2422
- );
1751
+ const lineageEdges = edges.filter((e) => e.edgeType === "derived_from");
2423
1752
  for (const edge of lineageEdges) {
2424
1753
  if (!edge.toNodeId) {
2425
1754
  continue;
@@ -2620,13 +1949,13 @@ var mirrorEdgeToConvex = internalMutation({
2620
1949
  console.log(
2621
1950
  `[Dual-Write] Skipping mirror - source node not in Convex: ${args.fromGlobalId}`
2622
1951
  );
2623
- return { success: false, reason: "source_not_in_convex" };
1952
+ return buildEdgeMirrorSkippedResult();
2624
1953
  }
2625
1954
  const toNode = await ctx.db.query("epistemicNodes").withIndex("by_globalId", (q) => q.eq("globalId", args.toGlobalId)).first();
2626
1955
  const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first();
2627
1956
  if (existing) {
2628
1957
  console.log(`[Dual-Write] Edge already exists: ${args.globalId}`);
2629
- return { success: true, edgeId: existing._id, existed: true };
1958
+ return buildEdgeMirrorWriteResult(existing._id, true);
2630
1959
  }
2631
1960
  const now = Date.now();
2632
1961
  const edgeId = await ctx.db.insert("epistemicEdges", {
@@ -2667,7 +1996,7 @@ var mirrorEdgeToConvex = internalMutation({
2667
1996
  console.log(
2668
1997
  `[Dual-Write] Mirrored edge to Convex: ${args.globalId} (${args.edgeType})`
2669
1998
  );
2670
- return { success: true, edgeId, existed: false };
1999
+ return buildEdgeMirrorWriteResult(edgeId, false);
2671
2000
  }
2672
2001
  });
2673
2002
  var deleteEdgeFromConvex = internalMutation({
@@ -2678,11 +2007,11 @@ var deleteEdgeFromConvex = internalMutation({
2678
2007
  handler: async (ctx, args) => {
2679
2008
  const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first();
2680
2009
  if (!existing) {
2681
- return { success: false, reason: "not_found" };
2010
+ return buildEdgeMirrorMissingResult();
2682
2011
  }
2683
2012
  await ctx.db.delete(existing._id);
2684
2013
  console.log(`[Dual-Write] Deleted edge from Convex: ${args.globalId}`);
2685
- return { success: true };
2014
+ return buildEdgeStatusSuccessResult();
2686
2015
  }
2687
2016
  });
2688
2017
  var updateEdgeInConvex = internalMutation({
@@ -2703,7 +2032,7 @@ var updateEdgeInConvex = internalMutation({
2703
2032
  handler: async (ctx, args) => {
2704
2033
  const existing = await ctx.db.query("epistemicEdges").withIndex("by_globalId", (q) => q.eq("globalId", args.globalId)).first();
2705
2034
  if (!existing) {
2706
- return { success: false, reason: "not_found" };
2035
+ return buildEdgeMirrorMissingResult();
2707
2036
  }
2708
2037
  const updates = {
2709
2038
  updatedAt: Date.now()
@@ -2740,7 +2069,7 @@ var updateEdgeInConvex = internalMutation({
2740
2069
  }
2741
2070
  await ctx.db.patch(existing._id, updates);
2742
2071
  console.log(`[Dual-Write] Updated edge in Convex: ${args.globalId}`);
2743
- return { success: true };
2072
+ return buildEdgeStatusSuccessResult();
2744
2073
  }
2745
2074
  });
2746
2075