@lucern/graph-primitives 0.1.0-alpha.4 → 0.3.0-alpha.1

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 (78) hide show
  1. package/dist/beliefDecay.js +229 -1115
  2. package/dist/beliefDecay.js.map +1 -1
  3. package/dist/beliefEvidenceLinks.js +53 -834
  4. package/dist/beliefEvidenceLinks.js.map +1 -1
  5. package/dist/confidencePropagationDispatch.d.ts +3 -3
  6. package/dist/confidencePropagationDispatch.js +30 -308
  7. package/dist/confidencePropagationDispatch.js.map +1 -1
  8. package/dist/contradictions.js +5 -797
  9. package/dist/contradictions.js.map +1 -1
  10. package/dist/edges/contradicts.js +1 -122
  11. package/dist/edges/contradicts.js.map +1 -1
  12. package/dist/edges/dependsOn.js +14 -172
  13. package/dist/edges/dependsOn.js.map +1 -1
  14. package/dist/edges/elaborates.js +1 -49
  15. package/dist/edges/elaborates.js.map +1 -1
  16. package/dist/edges/index.js +14 -277
  17. package/dist/edges/index.js.map +1 -1
  18. package/dist/edges/informs.js +1 -62
  19. package/dist/edges/informs.js.map +1 -1
  20. package/dist/edges/propagationTypes.d.ts +2 -2
  21. package/dist/edges/propagationTypes.js.map +1 -1
  22. package/dist/edges/refutes.js +1 -62
  23. package/dist/edges/refutes.js.map +1 -1
  24. package/dist/edges/supports.js +1 -122
  25. package/dist/edges/supports.js.map +1 -1
  26. package/dist/edges/utils.d.ts +6 -6
  27. package/dist/edges/utils.js +1 -130
  28. package/dist/edges/utils.js.map +1 -1
  29. package/dist/entityBridge.js +2 -17
  30. package/dist/entityBridge.js.map +1 -1
  31. package/dist/entityLifecycle.js +62 -848
  32. package/dist/entityLifecycle.js.map +1 -1
  33. package/dist/epistemicAnswers.js +27 -838
  34. package/dist/epistemicAnswers.js.map +1 -1
  35. package/dist/epistemicBeliefs.js +186 -2214
  36. package/dist/epistemicBeliefs.js.map +1 -1
  37. package/dist/epistemicContractHelpers.js +1 -318
  38. package/dist/epistemicContractHelpers.js.map +1 -1
  39. package/dist/epistemicContracts.js +163 -2467
  40. package/dist/epistemicContracts.js.map +1 -1
  41. package/dist/epistemicEdges.js +60 -863
  42. package/dist/epistemicEdges.js.map +1 -1
  43. package/dist/epistemicEvidence.js +116 -1647
  44. package/dist/epistemicEvidence.js.map +1 -1
  45. package/dist/epistemicHelpers.js +3 -2
  46. package/dist/epistemicHelpers.js.map +1 -1
  47. package/dist/epistemicLinking.js +2 -785
  48. package/dist/epistemicLinking.js.map +1 -1
  49. package/dist/epistemicNodes.js +34 -1427
  50. package/dist/epistemicNodes.js.map +1 -1
  51. package/dist/epistemicQuestions.js +88 -1637
  52. package/dist/epistemicQuestions.js.map +1 -1
  53. package/dist/epistemicSources.js +28 -1421
  54. package/dist/epistemicSources.js.map +1 -1
  55. package/dist/evaluators/index.js +163 -2467
  56. package/dist/evaluators/index.js.map +1 -1
  57. package/dist/index.js +486 -3649
  58. package/dist/index.js.map +1 -1
  59. package/dist/ontology-matching.js +1 -344
  60. package/dist/ontology-matching.js.map +1 -1
  61. package/dist/ontologyApproval.js +1 -13
  62. package/dist/ontologyApproval.js.map +1 -1
  63. package/dist/ontologyDefinitions.js +2 -17
  64. package/dist/ontologyDefinitions.js.map +1 -1
  65. package/dist/ontologyRegistry.js +2 -17
  66. package/dist/ontologyRegistry.js.map +1 -1
  67. package/dist/projectionReconciliation.js +2 -17
  68. package/dist/projectionReconciliation.js.map +1 -1
  69. package/dist/questionEvidenceLinks.js +242 -837
  70. package/dist/questionEvidenceLinks.js.map +1 -1
  71. package/dist/text-matching.js +1 -244
  72. package/dist/text-matching.js.map +1 -1
  73. package/dist/workflowBridge.d.ts +27 -0
  74. package/dist/workflowBridge.js +303 -0
  75. package/dist/workflowBridge.js.map +1 -0
  76. package/dist/workspaceIsolation.js +8 -609
  77. package/dist/workspaceIsolation.js.map +1 -1
  78. package/package.json +6 -6
@@ -1,814 +1,11 @@
1
1
  import { v } from 'convex/values';
2
+ import { checkScopeAccess, requireProjectAccess } from '@lucern/access-control/access';
3
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
2
4
  import { componentsGeneric, queryGeneric, mutationGeneric, anyApi, internalMutationGeneric } from 'convex/server';
3
5
 
4
6
  // src/epistemicEdges.ts
5
7
  var api = anyApi;
6
8
  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
- 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
9
  var internal = anyApi;
813
10
  var internalMutation = internalMutationGeneric;
814
11
  var mutation = mutationGeneric;
@@ -1153,48 +350,48 @@ function validateEdgeLayers(edgeType, fromLayer, toLayer) {
1153
350
  }
1154
351
 
1155
352
  // src/topicProjectOverlay.ts
1156
- var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
1157
- function readNonEmptyString2(value) {
353
+ var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
354
+ function readNonEmptyString(value) {
1158
355
  if (typeof value !== "string") {
1159
356
  return;
1160
357
  }
1161
358
  const normalized = value.trim();
1162
359
  return normalized.length > 0 ? normalized : void 0;
1163
360
  }
1164
- function readStringArray2(value) {
361
+ function readStringArray(value) {
1165
362
  if (!Array.isArray(value)) {
1166
363
  return [];
1167
364
  }
1168
- return value.map((entry) => readNonEmptyString2(entry)).filter((entry) => Boolean(entry));
365
+ return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
1169
366
  }
1170
- function readMetadata2(topic) {
367
+ function readMetadata(topic) {
1171
368
  return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
1172
369
  }
1173
- function readLegacyProjectId2(value) {
370
+ function readLegacyProjectId(value) {
1174
371
  if (!value) {
1175
372
  return;
1176
373
  }
1177
- return readNonEmptyString2(value[LEGACY_SCOPE_FIELD2]);
374
+ return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
1178
375
  }
1179
- function coerceVisibility2(value) {
376
+ function coerceVisibility(value) {
1180
377
  return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
1181
378
  }
1182
- function coerceStatus2(value) {
379
+ function coerceStatus(value) {
1183
380
  return value === "active" || value === "archived" || value === "watching" ? value : void 0;
1184
381
  }
1185
- function mapProjectType2(topic, metadata) {
1186
- const explicit = readNonEmptyString2(metadata.projectType);
382
+ function mapProjectType(topic, metadata) {
383
+ const explicit = readNonEmptyString(metadata.projectType);
1187
384
  if (explicit) {
1188
385
  return explicit;
1189
386
  }
1190
387
  if (topic.type === "theme") {
1191
388
  return "thematic";
1192
389
  }
1193
- return readNonEmptyString2(topic.type) || "general";
390
+ return readNonEmptyString(topic.type) || "general";
1194
391
  }
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;
392
+ function isProjectLikeTopic(topic) {
393
+ const metadata = readMetadata(topic);
394
+ return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
1198
395
  }
1199
396
  function isMissingLucernChildComponentError(error) {
1200
397
  const message = error instanceof Error ? error.message : String(error);
@@ -1202,7 +399,7 @@ function isMissingLucernChildComponentError(error) {
1202
399
  'Child component ComponentName(Identifier("lucern")) not found'
1203
400
  ) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
1204
401
  }
1205
- async function resolveTopicDoc2(ctx, scopeId) {
402
+ async function resolveTopicDoc(ctx, scopeId) {
1206
403
  if (ctx?.db && typeof ctx.db.get === "function") {
1207
404
  try {
1208
405
  const directTopic = await ctx.db.get(scopeId);
@@ -1216,7 +413,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
1216
413
  return null;
1217
414
  }
1218
415
  try {
1219
- const topic = await ctx.runQuery(api2.topics.get, {
416
+ const topic = await ctx.runQuery(api.topics.get, {
1220
417
  id: String(scopeId)
1221
418
  });
1222
419
  if (topic?.name !== void 0 && topic?.type !== void 0) {
@@ -1225,7 +422,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
1225
422
  } catch {
1226
423
  }
1227
424
  try {
1228
- const topic = await ctx.runQuery(api2.topics.getByLegacyScopeId, {
425
+ const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
1229
426
  projectId: String(scopeId)
1230
427
  });
1231
428
  if (topic?.name !== void 0 && topic?.type !== void 0) {
@@ -1235,14 +432,14 @@ async function resolveTopicDoc2(ctx, scopeId) {
1235
432
  }
1236
433
  return null;
1237
434
  }
1238
- function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1239
- const metadata = readMetadata2(topic);
435
+ function materializeTopicProjectOverlay(topic, idMode = "legacy") {
436
+ const metadata = readMetadata(topic);
1240
437
  const topicId = String(topic._id);
1241
- const legacyProjectId = readLegacyProjectId2(topic) || readLegacyProjectId2(metadata) || readNonEmptyString2(metadata.legacyProjectId);
438
+ const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
1242
439
  const storageProjectId = legacyProjectId || topicId;
1243
440
  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";
441
+ const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
442
+ const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
1246
443
  const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
1247
444
  const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
1248
445
  return {
@@ -1252,16 +449,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1252
449
  topicId,
1253
450
  storageProjectId,
1254
451
  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),
452
+ name: readNonEmptyString(topic.name) || "Untitled Theme",
453
+ type: mapProjectType(topic, metadata),
454
+ description: readNonEmptyString(topic.description),
455
+ ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
456
+ sharedWith: readStringArray(metadata.sharedWith),
1260
457
  visibility,
1261
- tenantId: readNonEmptyString2(topic.tenantId) || readNonEmptyString2(metadata.tenantId),
1262
- workspaceId: readNonEmptyString2(topic.workspaceId) || readNonEmptyString2(metadata.workspaceId),
458
+ tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
459
+ workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
1263
460
  status,
1264
- tags: readStringArray2(metadata.tags),
461
+ tags: readStringArray(metadata.tags),
1265
462
  chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
1266
463
  artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
1267
464
  lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
@@ -1270,17 +467,17 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
1270
467
  updatedAt
1271
468
  };
1272
469
  }
1273
- async function resolveTopicProjectOverlay2(ctx, scopeId, options = {}) {
1274
- const topic = await resolveTopicDoc2(ctx, scopeId);
470
+ async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
471
+ const topic = await resolveTopicDoc(ctx, scopeId);
1275
472
  if (!topic) {
1276
473
  return null;
1277
474
  }
1278
- if (options.projectLikeOnly !== false && !isProjectLikeTopic2(topic)) {
475
+ if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
1279
476
  return null;
1280
477
  }
1281
- return materializeTopicProjectOverlay2(topic, options.idMode);
478
+ return materializeTopicProjectOverlay(topic, options.idMode);
1282
479
  }
1283
- async function listTopicProjectOverlays2(ctx, options = {}) {
480
+ async function listTopicProjectOverlays(ctx, options = {}) {
1284
481
  let allTopics = [];
1285
482
  if (ctx?.db?.query && typeof ctx.db.query === "function") {
1286
483
  try {
@@ -1290,18 +487,18 @@ async function listTopicProjectOverlays2(ctx, options = {}) {
1290
487
  }
1291
488
  }
1292
489
  if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
1293
- allTopics = (await ctx.runQuery(api2.topics.list, {}) ?? []) || [];
490
+ allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
1294
491
  }
1295
492
  return allTopics.filter(
1296
- (topic) => options.projectLikeOnly === false || isProjectLikeTopic2(topic)
1297
- ).map((topic) => materializeTopicProjectOverlay2(topic, options.idMode));
493
+ (topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
494
+ ).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
1298
495
  }
1299
496
  async function patchTopicProjectOverlay(ctx, scopeId, value) {
1300
- const topic = await resolveTopicDoc2(ctx, scopeId);
497
+ const topic = await resolveTopicDoc(ctx, scopeId);
1301
498
  if (!topic) {
1302
499
  return null;
1303
500
  }
1304
- const nextMetadata = { ...readMetadata2(topic) };
501
+ const nextMetadata = { ...readMetadata(topic) };
1305
502
  const patch = {};
1306
503
  const topicUpdateArgs = {
1307
504
  id: String(topic._id)
@@ -1326,7 +523,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1326
523
  `patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
1327
524
  );
1328
525
  case "status": {
1329
- const status = coerceStatus2(rawValue);
526
+ const status = coerceStatus(rawValue);
1330
527
  if (status) {
1331
528
  patch.status = status;
1332
529
  topicUpdateArgs.status = status;
@@ -1334,7 +531,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1334
531
  break;
1335
532
  }
1336
533
  case "visibility": {
1337
- const visibility = coerceVisibility2(rawValue);
534
+ const visibility = coerceVisibility(rawValue);
1338
535
  if (visibility) {
1339
536
  patch.visibility = visibility;
1340
537
  topicUpdateArgs.visibility = visibility;
@@ -1342,7 +539,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1342
539
  break;
1343
540
  }
1344
541
  case "type": {
1345
- const projectType = readNonEmptyString2(rawValue);
542
+ const projectType = readNonEmptyString(rawValue);
1346
543
  if (projectType) {
1347
544
  nextMetadata.projectType = projectType;
1348
545
  } else {
@@ -1366,7 +563,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1366
563
  topicUpdateArgs.metadata = nextMetadata;
1367
564
  if (typeof ctx.runMutation === "function") {
1368
565
  try {
1369
- await ctx.runMutation(api2.topics.update, topicUpdateArgs);
566
+ await ctx.runMutation(api.topics.update, topicUpdateArgs);
1370
567
  } catch (error) {
1371
568
  if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
1372
569
  throw error;
@@ -1380,7 +577,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
1380
577
  "Cannot patch topic without component adapter (ctx.runMutation unavailable)"
1381
578
  );
1382
579
  }
1383
- return materializeTopicProjectOverlay2(
580
+ return materializeTopicProjectOverlay(
1384
581
  {
1385
582
  ...topic,
1386
583
  ...patch,
@@ -1413,10 +610,10 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
1413
610
  });
1414
611
  }
1415
612
  }
1416
- function defaultResolvers2() {
613
+ function defaultResolvers() {
1417
614
  return {
1418
615
  async getProject(ctx, projectId) {
1419
- return await resolveTopicProjectOverlay2(ctx, projectId, {
616
+ return await resolveTopicProjectOverlay(ctx, projectId, {
1420
617
  idMode: "legacy",
1421
618
  projectLikeOnly: false
1422
619
  });
@@ -1425,7 +622,7 @@ function defaultResolvers2() {
1425
622
  await patchProjectWithTolerance(ctx, projectId, value);
1426
623
  },
1427
624
  async listTopics(ctx) {
1428
- return await listTopicProjectOverlays2(ctx, {
625
+ return await listTopicProjectOverlays(ctx, {
1429
626
  idMode: "legacy"
1430
627
  });
1431
628
  },
@@ -1434,24 +631,24 @@ function defaultResolvers2() {
1434
631
  }
1435
632
  };
1436
633
  }
1437
- var resolverOverrides2 = {};
634
+ var resolverOverrides = {};
1438
635
  function resolveGraphPrimitivesAppResolvers(_ctx) {
1439
636
  return {
1440
- ...defaultResolvers2(),
1441
- ...resolverOverrides2
637
+ ...defaultResolvers(),
638
+ ...resolverOverrides
1442
639
  };
1443
640
  }
1444
- var LEGACY_SCOPE_FIELD3 = "graphScopeProjectId";
641
+ var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
1445
642
  function asMappedProjectId(topic) {
1446
643
  if (!topic) {
1447
644
  return;
1448
645
  }
1449
- const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD3]);
646
+ const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
1450
647
  if (directLegacyProjectId) {
1451
648
  return directLegacyProjectId;
1452
649
  }
1453
650
  const metadata = topic.metadata || {};
1454
- const candidate = metadata[LEGACY_SCOPE_FIELD3] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
651
+ const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
1455
652
  return candidate ? candidate : void 0;
1456
653
  }
1457
654
  function normalizeScopeValue(value) {
@@ -1480,7 +677,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
1480
677
  try {
1481
678
  return await ctx.db.query("topics").withIndex(
1482
679
  "by_graph_scope_project",
1483
- (q) => q.eq(LEGACY_SCOPE_FIELD3, scopeId)
680
+ (q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
1484
681
  ).collect();
1485
682
  } catch {
1486
683
  const topics = await ctx.db.query("topics").collect();
@@ -1496,7 +693,7 @@ async function tryResolveHostTopicById(ctx, topicId) {
1496
693
  return null;
1497
694
  }
1498
695
  try {
1499
- return await ctx.runQuery(api2.topics.get, {
696
+ return await ctx.runQuery(api.topics.get, {
1500
697
  id: topicId
1501
698
  }) ?? null;
1502
699
  } catch {
@@ -1508,7 +705,7 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
1508
705
  return null;
1509
706
  }
1510
707
  try {
1511
- return await ctx.runQuery(api2.topics.getByLegacyScopeId, {
708
+ return await ctx.runQuery(api.topics.getByLegacyScopeId, {
1512
709
  projectId: legacyScopeId
1513
710
  }) ?? null;
1514
711
  } catch {