@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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@lucern/graph-sync` will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+ - No unreleased changes yet.
7
+
8
+ ## [0.3.0-alpha.7] - 2026-05-03
9
+ - Rebuild the coherent Lucern package line after Campaign 1 SDK hardening fixes.
10
+
11
+ ## [0.3.0-alpha.6] - 2026-05-03
12
+ - Promote `@lucern/graph-sync` to a publishable opt-in Neo4j graph mirroring package.
13
+ - Add the host `neo4jEdgeAPI` and query route helper surfaces for tenant Convex and Vercel installs.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @lucern/graph-sync
2
+
3
+ Optional Neo4j graph mirroring for Lucern tenant Convex hosts.
4
+
5
+ `@lucern/graph-sync` packages the host-side surface that the reasoning kernel
6
+ expects when graph mirroring is enabled: Neo4j driver access, node/edge sync
7
+ actions, edge topology writes, backfill/health actions, query proxy actions, and
8
+ a small route-handler helper for Vercel/Next API routes.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pnpm add @lucern/graph-sync
14
+ ```
15
+
16
+ Most tenants should install this only after the regular Convex component
17
+ profile is present:
18
+
19
+ ```bash
20
+ pnpm add @lucern/identity @lucern/reasoning-kernel @lucern/graph-sync
21
+ ```
22
+
23
+ ## Environment
24
+
25
+ Required for direct Neo4j sync and edge actions:
26
+
27
+ - `NEO4J_URI`
28
+ - `NEO4J_USER`
29
+ - `NEO4J_PASSWORD`
30
+
31
+ Required only when using the HTTP query proxy actions:
32
+
33
+ - `NEO4J_SYNC_SECRET`
34
+
35
+ Optional query proxy configuration:
36
+
37
+ - `LUCERN_GRAPH_SYNC_QUERY_BASE_URL` or `NEXT_PUBLIC_APP_URL`
38
+ - `LUCERN_GRAPH_SYNC_ALLOWED_PROXY_HOSTS`
39
+ - `VERCEL_AUTOMATION_BYPASS_SECRET`
40
+
41
+ ## Convex Host Re-Exports
42
+
43
+ Tenant Convex hosts opt in explicitly by adding small wrapper files. The package
44
+ does not inject host schema or tables by installing.
45
+
46
+ ```ts
47
+ // convex/neo4jSync.ts
48
+ export * from "@lucern/graph-sync/neo4jSync";
49
+ ```
50
+
51
+ ```ts
52
+ // convex/neo4jSyncHelpers.ts
53
+ export * from "@lucern/graph-sync/neo4jSyncHelpers";
54
+ ```
55
+
56
+ ```ts
57
+ // convex/neo4jDriver.ts
58
+ export * from "@lucern/graph-sync/neo4jDriver";
59
+ ```
60
+
61
+ ```ts
62
+ // convex/neo4jEdgeAPI.ts
63
+ export * from "@lucern/graph-sync/neo4jEdgeAPI";
64
+ ```
65
+
66
+ ```ts
67
+ // convex/neo4jQueries.ts
68
+ export * from "@lucern/graph-sync/neo4jQueries";
69
+ ```
70
+
71
+ The host must already expose the reasoning-kernel tables that these actions
72
+ read and mirror, including `epistemicNodes`, `epistemicEdges`,
73
+ `epistemicNodeEmbeddings`, and `neo4jSyncQueue`.
74
+
75
+ ## Query Proxy Route
76
+
77
+ `neo4jQueries` actions call a tenant-owned HTTP route so Convex can execute
78
+ predefined graph queries through a host app. The route helper accepts named
79
+ queries only; it never accepts raw Cypher from request bodies.
80
+
81
+ ```ts
82
+ // app/api/neo4j-query/route.ts
83
+ import {
84
+ createNeo4jQueryRouteHandler,
85
+ type Neo4jQueryRegistry,
86
+ } from "@lucern/graph-sync/neo4jQueryRoute";
87
+
88
+ const queries = {
89
+ nodeLineage: {
90
+ cypher: `
91
+ MATCH path = (n {globalId: $globalId})-[:EXTRACTED_FROM|BASED_ON*1..10]->(ancestor)
92
+ RETURN [node in nodes(path) | {
93
+ globalId: node.globalId,
94
+ title: node.title,
95
+ text: node.canonicalText
96
+ }] as lineage
97
+ ORDER BY length(path) DESC
98
+ LIMIT 1
99
+ `,
100
+ },
101
+ } satisfies Neo4jQueryRegistry;
102
+
103
+ export const POST = createNeo4jQueryRouteHandler({ queries });
104
+ ```
105
+
106
+ ## Component Safety
107
+
108
+ This package is an opt-in host add-on. It does not install a Convex component
109
+ and does not mutate the tenant host schema. Any future queue/table requirement
110
+ must be represented in the reasoning-kernel schema contract or documented as an
111
+ explicit tenant host addition before release.
112
+
113
+ ## Public Exports
114
+
115
+ - `@lucern/graph-sync`
116
+ - `@lucern/graph-sync/neo4jDriver`
117
+ - `@lucern/graph-sync/neo4jEdgeAPI`
118
+ - `@lucern/graph-sync/neo4jQueries`
119
+ - `@lucern/graph-sync/neo4jQueryRoute`
120
+ - `@lucern/graph-sync/neo4jSync`
121
+ - `@lucern/graph-sync/neo4jSyncHelpers`
@@ -0,0 +1,207 @@
1
+ export { n as neo4jQueries } from './neo4jQueries-eF7YNiqS.js';
2
+ export { n as neo4jSyncHelpers } from './neo4jSyncHelpers-vxe1-Gvw.js';
3
+
4
+ /**
5
+ * neo4jDriver module implementation.
6
+ */
7
+ declare function validateLabel(label: string): void;
8
+ declare function validateRelType(relType: string): void;
9
+ /**
10
+ * Default query timeout in milliseconds.
11
+ * Prevents expensive graph traversals from hanging indefinitely.
12
+ */
13
+ declare const DEFAULT_QUERY_TIMEOUT_MS = 30000;
14
+ /**
15
+ * Timeout for complex graph queries (cascade simulation, lineage traversal)
16
+ */
17
+ declare const COMPLEX_QUERY_TIMEOUT_MS = 60000;
18
+ /**
19
+ * Execute a Cypher query and return results as typed objects
20
+ *
21
+ * @param query - Cypher query string
22
+ * @param params - Query parameters
23
+ * @param timeoutMs - Query timeout in milliseconds (default: 30s)
24
+ */
25
+ declare function runCypher<T = Record<string, unknown>>(query: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<T[]>;
26
+ /**
27
+ * Execute a write transaction (for mutations)
28
+ *
29
+ * @param query - Cypher query string
30
+ * @param params - Query parameters
31
+ * @param timeoutMs - Transaction timeout in milliseconds (default: 30s)
32
+ */
33
+ declare function runWriteTransaction<T = Record<string, unknown>>(query: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<T[]>;
34
+ /**
35
+ * Execute multiple queries in a single transaction
36
+ *
37
+ * @param queries - Array of queries with parameters
38
+ * @param timeoutMs - Transaction timeout in milliseconds (default: 60s for batch)
39
+ */
40
+ declare function runBatchTransaction(queries: Array<{
41
+ query: string;
42
+ params: Record<string, unknown>;
43
+ }>, timeoutMs?: number): Promise<void>;
44
+ /**
45
+ * Upsert a node by globalId
46
+ */
47
+ declare function upsertNode(label: string, globalId: string, properties: Record<string, unknown>): Promise<void>;
48
+ /**
49
+ * Delete a node by globalId
50
+ */
51
+ declare function deleteNode(globalId: string): Promise<void>;
52
+ /**
53
+ * Batch upsert nodes
54
+ */
55
+ declare function batchUpsertNodes(label: string, nodes: Array<{
56
+ globalId: string;
57
+ properties: Record<string, unknown>;
58
+ }>): Promise<void>;
59
+ /**
60
+ * Upsert an edge by globalId
61
+ */
62
+ declare function upsertEdge(relType: string, globalId: string, fromGlobalId: string, toGlobalId: string, properties?: Record<string, unknown>): Promise<void>;
63
+ /**
64
+ * Delete an edge by globalId
65
+ */
66
+ declare function deleteEdge$1(globalId: string): Promise<void>;
67
+ /**
68
+ * Batch upsert edges
69
+ */
70
+ declare function batchUpsertEdges(edges: Array<{
71
+ relType: string;
72
+ globalId: string;
73
+ fromGlobalId: string;
74
+ toGlobalId: string;
75
+ properties?: Record<string, unknown>;
76
+ }>): Promise<void>;
77
+ /**
78
+ * Check if Neo4j connection is healthy
79
+ */
80
+ declare function healthCheck(): Promise<{
81
+ healthy: boolean;
82
+ nodeCount?: number;
83
+ error?: string;
84
+ }>;
85
+ /**
86
+ * Get connection info (for debugging)
87
+ */
88
+ declare function getConnectionInfo(): {
89
+ uri: string | undefined;
90
+ user: string | undefined;
91
+ configured: boolean;
92
+ };
93
+ /**
94
+ * Close the driver connection (for graceful shutdown)
95
+ */
96
+ declare function closeDriver(): Promise<void>;
97
+
98
+ declare const neo4jDriver_COMPLEX_QUERY_TIMEOUT_MS: typeof COMPLEX_QUERY_TIMEOUT_MS;
99
+ declare const neo4jDriver_DEFAULT_QUERY_TIMEOUT_MS: typeof DEFAULT_QUERY_TIMEOUT_MS;
100
+ declare const neo4jDriver_batchUpsertEdges: typeof batchUpsertEdges;
101
+ declare const neo4jDriver_batchUpsertNodes: typeof batchUpsertNodes;
102
+ declare const neo4jDriver_closeDriver: typeof closeDriver;
103
+ declare const neo4jDriver_deleteNode: typeof deleteNode;
104
+ declare const neo4jDriver_getConnectionInfo: typeof getConnectionInfo;
105
+ declare const neo4jDriver_healthCheck: typeof healthCheck;
106
+ declare const neo4jDriver_runBatchTransaction: typeof runBatchTransaction;
107
+ declare const neo4jDriver_runCypher: typeof runCypher;
108
+ declare const neo4jDriver_runWriteTransaction: typeof runWriteTransaction;
109
+ declare const neo4jDriver_upsertEdge: typeof upsertEdge;
110
+ declare const neo4jDriver_upsertNode: typeof upsertNode;
111
+ declare const neo4jDriver_validateLabel: typeof validateLabel;
112
+ declare const neo4jDriver_validateRelType: typeof validateRelType;
113
+ declare namespace neo4jDriver {
114
+ export { neo4jDriver_COMPLEX_QUERY_TIMEOUT_MS as COMPLEX_QUERY_TIMEOUT_MS, neo4jDriver_DEFAULT_QUERY_TIMEOUT_MS as DEFAULT_QUERY_TIMEOUT_MS, neo4jDriver_batchUpsertEdges as batchUpsertEdges, neo4jDriver_batchUpsertNodes as batchUpsertNodes, neo4jDriver_closeDriver as closeDriver, deleteEdge$1 as deleteEdge, neo4jDriver_deleteNode as deleteNode, neo4jDriver_getConnectionInfo as getConnectionInfo, neo4jDriver_healthCheck as healthCheck, neo4jDriver_runBatchTransaction as runBatchTransaction, neo4jDriver_runCypher as runCypher, neo4jDriver_runWriteTransaction as runWriteTransaction, neo4jDriver_upsertEdge as upsertEdge, neo4jDriver_upsertNode as upsertNode, neo4jDriver_validateLabel as validateLabel, neo4jDriver_validateRelType as validateRelType };
115
+ }
116
+
117
+ declare const DUAL_WRITE_EDGE_TYPES: readonly ["supports", "informs", "tests", "depends_on", "derived_from", "contains", "supersedes", "extracted_from", "responds_to", "based_on", "answers", "belongs_to", "relates_to_thesis", "corroborates", "extends", "same_source_as", "same_theme_as", "plays_theme", "impacts", "evaluates", "mentioned_in", "perspective_on"];
118
+ type DualWriteEdgeType = (typeof DUAL_WRITE_EDGE_TYPES)[number];
119
+ declare function needsDualWrite(edgeType: string): boolean;
120
+ declare const createEdge: any;
121
+ declare const deleteEdge: any;
122
+ declare const updateEdge: any;
123
+ declare const getEdge: any;
124
+ declare const retryProjectionByGlobalId: any;
125
+
126
+ declare const neo4jEdgeAPI_DUAL_WRITE_EDGE_TYPES: typeof DUAL_WRITE_EDGE_TYPES;
127
+ type neo4jEdgeAPI_DualWriteEdgeType = DualWriteEdgeType;
128
+ declare const neo4jEdgeAPI_createEdge: typeof createEdge;
129
+ declare const neo4jEdgeAPI_deleteEdge: typeof deleteEdge;
130
+ declare const neo4jEdgeAPI_getEdge: typeof getEdge;
131
+ declare const neo4jEdgeAPI_needsDualWrite: typeof needsDualWrite;
132
+ declare const neo4jEdgeAPI_retryProjectionByGlobalId: typeof retryProjectionByGlobalId;
133
+ declare const neo4jEdgeAPI_updateEdge: typeof updateEdge;
134
+ declare namespace neo4jEdgeAPI {
135
+ export { neo4jEdgeAPI_DUAL_WRITE_EDGE_TYPES as DUAL_WRITE_EDGE_TYPES, type neo4jEdgeAPI_DualWriteEdgeType as DualWriteEdgeType, neo4jEdgeAPI_createEdge as createEdge, neo4jEdgeAPI_deleteEdge as deleteEdge, neo4jEdgeAPI_getEdge as getEdge, neo4jEdgeAPI_needsDualWrite as needsDualWrite, neo4jEdgeAPI_retryProjectionByGlobalId as retryProjectionByGlobalId, neo4jEdgeAPI_updateEdge as updateEdge };
136
+ }
137
+
138
+ /**
139
+ * Vercel/Next route helper for the Neo4j query proxy.
140
+ *
141
+ * The helper accepts named Cypher only. It never accepts raw Cypher from a
142
+ * request body, which keeps tenant routes small without opening an injection
143
+ * surface.
144
+ */
145
+ type Neo4jNamedQuery = Readonly<{
146
+ cypher: string;
147
+ timeoutMs?: number;
148
+ }>;
149
+ type Neo4jQueryRegistry = Readonly<Record<string, Neo4jNamedQuery>>;
150
+ type Neo4jQueryRouteOptions = Readonly<{
151
+ queries: Neo4jQueryRegistry;
152
+ syncSecret?: string;
153
+ requireTenantContext?: boolean;
154
+ }>;
155
+ declare function createNeo4jQueryRouteHandler(options: Neo4jQueryRouteOptions): (request: Request) => Promise<Response>;
156
+
157
+ type neo4jQueryRoute_Neo4jNamedQuery = Neo4jNamedQuery;
158
+ type neo4jQueryRoute_Neo4jQueryRegistry = Neo4jQueryRegistry;
159
+ type neo4jQueryRoute_Neo4jQueryRouteOptions = Neo4jQueryRouteOptions;
160
+ declare const neo4jQueryRoute_createNeo4jQueryRouteHandler: typeof createNeo4jQueryRouteHandler;
161
+ declare namespace neo4jQueryRoute {
162
+ export { type neo4jQueryRoute_Neo4jNamedQuery as Neo4jNamedQuery, type neo4jQueryRoute_Neo4jQueryRegistry as Neo4jQueryRegistry, type neo4jQueryRoute_Neo4jQueryRouteOptions as Neo4jQueryRouteOptions, neo4jQueryRoute_createNeo4jQueryRouteHandler as createNeo4jQueryRouteHandler };
163
+ }
164
+
165
+ /**
166
+ * neo4jSync module implementation.
167
+ */
168
+ declare const syncNodeToNeo4j: any;
169
+ declare const syncEdgeToNeo4j: any;
170
+ declare const syncAllNodesToNeo4j: any;
171
+ declare const syncAllEdgesToNeo4j: any;
172
+ declare const backfillAllToNeo4j: any;
173
+ declare const processRetryQueue: any;
174
+ /**
175
+ * Sync an embedding vector to an existing Neo4j node.
176
+ * Called after saveEpistemicNodeEmbedding completes, ensuring the
177
+ * embedding reaches Neo4j even if the initial node sync ran before
178
+ * the embedding was generated.
179
+ */
180
+ declare const syncEmbeddingToNeo4j: any;
181
+ declare const checkNeo4jHealth: any;
182
+ /**
183
+ * Re-sync all epistemic nodes to Neo4j with updated properties (paginated)
184
+ * Use after adding new fields to the sync
185
+ */
186
+ declare const resyncAllNodes: any;
187
+ /**
188
+ * Re-sync all epistemic edges to Neo4j with updated properties (paginated)
189
+ * Use after adding new fields to the sync (e.g., classification fields)
190
+ */
191
+ declare const resyncAllEdges: any;
192
+
193
+ declare const neo4jSync_backfillAllToNeo4j: typeof backfillAllToNeo4j;
194
+ declare const neo4jSync_checkNeo4jHealth: typeof checkNeo4jHealth;
195
+ declare const neo4jSync_processRetryQueue: typeof processRetryQueue;
196
+ declare const neo4jSync_resyncAllEdges: typeof resyncAllEdges;
197
+ declare const neo4jSync_resyncAllNodes: typeof resyncAllNodes;
198
+ declare const neo4jSync_syncAllEdgesToNeo4j: typeof syncAllEdgesToNeo4j;
199
+ declare const neo4jSync_syncAllNodesToNeo4j: typeof syncAllNodesToNeo4j;
200
+ declare const neo4jSync_syncEdgeToNeo4j: typeof syncEdgeToNeo4j;
201
+ declare const neo4jSync_syncEmbeddingToNeo4j: typeof syncEmbeddingToNeo4j;
202
+ declare const neo4jSync_syncNodeToNeo4j: typeof syncNodeToNeo4j;
203
+ declare namespace neo4jSync {
204
+ export { neo4jSync_backfillAllToNeo4j as backfillAllToNeo4j, neo4jSync_checkNeo4jHealth as checkNeo4jHealth, neo4jSync_processRetryQueue as processRetryQueue, neo4jSync_resyncAllEdges as resyncAllEdges, neo4jSync_resyncAllNodes as resyncAllNodes, neo4jSync_syncAllEdgesToNeo4j as syncAllEdgesToNeo4j, neo4jSync_syncAllNodesToNeo4j as syncAllNodesToNeo4j, neo4jSync_syncEdgeToNeo4j as syncEdgeToNeo4j, neo4jSync_syncEmbeddingToNeo4j as syncEmbeddingToNeo4j, neo4jSync_syncNodeToNeo4j as syncNodeToNeo4j };
205
+ }
206
+
207
+ export { neo4jDriver, neo4jEdgeAPI, neo4jQueryRoute, neo4jSync };