@onchaindb/sdk 0.4.0 → 0.4.2

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 (98) hide show
  1. package/.DS_Store +0 -0
  2. package/.claude/settings.local.json +8 -0
  3. package/.gitignore +5 -0
  4. package/.idea/.gitignore +5 -0
  5. package/.idea/compiler.xml +6 -0
  6. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  7. package/.idea/jsLinters/eslint.xml +6 -0
  8. package/.idea/modules.xml +8 -0
  9. package/.idea/prettier.xml +7 -0
  10. package/.idea/sdk.iml +12 -0
  11. package/.idea/vcs.xml +6 -0
  12. package/.idea/workspace.xml +257 -0
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +11 -3
  15. package/dist/client.js.map +1 -1
  16. package/dist/database.d.ts +0 -20
  17. package/dist/database.d.ts.map +1 -1
  18. package/dist/database.js +0 -40
  19. package/dist/database.js.map +1 -1
  20. package/dist/query-sdk/tests/setup.d.ts +16 -0
  21. package/dist/query-sdk/tests/setup.d.ts.map +1 -0
  22. package/dist/query-sdk/tests/setup.js +49 -0
  23. package/dist/query-sdk/tests/setup.js.map +1 -0
  24. package/examples/basic-usage.ts +136 -0
  25. package/examples/blob-upload-example.ts +140 -0
  26. package/examples/collection-schema-example.ts +304 -0
  27. package/examples/server-side-joins.ts +201 -0
  28. package/examples/tweet-self-joins-example.ts +352 -0
  29. package/package-lock.json +3823 -0
  30. package/package.json +1 -1
  31. package/skills.md +1096 -0
  32. package/src/.env +1 -0
  33. package/src/batch.d.ts +121 -0
  34. package/src/batch.js +205 -0
  35. package/src/batch.ts +257 -0
  36. package/src/client.ts +1856 -0
  37. package/src/database.d.ts +268 -0
  38. package/src/database.js +294 -0
  39. package/src/database.ts +695 -0
  40. package/src/index.d.ts +160 -0
  41. package/src/index.js +186 -0
  42. package/src/index.ts +253 -0
  43. package/src/query-sdk/ConditionBuilder.ts +103 -0
  44. package/src/query-sdk/FieldConditionBuilder.ts +2 -0
  45. package/src/query-sdk/NestedBuilders.ts +186 -0
  46. package/src/query-sdk/OnChainDB.ts +294 -0
  47. package/src/query-sdk/QueryBuilder.ts +1191 -0
  48. package/src/query-sdk/QueryResult.ts +375 -0
  49. package/src/query-sdk/README.md +866 -0
  50. package/src/query-sdk/SelectionBuilder.ts +94 -0
  51. package/src/query-sdk/adapters/HttpClientAdapter.ts +249 -0
  52. package/src/query-sdk/dist/ConditionBuilder.d.ts +22 -0
  53. package/src/query-sdk/dist/ConditionBuilder.js +90 -0
  54. package/src/query-sdk/dist/FieldConditionBuilder.d.ts +1 -0
  55. package/src/query-sdk/dist/FieldConditionBuilder.js +6 -0
  56. package/src/query-sdk/dist/NestedBuilders.d.ts +43 -0
  57. package/src/query-sdk/dist/NestedBuilders.js +144 -0
  58. package/src/query-sdk/dist/OnChainDB.d.ts +19 -0
  59. package/src/query-sdk/dist/OnChainDB.js +123 -0
  60. package/src/query-sdk/dist/QueryBuilder.d.ts +70 -0
  61. package/src/query-sdk/dist/QueryBuilder.js +295 -0
  62. package/src/query-sdk/dist/QueryResult.d.ts +52 -0
  63. package/src/query-sdk/dist/QueryResult.js +293 -0
  64. package/src/query-sdk/dist/SelectionBuilder.d.ts +20 -0
  65. package/src/query-sdk/dist/SelectionBuilder.js +80 -0
  66. package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +27 -0
  67. package/src/query-sdk/dist/adapters/HttpClientAdapter.js +170 -0
  68. package/src/query-sdk/dist/index.d.ts +36 -0
  69. package/src/query-sdk/dist/index.js +27 -0
  70. package/src/query-sdk/dist/operators.d.ts +56 -0
  71. package/src/query-sdk/dist/operators.js +289 -0
  72. package/src/query-sdk/dist/tests/setup.d.ts +15 -0
  73. package/src/query-sdk/dist/tests/setup.js +46 -0
  74. package/src/query-sdk/index.ts +59 -0
  75. package/src/query-sdk/jest.config.js +25 -0
  76. package/src/query-sdk/operators.ts +335 -0
  77. package/src/query-sdk/package.json +46 -0
  78. package/src/query-sdk/tests/FieldConditionBuilder.test.ts +84 -0
  79. package/src/query-sdk/tests/LogicalOperator.test.ts +85 -0
  80. package/src/query-sdk/tests/NestedBuilders.test.ts +321 -0
  81. package/src/query-sdk/tests/QueryBuilder.test.ts +348 -0
  82. package/src/query-sdk/tests/QueryResult.test.ts +464 -0
  83. package/src/query-sdk/tests/aggregations.test.ts +653 -0
  84. package/src/query-sdk/tests/comprehensive.test.ts +279 -0
  85. package/src/query-sdk/tests/integration.test.ts +608 -0
  86. package/src/query-sdk/tests/operators.test.ts +327 -0
  87. package/src/query-sdk/tests/setup.ts +59 -0
  88. package/src/query-sdk/tests/unit.test.ts +794 -0
  89. package/src/query-sdk/tsconfig.json +26 -0
  90. package/src/query-sdk/yarn.lock +3092 -0
  91. package/src/types.d.ts +131 -0
  92. package/src/types.js +46 -0
  93. package/src/types.ts +534 -0
  94. package/src/x402/index.ts +12 -0
  95. package/src/x402/types.ts +250 -0
  96. package/src/x402/utils.ts +332 -0
  97. package/tsconfig.json +20 -0
  98. package/yarn.lock +2309 -0
@@ -0,0 +1,695 @@
1
+ // Database Management Client for OnChainDB SDK
2
+ // Provides comprehensive collection and index management capabilities
3
+
4
+ import { AxiosInstance, AxiosError } from 'axios';
5
+ import { OnChainDBError } from './types';
6
+ import { buildPaymentPayload, encodePaymentHeader } from './x402/utils';
7
+ import type { X402PaymentRequirement } from './x402/types';
8
+
9
+ // Database Management Types
10
+ export interface Collection {
11
+ name: string;
12
+ schema?: CollectionSchema;
13
+ indexes?: Index[];
14
+ metadata?: CollectionMetadata;
15
+ }
16
+
17
+ export interface CollectionSchema {
18
+ fields: { [fieldName: string]: FieldDefinition };
19
+ required?: string[];
20
+ relationships?: Relationship[];
21
+ }
22
+
23
+ export interface FieldDefinition {
24
+ type: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array';
25
+ index?: boolean;
26
+ unique?: boolean;
27
+ required?: boolean;
28
+ default?: any;
29
+ validation?: FieldValidation;
30
+ }
31
+
32
+ export interface FieldValidation {
33
+ min?: number;
34
+ max?: number;
35
+ pattern?: string;
36
+ enum?: any[];
37
+ custom?: (value: any) => boolean | string;
38
+ }
39
+
40
+ export interface Relationship {
41
+ type: 'one-to-one' | 'one-to-many' | 'many-to-many';
42
+ collection: string;
43
+ localField: string;
44
+ foreignField: string;
45
+ cascade?: boolean;
46
+ }
47
+
48
+ export interface Index {
49
+ name: string;
50
+ collection: string;
51
+ field_name: string;
52
+ index_type: 'btree' | 'hash' | 'fulltext' | 'composite' | 'price';
53
+ fields?: string[]; // For composite indexes
54
+ options?: IndexOptions;
55
+ price_config?: PriceConfig; // For price-based payment splits
56
+ status?: IndexStatus;
57
+ statistics?: IndexStatistics;
58
+ }
59
+
60
+ export interface PriceConfig {
61
+ app_owner_percentage: number; // e.g., 0.80 for 80%
62
+ platform_percentage: number; // e.g., 0.20 for 20%
63
+ app_owner_address: string; // Wallet to receive app owner share
64
+ }
65
+
66
+ export interface IndexOptions {
67
+ unique?: boolean;
68
+ sparse?: boolean;
69
+ background?: boolean;
70
+ partialFilter?: any;
71
+ textIndexVersion?: number;
72
+ weights?: { [field: string]: number };
73
+ defaultLanguage?: string;
74
+ }
75
+
76
+ export interface IndexStatus {
77
+ building: boolean;
78
+ progress?: number;
79
+ error?: string;
80
+ created_at?: string;
81
+ completed_at?: string;
82
+ }
83
+
84
+ export interface IndexStatistics {
85
+ size: number;
86
+ usage_count: number;
87
+ last_used?: string;
88
+ efficiency_score?: number;
89
+ memory_usage?: number;
90
+ }
91
+
92
+ export interface CollectionMetadata {
93
+ created_at: string;
94
+ updated_at: string;
95
+ document_count: number;
96
+ storage_size: number;
97
+ avg_document_size: number;
98
+ }
99
+
100
+ export interface DatabaseStats {
101
+ total_collections: number;
102
+ total_indexes: number;
103
+ total_documents: number;
104
+ storage_size: number;
105
+ index_size: number;
106
+ active_connections: number;
107
+ }
108
+
109
+ export interface QueryPlan {
110
+ query: any;
111
+ indexes_used: string[];
112
+ estimated_cost: number;
113
+ execution_time_estimate: number;
114
+ optimization_hints: string[];
115
+ }
116
+
117
+ export interface BatchOperation {
118
+ operation: 'create' | 'update' | 'delete';
119
+ collection: string;
120
+ data: any;
121
+ conditions?: any;
122
+ }
123
+
124
+ export interface BatchResult {
125
+ success: boolean;
126
+ operation: string;
127
+ collection: string;
128
+ result?: any;
129
+ error?: string;
130
+ }
131
+
132
+ // Materialized View Types
133
+ export interface MaterializedView {
134
+ name: string;
135
+ source_collections: string[];
136
+ query: any;
137
+ created_at?: string;
138
+ }
139
+
140
+ export interface ViewInfo {
141
+ name: string;
142
+ source_collections: string[];
143
+ created_at: string;
144
+ }
145
+
146
+ export interface ListViewsResponse {
147
+ views: ViewInfo[];
148
+ }
149
+
150
+ /**
151
+ * Comprehensive database management client for OnChainDB
152
+ *
153
+ * Provides full CRUD operations for collections, indexes, and schemas
154
+ * with advanced features like query planning, batch operations, and statistics
155
+ */
156
+ export class DatabaseManager {
157
+ constructor(
158
+ private httpClient: AxiosInstance,
159
+ private serverUrl: string,
160
+ private appId: string,
161
+ private apiKey?: string
162
+ ) {}
163
+
164
+ // ==================== Index Management ====================
165
+
166
+ /**
167
+ * Create a new index on a collection with x402 payment support
168
+ *
169
+ * Index creation is currently FREE but this method handles 402 responses
170
+ * in case payment is required in the future.
171
+ *
172
+ * @param indexDefinition - Index definition without status/statistics
173
+ * @param paymentOptions - Optional payment configuration (for future use)
174
+ * @returns Created index
175
+ */
176
+ async createIndex(
177
+ indexDefinition: Omit<Index, 'status' | 'statistics'>,
178
+ paymentOptions?: {
179
+ userWallet?: any;
180
+ }
181
+ ): Promise<Index> {
182
+ const endpoint = `/api/apps/${this.appId}/indexes`;
183
+
184
+ try {
185
+ // First attempt - server will return 402 if payment required
186
+ const response = await this.httpClient.post(
187
+ endpoint,
188
+ indexDefinition,
189
+ { headers: this.getHeaders() }
190
+ );
191
+ return response.data;
192
+ } catch (error) {
193
+ const axiosError = error as AxiosError;
194
+
195
+ // Handle 402 Payment Required (for future use when payment is enabled)
196
+ if (axiosError.response?.status === 402) {
197
+ if (!paymentOptions?.userWallet) {
198
+ throw new OnChainDBError(
199
+ 'Payment required but no wallet provided. Pass userWallet in paymentOptions.',
200
+ 'PAYMENT_REQUIRED'
201
+ );
202
+ }
203
+
204
+ console.log('[x402] Payment required for index creation');
205
+
206
+ // Parse payment requirement from response
207
+ const paymentData = axiosError.response.data as any;
208
+ const accepts = paymentData?.accepts;
209
+
210
+ if (!accepts || accepts.length === 0) {
211
+ throw new OnChainDBError('Invalid 402 response: no payment options', 'PAYMENT_ERROR');
212
+ }
213
+
214
+ // Use first accepted payment option (native TIA)
215
+ const requirement: X402PaymentRequirement = accepts[0];
216
+ const amountUtia = parseInt(requirement.maxAmountRequired || '0', 10);
217
+ const brokerAddress = requirement.payTo;
218
+
219
+ console.log(`[x402] Index creation cost: ${amountUtia} utia to ${brokerAddress}`);
220
+
221
+ // Execute payment via wallet
222
+ const paymentResult = await paymentOptions.userWallet.signAndBroadcast(
223
+ brokerAddress,
224
+ `${amountUtia}utia`,
225
+ `OnChainDB index creation - ${indexDefinition.field_name}`
226
+ );
227
+
228
+ if (!paymentResult.success) {
229
+ throw new OnChainDBError(`Payment failed: ${paymentResult.error}`, 'PAYMENT_ERROR');
230
+ }
231
+
232
+ console.log(`[x402] Payment successful: ${paymentResult.txHash}`);
233
+
234
+ // Build X-PAYMENT header and retry
235
+ const x402Payload = buildPaymentPayload(requirement, {
236
+ txHash: paymentResult.txHash,
237
+ network: requirement.network,
238
+ sender: paymentResult.sender || '',
239
+ chainType: 'cosmos',
240
+ paymentMethod: 'native'
241
+ });
242
+ const encodedPayment = encodePaymentHeader(x402Payload);
243
+
244
+ const retryResponse = await this.httpClient.post(
245
+ endpoint,
246
+ indexDefinition,
247
+ {
248
+ headers: {
249
+ ...this.getHeaders(),
250
+ 'X-PAYMENT': encodedPayment
251
+ }
252
+ }
253
+ );
254
+
255
+ return retryResponse.data;
256
+ }
257
+
258
+ // Re-throw other errors
259
+ throw error;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Create multiple indexes in a batch operation
265
+ */
266
+ async createIndexes(
267
+ collection: string,
268
+ indexes: Omit<Index, 'collection' | 'status' | 'statistics'>[]
269
+ ): Promise<Index[]> {
270
+ // Since batch endpoint doesn't exist, create indexes individually
271
+ const results: Index[] = [];
272
+
273
+ for (const indexDef of indexes) {
274
+ const result = await this.createIndex({ ...indexDef, collection });
275
+ results.push(result);
276
+ }
277
+
278
+ return results;
279
+ }
280
+
281
+ /**
282
+ * List all indexes for a collection or entire application
283
+ */
284
+ async listIndexes(collection?: string, includeStats?: boolean): Promise<Index[]> {
285
+ const endpoint = collection
286
+ ? `/api/apps/${this.appId}/collections/${collection}/indexes`
287
+ : `/api/apps/${this.appId}/indexes`;
288
+
289
+ const params = includeStats ? { include_stats: true } : {};
290
+
291
+ const response = await this.httpClient.get(endpoint, {
292
+ params,
293
+ headers: this.getHeaders()
294
+ });
295
+
296
+ return response.data;
297
+ }
298
+
299
+ /**
300
+ * Get detailed information about a specific index
301
+ */
302
+ async getIndex(collection: string, indexName: string): Promise<Index> {
303
+ const response = await this.httpClient.get(
304
+ `/api/apps/${this.appId}/collections/${collection}/indexes/${indexName}`,
305
+ { headers: this.getHeaders() }
306
+ );
307
+
308
+ return response.data;
309
+ }
310
+
311
+ /**
312
+ * Check the status of index building operations
313
+ */
314
+ async getIndexStatus(collection: string, indexName: string): Promise<IndexStatus> {
315
+ const response = await this.httpClient.get(
316
+ `/api/apps/${this.appId}/collections/${collection}/indexes/${indexName}/status`,
317
+ { headers: this.getHeaders() }
318
+ );
319
+
320
+ return response.data;
321
+ }
322
+
323
+ /**
324
+ * Get index performance statistics
325
+ */
326
+ async getIndexStatistics(collection: string, indexName: string): Promise<IndexStatistics> {
327
+ const response = await this.httpClient.get(
328
+ `/api/apps/${this.appId}/collections/${collection}/indexes/${indexName}/statistics`,
329
+ { headers: this.getHeaders() }
330
+ );
331
+
332
+ return response.data;
333
+ }
334
+
335
+ /**
336
+ * Rebuild an existing index
337
+ */
338
+ async rebuildIndex(
339
+ collection: string,
340
+ indexName: string,
341
+ options?: { background?: boolean }
342
+ ): Promise<{ success: boolean; message: string }> {
343
+ const response = await this.httpClient.post(
344
+ `/api/apps/${this.appId}/collections/${collection}/indexes/${indexName}/rebuild`,
345
+ options || {},
346
+ { headers: this.getHeaders() }
347
+ );
348
+
349
+ return response.data;
350
+ }
351
+
352
+ /**
353
+ * Drop/delete an index
354
+ */
355
+ async dropIndex(collection: string, indexName: string): Promise<{ success: boolean; message: string }> {
356
+ const response = await this.httpClient.delete(
357
+ `/api/apps/${this.appId}/collections/${collection}/indexes/${indexName}`,
358
+ { headers: this.getHeaders() }
359
+ );
360
+
361
+ return response.data;
362
+ }
363
+
364
+ /**
365
+ * Create a Price index for on-chain commerce
366
+ *
367
+ * Price indexes enable automatic payment splits when writing to collections.
368
+ * Perfect for order/purchase collections where you want to charge based on item price.
369
+ *
370
+ * @param collection - Collection name (e.g., 'orders')
371
+ * @param fieldName - Price field name (e.g., 'total_price')
372
+ * @param config - Optional payment split configuration
373
+ * @returns Created index
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * // Create price index on orders collection
378
+ * await db.createPriceIndex('orders', 'total_price', {
379
+ * app_owner_percentage: 0.80, // 80% to merchant
380
+ * platform_percentage: 0.20, // 20% to platform
381
+ * app_owner_address: 'celestia1abc...'
382
+ * });
383
+ *
384
+ * // Now when writing orders, price field triggers automatic split:
385
+ * await client.store({
386
+ * collection: 'orders',
387
+ * data: {
388
+ * order_id: '123',
389
+ * total_price: 100 // 80 TIA to merchant, 20 TIA to platform
390
+ * }
391
+ * });
392
+ * ```
393
+ */
394
+ async createPriceIndex(
395
+ collection: string,
396
+ fieldName: string,
397
+ config?: Partial<PriceConfig>
398
+ ): Promise<Index> {
399
+ const priceConfig: PriceConfig = {
400
+ app_owner_percentage: config?.app_owner_percentage ?? 0.80,
401
+ platform_percentage: config?.platform_percentage ?? 0.20,
402
+ app_owner_address: config?.app_owner_address ?? ''
403
+ };
404
+
405
+ return this.createIndex({
406
+ name: `${collection}_${fieldName}_price_index`,
407
+ collection,
408
+ field_name: fieldName,
409
+ index_type: 'price',
410
+ price_config: priceConfig
411
+ });
412
+ }
413
+
414
+ // ==================== Schema Management ====================
415
+
416
+ /**
417
+ * Validate data against collection schema
418
+ */
419
+ async validateSchema(collection: string, data: any): Promise<{ valid: boolean; errors?: string[] }> {
420
+ const response = await this.httpClient.post(
421
+ `/api/apps/${this.appId}/collections/${collection}/validate`,
422
+ { data },
423
+ { headers: this.getHeaders() }
424
+ );
425
+
426
+ return response.data;
427
+ }
428
+
429
+ /**
430
+ * Migrate collection data to new schema
431
+ */
432
+ async migrateSchema(
433
+ collection: string,
434
+ newSchema: CollectionSchema,
435
+ options?: { dryRun?: boolean; batchSize?: number }
436
+ ): Promise<{ success: boolean; migrated: number; errors?: any[] }> {
437
+ const payload = { schema: newSchema, ...options };
438
+
439
+ const response = await this.httpClient.post(
440
+ `/api/apps/${this.appId}/collections/${collection}/migrate`,
441
+ payload,
442
+ { headers: this.getHeaders() }
443
+ );
444
+
445
+ return response.data;
446
+ }
447
+
448
+ // ==================== Query Planning and Optimization ====================
449
+
450
+ /**
451
+ * Explain query execution plan
452
+ */
453
+ async explainQuery(collection: string, query: any): Promise<QueryPlan> {
454
+ const response = await this.httpClient.post(
455
+ `/api/apps/${this.appId}/collections/${collection}/explain`,
456
+ { query },
457
+ { headers: this.getHeaders() }
458
+ );
459
+
460
+ return response.data;
461
+ }
462
+
463
+ /**
464
+ * Get optimization suggestions for query performance
465
+ */
466
+ async getOptimizationSuggestions(collection: string): Promise<{
467
+ missing_indexes: string[];
468
+ unused_indexes: string[];
469
+ slow_queries: any[];
470
+ recommendations: string[];
471
+ }> {
472
+ const response = await this.httpClient.get(
473
+ `/api/apps/${this.appId}/collections/${collection}/optimize`,
474
+ { headers: this.getHeaders() }
475
+ );
476
+
477
+ return response.data;
478
+ }
479
+
480
+ // ==================== Batch Operations ====================
481
+
482
+ /**
483
+ * Execute multiple database operations in a single transaction
484
+ */
485
+ async executeBatch(operations: BatchOperation[]): Promise<BatchResult[]> {
486
+ const response = await this.httpClient.post(
487
+ `/api/apps/${this.appId}/batch`,
488
+ { operations },
489
+ { headers: this.getHeaders() }
490
+ );
491
+
492
+ return response.data;
493
+ }
494
+
495
+ // ==================== Materialized Views Management ====================
496
+
497
+ /**
498
+ * Create a materialized view
499
+ *
500
+ * Materialized views are pre-computed query results that update automatically
501
+ * when source data changes. Great for complex aggregations and joins.
502
+ *
503
+ * @param name - Unique name for the view
504
+ * @param sourceCollections - Collections this view depends on
505
+ * @param query - Query definition for the view
506
+ * @returns Created view definition
507
+ *
508
+ * @example
509
+ * ```typescript
510
+ * // Create a view for top-selling products
511
+ * await db.createView('topSellers', ['products', 'orders'], {
512
+ * select: ['id', 'name', 'price', 'salesCount'],
513
+ * where: { status: 'active' },
514
+ * orderBy: { salesCount: 'desc' },
515
+ * limit: 100
516
+ * });
517
+ * ```
518
+ */
519
+ async createView(
520
+ name: string,
521
+ sourceCollections: string[],
522
+ query: any
523
+ ): Promise<MaterializedView> {
524
+ const payload = {
525
+ name,
526
+ source_collections: sourceCollections,
527
+ query
528
+ };
529
+
530
+ const response = await this.httpClient.post(
531
+ `/apps/${this.appId}/views`,
532
+ payload,
533
+ { headers: this.getHeaders() }
534
+ );
535
+
536
+ return response.data;
537
+ }
538
+
539
+ /**
540
+ * List all materialized views for the app
541
+ */
542
+ async listViews(): Promise<ViewInfo[]> {
543
+ const response = await this.httpClient.get(
544
+ `/apps/${this.appId}/views`,
545
+ { headers: this.getHeaders() }
546
+ );
547
+
548
+ return response.data.views || response.data;
549
+ }
550
+
551
+ /**
552
+ * Get a specific materialized view
553
+ */
554
+ async getView(name: string): Promise<MaterializedView> {
555
+ const response = await this.httpClient.get(
556
+ `/apps/${this.appId}/views/${name}`,
557
+ { headers: this.getHeaders() }
558
+ );
559
+
560
+ return response.data;
561
+ }
562
+
563
+ /**
564
+ * Delete a materialized view
565
+ */
566
+ async deleteView(name: string): Promise<{ success: boolean; message: string }> {
567
+ const response = await this.httpClient.delete(
568
+ `/apps/${this.appId}/views/${name}`,
569
+ { headers: this.getHeaders() }
570
+ );
571
+
572
+ return response.data;
573
+ }
574
+
575
+ /**
576
+ * Refresh/rebuild a materialized view
577
+ */
578
+ async refreshView(name: string): Promise<{ success: boolean; message: string }> {
579
+ const response = await this.httpClient.post(
580
+ `/apps/${this.appId}/views/${name}/refresh`,
581
+ {},
582
+ { headers: this.getHeaders() }
583
+ );
584
+
585
+ return response.data;
586
+ }
587
+
588
+ // ==================== Statistics and Monitoring ====================
589
+
590
+ /**
591
+ * Get overall database statistics
592
+ */
593
+ async getDatabaseStats(): Promise<DatabaseStats> {
594
+ const response = await this.httpClient.get(
595
+ `/api/apps/${this.appId}/stats`,
596
+ { headers: this.getHeaders() }
597
+ );
598
+
599
+ return response.data;
600
+ }
601
+
602
+ /**
603
+ * Get collection-specific statistics
604
+ */
605
+ async getCollectionStats(collection: string): Promise<CollectionMetadata> {
606
+ const response = await this.httpClient.get(
607
+ `/api/apps/${this.appId}/collections/${collection}/stats`,
608
+ { headers: this.getHeaders() }
609
+ );
610
+
611
+ return response.data;
612
+ }
613
+
614
+ // ==================== Utility Methods ====================
615
+
616
+ /**
617
+ * Test database connection and basic functionality
618
+ */
619
+ async healthCheck(): Promise<{
620
+ healthy: boolean;
621
+ response_time: number;
622
+ version: string;
623
+ features: string[];
624
+ }> {
625
+ const startTime = Date.now();
626
+
627
+ try {
628
+ const response = await this.httpClient.get(
629
+ `/api/apps/${this.appId}/health`,
630
+ { headers: this.getHeaders() }
631
+ );
632
+
633
+ const responseTime = Date.now() - startTime;
634
+
635
+ return {
636
+ healthy: true,
637
+ response_time: responseTime,
638
+ version: response.data.version || '1.0.0',
639
+ features: response.data.features || []
640
+ };
641
+ } catch (error) {
642
+ return {
643
+ healthy: false,
644
+ response_time: Date.now() - startTime,
645
+ version: 'unknown',
646
+ features: []
647
+ };
648
+ }
649
+ }
650
+
651
+ /**
652
+ * Get API headers including authentication
653
+ */
654
+ private getHeaders(): { [key: string]: string } {
655
+ const headers: { [key: string]: string } = {
656
+ 'Content-Type': 'application/json'
657
+ };
658
+
659
+ if (this.apiKey) {
660
+ headers['Authorization'] = `Bearer ${this.apiKey}`;
661
+ }
662
+
663
+ return headers;
664
+ }
665
+
666
+ /**
667
+ * Set API key for authenticated operations
668
+ */
669
+ setApiKey(apiKey: string): void {
670
+ this.apiKey = apiKey;
671
+ }
672
+
673
+ /**
674
+ * Update server configuration
675
+ */
676
+ updateConfig(httpClient: AxiosInstance, serverUrl: string, appId: string): void {
677
+ this.httpClient = httpClient;
678
+ this.serverUrl = serverUrl;
679
+ this.appId = appId;
680
+ }
681
+ }
682
+
683
+ /**
684
+ * Factory function to create a DatabaseManager instance
685
+ */
686
+ export function createDatabaseManager(
687
+ httpClient: AxiosInstance,
688
+ serverUrl: string,
689
+ appId: string,
690
+ apiKey?: string
691
+ ): DatabaseManager {
692
+ return new DatabaseManager(httpClient, serverUrl, appId, apiKey);
693
+ }
694
+
695
+ // Types are available as imports, no need to re-export