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