@onchaindb/sdk 0.4.5 → 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.
- package/.claude/settings.local.json +10 -2
- package/README.md +422 -355
- package/dist/batch.d.ts +1 -10
- package/dist/batch.d.ts.map +1 -1
- package/dist/batch.js +4 -26
- package/dist/batch.js.map +1 -1
- package/dist/client.d.ts +31 -46
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +222 -357
- package/dist/client.js.map +1 -1
- package/dist/database.d.ts +14 -131
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +35 -131
- package/dist/database.js.map +1 -1
- package/dist/index.d.ts +10 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -18
- package/dist/index.js.map +1 -1
- package/dist/query-sdk/ConditionBuilder.d.ts +3 -11
- package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -1
- package/dist/query-sdk/ConditionBuilder.js +10 -48
- package/dist/query-sdk/ConditionBuilder.js.map +1 -1
- package/dist/query-sdk/NestedBuilders.d.ts +33 -30
- package/dist/query-sdk/NestedBuilders.d.ts.map +1 -1
- package/dist/query-sdk/NestedBuilders.js +46 -43
- package/dist/query-sdk/NestedBuilders.js.map +1 -1
- package/{src/query-sdk/dist/OnChainDB.d.ts → dist/query-sdk/OnDB.d.ts} +10 -2
- package/dist/query-sdk/OnDB.d.ts.map +1 -0
- package/{src/query-sdk/dist/OnChainDB.js → dist/query-sdk/OnDB.js} +86 -18
- package/dist/query-sdk/OnDB.js.map +1 -0
- package/dist/query-sdk/QueryBuilder.d.ts +4 -2
- package/dist/query-sdk/QueryBuilder.d.ts.map +1 -1
- package/dist/query-sdk/QueryBuilder.js +47 -169
- package/dist/query-sdk/QueryBuilder.js.map +1 -1
- package/dist/query-sdk/QueryResult.d.ts +0 -38
- package/dist/query-sdk/QueryResult.d.ts.map +1 -1
- package/dist/query-sdk/QueryResult.js +1 -227
- package/dist/query-sdk/QueryResult.js.map +1 -1
- package/dist/query-sdk/index.d.ts +2 -2
- package/dist/query-sdk/index.d.ts.map +1 -1
- package/dist/query-sdk/index.js +3 -3
- package/dist/query-sdk/index.js.map +1 -1
- package/dist/query-sdk/operators.d.ts +32 -28
- package/dist/query-sdk/operators.d.ts.map +1 -1
- package/dist/query-sdk/operators.js +45 -155
- package/dist/query-sdk/operators.js.map +1 -1
- package/dist/types.d.ts +159 -36
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -8
- package/dist/types.js.map +1 -1
- package/dist/x402/types.d.ts +1 -1
- package/dist/x402/types.d.ts.map +1 -1
- package/dist/x402/utils.js +2 -2
- package/dist/x402/utils.js.map +1 -1
- package/jest.config.js +4 -0
- package/package.json +1 -1
- package/skills.md +0 -1
- package/src/batch.d.ts +3 -3
- package/src/batch.js +1 -1
- package/src/client.ts +287 -823
- package/src/database.d.ts +1 -1
- package/src/database.js +4 -4
- package/src/database.ts +71 -494
- package/src/index.d.ts +18 -18
- package/src/index.js +16 -16
- package/src/index.ts +44 -198
- package/src/query-sdk/ConditionBuilder.ts +37 -89
- package/src/query-sdk/NestedBuilders.ts +90 -92
- package/src/query-sdk/{OnChainDB.ts → OnDB.ts} +1 -1
- package/src/query-sdk/QueryBuilder.ts +59 -218
- package/src/query-sdk/QueryResult.ts +4 -330
- package/src/query-sdk/README.md +218 -587
- package/src/query-sdk/index.ts +2 -2
- package/src/query-sdk/operators.ts +91 -200
- package/src/query-sdk/tests/FieldConditionBuilder.test.ts +70 -71
- package/src/query-sdk/tests/LogicalOperator.test.ts +43 -82
- package/src/query-sdk/tests/NestedBuilders.test.ts +229 -309
- package/src/query-sdk/tests/QueryBuilder.test.ts +5 -5
- package/src/query-sdk/tests/QueryResult.test.ts +41 -435
- package/src/query-sdk/tests/comprehensive.test.ts +4 -185
- package/src/tests/client-requests.test.ts +280 -0
- package/src/tests/client-validation.test.ts +80 -0
- package/src/types.d.ts +6 -6
- package/src/types.js +8 -8
- package/src/types.ts +239 -54
- package/src/x402/types.ts +3 -3
- package/src/x402/utils.ts +3 -3
- package/examples/blob-upload-example.ts +0 -140
- package/src/batch.ts +0 -257
- package/src/query-sdk/dist/ConditionBuilder.d.ts +0 -22
- package/src/query-sdk/dist/ConditionBuilder.js +0 -90
- package/src/query-sdk/dist/FieldConditionBuilder.d.ts +0 -1
- package/src/query-sdk/dist/FieldConditionBuilder.js +0 -6
- package/src/query-sdk/dist/NestedBuilders.d.ts +0 -43
- package/src/query-sdk/dist/NestedBuilders.js +0 -144
- package/src/query-sdk/dist/QueryBuilder.d.ts +0 -70
- package/src/query-sdk/dist/QueryBuilder.js +0 -295
- package/src/query-sdk/dist/QueryResult.d.ts +0 -52
- package/src/query-sdk/dist/QueryResult.js +0 -293
- package/src/query-sdk/dist/SelectionBuilder.d.ts +0 -20
- package/src/query-sdk/dist/SelectionBuilder.js +0 -80
- package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +0 -27
- package/src/query-sdk/dist/adapters/HttpClientAdapter.js +0 -170
- package/src/query-sdk/dist/index.d.ts +0 -36
- package/src/query-sdk/dist/index.js +0 -27
- package/src/query-sdk/dist/operators.d.ts +0 -56
- package/src/query-sdk/dist/operators.js +0 -289
- package/src/query-sdk/dist/tests/setup.d.ts +0 -15
- package/src/query-sdk/dist/tests/setup.js +0 -46
- package/src/query-sdk/jest.config.js +0 -25
- package/src/query-sdk/package.json +0 -46
- package/src/query-sdk/tests/aggregations.test.ts +0 -653
- package/src/query-sdk/tests/integration.test.ts +0 -608
- package/src/query-sdk/tests/operators.test.ts +0 -327
- package/src/query-sdk/tests/unit.test.ts +0 -794
- package/src/query-sdk/tsconfig.json +0 -26
- package/src/query-sdk/yarn.lock +0 -3092
package/src/database.ts
CHANGED
|
@@ -1,66 +1,28 @@
|
|
|
1
|
-
// Database Management Client for
|
|
2
|
-
// Provides comprehensive collection and index management capabilities
|
|
1
|
+
// Database Management Client for OnDB SDK
|
|
3
2
|
|
|
4
|
-
import { AxiosInstance
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
}
|
|
3
|
+
import { AxiosInstance } from 'axios';
|
|
4
|
+
import {
|
|
5
|
+
OnDBError,
|
|
6
|
+
SqlCreateViewRequest,
|
|
7
|
+
ViewDataResponse,
|
|
8
|
+
ViewQueryRequest,
|
|
9
|
+
CreateApiCollectionRequest,
|
|
10
|
+
} from './types';
|
|
47
11
|
|
|
48
12
|
export interface Index {
|
|
49
13
|
name: string;
|
|
50
14
|
collection: string;
|
|
51
15
|
field_name: string;
|
|
52
16
|
index_type: 'btree' | 'hash' | 'fulltext' | 'composite' | 'price';
|
|
53
|
-
fields?: string[];
|
|
17
|
+
fields?: string[];
|
|
54
18
|
options?: IndexOptions;
|
|
55
|
-
price_config?: PriceConfig;
|
|
56
|
-
status?: IndexStatus;
|
|
57
|
-
statistics?: IndexStatistics;
|
|
19
|
+
price_config?: PriceConfig;
|
|
58
20
|
}
|
|
59
21
|
|
|
60
22
|
export interface PriceConfig {
|
|
61
|
-
app_owner_percentage: number;
|
|
62
|
-
platform_percentage: number;
|
|
63
|
-
app_owner_address: string;
|
|
23
|
+
app_owner_percentage: number;
|
|
24
|
+
platform_percentage: number;
|
|
25
|
+
app_owner_address: string;
|
|
64
26
|
}
|
|
65
27
|
|
|
66
28
|
export interface IndexOptions {
|
|
@@ -73,62 +35,6 @@ export interface IndexOptions {
|
|
|
73
35
|
defaultLanguage?: string;
|
|
74
36
|
}
|
|
75
37
|
|
|
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
38
|
// Materialized View Types
|
|
133
39
|
export interface MaterializedView {
|
|
134
40
|
name: string;
|
|
@@ -147,217 +53,49 @@ export interface ListViewsResponse {
|
|
|
147
53
|
views: ViewInfo[];
|
|
148
54
|
}
|
|
149
55
|
|
|
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
56
|
export class DatabaseManager {
|
|
157
57
|
constructor(
|
|
158
58
|
private httpClient: AxiosInstance,
|
|
159
|
-
private serverUrl: string,
|
|
160
59
|
private appId: string,
|
|
161
60
|
private apiKey?: string
|
|
162
61
|
) {}
|
|
163
62
|
|
|
164
63
|
// ==================== Index Management ====================
|
|
165
64
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
}
|
|
65
|
+
async listIndexes(): Promise<Index[]> {
|
|
66
|
+
const response = await this.httpClient.get(
|
|
67
|
+
`/api/apps/${this.appId}/indexes`,
|
|
68
|
+
{ headers: this.getHeaders() }
|
|
69
|
+
);
|
|
70
|
+
return response.data;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async createIndex(indexDefinition: Omit<Index, 'status' | 'statistics'>): Promise<Index> {
|
|
74
|
+
const response = await this.httpClient.post(
|
|
75
|
+
`/api/apps/${this.appId}/indexes`,
|
|
76
|
+
indexDefinition,
|
|
77
|
+
{ headers: this.getHeaders() }
|
|
78
|
+
);
|
|
79
|
+
return response.data;
|
|
261
80
|
}
|
|
262
81
|
|
|
263
|
-
/**
|
|
264
|
-
* Create multiple indexes in a batch operation
|
|
265
|
-
*/
|
|
266
82
|
async createIndexes(
|
|
267
83
|
collection: string,
|
|
268
84
|
indexes: Omit<Index, 'collection' | 'status' | 'statistics'>[]
|
|
269
85
|
): Promise<Index[]> {
|
|
270
|
-
// Since batch endpoint doesn't exist, create indexes individually
|
|
271
86
|
const results: Index[] = [];
|
|
272
|
-
|
|
273
87
|
for (const indexDef of indexes) {
|
|
274
88
|
const result = await this.createIndex({ ...indexDef, collection });
|
|
275
89
|
results.push(result);
|
|
276
90
|
}
|
|
277
|
-
|
|
278
91
|
return results;
|
|
279
92
|
}
|
|
280
93
|
|
|
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 }> {
|
|
94
|
+
async dropIndex(indexId: string): Promise<{ success: boolean; message: string }> {
|
|
356
95
|
const response = await this.httpClient.delete(
|
|
357
|
-
`/api/apps/${this.appId}/
|
|
96
|
+
`/api/apps/${this.appId}/indexes/${indexId}`,
|
|
358
97
|
{ headers: this.getHeaders() }
|
|
359
98
|
);
|
|
360
|
-
|
|
361
99
|
return response.data;
|
|
362
100
|
}
|
|
363
101
|
|
|
@@ -367,28 +105,13 @@ export class DatabaseManager {
|
|
|
367
105
|
* Price indexes enable automatic payment splits when writing to collections.
|
|
368
106
|
* Perfect for order/purchase collections where you want to charge based on item price.
|
|
369
107
|
*
|
|
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
108
|
* @example
|
|
376
109
|
* ```typescript
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
* platform_percentage: 0.20, // 20% to platform
|
|
110
|
+
* await db.database().createPriceIndex('orders', 'total_price', {
|
|
111
|
+
* app_owner_percentage: 0.80,
|
|
112
|
+
* platform_percentage: 0.20,
|
|
381
113
|
* app_owner_address: 'celestia1abc...'
|
|
382
114
|
* });
|
|
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
115
|
* ```
|
|
393
116
|
*/
|
|
394
117
|
async createPriceIndex(
|
|
@@ -411,285 +134,139 @@ export class DatabaseManager {
|
|
|
411
134
|
});
|
|
412
135
|
}
|
|
413
136
|
|
|
414
|
-
// ====================
|
|
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 };
|
|
137
|
+
// ==================== Materialized Views ====================
|
|
438
138
|
|
|
139
|
+
async createViewSql(sql: string, refreshMode?: 'live' | 'lazy'): Promise<MaterializedView> {
|
|
140
|
+
const body: SqlCreateViewRequest = { sql };
|
|
141
|
+
if (refreshMode) body.refresh_mode = refreshMode;
|
|
439
142
|
const response = await this.httpClient.post(
|
|
440
|
-
`/
|
|
441
|
-
|
|
143
|
+
`/apps/${this.appId}/views/sql`,
|
|
144
|
+
body,
|
|
442
145
|
{ headers: this.getHeaders() }
|
|
443
146
|
);
|
|
444
|
-
|
|
445
147
|
return response.data;
|
|
446
148
|
}
|
|
447
149
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Explain query execution plan
|
|
452
|
-
*/
|
|
453
|
-
async explainQuery(collection: string, query: any): Promise<QueryPlan> {
|
|
150
|
+
async queryView<T = any>(viewName: string, query: ViewQueryRequest = {}): Promise<ViewDataResponse<T>> {
|
|
454
151
|
const response = await this.httpClient.post(
|
|
455
|
-
`/api/
|
|
456
|
-
|
|
152
|
+
`/api/views/${this.appId}/${viewName}/data`,
|
|
153
|
+
query,
|
|
457
154
|
{ headers: this.getHeaders() }
|
|
458
155
|
);
|
|
459
|
-
|
|
460
156
|
return response.data;
|
|
461
157
|
}
|
|
462
158
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
*/
|
|
466
|
-
async getOptimizationSuggestions(collection: string): Promise<{
|
|
467
|
-
missing_indexes: string[];
|
|
468
|
-
unused_indexes: string[];
|
|
469
|
-
slow_queries: any[];
|
|
470
|
-
recommendations: string[];
|
|
471
|
-
}> {
|
|
159
|
+
async countView(viewName: string, filter?: any): Promise<number> {
|
|
160
|
+
const params = filter ? { filter: JSON.stringify(filter) } : {};
|
|
472
161
|
const response = await this.httpClient.get(
|
|
473
|
-
`/api/
|
|
474
|
-
{ headers: this.getHeaders() }
|
|
162
|
+
`/api/views/${this.appId}/${viewName}/count`,
|
|
163
|
+
{ params, headers: this.getHeaders() }
|
|
475
164
|
);
|
|
476
|
-
|
|
477
|
-
return response.data;
|
|
165
|
+
return response.data?.count ?? response.data;
|
|
478
166
|
}
|
|
479
167
|
|
|
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
168
|
async createView(
|
|
520
169
|
name: string,
|
|
521
170
|
sourceCollections: string[],
|
|
522
171
|
query: any
|
|
523
172
|
): Promise<MaterializedView> {
|
|
524
|
-
const payload = {
|
|
525
|
-
name,
|
|
526
|
-
source_collections: sourceCollections,
|
|
527
|
-
query
|
|
528
|
-
};
|
|
529
|
-
|
|
530
173
|
const response = await this.httpClient.post(
|
|
531
174
|
`/apps/${this.appId}/views`,
|
|
532
|
-
|
|
175
|
+
{ name, source_collections: sourceCollections, query },
|
|
533
176
|
{ headers: this.getHeaders() }
|
|
534
177
|
);
|
|
535
|
-
|
|
536
178
|
return response.data;
|
|
537
179
|
}
|
|
538
180
|
|
|
539
|
-
/**
|
|
540
|
-
* List all materialized views for the app
|
|
541
|
-
*/
|
|
542
181
|
async listViews(): Promise<ViewInfo[]> {
|
|
543
182
|
const response = await this.httpClient.get(
|
|
544
183
|
`/apps/${this.appId}/views`,
|
|
545
184
|
{ headers: this.getHeaders() }
|
|
546
185
|
);
|
|
547
|
-
|
|
548
186
|
return response.data.views || response.data;
|
|
549
187
|
}
|
|
550
188
|
|
|
551
|
-
/**
|
|
552
|
-
* Get a specific materialized view
|
|
553
|
-
*/
|
|
554
189
|
async getView(name: string): Promise<MaterializedView> {
|
|
555
190
|
const response = await this.httpClient.get(
|
|
556
191
|
`/apps/${this.appId}/views/${name}`,
|
|
557
192
|
{ headers: this.getHeaders() }
|
|
558
193
|
);
|
|
559
|
-
|
|
560
194
|
return response.data;
|
|
561
195
|
}
|
|
562
196
|
|
|
563
|
-
/**
|
|
564
|
-
* Delete a materialized view
|
|
565
|
-
*/
|
|
566
197
|
async deleteView(name: string): Promise<{ success: boolean; message: string }> {
|
|
567
198
|
const response = await this.httpClient.delete(
|
|
568
199
|
`/apps/${this.appId}/views/${name}`,
|
|
569
200
|
{ headers: this.getHeaders() }
|
|
570
201
|
);
|
|
571
|
-
|
|
572
202
|
return response.data;
|
|
573
203
|
}
|
|
574
204
|
|
|
575
|
-
/**
|
|
576
|
-
* Refresh/rebuild a materialized view
|
|
577
|
-
*/
|
|
578
205
|
async refreshView(name: string): Promise<{ success: boolean; message: string }> {
|
|
579
206
|
const response = await this.httpClient.post(
|
|
580
207
|
`/apps/${this.appId}/views/${name}/refresh`,
|
|
581
208
|
{},
|
|
582
209
|
{ headers: this.getHeaders() }
|
|
583
210
|
);
|
|
584
|
-
|
|
585
211
|
return response.data;
|
|
586
212
|
}
|
|
587
213
|
|
|
588
|
-
// ====================
|
|
214
|
+
// ==================== API Collections ====================
|
|
589
215
|
|
|
590
|
-
|
|
591
|
-
* Get overall database statistics
|
|
592
|
-
*/
|
|
593
|
-
async getDatabaseStats(): Promise<DatabaseStats> {
|
|
216
|
+
async listApiCollections(): Promise<any[]> {
|
|
594
217
|
const response = await this.httpClient.get(
|
|
595
|
-
`/
|
|
218
|
+
`/apps/${this.appId}/api_collections`,
|
|
596
219
|
{ headers: this.getHeaders() }
|
|
597
220
|
);
|
|
221
|
+
return response.data;
|
|
222
|
+
}
|
|
598
223
|
|
|
224
|
+
async createApiCollection(request: CreateApiCollectionRequest): Promise<any> {
|
|
225
|
+
const response = await this.httpClient.post(
|
|
226
|
+
`/apps/${this.appId}/api_collections`,
|
|
227
|
+
request,
|
|
228
|
+
{ headers: this.getHeaders() }
|
|
229
|
+
);
|
|
599
230
|
return response.data;
|
|
600
231
|
}
|
|
601
232
|
|
|
602
|
-
|
|
603
|
-
* Get collection-specific statistics
|
|
604
|
-
*/
|
|
605
|
-
async getCollectionStats(collection: string): Promise<CollectionMetadata> {
|
|
233
|
+
async getApiCollection(collectionName: string): Promise<any> {
|
|
606
234
|
const response = await this.httpClient.get(
|
|
607
|
-
`/
|
|
235
|
+
`/apps/${this.appId}/api_collections/${collectionName}`,
|
|
608
236
|
{ headers: this.getHeaders() }
|
|
609
237
|
);
|
|
610
|
-
|
|
611
238
|
return response.data;
|
|
612
239
|
}
|
|
613
240
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
}
|
|
241
|
+
async deleteApiCollection(collectionName: string): Promise<{ success: boolean }> {
|
|
242
|
+
const response = await this.httpClient.delete(
|
|
243
|
+
`/apps/${this.appId}/api_collections/${collectionName}`,
|
|
244
|
+
{ headers: this.getHeaders() }
|
|
245
|
+
);
|
|
246
|
+
return response.data;
|
|
649
247
|
}
|
|
650
248
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
*/
|
|
249
|
+
// ==================== Utility ====================
|
|
250
|
+
|
|
654
251
|
private getHeaders(): { [key: string]: string } {
|
|
655
252
|
const headers: { [key: string]: string } = {
|
|
656
253
|
'Content-Type': 'application/json'
|
|
657
254
|
};
|
|
658
|
-
|
|
659
255
|
if (this.apiKey) {
|
|
660
256
|
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
661
257
|
}
|
|
662
|
-
|
|
663
258
|
return headers;
|
|
664
259
|
}
|
|
665
260
|
|
|
666
|
-
/**
|
|
667
|
-
* Set API key for authenticated operations
|
|
668
|
-
*/
|
|
669
261
|
setApiKey(apiKey: string): void {
|
|
670
262
|
this.apiKey = apiKey;
|
|
671
263
|
}
|
|
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
264
|
}
|
|
682
265
|
|
|
683
|
-
/**
|
|
684
|
-
* Factory function to create a DatabaseManager instance
|
|
685
|
-
*/
|
|
686
266
|
export function createDatabaseManager(
|
|
687
267
|
httpClient: AxiosInstance,
|
|
688
|
-
serverUrl: string,
|
|
689
268
|
appId: string,
|
|
690
269
|
apiKey?: string
|
|
691
270
|
): DatabaseManager {
|
|
692
|
-
return new DatabaseManager(httpClient,
|
|
271
|
+
return new DatabaseManager(httpClient, appId, apiKey);
|
|
693
272
|
}
|
|
694
|
-
|
|
695
|
-
// Types are available as imports, no need to re-export
|