@anvailabs/proximadb-client 0.4.1 → 0.5.0

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/dist/esm/graph.js DELETED
@@ -1,685 +0,0 @@
1
- /**
2
- * ProximaDB TypeScript SDK - Graph Operations
3
- *
4
- * Provides a fluent API for graph database operations including
5
- * node and edge management, traversal queries, and graph analytics.
6
- *
7
- * Copyright 2025 ProximaDB Contributors
8
- * Licensed under the Apache License, Version 2.0
9
- */
10
- import { TraversalDirection, } from "./types";
11
- // ============================================================================
12
- // NODE BUILDER
13
- // ============================================================================
14
- /**
15
- * Builder for creating graph nodes
16
- */
17
- export class NodeBuilder {
18
- handle;
19
- nodeId = null;
20
- nodeLabel = null;
21
- nodeProperties = {};
22
- nodeVector = null;
23
- constructor(handle) {
24
- this.handle = handle;
25
- }
26
- /**
27
- * Set the node ID
28
- */
29
- id(nodeId) {
30
- this.nodeId = nodeId;
31
- return this;
32
- }
33
- /**
34
- * Set the node label (type)
35
- */
36
- label(nodeLabel) {
37
- this.nodeLabel = nodeLabel;
38
- return this;
39
- }
40
- /**
41
- * Add a property to the node
42
- */
43
- property(key, value) {
44
- this.nodeProperties[key] = value;
45
- return this;
46
- }
47
- /**
48
- * Add multiple properties at once
49
- */
50
- properties(props) {
51
- Object.assign(this.nodeProperties, props);
52
- return this;
53
- }
54
- /**
55
- * Set the embedding vector for semantic operations
56
- */
57
- vector(vec) {
58
- this.nodeVector = vec;
59
- return this;
60
- }
61
- /**
62
- * Execute the node addition.
63
- *
64
- * Wire endpoint: POST /api/v2/graphs/{graph_id}/nodes
65
- * OpenAPI operationId: createNode
66
- *
67
- * Body shape (server-true, per `CreateNodeRequest`):
68
- * `{ node: NodeInput }` — NodeInput = `{ id, labels?, properties?, embedding? }`.
69
- */
70
- async execute() {
71
- if (this.nodeId === null) {
72
- throw new Error("Node ID is required");
73
- }
74
- const node = { id: this.nodeId };
75
- if (this.nodeLabel !== null) {
76
- node.labels = [this.nodeLabel];
77
- }
78
- if (Object.keys(this.nodeProperties).length > 0) {
79
- node.properties = this.nodeProperties;
80
- }
81
- if (this.nodeVector !== null) {
82
- const embedding = { vector: this.nodeVector };
83
- node.embedding = embedding;
84
- }
85
- const request = { node };
86
- await this.handle.getClient().createNodeRequest(this.handle.getName(), request);
87
- }
88
- /**
89
- * Build the node without executing
90
- */
91
- build() {
92
- if (this.nodeId === null) {
93
- throw new Error("Node ID is required");
94
- }
95
- return {
96
- id: this.nodeId,
97
- label: this.nodeLabel ?? undefined,
98
- properties: this.nodeProperties,
99
- vector: this.nodeVector ?? undefined,
100
- };
101
- }
102
- }
103
- // ============================================================================
104
- // EDGE BUILDER
105
- // ============================================================================
106
- /**
107
- * Builder for creating graph edges
108
- */
109
- export class EdgeBuilder {
110
- handle;
111
- edgeId = null;
112
- sourceNode = null;
113
- targetNode = null;
114
- relationshipType = null;
115
- edgeProperties = {};
116
- edgeWeight = null;
117
- constructor(handle) {
118
- this.handle = handle;
119
- }
120
- /**
121
- * Set the edge ID (required by server `EdgeInput.id`).
122
- */
123
- id(edgeId) {
124
- this.edgeId = edgeId;
125
- return this;
126
- }
127
- /**
128
- * Set the source node ID
129
- */
130
- from(nodeId) {
131
- this.sourceNode = nodeId;
132
- return this;
133
- }
134
- /**
135
- * Set the target node ID
136
- */
137
- to(nodeId) {
138
- this.targetNode = nodeId;
139
- return this;
140
- }
141
- /**
142
- * Set the relationship / edge type (server field: `edge_type`).
143
- */
144
- relationship(relType) {
145
- this.relationshipType = relType;
146
- return this;
147
- }
148
- /**
149
- * Alias for `relationship(...)` matching the server-side field name.
150
- */
151
- edgeType(edgeType) {
152
- this.relationshipType = edgeType;
153
- return this;
154
- }
155
- /**
156
- * Add a property to the edge
157
- */
158
- property(key, value) {
159
- this.edgeProperties[key] = value;
160
- return this;
161
- }
162
- /**
163
- * Add multiple properties at once
164
- */
165
- properties(props) {
166
- Object.assign(this.edgeProperties, props);
167
- return this;
168
- }
169
- /**
170
- * Set the edge weight
171
- */
172
- weight(w) {
173
- this.edgeWeight = w;
174
- return this;
175
- }
176
- /**
177
- * Execute the edge addition.
178
- *
179
- * Wire endpoint: POST /api/v2/graphs/{graph_id}/edges
180
- * OpenAPI operationId: createEdge
181
- *
182
- * Body shape (server-true, per `CreateEdgeRequest`):
183
- * `{ edge: EdgeInput }` — EdgeInput = `{ id, from_node_id, to_node_id,
184
- * edge_type, properties?, weight? }`.
185
- */
186
- async execute() {
187
- if (this.edgeId === null) {
188
- throw new Error("Edge ID is required (call .id(...))");
189
- }
190
- if (this.sourceNode === null) {
191
- throw new Error("Source node ID is required");
192
- }
193
- if (this.targetNode === null) {
194
- throw new Error("Target node ID is required");
195
- }
196
- if (this.relationshipType === null) {
197
- throw new Error("Edge type / relationship is required");
198
- }
199
- const edge = {
200
- id: this.edgeId,
201
- from_node_id: this.sourceNode,
202
- to_node_id: this.targetNode,
203
- edge_type: this.relationshipType,
204
- };
205
- if (Object.keys(this.edgeProperties).length > 0) {
206
- edge.properties = this.edgeProperties;
207
- }
208
- if (this.edgeWeight !== null) {
209
- edge.weight = this.edgeWeight;
210
- }
211
- const request = { edge };
212
- await this.handle.getClient().createEdgeRequest(this.handle.getName(), request);
213
- }
214
- /**
215
- * Build the edge without executing
216
- */
217
- build() {
218
- if (this.sourceNode === null) {
219
- throw new Error("Source node ID is required");
220
- }
221
- if (this.targetNode === null) {
222
- throw new Error("Target node ID is required");
223
- }
224
- if (this.relationshipType === null) {
225
- throw new Error("Relationship type is required");
226
- }
227
- return {
228
- source: this.sourceNode,
229
- target: this.targetNode,
230
- relationship: this.relationshipType,
231
- properties: this.edgeProperties,
232
- weight: this.edgeWeight ?? undefined,
233
- };
234
- }
235
- }
236
- // ============================================================================
237
- // TRAVERSAL BUILDER
238
- // ============================================================================
239
- /**
240
- * Builder for graph traversal queries
241
- */
242
- export class TraversalBuilder {
243
- handle;
244
- startNodeId = null;
245
- relationshipTypes = [];
246
- nodeLabelsList = [];
247
- traversalDirection = TraversalDirection.Outgoing;
248
- maxDepthValue = 3;
249
- limitValue = null;
250
- algorithmValue = null;
251
- filterExpr = null;
252
- constructor(handle) {
253
- this.handle = handle;
254
- }
255
- /**
256
- * Set the starting node
257
- */
258
- start(nodeId) {
259
- this.startNodeId = nodeId;
260
- return this;
261
- }
262
- /**
263
- * Add a relationship type to follow
264
- */
265
- relationship(relType) {
266
- this.relationshipTypes.push(relType);
267
- return this;
268
- }
269
- /**
270
- * Add multiple relationship types
271
- */
272
- relationships(relTypes) {
273
- this.relationshipTypes.push(...relTypes);
274
- return this;
275
- }
276
- /**
277
- * Set the traversal direction
278
- */
279
- direction(dir) {
280
- this.traversalDirection = dir;
281
- return this;
282
- }
283
- /**
284
- * Traverse outgoing edges only
285
- */
286
- outgoing() {
287
- this.traversalDirection = TraversalDirection.Outgoing;
288
- return this;
289
- }
290
- /**
291
- * Traverse incoming edges only
292
- */
293
- incoming() {
294
- this.traversalDirection = TraversalDirection.Incoming;
295
- return this;
296
- }
297
- /**
298
- * Traverse both directions
299
- */
300
- both() {
301
- this.traversalDirection = TraversalDirection.Both;
302
- return this;
303
- }
304
- /**
305
- * Set the maximum traversal depth
306
- */
307
- maxDepth(depth) {
308
- this.maxDepthValue = depth;
309
- return this;
310
- }
311
- /**
312
- * Set the maximum number of results
313
- */
314
- limit(lim) {
315
- this.limitValue = lim;
316
- return this;
317
- }
318
- /**
319
- * Add a label to filter visited nodes by.
320
- */
321
- nodeLabel(label) {
322
- this.nodeLabelsList.push(label);
323
- return this;
324
- }
325
- /**
326
- * Replace the set of node-label filters.
327
- */
328
- nodeLabels(labels) {
329
- this.nodeLabelsList = [...labels];
330
- return this;
331
- }
332
- /**
333
- * Select traversal algorithm (server accepts `bfs`, `dfs`, or `shortest_path`).
334
- */
335
- algorithm(name) {
336
- this.algorithmValue = name;
337
- return this;
338
- }
339
- /**
340
- * Add a filter expression for nodes (client-side helper, not sent on wire).
341
- */
342
- filter(filterStr) {
343
- this.filterExpr = filterStr;
344
- return this;
345
- }
346
- /**
347
- * Execute the traversal.
348
- *
349
- * Wire endpoint: POST /api/v2/graphs/{graph_id}/traverse
350
- * OpenAPI operationId: traverseGraph
351
- *
352
- * Body shape (server-true, per `TraverseRequest`):
353
- * `{ start_node_id, max_depth, edge_types?, node_labels?, algorithm?, limit? }`.
354
- * Note: spec is flat (no `graph` wrapper — `graph_id` is the URL path
355
- * parameter) and has no `direction` field; legacy `direction` /
356
- * `start_node` / `relationships` keys are intentionally not sent.
357
- */
358
- async execute() {
359
- if (this.startNodeId === null) {
360
- throw new Error("Start node is required for traversal");
361
- }
362
- const request = {
363
- start_node_id: this.startNodeId,
364
- max_depth: this.maxDepthValue,
365
- };
366
- if (this.relationshipTypes.length > 0) {
367
- request.edge_types = this.relationshipTypes;
368
- }
369
- if (this.nodeLabelsList.length > 0) {
370
- request.node_labels = this.nodeLabelsList;
371
- }
372
- if (this.algorithmValue !== null) {
373
- request.algorithm = this.algorithmValue;
374
- }
375
- if (this.limitValue !== null) {
376
- request.limit = this.limitValue;
377
- }
378
- // Note: direction (this.traversalDirection) and filter (this.filterExpr)
379
- // are intentionally NOT sent — the server `TraverseRequest` does not
380
- // accept them. The setters remain for SDK ergonomic continuity.
381
- void this.traversalDirection;
382
- void this.filterExpr;
383
- return this.handle.getClient().traverseGraphRequest(this.handle.getName(), request);
384
- }
385
- }
386
- // ============================================================================
387
- // GRAPH HANDLE
388
- // ============================================================================
389
- /**
390
- * Handle to a graph for fluent operations
391
- */
392
- export class GraphHandle {
393
- client;
394
- graphName;
395
- constructor(client, name) {
396
- this.client = client;
397
- this.graphName = name;
398
- }
399
- /**
400
- * Get the graph name
401
- */
402
- getName() {
403
- return this.graphName;
404
- }
405
- /**
406
- * Get the HTTP client (internal use)
407
- */
408
- getClient() {
409
- return this.client;
410
- }
411
- /**
412
- * Start building a node addition
413
- */
414
- addNode() {
415
- return new NodeBuilder(this);
416
- }
417
- /**
418
- * Start building an edge addition
419
- */
420
- addEdge() {
421
- return new EdgeBuilder(this);
422
- }
423
- /**
424
- * Start building a traversal query
425
- */
426
- traverse() {
427
- return new TraversalBuilder(this);
428
- }
429
- /**
430
- * Add a batch of nodes.
431
- *
432
- * Wire endpoint: POST /api/v2/graphs/{graph_id}/nodes/batch
433
- * OpenAPI operationId: batchCreateNodes
434
- *
435
- * Body shape (server-true, per `BatchCreateNodesRequest`):
436
- * `{ nodes: [NodeInput, ...] }`. `graph_id` lives in the URL path.
437
- *
438
- * Accepts either spec-true `NodeInput[]` or legacy `GraphNode[]`; the
439
- * latter is normalized (singular `label` → `labels: [label]`,
440
- * `vector` → `embedding.vector`).
441
- */
442
- async addNodes(nodes) {
443
- const normalized = nodes.map(n => normalizeNodeInput(n));
444
- const request = { nodes: normalized };
445
- const response = (await this.client.batchCreateNodesRequest(this.graphName, request));
446
- return (response?.data?.created_count ??
447
- response?.data?.count ??
448
- response?.created_count ??
449
- response?.count ??
450
- response?.added_count ??
451
- 0);
452
- }
453
- /**
454
- * Add a batch of edges.
455
- *
456
- * Wire endpoint: POST /api/v2/graphs/{graph_id}/edges/batch
457
- * OpenAPI operationId: batchCreateEdges
458
- *
459
- * Body shape (server-true, per `BatchCreateEdgesRequest`):
460
- * `{ edges: [EdgeInput, ...] }`. `graph_id` lives in the URL path.
461
- *
462
- * Accepts either spec-true `EdgeInput[]` or legacy `GraphEdge[]`; the
463
- * latter is normalized (`source` → `from_node_id`, `target` →
464
- * `to_node_id`, `relationship` → `edge_type`). Legacy `GraphEdge` has no
465
- * `id` field; callers MUST upgrade to `EdgeInput` for batch use (the
466
- * normalizer throws if `id` is missing).
467
- */
468
- async addEdges(edges) {
469
- const normalized = edges.map(e => normalizeEdgeInput(e));
470
- const request = { edges: normalized };
471
- const response = (await this.client.batchCreateEdgesRequest(this.graphName, request));
472
- return (response?.data?.created_count ??
473
- response?.data?.count ??
474
- response?.created_count ??
475
- response?.count ??
476
- response?.added_count ??
477
- 0);
478
- }
479
- /**
480
- * Get a node by ID
481
- */
482
- async getNode(nodeId) {
483
- try {
484
- return await this.client.getNodeRequest(this.graphName, nodeId);
485
- }
486
- catch (e) {
487
- // The facade maps a 404 to a ProximaDBError (statusCode 404, message =
488
- // server body). Treat "not found" as a null result, preserving the
489
- // pre-rebase behavior; match on status or the body text.
490
- const status = e.statusCode;
491
- if (status === 404) {
492
- return null;
493
- }
494
- if (e instanceof Error && e.message.includes("404")) {
495
- return null;
496
- }
497
- throw e;
498
- }
499
- }
500
- /**
501
- * Delete a node by ID
502
- */
503
- async deleteNode(nodeId) {
504
- await this.client.deleteNodeRequest(this.graphName, nodeId);
505
- }
506
- // deleteEdge is intentionally LEFT facade-built (generic URL path): the
507
- // generated REST client has no matching typed op for this 3-segment route
508
- // shape. The OpenAPI spec / generated client model edge deletion as
509
- // `DELETE /api/v2/graphs/{graph_id}/edges/{edge_id}` (single `edge_id`),
510
- // whereas this SDK's public signature is `(source, target, relationship)` →
511
- // `/edges/{source}/{target}/{relationship}`. Until the SDK signature is
512
- // reconciled to `{edge_id}` (separate change), this cannot route through the
513
- // generated client. Tracked separately.
514
- /**
515
- * Delete an edge
516
- */
517
- async deleteEdge(source, target, relationship) {
518
- const url = this.client.url() + "/api/v2/graphs/" + this.graphName + "/edges/" + source + "/" + target + "/" + relationship;
519
- await this.client.delete(url);
520
- }
521
- /**
522
- * Get graph statistics.
523
- *
524
- * Routed through the generated typed transport (TD-126 Phase 4).
525
- * Wire endpoint: GET /api/v2/graphs/{graph_id} (getGraph)
526
- */
527
- async info() {
528
- return this.client.getGraphRequest(this.graphName);
529
- }
530
- }
531
- // ============================================================================
532
- // GRAPH BUILDER
533
- // ============================================================================
534
- /**
535
- * Builder for creating graphs
536
- */
537
- export class GraphBuilder {
538
- client;
539
- graphId;
540
- graphDisplayName = null;
541
- graphDescription = null;
542
- constructor(client, graphId) {
543
- this.client = client;
544
- this.graphId = graphId;
545
- }
546
- /**
547
- * Set an optional human-readable name (defaults server-side to graph_id).
548
- */
549
- name(displayName) {
550
- this.graphDisplayName = displayName;
551
- return this;
552
- }
553
- /**
554
- * Set the graph description
555
- */
556
- description(desc) {
557
- this.graphDescription = desc;
558
- return this;
559
- }
560
- /**
561
- * Execute the graph creation.
562
- *
563
- * Wire endpoint: POST /api/v2/graphs
564
- * OpenAPI operationId: createGraph
565
- *
566
- * Body shape (server-true, per `CreateGraphRequest`):
567
- * `{ graph_id, name?, description? }`.
568
- */
569
- async execute() {
570
- const request = { graph_id: this.graphId };
571
- if (this.graphDisplayName !== null) {
572
- request.name = this.graphDisplayName;
573
- }
574
- if (this.graphDescription !== null) {
575
- request.description = this.graphDescription;
576
- }
577
- await this.client.createGraphRequest(request);
578
- }
579
- }
580
- // ============================================================================
581
- // INTERNAL: legacy -> spec normalizers
582
- // ============================================================================
583
- function normalizeNodeInput(input) {
584
- // Distinguish legacy `GraphNode` (has singular `label` and/or `vector`) from
585
- // spec-true `NodeInput` (has `labels[]` / `embedding`). Field absence is
586
- // fine — `NodeInput` only requires `id`.
587
- const out = { id: input.id };
588
- // Inline structural check avoids a hard cross-cast.
589
- const legacy = input;
590
- const spec = input;
591
- if (spec.labels !== undefined) {
592
- out.labels = spec.labels;
593
- }
594
- else if (legacy.label !== undefined) {
595
- out.labels = [legacy.label];
596
- }
597
- if (spec.properties !== undefined) {
598
- out.properties = spec.properties;
599
- }
600
- else if (legacy.properties !== undefined) {
601
- out.properties = legacy.properties;
602
- }
603
- if (spec.embedding !== undefined) {
604
- out.embedding = spec.embedding;
605
- }
606
- else if (legacy.vector !== undefined) {
607
- out.embedding = { vector: legacy.vector };
608
- }
609
- return out;
610
- }
611
- function normalizeEdgeInput(input) {
612
- const spec = input;
613
- const legacy = input;
614
- // Spec edge has `id` (required), legacy has none. If absent on a legacy
615
- // value, surface a hard error — server `EdgeInput.id` is required.
616
- if (spec.id === undefined) {
617
- throw new Error("addEdges: edge.id is required by EdgeInput (legacy GraphEdge has no id; pass an EdgeInput)");
618
- }
619
- const out = {
620
- id: spec.id,
621
- from_node_id: spec.from_node_id ?? legacy.source,
622
- to_node_id: spec.to_node_id ?? legacy.target,
623
- edge_type: spec.edge_type ?? legacy.relationship,
624
- };
625
- if (spec.properties !== undefined) {
626
- out.properties = spec.properties;
627
- }
628
- else if (legacy.properties !== undefined) {
629
- out.properties = legacy.properties;
630
- }
631
- if (spec.weight !== undefined) {
632
- out.weight = spec.weight;
633
- }
634
- else if (legacy.weight !== undefined) {
635
- out.weight = legacy.weight;
636
- }
637
- return out;
638
- }
639
- // ============================================================================
640
- // CONVENIENCE FUNCTIONS
641
- // ============================================================================
642
- /**
643
- * Create a new GraphNode
644
- */
645
- export function createNode(id) {
646
- return {
647
- id,
648
- properties: {},
649
- };
650
- }
651
- /**
652
- * Create a new GraphNode with builder pattern
653
- */
654
- export function node(id) {
655
- const n = { id, properties: {} };
656
- return {
657
- label: (l) => { n.label = l; return node(id); },
658
- property: (k, v) => { n.properties[k] = v; return node(id); },
659
- vector: (v) => { n.vector = v; return node(id); },
660
- build: () => n,
661
- };
662
- }
663
- /**
664
- * Create a new GraphEdge
665
- */
666
- export function createEdge(source, target, relationship) {
667
- return {
668
- source,
669
- target,
670
- relationship,
671
- properties: {},
672
- };
673
- }
674
- /**
675
- * Create a new GraphEdge with builder pattern
676
- */
677
- export function edge(source, target, relationship) {
678
- const e = { source, target, relationship, properties: {} };
679
- return {
680
- property: (k, v) => { e.properties[k] = v; return edge(source, target, relationship); },
681
- weight: (w) => { e.weight = w; return edge(source, target, relationship); },
682
- build: () => e,
683
- };
684
- }
685
- //# sourceMappingURL=graph.js.map