@bardioc/app-sdk 0.4.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 (41) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +368 -0
  3. package/assets/fonts/README.md +11 -0
  4. package/assets/fonts/bardioc-fonts.css +55 -0
  5. package/assets/fonts/v1/geist-mono-latin-wght-normal.woff2 +0 -0
  6. package/assets/fonts/v1/nunito-sans-latin-wght-italic.woff2 +0 -0
  7. package/assets/fonts/v1/nunito-sans-latin-wght-normal.woff2 +0 -0
  8. package/dist/contract-matrix.d.ts +130 -0
  9. package/dist/contract-matrix.js +132 -0
  10. package/dist/dev-auth-proxy-core.d.ts +24 -0
  11. package/dist/dev-auth-proxy-core.js +59 -0
  12. package/dist/dev-auth-vite.d.ts +34 -0
  13. package/dist/dev-auth-vite.js +221 -0
  14. package/dist/dev-proxy.d.ts +8 -0
  15. package/dist/dev-proxy.js +40 -0
  16. package/dist/dev-session-cli.d.ts +34 -0
  17. package/dist/dev-session-cli.js +125 -0
  18. package/dist/dev.d.ts +33 -0
  19. package/dist/dev.js +223 -0
  20. package/dist/dot-env.d.ts +2 -0
  21. package/dist/dot-env.js +22 -0
  22. package/dist/errors.d.ts +27 -0
  23. package/dist/errors.js +57 -0
  24. package/dist/host-bridge.d.ts +3 -0
  25. package/dist/host-bridge.js +260 -0
  26. package/dist/index.d.ts +9 -0
  27. package/dist/index.js +6 -0
  28. package/dist/manifest.d.ts +78 -0
  29. package/dist/manifest.js +169 -0
  30. package/dist/protocol.d.ts +26 -0
  31. package/dist/protocol.js +28 -0
  32. package/dist/react.d.ts +26 -0
  33. package/dist/react.js +208 -0
  34. package/dist/transports/graph-transport.d.ts +224 -0
  35. package/dist/transports/graph-transport.js +584 -0
  36. package/dist/transports/os-transport.d.ts +189 -0
  37. package/dist/transports/os-transport.js +444 -0
  38. package/dist/types.d.ts +343 -0
  39. package/dist/vite.d.ts +9 -0
  40. package/dist/vite.js +262 -0
  41. package/package.json +101 -0
@@ -0,0 +1,584 @@
1
+ import { EntityNotFoundError, NetworkError, PermissionError, SdkError, TimeoutError, ValidationError, } from '../errors.js';
2
+ const GRAPH_BASE = '/bardioc/api/graph/7.4';
3
+ function buildQueryString(params) {
4
+ const searchParams = new URLSearchParams();
5
+ for (const [key, value] of Object.entries(params)) {
6
+ if (value !== undefined && value !== '') {
7
+ searchParams.set(key, String(value));
8
+ }
9
+ }
10
+ const qs = searchParams.toString();
11
+ return qs ? `?${qs}` : '';
12
+ }
13
+ // Maps backend errors to typed SDK errors
14
+ function parseError(error, operation, context) {
15
+ let statusCode;
16
+ let errorMessage = 'Unknown error';
17
+ // Check for direct message on error object (e.g. from fetch/network errors)
18
+ if (error.message && typeof error.message === 'string') {
19
+ errorMessage = error.message;
20
+ }
21
+ // Backend may return status in responsePayload
22
+ if (error.responsePayload && typeof error.responsePayload === 'object') {
23
+ statusCode = error.responsePayload.status || error.responsePayload.statusCode;
24
+ if (error.responsePayload.message) {
25
+ errorMessage = error.responsePayload.message;
26
+ }
27
+ }
28
+ // Check for timeout errors
29
+ if (errorMessage.includes('timed out') || errorMessage.includes('timeout')) {
30
+ return new TimeoutError(operation, 10000);
31
+ }
32
+ // Map by status code
33
+ if (statusCode) {
34
+ const fullContext = {
35
+ ...context,
36
+ operation,
37
+ timestamp: Date.now(),
38
+ statusCode,
39
+ };
40
+ switch (statusCode) {
41
+ case 404:
42
+ return new EntityNotFoundError(context?.id || context?.xid || 'unknown');
43
+ case 400:
44
+ return new ValidationError(errorMessage, context?.field);
45
+ case 403:
46
+ return new PermissionError(errorMessage);
47
+ case 408:
48
+ return new TimeoutError(operation, 10000);
49
+ case 500:
50
+ case 502:
51
+ case 503:
52
+ return new NetworkError(errorMessage, statusCode);
53
+ default:
54
+ if (statusCode >= 400) {
55
+ return new NetworkError(errorMessage, statusCode);
56
+ }
57
+ }
58
+ }
59
+ // Default to generic SDK error
60
+ return new SdkError(errorMessage, 'UNKNOWN_ERROR', statusCode, {
61
+ ...context,
62
+ operation,
63
+ timestamp: Date.now(),
64
+ });
65
+ }
66
+ /** Graph transport class providing graph database operations */
67
+ export class GraphTransport {
68
+ _request;
69
+ _debug;
70
+ constructor(_request, options) {
71
+ this._request = _request;
72
+ this._debug = options?.debug ?? false;
73
+ }
74
+ log(...args) {
75
+ if (this._debug)
76
+ console.log('[graph-transport]', ...args);
77
+ }
78
+ withScope(payload, options) {
79
+ if (!options?.scopeId) {
80
+ return payload;
81
+ }
82
+ return {
83
+ ...payload,
84
+ scopeId: options.scopeId,
85
+ scopePolicy: 'required',
86
+ };
87
+ }
88
+ /**
89
+ * Get single node by ID
90
+ * Returns raw backend format (ogit/* fields)
91
+ *
92
+ * @throws {EntityNotFoundError} if node doesn't exist
93
+ * @throws {NetworkError} on HTTP errors
94
+ * @throws {TimeoutError} on request timeout
95
+ */
96
+ async get(id, options) {
97
+ try {
98
+ const qs = buildQueryString({
99
+ fields: options?.fields,
100
+ includeDeleted: options?.includeDeleted,
101
+ listMeta: options?.listMeta,
102
+ vid: options?.vid,
103
+ });
104
+ return await this._request(this.withScope({
105
+ path: `${GRAPH_BASE}/${encodeURIComponent(id)}${qs}`,
106
+ method: 'GET',
107
+ }, options));
108
+ }
109
+ catch (error) {
110
+ throw parseError(error, 'graph.get', { id, ...options });
111
+ }
112
+ }
113
+ /**
114
+ * Get multiple nodes by IDs (batched request)
115
+ * Uses /query/ids endpoint
116
+ *
117
+ * @returns Array in same order as input IDs (missing nodes = undefined)
118
+ * @throws {ValidationError} if ids array is empty or invalid
119
+ * @throws {NetworkError} on HTTP errors
120
+ * @throws {TimeoutError} on request timeout
121
+ */
122
+ async getMany(ids, options) {
123
+ try {
124
+ const qs = buildQueryString({
125
+ query: ids.join(','),
126
+ fields: options?.fields,
127
+ includeDeleted: options?.includeDeleted,
128
+ listMeta: options?.listMeta,
129
+ });
130
+ this.log('getMany', { path: `${GRAPH_BASE}/query/ids${qs}`, count: ids.length });
131
+ return await this._request(this.withScope({
132
+ path: `${GRAPH_BASE}/query/ids${qs}`,
133
+ method: 'GET',
134
+ }, options));
135
+ }
136
+ catch (error) {
137
+ throw parseError(error, 'graph.getMany', { ids: ids.length, ...options });
138
+ }
139
+ }
140
+ /**
141
+ * Get node by external ID
142
+ *
143
+ * @throws {EntityNotFoundError} if XID doesn't exist
144
+ * @throws {NetworkError} on HTTP errors
145
+ * @throws {TimeoutError} on request timeout
146
+ */
147
+ async getByXid(xid, options) {
148
+ try {
149
+ const qs = buildQueryString({
150
+ fields: options?.fields,
151
+ includeDeleted: options?.includeDeleted,
152
+ listMeta: options?.listMeta,
153
+ });
154
+ return await this._request(this.withScope({
155
+ path: `${GRAPH_BASE}/xid/${encodeURIComponent(xid)}${qs}`,
156
+ method: 'GET',
157
+ }, options));
158
+ }
159
+ catch (error) {
160
+ throw parseError(error, 'graph.getByXid', { xid, ...options });
161
+ }
162
+ }
163
+ /**
164
+ * Query nodes using Lucene syntax
165
+ * Returns raw backend format (ogit/* fields)
166
+ *
167
+ * @example
168
+ * const people = await graph.query<GraphNodeRaw>(
169
+ * 'ogit/_type:ogit/Person',
170
+ * { limit: 50, offset: 0 }
171
+ * );
172
+ *
173
+ * @throws {ValidationError} if query syntax is invalid
174
+ * @throws {NetworkError} on HTTP errors
175
+ * @throws {TimeoutError} on request timeout
176
+ */
177
+ async query(lucene, options) {
178
+ try {
179
+ const qs = buildQueryString({
180
+ query: lucene,
181
+ limit: options?.limit,
182
+ offset: options?.offset,
183
+ fields: options?.fields,
184
+ includeDeleted: options?.includeDeleted,
185
+ listMeta: options?.listMeta,
186
+ order: options?.order,
187
+ });
188
+ this.log('query', {
189
+ path: `${GRAPH_BASE}/query/vertices${qs}`,
190
+ lucene,
191
+ limit: options?.limit,
192
+ offset: options?.offset,
193
+ });
194
+ return await this._request(this.withScope({
195
+ path: `${GRAPH_BASE}/query/vertices${qs}`,
196
+ method: 'GET',
197
+ }, options));
198
+ }
199
+ catch (error) {
200
+ throw parseError(error, 'graph.query', { lucene, ...options });
201
+ }
202
+ }
203
+ /**
204
+ * Execute Gremlin traversal query
205
+ * Returns raw results (structure varies by query)
206
+ *
207
+ * @example
208
+ * const edges = await graph.gremlin(rootId, "outE('ogit/relates')");
209
+ *
210
+ * @throws {EntityNotFoundError} if root node doesn't exist
211
+ * @throws {ValidationError} if query syntax is invalid
212
+ * @throws {NetworkError} on HTTP errors
213
+ * @throws {TimeoutError} on request timeout
214
+ */
215
+ async gremlin(rootId, query, options) {
216
+ try {
217
+ return await this._request(this.withScope({
218
+ path: `${GRAPH_BASE}/query/gremlin`,
219
+ method: 'POST',
220
+ body: {
221
+ root: rootId,
222
+ query,
223
+ fields: options?.fields,
224
+ includeDeleted: options?.includeDeleted,
225
+ listMeta: options?.listMeta,
226
+ },
227
+ contentType: 'application/json',
228
+ }, options));
229
+ }
230
+ catch (error) {
231
+ throw parseError(error, 'graph.gremlin', { rootId, query, ...options });
232
+ }
233
+ }
234
+ /**
235
+ * Execute combined query (Lucene + Gremlin in single call)
236
+ * Returns raw results (structure varies by query)
237
+ *
238
+ * @example
239
+ * const results = await graph.combined(
240
+ * 'ogit/_type:ogit/Person AND name:Test',
241
+ * { limit: 50 }
242
+ * );
243
+ *
244
+ * @throws {ValidationError} if query syntax is invalid
245
+ * @throws {NetworkError} on HTTP errors
246
+ * @throws {TimeoutError} on request timeout
247
+ */
248
+ async combined(query, options) {
249
+ try {
250
+ const qs = buildQueryString({
251
+ query,
252
+ limit: options?.limit,
253
+ offset: options?.offset,
254
+ fields: options?.fields,
255
+ includeDeleted: options?.includeDeleted,
256
+ listMeta: options?.listMeta,
257
+ order: options?.order,
258
+ });
259
+ this.log('combined', { path: `${GRAPH_BASE}/query/combined${qs}`, query });
260
+ return await this._request(this.withScope({
261
+ path: `${GRAPH_BASE}/query/combined${qs}`,
262
+ method: 'GET',
263
+ }, options));
264
+ }
265
+ catch (error) {
266
+ throw parseError(error, 'graph.combined', { query, ...options });
267
+ }
268
+ }
269
+ /**
270
+ * Create new graph node
271
+ *
272
+ * @param type - OGIT type (e.g., 'ogit/Person')
273
+ * @param data - Node attributes (ogit/* format)
274
+ * @returns Created node (raw backend format)
275
+ *
276
+ * @throws {ValidationError} if data is invalid or missing required fields
277
+ * @throws {PermissionError} if user lacks permission to create this type
278
+ * @throws {NetworkError} on HTTP errors
279
+ * @throws {TimeoutError} on request timeout
280
+ */
281
+ async create(type, data, options) {
282
+ try {
283
+ const qs = buildQueryString({
284
+ fullResponse: options?.fullResponse,
285
+ listMeta: options?.listMeta,
286
+ });
287
+ return await this._request(this.withScope({
288
+ path: `${GRAPH_BASE}/new/${encodeURIComponent(type)}${qs}`,
289
+ method: 'POST',
290
+ body: data,
291
+ contentType: 'application/json',
292
+ }, options));
293
+ }
294
+ catch (error) {
295
+ throw parseError(error, 'graph.create', { type, ...options });
296
+ }
297
+ }
298
+ /**
299
+ * Update existing node
300
+ *
301
+ * @param id - Node ID
302
+ * @param data - Fields to update (ogit/* format)
303
+ * @returns Updated node (raw backend format)
304
+ *
305
+ * @throws {EntityNotFoundError} if node doesn't exist
306
+ * @throws {ValidationError} if data is invalid
307
+ * @throws {PermissionError} if user lacks permission to update this node
308
+ * @throws {NetworkError} on HTTP errors
309
+ * @throws {TimeoutError} on request timeout
310
+ */
311
+ async update(id, data, options) {
312
+ try {
313
+ const qs = buildQueryString({
314
+ fullResponse: options?.fullResponse,
315
+ listMeta: options?.listMeta,
316
+ });
317
+ return await this._request(this.withScope({
318
+ path: `${GRAPH_BASE}/${encodeURIComponent(id)}${qs}`,
319
+ method: 'POST',
320
+ body: data,
321
+ contentType: 'application/json',
322
+ }, options));
323
+ }
324
+ catch (error) {
325
+ throw parseError(error, 'graph.update', { id, ...options });
326
+ }
327
+ }
328
+ /**
329
+ * Delete node (soft delete by default)
330
+ *
331
+ * @throws {EntityNotFoundError} if node doesn't exist
332
+ * @throws {PermissionError} if user lacks permission to delete this node
333
+ * @throws {NetworkError} on HTTP errors
334
+ * @throws {TimeoutError} on request timeout
335
+ */
336
+ async delete(id, options) {
337
+ try {
338
+ const qs = buildQueryString({
339
+ hard: options?.hard,
340
+ });
341
+ await this._request(this.withScope({
342
+ path: `${GRAPH_BASE}/${encodeURIComponent(id)}${qs}`,
343
+ method: 'DELETE',
344
+ }, options));
345
+ }
346
+ catch (error) {
347
+ throw parseError(error, 'graph.delete', { id, ...options });
348
+ }
349
+ }
350
+ /**
351
+ * Create edge between two nodes
352
+ *
353
+ * @param from - Source node ID
354
+ * @param to - Target node ID
355
+ * @param type - Edge type (e.g., 'ogit/relates')
356
+ * @returns Created edge (raw backend format)
357
+ *
358
+ * @throws {EntityNotFoundError} if either node doesn't exist
359
+ * @throws {ValidationError} if edge type is invalid or not allowed
360
+ * @throws {PermissionError} if user lacks permission to create this edge
361
+ * @throws {NetworkError} on HTTP errors
362
+ * @throws {TimeoutError} on request timeout
363
+ */
364
+ async connect(from, to, type, options) {
365
+ try {
366
+ const qs = buildQueryString({
367
+ fullResponse: options?.fullResponse,
368
+ listMeta: options?.listMeta,
369
+ });
370
+ return await this._request(this.withScope({
371
+ path: `${GRAPH_BASE}/connect${qs}`,
372
+ method: 'POST',
373
+ body: { fromId: from, toId: to, edgeType: type },
374
+ contentType: 'application/json',
375
+ }, options));
376
+ }
377
+ catch (error) {
378
+ throw parseError(error, 'graph.connect', { from, to, type, ...options });
379
+ }
380
+ }
381
+ /**
382
+ * Get node history
383
+ *
384
+ * @param nodeId - Node ID
385
+ * @param options - Time range, limit, etc.
386
+ * @returns Array of historical versions
387
+ *
388
+ * @throws {EntityNotFoundError} if node doesn't exist
389
+ * @throws {NetworkError} on HTTP errors
390
+ * @throws {TimeoutError} on request timeout
391
+ */
392
+ async history(nodeId, options) {
393
+ try {
394
+ const qs = buildQueryString({
395
+ from: options?.from,
396
+ to: options?.to,
397
+ limit: options?.limit,
398
+ offset: options?.offset,
399
+ includeDeleted: options?.includeDeleted,
400
+ });
401
+ return await this._request(this.withScope({
402
+ path: `${GRAPH_BASE}/${encodeURIComponent(nodeId)}/history${qs}`,
403
+ method: 'GET',
404
+ }, options));
405
+ }
406
+ catch (error) {
407
+ throw parseError(error, 'graph.history', { nodeId, ...options });
408
+ }
409
+ }
410
+ /**
411
+ * Batch operations
412
+ */
413
+ batch = {
414
+ /**
415
+ * Delete multiple nodes in parallel
416
+ * Continues deleting even if some fail
417
+ *
418
+ * @param ids - Array of node IDs to delete
419
+ * @param options - Delete options (hard delete, etc.)
420
+ * @returns BatchResult with succeeded and failed IDs
421
+ *
422
+ * @example
423
+ * const result = await graph.batch.delete(['id1', 'id2', 'id3'], { hard: false });
424
+ * console.log(`Deleted: ${result.succeeded.length}, Failed: ${result.failed.length}`);
425
+ * result.failed.forEach(f => console.error(`Failed ${f.id}: ${f.error}`));
426
+ */
427
+ delete: async (ids, options) => {
428
+ const results = await Promise.allSettled(ids.map(id => this.delete(id, options)));
429
+ const succeeded = [];
430
+ const failed = [];
431
+ results.forEach((result, index) => {
432
+ const id = ids[index];
433
+ if (!id)
434
+ return;
435
+ if (result.status === 'fulfilled') {
436
+ succeeded.push(id);
437
+ }
438
+ else {
439
+ failed.push({
440
+ id,
441
+ error: result.reason?.message || 'Unknown error',
442
+ });
443
+ }
444
+ });
445
+ return { succeeded, failed };
446
+ },
447
+ };
448
+ /**
449
+ * Time series operations
450
+ */
451
+ timeseries = {
452
+ /**
453
+ * Get time series values
454
+ *
455
+ * @throws {EntityNotFoundError} if time series doesn't exist
456
+ * @throws {NetworkError} on HTTP errors
457
+ * @throws {TimeoutError} on request timeout
458
+ */
459
+ get: async (id, options) => {
460
+ try {
461
+ const qs = buildQueryString({
462
+ from: options?.from,
463
+ to: options?.to,
464
+ limit: options?.limit,
465
+ order: options?.order,
466
+ });
467
+ return await this._request(this.withScope({
468
+ path: `${GRAPH_BASE}/${encodeURIComponent(id)}/values${qs}`,
469
+ method: 'GET',
470
+ }, options));
471
+ }
472
+ catch (error) {
473
+ throw parseError(error, 'graph.timeseries.get', { id, ...options });
474
+ }
475
+ },
476
+ /**
477
+ * Add time series values
478
+ *
479
+ * @throws {EntityNotFoundError} if time series doesn't exist
480
+ * @throws {ValidationError} if values format is invalid
481
+ * @throws {NetworkError} on HTTP errors
482
+ * @throws {TimeoutError} on request timeout
483
+ */
484
+ add: async (id, values, options) => {
485
+ try {
486
+ await this._request(this.withScope({
487
+ path: `${GRAPH_BASE}/${encodeURIComponent(id)}/values`,
488
+ method: 'POST',
489
+ body: { values },
490
+ contentType: 'application/json',
491
+ }, options));
492
+ }
493
+ catch (error) {
494
+ throw parseError(error, 'graph.timeseries.add', { id, count: values.length, ...options });
495
+ }
496
+ },
497
+ /**
498
+ * Query time series with filters
499
+ *
500
+ * @throws {ValidationError} if query options are invalid
501
+ * @throws {NetworkError} on HTTP errors
502
+ * @throws {TimeoutError} on request timeout
503
+ */
504
+ query: async (options) => {
505
+ try {
506
+ const qs = buildQueryString({
507
+ ids: options.ids?.join(','),
508
+ from: options.from,
509
+ to: options.to,
510
+ limit: options.limit,
511
+ order: options.order,
512
+ aggregate: options.aggregate,
513
+ });
514
+ return await this._request(this.withScope({
515
+ path: `${GRAPH_BASE}/query/values${qs}`,
516
+ method: 'GET',
517
+ }, options));
518
+ }
519
+ catch (error) {
520
+ throw parseError(error, 'graph.timeseries.query', { ...options });
521
+ }
522
+ },
523
+ };
524
+ /**
525
+ * Content/blob operations
526
+ */
527
+ content = {
528
+ /**
529
+ * Upload binary content to a node
530
+ *
531
+ * @param nodeId - Node ID to attach content to
532
+ * @param data - Binary content as Blob
533
+ * @param contentType - MIME type (e.g., 'image/png', 'application/pdf')
534
+ * @returns Content ID for later retrieval
535
+ *
536
+ * @throws {EntityNotFoundError} if node doesn't exist
537
+ * @throws {ValidationError} if content is invalid
538
+ * @throws {NetworkError} on HTTP errors
539
+ * @throws {TimeoutError} on request timeout
540
+ */
541
+ set: async (nodeId, data, contentType, options) => {
542
+ try {
543
+ const result = await this._request(this.withScope({
544
+ path: `${GRAPH_BASE}/${encodeURIComponent(nodeId)}/content`,
545
+ method: 'POST',
546
+ body: data,
547
+ contentType,
548
+ }, options));
549
+ return result.contentId;
550
+ }
551
+ catch (error) {
552
+ throw parseError(error, 'graph.content.set', {
553
+ nodeId,
554
+ contentType,
555
+ size: data.size,
556
+ ...options,
557
+ });
558
+ }
559
+ },
560
+ /**
561
+ * Download binary content from a node
562
+ *
563
+ * @param nodeId - Node ID
564
+ * @param contentId - Content ID returned from set operation
565
+ * @returns Binary content as Blob
566
+ *
567
+ * @throws {EntityNotFoundError} if node or content doesn't exist
568
+ * @throws {NetworkError} on HTTP errors
569
+ * @throws {TimeoutError} on request timeout
570
+ */
571
+ get: async (nodeId, contentId, options) => {
572
+ try {
573
+ const qs = buildQueryString({ contentId });
574
+ return await this._request(this.withScope({
575
+ path: `${GRAPH_BASE}/${encodeURIComponent(nodeId)}/content${qs}`,
576
+ method: 'GET',
577
+ }, options));
578
+ }
579
+ catch (error) {
580
+ throw parseError(error, 'graph.content.get', { nodeId, contentId, ...options });
581
+ }
582
+ },
583
+ };
584
+ }