hydrousdb 1.1.1 → 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/README.md +224 -133
- package/dist/analytics/index.d.mts +64 -57
- package/dist/analytics/index.d.ts +64 -57
- package/dist/analytics/index.js +57 -94
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +57 -94
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.d.ts +2 -2
- package/dist/{http-DbiqdKlw.d.mts → http-DTukpdAU.d.mts} +12 -8
- package/dist/{http-DbiqdKlw.d.ts → http-DTukpdAU.d.ts} +12 -8
- package/dist/index.d.mts +16 -10
- package/dist/index.d.ts +16 -10
- package/dist/index.js +126 -161
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -161
- package/dist/index.mjs.map +1 -1
- package/dist/records/index.d.mts +57 -44
- package/dist/records/index.d.ts +57 -44
- package/dist/records/index.js +67 -65
- package/dist/records/index.js.map +1 -1
- package/dist/records/index.mjs +67 -65
- package/dist/records/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504])
|
|
|
33
33
|
var HttpClient = class {
|
|
34
34
|
constructor(config) {
|
|
35
35
|
this.authKey = config.authKey;
|
|
36
|
-
this.
|
|
36
|
+
this.securityKey = config.securityKey;
|
|
37
37
|
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
38
38
|
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
39
39
|
this.retries = config.retries ?? DEFAULT_RETRIES;
|
|
@@ -168,31 +168,34 @@ var RecordsClient = class {
|
|
|
168
168
|
constructor(http) {
|
|
169
169
|
this.http = http;
|
|
170
170
|
}
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
/** Builds the base path for a given bucket: /api/:bucketKey/:securityKey */
|
|
172
|
+
path(bucketKey) {
|
|
173
|
+
return `/api/${bucketKey}/${this.http.securityKey}`;
|
|
173
174
|
}
|
|
174
175
|
// ── GET — single record ────────────────────────────────────────────────────
|
|
175
176
|
/**
|
|
176
177
|
* Fetch a single record by ID.
|
|
177
178
|
*
|
|
178
179
|
* @example
|
|
179
|
-
* const { data } = await db.records.get('rec_abc123');
|
|
180
|
-
* const { data, history } = await db.records.get('rec_abc123', { showHistory: true });
|
|
180
|
+
* const { data } = await db.records.get('rec_abc123', { bucketKey: 'users' });
|
|
181
|
+
* const { data, history } = await db.records.get('rec_abc123', { bucketKey: 'users', showHistory: true });
|
|
181
182
|
*/
|
|
182
183
|
async get(recordId, options) {
|
|
184
|
+
const { bucketKey, showHistory, ...rest } = options;
|
|
183
185
|
const params = { recordId };
|
|
184
|
-
if (
|
|
185
|
-
return this.http.get(this.path, params,
|
|
186
|
+
if (showHistory) params["showHistory"] = "true";
|
|
187
|
+
return this.http.get(this.path(bucketKey), params, rest);
|
|
186
188
|
}
|
|
187
189
|
// ── GET — historical snapshot ──────────────────────────────────────────────
|
|
188
190
|
/**
|
|
189
191
|
* Fetch a specific historical version (generation) of a record.
|
|
190
192
|
*
|
|
191
193
|
* @example
|
|
192
|
-
* const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000');
|
|
194
|
+
* const { data } = await db.records.getSnapshot('rec_abc123', '1700000000000000', { bucketKey: 'users' });
|
|
193
195
|
*/
|
|
194
|
-
async getSnapshot(recordId, generation,
|
|
195
|
-
|
|
196
|
+
async getSnapshot(recordId, generation, options) {
|
|
197
|
+
const { bucketKey, ...rest } = options;
|
|
198
|
+
return this.http.get(this.path(bucketKey), { recordId, generation }, rest);
|
|
196
199
|
}
|
|
197
200
|
// ── GET — collection query ─────────────────────────────────────────────────
|
|
198
201
|
/**
|
|
@@ -200,10 +203,11 @@ var RecordsClient = class {
|
|
|
200
203
|
*
|
|
201
204
|
* @example
|
|
202
205
|
* // Simple query
|
|
203
|
-
* const { data, meta } = await db.records.query({
|
|
206
|
+
* const { data, meta } = await db.records.query({ bucketKey: 'orders', limit: 50 });
|
|
204
207
|
*
|
|
205
208
|
* // Filtered query
|
|
206
209
|
* const { data } = await db.records.query({
|
|
210
|
+
* bucketKey: 'orders',
|
|
207
211
|
* filters: [{ field: 'status', op: '==', value: 'active' }],
|
|
208
212
|
* timeScope: '7d',
|
|
209
213
|
* });
|
|
@@ -211,52 +215,54 @@ var RecordsClient = class {
|
|
|
211
215
|
* // Paginated
|
|
212
216
|
* let cursor: string | null = null;
|
|
213
217
|
* do {
|
|
214
|
-
* const result = await db.records.query({ limit: 100, cursor: cursor ?? undefined });
|
|
218
|
+
* const result = await db.records.query({ bucketKey: 'orders', limit: 100, cursor: cursor ?? undefined });
|
|
215
219
|
* cursor = result.meta.nextCursor;
|
|
216
220
|
* } while (cursor);
|
|
217
221
|
*/
|
|
218
222
|
async query(options) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
const { bucketKey, ...rest } = options;
|
|
224
|
+
if (rest.filters) validateFilters(rest.filters);
|
|
225
|
+
const params = buildQueryParams(rest);
|
|
226
|
+
return this.http.get(this.path(bucketKey), params, rest);
|
|
222
227
|
}
|
|
223
228
|
// ── POST — insert ──────────────────────────────────────────────────────────
|
|
224
229
|
/**
|
|
225
|
-
* Insert a new record.
|
|
230
|
+
* Insert a new record into the specified bucket.
|
|
226
231
|
*
|
|
227
232
|
* @example
|
|
228
|
-
* const { data, meta } = await db.records.insert(
|
|
229
|
-
* values: { name: 'Alice', score: 99 },
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* });
|
|
233
|
+
* const { data, meta } = await db.records.insert(
|
|
234
|
+
* { values: { name: 'Alice', score: 99 }, queryableFields: ['name'] },
|
|
235
|
+
* { bucketKey: 'users' }
|
|
236
|
+
* );
|
|
233
237
|
*/
|
|
234
|
-
async insert(payload,
|
|
235
|
-
|
|
238
|
+
async insert(payload, options) {
|
|
239
|
+
const { bucketKey, ...rest } = options;
|
|
240
|
+
return this.http.post(this.path(bucketKey), payload, rest);
|
|
236
241
|
}
|
|
237
242
|
// ── PATCH — update ─────────────────────────────────────────────────────────
|
|
238
243
|
/**
|
|
239
244
|
* Update an existing record.
|
|
240
245
|
*
|
|
241
246
|
* @example
|
|
242
|
-
* await db.records.update(
|
|
243
|
-
* recordId: 'rec_abc123',
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
* });
|
|
247
|
+
* await db.records.update(
|
|
248
|
+
* { recordId: 'rec_abc123', values: { score: 100 }, track_record_history: true },
|
|
249
|
+
* { bucketKey: 'users' }
|
|
250
|
+
* );
|
|
247
251
|
*/
|
|
248
|
-
async update(payload,
|
|
249
|
-
|
|
252
|
+
async update(payload, options) {
|
|
253
|
+
const { bucketKey, ...rest } = options;
|
|
254
|
+
return this.http.patch(this.path(bucketKey), payload, rest);
|
|
250
255
|
}
|
|
251
256
|
// ── DELETE ─────────────────────────────────────────────────────────────────
|
|
252
257
|
/**
|
|
253
258
|
* Delete a record permanently.
|
|
254
259
|
*
|
|
255
260
|
* @example
|
|
256
|
-
* await db.records.delete('rec_abc123');
|
|
261
|
+
* await db.records.delete('rec_abc123', { bucketKey: 'users' });
|
|
257
262
|
*/
|
|
258
|
-
async delete(recordId,
|
|
259
|
-
|
|
263
|
+
async delete(recordId, options) {
|
|
264
|
+
const { bucketKey, ...rest } = options;
|
|
265
|
+
return this.http.delete(this.path(bucketKey), { recordId }, rest);
|
|
260
266
|
}
|
|
261
267
|
// ── HEAD — existence check ─────────────────────────────────────────────────
|
|
262
268
|
/**
|
|
@@ -264,11 +270,12 @@ var RecordsClient = class {
|
|
|
264
270
|
* Returns `null` if the record is not found.
|
|
265
271
|
*
|
|
266
272
|
* @example
|
|
267
|
-
* const info = await db.records.exists('rec_abc123');
|
|
273
|
+
* const info = await db.records.exists('rec_abc123', { bucketKey: 'users' });
|
|
268
274
|
* if (info?.exists) console.log('found at', info.updatedAt);
|
|
269
275
|
*/
|
|
270
|
-
async exists(recordId,
|
|
271
|
-
const
|
|
276
|
+
async exists(recordId, options) {
|
|
277
|
+
const { bucketKey, ...rest } = options;
|
|
278
|
+
const res = await this.http.head(this.path(bucketKey), { recordId }, rest);
|
|
272
279
|
if (res.status === 404) return null;
|
|
273
280
|
if (!res.ok) return null;
|
|
274
281
|
return {
|
|
@@ -284,33 +291,28 @@ var RecordsClient = class {
|
|
|
284
291
|
* Update up to 500 records in a single request.
|
|
285
292
|
*
|
|
286
293
|
* @example
|
|
287
|
-
* await db.records.batchUpdate(
|
|
288
|
-
* updates: [
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* ],
|
|
292
|
-
* });
|
|
294
|
+
* await db.records.batchUpdate(
|
|
295
|
+
* { updates: [{ recordId: 'rec_1', values: { status: 'archived' } }] },
|
|
296
|
+
* { bucketKey: 'orders' }
|
|
297
|
+
* );
|
|
293
298
|
*/
|
|
294
|
-
async batchUpdate(payload,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
payload,
|
|
298
|
-
opts
|
|
299
|
-
);
|
|
299
|
+
async batchUpdate(payload, options) {
|
|
300
|
+
const { bucketKey, ...rest } = options;
|
|
301
|
+
return this.http.post(`${this.path(bucketKey)}/batch/update`, payload, rest);
|
|
300
302
|
}
|
|
301
303
|
// ── Batch — delete ─────────────────────────────────────────────────────────
|
|
302
304
|
/**
|
|
303
305
|
* Delete up to 500 records in a single request.
|
|
304
306
|
*
|
|
305
307
|
* @example
|
|
306
|
-
* await db.records.batchDelete(
|
|
308
|
+
* await db.records.batchDelete(
|
|
309
|
+
* { recordIds: ['rec_1', 'rec_2'] },
|
|
310
|
+
* { bucketKey: 'orders' }
|
|
311
|
+
* );
|
|
307
312
|
*/
|
|
308
|
-
async batchDelete(payload,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
payload,
|
|
312
|
-
opts
|
|
313
|
-
);
|
|
313
|
+
async batchDelete(payload, options) {
|
|
314
|
+
const { bucketKey, ...rest } = options;
|
|
315
|
+
return this.http.post(`${this.path(bucketKey)}/batch/delete`, payload, rest);
|
|
314
316
|
}
|
|
315
317
|
// ── Batch — insert ─────────────────────────────────────────────────────────
|
|
316
318
|
/**
|
|
@@ -318,17 +320,14 @@ var RecordsClient = class {
|
|
|
318
320
|
* Returns HTTP 207 (multi-status) — check `meta.failed` for partial failures.
|
|
319
321
|
*
|
|
320
322
|
* @example
|
|
321
|
-
* const result = await db.records.batchInsert(
|
|
322
|
-
* records: [{ name: 'Alice' }, { name: 'Bob' }],
|
|
323
|
-
*
|
|
324
|
-
*
|
|
323
|
+
* const result = await db.records.batchInsert(
|
|
324
|
+
* { records: [{ name: 'Alice' }, { name: 'Bob' }], queryableFields: ['name'] },
|
|
325
|
+
* { bucketKey: 'users' }
|
|
326
|
+
* );
|
|
325
327
|
*/
|
|
326
|
-
async batchInsert(payload,
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
payload,
|
|
330
|
-
opts
|
|
331
|
-
);
|
|
328
|
+
async batchInsert(payload, options) {
|
|
329
|
+
const { bucketKey, ...rest } = options;
|
|
330
|
+
return this.http.post(`${this.path(bucketKey)}/batch/insert`, payload, rest);
|
|
332
331
|
}
|
|
333
332
|
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
334
333
|
/**
|
|
@@ -337,6 +336,7 @@ var RecordsClient = class {
|
|
|
337
336
|
*
|
|
338
337
|
* @example
|
|
339
338
|
* const allRecords = await db.records.queryAll({
|
|
339
|
+
* bucketKey: 'orders',
|
|
340
340
|
* filters: [{ field: 'type', op: '==', value: 'invoice' }],
|
|
341
341
|
* });
|
|
342
342
|
*/
|
|
@@ -344,7 +344,9 @@ var RecordsClient = class {
|
|
|
344
344
|
const all = [];
|
|
345
345
|
let cursor = null;
|
|
346
346
|
do {
|
|
347
|
-
const result = await this.query(
|
|
347
|
+
const result = await this.query(
|
|
348
|
+
cursor ? { ...options, cursor } : { ...options }
|
|
349
|
+
);
|
|
348
350
|
all.push(...result.data);
|
|
349
351
|
cursor = result.meta.nextCursor ?? null;
|
|
350
352
|
} while (cursor);
|
|
@@ -572,214 +574,177 @@ var AnalyticsClient = class {
|
|
|
572
574
|
constructor(http) {
|
|
573
575
|
this.http = http;
|
|
574
576
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
+
/** Builds the path for a given bucket: /api/analytics/:bucketKey/:securityKey */
|
|
578
|
+
path(bucketKey) {
|
|
579
|
+
return `/api/analytics/${bucketKey}/${this.http.securityKey}`;
|
|
577
580
|
}
|
|
578
581
|
// ── Raw query ─────────────────────────────────────────────────────────────
|
|
579
582
|
/**
|
|
580
583
|
* Run any analytics query with full control over the payload.
|
|
581
584
|
* Prefer the typed convenience methods below for everyday use.
|
|
582
585
|
*/
|
|
583
|
-
async query(payload,
|
|
584
|
-
|
|
586
|
+
async query(payload, options) {
|
|
587
|
+
const { bucketKey, ...rest } = options;
|
|
588
|
+
return this.http.post(this.path(bucketKey), payload, rest);
|
|
585
589
|
}
|
|
586
590
|
// ── count ──────────────────────────────────────────────────────────────────
|
|
587
591
|
/**
|
|
588
592
|
* Total record count, optionally scoped to a date range.
|
|
589
|
-
* Server `queryType`: **"count"**
|
|
590
593
|
*
|
|
591
594
|
* @example
|
|
592
|
-
* const { data } = await db.analytics.count();
|
|
593
|
-
* console.log(data.count);
|
|
595
|
+
* const { data } = await db.analytics.count({ bucketKey: 'orders' });
|
|
596
|
+
* console.log(data.count);
|
|
594
597
|
*
|
|
595
|
-
* // With date range
|
|
596
598
|
* const { data } = await db.analytics.count({
|
|
597
|
-
*
|
|
599
|
+
* bucketKey: 'orders',
|
|
600
|
+
* dateRange: { startDate: '2025-01-01', endDate: '2025-12-31' },
|
|
598
601
|
* });
|
|
599
602
|
*/
|
|
600
603
|
async count(options) {
|
|
601
|
-
|
|
604
|
+
const { dateRange, ...rest } = options;
|
|
605
|
+
return this.query({ queryType: "count", dateRange }, rest);
|
|
602
606
|
}
|
|
603
607
|
// ── distribution ───────────────────────────────────────────────────────────
|
|
604
608
|
/**
|
|
605
609
|
* Value distribution (histogram) for a field.
|
|
606
|
-
* Server `queryType`: **"distribution"**
|
|
607
610
|
*
|
|
608
611
|
* @example
|
|
609
|
-
* const { data } = await db.analytics.distribution('status');
|
|
612
|
+
* const { data } = await db.analytics.distribution('status', { bucketKey: 'orders' });
|
|
610
613
|
* // [{ value: 'active', count: 80 }, { value: 'archived', count: 20 }]
|
|
611
614
|
*/
|
|
612
615
|
async distribution(field, options) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
field,
|
|
616
|
-
limit: options?.limit,
|
|
617
|
-
order: options?.order,
|
|
618
|
-
dateRange: options?.dateRange
|
|
619
|
-
}, options);
|
|
616
|
+
const { limit, order, dateRange, ...rest } = options;
|
|
617
|
+
return this.query({ queryType: "distribution", field, limit, order, dateRange }, rest);
|
|
620
618
|
}
|
|
621
619
|
// ── sum ────────────────────────────────────────────────────────────────────
|
|
622
620
|
/**
|
|
623
621
|
* Sum a numeric field, with optional group-by.
|
|
624
|
-
* Server `queryType`: **"sum"**
|
|
625
622
|
*
|
|
626
623
|
* @example
|
|
627
|
-
* const { data } = await db.analytics.sum('revenue', { groupBy: 'region' });
|
|
624
|
+
* const { data } = await db.analytics.sum('revenue', { bucketKey: 'orders', groupBy: 'region' });
|
|
628
625
|
*/
|
|
629
626
|
async sum(field, options) {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
field,
|
|
633
|
-
groupBy: options?.groupBy,
|
|
634
|
-
limit: options?.limit,
|
|
635
|
-
dateRange: options?.dateRange
|
|
636
|
-
}, options);
|
|
627
|
+
const { groupBy, limit, dateRange, ...rest } = options;
|
|
628
|
+
return this.query({ queryType: "sum", field, groupBy, limit, dateRange }, rest);
|
|
637
629
|
}
|
|
638
630
|
// ── timeSeries ─────────────────────────────────────────────────────────────
|
|
639
631
|
/**
|
|
640
632
|
* Record count grouped over time.
|
|
641
|
-
* Server `queryType`: **"timeSeries"**
|
|
642
633
|
*
|
|
643
634
|
* @example
|
|
644
|
-
* const { data } = await db.analytics.timeSeries({ granularity: 'day' });
|
|
635
|
+
* const { data } = await db.analytics.timeSeries({ bucketKey: 'orders', granularity: 'day' });
|
|
645
636
|
* // [{ date: '2025-01-01', count: 42 }, ...]
|
|
646
637
|
*/
|
|
647
638
|
async timeSeries(options) {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
granularity: options?.granularity,
|
|
651
|
-
dateRange: options?.dateRange
|
|
652
|
-
}, options);
|
|
639
|
+
const { granularity, dateRange, ...rest } = options;
|
|
640
|
+
return this.query({ queryType: "timeSeries", granularity, dateRange }, rest);
|
|
653
641
|
}
|
|
654
642
|
// ── fieldTimeSeries ────────────────────────────────────────────────────────
|
|
655
643
|
/**
|
|
656
644
|
* Aggregate a numeric field over time.
|
|
657
|
-
* Server `queryType`: **"fieldTimeSeries"**
|
|
658
645
|
*
|
|
659
646
|
* @example
|
|
660
647
|
* const { data } = await db.analytics.fieldTimeSeries('revenue', {
|
|
648
|
+
* bucketKey: 'orders',
|
|
661
649
|
* granularity: 'month',
|
|
662
650
|
* aggregation: 'sum',
|
|
663
651
|
* });
|
|
664
652
|
*/
|
|
665
653
|
async fieldTimeSeries(field, options) {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
field,
|
|
669
|
-
aggregation: options?.aggregation,
|
|
670
|
-
granularity: options?.granularity,
|
|
671
|
-
dateRange: options?.dateRange
|
|
672
|
-
}, options);
|
|
654
|
+
const { aggregation, granularity, dateRange, ...rest } = options;
|
|
655
|
+
return this.query({ queryType: "fieldTimeSeries", field, aggregation, granularity, dateRange }, rest);
|
|
673
656
|
}
|
|
674
657
|
// ── topN ───────────────────────────────────────────────────────────────────
|
|
675
658
|
/**
|
|
676
659
|
* Top N most frequent values for a field.
|
|
677
|
-
* Server `queryType`: **"topN"**
|
|
678
660
|
*
|
|
679
661
|
* @example
|
|
680
|
-
* const { data } = await db.analytics.topN('country', 5);
|
|
662
|
+
* const { data } = await db.analytics.topN('country', 5, { bucketKey: 'users' });
|
|
681
663
|
* // [{ label: 'US', value: 'US', count: 500 }, ...]
|
|
682
664
|
*/
|
|
683
665
|
async topN(field, n = 10, options) {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
field,
|
|
687
|
-
n,
|
|
688
|
-
labelField: options?.labelField,
|
|
689
|
-
order: options?.order,
|
|
690
|
-
dateRange: options?.dateRange
|
|
691
|
-
}, options);
|
|
666
|
+
const { labelField, order, dateRange, ...rest } = options;
|
|
667
|
+
return this.query({ queryType: "topN", field, n, labelField, order, dateRange }, rest);
|
|
692
668
|
}
|
|
693
669
|
// ── stats ──────────────────────────────────────────────────────────────────
|
|
694
670
|
/**
|
|
695
671
|
* Statistical summary for a numeric field: min, max, avg, stddev, p50, p90, p99.
|
|
696
|
-
* Server `queryType`: **"stats"**
|
|
697
672
|
*
|
|
698
673
|
* @example
|
|
699
|
-
* const { data } = await db.analytics.stats('score');
|
|
674
|
+
* const { data } = await db.analytics.stats('score', { bucketKey: 'users' });
|
|
700
675
|
* console.log(data.avg, data.p99);
|
|
701
676
|
*/
|
|
702
677
|
async stats(field, options) {
|
|
703
|
-
|
|
678
|
+
const { dateRange, ...rest } = options;
|
|
679
|
+
return this.query({ queryType: "stats", field, dateRange }, rest);
|
|
704
680
|
}
|
|
705
681
|
// ── records ────────────────────────────────────────────────────────────────
|
|
706
682
|
/**
|
|
707
683
|
* Filtered, paginated raw records with optional field projection.
|
|
708
684
|
* Supports filter ops: == != > < >= <= CONTAINS
|
|
709
|
-
* Server `queryType`: **"records"**
|
|
710
685
|
*
|
|
711
686
|
* @example
|
|
712
687
|
* const { data } = await db.analytics.records({
|
|
688
|
+
* bucketKey: 'users',
|
|
713
689
|
* filters: [{ field: 'role', op: '==', value: 'admin' }],
|
|
714
690
|
* selectFields: ['email', 'createdAt'],
|
|
715
691
|
* limit: 25,
|
|
716
692
|
* });
|
|
717
|
-
* console.log(data.data, data.hasMore);
|
|
718
693
|
*/
|
|
719
694
|
async records(options) {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
offset: options?.offset,
|
|
726
|
-
orderBy: options?.orderBy,
|
|
727
|
-
order: options?.order,
|
|
728
|
-
dateRange: options?.dateRange
|
|
729
|
-
}, options);
|
|
695
|
+
const { filters, selectFields, limit, offset, orderBy, order, dateRange, ...rest } = options;
|
|
696
|
+
return this.query(
|
|
697
|
+
{ queryType: "records", filters, selectFields, limit, offset, orderBy, order, dateRange },
|
|
698
|
+
rest
|
|
699
|
+
);
|
|
730
700
|
}
|
|
731
701
|
// ── multiMetric ────────────────────────────────────────────────────────────
|
|
732
702
|
/**
|
|
733
|
-
* Multiple aggregations in a single
|
|
734
|
-
* Server `queryType`: **"multiMetric"**
|
|
703
|
+
* Multiple aggregations in a single call — ideal for dashboard stat cards.
|
|
735
704
|
*
|
|
736
705
|
* @example
|
|
737
|
-
* const { data } = await db.analytics.multiMetric(
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
741
|
-
*
|
|
742
|
-
*
|
|
706
|
+
* const { data } = await db.analytics.multiMetric(
|
|
707
|
+
* [
|
|
708
|
+
* { name: 'totalRevenue', field: 'amount', aggregation: 'sum' },
|
|
709
|
+
* { name: 'avgScore', field: 'score', aggregation: 'avg' },
|
|
710
|
+
* ],
|
|
711
|
+
* { bucketKey: 'orders' }
|
|
712
|
+
* );
|
|
713
|
+
* console.log(data.totalRevenue, data.avgScore);
|
|
743
714
|
*/
|
|
744
715
|
async multiMetric(metrics, options) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
metrics,
|
|
748
|
-
dateRange: options?.dateRange
|
|
749
|
-
}, options);
|
|
716
|
+
const { dateRange, ...rest } = options;
|
|
717
|
+
return this.query({ queryType: "multiMetric", metrics, dateRange }, rest);
|
|
750
718
|
}
|
|
751
719
|
// ── storageStats ───────────────────────────────────────────────────────────
|
|
752
720
|
/**
|
|
753
|
-
* Storage statistics for
|
|
754
|
-
* Server `queryType`: **"storageStats"**
|
|
721
|
+
* Storage statistics for a bucket — total records, bytes, avg/min/max size.
|
|
755
722
|
*
|
|
756
723
|
* @example
|
|
757
|
-
* const { data } = await db.analytics.storageStats();
|
|
724
|
+
* const { data } = await db.analytics.storageStats({ bucketKey: 'orders' });
|
|
758
725
|
* console.log(data.totalRecords, data.totalBytes);
|
|
759
726
|
*/
|
|
760
727
|
async storageStats(options) {
|
|
761
|
-
|
|
728
|
+
const { dateRange, ...rest } = options;
|
|
729
|
+
return this.query({ queryType: "storageStats", dateRange }, rest);
|
|
762
730
|
}
|
|
763
731
|
// ── crossBucket ────────────────────────────────────────────────────────────
|
|
764
732
|
/**
|
|
765
733
|
* Compare a metric across multiple buckets in one query.
|
|
766
|
-
*
|
|
734
|
+
* Note: `bucketKey` here is used only for auth — the actual buckets
|
|
735
|
+
* compared are specified via `bucketKeys`.
|
|
767
736
|
*
|
|
768
737
|
* @example
|
|
769
738
|
* const { data } = await db.analytics.crossBucket({
|
|
770
|
-
*
|
|
739
|
+
* bucketKey: 'sales-2025', // auth bucket
|
|
740
|
+
* bucketKeys: ['sales-2024', 'sales-2025'],
|
|
771
741
|
* field: 'amount',
|
|
772
742
|
* aggregation: 'sum',
|
|
773
743
|
* });
|
|
774
744
|
*/
|
|
775
745
|
async crossBucket(options) {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
bucketKeys: options.bucketKeys,
|
|
779
|
-
field: options.field,
|
|
780
|
-
aggregation: options.aggregation,
|
|
781
|
-
dateRange: options.dateRange
|
|
782
|
-
}, options);
|
|
746
|
+
const { bucketKeys, field, aggregation, dateRange, ...rest } = options;
|
|
747
|
+
return this.query({ queryType: "crossBucket", bucketKeys, field, aggregation, dateRange }, rest);
|
|
783
748
|
}
|
|
784
749
|
};
|
|
785
750
|
|
|
@@ -787,7 +752,7 @@ var AnalyticsClient = class {
|
|
|
787
752
|
var HydrousClient = class {
|
|
788
753
|
constructor(config) {
|
|
789
754
|
if (!config.authKey) throw new Error("[hydrousdb] authKey is required");
|
|
790
|
-
if (!config.
|
|
755
|
+
if (!config.securityKey) throw new Error("[hydrousdb] securityKey is required");
|
|
791
756
|
this.http = new HttpClient(config);
|
|
792
757
|
this.records = new RecordsClient(this.http);
|
|
793
758
|
this.auth = new AuthClient(this.http);
|