@lucern/graph-sync 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.
@@ -0,0 +1,523 @@
1
+ "use node";
2
+ import { v } from 'convex/values';
3
+ import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
4
+ import { getNeo4jRelType } from '@lucern/graph-primitives/graphTypes';
5
+ import { componentsGeneric, anyApi, internalActionGeneric } from 'convex/server';
6
+ import neo4j from 'neo4j-driver';
7
+
8
+ componentsGeneric();
9
+ var internal = anyApi;
10
+ var internalAction = internalActionGeneric;
11
+ var driver = null;
12
+ function getDriver() {
13
+ if (!driver) {
14
+ const uri = process.env.NEO4J_URI;
15
+ const user = process.env.NEO4J_USER;
16
+ const password = process.env.NEO4J_PASSWORD;
17
+ if (!uri || !user || !password) {
18
+ throw new Error(
19
+ "[Neo4j Driver] Missing credentials. Set NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD via `npx convex env set`"
20
+ );
21
+ }
22
+ driver = neo4j.driver(uri, neo4j.auth.basic(user, password), {
23
+ // Connection pool settings
24
+ maxConnectionPoolSize: 50,
25
+ connectionAcquisitionTimeout: 3e4,
26
+ // Logging
27
+ logging: {
28
+ level: "warn",
29
+ logger: (level, message) => console.log(`[Neo4j ${level}] ${message}`)
30
+ }
31
+ });
32
+ }
33
+ return driver;
34
+ }
35
+ var DEFAULT_QUERY_TIMEOUT_MS = 3e4;
36
+ function toNeo4jParams(params) {
37
+ const result = {};
38
+ for (const [key, value] of Object.entries(params)) {
39
+ if (typeof value === "number" && Number.isInteger(value)) {
40
+ result[key] = neo4j.int(value);
41
+ } else if (Array.isArray(value)) {
42
+ result[key] = value.map(
43
+ (v2) => typeof v2 === "number" && Number.isInteger(v2) ? neo4j.int(v2) : v2
44
+ );
45
+ } else {
46
+ result[key] = value;
47
+ }
48
+ }
49
+ return result;
50
+ }
51
+ async function runCypher(query, params = {}, timeoutMs = DEFAULT_QUERY_TIMEOUT_MS) {
52
+ const neo4jDriver = getDriver();
53
+ const session = neo4jDriver.session();
54
+ try {
55
+ const neo4jParams = toNeo4jParams(params);
56
+ const result = await session.run(query, neo4jParams, {
57
+ timeout: neo4j.int(timeoutMs)
58
+ });
59
+ return result.records.map((record) => {
60
+ const obj = {};
61
+ for (const key of record.keys) {
62
+ const field = String(key);
63
+ obj[field] = convertNeo4jValue(record.get(field));
64
+ }
65
+ return obj;
66
+ });
67
+ } finally {
68
+ await session.close();
69
+ }
70
+ }
71
+ async function runWriteTransaction(query, params = {}, timeoutMs = DEFAULT_QUERY_TIMEOUT_MS) {
72
+ const neo4jDriver = getDriver();
73
+ const session = neo4jDriver.session();
74
+ try {
75
+ const neo4jParams = toNeo4jParams(params);
76
+ const result = await session.executeWrite(
77
+ async (tx) => {
78
+ return await tx.run(query, neo4jParams);
79
+ },
80
+ { timeout: timeoutMs }
81
+ );
82
+ return result.records.map((record) => {
83
+ const obj = {};
84
+ for (const key of record.keys) {
85
+ const field = String(key);
86
+ obj[field] = convertNeo4jValue(record.get(field));
87
+ }
88
+ return obj;
89
+ });
90
+ } finally {
91
+ await session.close();
92
+ }
93
+ }
94
+ function getConnectionInfo() {
95
+ return {
96
+ uri: process.env.NEO4J_URI,
97
+ user: process.env.NEO4J_USER,
98
+ configured: Boolean(
99
+ process.env.NEO4J_URI && process.env.NEO4J_USER && process.env.NEO4J_PASSWORD
100
+ )
101
+ };
102
+ }
103
+ function convertNeo4jValue(value) {
104
+ if (value === null || value === void 0) {
105
+ return null;
106
+ }
107
+ if (neo4j.isInt(value)) {
108
+ return neo4j.integer.toNumber(value);
109
+ }
110
+ if (neo4j.isDate(value) || neo4j.isDateTime(value) || neo4j.isTime(value)) {
111
+ return value.toString();
112
+ }
113
+ if (Array.isArray(value)) {
114
+ return value.map(convertNeo4jValue);
115
+ }
116
+ if (value && typeof value === "object" && "properties" in value) {
117
+ const nodeObj = value;
118
+ const result = {};
119
+ for (const [k, v2] of Object.entries(nodeObj.properties)) {
120
+ result[k] = convertNeo4jValue(v2);
121
+ }
122
+ return result;
123
+ }
124
+ if (typeof value === "object") {
125
+ const result = {};
126
+ for (const [k, v2] of Object.entries(value)) {
127
+ result[k] = convertNeo4jValue(v2);
128
+ }
129
+ return result;
130
+ }
131
+ return value;
132
+ }
133
+
134
+ // src/neo4jEdgeAPI.ts
135
+ var DUAL_WRITE_EDGE_TYPES = [
136
+ "supports",
137
+ "informs",
138
+ "tests",
139
+ "depends_on",
140
+ "derived_from",
141
+ "contains",
142
+ "supersedes",
143
+ "extracted_from",
144
+ "responds_to",
145
+ "based_on",
146
+ "answers",
147
+ "belongs_to",
148
+ "relates_to_thesis",
149
+ "corroborates",
150
+ "extends",
151
+ "same_source_as",
152
+ "same_theme_as",
153
+ "plays_theme",
154
+ "impacts",
155
+ "evaluates",
156
+ "mentioned_in",
157
+ "perspective_on"
158
+ ];
159
+ function needsDualWrite(edgeType) {
160
+ return DUAL_WRITE_EDGE_TYPES.includes(edgeType);
161
+ }
162
+ function normalizeEdgeType(edgeType) {
163
+ const normalized = edgeType.trim().toLowerCase();
164
+ if (!/^[a-z0-9_]+$/u.test(normalized)) {
165
+ throw new Error(`[Neo4j Edge API] Invalid edge type: ${edgeType}`);
166
+ }
167
+ return normalized;
168
+ }
169
+ function resolveRelationshipType(edgeType) {
170
+ const relType = getNeo4jRelType(edgeType);
171
+ if (!/^[A-Z0-9_]+$/u.test(relType)) {
172
+ throw new Error(`[Neo4j Edge API] Invalid relationship type: ${relType}`);
173
+ }
174
+ return relType;
175
+ }
176
+ function readStringProperty(source, key) {
177
+ const value = source?.[key];
178
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
179
+ }
180
+ function readNumberProperty(source, key) {
181
+ const value = source?.[key];
182
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
183
+ }
184
+ function metadataSummary(metadata) {
185
+ if (!metadata) {
186
+ return void 0;
187
+ }
188
+ return Object.entries(metadata).map(([key, value]) => `${key}=${String(value)}`).slice(0, 8).join(" | ");
189
+ }
190
+ async function mirrorEdgeToConvex(ctx, args) {
191
+ await ctx.runMutation(internal.epistemicEdges.mirrorEdgeToConvex, args);
192
+ }
193
+ async function queueEdgeRetry(ctx, args) {
194
+ await ctx.runMutation(internal.neo4jSyncHelpers.queueForRetry, {
195
+ entityType: "edge",
196
+ entityId: args.globalId,
197
+ operation: args.operation,
198
+ error: args.error
199
+ });
200
+ }
201
+ var createEdge = internalAction({
202
+ args: {
203
+ globalId: v.string(),
204
+ fromGlobalId: v.string(),
205
+ toGlobalId: v.string(),
206
+ edgeType: v.string(),
207
+ weight: v.optional(v.number()),
208
+ confidence: v.optional(v.number()),
209
+ context: v.optional(v.string()),
210
+ derivationType: v.optional(v.string()),
211
+ metadata: v.optional(v.any()),
212
+ createdBy: v.string(),
213
+ topicId: v.optional(v.string()),
214
+ tenantId: v.optional(v.string()),
215
+ workspaceId: v.optional(v.string()),
216
+ fromLayer: v.optional(v.string()),
217
+ toLayer: v.optional(v.string()),
218
+ fromNodeType: v.optional(v.string()),
219
+ toNodeType: v.optional(v.string()),
220
+ reasoningMethod: v.optional(v.string()),
221
+ logicalRole: v.optional(v.string()),
222
+ temporalClass: v.optional(v.string()),
223
+ validFrom: v.optional(v.number()),
224
+ validUntil: v.optional(v.number())
225
+ },
226
+ returns: permissiveReturn,
227
+ handler: async (ctx, args) => {
228
+ const connInfo = getConnectionInfo();
229
+ if (!connInfo.configured) {
230
+ throw new Error(
231
+ "[Neo4j Edge API] Neo4j not configured. Set NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD"
232
+ );
233
+ }
234
+ const edgeType = normalizeEdgeType(args.edgeType);
235
+ const relType = resolveRelationshipType(edgeType);
236
+ const metadata = args.metadata && typeof args.metadata === "object" ? args.metadata : void 0;
237
+ const now = Date.now();
238
+ const properties = {
239
+ globalId: args.globalId,
240
+ edgeType,
241
+ weight: args.weight ?? 1,
242
+ confidence: args.confidence ?? readNumberProperty(metadata, "confidence") ?? 1,
243
+ context: args.context ?? metadataSummary(metadata) ?? "",
244
+ derivationType: args.derivationType ?? "",
245
+ createdBy: args.createdBy,
246
+ createdAt: now,
247
+ updatedAt: now,
248
+ topicId: args.topicId ?? readStringProperty(metadata, "topicId") ?? "",
249
+ tenantId: args.tenantId ?? readStringProperty(metadata, "tenantId") ?? "",
250
+ workspaceId: args.workspaceId ?? readStringProperty(metadata, "workspaceId") ?? "",
251
+ fromLayer: args.fromLayer ?? "",
252
+ toLayer: args.toLayer ?? "",
253
+ fromNodeType: args.fromNodeType ?? "",
254
+ toNodeType: args.toNodeType ?? "",
255
+ reasoningMethod: args.reasoningMethod ?? "",
256
+ logicalRole: args.logicalRole ?? "",
257
+ temporalClass: args.temporalClass ?? "structural",
258
+ validFrom: args.validFrom ?? now,
259
+ validUntil: args.validUntil ?? null
260
+ };
261
+ const result = await runWriteTransaction(
262
+ `
263
+ MATCH (from {globalId: $fromGlobalId})
264
+ MATCH (to {globalId: $toGlobalId})
265
+ MERGE (from)-[r:${relType} {globalId: $globalId}]->(to)
266
+ SET r += $properties
267
+ RETURN r.globalId as globalId
268
+ `,
269
+ {
270
+ fromGlobalId: args.fromGlobalId,
271
+ toGlobalId: args.toGlobalId,
272
+ globalId: args.globalId,
273
+ properties
274
+ }
275
+ );
276
+ if (result.length === 0) {
277
+ await queueEdgeRetry(ctx, {
278
+ globalId: args.globalId,
279
+ operation: "upsert",
280
+ error: `Source or target node not yet synced to Neo4j (from: ${args.fromGlobalId}, to: ${args.toGlobalId})`
281
+ });
282
+ if (needsDualWrite(edgeType)) {
283
+ try {
284
+ await mirrorEdgeToConvex(ctx, {
285
+ globalId: args.globalId,
286
+ fromGlobalId: args.fromGlobalId,
287
+ toGlobalId: args.toGlobalId,
288
+ edgeType,
289
+ weight: args.weight,
290
+ confidence: args.confidence,
291
+ context: args.context,
292
+ derivationType: args.derivationType,
293
+ createdBy: args.createdBy,
294
+ topicId: args.topicId,
295
+ fromLayer: args.fromLayer,
296
+ toLayer: args.toLayer,
297
+ fromNodeType: args.fromNodeType,
298
+ toNodeType: args.toNodeType,
299
+ reasoningMethod: args.reasoningMethod,
300
+ logicalRole: args.logicalRole,
301
+ temporalClass: args.temporalClass,
302
+ validFrom: args.validFrom,
303
+ validUntil: args.validUntil
304
+ });
305
+ } catch {
306
+ }
307
+ }
308
+ return {
309
+ success: false,
310
+ globalId: args.globalId,
311
+ edgeType,
312
+ queuedForRetry: true,
313
+ reason: "nodes_not_synced"
314
+ };
315
+ }
316
+ if (needsDualWrite(edgeType)) {
317
+ try {
318
+ await mirrorEdgeToConvex(ctx, {
319
+ globalId: args.globalId,
320
+ fromGlobalId: args.fromGlobalId,
321
+ toGlobalId: args.toGlobalId,
322
+ edgeType,
323
+ weight: args.weight,
324
+ confidence: args.confidence,
325
+ context: args.context,
326
+ derivationType: args.derivationType,
327
+ createdBy: args.createdBy,
328
+ topicId: args.topicId,
329
+ fromLayer: args.fromLayer,
330
+ toLayer: args.toLayer,
331
+ fromNodeType: args.fromNodeType,
332
+ toNodeType: args.toNodeType,
333
+ reasoningMethod: args.reasoningMethod,
334
+ logicalRole: args.logicalRole,
335
+ temporalClass: args.temporalClass,
336
+ validFrom: args.validFrom,
337
+ validUntil: args.validUntil
338
+ });
339
+ } catch (error) {
340
+ await queueEdgeRetry(ctx, {
341
+ globalId: args.globalId,
342
+ operation: "upsert",
343
+ error: `Convex mirror failed: ${error instanceof Error ? error.message : "Unknown error"}`
344
+ });
345
+ }
346
+ }
347
+ return {
348
+ success: true,
349
+ globalId: args.globalId,
350
+ edgeType,
351
+ dualWritten: needsDualWrite(edgeType)
352
+ };
353
+ }
354
+ });
355
+ var deleteEdge = internalAction({
356
+ args: {
357
+ globalId: v.string()
358
+ },
359
+ returns: permissiveReturn,
360
+ handler: async (ctx, args) => {
361
+ const connInfo = getConnectionInfo();
362
+ if (!connInfo.configured) {
363
+ throw new Error("[Neo4j Edge API] Neo4j not configured");
364
+ }
365
+ await runWriteTransaction(
366
+ `
367
+ MATCH ()-[r {globalId: $globalId}]->()
368
+ DELETE r
369
+ `,
370
+ { globalId: args.globalId }
371
+ );
372
+ try {
373
+ await ctx.runMutation(internal.epistemicEdges.deleteEdgeFromConvex, {
374
+ globalId: args.globalId
375
+ });
376
+ } catch {
377
+ }
378
+ return { success: true };
379
+ }
380
+ });
381
+ var updateEdge = internalAction({
382
+ args: {
383
+ globalId: v.string(),
384
+ weight: v.optional(v.number()),
385
+ confidence: v.optional(v.number()),
386
+ context: v.optional(v.string()),
387
+ derivationType: v.optional(v.string())
388
+ },
389
+ returns: permissiveReturn,
390
+ handler: async (ctx, args) => {
391
+ const connInfo = getConnectionInfo();
392
+ if (!connInfo.configured) {
393
+ throw new Error("[Neo4j Edge API] Neo4j not configured");
394
+ }
395
+ const updates = { updatedAt: Date.now() };
396
+ if (args.weight !== void 0) updates.weight = args.weight;
397
+ if (args.confidence !== void 0) updates.confidence = args.confidence;
398
+ if (args.context !== void 0) updates.context = args.context;
399
+ if (args.derivationType !== void 0) {
400
+ updates.derivationType = args.derivationType;
401
+ }
402
+ const result = await runWriteTransaction(
403
+ `
404
+ MATCH ()-[r {globalId: $globalId}]->()
405
+ SET r += $updates
406
+ RETURN true as updated, r.edgeType as edgeType
407
+ `,
408
+ { globalId: args.globalId, updates }
409
+ );
410
+ if (result.length === 0) {
411
+ return { success: false, error: "Edge not found" };
412
+ }
413
+ const edgeType = result[0]?.edgeType;
414
+ if (edgeType && needsDualWrite(edgeType)) {
415
+ try {
416
+ await ctx.runMutation(internal.epistemicEdges.updateEdgeInConvex, {
417
+ globalId: args.globalId,
418
+ weight: args.weight,
419
+ confidence: args.confidence,
420
+ context: args.context,
421
+ derivationType: args.derivationType
422
+ });
423
+ } catch {
424
+ await queueEdgeRetry(ctx, {
425
+ globalId: args.globalId,
426
+ operation: "upsert",
427
+ error: "Convex mirror update failed"
428
+ });
429
+ }
430
+ }
431
+ return { success: true, globalId: args.globalId };
432
+ }
433
+ });
434
+ var getEdge = internalAction({
435
+ args: {
436
+ globalId: v.string()
437
+ },
438
+ returns: permissiveReturn,
439
+ handler: async (_ctx, args) => {
440
+ const connInfo = getConnectionInfo();
441
+ if (!connInfo.configured) {
442
+ return null;
443
+ }
444
+ const result = await runCypher(
445
+ `
446
+ MATCH (from)-[r {globalId: $globalId}]->(to)
447
+ RETURN r.globalId as globalId,
448
+ r.edgeType as edgeType,
449
+ from.globalId as fromGlobalId,
450
+ to.globalId as toGlobalId,
451
+ properties(r) as properties
452
+ LIMIT 1
453
+ `,
454
+ { globalId: args.globalId }
455
+ );
456
+ return result[0] ?? null;
457
+ }
458
+ });
459
+ var retryProjectionByGlobalId = internalAction({
460
+ args: {
461
+ globalId: v.string()
462
+ },
463
+ returns: permissiveReturn,
464
+ handler: async (ctx, args) => {
465
+ const connInfo = getConnectionInfo();
466
+ if (!connInfo.configured) {
467
+ return { success: false, error: "[Neo4j Edge API] Neo4j not configured" };
468
+ }
469
+ const result = await runCypher(
470
+ `
471
+ MATCH (from)-[r {globalId: $globalId}]->(to)
472
+ RETURN r.edgeType as edgeType,
473
+ from.globalId as fromGlobalId,
474
+ to.globalId as toGlobalId,
475
+ properties(r) as properties
476
+ LIMIT 1
477
+ `,
478
+ { globalId: args.globalId }
479
+ );
480
+ if (result.length === 0) {
481
+ return {
482
+ success: false,
483
+ error: `[Neo4j Edge API] Edge not found in Neo4j: ${args.globalId}`
484
+ };
485
+ }
486
+ const edge = result[0];
487
+ if (!needsDualWrite(edge.edgeType)) {
488
+ return {
489
+ success: true,
490
+ skipped: true,
491
+ reason: "edge_type_not_projected",
492
+ edgeType: edge.edgeType
493
+ };
494
+ }
495
+ const props = edge.properties || {};
496
+ await mirrorEdgeToConvex(ctx, {
497
+ globalId: args.globalId,
498
+ fromGlobalId: edge.fromGlobalId,
499
+ toGlobalId: edge.toGlobalId,
500
+ edgeType: edge.edgeType,
501
+ weight: readNumberProperty(props, "weight"),
502
+ confidence: readNumberProperty(props, "confidence"),
503
+ context: readStringProperty(props, "context"),
504
+ derivationType: readStringProperty(props, "derivationType"),
505
+ createdBy: readStringProperty(props, "createdBy") ?? "neo4j_projection_retry",
506
+ topicId: readStringProperty(props, "topicId"),
507
+ fromLayer: readStringProperty(props, "fromLayer"),
508
+ toLayer: readStringProperty(props, "toLayer"),
509
+ fromNodeType: readStringProperty(props, "fromNodeType"),
510
+ toNodeType: readStringProperty(props, "toNodeType"),
511
+ reasoningMethod: readStringProperty(props, "reasoningMethod"),
512
+ logicalRole: readStringProperty(props, "logicalRole"),
513
+ temporalClass: readStringProperty(props, "temporalClass"),
514
+ validFrom: readNumberProperty(props, "validFrom"),
515
+ validUntil: readNumberProperty(props, "validUntil")
516
+ });
517
+ return { success: true, globalId: args.globalId, projected: true };
518
+ }
519
+ });
520
+
521
+ export { DUAL_WRITE_EDGE_TYPES, createEdge, deleteEdge, getEdge, needsDualWrite, retryProjectionByGlobalId, updateEdge };
522
+ //# sourceMappingURL=neo4jEdgeAPI.js.map
523
+ //# sourceMappingURL=neo4jEdgeAPI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/convex.ts","../src/neo4jDriver.ts","../src/neo4jEdgeAPI.ts"],"names":["v"],"mappings":"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","file":"neo4jEdgeAPI.js","sourcesContent":["import {\n actionGeneric,\n anyApi,\n componentsGeneric,\n httpActionGeneric,\n internalActionGeneric,\n internalMutationGeneric,\n internalQueryGeneric,\n mutationGeneric,\n queryGeneric,\n} from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nexport const api = anyApi as any;\nexport const components = componentsGeneric() as any;\nexport const internal = anyApi as any;\n\nexport type TableNames = string;\nexport type Id<TableName extends TableNames = string> = GenericId<TableName>;\nexport type Doc<TableName extends TableNames = string> = any;\nexport type DataModel = any;\nexport type ActionCtx = any;\nexport type DatabaseReader = any;\nexport type DatabaseWriter = any;\nexport type MutationCtx = any;\nexport type QueryCtx = any;\n\ntype ConvexFunctionBuilder = <Definition extends {\n handler?: (...args: any[]) => any;\n}>(\n definition: Definition\n) => any;\n\nexport const action = actionGeneric as unknown as ConvexFunctionBuilder;\nexport const httpAction = httpActionGeneric as unknown as ConvexFunctionBuilder;\nexport const internalAction =\n internalActionGeneric as unknown as ConvexFunctionBuilder;\nexport const internalMutation =\n internalMutationGeneric as unknown as ConvexFunctionBuilder;\nexport const internalQuery =\n internalQueryGeneric as unknown as ConvexFunctionBuilder;\nexport const mutation = mutationGeneric as unknown as ConvexFunctionBuilder;\nexport const query = queryGeneric as unknown as ConvexFunctionBuilder;\n","/**\n * neo4jDriver module implementation.\n */\n\n\"use node\";\n/**\n * Direct Neo4j Driver for Convex\n *\n * Uses the \"use node\" directive to enable Node.js runtime, allowing\n * direct use of the neo4j-driver package instead of HTTP proxies.\n *\n * Environment Variables (set per deployment via `npx convex env set`):\n * - NEO4J_URI: neo4j+s://xxx.databases.neo4j.io\n * - NEO4J_USER: neo4j\n * - NEO4J_PASSWORD: your-password\n *\n * @see /docs/architecture/UNIFIED_GRAPH_ARCHITECTURE.md\n */\n\nimport neo4j, { type Driver } from \"neo4j-driver\";\n\n// =============================================================================\n// VALID LABELS AND RELATIONSHIP TYPES (Security: Prevent Cypher Injection)\n// =============================================================================\n\nconst VALID_NODE_LABELS = new Set([\n // Ontological\n \"ValueChain\",\n \"Function\",\n \"FinSector\",\n \"Company\",\n \"Person\",\n \"Investor\",\n // Epistemic\n \"Theme\",\n \"Belief\",\n \"Question\",\n \"Evidence\",\n \"Source\",\n \"Decision\",\n \"Sprint\",\n \"Claim\",\n \"Synthesis\",\n \"Answer\",\n]);\n\nconst VALID_RELATIONSHIP_TYPES = new Set([\n // Cross-layer edges\n \"EXTRACTED_FROM\",\n \"ANSWERS\",\n \"RESPONDS_TO\",\n \"INFORMS\",\n \"QUALIFIES\",\n \"TESTS\",\n \"EXPLORES\",\n \"BASED_ON\",\n \"RELATES_TO_THESIS\",\n \"BELONGS_TO\",\n \"PLAYS_THEME\",\n // Same-layer edges\n \"SUPERSEDES\",\n \"SAME_AS\",\n \"DEPENDS_ON\",\n \"REINFORCES\",\n \"PARENT_OF\",\n \"CHILD_OF\",\n \"FALSIFIED_BY\",\n \"EXCLUSIVE_WITH\",\n \"COLLAPSES_IF\",\n \"CASCADE_FROM\",\n \"STRENGTHENED_BY\",\n \"WEAKENED_BY\",\n \"ALTERNATIVE_TO\",\n \"SUBSUMES\",\n \"VALIDATED_BY\",\n \"REQUIRED_FOR\",\n \"PREREQUISITE_FOR\",\n \"PARALLEL_TO\",\n \"CORROBORATES\",\n \"EXTENDS\",\n \"SAME_SOURCE_AS\",\n \"SAME_THEME_AS\",\n // Ontological\n \"EVALUATES\",\n \"PERSPECTIVE_ON\",\n \"WORKS_AT\",\n \"PARTICIPATES_IN\",\n \"PERFORMS\",\n \"FUNCTION_IN\",\n \"IMPACTS\",\n \"INVESTED_IN\",\n \"RAISED_FROM\",\n \"BASED_ON_BELIEF\",\n \"BASED_ON_QUESTION\",\n \"BLOCKED_BY_CONTRADICTION\",\n \"INFORMED_BY_THEME\",\n]);\n\nexport function validateLabel(label: string): void {\n if (!VALID_NODE_LABELS.has(label)) {\n throw new Error(\n `[Neo4j Security] Invalid node label: ${label}. Must be one of: ${Array.from(VALID_NODE_LABELS).join(\", \")}`\n );\n }\n}\n\nexport function validateRelType(relType: string): void {\n if (!VALID_RELATIONSHIP_TYPES.has(relType)) {\n throw new Error(\n `[Neo4j Security] Invalid relationship type: ${relType}. Must be one of: ${Array.from(VALID_RELATIONSHIP_TYPES).join(\", \")}`\n );\n }\n}\n\n// =============================================================================\n// DRIVER SINGLETON\n// =============================================================================\n\nlet driver: Driver | null = null;\n\nfunction getDriver(): Driver {\n if (!driver) {\n const uri = process.env.NEO4J_URI;\n const user = process.env.NEO4J_USER;\n const password = process.env.NEO4J_PASSWORD;\n\n if (!uri || !user || !password) {\n throw new Error(\n \"[Neo4j Driver] Missing credentials. Set NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD via `npx convex env set`\"\n );\n }\n\n driver = neo4j.driver(uri, neo4j.auth.basic(user, password), {\n // Connection pool settings\n maxConnectionPoolSize: 50,\n connectionAcquisitionTimeout: 30_000,\n // Logging\n logging: {\n level: \"warn\",\n logger: (level, message) => console.log(`[Neo4j ${level}] ${message}`),\n },\n });\n }\n return driver;\n}\n\n// =============================================================================\n// QUERY CONFIGURATION\n// =============================================================================\n\n/**\n * Default query timeout in milliseconds.\n * Prevents expensive graph traversals from hanging indefinitely.\n */\nexport const DEFAULT_QUERY_TIMEOUT_MS = 30_000; // 30 seconds\n\n/**\n * Timeout for complex graph queries (cascade simulation, lineage traversal)\n */\nexport const COMPLEX_QUERY_TIMEOUT_MS = 60_000; // 60 seconds\n\n// =============================================================================\n// QUERY EXECUTION\n// =============================================================================\n\n/**\n * Convert JavaScript values to Neo4j-compatible types\n * Neo4j requires explicit integers, not floats\n */\nfunction toNeo4jParams(\n params: Record<string, unknown>\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(params)) {\n if (typeof value === \"number\" && Number.isInteger(value)) {\n // Convert JavaScript integers to Neo4j integers\n result[key] = neo4j.int(value);\n } else if (Array.isArray(value)) {\n result[key] = value.map((v) =>\n typeof v === \"number\" && Number.isInteger(v) ? neo4j.int(v) : v\n );\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * Execute a Cypher query and return results as typed objects\n *\n * @param query - Cypher query string\n * @param params - Query parameters\n * @param timeoutMs - Query timeout in milliseconds (default: 30s)\n */\nexport async function runCypher<T = Record<string, unknown>>(\n query: string,\n params: Record<string, unknown> = {},\n timeoutMs: number = DEFAULT_QUERY_TIMEOUT_MS\n): Promise<T[]> {\n const neo4jDriver = getDriver();\n const session = neo4jDriver.session();\n\n try {\n const neo4jParams = toNeo4jParams(params);\n const result = await session.run(query, neo4jParams, {\n timeout: neo4j.int(timeoutMs),\n });\n return result.records.map((record) => {\n const obj: Record<string, unknown> = {};\n for (const key of record.keys) {\n const field = String(key);\n obj[field] = convertNeo4jValue(record.get(field));\n }\n return obj as T;\n });\n } finally {\n await session.close();\n }\n}\n\n/**\n * Execute a write transaction (for mutations)\n *\n * @param query - Cypher query string\n * @param params - Query parameters\n * @param timeoutMs - Transaction timeout in milliseconds (default: 30s)\n */\nexport async function runWriteTransaction<T = Record<string, unknown>>(\n query: string,\n params: Record<string, unknown> = {},\n timeoutMs: number = DEFAULT_QUERY_TIMEOUT_MS\n): Promise<T[]> {\n const neo4jDriver = getDriver();\n const session = neo4jDriver.session();\n\n try {\n const neo4jParams = toNeo4jParams(params);\n const result = await session.executeWrite(\n async (tx) => {\n return await tx.run(query, neo4jParams);\n },\n { timeout: timeoutMs }\n );\n return result.records.map((record) => {\n const obj: Record<string, unknown> = {};\n for (const key of record.keys) {\n const field = String(key);\n obj[field] = convertNeo4jValue(record.get(field));\n }\n return obj as T;\n });\n } finally {\n await session.close();\n }\n}\n\n/**\n * Execute multiple queries in a single transaction\n *\n * @param queries - Array of queries with parameters\n * @param timeoutMs - Transaction timeout in milliseconds (default: 60s for batch)\n */\nexport async function runBatchTransaction(\n queries: Array<{ query: string; params: Record<string, unknown> }>,\n timeoutMs: number = COMPLEX_QUERY_TIMEOUT_MS\n): Promise<void> {\n const neo4jDriver = getDriver();\n const session = neo4jDriver.session();\n\n try {\n await session.executeWrite(\n async (tx) => {\n for (const { query, params } of queries) {\n await tx.run(query, params);\n }\n },\n { timeout: timeoutMs }\n );\n } finally {\n await session.close();\n }\n}\n\n// =============================================================================\n// NODE OPERATIONS\n// =============================================================================\n\n/**\n * Upsert a node by globalId\n */\nexport async function upsertNode(\n label: string,\n globalId: string,\n properties: Record<string, unknown>\n): Promise<void> {\n validateLabel(label); // Security: prevent Cypher injection\n await runWriteTransaction(\n `\n MERGE (n:${label} {globalId: $globalId})\n SET n += $properties\n SET n.updatedAt = timestamp()\n `,\n { globalId, properties }\n );\n}\n\n/**\n * Delete a node by globalId\n */\nexport async function deleteNode(globalId: string): Promise<void> {\n await runWriteTransaction(\n `\n MATCH (n {globalId: $globalId})\n DETACH DELETE n\n `,\n { globalId }\n );\n}\n\n/**\n * Batch upsert nodes\n */\nexport async function batchUpsertNodes(\n label: string,\n nodes: Array<{ globalId: string; properties: Record<string, unknown> }>\n): Promise<void> {\n if (nodes.length === 0) {\n return;\n }\n\n validateLabel(label); // Security: prevent Cypher injection\n await runWriteTransaction(\n `\n UNWIND $nodes as node\n MERGE (n:${label} {globalId: node.globalId})\n SET n += node.properties\n SET n.updatedAt = timestamp()\n `,\n { nodes }\n );\n}\n\n// =============================================================================\n// EDGE OPERATIONS\n// =============================================================================\n\n/**\n * Upsert an edge by globalId\n */\nexport async function upsertEdge(\n relType: string,\n globalId: string,\n fromGlobalId: string,\n toGlobalId: string,\n properties: Record<string, unknown> = {}\n): Promise<void> {\n validateRelType(relType); // Security: prevent Cypher injection\n await runWriteTransaction(\n `\n MATCH (from {globalId: $fromGlobalId})\n MATCH (to {globalId: $toGlobalId})\n MERGE (from)-[r:${relType} {globalId: $globalId}]->(to)\n SET r += $properties\n SET r.updatedAt = timestamp()\n `,\n { globalId, fromGlobalId, toGlobalId, properties }\n );\n}\n\n/**\n * Delete an edge by globalId\n */\nexport async function deleteEdge(globalId: string): Promise<void> {\n await runWriteTransaction(\n `\n MATCH ()-[r {globalId: $globalId}]->()\n DELETE r\n `,\n { globalId }\n );\n}\n\n/**\n * Batch upsert edges\n */\nexport async function batchUpsertEdges(\n edges: Array<{\n relType: string;\n globalId: string;\n fromGlobalId: string;\n toGlobalId: string;\n properties?: Record<string, unknown>;\n }>\n): Promise<void> {\n if (edges.length === 0) {\n return;\n }\n\n // Group by relationship type for efficient batching\n const byType = new Map<string, typeof edges>();\n for (const edge of edges) {\n const existing = byType.get(edge.relType) || [];\n existing.push(edge);\n byType.set(edge.relType, existing);\n }\n\n const queries: Array<{ query: string; params: Record<string, unknown> }> = [];\n for (const [relType, typeEdges] of byType) {\n queries.push({\n query: `\n UNWIND $edges as edge\n MATCH (from {globalId: edge.fromGlobalId})\n MATCH (to {globalId: edge.toGlobalId})\n MERGE (from)-[r:${relType} {globalId: edge.globalId}]->(to)\n SET r += edge.properties\n SET r.updatedAt = timestamp()\n `,\n params: {\n edges: typeEdges.map((e) => ({\n globalId: e.globalId,\n fromGlobalId: e.fromGlobalId,\n toGlobalId: e.toGlobalId,\n properties: e.properties || {},\n })),\n },\n });\n }\n\n await runBatchTransaction(queries);\n}\n\n// =============================================================================\n// HEALTH CHECK\n// =============================================================================\n\n/**\n * Check if Neo4j connection is healthy\n */\nexport async function healthCheck(): Promise<{\n healthy: boolean;\n nodeCount?: number;\n error?: string;\n}> {\n try {\n const result = await runCypher<{ count: number }>(\n \"MATCH (n) RETURN count(n) as count LIMIT 1\"\n );\n return {\n healthy: true,\n nodeCount: result[0]?.count || 0,\n };\n } catch (error) {\n return {\n healthy: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n\n/**\n * Get connection info (for debugging)\n */\nexport function getConnectionInfo(): {\n uri: string | undefined;\n user: string | undefined;\n configured: boolean;\n} {\n return {\n uri: process.env.NEO4J_URI,\n user: process.env.NEO4J_USER,\n configured: Boolean(\n process.env.NEO4J_URI &&\n process.env.NEO4J_USER &&\n process.env.NEO4J_PASSWORD\n ),\n };\n}\n\n// =============================================================================\n// VALUE CONVERSION\n// =============================================================================\n\n/**\n * Convert Neo4j types to plain JavaScript\n */\nfunction convertNeo4jValue(value: unknown): unknown {\n if (value === null || value === undefined) {\n return null;\n }\n\n // Handle Neo4j Integer\n if (neo4j.isInt(value)) {\n return neo4j.integer.toNumber(value);\n }\n\n // Handle Neo4j Date/Time types\n if (neo4j.isDate(value) || neo4j.isDateTime(value) || neo4j.isTime(value)) {\n return value.toString();\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map(convertNeo4jValue);\n }\n\n // Handle Node objects\n if (value && typeof value === \"object\" && \"properties\" in value) {\n const nodeObj = value as { properties: Record<string, unknown> };\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(nodeObj.properties)) {\n result[k] = convertNeo4jValue(v);\n }\n return result;\n }\n\n // Handle plain objects\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n result[k] = convertNeo4jValue(v);\n }\n return result;\n }\n\n return value;\n}\n\n// =============================================================================\n// CLEANUP\n// =============================================================================\n\n/**\n * Close the driver connection (for graceful shutdown)\n */\nexport async function closeDriver(): Promise<void> {\n if (driver) {\n await driver.close();\n driver = null;\n }\n}\n","\"use node\";\n\n/**\n * Neo4j edge API host actions.\n *\n * The reasoning kernel schedules these actions through the tenant Convex host\n * when optional graph mirroring is installed. Neo4j remains authoritative for\n * traversal topology; Convex mirrors selected edge types for realtime reads.\n */\n\nimport { v } from \"convex/values\";\nimport { permissiveReturn } from \"@lucern/contracts/schema-helpers/validators\";\nimport { getNeo4jRelType } from \"@lucern/graph-primitives/graphTypes\";\nimport { internal, internalAction } from \"./convex\";\nimport {\n getConnectionInfo,\n runCypher,\n runWriteTransaction,\n} from \"./neo4jDriver\";\n\nexport const DUAL_WRITE_EDGE_TYPES = [\n \"supports\",\n \"informs\",\n \"tests\",\n \"depends_on\",\n \"derived_from\",\n \"contains\",\n \"supersedes\",\n \"extracted_from\",\n \"responds_to\",\n \"based_on\",\n \"answers\",\n \"belongs_to\",\n \"relates_to_thesis\",\n \"corroborates\",\n \"extends\",\n \"same_source_as\",\n \"same_theme_as\",\n \"plays_theme\",\n \"impacts\",\n \"evaluates\",\n \"mentioned_in\",\n \"perspective_on\",\n] as const;\n\nexport type DualWriteEdgeType = (typeof DUAL_WRITE_EDGE_TYPES)[number];\n\nexport function needsDualWrite(edgeType: string): boolean {\n return DUAL_WRITE_EDGE_TYPES.includes(edgeType as DualWriteEdgeType);\n}\n\nfunction normalizeEdgeType(edgeType: string): string {\n const normalized = edgeType.trim().toLowerCase();\n if (!/^[a-z0-9_]+$/u.test(normalized)) {\n throw new Error(`[Neo4j Edge API] Invalid edge type: ${edgeType}`);\n }\n return normalized;\n}\n\nfunction resolveRelationshipType(edgeType: string): string {\n const relType = getNeo4jRelType(edgeType);\n if (!/^[A-Z0-9_]+$/u.test(relType)) {\n throw new Error(`[Neo4j Edge API] Invalid relationship type: ${relType}`);\n }\n return relType;\n}\n\nfunction readStringProperty(\n source: Record<string, unknown> | undefined,\n key: string\n): string | undefined {\n const value = source?.[key];\n return typeof value === \"string\" && value.trim().length > 0\n ? value.trim()\n : undefined;\n}\n\nfunction readNumberProperty(\n source: Record<string, unknown> | undefined,\n key: string\n): number | undefined {\n const value = source?.[key];\n return typeof value === \"number\" && Number.isFinite(value)\n ? value\n : undefined;\n}\n\nfunction metadataSummary(\n metadata: Record<string, unknown> | undefined\n): string | undefined {\n if (!metadata) {\n return undefined;\n }\n\n return Object.entries(metadata)\n .map(([key, value]) => `${key}=${String(value)}`)\n .slice(0, 8)\n .join(\" | \");\n}\n\nasync function mirrorEdgeToConvex(\n ctx: any,\n args: {\n globalId: string;\n fromGlobalId: string;\n toGlobalId: string;\n edgeType: string;\n weight?: number;\n confidence?: number;\n context?: string;\n derivationType?: string;\n createdBy: string;\n topicId?: string;\n fromLayer?: string;\n toLayer?: string;\n fromNodeType?: string;\n toNodeType?: string;\n reasoningMethod?: string;\n logicalRole?: string;\n temporalClass?: string;\n validFrom?: number;\n validUntil?: number;\n }\n): Promise<void> {\n await ctx.runMutation(internal.epistemicEdges.mirrorEdgeToConvex, args);\n}\n\nasync function queueEdgeRetry(\n ctx: any,\n args: { globalId: string; operation: \"upsert\" | \"delete\"; error: string }\n): Promise<void> {\n await ctx.runMutation(internal.neo4jSyncHelpers.queueForRetry, {\n entityType: \"edge\",\n entityId: args.globalId,\n operation: args.operation,\n error: args.error,\n });\n}\n\nexport const createEdge = internalAction({\n args: {\n globalId: v.string(),\n fromGlobalId: v.string(),\n toGlobalId: v.string(),\n edgeType: v.string(),\n weight: v.optional(v.number()),\n confidence: v.optional(v.number()),\n context: v.optional(v.string()),\n derivationType: v.optional(v.string()),\n metadata: v.optional(v.any()),\n createdBy: v.string(),\n topicId: v.optional(v.string()),\n tenantId: v.optional(v.string()),\n workspaceId: v.optional(v.string()),\n fromLayer: v.optional(v.string()),\n toLayer: v.optional(v.string()),\n fromNodeType: v.optional(v.string()),\n toNodeType: v.optional(v.string()),\n reasoningMethod: v.optional(v.string()),\n logicalRole: v.optional(v.string()),\n temporalClass: v.optional(v.string()),\n validFrom: v.optional(v.number()),\n validUntil: v.optional(v.number()),\n },\n returns: permissiveReturn,\n handler: async (ctx, args) => {\n const connInfo = getConnectionInfo();\n if (!connInfo.configured) {\n throw new Error(\n \"[Neo4j Edge API] Neo4j not configured. Set NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD\"\n );\n }\n\n const edgeType = normalizeEdgeType(args.edgeType);\n const relType = resolveRelationshipType(edgeType);\n const metadata =\n args.metadata && typeof args.metadata === \"object\"\n ? (args.metadata as Record<string, unknown>)\n : undefined;\n const now = Date.now();\n const properties = {\n globalId: args.globalId,\n edgeType,\n weight: args.weight ?? 1,\n confidence:\n args.confidence ?? readNumberProperty(metadata, \"confidence\") ?? 1,\n context: args.context ?? metadataSummary(metadata) ?? \"\",\n derivationType: args.derivationType ?? \"\",\n createdBy: args.createdBy,\n createdAt: now,\n updatedAt: now,\n topicId: args.topicId ?? readStringProperty(metadata, \"topicId\") ?? \"\",\n tenantId: args.tenantId ?? readStringProperty(metadata, \"tenantId\") ?? \"\",\n workspaceId:\n args.workspaceId ?? readStringProperty(metadata, \"workspaceId\") ?? \"\",\n fromLayer: args.fromLayer ?? \"\",\n toLayer: args.toLayer ?? \"\",\n fromNodeType: args.fromNodeType ?? \"\",\n toNodeType: args.toNodeType ?? \"\",\n reasoningMethod: args.reasoningMethod ?? \"\",\n logicalRole: args.logicalRole ?? \"\",\n temporalClass: args.temporalClass ?? \"structural\",\n validFrom: args.validFrom ?? now,\n validUntil: args.validUntil ?? null,\n };\n\n const result = await runWriteTransaction<{ globalId: string }>(\n `\n MATCH (from {globalId: $fromGlobalId})\n MATCH (to {globalId: $toGlobalId})\n MERGE (from)-[r:${relType} {globalId: $globalId}]->(to)\n SET r += $properties\n RETURN r.globalId as globalId\n `,\n {\n fromGlobalId: args.fromGlobalId,\n toGlobalId: args.toGlobalId,\n globalId: args.globalId,\n properties,\n }\n );\n\n if (result.length === 0) {\n await queueEdgeRetry(ctx, {\n globalId: args.globalId,\n operation: \"upsert\",\n error: `Source or target node not yet synced to Neo4j (from: ${args.fromGlobalId}, to: ${args.toGlobalId})`,\n });\n\n if (needsDualWrite(edgeType)) {\n try {\n await mirrorEdgeToConvex(ctx, {\n globalId: args.globalId,\n fromGlobalId: args.fromGlobalId,\n toGlobalId: args.toGlobalId,\n edgeType,\n weight: args.weight,\n confidence: args.confidence,\n context: args.context,\n derivationType: args.derivationType,\n createdBy: args.createdBy,\n topicId: args.topicId,\n fromLayer: args.fromLayer,\n toLayer: args.toLayer,\n fromNodeType: args.fromNodeType,\n toNodeType: args.toNodeType,\n reasoningMethod: args.reasoningMethod,\n logicalRole: args.logicalRole,\n temporalClass: args.temporalClass,\n validFrom: args.validFrom,\n validUntil: args.validUntil,\n });\n } catch {\n // Retry was already queued for the Neo4j write; avoid hiding it.\n }\n }\n\n return {\n success: false,\n globalId: args.globalId,\n edgeType,\n queuedForRetry: true,\n reason: \"nodes_not_synced\",\n };\n }\n\n if (needsDualWrite(edgeType)) {\n try {\n await mirrorEdgeToConvex(ctx, {\n globalId: args.globalId,\n fromGlobalId: args.fromGlobalId,\n toGlobalId: args.toGlobalId,\n edgeType,\n weight: args.weight,\n confidence: args.confidence,\n context: args.context,\n derivationType: args.derivationType,\n createdBy: args.createdBy,\n topicId: args.topicId,\n fromLayer: args.fromLayer,\n toLayer: args.toLayer,\n fromNodeType: args.fromNodeType,\n toNodeType: args.toNodeType,\n reasoningMethod: args.reasoningMethod,\n logicalRole: args.logicalRole,\n temporalClass: args.temporalClass,\n validFrom: args.validFrom,\n validUntil: args.validUntil,\n });\n } catch (error) {\n await queueEdgeRetry(ctx, {\n globalId: args.globalId,\n operation: \"upsert\",\n error: `Convex mirror failed: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n });\n }\n }\n\n return {\n success: true,\n globalId: args.globalId,\n edgeType,\n dualWritten: needsDualWrite(edgeType),\n };\n },\n});\n\nexport const deleteEdge = internalAction({\n args: {\n globalId: v.string(),\n },\n returns: permissiveReturn,\n handler: async (ctx, args) => {\n const connInfo = getConnectionInfo();\n if (!connInfo.configured) {\n throw new Error(\"[Neo4j Edge API] Neo4j not configured\");\n }\n\n await runWriteTransaction(\n `\n MATCH ()-[r {globalId: $globalId}]->()\n DELETE r\n `,\n { globalId: args.globalId }\n );\n\n try {\n await ctx.runMutation(internal.epistemicEdges.deleteEdgeFromConvex, {\n globalId: args.globalId,\n });\n } catch {\n // The mirror may not exist for non-dual-write edge types.\n }\n\n return { success: true };\n },\n});\n\nexport const updateEdge = internalAction({\n args: {\n globalId: v.string(),\n weight: v.optional(v.number()),\n confidence: v.optional(v.number()),\n context: v.optional(v.string()),\n derivationType: v.optional(v.string()),\n },\n returns: permissiveReturn,\n handler: async (ctx, args) => {\n const connInfo = getConnectionInfo();\n if (!connInfo.configured) {\n throw new Error(\"[Neo4j Edge API] Neo4j not configured\");\n }\n\n const updates: Record<string, unknown> = { updatedAt: Date.now() };\n if (args.weight !== undefined) updates.weight = args.weight;\n if (args.confidence !== undefined) updates.confidence = args.confidence;\n if (args.context !== undefined) updates.context = args.context;\n if (args.derivationType !== undefined) {\n updates.derivationType = args.derivationType;\n }\n\n const result = await runWriteTransaction<{\n updated: boolean;\n edgeType: string;\n }>(\n `\n MATCH ()-[r {globalId: $globalId}]->()\n SET r += $updates\n RETURN true as updated, r.edgeType as edgeType\n `,\n { globalId: args.globalId, updates }\n );\n\n if (result.length === 0) {\n return { success: false, error: \"Edge not found\" };\n }\n\n const edgeType = result[0]?.edgeType;\n if (edgeType && needsDualWrite(edgeType)) {\n try {\n await ctx.runMutation(internal.epistemicEdges.updateEdgeInConvex, {\n globalId: args.globalId,\n weight: args.weight,\n confidence: args.confidence,\n context: args.context,\n derivationType: args.derivationType,\n });\n } catch {\n await queueEdgeRetry(ctx, {\n globalId: args.globalId,\n operation: \"upsert\",\n error: \"Convex mirror update failed\",\n });\n }\n }\n\n return { success: true, globalId: args.globalId };\n },\n});\n\nexport const getEdge = internalAction({\n args: {\n globalId: v.string(),\n },\n returns: permissiveReturn,\n handler: async (_ctx, args) => {\n const connInfo = getConnectionInfo();\n if (!connInfo.configured) {\n return null;\n }\n\n const result = await runCypher<{\n globalId: string;\n edgeType: string;\n fromGlobalId: string;\n toGlobalId: string;\n properties: Record<string, unknown>;\n }>(\n `\n MATCH (from)-[r {globalId: $globalId}]->(to)\n RETURN r.globalId as globalId,\n r.edgeType as edgeType,\n from.globalId as fromGlobalId,\n to.globalId as toGlobalId,\n properties(r) as properties\n LIMIT 1\n `,\n { globalId: args.globalId }\n );\n\n return result[0] ?? null;\n },\n});\n\nexport const retryProjectionByGlobalId = internalAction({\n args: {\n globalId: v.string(),\n },\n returns: permissiveReturn,\n handler: async (ctx, args) => {\n const connInfo = getConnectionInfo();\n if (!connInfo.configured) {\n return { success: false, error: \"[Neo4j Edge API] Neo4j not configured\" };\n }\n\n const result = await runCypher<{\n edgeType: string;\n fromGlobalId: string;\n toGlobalId: string;\n properties: Record<string, unknown>;\n }>(\n `\n MATCH (from)-[r {globalId: $globalId}]->(to)\n RETURN r.edgeType as edgeType,\n from.globalId as fromGlobalId,\n to.globalId as toGlobalId,\n properties(r) as properties\n LIMIT 1\n `,\n { globalId: args.globalId }\n );\n\n if (result.length === 0) {\n return {\n success: false,\n error: `[Neo4j Edge API] Edge not found in Neo4j: ${args.globalId}`,\n };\n }\n\n const edge = result[0]!;\n if (!needsDualWrite(edge.edgeType)) {\n return {\n success: true,\n skipped: true,\n reason: \"edge_type_not_projected\",\n edgeType: edge.edgeType,\n };\n }\n\n const props = edge.properties || {};\n await mirrorEdgeToConvex(ctx, {\n globalId: args.globalId,\n fromGlobalId: edge.fromGlobalId,\n toGlobalId: edge.toGlobalId,\n edgeType: edge.edgeType,\n weight: readNumberProperty(props, \"weight\"),\n confidence: readNumberProperty(props, \"confidence\"),\n context: readStringProperty(props, \"context\"),\n derivationType: readStringProperty(props, \"derivationType\"),\n createdBy:\n readStringProperty(props, \"createdBy\") ?? \"neo4j_projection_retry\",\n topicId: readStringProperty(props, \"topicId\"),\n fromLayer: readStringProperty(props, \"fromLayer\"),\n toLayer: readStringProperty(props, \"toLayer\"),\n fromNodeType: readStringProperty(props, \"fromNodeType\"),\n toNodeType: readStringProperty(props, \"toNodeType\"),\n reasoningMethod: readStringProperty(props, \"reasoningMethod\"),\n logicalRole: readStringProperty(props, \"logicalRole\"),\n temporalClass: readStringProperty(props, \"temporalClass\"),\n validFrom: readNumberProperty(props, \"validFrom\"),\n validUntil: readNumberProperty(props, \"validUntil\"),\n });\n\n return { success: true, globalId: args.globalId, projected: true };\n },\n});\n"]}