@onchaindb/sdk 0.0.6

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 (66) hide show
  1. package/README.md +475 -0
  2. package/dist/batch.d.ts +42 -0
  3. package/dist/batch.d.ts.map +1 -0
  4. package/dist/batch.js +124 -0
  5. package/dist/batch.js.map +1 -0
  6. package/dist/client.d.ts +93 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +679 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/database.d.ts +194 -0
  11. package/dist/database.d.ts.map +1 -0
  12. package/dist/database.js +211 -0
  13. package/dist/database.js.map +1 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +37 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/query-sdk/ConditionBuilder.d.ts +23 -0
  19. package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -0
  20. package/dist/query-sdk/ConditionBuilder.js +76 -0
  21. package/dist/query-sdk/ConditionBuilder.js.map +1 -0
  22. package/dist/query-sdk/FieldConditionBuilder.d.ts +2 -0
  23. package/dist/query-sdk/FieldConditionBuilder.d.ts.map +1 -0
  24. package/dist/query-sdk/FieldConditionBuilder.js +6 -0
  25. package/dist/query-sdk/FieldConditionBuilder.js.map +1 -0
  26. package/dist/query-sdk/NestedBuilders.d.ts +44 -0
  27. package/dist/query-sdk/NestedBuilders.d.ts.map +1 -0
  28. package/dist/query-sdk/NestedBuilders.js +131 -0
  29. package/dist/query-sdk/NestedBuilders.js.map +1 -0
  30. package/dist/query-sdk/OnChainDB.d.ts +27 -0
  31. package/dist/query-sdk/OnChainDB.d.ts.map +1 -0
  32. package/dist/query-sdk/OnChainDB.js +191 -0
  33. package/dist/query-sdk/OnChainDB.js.map +1 -0
  34. package/dist/query-sdk/PrismaLikeClient.d.ts +41 -0
  35. package/dist/query-sdk/PrismaLikeClient.d.ts.map +1 -0
  36. package/dist/query-sdk/PrismaLikeClient.js +202 -0
  37. package/dist/query-sdk/PrismaLikeClient.js.map +1 -0
  38. package/dist/query-sdk/QueryBuilder.d.ts +155 -0
  39. package/dist/query-sdk/QueryBuilder.d.ts.map +1 -0
  40. package/dist/query-sdk/QueryBuilder.js +757 -0
  41. package/dist/query-sdk/QueryBuilder.js.map +1 -0
  42. package/dist/query-sdk/QueryResult.d.ts +53 -0
  43. package/dist/query-sdk/QueryResult.d.ts.map +1 -0
  44. package/dist/query-sdk/QueryResult.js +267 -0
  45. package/dist/query-sdk/QueryResult.js.map +1 -0
  46. package/dist/query-sdk/SelectionBuilder.d.ts +21 -0
  47. package/dist/query-sdk/SelectionBuilder.d.ts.map +1 -0
  48. package/dist/query-sdk/SelectionBuilder.js +67 -0
  49. package/dist/query-sdk/SelectionBuilder.js.map +1 -0
  50. package/dist/query-sdk/adapters/HttpClientAdapter.d.ts +28 -0
  51. package/dist/query-sdk/adapters/HttpClientAdapter.d.ts.map +1 -0
  52. package/dist/query-sdk/adapters/HttpClientAdapter.js +206 -0
  53. package/dist/query-sdk/adapters/HttpClientAdapter.js.map +1 -0
  54. package/dist/query-sdk/index.d.ts +38 -0
  55. package/dist/query-sdk/index.d.ts.map +1 -0
  56. package/dist/query-sdk/index.js +28 -0
  57. package/dist/query-sdk/index.js.map +1 -0
  58. package/dist/query-sdk/operators.d.ts +57 -0
  59. package/dist/query-sdk/operators.d.ts.map +1 -0
  60. package/dist/query-sdk/operators.js +275 -0
  61. package/dist/query-sdk/operators.js.map +1 -0
  62. package/dist/types.d.ts +263 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +46 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +47 -0
package/dist/client.js ADDED
@@ -0,0 +1,679 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OnChainDBClient = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const eventemitter3_1 = require("eventemitter3");
9
+ const types_1 = require("./types");
10
+ const database_1 = require("./database");
11
+ const query_sdk_1 = require("./query-sdk");
12
+ class OnChainDBClient extends eventemitter3_1.EventEmitter {
13
+ constructor(config) {
14
+ super();
15
+ const appKey = config.appKey || config.apiKey || '';
16
+ const userKey = config.userKey || '';
17
+ this.config = {
18
+ endpoint: config.endpoint,
19
+ apiKey: appKey,
20
+ appKey: appKey,
21
+ userKey: userKey,
22
+ appId: config.appId || undefined,
23
+ timeout: config.timeout || 30000,
24
+ retryCount: config.retryCount || 3,
25
+ retryDelay: config.retryDelay || 1000
26
+ };
27
+ const headers = {
28
+ 'Content-Type': 'application/json'
29
+ };
30
+ if (appKey) {
31
+ headers['X-App-Key'] = appKey;
32
+ }
33
+ if (userKey) {
34
+ headers['X-User-Key'] = userKey;
35
+ }
36
+ this.http = axios_1.default.create({
37
+ baseURL: this.config.endpoint,
38
+ timeout: this.config.timeout,
39
+ headers
40
+ });
41
+ }
42
+ database(appId) {
43
+ if (!this._database || this._database['appId'] !== appId) {
44
+ this._database = (0, database_1.createDatabaseManager)(this.http, this.config.endpoint, appId, this.config.apiKey);
45
+ }
46
+ return this._database;
47
+ }
48
+ async handlex420(response, paymentCallback, finalRequest, waitForConfirmation) {
49
+ console.log('💰 Received 402 Payment Required for store');
50
+ const x402Quote = response.data;
51
+ const requirement = x402Quote.accepts?.[0];
52
+ if (!requirement) {
53
+ throw new types_1.OnChainDBError('Invalid 402 response - no payment requirement', 'PAYMENT_ERROR', 402);
54
+ }
55
+ const quote = {
56
+ quote_id: requirement.quoteId,
57
+ total_cost_tia: parseInt(requirement.maxAmountRequired) / 1000000,
58
+ broker_address: requirement.payTo,
59
+ description: requirement.description,
60
+ expires_at: requirement.expiresAt
61
+ };
62
+ console.log('💳 Quote:', quote);
63
+ if (!paymentCallback) {
64
+ throw new types_1.OnChainDBError('Payment required but no payment callback provided. Please provide a payment callback to handle 402.', 'PAYMENT_REQUIRED', 402, quote);
65
+ }
66
+ console.log('💰 Calling payment callback...');
67
+ const payment = await paymentCallback(quote);
68
+ console.log('✅ Payment completed, tx hash:', payment.txHash);
69
+ const x402Payload = {
70
+ x402Version: 1,
71
+ scheme: payment.network?.includes('ethereum') || payment.network?.includes('sepolia')
72
+ ? 'ethereum-native' : 'celestia-native',
73
+ network: payment.network || 'mocha-4',
74
+ quoteId: requirement.quoteId,
75
+ payload: {
76
+ txHash: payment.txHash.toLowerCase(),
77
+ sender: 'unknown',
78
+ timestamp: Math.floor(Date.now() / 1000)
79
+ }
80
+ };
81
+ const encodedPayment = Buffer.from(JSON.stringify(x402Payload)).toString('base64');
82
+ console.log('📤 Retrying store with X-PAYMENT header...');
83
+ const retryResponse = await this.http.post('/store', finalRequest, {
84
+ headers: {
85
+ 'X-PAYMENT': encodedPayment
86
+ }
87
+ });
88
+ console.log('Server response after payment:', retryResponse.data);
89
+ const serverResult = retryResponse.data;
90
+ if (serverResult.ticket_id) {
91
+ if (waitForConfirmation) {
92
+ const taskInfo = await this.waitForTaskCompletion(serverResult.ticket_id);
93
+ if (taskInfo.result && taskInfo.result.results && taskInfo.result.results.length > 0) {
94
+ const firstResult = taskInfo.result.results[0];
95
+ return {
96
+ id: firstResult.id,
97
+ block_height: firstResult.celestia_height || 0,
98
+ transaction_hash: firstResult.blob_id || '',
99
+ celestia_height: firstResult.celestia_height || 0,
100
+ namespace: firstResult.namespace || '',
101
+ confirmed: firstResult.celestia_height > 0
102
+ };
103
+ }
104
+ }
105
+ else {
106
+ return {
107
+ id: serverResult.ticket_id,
108
+ block_height: 0,
109
+ transaction_hash: '',
110
+ celestia_height: 0,
111
+ namespace: '',
112
+ confirmed: false,
113
+ ticket_id: serverResult.ticket_id
114
+ };
115
+ }
116
+ }
117
+ throw new types_1.OnChainDBError('No ticket_id in response after payment', 'STORE_ERROR');
118
+ }
119
+ async store(request, paymentCallback, waitForConfirmation = true) {
120
+ this.validateStoreRequest(request);
121
+ const resolvedRequest = {
122
+ ...request,
123
+ root: this.resolveRoot(request)
124
+ };
125
+ try {
126
+ delete resolvedRequest.collection;
127
+ const response = await this.http.post('/store', resolvedRequest);
128
+ if (response.status === 402 && response.data) {
129
+ const v = await this.handlex420(response, paymentCallback, resolvedRequest, waitForConfirmation);
130
+ if (v) {
131
+ return v;
132
+ }
133
+ }
134
+ console.log('Server response:', response.data);
135
+ const serverResult = response.data;
136
+ if (serverResult.ticket_id) {
137
+ console.log(`🎫 Got ticket ${serverResult.ticket_id}, polling for completion...`);
138
+ this.emit('transaction:queued', {
139
+ ticket_id: serverResult.ticket_id,
140
+ status: serverResult.status,
141
+ message: serverResult.message
142
+ });
143
+ if (waitForConfirmation) {
144
+ const taskInfo = await this.waitForTaskCompletion(serverResult.ticket_id);
145
+ if (taskInfo.result && taskInfo.result.results && taskInfo.result.results.length > 0) {
146
+ const firstResult = taskInfo.result.results[0];
147
+ const result = {
148
+ id: firstResult.id,
149
+ block_height: firstResult.celestia_height || 0,
150
+ transaction_hash: firstResult.blob_id || '',
151
+ celestia_height: firstResult.celestia_height || 0,
152
+ namespace: firstResult.namespace || '',
153
+ confirmed: firstResult.celestia_height > 0
154
+ };
155
+ this.emit('transaction:confirmed', {
156
+ id: result.id,
157
+ status: 'confirmed',
158
+ block_height: result.block_height,
159
+ transaction_hash: result.transaction_hash,
160
+ celestia_height: result.celestia_height
161
+ });
162
+ return result;
163
+ }
164
+ else {
165
+ throw new types_1.OnChainDBError('Task completed but no storage results found', 'STORE_ERROR');
166
+ }
167
+ }
168
+ else {
169
+ return {
170
+ id: serverResult.ticket_id,
171
+ block_height: 0,
172
+ transaction_hash: '',
173
+ celestia_height: 0,
174
+ namespace: '',
175
+ confirmed: false,
176
+ ticket_id: serverResult.ticket_id
177
+ };
178
+ }
179
+ }
180
+ const firstResult = serverResult.results && serverResult.results[0];
181
+ if (!firstResult) {
182
+ throw new types_1.OnChainDBError('No results returned from server', 'STORE_ERROR');
183
+ }
184
+ const result = {
185
+ id: firstResult.id,
186
+ block_height: firstResult.celestia_height || 0,
187
+ transaction_hash: firstResult.blob_id || '',
188
+ celestia_height: firstResult.celestia_height || 0,
189
+ namespace: firstResult.namespace || '',
190
+ confirmed: firstResult.celestia_height > 0
191
+ };
192
+ if (result.block_height === 0) {
193
+ this.emit('transaction:pending', {
194
+ id: result.id,
195
+ status: 'pending',
196
+ block_height: result.block_height,
197
+ transaction_hash: result.transaction_hash
198
+ });
199
+ }
200
+ else {
201
+ this.emit('transaction:confirmed', {
202
+ id: result.id,
203
+ status: 'confirmed',
204
+ block_height: result.block_height,
205
+ transaction_hash: result.transaction_hash,
206
+ celestia_height: result.celestia_height
207
+ });
208
+ }
209
+ console.log('Transaction result:', result);
210
+ if (waitForConfirmation && result.block_height === 0) {
211
+ return await this.waitForConfirmation(result.transaction_hash);
212
+ }
213
+ return result;
214
+ }
215
+ catch (error) {
216
+ console.log("HERE", error);
217
+ if (error?.response) {
218
+ const err = error;
219
+ if (err.response?.status === 402 && err?.response?.data) {
220
+ const v = await this.handlex420(err.response, paymentCallback, resolvedRequest, waitForConfirmation);
221
+ if (v) {
222
+ return v;
223
+ }
224
+ }
225
+ }
226
+ const dbError = error instanceof types_1.OnChainDBError ? error :
227
+ new types_1.OnChainDBError('Failed to store data', 'STORE_ERROR');
228
+ this.emit('error', dbError);
229
+ throw dbError;
230
+ }
231
+ }
232
+ async storeAndConfirm(request) {
233
+ return this.store(request, undefined, true);
234
+ }
235
+ async waitForConfirmation(transactionHash, maxWaitTime = 300000) {
236
+ const startTime = Date.now();
237
+ const pollInterval = 3000;
238
+ console.log(`🔄 Waiting for transaction ${transactionHash} confirmation...`);
239
+ while (Date.now() - startTime < maxWaitTime) {
240
+ try {
241
+ const rpcUrl = 'https://celestia-mocha-rpc.publicnode.com:443';
242
+ const txUrl = `${rpcUrl}/tx?hash=0x${transactionHash}`;
243
+ console.log(`🔍 Checking transaction status: attempt ${Math.floor((Date.now() - startTime) / pollInterval) + 1}`);
244
+ const response = await axios_1.default.get(txUrl);
245
+ if (response.data?.result && response.data.result !== null) {
246
+ const txResult = response.data.result;
247
+ const height = parseInt(txResult.height);
248
+ if (height > 0) {
249
+ console.log(`✅ Transaction ${transactionHash} confirmed at height ${height}`);
250
+ const confirmedTx = {
251
+ id: transactionHash,
252
+ namespace: '',
253
+ block_height: height,
254
+ transaction_hash: transactionHash,
255
+ celestia_height: height,
256
+ confirmed: true
257
+ };
258
+ this.emit('transaction:confirmed', {
259
+ id: transactionHash,
260
+ status: 'confirmed',
261
+ block_height: height,
262
+ transaction_hash: transactionHash
263
+ });
264
+ return confirmedTx;
265
+ }
266
+ }
267
+ console.log(`⏳ Transaction still pending, waiting ${pollInterval}ms...`);
268
+ this.emit('transaction:pending', {
269
+ id: transactionHash,
270
+ status: 'pending',
271
+ block_height: 0,
272
+ transaction_hash: transactionHash
273
+ });
274
+ await this.sleep(pollInterval);
275
+ }
276
+ catch (error) {
277
+ if (Date.now() - startTime >= maxWaitTime) {
278
+ throw new types_1.TransactionError(`Transaction confirmation timeout after ${maxWaitTime}ms`, transactionHash);
279
+ }
280
+ console.log(`⚠️ Error checking transaction (will retry): ${error}`);
281
+ await this.sleep(pollInterval);
282
+ }
283
+ }
284
+ throw new types_1.TransactionError(`Transaction confirmation timeout after ${maxWaitTime}ms`, transactionHash);
285
+ }
286
+ async getIndexCostEstimate(request) {
287
+ try {
288
+ const response = await this.http.post('/api/indexes/cost-estimate', request);
289
+ return response.data;
290
+ }
291
+ catch (error) {
292
+ throw error instanceof types_1.OnChainDBError ? error :
293
+ new types_1.OnChainDBError('Failed to get index cost estimate', 'COST_ESTIMATE_ERROR');
294
+ }
295
+ }
296
+ async getStorageCostEstimate(dataSizeBytes) {
297
+ try {
298
+ const response = await this.http.post('/api/storage/cost-estimate', {
299
+ data_size_bytes: dataSizeBytes
300
+ });
301
+ return response.data;
302
+ }
303
+ catch (error) {
304
+ throw error instanceof types_1.OnChainDBError ? error :
305
+ new types_1.OnChainDBError('Failed to get storage cost estimate', 'COST_ESTIMATE_ERROR');
306
+ }
307
+ }
308
+ async getPricingQuote(request) {
309
+ try {
310
+ const response = await this.http.post('/api/pricing/quote', request);
311
+ return response.data;
312
+ }
313
+ catch (error) {
314
+ throw error instanceof types_1.OnChainDBError ? error :
315
+ new types_1.OnChainDBError('Failed to get pricing quote', 'PRICING_QUOTE_ERROR');
316
+ }
317
+ }
318
+ async createIndex(request) {
319
+ try {
320
+ const appId = this.config.appId || 'default';
321
+ const response = await this.http.post(`/api/apps/${appId}/indexes`, request);
322
+ return response.data;
323
+ }
324
+ catch (error) {
325
+ throw error instanceof types_1.OnChainDBError ? error :
326
+ new types_1.OnChainDBError('Failed to create index', 'INDEX_ERROR');
327
+ }
328
+ }
329
+ async createRelation(request) {
330
+ try {
331
+ const appId = this.config.appId || 'default';
332
+ const response = await this.http.post(`/api/apps/${appId}/relations`, request);
333
+ return response.data;
334
+ }
335
+ catch (error) {
336
+ throw error instanceof types_1.OnChainDBError ? error :
337
+ new types_1.OnChainDBError('Failed to create relation', 'RELATION_ERROR');
338
+ }
339
+ }
340
+ async health() {
341
+ try {
342
+ const response = await this.http.get('/');
343
+ return response.data;
344
+ }
345
+ catch (error) {
346
+ throw new types_1.OnChainDBError('Health check failed', 'HEALTH_ERROR');
347
+ }
348
+ }
349
+ async query(request) {
350
+ try {
351
+ const queryBuilder = this.queryBuilder();
352
+ const root = this.resolveRoot(request);
353
+ if (request.filters) {
354
+ queryBuilder.find(builder => {
355
+ const conditions = Object.entries(request.filters).map(([field, value]) => query_sdk_1.LogicalOperator.Condition(builder.field(field).equals(value)));
356
+ return conditions.length === 1 ? conditions[0] : query_sdk_1.LogicalOperator.And(conditions);
357
+ });
358
+ }
359
+ if (request.limit) {
360
+ queryBuilder.limit(request.limit);
361
+ }
362
+ if (request.offset) {
363
+ queryBuilder.offset(request.offset);
364
+ }
365
+ return await queryBuilder.execute();
366
+ }
367
+ catch (error) {
368
+ throw error instanceof types_1.OnChainDBError ? error :
369
+ new types_1.OnChainDBError('Failed to execute query', 'QUERY_ERROR');
370
+ }
371
+ }
372
+ queryBuilder() {
373
+ const { AxiosHttpClient } = require('./query-sdk/adapters/HttpClientAdapter');
374
+ const httpClient = new AxiosHttpClient(this.http);
375
+ return new query_sdk_1.QueryBuilder(httpClient, this.config.endpoint, this.config.appId);
376
+ }
377
+ batch() {
378
+ const { BatchOperations } = require('./batch');
379
+ return new BatchOperations(this);
380
+ }
381
+ async findUnique(collection, where) {
382
+ try {
383
+ let queryBuilder = this.queryBuilder().collection(collection);
384
+ for (const [field, value] of Object.entries(where)) {
385
+ queryBuilder = queryBuilder.whereField(field).equals(value);
386
+ }
387
+ return await queryBuilder.selectAll().executeUnique();
388
+ }
389
+ catch (error) {
390
+ console.error(`findUnique error for ${collection}:`, error);
391
+ return null;
392
+ }
393
+ }
394
+ async findMany(collection, where = {}, options = {}) {
395
+ try {
396
+ let queryBuilder = this.queryBuilder().collection(collection);
397
+ for (const [field, value] of Object.entries(where)) {
398
+ queryBuilder = queryBuilder.whereField(field).equals(value);
399
+ }
400
+ if (options.limit) {
401
+ queryBuilder = queryBuilder.limit(options.limit);
402
+ }
403
+ if (options.offset) {
404
+ queryBuilder = queryBuilder.offset(options.offset);
405
+ }
406
+ const result = await queryBuilder.selectAll().execute();
407
+ if (!result.records) {
408
+ return [];
409
+ }
410
+ let records = result.records;
411
+ if (options.sort) {
412
+ records = records.sort((a, b) => {
413
+ const aVal = a[options.sort.field];
414
+ const bVal = b[options.sort.field];
415
+ if (options.sort.order === 'asc') {
416
+ return aVal > bVal ? 1 : -1;
417
+ }
418
+ else {
419
+ return aVal < bVal ? 1 : -1;
420
+ }
421
+ });
422
+ }
423
+ return records;
424
+ }
425
+ catch (error) {
426
+ console.error(`findMany error for ${collection}:`, error);
427
+ return [];
428
+ }
429
+ }
430
+ async createDocument(collection, data, paymentProof, options) {
431
+ const document = {
432
+ id: options?.idGenerator ? options.idGenerator() : this.generateId(),
433
+ ...data,
434
+ createdAt: new Date().toISOString(),
435
+ updatedAt: new Date().toISOString(),
436
+ };
437
+ await this.store({
438
+ collection,
439
+ data: [document],
440
+ ...paymentProof
441
+ });
442
+ return document;
443
+ }
444
+ async updateDocument(collection, where, data, paymentProof) {
445
+ const current = await this.findUnique(collection, where);
446
+ if (!current) {
447
+ return null;
448
+ }
449
+ const updated = {
450
+ ...current,
451
+ ...data,
452
+ updatedAt: new Date().toISOString(),
453
+ };
454
+ await this.store({
455
+ collection,
456
+ data: [updated],
457
+ ...paymentProof
458
+ });
459
+ return updated;
460
+ }
461
+ async upsertDocument(collection, where, create, update, paymentProof, options) {
462
+ const existing = await this.findUnique(collection, where);
463
+ if (existing) {
464
+ return (await this.updateDocument(collection, where, update, paymentProof));
465
+ }
466
+ else {
467
+ return await this.createDocument(collection, create, paymentProof, options);
468
+ }
469
+ }
470
+ async deleteDocument(collection, where, paymentProof) {
471
+ const existing = await this.findUnique(collection, where);
472
+ if (!existing) {
473
+ return false;
474
+ }
475
+ const deleted = {
476
+ ...existing,
477
+ deleted: true,
478
+ updatedAt: new Date().toISOString(),
479
+ };
480
+ await this.store({
481
+ collection,
482
+ data: [deleted],
483
+ ...paymentProof
484
+ });
485
+ return true;
486
+ }
487
+ async countDocuments(collection, where = {}) {
488
+ try {
489
+ let queryBuilder = this.queryBuilder().collection(collection);
490
+ for (const [field, value] of Object.entries(where)) {
491
+ queryBuilder = queryBuilder.whereField(field).equals(value);
492
+ }
493
+ return await queryBuilder.count();
494
+ }
495
+ catch (error) {
496
+ console.error(`countDocuments error for ${collection}:`, error);
497
+ return 0;
498
+ }
499
+ }
500
+ generateId() {
501
+ const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
502
+ let id = '';
503
+ for (let i = 0; i < 24; i++) {
504
+ id += chars[Math.floor(Math.random() * chars.length)];
505
+ }
506
+ return id;
507
+ }
508
+ async getTaskStatus(ticketId) {
509
+ try {
510
+ const response = await this.http.get(`/task/${ticketId}`);
511
+ return response.data;
512
+ }
513
+ catch (error) {
514
+ throw error instanceof types_1.OnChainDBError ? error :
515
+ new types_1.OnChainDBError('Failed to get task status', 'TASK_STATUS_ERROR');
516
+ }
517
+ }
518
+ async waitForTaskCompletion(ticketId, pollInterval = 2000, maxWaitTime = 600000) {
519
+ const startTime = Date.now();
520
+ console.log(`🔄 Waiting for task ${ticketId} to complete...`);
521
+ while (Date.now() - startTime < maxWaitTime) {
522
+ try {
523
+ const taskInfo = await this.getTaskStatus(ticketId);
524
+ if (typeof taskInfo.status === 'object') {
525
+ console.log(`📊 Task ${ticketId} status:`, JSON.stringify(taskInfo.status));
526
+ }
527
+ else {
528
+ console.log(`📊 Task ${ticketId} status: ${taskInfo.status}`);
529
+ }
530
+ if (taskInfo.status === 'Completed') {
531
+ console.log(`✅ Task ${ticketId} completed successfully`);
532
+ return taskInfo;
533
+ }
534
+ if (typeof taskInfo.status === 'object' && 'Failed' in taskInfo.status) {
535
+ const error = taskInfo.status.Failed.error;
536
+ console.error(`🚫 Task ${ticketId} failed: ${error}`);
537
+ throw new types_1.OnChainDBError(`Task failed: ${error}`, 'TASK_FAILED');
538
+ }
539
+ if (typeof taskInfo.status === 'string' && taskInfo.status.toLowerCase().includes('error')) {
540
+ console.error(`🚫 Task ${ticketId} has error status: ${taskInfo.status}`);
541
+ throw new types_1.OnChainDBError(`Task error: ${taskInfo.status}`, 'TASK_FAILED');
542
+ }
543
+ await this.sleep(pollInterval);
544
+ }
545
+ catch (error) {
546
+ console.error(`❌ Error polling task ${ticketId}:`, error);
547
+ if (error instanceof types_1.OnChainDBError) {
548
+ if (error.code === 'TASK_FAILED' || error.statusCode === 404 || error.statusCode === 400) {
549
+ console.error(`🚫 Stopping polling due to permanent error: ${error.message}`);
550
+ throw error;
551
+ }
552
+ }
553
+ if (Date.now() - startTime >= maxWaitTime) {
554
+ throw new types_1.OnChainDBError(`Task completion timeout after ${maxWaitTime}ms. Last error: ${error instanceof Error ? error.message : String(error)}`, 'TASK_TIMEOUT');
555
+ }
556
+ console.warn(`⚠️ Temporary error polling task ${ticketId}, retrying in ${pollInterval}ms...`);
557
+ await this.sleep(pollInterval);
558
+ }
559
+ }
560
+ throw new types_1.OnChainDBError(`Task completion timeout after ${maxWaitTime}ms`, 'TASK_TIMEOUT');
561
+ }
562
+ async uploadBlob(request) {
563
+ try {
564
+ const appId = this.config.appId;
565
+ if (!appId) {
566
+ throw new types_1.ValidationError('appId must be configured to upload blobs');
567
+ }
568
+ const formData = new FormData();
569
+ formData.append('blob', request.blob);
570
+ if (request.metadata) {
571
+ formData.append('metadata', JSON.stringify(request.metadata));
572
+ }
573
+ formData.append('payment_tx_hash', request.payment_tx_hash);
574
+ formData.append('user_address', request.user_address);
575
+ formData.append('broker_address', request.broker_address);
576
+ formData.append('amount_utia', String(request.amount_utia));
577
+ const response = await this.http.post(`/api/apps/${appId}/blobs/${request.collection}`, formData, {
578
+ headers: {
579
+ 'Content-Type': 'multipart/form-data',
580
+ 'X-App-Key': this.config.appKey
581
+ }
582
+ });
583
+ return response.data;
584
+ }
585
+ catch (error) {
586
+ throw error instanceof types_1.OnChainDBError ? error :
587
+ new types_1.OnChainDBError('Failed to upload blob', 'BLOB_UPLOAD_ERROR');
588
+ }
589
+ }
590
+ async retrieveBlob(request) {
591
+ try {
592
+ const appId = this.config.appId;
593
+ if (!appId) {
594
+ throw new types_1.ValidationError('appId must be configured to retrieve blobs');
595
+ }
596
+ const response = await this.http.get(`/api/apps/${appId}/blobs/${request.collection}/${request.blob_id}`, {
597
+ responseType: 'arraybuffer',
598
+ headers: {
599
+ 'X-App-Key': this.config.appKey
600
+ }
601
+ });
602
+ if (typeof global.window !== 'undefined' && typeof Blob !== 'undefined') {
603
+ const contentType = response.headers['content-type'] || 'application/octet-stream';
604
+ return new Blob([response.data], { type: contentType });
605
+ }
606
+ else {
607
+ return Buffer.from(response.data);
608
+ }
609
+ }
610
+ catch (error) {
611
+ throw error instanceof types_1.OnChainDBError ? error :
612
+ new types_1.OnChainDBError('Failed to retrieve blob', 'BLOB_RETRIEVAL_ERROR');
613
+ }
614
+ }
615
+ async queryBlobMetadata(collection, where = {}) {
616
+ return await this.findMany(collection, where);
617
+ }
618
+ validateStoreRequest(request) {
619
+ if (!request.root && !request.collection) {
620
+ throw new types_1.ValidationError('Either root or collection must be provided');
621
+ }
622
+ if (request.root && typeof request.root !== 'string') {
623
+ throw new types_1.ValidationError('Root must be a valid string in format "app::collection"');
624
+ }
625
+ if (request.collection && typeof request.collection !== 'string') {
626
+ throw new types_1.ValidationError('Collection must be a valid string');
627
+ }
628
+ if (!request.data || !Array.isArray(request.data)) {
629
+ throw new types_1.ValidationError('Data must be an array of objects');
630
+ }
631
+ if (request.data.length === 0) {
632
+ throw new types_1.ValidationError('Data array cannot be empty');
633
+ }
634
+ for (const item of request.data) {
635
+ if (!item || typeof item !== 'object') {
636
+ throw new types_1.ValidationError('Each data item must be a valid object');
637
+ }
638
+ }
639
+ const dataSize = JSON.stringify(request.data).length;
640
+ if (dataSize > 5 * 1024 * 1024) {
641
+ throw new types_1.ValidationError('Total data size exceeds 5MB limit');
642
+ }
643
+ }
644
+ handleHttpError(error) {
645
+ console.error(error);
646
+ if (error.response) {
647
+ const statusCode = error.response.status;
648
+ const message = error.response.data?.error || error.message;
649
+ if (statusCode >= 400 && statusCode < 500) {
650
+ return new types_1.ValidationError(message, error.response.data);
651
+ }
652
+ return new types_1.OnChainDBError(message, 'HTTP_ERROR', statusCode, error.response.data);
653
+ }
654
+ if (error.request) {
655
+ return new types_1.OnChainDBError('Network error - could not reach OnChainDB service', 'NETWORK_ERROR');
656
+ }
657
+ return new types_1.OnChainDBError(error.message, 'UNKNOWN_ERROR');
658
+ }
659
+ sleep(ms) {
660
+ return new Promise(resolve => setTimeout(resolve, ms));
661
+ }
662
+ buildRoot(collection) {
663
+ if (!this.config.appId) {
664
+ return collection;
665
+ }
666
+ return `${this.config.appId}::${collection}`;
667
+ }
668
+ resolveRoot(request) {
669
+ if (request.root) {
670
+ return request.root;
671
+ }
672
+ if (request.collection) {
673
+ return this.buildRoot(request.collection);
674
+ }
675
+ throw new types_1.ValidationError('Either root or collection must be provided');
676
+ }
677
+ }
678
+ exports.OnChainDBClient = OnChainDBClient;
679
+ //# sourceMappingURL=client.js.map