@ultipa-graph/ultipa-driver
Version:
NodeJS SDK for Ultipa GQL
527 lines (526 loc) • 27.3 kB
TypeScript
/**
* Main client for GQLDB Node.js driver.
*/
import { EventEmitter } from 'events';
import { GqldbConfig } from './config';
import { Response, InsertNodesResult, InsertEdgesResult, ExportNodesResult, ExportEdgesResult, ExportConfig, ExportChunk } from './response';
import { Session } from './session';
import { Transaction } from './transaction';
import { GraphInfo, GraphType, EdgeIdMode, HealthStatus, CacheStats, CacheType, Statistics, CompactResult, ComputeTopologyResult, SystemMetrics, NodeData, EdgeData, BulkCreateNodesOptions, BulkCreateEdgesOptions, BulkImportOptions, BulkImportSession, CheckpointResult, EndBulkImportResult, AbortBulkImportResult, BulkImportStatus, TransactionInfo, TransactionRow, DBType, InsertType, LabelInfo, NodeTypeInfo, EdgeTypeInfo, ConvPropertyDef, IndexProperty, IndexInfo, FulltextInfo, TaskInfo, ProcessInfo, GraphStats, AlgoInfo, AiReadResult } from './types';
/** Configuration for a query */
export interface QueryConfig {
graphName?: string;
parameters?: Record<string, any>;
transactionId?: number;
timeout?: number;
readOnly?: boolean;
/** Max paths to return from path queries (0 = unlimited) */
maxPathResults?: number;
}
/**
* Configuration for insertNodes / insertEdges convenience methods.
* Extends QueryConfig with insert-specific options.
*/
export interface InsertConfig extends QueryConfig {
/** Insert mode. Defaults to InsertType.Normal when omitted. */
insertType?: InsertType;
}
/**
* Configuration for the new GQL-emitter delete API
* (deleteNodesByIds / deleteNodesByCondition /
* deleteEdgesByIds / deleteEdgesByCondition).
*
* Extends QueryConfig so per-call `graphName` works the same as
* `InsertConfig`. Two delete-specific knobs:
* - `returnDeleted` (default true) — emit `RETURN ...` so the
* response carries the full deleted node/edge data. Set to false
* on large bulk deletes to save bandwidth; `rowsAffected` still
* carries the count.
* - `allowDeleteAll` (default false) — safety latch. With empty
* labels/ids AND empty where, the SDK would otherwise emit a
* graph-wide delete; this flag must be explicitly true to opt in.
*/
export interface DeleteConfig extends QueryConfig {
returnDeleted?: boolean;
allowDeleteAll?: boolean;
}
/** Configuration for insert nodes */
export interface InsertNodesConfig {
options?: BulkCreateNodesOptions;
bulkImportSessionId?: string;
}
/** Configuration for insert edges */
export interface InsertEdgesConfig {
options?: BulkCreateEdgesOptions;
bulkImportSessionId?: string;
}
/**
* Health watcher interface for streaming health updates.
* Emits 'status' events with HealthStatus and 'error' events on failure.
*/
export interface HealthWatcher extends EventEmitter {
/** Stop watching health updates */
stop(): void;
}
/** Main client for interacting with GQLDB */
export declare class GqldbClient {
private config;
private clients;
private sessions;
private txManager;
private closed;
private storedUsername;
private storedPassword;
private storedGraph;
private grpcHost;
private grpcCredentials;
private grpcChannelOptions;
private ctx;
private sessionService;
private queryService;
private graphService;
private transactionService;
private dataService;
private healthService;
private adminService;
private bulkImportService;
/** Get session metadata for authenticated requests */
private getSessionMetadata;
/**
* Stable per-client logical session id surfaced under the
* transaction-branch model. See TRANSACTIONS_DRIVER_GUIDE.md §2.0–2.1.
* Falls back to a UUID v4 hex generated at construction when
* `config.sessionId` is not set.
*/
readonly clientSessionId: string;
constructor(config: GqldbConfig);
/** Close the client and all connections */
close(): Promise<void>;
/** Authenticate the user and create a session */
login(username: string, password: string): Promise<Session>;
/**
* Rebuild every gRPC client used by this connection so the next call
* gets a fresh channel. Called by withAutoReconnect when a call
* fails with UNAVAILABLE / connection reset. Does NOT close old
* clients synchronously — in-flight calls hold them alive and gRPC
* closes them once those calls complete.
*/
private forceReconnectAll;
/**
* Execute fn with two recovery behaviours:
* - UNAUTHENTICATED — if stored credentials are available, re-login
* and retry fn once.
* - UNAVAILABLE / connection reset — rebuild every gRPC client so
* the NEXT call gets a fresh channel. The current call is NOT
* retried; caller decides (write-side calls are often not
* idempotent).
*/
private withAutoReconnect;
/** Terminate the current session */
logout(): Promise<void>;
/** Check the connection and return the latency in nanoseconds */
ping(): Promise<number>;
/** Execute a GQL query and return the result */
gql(query: string, config?: QueryConfig): Promise<Response>;
/** Execute a GQL query and stream the results */
gqlStream(query: string, config?: QueryConfig, callback?: (response: Response) => void): Promise<void>;
/** Return the execution plan for a query */
explain(query: string, config?: QueryConfig): Promise<string>;
/** Execute a query with profiling and return statistics */
profile(query: string, config?: QueryConfig): Promise<string>;
/**
* Create a new graph.
*
* When `edgeId` is provided, the driver issues a follow-up
* `ALTER GRAPH <name> SET EDGE_ID ENABLED|DISABLED` via GQL after the
* gRPC create call. When `edgeId` is undefined, behavior is unchanged
* and no ALTER statement is sent.
*/
createGraph(name: string, graphType?: GraphType, description?: string, edgeId?: EdgeIdMode): Promise<void>;
/** Delete a graph */
dropGraph(name: string, ifExists?: boolean): Promise<void>;
/** Set the current graph for the session */
useGraph(name: string): Promise<void>;
/** Return all available graphs */
listGraphs(): Promise<GraphInfo[]>;
/** Return information about a specific graph */
getGraphInfo(name: string): Promise<GraphInfo>;
/** Start a new transaction */
beginTransaction(graphName: string, readOnly?: boolean, timeout?: number): Promise<Transaction>;
/** Commit a transaction */
commit(transactionId: number): Promise<boolean>;
/** Rollback a transaction */
rollback(transactionId: number): Promise<boolean>;
/** Return active transactions */
listTransactions(): Promise<TransactionInfo[]>;
/** Execute a function within a transaction */
withTransaction<T>(graphName: string, fn: (txId: number) => Promise<T>, readOnly?: boolean): Promise<T>;
/**
* Return active transactions via the GQL admin DDL `SHOW TRANSACTIONS`.
* Each row mirrors the 5-column server-side schema:
* `transactionId / status / readOnly / startTime / sessionId`.
* `sessionId` is `''` unless the server has auto-derived one from peer
* info or the driver explicitly surfaces `x-ultipa-session-id` metadata.
*
* Distinct from `listTransactions()` which uses the legacy gRPC.
*/
showTransactions(): Promise<TransactionRow[]>;
/**
* Roll back a single transaction by id via `KILL TRANSACTION '<id>'`.
* `transactionId` here is the **string** id surfaced by
* `showTransactions()` (e.g. `'tx_a396c531-...'`), distinct from the
* `number` id returned by `begin()`.
*/
killTransaction(transactionId: string): Promise<Response>;
/**
* Roll back every active transaction via `RESET TRANSACTIONS`. Admin-only.
* Intended as an escape hatch when an orphan tx blocks new BEGINs.
*/
resetTransactions(): Promise<Response>;
/** Insert multiple nodes into a graph using gRPC bulk insert */
insertNodesBatchAuto(graphName: string, nodes: NodeData[], config?: InsertNodesConfig): Promise<InsertNodesResult>;
/** Insert multiple edges into a graph using gRPC bulk insert */
insertEdgesBatchAuto(graphName: string, edges: EdgeData[], config?: InsertEdgesConfig): Promise<InsertEdgesResult>;
/**
* Delete nodes by id list. Emits
* `MATCH (n) WHERE id(n) IN [...] DETACH DELETE n RETURN n`.
*
* Empty/null `nodeIds` short-circuits without contacting the server.
*
* @returns Response with one row per actually-deleted node, column
* "n" holding the GqldbNode. Use `response.alias("n").asNodes()`.
* With `returnDeleted=false` the response carries only
* `rowsAffected`; rows is empty.
*/
deleteNodesByIds(nodeIds: string[], config?: DeleteConfig): Promise<Response>;
/**
* Delete nodes matching labels and/or where clause. Emits
* `MATCH (n:L1|L2) WHERE <where> DETACH DELETE n RETURN n`.
*
* Throws if both labels and where are empty unless
* `config.allowDeleteAll = true`.
*/
deleteNodesByCondition(labels: string[] | undefined, where: string | undefined, limit?: number, config?: DeleteConfig): Promise<Response>;
/**
* Delete edges by id list. Emits 5-column GQL with `id(e)` so the
* same emit works on graphs with or without EDGE_ID enabled, then
* reshapes the response into a single "e" column holding GqldbEdge.
*/
deleteEdgesByIds(edgeIds: string[], config?: DeleteConfig): Promise<Response>;
/**
* Delete edges matching label and/or where. Emits 5-column GQL,
* then reshapes into a single "e" column.
*
* Throws if both label and where are empty unless
* `config.allowDeleteAll = true`.
*/
deleteEdgesByCondition(label: string | undefined, where: string | undefined, limit?: number, config?: DeleteConfig): Promise<Response>;
/**
* Export graph data in JSON Lines format (streaming).
* @param config Export configuration
* @param callback Callback for each exported chunk
*/
export(config: ExportConfig, callback?: (chunk: ExportChunk) => void): Promise<void>;
/**
* Stream nodes from a graph
* @deprecated Use export() with ExportConfig instead
*/
exportNodes(graphName: string, labels?: string[], limit?: number, callback?: (result: ExportNodesResult) => void): Promise<void>;
/**
* Stream edges from a graph
* @deprecated Use export() with ExportConfig instead
*/
exportEdges(graphName: string, labels?: string[], limit?: number, callback?: (result: ExportEdgesResult) => void): Promise<void>;
/** Check the health of a service */
healthCheck(service?: string): Promise<HealthStatus>;
/**
* Watch health status changes via server-side streaming.
* Returns a HealthWatcher that emits 'status' events with HealthStatus values.
* Call stop() to cancel the stream.
*
* @param service - Optional service name to watch
* @returns HealthWatcher - EventEmitter with stop() method
*
* @example
* ```typescript
* const watcher = client.watch();
* watcher.on('status', (status: HealthStatus) => {
* console.log('Health status:', status);
* });
* watcher.on('error', (err) => {
* console.error('Watch error:', err);
* });
* watcher.on('end', () => {
* console.log('Watch stream ended');
* });
* // Later, to stop watching:
* watcher.stop();
* ```
*/
watch(service?: string): HealthWatcher;
/** Pre-allocate parser instances */
warmupParser(count: number): Promise<void>;
/** Return cache statistics */
getCacheStats(cacheType?: CacheType): Promise<CacheStats>;
/** Clear the specified cache */
clearCache(cacheType?: CacheType): Promise<void>;
/** Return database statistics */
getStatistics(graphName?: string): Promise<Statistics>;
/** Invalidate the RBAC permission cache */
invalidatePermissionCache(username?: string): Promise<void>;
/** Return system-level metrics (CPU, memory, disk I/O, storage, network) */
getSystemMetrics(): Promise<SystemMetrics>;
/** Trigger manual compaction of the database storage */
compact(): Promise<CompactResult>;
/** Wait for the computing engine topology to be ready */
waitForComputeTopology(graphName: string, timeoutMs?: number): Promise<ComputeTopologyResult>;
/**
* Start a bulk import session for optimized high-throughput inserts.
* @param graphName Target graph name
* @param options Optional bulk import configuration
*/
startBulkImport(graphName: string, options?: BulkImportOptions): Promise<BulkImportSession>;
/**
* @deprecated Checkpoint is no longer needed. Use endBulkImport() which performs a final flush.
*/
checkpoint(sessionId: string): Promise<CheckpointResult>;
/**
* End the bulk import session with a final checkpoint.
* @param sessionId Bulk import session ID
*/
endBulkImport(sessionId: string): Promise<EndBulkImportResult>;
/**
* Cancel the bulk import session without final sync.
* @param sessionId Bulk import session ID
*/
abortBulkImport(sessionId: string): Promise<AbortBulkImportResult>;
/**
* Return the current status of a bulk import session.
* @param sessionId Bulk import session ID
*/
getBulkImportStatus(sessionId: string): Promise<BulkImportStatus>;
/** Get the current session */
getSession(): Session | null;
/** Check if there is an active session */
isLoggedIn(): boolean;
/** Get the client configuration */
getConfig(): GqldbConfig;
/** Whether this client is connected to a cluster deployment */
get isCluster(): boolean;
/** Get the cluster ID (empty string if not a cluster) */
get clusterId(): string;
/** Get the partition count (0 if not a cluster) */
get partitionCount(): number;
/** Wrap a name in backticks for safe use in GQL statements.
* Label names and property names should be quoted; graph names, index names,
* and fulltext names should NOT be wrapped in backticks. */
private static quoteLabel;
/** Wrap each label name in backticks and join with ", " */
private static quoteLabels;
/** Join names with ", " without backtick wrapping */
private static joinNames;
/** Create a new open (schema-less) graph
*
* v6 GQL: `CREATE GRAPH g` -> OPEN; `CREATE GRAPH g {}` -> CLOSED. The
* brace-less form is required to actually get an OPEN graph.
*/
createOpenGraph(name: string, config?: QueryConfig): Promise<Response>;
/** Create a new closed (schema-enforced) graph with node and edge label definitions */
createClosedGraph(name: string, config?: QueryConfig): Promise<Response>;
/**
* Create a graph if it does not already exist.
* Returns true if the graph was created, false if it already existed.
*/
createGraphIfNotExist(name: string, graphType?: GraphType, description?: string, edgeId?: EdgeIdMode): Promise<boolean>;
/** Check whether a graph with the given name exists */
hasGraph(name: string): Promise<boolean>;
/** Rename a graph */
alterGraph(graphName: string, newName: string, config?: QueryConfig): Promise<Response>;
/** Remove all data from a graph while keeping its structure */
truncate(graphName: string, config?: QueryConfig): Promise<Response>;
/** Return all labels (node and edge) in the current graph */
showLabels(config?: QueryConfig): Promise<LabelInfo[]>;
/** Return all node labels in the current graph */
showNodeLabels(config?: QueryConfig): Promise<LabelInfo[]>;
/** Return all edge labels in the current graph */
showEdgeLabels(config?: QueryConfig): Promise<LabelInfo[]>;
/** Return all node types with their properties */
showNodeTypes(config?: QueryConfig): Promise<NodeTypeInfo[]>;
/** Return all edge types with their properties */
showEdgeTypes(config?: QueryConfig): Promise<EdgeTypeInfo[]>;
/**
* Return a specific label by name from the current graph, or null if not found.
* Matches entries where `name` is one of the .labels (supports multi-label groups).
*/
getLabel(name: string, config?: QueryConfig): Promise<LabelInfo | null>;
/** Return a specific node type by name, or null if not found */
getNodeLabel(name: string, config?: QueryConfig): Promise<NodeTypeInfo | null>;
/** Return a specific edge type by name, or null if not found */
getEdgeLabel(name: string, config?: QueryConfig): Promise<EdgeTypeInfo | null>;
/** Create a node label in the current graph (closed graph) */
createNodeLabel(name: string, props?: ConvPropertyDef[], config?: QueryConfig): Promise<Response>;
/** Create an edge label in the current graph (closed graph) */
createEdgeLabel(name: string, props?: ConvPropertyDef[], config?: QueryConfig): Promise<Response>;
/** Drop a node label from the current graph (closed graph) */
dropNodeLabel(name: string, config?: QueryConfig): Promise<Response>;
/** Drop one or more edge labels from the current graph (closed graph) */
dropEdgeLabel(...namesOrConfig: Array<string | QueryConfig | undefined>): Promise<Response>;
/**
* Create a label if it does not already exist.
* Returns true if created, false if it already existed.
*/
createLabelIfNotExist(nodeOrEdge: DBType, name: string, props?: ConvPropertyDef[], config?: QueryConfig): Promise<boolean>;
/** Rename a node label */
alterNodeLabel(oldName: string, newName: string, config?: QueryConfig): Promise<Response>;
/** Rename an edge label */
alterEdgeLabel(oldName: string, newName: string, config?: QueryConfig): Promise<Response>;
/** Return all available algorithms */
showAlgos(config?: QueryConfig): Promise<AlgoInfo[]>;
/** Return properties for a label (node or edge) in the current graph */
showProperty(nodeOrEdge: DBType, labelName: string, config?: QueryConfig): Promise<ConvPropertyDef[]>;
/** Return properties for a node label */
showNodeProperty(labelName: string, config?: QueryConfig): Promise<ConvPropertyDef[]>;
/** Return properties for an edge label */
showEdgeProperty(labelName: string, config?: QueryConfig): Promise<ConvPropertyDef[]>;
/** Return a specific property definition for a label, or null if not found */
getProperty(nodeOrEdge: DBType, labelName: string, propName: string, config?: QueryConfig): Promise<ConvPropertyDef | null>;
/** Return a specific property definition for a node label, or null if not found */
getNodeProperty(labelName: string, propName: string, config?: QueryConfig): Promise<ConvPropertyDef | null>;
/** Return a specific property definition for an edge label, or null if not found */
getEdgeProperty(labelName: string, propName: string, config?: QueryConfig): Promise<ConvPropertyDef | null>;
/** Add properties to a label (node or edge) */
createProperty(nodeOrEdge: DBType, labelName: string, props: ConvPropertyDef[], config?: QueryConfig): Promise<Response>;
/** Add properties to a node label */
createNodeProperty(labelName: string, props: ConvPropertyDef[], config?: QueryConfig): Promise<Response>;
/** Add properties to an edge label */
createEdgeProperty(labelName: string, props: ConvPropertyDef[], config?: QueryConfig): Promise<Response>;
/** Drop properties from a label (node or edge) */
dropProperty(nodeOrEdge: DBType, labelName: string, ...propNamesOrConfig: Array<string | QueryConfig | undefined>): Promise<Response>;
/** Drop properties from a node label */
dropNodeProperty(labelName: string, ...propNamesOrConfig: Array<string | QueryConfig | undefined>): Promise<Response>;
/** Drop properties from an edge label */
dropEdgeProperty(labelName: string, ...propNamesOrConfig: Array<string | QueryConfig | undefined>): Promise<Response>;
/**
* Create properties on a label if they do not already exist.
* Returns true if any properties were created, false if all already existed.
*/
createPropertyIfNotExist(nodeOrEdge: DBType, labelName: string, props: ConvPropertyDef[], config?: QueryConfig): Promise<boolean>;
private static constraintName;
private static constraintTarget;
/** Create a NOT NULL constraint on a property */
createNotNullConstraint(nodeOrEdge: DBType, labelName: string, propName: string, config?: QueryConfig): Promise<Response>;
/** Create a UNIQUE constraint on one or more properties */
createUniqueConstraint(nodeOrEdge: DBType, labelName: string, ...propNamesOrConfig: Array<string | QueryConfig | undefined>): Promise<Response>;
/** Remove a NOT NULL constraint from a property */
dropNotNullConstraint(nodeOrEdge: DBType, labelName: string, propName: string, config?: QueryConfig): Promise<Response>;
/** Remove a UNIQUE constraint from one or more properties */
dropUniqueConstraint(nodeOrEdge: DBType, labelName: string, ...propNamesOrConfig: Array<string | QueryConfig | undefined>): Promise<Response>;
/** Return all indexes in the current graph */
showIndex(config?: QueryConfig): Promise<IndexInfo[]>;
/** Return all node indexes in the current graph */
showNodeIndex(config?: QueryConfig): Promise<IndexInfo[]>;
/** Return all edge indexes in the current graph */
showEdgeIndex(config?: QueryConfig): Promise<IndexInfo[]>;
/** Create an index on a node label */
createNodeIndex(indexName: string, labelName: string, props: IndexProperty[], config?: QueryConfig): Promise<Response>;
/** Create an index on an edge label */
createEdgeIndex(indexName: string, labelName: string, props: IndexProperty[], config?: QueryConfig): Promise<Response>;
/** Drop a node index by name */
dropNodeIndex(indexName: string, config?: QueryConfig): Promise<Response>;
/** Drop an edge index by name */
dropEdgeIndex(indexName: string, config?: QueryConfig): Promise<Response>;
/** Return all fulltext indexes in the current graph */
showFulltext(config?: QueryConfig): Promise<FulltextInfo[]>;
/** Return all node fulltext indexes */
showNodeFulltext(config?: QueryConfig): Promise<FulltextInfo[]>;
/** Return all edge fulltext indexes */
showEdgeFulltext(config?: QueryConfig): Promise<FulltextInfo[]>;
/** Create a fulltext index on a node label */
createNodeFulltext(indexName: string, labelName: string, props: string[], config?: QueryConfig): Promise<Response>;
/** Create a fulltext index on an edge label */
createEdgeFulltext(indexName: string, labelName: string, props: string[], config?: QueryConfig): Promise<Response>;
/** Drop a node fulltext index by name */
dropNodeFulltext(indexName: string, config?: QueryConfig): Promise<Response>;
/** Drop an edge fulltext index by name */
dropEdgeFulltext(indexName: string, config?: QueryConfig): Promise<Response>;
/** Return all tasks in the current graph */
showTasks(config?: QueryConfig): Promise<TaskInfo[]>;
/** Delete a task by ID */
deleteTask(taskId: string, config?: QueryConfig): Promise<Response>;
/** Stop a running task by ID */
stopTask(taskId: string, config?: QueryConfig): Promise<Response>;
/**
* Insert nodes — backward-compatible overloaded entry point.
*
* Two call shapes are accepted; the runtime dispatches on the first
* argument's type:
*
* 1. `insertNodes(graphName: string, nodes, config?)` — original
* 6.0.0 signature, routes through the bulk-import gRPC path
* (`DataService.InsertNodes`). Returns `InsertNodesResult` with
* gRPC-level metadata. Preserves source compatibility for
* callers written against 6.0.0 (e.g. gqldb-manager).
*
* 2. `insertNodes(nodes, config?)` — convenience GQL emitter (added
* after 6.0.0). Synthesizes
* `INSERT (n0:Label {...}), (n1:Label {...}) RETURN n0, n1`,
* with `INSERT OVERWRITE` when `config.insertType` is
* `InsertType.Overwrite`. Returns the raw `Response`.
* `NodeData.id`, when set, is emitted as the `_id` property.
*/
insertNodes(graphName: string, nodes: NodeData[], config?: InsertNodesConfig): Promise<InsertNodesResult>;
insertNodes(nodes: NodeData[], config?: InsertConfig): Promise<Response>;
/**
* Insert edges — backward-compatible overloaded entry point.
*
* Two call shapes are accepted; the runtime dispatches on the first
* argument's type:
*
* 1. `insertEdges(graphName: string, edges, config?)` — original
* 6.0.0 signature, routes through the bulk-import gRPC path
* (`DataService.InsertEdges`). Returns `InsertEdgesResult` with
* gRPC-level metadata. Preserves source compatibility for
* 6.0.0 callers.
*
* 2. `insertEdges(edges, config?)` — convenience GQL emitter
* (added after 6.0.0). Per edge synthesizes
* `MATCH (src WHERE id(src) = 'from'), (dst WHERE id(dst) = 'to') INSERT (src)-[e0:Label {...}]->(dst) RETURN e0`.
* Returns a merged `Response` with columns `e0, e1, ...`.
*/
insertEdges(graphName: string, edges: EdgeData[], config?: InsertEdgesConfig): Promise<InsertEdgesResult>;
insertEdges(edges: EdgeData[], config?: InsertConfig): Promise<Response>;
/** Return currently running processes (queries) */
top(config?: QueryConfig): Promise<ProcessInfo[]>;
/** Terminate a running query by its query ID */
kill(queryId: string, config?: QueryConfig): Promise<Response>;
/** Return statistics for the current graph using db.stats() */
stats(config?: QueryConfig): Promise<GraphStats>;
/** Send a ping to the server and return the latency in nanoseconds */
test(): Promise<number>;
/** YIELD columns emitted by CALL ai.read / ai.gql (kept in sync with server). */
private static readonly AI_YIELD_COLS;
/**
* Ask the AI to generate a GQL statement and auto-execute it.
*
* Wraps `CALL ai.read("<prompt>") YIELD ...`, streams stage rows from the
* server, and re-runs the synthesized GQL so `AiReadResult.data` holds the
* real query {@link Response} (rows / columns) — matching what Manager UI
* displays.
*
* Only transport-level errors reject the Promise. AI-side errors set
* `success=false` and populate `error`, with `data=null`.
*/
aiRead(prompt: string, config?: QueryConfig): Promise<AiReadResult>;
/**
* Ask the AI to generate a GQL statement *without* executing it.
*
* The generated GQL is exposed via `AiReadResult.generatedGql` so the
* caller can review / edit it before running it manually via `client.gql()`.
* `AiReadResult.data` is always null for `aiGql`.
*/
aiGql(prompt: string, config?: QueryConfig): Promise<AiReadResult>;
/** Escape a prompt so it can be safely embedded in a double-quoted GQL string. */
private static escapeAiPrompt;
/** Parse a CALL ai.read / ai.gql YIELD response into an AiReadResult. */
private parseAiResult;
}