@anvailabs/proximadb-client 0.3.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.
Files changed (65) hide show
  1. package/README.md +160 -0
  2. package/dist/client.d.ts +319 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +681 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/collection.d.ts +227 -0
  7. package/dist/collection.d.ts.map +1 -0
  8. package/dist/collection.js +436 -0
  9. package/dist/collection.js.map +1 -0
  10. package/dist/esm/client.js +674 -0
  11. package/dist/esm/client.js.map +1 -0
  12. package/dist/esm/collection.js +428 -0
  13. package/dist/esm/collection.js.map +1 -0
  14. package/dist/esm/filter.js +228 -0
  15. package/dist/esm/filter.js.map +1 -0
  16. package/dist/esm/generated/schema.js +6 -0
  17. package/dist/esm/generated/schema.js.map +1 -0
  18. package/dist/esm/graph.js +678 -0
  19. package/dist/esm/graph.js.map +1 -0
  20. package/dist/esm/index.js +86 -0
  21. package/dist/esm/index.js.map +1 -0
  22. package/dist/esm/search.js +317 -0
  23. package/dist/esm/search.js.map +1 -0
  24. package/dist/esm/transport.js +33 -0
  25. package/dist/esm/transport.js.map +1 -0
  26. package/dist/esm/types.js +181 -0
  27. package/dist/esm/types.js.map +1 -0
  28. package/dist/filter.d.ts +51 -0
  29. package/dist/filter.d.ts.map +1 -0
  30. package/dist/filter.js +241 -0
  31. package/dist/filter.js.map +1 -0
  32. package/dist/generated/schema.d.ts +4271 -0
  33. package/dist/generated/schema.d.ts.map +1 -0
  34. package/dist/generated/schema.js +7 -0
  35. package/dist/generated/schema.js.map +1 -0
  36. package/dist/graph.d.ts +350 -0
  37. package/dist/graph.d.ts.map +1 -0
  38. package/dist/graph.js +690 -0
  39. package/dist/graph.js.map +1 -0
  40. package/dist/index.d.ts +58 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +126 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/search.d.ts +160 -0
  45. package/dist/search.d.ts.map +1 -0
  46. package/dist/search.js +322 -0
  47. package/dist/search.js.map +1 -0
  48. package/dist/transport.d.ts +40 -0
  49. package/dist/transport.d.ts.map +1 -0
  50. package/dist/transport.js +39 -0
  51. package/dist/transport.js.map +1 -0
  52. package/dist/types.d.ts +632 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +185 -0
  55. package/dist/types.js.map +1 -0
  56. package/package.json +72 -0
  57. package/src/client.ts +856 -0
  58. package/src/collection.ts +578 -0
  59. package/src/filter.ts +280 -0
  60. package/src/generated/schema.ts +4271 -0
  61. package/src/graph.ts +811 -0
  62. package/src/index.ts +184 -0
  63. package/src/search.ts +401 -0
  64. package/src/transport.ts +52 -0
  65. package/src/types.ts +726 -0
package/src/client.ts ADDED
@@ -0,0 +1,856 @@
1
+ /**
2
+ * ProximaDB TypeScript SDK - Client
3
+ *
4
+ * Main entry point for connecting to ProximaDB servers.
5
+ *
6
+ * Copyright 2025 ProximaDB Contributors
7
+ * Licensed under the Apache License, Version 2.0
8
+ */
9
+
10
+ import {
11
+ ClientConfig,
12
+ HealthStatus,
13
+ CollectionInfo,
14
+ GraphInfo,
15
+ ProximaDBError,
16
+ ErrorCode,
17
+ ProbeResponse,
18
+ SchemaResponse,
19
+ UpdateSchemaRequest,
20
+ UpdateSchemaResponse,
21
+ QueryRequest,
22
+ ExplainQueryRequest,
23
+ QueryResponse,
24
+ } from "./types";
25
+ import { CollectionBuilder, CollectionHandle, CollectionHttpClient } from "./collection";
26
+ import { GraphBuilder, GraphHandle, GraphHttpClient } from "./graph";
27
+ import { createTransport, GeneratedClient } from "./transport";
28
+
29
+ /**
30
+ * Default client configuration
31
+ */
32
+ const DEFAULT_CONFIG: Required<ClientConfig> = {
33
+ url: "http://localhost:5678",
34
+ timeoutMs: 30000,
35
+ maxRetries: 3,
36
+ apiKey: "",
37
+ poolConnections: true,
38
+ maxIdleConnections: 10,
39
+ };
40
+
41
+ /**
42
+ * HTTP response interface
43
+ */
44
+ interface HttpResponse {
45
+ ok: boolean;
46
+ status: number;
47
+ statusText: string;
48
+ json(): Promise<unknown>;
49
+ text(): Promise<string>;
50
+ }
51
+
52
+ /**
53
+ * Minimal fetch-like function signature
54
+ */
55
+ type FetchFunction = (url: string, init?: RequestInit) => Promise<HttpResponse>;
56
+
57
+ /**
58
+ * Get the global fetch function
59
+ */
60
+ function getFetch(): FetchFunction {
61
+ if (typeof globalThis.fetch === "function") {
62
+ return globalThis.fetch.bind(globalThis) as FetchFunction;
63
+ }
64
+ throw new Error(
65
+ "No fetch implementation available. " +
66
+ "Node.js 18+ includes fetch natively. " +
67
+ "For older versions, install node-fetch."
68
+ );
69
+ }
70
+
71
+ /**
72
+ * ProximaDB client for connecting to a remote server
73
+ */
74
+ export class ProximaDBClient implements CollectionHttpClient, GraphHttpClient {
75
+ private config: Required<ClientConfig>;
76
+ private fetchFn: FetchFunction;
77
+ /**
78
+ * Generated typed REST transport (TD-126 Phase 4). Core collection / record
79
+ * / search operations route through this; it owns URL/path/query/body
80
+ * encoding per the OpenAPI spec. Auth, retries, and error mapping are
81
+ * injected via the facade's own fetch (see `transportFetch`).
82
+ */
83
+ private gen: GeneratedClient;
84
+
85
+ /**
86
+ * Create a new client with the given URL
87
+ */
88
+ static connect(url: string): ProximaDBClient {
89
+ return new ProximaDBClient({ url });
90
+ }
91
+
92
+ /**
93
+ * Create a new client builder
94
+ */
95
+ static builder(): ClientBuilder {
96
+ return new ClientBuilder();
97
+ }
98
+
99
+ /**
100
+ * Create a client with configuration
101
+ */
102
+ constructor(config: Partial<ClientConfig> = {}) {
103
+ this.config = { ...DEFAULT_CONFIG, ...config };
104
+ this.fetchFn = getFetch();
105
+
106
+ // Validate URL
107
+ try {
108
+ new URL(this.config.url);
109
+ } catch {
110
+ throw new ProximaDBError(
111
+ "Invalid URL: " + this.config.url,
112
+ ErrorCode.InvalidConfig
113
+ );
114
+ }
115
+
116
+ // Wire the generated typed transport. Every request the generated client
117
+ // issues is funneled through `transportFetch`, the facade's own fetch that
118
+ // applies bearer auth, retries, and error mapping — exactly as the legacy
119
+ // generic `request<T>` path does.
120
+ this.gen = createTransport(this.config.url, (req) => this.transportFetch(req));
121
+ }
122
+
123
+ /**
124
+ * Transport `fetch` handed to the generated client.
125
+ *
126
+ * The generated client assembles a resolved `Request` (URL + method + body);
127
+ * this normalizes it back to the facade's `(url, init)` retry/auth/error
128
+ * pipeline so the wire plumbing and transport policy compose. The returned
129
+ * value is a `Response`-shaped object the generated client parses; on an
130
+ * unsuccessful response a `ProximaDBError` is thrown (matching the generic
131
+ * path), so callers never see a non-2xx body.
132
+ */
133
+ private async transportFetch(req: Request): Promise<Response> {
134
+ const method = (req.method || "GET").toUpperCase();
135
+ let body: unknown = undefined;
136
+ if (method !== "GET" && method !== "DELETE") {
137
+ const text = await req.clone().text();
138
+ if (text.length > 0) {
139
+ body = JSON.parse(text);
140
+ }
141
+ }
142
+ const data = await this.request<unknown>(method, req.url, body);
143
+ // Re-wrap the already-parsed, validated body as a Response so the
144
+ // generated client can read `.json()`/`.ok` without re-issuing I/O.
145
+ return new Response(JSON.stringify(data ?? null), {
146
+ status: 200,
147
+ headers: { "Content-Type": "application/json" },
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Get the server URL
153
+ */
154
+ url(): string {
155
+ return this.config.url;
156
+ }
157
+
158
+ /**
159
+ * Get the client configuration
160
+ */
161
+ getConfig(): ClientConfig {
162
+ return { ...this.config };
163
+ }
164
+
165
+ // =========================================================================
166
+ // HTTP Methods
167
+ // =========================================================================
168
+
169
+ /**
170
+ * Make a GET request
171
+ */
172
+ async get<T>(requestUrl: string): Promise<T> {
173
+ return this.request<T>("GET", requestUrl);
174
+ }
175
+
176
+ /**
177
+ * Make a POST request
178
+ */
179
+ async post<T>(requestUrl: string, body: unknown): Promise<T> {
180
+ return this.request<T>("POST", requestUrl, body);
181
+ }
182
+
183
+ /**
184
+ * Make a PUT request
185
+ */
186
+ async put<T>(requestUrl: string, body: unknown): Promise<T> {
187
+ return this.request<T>("PUT", requestUrl, body);
188
+ }
189
+
190
+ /**
191
+ * Make a DELETE request
192
+ */
193
+ async delete<T>(requestUrl: string): Promise<T> {
194
+ return this.request<T>("DELETE", requestUrl);
195
+ }
196
+
197
+ /**
198
+ * Make an HTTP request with retry logic
199
+ */
200
+ private async request<T>(
201
+ method: string,
202
+ requestUrl: string,
203
+ body?: unknown
204
+ ): Promise<T> {
205
+ const headers: Record<string, string> = {
206
+ "Content-Type": "application/json",
207
+ Accept: "application/json",
208
+ };
209
+
210
+ if (this.config.apiKey) {
211
+ headers["Authorization"] = "Bearer " + this.config.apiKey;
212
+ }
213
+
214
+ const buildInit = (signal?: AbortSignal): RequestInit => {
215
+ const init: RequestInit = {
216
+ method,
217
+ headers,
218
+ signal,
219
+ };
220
+
221
+ if (body !== undefined) {
222
+ init.body = JSON.stringify(body);
223
+ }
224
+
225
+ return init;
226
+ };
227
+
228
+ let lastError: Error | null = null;
229
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
230
+ const controller = new AbortController();
231
+ const timeoutId = setTimeout(
232
+ () => controller.abort(),
233
+ this.config.timeoutMs,
234
+ );
235
+ try {
236
+ const response = await this.fetchFn(
237
+ requestUrl,
238
+ buildInit(controller.signal),
239
+ );
240
+ return await this.handleResponse<T>(response);
241
+ } catch (e: unknown) {
242
+ lastError = e instanceof Error ? e : new Error(String(e));
243
+
244
+ // Don't retry on client errors (4xx)
245
+ if (lastError instanceof ProximaDBError) {
246
+ if (lastError.statusCode && lastError.statusCode >= 400 && lastError.statusCode < 500) {
247
+ throw lastError;
248
+ }
249
+ }
250
+
251
+ // Wait before retry with exponential backoff
252
+ if (attempt < this.config.maxRetries) {
253
+ const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
254
+ await new Promise((resolve) => setTimeout(resolve, delay));
255
+ }
256
+ } finally {
257
+ clearTimeout(timeoutId);
258
+ }
259
+ }
260
+
261
+ throw lastError ?? new Error("Request failed after retries");
262
+ }
263
+
264
+ /**
265
+ * Handle HTTP response
266
+ */
267
+ private async handleResponse<T>(response: HttpResponse): Promise<T> {
268
+ if (response.ok) {
269
+ try {
270
+ return (await response.json()) as T;
271
+ } catch {
272
+ throw new ProximaDBError(
273
+ "Failed to parse response JSON",
274
+ ErrorCode.ServerError,
275
+ response.status
276
+ );
277
+ }
278
+ }
279
+
280
+ const statusCode = response.status;
281
+ let message = response.statusText;
282
+
283
+ try {
284
+ message = await response.text();
285
+ } catch {
286
+ // Use statusText if text() fails
287
+ }
288
+
289
+ switch (statusCode) {
290
+ case 401:
291
+ throw new ProximaDBError(message, ErrorCode.AuthenticationFailed, statusCode);
292
+ case 403:
293
+ throw new ProximaDBError(message, ErrorCode.AuthorizationDenied, statusCode);
294
+ case 404:
295
+ throw new ProximaDBError(message, ErrorCode.CollectionNotFound, statusCode);
296
+ case 409:
297
+ throw new ProximaDBError(message, ErrorCode.CollectionExists, statusCode);
298
+ case 429:
299
+ throw new ProximaDBError(message, ErrorCode.RateLimited, statusCode);
300
+ default:
301
+ throw new ProximaDBError(message, ErrorCode.ServerError, statusCode);
302
+ }
303
+ }
304
+
305
+ // =========================================================================
306
+ // Collection Operations
307
+ // =========================================================================
308
+
309
+ /**
310
+ * Get a handle to a collection for fluent operations
311
+ */
312
+ collection(name: string): CollectionHandle {
313
+ return new CollectionHandle(this, name);
314
+ }
315
+
316
+ /**
317
+ * Create a collection builder
318
+ */
319
+ createCollection(name: string): CollectionBuilder {
320
+ return new CollectionBuilder(this, name);
321
+ }
322
+
323
+ /**
324
+ * Delete a collection
325
+ *
326
+ * Routed through the generated typed transport (TD-126 Phase 4).
327
+ * Wire endpoint: DELETE /api/v2/collections/{collection_id}
328
+ * OpenAPI operationId: deleteCollection
329
+ */
330
+ async deleteCollection(name: string): Promise<void> {
331
+ await this.gen.DELETE("/api/v2/collections/{collection_id}", {
332
+ params: { path: { collection_id: name } },
333
+ });
334
+ }
335
+
336
+ /**
337
+ * List all collections
338
+ *
339
+ * Routed through the generated typed transport (TD-126 Phase 4).
340
+ * Wire endpoint: GET /api/v2/collections
341
+ * OpenAPI operationId: listCollections
342
+ */
343
+ async listCollections(): Promise<CollectionInfo[]> {
344
+ const { data } = await this.gen.GET("/api/v2/collections", {});
345
+ return ((data?.collections ?? []) as unknown[]) as CollectionInfo[];
346
+ }
347
+
348
+ /**
349
+ * Get the schema for a collection
350
+ *
351
+ * Routed through the generated typed transport (TD-126 Phase 4).
352
+ * Wire endpoint: GET /api/v2/collections/{collection_id}/schema
353
+ * OpenAPI operationId: getCollectionSchema
354
+ */
355
+ async getCollectionSchema(collectionId: string): Promise<SchemaResponse> {
356
+ const { data } = await this.gen.GET(
357
+ "/api/v2/collections/{collection_id}/schema",
358
+ { params: { path: { collection_id: collectionId } } },
359
+ );
360
+ return data as unknown as SchemaResponse;
361
+ }
362
+
363
+ /**
364
+ * Update the schema for a collection
365
+ *
366
+ * Routed through the generated typed transport (TD-126 Phase 4).
367
+ * Wire endpoint: PUT /api/v2/collections/{collection_id}/schema
368
+ * OpenAPI operationId: updateCollectionSchema
369
+ */
370
+ async updateCollectionSchema(
371
+ collectionId: string,
372
+ schema: UpdateSchemaRequest
373
+ ): Promise<UpdateSchemaResponse> {
374
+ const { data } = await this.gen.PUT(
375
+ "/api/v2/collections/{collection_id}/schema",
376
+ {
377
+ params: { path: { collection_id: collectionId } },
378
+ body: schema as never,
379
+ },
380
+ );
381
+ return data as unknown as UpdateSchemaResponse;
382
+ }
383
+
384
+ // =========================================================================
385
+ // Query Facade (AQL / UQL)
386
+ // =========================================================================
387
+
388
+ /**
389
+ * Execute an AQL or UQL query through the shared query facade
390
+ *
391
+ * Wire endpoint: POST /api/v2/query
392
+ * OpenAPI operationId: executeQuery
393
+ */
394
+ async executeQuery(req: QueryRequest): Promise<QueryResponse> {
395
+ const { data } = await this.gen.POST("/api/v2/query", {
396
+ body: req as never,
397
+ });
398
+ return data as unknown as QueryResponse;
399
+ }
400
+
401
+ /**
402
+ * Explain an AQL or UQL query through the shared query facade
403
+ *
404
+ * Wire endpoint: POST /api/v2/query/explain
405
+ * OpenAPI operationId: explainQuery
406
+ */
407
+ async explainQuery(req: ExplainQueryRequest): Promise<QueryResponse> {
408
+ const { data } = await this.gen.POST("/api/v2/query/explain", {
409
+ body: req as never,
410
+ });
411
+ return data as unknown as QueryResponse;
412
+ }
413
+
414
+ // =========================================================================
415
+ // Typed transport for core collection / record / search ops (TD-126 Phase 4)
416
+ //
417
+ // These route the builders' wire calls through the generated typed client.
418
+ // The builders (collection.ts / search.ts) call these instead of hand-building
419
+ // URL strings, so the core CRUD + search paths are spec-generated end to end.
420
+ // =========================================================================
421
+
422
+ /**
423
+ * Create a collection.
424
+ * Wire endpoint: POST /api/v2/collections (createCollection)
425
+ */
426
+ async createCollectionRequest(body: Record<string, unknown>): Promise<unknown> {
427
+ const { data } = await this.gen.POST("/api/v2/collections", {
428
+ body: body as never,
429
+ });
430
+ return data;
431
+ }
432
+
433
+ /**
434
+ * Get collection details.
435
+ * Wire endpoint: GET /api/v2/collections/{collection_id} (getCollection)
436
+ */
437
+ async getCollectionRequest(collectionId: string): Promise<unknown> {
438
+ const { data } = await this.gen.GET("/api/v2/collections/{collection_id}", {
439
+ params: { path: { collection_id: collectionId } },
440
+ });
441
+ return data;
442
+ }
443
+
444
+ /**
445
+ * Insert / upsert records in a batch.
446
+ * Wire endpoint: POST /api/v2/collections/{collection_id}/records/batch (insertRecords)
447
+ */
448
+ async insertRecordsRequest(
449
+ collectionId: string,
450
+ body: Record<string, unknown>,
451
+ ): Promise<unknown> {
452
+ const { data } = await this.gen.POST(
453
+ "/api/v2/collections/{collection_id}/records/batch",
454
+ {
455
+ params: { path: { collection_id: collectionId } },
456
+ body: body as never,
457
+ },
458
+ );
459
+ return data;
460
+ }
461
+
462
+ /**
463
+ * Get a single record by ID.
464
+ * Wire endpoint: GET /api/v2/collections/{collection_id}/records/{record_id} (getRecord)
465
+ */
466
+ async getRecordRequest(
467
+ collectionId: string,
468
+ recordId: string,
469
+ includeVector: boolean,
470
+ includeText: boolean,
471
+ ): Promise<unknown> {
472
+ const { data } = await this.gen.GET(
473
+ "/api/v2/collections/{collection_id}/records/{record_id}",
474
+ {
475
+ params: {
476
+ path: { collection_id: collectionId, record_id: recordId },
477
+ query: { include_vector: includeVector, include_text: includeText },
478
+ },
479
+ },
480
+ );
481
+ return data;
482
+ }
483
+
484
+ /**
485
+ * Delete a single record by ID.
486
+ * Wire endpoint: DELETE /api/v2/collections/{collection_id}/records/{record_id} (deleteRecord)
487
+ */
488
+ async deleteRecordRequest(collectionId: string, recordId: string): Promise<void> {
489
+ await this.gen.DELETE(
490
+ "/api/v2/collections/{collection_id}/records/{record_id}",
491
+ {
492
+ params: { path: { collection_id: collectionId, record_id: recordId } },
493
+ },
494
+ );
495
+ }
496
+
497
+ /**
498
+ * Vector similarity search.
499
+ * Wire endpoint: POST /api/v2/collections/{collection_id}/search (searchRecords)
500
+ */
501
+ async searchRecordsRequest(
502
+ collectionId: string,
503
+ body: Record<string, unknown>,
504
+ ): Promise<unknown> {
505
+ const { data } = await this.gen.POST(
506
+ "/api/v2/collections/{collection_id}/search",
507
+ {
508
+ params: { path: { collection_id: collectionId } },
509
+ body: body as never,
510
+ },
511
+ );
512
+ return data;
513
+ }
514
+
515
+ // =========================================================================
516
+ // Graph Operations
517
+ // =========================================================================
518
+
519
+ /**
520
+ * Get a handle to a graph for fluent operations
521
+ */
522
+ graph(name: string): GraphHandle {
523
+ return new GraphHandle(this, name);
524
+ }
525
+
526
+ /**
527
+ * Create a graph builder.
528
+ *
529
+ * Wire endpoint: POST /api/v2/graphs
530
+ * OpenAPI operationId: createGraph
531
+ *
532
+ * The argument is the server-true `graph_id` (unique identifier). Use
533
+ * `.name(...)` on the returned builder to set the optional display name.
534
+ */
535
+ createGraph(graphId: string): GraphBuilder {
536
+ return new GraphBuilder(this, graphId);
537
+ }
538
+
539
+ /**
540
+ * Delete a graph
541
+ *
542
+ * Routed through the generated typed transport (TD-126 Phase 4).
543
+ * Wire endpoint: DELETE /api/v2/graphs/{graph_id}
544
+ * OpenAPI operationId: deleteGraph
545
+ */
546
+ async deleteGraph(name: string): Promise<void> {
547
+ await this.gen.DELETE("/api/v2/graphs/{graph_id}", {
548
+ params: { path: { graph_id: name } },
549
+ });
550
+ }
551
+
552
+ /**
553
+ * List all graphs
554
+ *
555
+ * Routed through the generated typed transport (TD-126 Phase 4).
556
+ * Wire endpoint: GET /api/v2/graphs
557
+ * OpenAPI operationId: listGraphs
558
+ */
559
+ async listGraphs(): Promise<GraphInfo[]> {
560
+ const { data } = await this.gen.GET("/api/v2/graphs", {});
561
+ return ((data?.graphs ?? []) as unknown[]) as GraphInfo[];
562
+ }
563
+
564
+ // =========================================================================
565
+ // Typed transport for graph ops (TD-126 Phase 4)
566
+ //
567
+ // Seam methods routing the graph builders' / handle's wire calls through the
568
+ // generated typed client, mirroring the core collection / record / search
569
+ // seams above. The public graph API (graph.ts builders + handle) is
570
+ // unchanged; only the wire dispatch moves off hand-built URL strings.
571
+ // =========================================================================
572
+
573
+ /**
574
+ * Create a graph collection.
575
+ * Wire endpoint: POST /api/v2/graphs (createGraph)
576
+ */
577
+ async createGraphRequest(body: Record<string, unknown>): Promise<unknown> {
578
+ const { data } = await this.gen.POST("/api/v2/graphs", {
579
+ body: body as never,
580
+ });
581
+ return data;
582
+ }
583
+
584
+ /**
585
+ * Get a graph collection by id.
586
+ * Wire endpoint: GET /api/v2/graphs/{graph_id} (getGraph)
587
+ */
588
+ async getGraphRequest(graphId: string): Promise<unknown> {
589
+ const { data } = await this.gen.GET("/api/v2/graphs/{graph_id}", {
590
+ params: { path: { graph_id: graphId } },
591
+ });
592
+ return data;
593
+ }
594
+
595
+ /**
596
+ * Get graph statistics.
597
+ * Wire endpoint: GET /api/v2/graphs/{graph_id}/stats (getGraphStats)
598
+ */
599
+ async getGraphStatsRequest(graphId: string): Promise<unknown> {
600
+ const { data } = await this.gen.GET("/api/v2/graphs/{graph_id}/stats", {
601
+ params: { path: { graph_id: graphId } },
602
+ });
603
+ return data;
604
+ }
605
+
606
+ /**
607
+ * Create a node in a graph.
608
+ * Wire endpoint: POST /api/v2/graphs/{graph_id}/nodes (createNode)
609
+ */
610
+ async createNodeRequest(
611
+ graphId: string,
612
+ body: Record<string, unknown>,
613
+ ): Promise<unknown> {
614
+ const { data } = await this.gen.POST("/api/v2/graphs/{graph_id}/nodes", {
615
+ params: { path: { graph_id: graphId } },
616
+ body: body as never,
617
+ });
618
+ return data;
619
+ }
620
+
621
+ /**
622
+ * Create multiple nodes in a single call.
623
+ * Wire endpoint: POST /api/v2/graphs/{graph_id}/nodes/batch (batchCreateNodes)
624
+ */
625
+ async batchCreateNodesRequest(
626
+ graphId: string,
627
+ body: Record<string, unknown>,
628
+ ): Promise<unknown> {
629
+ const { data } = await this.gen.POST(
630
+ "/api/v2/graphs/{graph_id}/nodes/batch",
631
+ {
632
+ params: { path: { graph_id: graphId } },
633
+ body: body as never,
634
+ },
635
+ );
636
+ return data;
637
+ }
638
+
639
+ /**
640
+ * Get a node by id.
641
+ * Wire endpoint: GET /api/v2/graphs/{graph_id}/nodes/{node_id} (getNode)
642
+ */
643
+ async getNodeRequest(graphId: string, nodeId: string): Promise<unknown> {
644
+ const { data } = await this.gen.GET(
645
+ "/api/v2/graphs/{graph_id}/nodes/{node_id}",
646
+ {
647
+ params: { path: { graph_id: graphId, node_id: nodeId } },
648
+ },
649
+ );
650
+ return data;
651
+ }
652
+
653
+ /**
654
+ * Delete a node by id.
655
+ * Wire endpoint: DELETE /api/v2/graphs/{graph_id}/nodes/{node_id} (deleteNode)
656
+ */
657
+ async deleteNodeRequest(graphId: string, nodeId: string): Promise<void> {
658
+ await this.gen.DELETE("/api/v2/graphs/{graph_id}/nodes/{node_id}", {
659
+ params: { path: { graph_id: graphId, node_id: nodeId } },
660
+ });
661
+ }
662
+
663
+ /**
664
+ * Create an edge in a graph.
665
+ * Wire endpoint: POST /api/v2/graphs/{graph_id}/edges (createEdge)
666
+ */
667
+ async createEdgeRequest(
668
+ graphId: string,
669
+ body: Record<string, unknown>,
670
+ ): Promise<unknown> {
671
+ const { data } = await this.gen.POST("/api/v2/graphs/{graph_id}/edges", {
672
+ params: { path: { graph_id: graphId } },
673
+ body: body as never,
674
+ });
675
+ return data;
676
+ }
677
+
678
+ /**
679
+ * Create multiple edges in a single call.
680
+ * Wire endpoint: POST /api/v2/graphs/{graph_id}/edges/batch (batchCreateEdges)
681
+ */
682
+ async batchCreateEdgesRequest(
683
+ graphId: string,
684
+ body: Record<string, unknown>,
685
+ ): Promise<unknown> {
686
+ const { data } = await this.gen.POST(
687
+ "/api/v2/graphs/{graph_id}/edges/batch",
688
+ {
689
+ params: { path: { graph_id: graphId } },
690
+ body: body as never,
691
+ },
692
+ );
693
+ return data;
694
+ }
695
+
696
+ /**
697
+ * Traverse a graph from a start node.
698
+ * Wire endpoint: POST /api/v2/graphs/{graph_id}/traverse (traverseGraph)
699
+ */
700
+ async traverseGraphRequest(
701
+ graphId: string,
702
+ body: Record<string, unknown>,
703
+ ): Promise<unknown> {
704
+ const { data } = await this.gen.POST(
705
+ "/api/v2/graphs/{graph_id}/traverse",
706
+ {
707
+ params: { path: { graph_id: graphId } },
708
+ body: body as never,
709
+ },
710
+ );
711
+ return data;
712
+ }
713
+
714
+ // =========================================================================
715
+ // Health and Monitoring
716
+ // =========================================================================
717
+
718
+ /**
719
+ * Check if the server is healthy
720
+ *
721
+ * Routed through the generated typed transport (TD-126 Phase 4).
722
+ * Wire endpoint: GET /health
723
+ * OpenAPI operationId: getHealth
724
+ */
725
+ async health(): Promise<HealthStatus> {
726
+ const { data } = await this.gen.GET("/health", {});
727
+ return data as unknown as HealthStatus;
728
+ }
729
+
730
+ /**
731
+ * Kubernetes-style liveness probe
732
+ *
733
+ * Routed through the generated typed transport (TD-126 Phase 4).
734
+ * Wire endpoint: GET /health/live
735
+ * OpenAPI operationId: getLiveness
736
+ */
737
+ async healthLive(): Promise<ProbeResponse> {
738
+ const { data } = await this.gen.GET("/health/live", {});
739
+ return data as unknown as ProbeResponse;
740
+ }
741
+
742
+ /**
743
+ * Kubernetes-style readiness probe
744
+ *
745
+ * Routed through the generated typed transport (TD-126 Phase 4).
746
+ * Wire endpoint: GET /health/ready
747
+ * OpenAPI operationId: getReadiness
748
+ */
749
+ async healthReady(): Promise<ProbeResponse> {
750
+ const { data } = await this.gen.GET("/health/ready", {});
751
+ return data as unknown as ProbeResponse;
752
+ }
753
+
754
+ /**
755
+ * Check if the server is reachable
756
+ */
757
+ async ping(): Promise<boolean> {
758
+ try {
759
+ await this.health();
760
+ return true;
761
+ } catch {
762
+ return false;
763
+ }
764
+ }
765
+ }
766
+
767
+ // ============================================================================
768
+ // CLIENT BUILDER
769
+ // ============================================================================
770
+
771
+ /**
772
+ * Builder for creating a ProximaDB client with custom configuration
773
+ */
774
+ export class ClientBuilder {
775
+ private config: Partial<ClientConfig> = {};
776
+
777
+ /**
778
+ * Set the server URL
779
+ */
780
+ url(serverUrl: string): ClientBuilder {
781
+ this.config.url = serverUrl;
782
+ return this;
783
+ }
784
+
785
+ /**
786
+ * Set the request timeout in milliseconds
787
+ */
788
+ timeoutMs(timeout: number): ClientBuilder {
789
+ this.config.timeoutMs = timeout;
790
+ return this;
791
+ }
792
+
793
+ /**
794
+ * Set the maximum number of retries
795
+ */
796
+ maxRetries(retries: number): ClientBuilder {
797
+ this.config.maxRetries = retries;
798
+ return this;
799
+ }
800
+
801
+ /**
802
+ * Set the API key for authentication
803
+ */
804
+ apiKey(key: string): ClientBuilder {
805
+ this.config.apiKey = key;
806
+ return this;
807
+ }
808
+
809
+ /**
810
+ * Enable or disable connection pooling
811
+ */
812
+ poolConnections(enable: boolean): ClientBuilder {
813
+ this.config.poolConnections = enable;
814
+ return this;
815
+ }
816
+
817
+ /**
818
+ * Set maximum idle connections in pool
819
+ */
820
+ maxIdleConnections(max: number): ClientBuilder {
821
+ this.config.maxIdleConnections = max;
822
+ return this;
823
+ }
824
+
825
+ /**
826
+ * Build the ProximaDB client
827
+ */
828
+ build(): ProximaDBClient {
829
+ return new ProximaDBClient(this.config);
830
+ }
831
+
832
+ /**
833
+ * Connect to the server (alias for build)
834
+ */
835
+ connect(): ProximaDBClient {
836
+ return this.build();
837
+ }
838
+ }
839
+
840
+ // ============================================================================
841
+ // CONVENIENCE FUNCTIONS
842
+ // ============================================================================
843
+
844
+ /**
845
+ * Create a ProximaDB client connected to the given URL
846
+ */
847
+ export function connect(url: string = "http://localhost:5678"): ProximaDBClient {
848
+ return ProximaDBClient.connect(url);
849
+ }
850
+
851
+ /**
852
+ * Create a ProximaDB client with REST protocol
853
+ */
854
+ export function connectRest(url: string = "http://localhost:5678"): ProximaDBClient {
855
+ return ProximaDBClient.connect(url);
856
+ }