/** * Graph Service * * Main service class for graph database operations. * Provides a unified API for working with different graph databases * (Neo4j, Neptune, ArangoDB, Memgraph). * * Based on Ductape Graph API documentation. */ import { GraphFeature } from './types/enums'; import { IGraphConfig, IGraphConnectionConfig, IGraphConnectionResult, IGraphConnectionContext, IGraphServiceConfig, IGraphTestConnectionResult } from './types/connection.interface'; import { ICreateNodeOptions, ICreateNodeResult, IFindNodesOptions, IFindNodesResult, IUpdateNodeOptions, IUpdateNodeResult, IDeleteNodeOptions, IDeleteNodeResult, IMergeNodeOptions, IMergeNodeResult, INode, IAddLabelsOptions, IAddLabelsResult, IRemoveLabelsOptions, IRemoveLabelsResult, ISetLabelsOptions, ISetLabelsResult } from './types/node.interface'; import { ICreateRelationshipOptions, ICreateRelationshipResult, IFindRelationshipsOptions, IFindRelationshipsResult, IUpdateRelationshipOptions, IUpdateRelationshipResult, IDeleteRelationshipOptions, IDeleteRelationshipResult, IMergeRelationshipOptions, IMergeRelationshipResult, IRelationship, ICreateRelationshipIndexOptions } from './types/relationship.interface'; import { ITraverseOptions, ITraverseResult, IShortestPathOptions, IShortestPathResult, IAllPathsOptions, IAllPathsResult, INeighborhoodOptions, INeighborhoodResult, IConnectedComponentsOptions, IConnectedComponentsResult } from './types/traversal.interface'; import { IGraphTransaction, IGraphTransactionOptions, TransactionCallback } from './types/transaction.interface'; import { IRawQueryResult, ICountNodesResult, ICountRelationshipsResult, IGraphStatistics, IFullTextSearchOptions, IFullTextSearchResult, IVectorSearchOptions, IVectorSearchResult, IGraphWhereClause } from './types/query.interface'; import { ICreateNodeIndexOptions, ICreateNodeConstraintOptions, IListIndexesResult, IListConstraintsResult, ICreateIndexResult, ICreateConstraintResult, IDropIndexResult, IDropConstraintResult, IListLabelsResult, IListRelationshipTypesResult, IGraphAction, ICreateGraphActionOptions, IUpdateGraphActionOptions, IExecuteGraphActionOptions, IExecuteGraphActionResult, IListGraphActionsResult } from './types/schema.interface'; import { IProductGraph } from '../types/productsBuilder.types'; import { IGraphActionDispatchInput, IGraphOperationDispatchInput, IDispatchResult } from '../types/processor.types'; export interface IGraphSchemaSnapshot { graph: string; env: string; labels: IListLabelsResult['labels']; relationshipTypes: IListRelationshipTypesResult['types']; indexes: IListIndexesResult['indexes']; constraints: IListConstraintsResult['constraints']; generatedAt: string; } /** * Main graph database service */ export declare class GraphService { /** Adapter factory for creating database-specific adapters */ private adapterFactory; /** Cache of adapters by connection key */ private adapters; /** Connection contexts */ private connectionContexts; /** Currently active connection context */ private currentContext; /** Transaction manager */ private transactionManager; /** Service configuration */ private config; /** Product builders cache */ private productBuilders; /** Local graph configurations */ private localConfigs; /** LogService instance for logging operations */ private logService; /** Current product ID for logging */ private productId; /** CacheManager for two-tier caching (Redis + remote) */ private cacheManager; /** Private keys cache for products (keyed by product tag) */ private privateKeys; private _privateKey; private runtimeDefaults; constructor(config?: IGraphServiceConfig & { private_key: string; access_key: string; }); /** * Update service configuration */ updateConfig(config: IGraphServiceConfig & { private_key: string; access_key: string; }): void; private mergeGraphConnectionConfig; /** * Get service configuration */ getConfig(): IGraphServiceConfig | null; /** * Create a new ProductBuilder instance */ private createNewProductBuilder; /** * Get or create a ProductBuilder without prefetching product metadata (use bootstrap on connect). */ private getOrCreateProductBuilder; private cacheBootstrapProductContext; /** * Get or create a ProductBuilder instance for the given product tag */ private getProductBuilder; /** * Initialize logging service */ private initializeLogService; /** * Create a new ProcessorService instance for job scheduling */ private createNewProcessor; /** * Register a new graph database * As documented in getting-started.md * * @example * ```ts * await ductape.graph.create({ * name: 'Social Graph', * tag: 'social-graph', * type: 'neo4j', * description: 'Stores user relationships', * envs: [ * { slug: 'dev', connection_url: 'bolt://localhost:7687' }, * { slug: 'prd', connection_url: 'bolt://neo4j-prod:7687' }, * ], * }); * ``` */ create(graphConfig: IGraphConfig, productTag?: string): Promise; /** * Create local adapter and connection context for a graph configuration. * This is a lightweight operation that only sets up local state without API calls. * Use this when the graph config is already fetched/decrypted from the API. * * @param graphConfig - The graph configuration (already decrypted if from API) */ private createAdapter; /** * Generate a secret key for graph configuration field * Format: GRAPH_{PRODUCT}_{ASSET_TAG}_{ENV}_{KEY} * * Where: * - PRODUCT = productTag.split('.')[1] (second part after workspace) * - ASSET_TAG = if graphTag starts with same workspace prefix, use second part; otherwise sanitize full tag * - All parts are automatically capitalized */ private generateGraphSecretKey; /** * Store a value as a secret if it exists and is not already a secret reference * @returns The secret reference if stored, or the original value */ private storeAsSecretIfNeeded; /** * Fetch all graphs for a product using the product component fetcher * @param productTag - The product tag * @returns Array of IProductGraph objects */ fetchAllGraphs(productTag: string): Promise; /** * Fetch a specific graph by tag using the product component fetcher * @param productTag - The product tag * @param graphTag - The graph tag * @returns The IProductGraph object or null if not found */ fetchGraphByTag(productTag: string, graphTag: string): Promise; /** * Fetch all registered graphs for a product */ fetchAll(productTag?: string): Promise; /** * Fetch a specific graph configuration */ fetch(graphTag: string, productTag?: string): Promise; /** * Update graph configuration */ update(graphTag: string, updates: Partial, productTag?: string): Promise; /** * Delete a graph configuration */ delete(graphTag: string, productTag?: string): Promise; /** * Convert IProductGraph to IGraphConfig */ private productGraphToConfig; /** * Connect to a graph database * As documented in getting-started.md * * @example * ```ts * await ductape.graph.connect({ * env: 'dev', * product: 'my-app', * graph: 'social-graph', * }); * ``` */ connect(config: IGraphConnectionConfig): Promise; /** * Disconnect any existing connection to this graph from the SDK (shared registry and this instance) before creating a fresh one. */ private disconnectExistingForResource; private connectAndRegisterGraphShared; private runGraphConnect; /** * Test connection to a graph database */ testConnection(config: IGraphConnectionConfig): Promise; /** * Disconnect from current graph */ disconnect(): Promise; /** * Disconnect from all graphs */ closeAll(): Promise; /** * Create a node * As documented in nodes.md */ createNode(options: ICreateNodeOptions & { session?: string; }, transaction?: IGraphTransaction): Promise>; /** * Find nodes by criteria * As documented in nodes.md */ findNodes(options: IFindNodesOptions & { session?: string; }, transaction?: IGraphTransaction): Promise>; /** * Find a node by ID * As documented in nodes.md */ findNodeById(id: string | number, transaction?: IGraphTransaction): Promise | null>; /** * Update a node * As documented in nodes.md */ updateNode(options: IUpdateNodeOptions & { session?: string; }, transaction?: IGraphTransaction): Promise>; /** * Delete a node * As documented in nodes.md */ deleteNode(options: IDeleteNodeOptions & { session?: string; }, transaction?: IGraphTransaction): Promise; /** * Merge (upsert) a node * As documented in nodes.md */ mergeNode(options: IMergeNodeOptions, transaction?: IGraphTransaction): Promise>; /** * Add labels to an existing node * * @example * ```ts * const result = await graphService.addLabels({ * id: 'node-123', * labels: ['Admin', 'Verified'], * }); * console.log(result.node.labels); // ['Person', 'Admin', 'Verified'] * ``` */ addLabels(options: IAddLabelsOptions, transaction?: IGraphTransaction): Promise>; /** * Remove labels from an existing node * * @example * ```ts * const result = await graphService.removeLabels({ * id: 'node-123', * labels: ['Temporary'], * }); * console.log(result.removedLabels); // ['Temporary'] * ``` */ removeLabels(options: IRemoveLabelsOptions, transaction?: IGraphTransaction): Promise>; /** * Set labels on a node (replaces all existing labels) * * @example * ```ts * const result = await graphService.setLabels({ * id: 'node-123', * labels: ['Person', 'Employee'], * }); * console.log(result.previousLabels); // ['Person', 'Contractor'] * console.log(result.newLabels); // ['Person', 'Employee'] * ``` */ setLabels(options: ISetLabelsOptions, transaction?: IGraphTransaction): Promise>; /** * Create a relationship * As documented in relationships.md */ createRelationship(options: ICreateRelationshipOptions & { session?: string; }, transaction?: IGraphTransaction): Promise>; /** * Find relationships by criteria * As documented in relationships.md */ findRelationships(options: IFindRelationshipsOptions & { session?: string; }, transaction?: IGraphTransaction): Promise>; /** * Find a relationship by ID */ findRelationshipById(id: string | number, transaction?: IGraphTransaction): Promise | null>; /** * Update a relationship * As documented in relationships.md */ updateRelationship(options: IUpdateRelationshipOptions & { session?: string; }, transaction?: IGraphTransaction): Promise>; /** * Delete a relationship * As documented in relationships.md */ deleteRelationship(options: IDeleteRelationshipOptions & { session?: string; }, transaction?: IGraphTransaction): Promise; /** * Merge (upsert) a relationship * As documented in relationships.md */ mergeRelationship(options: IMergeRelationshipOptions, transaction?: IGraphTransaction): Promise>; /** * Traverse the graph from a starting node * As documented in traversals.md */ traverse(options: ITraverseOptions, transaction?: IGraphTransaction): Promise>; /** * Find the shortest path between two nodes * As documented in traversals.md */ shortestPath(options: IShortestPathOptions, transaction?: IGraphTransaction): Promise>; /** * Find all paths between two nodes * As documented in traversals.md */ allPaths(options: IAllPathsOptions, transaction?: IGraphTransaction): Promise>; /** * Get the neighborhood of a node * As documented in traversals.md */ getNeighborhood(options: INeighborhoodOptions, transaction?: IGraphTransaction): Promise>; /** * Find connected components in the graph * As documented in traversals.md */ findConnectedComponents(options: IConnectedComponentsOptions, transaction?: IGraphTransaction): Promise>; /** * Count nodes matching criteria * As documented in overview.md */ countNodes(labels?: string[], where?: IGraphWhereClause, transaction?: IGraphTransaction, session?: string): Promise; /** * Count relationships matching criteria * As documented in overview.md */ countRelationships(types?: string[], where?: IGraphWhereClause, transaction?: IGraphTransaction): Promise; /** * Get graph statistics * As documented in overview.md */ getStatistics(transaction?: IGraphTransaction, session?: string): Promise; /** * Full-text search * As documented in overview.md */ fullTextSearch(options: IFullTextSearchOptions, transaction?: IGraphTransaction): Promise>; /** * Vector similarity search * As documented in overview.md */ vectorSearch(options: IVectorSearchOptions, transaction?: IGraphTransaction): Promise>; /** * Execute a raw query * As documented in overview.md * * @example * ```ts * const result = await ductape.graph.query( * 'MATCH (p:Person)-[:FRIENDS_WITH]->(f) WHERE p.name = $name RETURN f', * { name: 'Alice' } * ); * ``` */ query(queryString: string, params?: Record, transaction?: IGraphTransaction, session?: string): Promise>; /** * Create a node index * As documented in overview.md */ createNodeIndex(options: ICreateNodeIndexOptions): Promise; /** * Create a node constraint * As documented in overview.md */ createNodeConstraint(options: ICreateNodeConstraintOptions): Promise; /** * Create a relationship index */ createRelationshipIndex(options: ICreateRelationshipIndexOptions): Promise; /** * List all indexes * As documented in overview.md */ listIndexes(): Promise; /** * List all constraints */ listConstraints(): Promise; /** * Drop an index */ dropIndex(name: string): Promise; /** * Drop a constraint */ dropConstraint(name: string): Promise; /** * List all node labels with counts and property information * Used for GraphExplorer sidebar */ listLabels(): Promise; /** * List all relationship types with counts * Used for GraphExplorer sidebar */ listRelationshipTypes(): Promise; /** * Get a normalized graph schema snapshot (labels, relationship types, indexes, constraints). * Useful for generating accurate executable payload templates and metadata UIs. */ getSchemaSnapshot(): Promise; /** Local cache of graph actions */ private actions; /** * Generate a URL-safe tag from a name */ private generateActionTag; /** * Create a new graph action (saved parameterized query) * * @example * ```ts * const action = await graphService.createAction({ * name: 'Find Persons', * description: 'Find persons with configurable limit', * operation: 'findNodes', * query: { * operation: 'findNodes', * options: { labels: ['Person'], limit: '{{limit}}' } * }, * parameters: [ * { name: 'limit', path: 'options.limit', defaultValue: 25, type: 'number' } * ] * }, 'my-product'); * ``` */ createAction(options: ICreateGraphActionOptions, productTag?: string): Promise; /** * List all actions for a graph */ listActions(graphTag?: string, productTag?: string): Promise; /** * Get a specific action by tag */ getAction(actionTag: string, graphTag?: string, productTag?: string): Promise; /** * Update an existing action */ updateAction(actionTag: string, updates: IUpdateGraphActionOptions, graphTag?: string, productTag?: string): Promise; /** * Delete an action */ deleteAction(actionTag: string, graphTag?: string, productTag?: string): Promise; /** * Execute a graph action with parameter substitution * * @example * ```ts * const result = await graphService.execute({ * product: 'my-app', * env: 'dev', * graph: 'social-graph', * action: 'find-persons', * input: { limit: 50 } * }); * ``` */ execute(options: IExecuteGraphActionOptions): Promise>; /** * Action dispatch operations - for predefined graph actions. */ get action(): { /** * Dispatches a graph action to run as a scheduled job. * @param {IGraphActionDispatchInput} data - The graph action dispatch input. * @returns {Promise} The dispatch result with job ID and status. * @example * // Schedule a graph action to run in 1 hour * await ductape.graph.action.dispatch({ * product: 'my-product', * env: 'production', * graph: 'social-graph', * event: 'compute-recommendations', * input: { userId: '123', limit: 10 }, * schedule: { start_at: Date.now() + 3600000 } * }); * * // Run on a cron schedule * await ductape.graph.action.dispatch({ * product: 'my-product', * env: 'production', * graph: 'analytics-graph', * event: 'aggregate-metrics', * input: {}, * schedule: { cron: '0 0 * * *' } // Daily at midnight * }); */ dispatch: (data: IGraphActionDispatchInput) => Promise; }; /** * Dispatches a graph operation to run as a scheduled job. * Use this for direct graph operations (traverse, query, createNode, deleteNodes, etc.). * @param {IGraphOperationDispatchInput} data - The graph operation dispatch input. * @returns {Promise} The dispatch result with job ID and status. * @example * // Schedule a graph traversal operation * await ductape.graph.dispatch({ * product: 'my-product', * env: 'production', * graph: 'social-graph', * operation: 'traverse', * input: { startNode: 'user:123', direction: 'outgoing', depth: 3 }, * schedule: { start_at: Date.now() + 3600000 } * }); * * // Run periodic graph cleanup * await ductape.graph.dispatch({ * product: 'my-product', * env: 'production', * graph: 'session-graph', * operation: 'deleteNodes', * input: { filter: { expired: true } }, * schedule: { cron: '0 4 * * *' } // Daily at 4 AM * }); */ dispatch(data: IGraphOperationDispatchInput): Promise; /** * Substitute parameter placeholders in a query object */ private substituteParameters; /** * Set a nested value in an object using a dot-notation path */ private setNestedValue; /** * Execute an operation based on the query object */ private executeOperation; /** * Execute operations within a transaction * As documented in transactions.md * * @example * ```ts * const result = await ductape.graph.executeTransaction(async (transaction) => { * const alice = await ductape.graph.createNode({ * labels: ['Person'], * properties: { name: 'Alice' }, * }, transaction); * * const bob = await ductape.graph.createNode({ * labels: ['Person'], * properties: { name: 'Bob' }, * }, transaction); * * await ductape.graph.createRelationship({ * type: 'FRIENDS_WITH', * startNodeId: alice.node.id, * endNodeId: bob.node.id, * }, transaction); * * return { alice, bob }; * }); * ``` */ executeTransaction(callback: TransactionCallback, options?: IGraphTransactionOptions): Promise; /** * Begin a manual transaction * As documented in transactions.md */ beginTransaction(options?: IGraphTransactionOptions): Promise; /** * Commit a transaction */ commitTransaction(transaction: IGraphTransaction): Promise; /** * Rollback a transaction */ rollbackTransaction(transaction: IGraphTransaction): Promise; /** Maximum retry attempts for auto-reconnection */ private readonly maxReconnectRetries; /** Flag to track if reconnection is in progress */ private reconnecting; /** * Get the current adapter (synchronous, throws if not connected) */ private getAdapter; /** * Ensure a connection exists for the given graph/env/product (connect on demand). * Use before getAdapter() when options include graph+env+product (e.g. from proxy). */ private ensureConnectionFor; /** * Get the current adapter with automatic reconnection on failure */ private getAdapterWithReconnect; /** * Attempt to reconnect to the graph database */ private attemptReconnect; /** * Execute an operation with automatic retry on connection errors */ private executeWithRetry; /** * Check if an error is a connection-related error that can be retried * Note: "No connection established" errors are NOT retriable - they indicate * connect() was never called, not that a connection was lost */ private isConnectionError; /** Single bootstrap API call for connect — product + graph config + private_key. */ private bootstrapGraphForConnect; private fetchGraphFromProduct; /** * Get current connection context */ getCurrentContext(): IGraphConnectionContext | null; /** * Check if a feature is supported by the current adapter */ supportsFeature(feature: GraphFeature): boolean; }