@arke-institute/sdk 0.1.3 → 2.0.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 (64) hide show
  1. package/README.md +126 -184
  2. package/dist/generated/index.cjs +19 -0
  3. package/dist/generated/index.cjs.map +1 -0
  4. package/dist/generated/index.d.cts +6192 -0
  5. package/dist/generated/index.d.ts +6192 -0
  6. package/dist/generated/index.js +1 -0
  7. package/dist/generated/index.js.map +1 -0
  8. package/dist/index-BrXke2kI.d.ts +302 -0
  9. package/dist/index-FHcLPBSV.d.cts +302 -0
  10. package/dist/index.cjs +188 -4254
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +62 -7
  13. package/dist/index.d.ts +62 -7
  14. package/dist/index.js +168 -4226
  15. package/dist/index.js.map +1 -1
  16. package/dist/operations/index.cjs +113 -0
  17. package/dist/operations/index.cjs.map +1 -0
  18. package/dist/operations/index.d.cts +3 -0
  19. package/dist/operations/index.d.ts +3 -0
  20. package/dist/operations/index.js +84 -0
  21. package/dist/operations/index.js.map +1 -0
  22. package/package.json +44 -53
  23. package/dist/client-dAk3E64p.d.cts +0 -183
  24. package/dist/client-dAk3E64p.d.ts +0 -183
  25. package/dist/collections/index.cjs +0 -233
  26. package/dist/collections/index.cjs.map +0 -1
  27. package/dist/collections/index.d.cts +0 -9
  28. package/dist/collections/index.d.ts +0 -9
  29. package/dist/collections/index.js +0 -205
  30. package/dist/collections/index.js.map +0 -1
  31. package/dist/content/index.cjs +0 -591
  32. package/dist/content/index.cjs.map +0 -1
  33. package/dist/content/index.d.cts +0 -516
  34. package/dist/content/index.d.ts +0 -516
  35. package/dist/content/index.js +0 -558
  36. package/dist/content/index.js.map +0 -1
  37. package/dist/edit/index.cjs +0 -1503
  38. package/dist/edit/index.cjs.map +0 -1
  39. package/dist/edit/index.d.cts +0 -78
  40. package/dist/edit/index.d.ts +0 -78
  41. package/dist/edit/index.js +0 -1447
  42. package/dist/edit/index.js.map +0 -1
  43. package/dist/errors-3L7IiHcr.d.cts +0 -480
  44. package/dist/errors-BTe8GKRQ.d.ts +0 -480
  45. package/dist/errors-CT7yzKkU.d.cts +0 -874
  46. package/dist/errors-CT7yzKkU.d.ts +0 -874
  47. package/dist/graph/index.cjs +0 -427
  48. package/dist/graph/index.cjs.map +0 -1
  49. package/dist/graph/index.d.cts +0 -485
  50. package/dist/graph/index.d.ts +0 -485
  51. package/dist/graph/index.js +0 -396
  52. package/dist/graph/index.js.map +0 -1
  53. package/dist/query/index.cjs +0 -356
  54. package/dist/query/index.cjs.map +0 -1
  55. package/dist/query/index.d.cts +0 -636
  56. package/dist/query/index.d.ts +0 -636
  57. package/dist/query/index.js +0 -328
  58. package/dist/query/index.js.map +0 -1
  59. package/dist/upload/index.cjs +0 -1634
  60. package/dist/upload/index.cjs.map +0 -1
  61. package/dist/upload/index.d.cts +0 -150
  62. package/dist/upload/index.d.ts +0 -150
  63. package/dist/upload/index.js +0 -1597
  64. package/dist/upload/index.js.map +0 -1
@@ -1,427 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/graph/index.ts
21
- var graph_exports = {};
22
- __export(graph_exports, {
23
- GraphClient: () => GraphClient,
24
- GraphEntityNotFoundError: () => GraphEntityNotFoundError,
25
- GraphError: () => GraphError,
26
- NetworkError: () => NetworkError,
27
- NoPathFoundError: () => NoPathFoundError
28
- });
29
- module.exports = __toCommonJS(graph_exports);
30
-
31
- // src/graph/errors.ts
32
- var GraphError = class extends Error {
33
- constructor(message, code = "GRAPH_ERROR", details) {
34
- super(message);
35
- this.code = code;
36
- this.details = details;
37
- this.name = "GraphError";
38
- }
39
- };
40
- var GraphEntityNotFoundError = class extends GraphError {
41
- constructor(canonicalId) {
42
- super(`Graph entity not found: ${canonicalId}`, "ENTITY_NOT_FOUND", { canonicalId });
43
- this.name = "GraphEntityNotFoundError";
44
- }
45
- };
46
- var NoPathFoundError = class extends GraphError {
47
- constructor(sourceIds, targetIds) {
48
- super(
49
- `No path found between sources and targets`,
50
- "NO_PATH_FOUND",
51
- { sourceIds, targetIds }
52
- );
53
- this.name = "NoPathFoundError";
54
- }
55
- };
56
- var NetworkError = class extends GraphError {
57
- constructor(message, statusCode) {
58
- super(message, "NETWORK_ERROR", { statusCode });
59
- this.statusCode = statusCode;
60
- this.name = "NetworkError";
61
- }
62
- };
63
-
64
- // src/graph/client.ts
65
- var GraphClient = class {
66
- constructor(config) {
67
- this.baseUrl = config.gatewayUrl.replace(/\/$/, "");
68
- this.fetchImpl = config.fetchImpl ?? fetch;
69
- }
70
- // ---------------------------------------------------------------------------
71
- // Request helpers
72
- // ---------------------------------------------------------------------------
73
- buildUrl(path, query) {
74
- const url = new URL(`${this.baseUrl}${path}`);
75
- if (query) {
76
- Object.entries(query).forEach(([key, value]) => {
77
- if (value !== void 0 && value !== null) {
78
- url.searchParams.set(key, String(value));
79
- }
80
- });
81
- }
82
- return url.toString();
83
- }
84
- async request(path, options = {}) {
85
- const url = this.buildUrl(path, options.query);
86
- const headers = new Headers({ "Content-Type": "application/json" });
87
- if (options.headers) {
88
- Object.entries(options.headers).forEach(([k, v]) => {
89
- if (v !== void 0) headers.set(k, v);
90
- });
91
- }
92
- let response;
93
- try {
94
- response = await this.fetchImpl(url, { ...options, headers });
95
- } catch (err) {
96
- throw new NetworkError(
97
- err instanceof Error ? err.message : "Network request failed"
98
- );
99
- }
100
- if (response.ok) {
101
- const contentType = response.headers.get("content-type") || "";
102
- if (contentType.includes("application/json")) {
103
- return await response.json();
104
- }
105
- return await response.text();
106
- }
107
- let body;
108
- const text = await response.text();
109
- try {
110
- body = JSON.parse(text);
111
- } catch {
112
- body = text;
113
- }
114
- if (response.status === 404) {
115
- throw new GraphError(
116
- body?.message || "Not found",
117
- "NOT_FOUND",
118
- body
119
- );
120
- }
121
- const message = body?.error && typeof body.error === "string" ? body.error : body?.message && typeof body.message === "string" ? body.message : `Request failed with status ${response.status}`;
122
- throw new GraphError(message, "HTTP_ERROR", {
123
- status: response.status,
124
- body
125
- });
126
- }
127
- // ---------------------------------------------------------------------------
128
- // Code-based Lookups (indexed in GraphDB)
129
- // ---------------------------------------------------------------------------
130
- /**
131
- * Query entities by code with optional type filter.
132
- *
133
- * @param code - Entity code to search for
134
- * @param type - Optional entity type filter
135
- * @returns Matching entities
136
- *
137
- * @example
138
- * ```typescript
139
- * // Find by code
140
- * const entities = await graph.queryByCode('person_john');
141
- *
142
- * // With type filter
143
- * const people = await graph.queryByCode('john', 'person');
144
- * ```
145
- */
146
- async queryByCode(code, type) {
147
- const response = await this.request(
148
- "/graphdb/entity/query",
149
- {
150
- method: "POST",
151
- body: JSON.stringify({ code, type })
152
- }
153
- );
154
- if (!response.found || !response.entity) {
155
- return [];
156
- }
157
- return [response.entity];
158
- }
159
- /**
160
- * Look up entities by code across all PIs.
161
- *
162
- * @param code - Entity code to search for
163
- * @param type - Optional entity type filter
164
- * @returns Matching entities
165
- *
166
- * @example
167
- * ```typescript
168
- * const entities = await graph.lookupByCode('alice_austen', 'person');
169
- * ```
170
- */
171
- async lookupByCode(code, type) {
172
- const response = await this.request(
173
- "/graphdb/entities/lookup-by-code",
174
- {
175
- method: "POST",
176
- body: JSON.stringify({ code, type })
177
- }
178
- );
179
- return response.entities || [];
180
- }
181
- // ---------------------------------------------------------------------------
182
- // PI-based Operations
183
- // ---------------------------------------------------------------------------
184
- /**
185
- * List entities extracted from a specific PI or multiple PIs.
186
- *
187
- * This returns knowledge graph entities (persons, places, events, etc.)
188
- * that were extracted from the given PI(s), not the PI entity itself.
189
- *
190
- * @param pi - Single PI or array of PIs
191
- * @param options - Filter options
192
- * @returns Extracted entities from the PI(s)
193
- *
194
- * @example
195
- * ```typescript
196
- * // From single PI
197
- * const entities = await graph.listEntitiesFromPi('01K75HQQXNTDG7BBP7PS9AWYAN');
198
- *
199
- * // With type filter
200
- * const people = await graph.listEntitiesFromPi('01K75HQQXNTDG7BBP7PS9AWYAN', { type: 'person' });
201
- *
202
- * // From multiple PIs
203
- * const all = await graph.listEntitiesFromPi(['pi-1', 'pi-2']);
204
- * ```
205
- */
206
- async listEntitiesFromPi(pi, options = {}) {
207
- const pis = Array.isArray(pi) ? pi : [pi];
208
- const response = await this.request(
209
- "/graphdb/entities/list",
210
- {
211
- method: "POST",
212
- body: JSON.stringify({
213
- pis,
214
- type: options.type
215
- })
216
- }
217
- );
218
- return response.entities || [];
219
- }
220
- /**
221
- * Get entities with their relationships from a PI.
222
- *
223
- * This is an optimized query that returns entities along with all their
224
- * relationship data in a single request.
225
- *
226
- * @param pi - Persistent Identifier
227
- * @param type - Optional entity type filter
228
- * @returns Entities with relationships
229
- *
230
- * @example
231
- * ```typescript
232
- * const entities = await graph.getEntitiesWithRelationships('01K75HQQXNTDG7BBP7PS9AWYAN');
233
- * entities.forEach(e => {
234
- * console.log(`${e.label} has ${e.relationships.length} relationships`);
235
- * });
236
- * ```
237
- */
238
- async getEntitiesWithRelationships(pi, type) {
239
- const response = await this.request(
240
- "/graphdb/pi/entities-with-relationships",
241
- {
242
- method: "POST",
243
- body: JSON.stringify({ pi, type })
244
- }
245
- );
246
- return response.entities || [];
247
- }
248
- /**
249
- * Get the lineage (ancestors and/or descendants) of a PI.
250
- *
251
- * This traverses the PI hierarchy (parent_pi/children_pi relationships)
252
- * which is indexed in GraphDB for fast lookups.
253
- *
254
- * @param pi - Source PI
255
- * @param direction - 'ancestors', 'descendants', or 'both'
256
- * @param maxHops - Maximum depth to traverse (default: 10)
257
- * @returns Lineage data with PIs at each hop level
258
- *
259
- * @example
260
- * ```typescript
261
- * // Get ancestors (parent chain)
262
- * const lineage = await graph.getLineage('01K75HQQXNTDG7BBP7PS9AWYAN', 'ancestors');
263
- *
264
- * // Get both directions
265
- * const full = await graph.getLineage('01K75HQQXNTDG7BBP7PS9AWYAN', 'both');
266
- * ```
267
- */
268
- async getLineage(pi, direction = "both", maxHops = 10) {
269
- return this.request("/graphdb/pi/lineage", {
270
- method: "POST",
271
- body: JSON.stringify({ sourcePi: pi, direction, maxHops })
272
- });
273
- }
274
- // ---------------------------------------------------------------------------
275
- // Relationship Operations
276
- // ---------------------------------------------------------------------------
277
- /**
278
- * Get relationships for an entity from the GraphDB index.
279
- *
280
- * **Important distinction from ContentClient.getRelationships():**
281
- * - **ContentClient.getRelationships()**: Returns OUTBOUND relationships only
282
- * (from the entity's relationships.json in IPFS - source of truth)
283
- * - **GraphClient.getRelationships()**: Returns BOTH inbound AND outbound
284
- * relationships (from the indexed GraphDB mirror)
285
- *
286
- * Use this method when you need to find "what references this entity" (inbound)
287
- * or want a complete bidirectional view.
288
- *
289
- * @param id - Entity identifier (works for both PIs and KG entities)
290
- * @param direction - Filter by direction: 'outgoing', 'incoming', or 'both' (default)
291
- * @returns Array of relationships with direction indicator
292
- *
293
- * @example
294
- * ```typescript
295
- * // Get all relationships (both directions)
296
- * const all = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN');
297
- *
298
- * // Get only inbound relationships ("who references this entity?")
299
- * const incoming = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN', 'incoming');
300
- *
301
- * // Get only outbound relationships (similar to IPFS, but from index)
302
- * const outgoing = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN', 'outgoing');
303
- *
304
- * // Process by direction
305
- * const rels = await graph.getRelationships('entity-id');
306
- * rels.forEach(r => {
307
- * if (r.direction === 'incoming') {
308
- * console.log(`${r.target_label} references this entity via ${r.predicate}`);
309
- * } else {
310
- * console.log(`This entity ${r.predicate} -> ${r.target_label}`);
311
- * }
312
- * });
313
- * ```
314
- */
315
- async getRelationships(id, direction = "both") {
316
- const response = await this.request(`/graphdb/relationships/${encodeURIComponent(id)}`);
317
- if (!response.found || !response.relationships) {
318
- return [];
319
- }
320
- let relationships = response.relationships;
321
- if (direction !== "both") {
322
- relationships = relationships.filter((rel) => rel.direction === direction);
323
- }
324
- return relationships.map((rel) => ({
325
- direction: rel.direction,
326
- predicate: rel.predicate,
327
- target_id: rel.target_id,
328
- target_code: rel.target_code || "",
329
- target_label: rel.target_label,
330
- target_type: rel.target_type,
331
- properties: rel.properties,
332
- source_pi: rel.source_pi,
333
- created_at: rel.created_at
334
- }));
335
- }
336
- // ---------------------------------------------------------------------------
337
- // Path Finding
338
- // ---------------------------------------------------------------------------
339
- /**
340
- * Find shortest paths between sets of entities.
341
- *
342
- * @param sourceIds - Starting entity IDs
343
- * @param targetIds - Target entity IDs
344
- * @param options - Path finding options
345
- * @returns Found paths
346
- *
347
- * @example
348
- * ```typescript
349
- * const paths = await graph.findPaths(
350
- * ['entity-alice'],
351
- * ['entity-bob'],
352
- * { max_depth: 4, direction: 'both' }
353
- * );
354
- *
355
- * paths.forEach(path => {
356
- * console.log(`Path of length ${path.length}:`);
357
- * path.edges.forEach(e => {
358
- * console.log(` ${e.subject_label} -[${e.predicate}]-> ${e.object_label}`);
359
- * });
360
- * });
361
- * ```
362
- */
363
- async findPaths(sourceIds, targetIds, options = {}) {
364
- const response = await this.request("/graphdb/paths/between", {
365
- method: "POST",
366
- body: JSON.stringify({
367
- source_ids: sourceIds,
368
- target_ids: targetIds,
369
- max_depth: options.max_depth,
370
- direction: options.direction,
371
- limit: options.limit
372
- })
373
- });
374
- return response.paths || [];
375
- }
376
- /**
377
- * Find entities of a specific type reachable from starting entities.
378
- *
379
- * @param startIds - Starting entity IDs
380
- * @param targetType - Type of entities to find
381
- * @param options - Search options
382
- * @returns Reachable entities of the specified type
383
- *
384
- * @example
385
- * ```typescript
386
- * // Find all people reachable from an event
387
- * const people = await graph.findReachable(
388
- * ['event-id'],
389
- * 'person',
390
- * { max_depth: 3 }
391
- * );
392
- * ```
393
- */
394
- async findReachable(startIds, targetType, options = {}) {
395
- const response = await this.request(
396
- "/graphdb/paths/reachable",
397
- {
398
- method: "POST",
399
- body: JSON.stringify({
400
- start_ids: startIds,
401
- target_type: targetType,
402
- max_depth: options.max_depth,
403
- direction: options.direction,
404
- limit: options.limit
405
- })
406
- }
407
- );
408
- return response.entities || [];
409
- }
410
- /**
411
- * Check the health of the graph service.
412
- *
413
- * @returns Health status
414
- */
415
- async health() {
416
- return this.request("/graphdb/health", { method: "GET" });
417
- }
418
- };
419
- // Annotate the CommonJS export names for ESM import in node:
420
- 0 && (module.exports = {
421
- GraphClient,
422
- GraphEntityNotFoundError,
423
- GraphError,
424
- NetworkError,
425
- NoPathFoundError
426
- });
427
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/graph/index.ts","../../src/graph/errors.ts","../../src/graph/client.ts"],"sourcesContent":["/**\n * Graph package for the Arke SDK\n *\n * Provides read-only access to the GraphDB Gateway service, which is an indexed\n * mirror of entity data stored in IPFS.\n *\n * Use GraphClient for:\n * - **Bidirectional relationship queries** (IPFS only stores outbound)\n * - **Path finding** between entities\n * - **PI lineage queries** (ancestors/descendants)\n * - **Code-based lookups** (indexed for fast search)\n *\n * For entity CRUD operations, use ContentClient or EditClient (IPFS source of truth).\n *\n * @example\n * ```typescript\n * import { GraphClient } from '@arke-institute/sdk/graph';\n *\n * const graph = new GraphClient({\n * gatewayUrl: 'https://gateway.arke.institute',\n * });\n *\n * // Get ALL relationships (both directions - only GraphDB has inbound)\n * const allRels = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN');\n *\n * // Get only inbound relationships (\"who references this entity?\")\n * const incoming = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN', 'incoming');\n *\n * // Find paths between entities\n * const paths = await graph.findPaths(['entity-1'], ['entity-2']);\n *\n * // Get PI lineage (ancestors/descendants)\n * const lineage = await graph.getLineage('01K75HQQXNTDG7BBP7PS9AWYAN', 'ancestors');\n * ```\n */\n\nexport { GraphClient, type GraphClientConfig } from './client.js';\nexport * from './types.js';\nexport * from './errors.js';\n","/**\n * Graph package error classes for the Arke SDK\n */\n\n/**\n * Base error class for graph operations\n */\nexport class GraphError extends Error {\n constructor(\n message: string,\n public code: string = 'GRAPH_ERROR',\n public details?: unknown\n ) {\n super(message);\n this.name = 'GraphError';\n }\n}\n\n/**\n * Thrown when an entity is not found by canonical ID\n */\nexport class GraphEntityNotFoundError extends GraphError {\n constructor(canonicalId: string) {\n super(`Graph entity not found: ${canonicalId}`, 'ENTITY_NOT_FOUND', { canonicalId });\n this.name = 'GraphEntityNotFoundError';\n }\n}\n\n/**\n * Thrown when no paths are found between entities\n */\nexport class NoPathFoundError extends GraphError {\n constructor(sourceIds: string[], targetIds: string[]) {\n super(\n `No path found between sources and targets`,\n 'NO_PATH_FOUND',\n { sourceIds, targetIds }\n );\n this.name = 'NoPathFoundError';\n }\n}\n\n/**\n * Thrown when a network error occurs\n */\nexport class NetworkError extends GraphError {\n constructor(message: string, public statusCode?: number) {\n super(message, 'NETWORK_ERROR', { statusCode });\n this.name = 'NetworkError';\n }\n}\n","import {\n GraphError,\n NetworkError,\n} from './errors.js';\nimport type {\n GraphEntity,\n EntityWithRelationships,\n Relationship,\n RelationshipDirection,\n Path,\n PathOptions,\n ReachableOptions,\n ListFromPiOptions,\n EntityQueryResponse,\n EntitiesWithRelationshipsResponse,\n PathsResponse,\n LineageResponse,\n} from './types.js';\n\n/**\n * Configuration for GraphClient\n */\nexport interface GraphClientConfig {\n /**\n * Gateway base URL (e.g., https://gateway.arke.institute).\n * The client will call /graphdb/* endpoints for GraphDB Gateway.\n */\n gatewayUrl: string;\n /**\n * Optional custom fetch implementation (useful for testing).\n */\n fetchImpl?: typeof fetch;\n}\n\ntype JsonBody = Record<string, unknown>;\n\n/**\n * Client for querying entity relationships and graph traversal from the Arke knowledge graph.\n *\n * The GraphDB is an indexed mirror of entity data stored in IPFS. Use this client for:\n * - **Bidirectional relationship queries** (IPFS only stores outbound relationships)\n * - **Path finding** between entities\n * - **Lineage queries** (PI ancestors/descendants)\n * - **Code-based lookups** (indexed for fast search)\n * - **Listing extracted entities** from a PI\n *\n * For entity CRUD operations, use ContentClient (source of truth in IPFS).\n * For write operations, use EditClient.\n *\n * All endpoints are public and do not require authentication.\n *\n * @example\n * ```typescript\n * const graph = new GraphClient({\n * gatewayUrl: 'https://gateway.arke.institute',\n * });\n *\n * // Get BOTH inbound and outbound relationships (GraphDB indexed)\n * const allRels = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN');\n *\n * // Get only inbound relationships (\"who references this entity?\")\n * const incoming = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN', 'incoming');\n *\n * // Find paths between entities\n * const paths = await graph.findPaths(['entity-1'], ['entity-2'], { max_depth: 4 });\n *\n * // Get PI lineage\n * const lineage = await graph.getLineage('01K75HQQXNTDG7BBP7PS9AWYAN', 'ancestors');\n * ```\n */\nexport class GraphClient {\n private baseUrl: string;\n private fetchImpl: typeof fetch;\n\n constructor(config: GraphClientConfig) {\n this.baseUrl = config.gatewayUrl.replace(/\\/$/, '');\n this.fetchImpl = config.fetchImpl ?? fetch;\n }\n\n // ---------------------------------------------------------------------------\n // Request helpers\n // ---------------------------------------------------------------------------\n\n private buildUrl(path: string, query?: Record<string, string | number | boolean | undefined>) {\n const url = new URL(`${this.baseUrl}${path}`);\n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n });\n }\n return url.toString();\n }\n\n private async request<T>(\n path: string,\n options: RequestInit & {\n query?: Record<string, string | number | boolean | undefined>;\n } = {}\n ): Promise<T> {\n const url = this.buildUrl(path, options.query);\n const headers = new Headers({ 'Content-Type': 'application/json' });\n if (options.headers) {\n Object.entries(options.headers).forEach(([k, v]) => {\n if (v !== undefined) headers.set(k, v as string);\n });\n }\n\n let response: Response;\n try {\n response = await this.fetchImpl(url, { ...options, headers });\n } catch (err) {\n throw new NetworkError(\n err instanceof Error ? err.message : 'Network request failed'\n );\n }\n\n if (response.ok) {\n const contentType = response.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n return (await response.json()) as T;\n }\n return (await response.text()) as unknown as T;\n }\n\n let body: unknown;\n const text = await response.text();\n try {\n body = JSON.parse(text);\n } catch {\n body = text;\n }\n\n // Handle 404 specifically\n if (response.status === 404) {\n throw new GraphError(\n (body as JsonBody)?.message as string || 'Not found',\n 'NOT_FOUND',\n body\n );\n }\n\n const message =\n (body as JsonBody)?.error && typeof (body as JsonBody).error === 'string'\n ? ((body as JsonBody).error as string)\n : (body as JsonBody)?.message && typeof (body as JsonBody).message === 'string'\n ? ((body as JsonBody).message as string)\n : `Request failed with status ${response.status}`;\n\n throw new GraphError(message, 'HTTP_ERROR', {\n status: response.status,\n body,\n });\n }\n\n // ---------------------------------------------------------------------------\n // Code-based Lookups (indexed in GraphDB)\n // ---------------------------------------------------------------------------\n\n /**\n * Query entities by code with optional type filter.\n *\n * @param code - Entity code to search for\n * @param type - Optional entity type filter\n * @returns Matching entities\n *\n * @example\n * ```typescript\n * // Find by code\n * const entities = await graph.queryByCode('person_john');\n *\n * // With type filter\n * const people = await graph.queryByCode('john', 'person');\n * ```\n */\n async queryByCode(code: string, type?: string): Promise<GraphEntity[]> {\n const response = await this.request<EntityQueryResponse>(\n '/graphdb/entity/query',\n {\n method: 'POST',\n body: JSON.stringify({ code, type }),\n }\n );\n\n if (!response.found || !response.entity) {\n return [];\n }\n\n return [response.entity];\n }\n\n /**\n * Look up entities by code across all PIs.\n *\n * @param code - Entity code to search for\n * @param type - Optional entity type filter\n * @returns Matching entities\n *\n * @example\n * ```typescript\n * const entities = await graph.lookupByCode('alice_austen', 'person');\n * ```\n */\n async lookupByCode(code: string, type?: string): Promise<GraphEntity[]> {\n const response = await this.request<{ entities: GraphEntity[] }>(\n '/graphdb/entities/lookup-by-code',\n {\n method: 'POST',\n body: JSON.stringify({ code, type }),\n }\n );\n return response.entities || [];\n }\n\n // ---------------------------------------------------------------------------\n // PI-based Operations\n // ---------------------------------------------------------------------------\n\n /**\n * List entities extracted from a specific PI or multiple PIs.\n *\n * This returns knowledge graph entities (persons, places, events, etc.)\n * that were extracted from the given PI(s), not the PI entity itself.\n *\n * @param pi - Single PI or array of PIs\n * @param options - Filter options\n * @returns Extracted entities from the PI(s)\n *\n * @example\n * ```typescript\n * // From single PI\n * const entities = await graph.listEntitiesFromPi('01K75HQQXNTDG7BBP7PS9AWYAN');\n *\n * // With type filter\n * const people = await graph.listEntitiesFromPi('01K75HQQXNTDG7BBP7PS9AWYAN', { type: 'person' });\n *\n * // From multiple PIs\n * const all = await graph.listEntitiesFromPi(['pi-1', 'pi-2']);\n * ```\n */\n async listEntitiesFromPi(\n pi: string | string[],\n options: ListFromPiOptions = {}\n ): Promise<GraphEntity[]> {\n const pis = Array.isArray(pi) ? pi : [pi];\n const response = await this.request<{ entities: GraphEntity[] }>(\n '/graphdb/entities/list',\n {\n method: 'POST',\n body: JSON.stringify({\n pis,\n type: options.type,\n }),\n }\n );\n return response.entities || [];\n }\n\n /**\n * Get entities with their relationships from a PI.\n *\n * This is an optimized query that returns entities along with all their\n * relationship data in a single request.\n *\n * @param pi - Persistent Identifier\n * @param type - Optional entity type filter\n * @returns Entities with relationships\n *\n * @example\n * ```typescript\n * const entities = await graph.getEntitiesWithRelationships('01K75HQQXNTDG7BBP7PS9AWYAN');\n * entities.forEach(e => {\n * console.log(`${e.label} has ${e.relationships.length} relationships`);\n * });\n * ```\n */\n async getEntitiesWithRelationships(\n pi: string,\n type?: string\n ): Promise<EntityWithRelationships[]> {\n const response = await this.request<EntitiesWithRelationshipsResponse>(\n '/graphdb/pi/entities-with-relationships',\n {\n method: 'POST',\n body: JSON.stringify({ pi, type }),\n }\n );\n return response.entities || [];\n }\n\n /**\n * Get the lineage (ancestors and/or descendants) of a PI.\n *\n * This traverses the PI hierarchy (parent_pi/children_pi relationships)\n * which is indexed in GraphDB for fast lookups.\n *\n * @param pi - Source PI\n * @param direction - 'ancestors', 'descendants', or 'both'\n * @param maxHops - Maximum depth to traverse (default: 10)\n * @returns Lineage data with PIs at each hop level\n *\n * @example\n * ```typescript\n * // Get ancestors (parent chain)\n * const lineage = await graph.getLineage('01K75HQQXNTDG7BBP7PS9AWYAN', 'ancestors');\n *\n * // Get both directions\n * const full = await graph.getLineage('01K75HQQXNTDG7BBP7PS9AWYAN', 'both');\n * ```\n */\n async getLineage(\n pi: string,\n direction: 'ancestors' | 'descendants' | 'both' = 'both',\n maxHops: number = 10\n ): Promise<LineageResponse> {\n return this.request<LineageResponse>('/graphdb/pi/lineage', {\n method: 'POST',\n body: JSON.stringify({ sourcePi: pi, direction, maxHops }),\n });\n }\n\n // ---------------------------------------------------------------------------\n // Relationship Operations\n // ---------------------------------------------------------------------------\n\n /**\n * Get relationships for an entity from the GraphDB index.\n *\n * **Important distinction from ContentClient.getRelationships():**\n * - **ContentClient.getRelationships()**: Returns OUTBOUND relationships only\n * (from the entity's relationships.json in IPFS - source of truth)\n * - **GraphClient.getRelationships()**: Returns BOTH inbound AND outbound\n * relationships (from the indexed GraphDB mirror)\n *\n * Use this method when you need to find \"what references this entity\" (inbound)\n * or want a complete bidirectional view.\n *\n * @param id - Entity identifier (works for both PIs and KG entities)\n * @param direction - Filter by direction: 'outgoing', 'incoming', or 'both' (default)\n * @returns Array of relationships with direction indicator\n *\n * @example\n * ```typescript\n * // Get all relationships (both directions)\n * const all = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN');\n *\n * // Get only inbound relationships (\"who references this entity?\")\n * const incoming = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN', 'incoming');\n *\n * // Get only outbound relationships (similar to IPFS, but from index)\n * const outgoing = await graph.getRelationships('01K75HQQXNTDG7BBP7PS9AWYAN', 'outgoing');\n *\n * // Process by direction\n * const rels = await graph.getRelationships('entity-id');\n * rels.forEach(r => {\n * if (r.direction === 'incoming') {\n * console.log(`${r.target_label} references this entity via ${r.predicate}`);\n * } else {\n * console.log(`This entity ${r.predicate} -> ${r.target_label}`);\n * }\n * });\n * ```\n */\n async getRelationships(\n id: string,\n direction: RelationshipDirection = 'both'\n ): Promise<Relationship[]> {\n const response = await this.request<{\n found: boolean;\n canonical_id?: string;\n relationships?: Array<{\n direction: 'outgoing' | 'incoming';\n predicate: string;\n target_id: string;\n target_code?: string;\n target_label: string;\n target_type: string;\n properties?: Record<string, unknown>;\n source_pi?: string;\n created_at?: string;\n }>;\n }>(`/graphdb/relationships/${encodeURIComponent(id)}`);\n\n if (!response.found || !response.relationships) {\n return [];\n }\n\n let relationships = response.relationships;\n\n // Filter by direction if specified\n if (direction !== 'both') {\n relationships = relationships.filter(rel => rel.direction === direction);\n }\n\n return relationships.map(rel => ({\n direction: rel.direction,\n predicate: rel.predicate,\n target_id: rel.target_id,\n target_code: rel.target_code || '',\n target_label: rel.target_label,\n target_type: rel.target_type,\n properties: rel.properties,\n source_pi: rel.source_pi,\n created_at: rel.created_at,\n }));\n }\n\n // ---------------------------------------------------------------------------\n // Path Finding\n // ---------------------------------------------------------------------------\n\n /**\n * Find shortest paths between sets of entities.\n *\n * @param sourceIds - Starting entity IDs\n * @param targetIds - Target entity IDs\n * @param options - Path finding options\n * @returns Found paths\n *\n * @example\n * ```typescript\n * const paths = await graph.findPaths(\n * ['entity-alice'],\n * ['entity-bob'],\n * { max_depth: 4, direction: 'both' }\n * );\n *\n * paths.forEach(path => {\n * console.log(`Path of length ${path.length}:`);\n * path.edges.forEach(e => {\n * console.log(` ${e.subject_label} -[${e.predicate}]-> ${e.object_label}`);\n * });\n * });\n * ```\n */\n async findPaths(\n sourceIds: string[],\n targetIds: string[],\n options: PathOptions = {}\n ): Promise<Path[]> {\n const response = await this.request<PathsResponse>('/graphdb/paths/between', {\n method: 'POST',\n body: JSON.stringify({\n source_ids: sourceIds,\n target_ids: targetIds,\n max_depth: options.max_depth,\n direction: options.direction,\n limit: options.limit,\n }),\n });\n return response.paths || [];\n }\n\n /**\n * Find entities of a specific type reachable from starting entities.\n *\n * @param startIds - Starting entity IDs\n * @param targetType - Type of entities to find\n * @param options - Search options\n * @returns Reachable entities of the specified type\n *\n * @example\n * ```typescript\n * // Find all people reachable from an event\n * const people = await graph.findReachable(\n * ['event-id'],\n * 'person',\n * { max_depth: 3 }\n * );\n * ```\n */\n async findReachable(\n startIds: string[],\n targetType: string,\n options: ReachableOptions = {}\n ): Promise<GraphEntity[]> {\n const response = await this.request<{ entities: GraphEntity[] }>(\n '/graphdb/paths/reachable',\n {\n method: 'POST',\n body: JSON.stringify({\n start_ids: startIds,\n target_type: targetType,\n max_depth: options.max_depth,\n direction: options.direction,\n limit: options.limit,\n }),\n }\n );\n return response.entities || [];\n }\n\n /**\n * Check the health of the graph service.\n *\n * @returns Health status\n */\n async health(): Promise<{ status: string; service: string; version: string }> {\n return this.request('/graphdb/health', { method: 'GET' });\n }\n}\n"],"mappings":"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","names":[]}