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/analytics/index.js
CHANGED
|
@@ -5,214 +5,177 @@ var AnalyticsClient = class {
|
|
|
5
5
|
constructor(http) {
|
|
6
6
|
this.http = http;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/** Builds the path for a given bucket: /api/analytics/:bucketKey/:securityKey */
|
|
9
|
+
path(bucketKey) {
|
|
10
|
+
return `/api/analytics/${bucketKey}/${this.http.securityKey}`;
|
|
10
11
|
}
|
|
11
12
|
// ── Raw query ─────────────────────────────────────────────────────────────
|
|
12
13
|
/**
|
|
13
14
|
* Run any analytics query with full control over the payload.
|
|
14
15
|
* Prefer the typed convenience methods below for everyday use.
|
|
15
16
|
*/
|
|
16
|
-
async query(payload,
|
|
17
|
-
|
|
17
|
+
async query(payload, options) {
|
|
18
|
+
const { bucketKey, ...rest } = options;
|
|
19
|
+
return this.http.post(this.path(bucketKey), payload, rest);
|
|
18
20
|
}
|
|
19
21
|
// ── count ──────────────────────────────────────────────────────────────────
|
|
20
22
|
/**
|
|
21
23
|
* Total record count, optionally scoped to a date range.
|
|
22
|
-
* Server `queryType`: **"count"**
|
|
23
24
|
*
|
|
24
25
|
* @example
|
|
25
|
-
* const { data } = await db.analytics.count();
|
|
26
|
-
* console.log(data.count);
|
|
26
|
+
* const { data } = await db.analytics.count({ bucketKey: 'orders' });
|
|
27
|
+
* console.log(data.count);
|
|
27
28
|
*
|
|
28
|
-
* // With date range
|
|
29
29
|
* const { data } = await db.analytics.count({
|
|
30
|
-
*
|
|
30
|
+
* bucketKey: 'orders',
|
|
31
|
+
* dateRange: { startDate: '2025-01-01', endDate: '2025-12-31' },
|
|
31
32
|
* });
|
|
32
33
|
*/
|
|
33
34
|
async count(options) {
|
|
34
|
-
|
|
35
|
+
const { dateRange, ...rest } = options;
|
|
36
|
+
return this.query({ queryType: "count", dateRange }, rest);
|
|
35
37
|
}
|
|
36
38
|
// ── distribution ───────────────────────────────────────────────────────────
|
|
37
39
|
/**
|
|
38
40
|
* Value distribution (histogram) for a field.
|
|
39
|
-
* Server `queryType`: **"distribution"**
|
|
40
41
|
*
|
|
41
42
|
* @example
|
|
42
|
-
* const { data } = await db.analytics.distribution('status');
|
|
43
|
+
* const { data } = await db.analytics.distribution('status', { bucketKey: 'orders' });
|
|
43
44
|
* // [{ value: 'active', count: 80 }, { value: 'archived', count: 20 }]
|
|
44
45
|
*/
|
|
45
46
|
async distribution(field, options) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
field,
|
|
49
|
-
limit: options?.limit,
|
|
50
|
-
order: options?.order,
|
|
51
|
-
dateRange: options?.dateRange
|
|
52
|
-
}, options);
|
|
47
|
+
const { limit, order, dateRange, ...rest } = options;
|
|
48
|
+
return this.query({ queryType: "distribution", field, limit, order, dateRange }, rest);
|
|
53
49
|
}
|
|
54
50
|
// ── sum ────────────────────────────────────────────────────────────────────
|
|
55
51
|
/**
|
|
56
52
|
* Sum a numeric field, with optional group-by.
|
|
57
|
-
* Server `queryType`: **"sum"**
|
|
58
53
|
*
|
|
59
54
|
* @example
|
|
60
|
-
* const { data } = await db.analytics.sum('revenue', { groupBy: 'region' });
|
|
55
|
+
* const { data } = await db.analytics.sum('revenue', { bucketKey: 'orders', groupBy: 'region' });
|
|
61
56
|
*/
|
|
62
57
|
async sum(field, options) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
field,
|
|
66
|
-
groupBy: options?.groupBy,
|
|
67
|
-
limit: options?.limit,
|
|
68
|
-
dateRange: options?.dateRange
|
|
69
|
-
}, options);
|
|
58
|
+
const { groupBy, limit, dateRange, ...rest } = options;
|
|
59
|
+
return this.query({ queryType: "sum", field, groupBy, limit, dateRange }, rest);
|
|
70
60
|
}
|
|
71
61
|
// ── timeSeries ─────────────────────────────────────────────────────────────
|
|
72
62
|
/**
|
|
73
63
|
* Record count grouped over time.
|
|
74
|
-
* Server `queryType`: **"timeSeries"**
|
|
75
64
|
*
|
|
76
65
|
* @example
|
|
77
|
-
* const { data } = await db.analytics.timeSeries({ granularity: 'day' });
|
|
66
|
+
* const { data } = await db.analytics.timeSeries({ bucketKey: 'orders', granularity: 'day' });
|
|
78
67
|
* // [{ date: '2025-01-01', count: 42 }, ...]
|
|
79
68
|
*/
|
|
80
69
|
async timeSeries(options) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
granularity: options?.granularity,
|
|
84
|
-
dateRange: options?.dateRange
|
|
85
|
-
}, options);
|
|
70
|
+
const { granularity, dateRange, ...rest } = options;
|
|
71
|
+
return this.query({ queryType: "timeSeries", granularity, dateRange }, rest);
|
|
86
72
|
}
|
|
87
73
|
// ── fieldTimeSeries ────────────────────────────────────────────────────────
|
|
88
74
|
/**
|
|
89
75
|
* Aggregate a numeric field over time.
|
|
90
|
-
* Server `queryType`: **"fieldTimeSeries"**
|
|
91
76
|
*
|
|
92
77
|
* @example
|
|
93
78
|
* const { data } = await db.analytics.fieldTimeSeries('revenue', {
|
|
79
|
+
* bucketKey: 'orders',
|
|
94
80
|
* granularity: 'month',
|
|
95
81
|
* aggregation: 'sum',
|
|
96
82
|
* });
|
|
97
83
|
*/
|
|
98
84
|
async fieldTimeSeries(field, options) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
field,
|
|
102
|
-
aggregation: options?.aggregation,
|
|
103
|
-
granularity: options?.granularity,
|
|
104
|
-
dateRange: options?.dateRange
|
|
105
|
-
}, options);
|
|
85
|
+
const { aggregation, granularity, dateRange, ...rest } = options;
|
|
86
|
+
return this.query({ queryType: "fieldTimeSeries", field, aggregation, granularity, dateRange }, rest);
|
|
106
87
|
}
|
|
107
88
|
// ── topN ───────────────────────────────────────────────────────────────────
|
|
108
89
|
/**
|
|
109
90
|
* Top N most frequent values for a field.
|
|
110
|
-
* Server `queryType`: **"topN"**
|
|
111
91
|
*
|
|
112
92
|
* @example
|
|
113
|
-
* const { data } = await db.analytics.topN('country', 5);
|
|
93
|
+
* const { data } = await db.analytics.topN('country', 5, { bucketKey: 'users' });
|
|
114
94
|
* // [{ label: 'US', value: 'US', count: 500 }, ...]
|
|
115
95
|
*/
|
|
116
96
|
async topN(field, n = 10, options) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
field,
|
|
120
|
-
n,
|
|
121
|
-
labelField: options?.labelField,
|
|
122
|
-
order: options?.order,
|
|
123
|
-
dateRange: options?.dateRange
|
|
124
|
-
}, options);
|
|
97
|
+
const { labelField, order, dateRange, ...rest } = options;
|
|
98
|
+
return this.query({ queryType: "topN", field, n, labelField, order, dateRange }, rest);
|
|
125
99
|
}
|
|
126
100
|
// ── stats ──────────────────────────────────────────────────────────────────
|
|
127
101
|
/**
|
|
128
102
|
* Statistical summary for a numeric field: min, max, avg, stddev, p50, p90, p99.
|
|
129
|
-
* Server `queryType`: **"stats"**
|
|
130
103
|
*
|
|
131
104
|
* @example
|
|
132
|
-
* const { data } = await db.analytics.stats('score');
|
|
105
|
+
* const { data } = await db.analytics.stats('score', { bucketKey: 'users' });
|
|
133
106
|
* console.log(data.avg, data.p99);
|
|
134
107
|
*/
|
|
135
108
|
async stats(field, options) {
|
|
136
|
-
|
|
109
|
+
const { dateRange, ...rest } = options;
|
|
110
|
+
return this.query({ queryType: "stats", field, dateRange }, rest);
|
|
137
111
|
}
|
|
138
112
|
// ── records ────────────────────────────────────────────────────────────────
|
|
139
113
|
/**
|
|
140
114
|
* Filtered, paginated raw records with optional field projection.
|
|
141
115
|
* Supports filter ops: == != > < >= <= CONTAINS
|
|
142
|
-
* Server `queryType`: **"records"**
|
|
143
116
|
*
|
|
144
117
|
* @example
|
|
145
118
|
* const { data } = await db.analytics.records({
|
|
119
|
+
* bucketKey: 'users',
|
|
146
120
|
* filters: [{ field: 'role', op: '==', value: 'admin' }],
|
|
147
121
|
* selectFields: ['email', 'createdAt'],
|
|
148
122
|
* limit: 25,
|
|
149
123
|
* });
|
|
150
|
-
* console.log(data.data, data.hasMore);
|
|
151
124
|
*/
|
|
152
125
|
async records(options) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
offset: options?.offset,
|
|
159
|
-
orderBy: options?.orderBy,
|
|
160
|
-
order: options?.order,
|
|
161
|
-
dateRange: options?.dateRange
|
|
162
|
-
}, options);
|
|
126
|
+
const { filters, selectFields, limit, offset, orderBy, order, dateRange, ...rest } = options;
|
|
127
|
+
return this.query(
|
|
128
|
+
{ queryType: "records", filters, selectFields, limit, offset, orderBy, order, dateRange },
|
|
129
|
+
rest
|
|
130
|
+
);
|
|
163
131
|
}
|
|
164
132
|
// ── multiMetric ────────────────────────────────────────────────────────────
|
|
165
133
|
/**
|
|
166
|
-
* Multiple aggregations in a single
|
|
167
|
-
* Server `queryType`: **"multiMetric"**
|
|
134
|
+
* Multiple aggregations in a single call — ideal for dashboard stat cards.
|
|
168
135
|
*
|
|
169
136
|
* @example
|
|
170
|
-
* const { data } = await db.analytics.multiMetric(
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
137
|
+
* const { data } = await db.analytics.multiMetric(
|
|
138
|
+
* [
|
|
139
|
+
* { name: 'totalRevenue', field: 'amount', aggregation: 'sum' },
|
|
140
|
+
* { name: 'avgScore', field: 'score', aggregation: 'avg' },
|
|
141
|
+
* ],
|
|
142
|
+
* { bucketKey: 'orders' }
|
|
143
|
+
* );
|
|
144
|
+
* console.log(data.totalRevenue, data.avgScore);
|
|
176
145
|
*/
|
|
177
146
|
async multiMetric(metrics, options) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
metrics,
|
|
181
|
-
dateRange: options?.dateRange
|
|
182
|
-
}, options);
|
|
147
|
+
const { dateRange, ...rest } = options;
|
|
148
|
+
return this.query({ queryType: "multiMetric", metrics, dateRange }, rest);
|
|
183
149
|
}
|
|
184
150
|
// ── storageStats ───────────────────────────────────────────────────────────
|
|
185
151
|
/**
|
|
186
|
-
* Storage statistics for
|
|
187
|
-
* Server `queryType`: **"storageStats"**
|
|
152
|
+
* Storage statistics for a bucket — total records, bytes, avg/min/max size.
|
|
188
153
|
*
|
|
189
154
|
* @example
|
|
190
|
-
* const { data } = await db.analytics.storageStats();
|
|
155
|
+
* const { data } = await db.analytics.storageStats({ bucketKey: 'orders' });
|
|
191
156
|
* console.log(data.totalRecords, data.totalBytes);
|
|
192
157
|
*/
|
|
193
158
|
async storageStats(options) {
|
|
194
|
-
|
|
159
|
+
const { dateRange, ...rest } = options;
|
|
160
|
+
return this.query({ queryType: "storageStats", dateRange }, rest);
|
|
195
161
|
}
|
|
196
162
|
// ── crossBucket ────────────────────────────────────────────────────────────
|
|
197
163
|
/**
|
|
198
164
|
* Compare a metric across multiple buckets in one query.
|
|
199
|
-
*
|
|
165
|
+
* Note: `bucketKey` here is used only for auth — the actual buckets
|
|
166
|
+
* compared are specified via `bucketKeys`.
|
|
200
167
|
*
|
|
201
168
|
* @example
|
|
202
169
|
* const { data } = await db.analytics.crossBucket({
|
|
203
|
-
*
|
|
170
|
+
* bucketKey: 'sales-2025', // auth bucket
|
|
171
|
+
* bucketKeys: ['sales-2024', 'sales-2025'],
|
|
204
172
|
* field: 'amount',
|
|
205
173
|
* aggregation: 'sum',
|
|
206
174
|
* });
|
|
207
175
|
*/
|
|
208
176
|
async crossBucket(options) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
bucketKeys: options.bucketKeys,
|
|
212
|
-
field: options.field,
|
|
213
|
-
aggregation: options.aggregation,
|
|
214
|
-
dateRange: options.dateRange
|
|
215
|
-
}, options);
|
|
177
|
+
const { bucketKeys, field, aggregation, dateRange, ...rest } = options;
|
|
178
|
+
return this.query({ queryType: "crossBucket", bucketKeys, field, aggregation, dateRange }, rest);
|
|
216
179
|
}
|
|
217
180
|
};
|
|
218
181
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/analytics/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { HttpClient } from '../utils/http.js';\nimport type {\n AnalyticsQuery,\n AnalyticsResponse,\n DateRange,\n Filter,\n MultiMetricItem,\n RequestOptions,\n CountResult,\n DistributionItem,\n SumResult,\n TimeSeriesPoint,\n FieldTimeSeriesPoint,\n TopNItem,\n FieldStatsResult,\n FilteredRecordsResult,\n StorageStatsResult,\n} from '../types/index.js';\n\n// Typed wrapper so callers get proper return types\ntype AnalyticsResult<T> = Omit<AnalyticsResponse, 'data'> & { data: T; queryType: string; bucketKey: string | null; projectId: string };\n\nexport class AnalyticsClient {\n constructor(private readonly http: HttpClient) {}\n\n private get path() {\n // POST /api/analytics/:bucketKey/:key — both segments use bucketKey\n return `/api/analytics/${this.http.bucketKey}/${this.http.bucketKey}`;\n }\n\n // ── Raw query ─────────────────────────────────────────────────────────────\n\n /**\n * Run any analytics query with full control over the payload.\n * Prefer the typed convenience methods below for everyday use.\n */\n async query<T = unknown>(payload: AnalyticsQuery, opts?: RequestOptions): Promise<AnalyticsResult<T>> {\n return this.http.post<AnalyticsResult<T>>(this.path, payload, opts);\n }\n\n // ── count ──────────────────────────────────────────────────────────────────\n\n /**\n * Total record count, optionally scoped to a date range.\n * Server `queryType`: **\"count\"**\n *\n * @example\n * const { data } = await db.analytics.count();\n * console.log(data.count); // 1234\n *\n * // With date range\n * const { data } = await db.analytics.count({\n * dateRange: { startDate: '2025-01-01', endDate: '2025-12-31' }\n * });\n */\n async count(options?: { dateRange?: DateRange } & RequestOptions): Promise<AnalyticsResult<CountResult>> {\n return this.query<CountResult>({ queryType: 'count', dateRange: options?.dateRange }, options);\n }\n\n // ── distribution ───────────────────────────────────────────────────────────\n\n /**\n * Value distribution (histogram) for a field.\n * Server `queryType`: **\"distribution\"**\n *\n * @example\n * const { data } = await db.analytics.distribution('status');\n * // [{ value: 'active', count: 80 }, { value: 'archived', count: 20 }]\n */\n async distribution(\n field: string,\n options?: { limit?: number; order?: 'asc' | 'desc'; dateRange?: DateRange } & RequestOptions\n ): Promise<AnalyticsResult<DistributionItem[]>> {\n return this.query<DistributionItem[]>({\n queryType: 'distribution',\n field,\n limit: options?.limit,\n order: options?.order,\n dateRange: options?.dateRange,\n }, options);\n }\n\n // ── sum ────────────────────────────────────────────────────────────────────\n\n /**\n * Sum a numeric field, with optional group-by.\n * Server `queryType`: **\"sum\"**\n *\n * @example\n * const { data } = await db.analytics.sum('revenue', { groupBy: 'region' });\n */\n async sum(\n field: string,\n options?: { groupBy?: string; limit?: number; dateRange?: DateRange } & RequestOptions\n ): Promise<AnalyticsResult<SumResult>> {\n return this.query<SumResult>({\n queryType: 'sum',\n field,\n groupBy: options?.groupBy,\n limit: options?.limit,\n dateRange: options?.dateRange,\n }, options);\n }\n\n // ── timeSeries ─────────────────────────────────────────────────────────────\n\n /**\n * Record count grouped over time.\n * Server `queryType`: **\"timeSeries\"**\n *\n * @example\n * const { data } = await db.analytics.timeSeries({ granularity: 'day' });\n * // [{ date: '2025-01-01', count: 42 }, ...]\n */\n async timeSeries(options?: {\n granularity?: 'hour' | 'day' | 'week' | 'month';\n dateRange?: DateRange;\n } & RequestOptions): Promise<AnalyticsResult<TimeSeriesPoint[]>> {\n return this.query<TimeSeriesPoint[]>({\n queryType: 'timeSeries',\n granularity: options?.granularity,\n dateRange: options?.dateRange,\n }, options);\n }\n\n // ── fieldTimeSeries ────────────────────────────────────────────────────────\n\n /**\n * Aggregate a numeric field over time.\n * Server `queryType`: **\"fieldTimeSeries\"**\n *\n * @example\n * const { data } = await db.analytics.fieldTimeSeries('revenue', {\n * granularity: 'month',\n * aggregation: 'sum',\n * });\n */\n async fieldTimeSeries(\n field: string,\n options?: {\n aggregation?: 'sum' | 'avg' | 'min' | 'max' | 'count';\n granularity?: 'hour' | 'day' | 'week' | 'month';\n dateRange?: DateRange;\n } & RequestOptions\n ): Promise<AnalyticsResult<FieldTimeSeriesPoint[]>> {\n return this.query<FieldTimeSeriesPoint[]>({\n queryType: 'fieldTimeSeries',\n field,\n aggregation: options?.aggregation,\n granularity: options?.granularity,\n dateRange: options?.dateRange,\n }, options);\n }\n\n // ── topN ───────────────────────────────────────────────────────────────────\n\n /**\n * Top N most frequent values for a field.\n * Server `queryType`: **\"topN\"**\n *\n * @example\n * const { data } = await db.analytics.topN('country', 5);\n * // [{ label: 'US', value: 'US', count: 500 }, ...]\n */\n async topN(\n field: string,\n n = 10,\n options?: { labelField?: string; order?: 'asc' | 'desc'; dateRange?: DateRange } & RequestOptions\n ): Promise<AnalyticsResult<TopNItem[]>> {\n return this.query<TopNItem[]>({\n queryType: 'topN',\n field,\n n,\n labelField: options?.labelField,\n order: options?.order,\n dateRange: options?.dateRange,\n }, options);\n }\n\n // ── stats ──────────────────────────────────────────────────────────────────\n\n /**\n * Statistical summary for a numeric field: min, max, avg, stddev, p50, p90, p99.\n * Server `queryType`: **\"stats\"**\n *\n * @example\n * const { data } = await db.analytics.stats('score');\n * console.log(data.avg, data.p99);\n */\n async stats(\n field: string,\n options?: { dateRange?: DateRange } & RequestOptions\n ): Promise<AnalyticsResult<FieldStatsResult>> {\n return this.query<FieldStatsResult>({ queryType: 'stats', field, dateRange: options?.dateRange }, options);\n }\n\n // ── records ────────────────────────────────────────────────────────────────\n\n /**\n * Filtered, paginated raw records with optional field projection.\n * Supports filter ops: == != > < >= <= CONTAINS\n * Server `queryType`: **\"records\"**\n *\n * @example\n * const { data } = await db.analytics.records({\n * filters: [{ field: 'role', op: '==', value: 'admin' }],\n * selectFields: ['email', 'createdAt'],\n * limit: 25,\n * });\n * console.log(data.data, data.hasMore);\n */\n async records(options?: {\n filters?: Filter[];\n selectFields?: string[];\n limit?: number;\n offset?: number;\n orderBy?: string;\n order?: 'asc' | 'desc';\n dateRange?: DateRange;\n } & RequestOptions): Promise<AnalyticsResult<FilteredRecordsResult>> {\n return this.query<FilteredRecordsResult>({\n queryType: 'records',\n filters: options?.filters,\n selectFields: options?.selectFields,\n limit: options?.limit,\n offset: options?.offset,\n orderBy: options?.orderBy,\n order: options?.order,\n dateRange: options?.dateRange,\n }, options);\n }\n\n // ── multiMetric ────────────────────────────────────────────────────────────\n\n /**\n * Multiple aggregations in a single BigQuery call — ideal for dashboard stat cards.\n * Server `queryType`: **\"multiMetric\"**\n *\n * @example\n * const { data } = await db.analytics.multiMetric([\n * { name: 'totalRevenue', field: 'amount', aggregation: 'sum' },\n * { name: 'avgScore', field: 'score', aggregation: 'avg' },\n * { name: 'userCount', field: 'userId', aggregation: 'count' },\n * ]);\n * console.log(data.totalRevenue, data.avgScore, data.userCount);\n */\n async multiMetric(\n metrics: MultiMetricItem[],\n options?: { dateRange?: DateRange } & RequestOptions\n ): Promise<AnalyticsResult<Record<string, number>>> {\n return this.query<Record<string, number>>({\n queryType: 'multiMetric',\n metrics,\n dateRange: options?.dateRange,\n }, options);\n }\n\n // ── storageStats ───────────────────────────────────────────────────────────\n\n /**\n * Storage statistics for the bucket — total records, bytes, avg/min/max size.\n * Server `queryType`: **\"storageStats\"**\n *\n * @example\n * const { data } = await db.analytics.storageStats();\n * console.log(data.totalRecords, data.totalBytes);\n */\n async storageStats(\n options?: { dateRange?: DateRange } & RequestOptions\n ): Promise<AnalyticsResult<StorageStatsResult>> {\n return this.query<StorageStatsResult>({ queryType: 'storageStats', dateRange: options?.dateRange }, options);\n }\n\n // ── crossBucket ────────────────────────────────────────────────────────────\n\n /**\n * Compare a metric across multiple buckets in one query.\n * Server `queryType`: **\"crossBucket\"**\n *\n * @example\n * const { data } = await db.analytics.crossBucket({\n * bucketKeys: ['sales', 'refunds', 'trials'],\n * field: 'amount',\n * aggregation: 'sum',\n * });\n */\n async crossBucket(options: {\n bucketKeys: string[];\n field: string;\n aggregation?: 'sum' | 'avg' | 'min' | 'max' | 'count';\n dateRange?: DateRange;\n } & RequestOptions): Promise<AnalyticsResult<unknown>> {\n return this.query<unknown>({\n queryType: 'crossBucket',\n bucketKeys: options.bucketKeys,\n field: options.field,\n aggregation: options.aggregation,\n dateRange: options.dateRange,\n }, options);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/analytics/client.ts"],"names":[],"mappings":";;;AAuCO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,YAA6B,IAAA,EAAkB;AAAlB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAAmB;AAAA;AAAA,EAGxC,KAAK,SAAA,EAA2B;AACtC,IAAA,OAAO,CAAA,eAAA,EAAkB,SAAS,CAAA,CAAA,EAAI,IAAA,CAAK,KAAK,WAAW,CAAA,CAAA;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,KAAA,CACJ,OAAA,EACA,OAAA,EAC6B;AAC7B,IAAA,MAAM,EAAE,SAAA,EAAW,GAAG,IAAA,EAAK,GAAI,OAAA;AAC/B,IAAA,OAAO,IAAA,CAAK,KAAK,IAAA,CAAyB,IAAA,CAAK,KAAK,SAAS,CAAA,EAAG,SAAS,IAAI,CAAA;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,MACJ,OAAA,EACuC;AACvC,IAAA,MAAM,EAAE,SAAA,EAAW,GAAG,IAAA,EAAK,GAAI,OAAA;AAC/B,IAAA,OAAO,KAAK,KAAA,CAAmB,EAAE,WAAW,OAAA,EAAS,SAAA,IAAa,IAAI,CAAA;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,YAAA,CACJ,KAAA,EACA,OAAA,EAC8C;AAC9C,IAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAO,SAAA,EAAW,GAAG,MAAK,GAAI,OAAA;AAC7C,IAAA,OAAO,IAAA,CAAK,KAAA,CAA0B,EAAE,SAAA,EAAW,cAAA,EAAgB,OAAO,KAAA,EAAO,KAAA,EAAO,SAAA,EAAU,EAAG,IAAI,CAAA;AAAA,EAC3G;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,GAAA,CACJ,KAAA,EACA,OAAA,EACqC;AACrC,IAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAO,SAAA,EAAW,GAAG,MAAK,GAAI,OAAA;AAC/C,IAAA,OAAO,IAAA,CAAK,KAAA,CAAiB,EAAE,SAAA,EAAW,KAAA,EAAO,OAAO,OAAA,EAAS,KAAA,EAAO,SAAA,EAAU,EAAG,IAAI,CAAA;AAAA,EAC3F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,WACJ,OAAA,EAC6C;AAC7C,IAAA,MAAM,EAAE,WAAA,EAAa,SAAA,EAAW,GAAG,MAAK,GAAI,OAAA;AAC5C,IAAA,OAAO,IAAA,CAAK,MAAyB,EAAE,SAAA,EAAW,cAAc,WAAA,EAAa,SAAA,IAAa,IAAI,CAAA;AAAA,EAChG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,eAAA,CACJ,KAAA,EACA,OAAA,EAKkD;AAClD,IAAA,MAAM,EAAE,WAAA,EAAa,WAAA,EAAa,SAAA,EAAW,GAAG,MAAK,GAAI,OAAA;AACzD,IAAA,OAAO,IAAA,CAAK,KAAA,CAA8B,EAAE,SAAA,EAAW,iBAAA,EAAmB,OAAO,WAAA,EAAa,WAAA,EAAa,SAAA,EAAU,EAAG,IAAI,CAAA;AAAA,EAC9H;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,IAAA,CACJ,KAAA,EACA,CAAA,GAAI,IACJ,OAAA,EACsC;AACtC,IAAA,MAAM,EAAE,UAAA,EAAY,KAAA,EAAO,SAAA,EAAW,GAAG,MAAK,GAAI,OAAA;AAClD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAkB,EAAE,SAAA,EAAW,MAAA,EAAQ,KAAA,EAAO,CAAA,EAAG,UAAA,EAAY,KAAA,EAAO,SAAA,EAAU,EAAG,IAAI,CAAA;AAAA,EACnG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,KAAA,CACJ,KAAA,EACA,OAAA,EAC4C;AAC5C,IAAA,MAAM,EAAE,SAAA,EAAW,GAAG,IAAA,EAAK,GAAI,OAAA;AAC/B,IAAA,OAAO,IAAA,CAAK,MAAwB,EAAE,SAAA,EAAW,SAAS,KAAA,EAAO,SAAA,IAAa,IAAI,CAAA;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,QACJ,OAAA,EASiD;AACjD,IAAA,MAAM,EAAE,OAAA,EAAS,YAAA,EAAc,KAAA,EAAO,MAAA,EAAQ,SAAS,KAAA,EAAO,SAAA,EAAW,GAAG,IAAA,EAAK,GAAI,OAAA;AACrF,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,MACV,EAAE,WAAW,SAAA,EAAW,OAAA,EAAS,cAAc,KAAA,EAAO,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,SAAA,EAAU;AAAA,MACxF;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,WAAA,CACJ,OAAA,EACA,OAAA,EACkD;AAClD,IAAA,MAAM,EAAE,SAAA,EAAW,GAAG,IAAA,EAAK,GAAI,OAAA;AAC/B,IAAA,OAAO,IAAA,CAAK,MAA8B,EAAE,SAAA,EAAW,eAAe,OAAA,EAAS,SAAA,IAAa,IAAI,CAAA;AAAA,EAClG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,aACJ,OAAA,EAC8C;AAC9C,IAAA,MAAM,EAAE,SAAA,EAAW,GAAG,IAAA,EAAK,GAAI,OAAA;AAC/B,IAAA,OAAO,KAAK,KAAA,CAA0B,EAAE,WAAW,cAAA,EAAgB,SAAA,IAAa,IAAI,CAAA;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,YACJ,OAAA,EAMmC;AACnC,IAAA,MAAM,EAAE,UAAA,EAAY,KAAA,EAAO,aAAa,SAAA,EAAW,GAAG,MAAK,GAAI,OAAA;AAC/D,IAAA,OAAO,IAAA,CAAK,KAAA,CAAe,EAAE,SAAA,EAAW,aAAA,EAAe,YAAY,KAAA,EAAO,WAAA,EAAa,SAAA,EAAU,EAAG,IAAI,CAAA;AAAA,EAC1G;AACF","file":"index.js","sourcesContent":["import type { HttpClient } from '../utils/http.js';\nimport type {\n AnalyticsQuery,\n AnalyticsResponse,\n DateRange,\n Filter,\n MultiMetricItem,\n RequestOptions,\n CountResult,\n DistributionItem,\n SumResult,\n TimeSeriesPoint,\n FieldTimeSeriesPoint,\n TopNItem,\n FieldStatsResult,\n FilteredRecordsResult,\n StorageStatsResult,\n} from '../types/index.js';\n\n// Typed wrapper so callers get proper return types\ntype AnalyticsResult<T> = Omit<AnalyticsResponse, 'data'> & {\n data: T;\n queryType: string;\n bucketKey: string | null;\n projectId: string;\n};\n\n/** Options accepted by every analytics method */\nexport interface BucketOptions {\n /**\n * The bucket to run the analytics query against.\n * Required on all analytics calls.\n *\n * @example\n * const { data } = await db.analytics.count({ bucketKey: 'orders' });\n */\n bucketKey: string;\n}\n\nexport class AnalyticsClient {\n constructor(private readonly http: HttpClient) {}\n\n /** Builds the path for a given bucket: /api/analytics/:bucketKey/:securityKey */\n private path(bucketKey: string): string {\n return `/api/analytics/${bucketKey}/${this.http.securityKey}`;\n }\n\n // ── Raw query ─────────────────────────────────────────────────────────────\n\n /**\n * Run any analytics query with full control over the payload.\n * Prefer the typed convenience methods below for everyday use.\n */\n async query<T = unknown>(\n payload: AnalyticsQuery,\n options: BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<T>> {\n const { bucketKey, ...rest } = options;\n return this.http.post<AnalyticsResult<T>>(this.path(bucketKey), payload, rest);\n }\n\n // ── count ──────────────────────────────────────────────────────────────────\n\n /**\n * Total record count, optionally scoped to a date range.\n *\n * @example\n * const { data } = await db.analytics.count({ bucketKey: 'orders' });\n * console.log(data.count);\n *\n * const { data } = await db.analytics.count({\n * bucketKey: 'orders',\n * dateRange: { startDate: '2025-01-01', endDate: '2025-12-31' },\n * });\n */\n async count(\n options: { dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<CountResult>> {\n const { dateRange, ...rest } = options;\n return this.query<CountResult>({ queryType: 'count', dateRange }, rest);\n }\n\n // ── distribution ───────────────────────────────────────────────────────────\n\n /**\n * Value distribution (histogram) for a field.\n *\n * @example\n * const { data } = await db.analytics.distribution('status', { bucketKey: 'orders' });\n * // [{ value: 'active', count: 80 }, { value: 'archived', count: 20 }]\n */\n async distribution(\n field: string,\n options: { limit?: number; order?: 'asc' | 'desc'; dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<DistributionItem[]>> {\n const { limit, order, dateRange, ...rest } = options;\n return this.query<DistributionItem[]>({ queryType: 'distribution', field, limit, order, dateRange }, rest);\n }\n\n // ── sum ────────────────────────────────────────────────────────────────────\n\n /**\n * Sum a numeric field, with optional group-by.\n *\n * @example\n * const { data } = await db.analytics.sum('revenue', { bucketKey: 'orders', groupBy: 'region' });\n */\n async sum(\n field: string,\n options: { groupBy?: string; limit?: number; dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<SumResult>> {\n const { groupBy, limit, dateRange, ...rest } = options;\n return this.query<SumResult>({ queryType: 'sum', field, groupBy, limit, dateRange }, rest);\n }\n\n // ── timeSeries ─────────────────────────────────────────────────────────────\n\n /**\n * Record count grouped over time.\n *\n * @example\n * const { data } = await db.analytics.timeSeries({ bucketKey: 'orders', granularity: 'day' });\n * // [{ date: '2025-01-01', count: 42 }, ...]\n */\n async timeSeries(\n options: { granularity?: 'hour' | 'day' | 'week' | 'month'; dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<TimeSeriesPoint[]>> {\n const { granularity, dateRange, ...rest } = options;\n return this.query<TimeSeriesPoint[]>({ queryType: 'timeSeries', granularity, dateRange }, rest);\n }\n\n // ── fieldTimeSeries ────────────────────────────────────────────────────────\n\n /**\n * Aggregate a numeric field over time.\n *\n * @example\n * const { data } = await db.analytics.fieldTimeSeries('revenue', {\n * bucketKey: 'orders',\n * granularity: 'month',\n * aggregation: 'sum',\n * });\n */\n async fieldTimeSeries(\n field: string,\n options: {\n aggregation?: 'sum' | 'avg' | 'min' | 'max' | 'count';\n granularity?: 'hour' | 'day' | 'week' | 'month';\n dateRange?: DateRange;\n } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<FieldTimeSeriesPoint[]>> {\n const { aggregation, granularity, dateRange, ...rest } = options;\n return this.query<FieldTimeSeriesPoint[]>({ queryType: 'fieldTimeSeries', field, aggregation, granularity, dateRange }, rest);\n }\n\n // ── topN ───────────────────────────────────────────────────────────────────\n\n /**\n * Top N most frequent values for a field.\n *\n * @example\n * const { data } = await db.analytics.topN('country', 5, { bucketKey: 'users' });\n * // [{ label: 'US', value: 'US', count: 500 }, ...]\n */\n async topN(\n field: string,\n n = 10,\n options: { labelField?: string; order?: 'asc' | 'desc'; dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<TopNItem[]>> {\n const { labelField, order, dateRange, ...rest } = options;\n return this.query<TopNItem[]>({ queryType: 'topN', field, n, labelField, order, dateRange }, rest);\n }\n\n // ── stats ──────────────────────────────────────────────────────────────────\n\n /**\n * Statistical summary for a numeric field: min, max, avg, stddev, p50, p90, p99.\n *\n * @example\n * const { data } = await db.analytics.stats('score', { bucketKey: 'users' });\n * console.log(data.avg, data.p99);\n */\n async stats(\n field: string,\n options: { dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<FieldStatsResult>> {\n const { dateRange, ...rest } = options;\n return this.query<FieldStatsResult>({ queryType: 'stats', field, dateRange }, rest);\n }\n\n // ── records ────────────────────────────────────────────────────────────────\n\n /**\n * Filtered, paginated raw records with optional field projection.\n * Supports filter ops: == != > < >= <= CONTAINS\n *\n * @example\n * const { data } = await db.analytics.records({\n * bucketKey: 'users',\n * filters: [{ field: 'role', op: '==', value: 'admin' }],\n * selectFields: ['email', 'createdAt'],\n * limit: 25,\n * });\n */\n async records(\n options: {\n filters?: Filter[];\n selectFields?: string[];\n limit?: number;\n offset?: number;\n orderBy?: string;\n order?: 'asc' | 'desc';\n dateRange?: DateRange;\n } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<FilteredRecordsResult>> {\n const { filters, selectFields, limit, offset, orderBy, order, dateRange, ...rest } = options;\n return this.query<FilteredRecordsResult>(\n { queryType: 'records', filters, selectFields, limit, offset, orderBy, order, dateRange },\n rest\n );\n }\n\n // ── multiMetric ────────────────────────────────────────────────────────────\n\n /**\n * Multiple aggregations in a single call — ideal for dashboard stat cards.\n *\n * @example\n * const { data } = await db.analytics.multiMetric(\n * [\n * { name: 'totalRevenue', field: 'amount', aggregation: 'sum' },\n * { name: 'avgScore', field: 'score', aggregation: 'avg' },\n * ],\n * { bucketKey: 'orders' }\n * );\n * console.log(data.totalRevenue, data.avgScore);\n */\n async multiMetric(\n metrics: MultiMetricItem[],\n options: { dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<Record<string, number>>> {\n const { dateRange, ...rest } = options;\n return this.query<Record<string, number>>({ queryType: 'multiMetric', metrics, dateRange }, rest);\n }\n\n // ── storageStats ───────────────────────────────────────────────────────────\n\n /**\n * Storage statistics for a bucket — total records, bytes, avg/min/max size.\n *\n * @example\n * const { data } = await db.analytics.storageStats({ bucketKey: 'orders' });\n * console.log(data.totalRecords, data.totalBytes);\n */\n async storageStats(\n options: { dateRange?: DateRange } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<StorageStatsResult>> {\n const { dateRange, ...rest } = options;\n return this.query<StorageStatsResult>({ queryType: 'storageStats', dateRange }, rest);\n }\n\n // ── crossBucket ────────────────────────────────────────────────────────────\n\n /**\n * Compare a metric across multiple buckets in one query.\n * Note: `bucketKey` here is used only for auth — the actual buckets\n * compared are specified via `bucketKeys`.\n *\n * @example\n * const { data } = await db.analytics.crossBucket({\n * bucketKey: 'sales-2025', // auth bucket\n * bucketKeys: ['sales-2024', 'sales-2025'],\n * field: 'amount',\n * aggregation: 'sum',\n * });\n */\n async crossBucket(\n options: {\n bucketKeys: string[];\n field: string;\n aggregation?: 'sum' | 'avg' | 'min' | 'max' | 'count';\n dateRange?: DateRange;\n } & BucketOptions & RequestOptions\n ): Promise<AnalyticsResult<unknown>> {\n const { bucketKeys, field, aggregation, dateRange, ...rest } = options;\n return this.query<unknown>({ queryType: 'crossBucket', bucketKeys, field, aggregation, dateRange }, rest);\n }\n}\n"]}
|
package/dist/analytics/index.mjs
CHANGED
|
@@ -3,214 +3,177 @@ var AnalyticsClient = class {
|
|
|
3
3
|
constructor(http) {
|
|
4
4
|
this.http = http;
|
|
5
5
|
}
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/** Builds the path for a given bucket: /api/analytics/:bucketKey/:securityKey */
|
|
7
|
+
path(bucketKey) {
|
|
8
|
+
return `/api/analytics/${bucketKey}/${this.http.securityKey}`;
|
|
8
9
|
}
|
|
9
10
|
// ── Raw query ─────────────────────────────────────────────────────────────
|
|
10
11
|
/**
|
|
11
12
|
* Run any analytics query with full control over the payload.
|
|
12
13
|
* Prefer the typed convenience methods below for everyday use.
|
|
13
14
|
*/
|
|
14
|
-
async query(payload,
|
|
15
|
-
|
|
15
|
+
async query(payload, options) {
|
|
16
|
+
const { bucketKey, ...rest } = options;
|
|
17
|
+
return this.http.post(this.path(bucketKey), payload, rest);
|
|
16
18
|
}
|
|
17
19
|
// ── count ──────────────────────────────────────────────────────────────────
|
|
18
20
|
/**
|
|
19
21
|
* Total record count, optionally scoped to a date range.
|
|
20
|
-
* Server `queryType`: **"count"**
|
|
21
22
|
*
|
|
22
23
|
* @example
|
|
23
|
-
* const { data } = await db.analytics.count();
|
|
24
|
-
* console.log(data.count);
|
|
24
|
+
* const { data } = await db.analytics.count({ bucketKey: 'orders' });
|
|
25
|
+
* console.log(data.count);
|
|
25
26
|
*
|
|
26
|
-
* // With date range
|
|
27
27
|
* const { data } = await db.analytics.count({
|
|
28
|
-
*
|
|
28
|
+
* bucketKey: 'orders',
|
|
29
|
+
* dateRange: { startDate: '2025-01-01', endDate: '2025-12-31' },
|
|
29
30
|
* });
|
|
30
31
|
*/
|
|
31
32
|
async count(options) {
|
|
32
|
-
|
|
33
|
+
const { dateRange, ...rest } = options;
|
|
34
|
+
return this.query({ queryType: "count", dateRange }, rest);
|
|
33
35
|
}
|
|
34
36
|
// ── distribution ───────────────────────────────────────────────────────────
|
|
35
37
|
/**
|
|
36
38
|
* Value distribution (histogram) for a field.
|
|
37
|
-
* Server `queryType`: **"distribution"**
|
|
38
39
|
*
|
|
39
40
|
* @example
|
|
40
|
-
* const { data } = await db.analytics.distribution('status');
|
|
41
|
+
* const { data } = await db.analytics.distribution('status', { bucketKey: 'orders' });
|
|
41
42
|
* // [{ value: 'active', count: 80 }, { value: 'archived', count: 20 }]
|
|
42
43
|
*/
|
|
43
44
|
async distribution(field, options) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
field,
|
|
47
|
-
limit: options?.limit,
|
|
48
|
-
order: options?.order,
|
|
49
|
-
dateRange: options?.dateRange
|
|
50
|
-
}, options);
|
|
45
|
+
const { limit, order, dateRange, ...rest } = options;
|
|
46
|
+
return this.query({ queryType: "distribution", field, limit, order, dateRange }, rest);
|
|
51
47
|
}
|
|
52
48
|
// ── sum ────────────────────────────────────────────────────────────────────
|
|
53
49
|
/**
|
|
54
50
|
* Sum a numeric field, with optional group-by.
|
|
55
|
-
* Server `queryType`: **"sum"**
|
|
56
51
|
*
|
|
57
52
|
* @example
|
|
58
|
-
* const { data } = await db.analytics.sum('revenue', { groupBy: 'region' });
|
|
53
|
+
* const { data } = await db.analytics.sum('revenue', { bucketKey: 'orders', groupBy: 'region' });
|
|
59
54
|
*/
|
|
60
55
|
async sum(field, options) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
field,
|
|
64
|
-
groupBy: options?.groupBy,
|
|
65
|
-
limit: options?.limit,
|
|
66
|
-
dateRange: options?.dateRange
|
|
67
|
-
}, options);
|
|
56
|
+
const { groupBy, limit, dateRange, ...rest } = options;
|
|
57
|
+
return this.query({ queryType: "sum", field, groupBy, limit, dateRange }, rest);
|
|
68
58
|
}
|
|
69
59
|
// ── timeSeries ─────────────────────────────────────────────────────────────
|
|
70
60
|
/**
|
|
71
61
|
* Record count grouped over time.
|
|
72
|
-
* Server `queryType`: **"timeSeries"**
|
|
73
62
|
*
|
|
74
63
|
* @example
|
|
75
|
-
* const { data } = await db.analytics.timeSeries({ granularity: 'day' });
|
|
64
|
+
* const { data } = await db.analytics.timeSeries({ bucketKey: 'orders', granularity: 'day' });
|
|
76
65
|
* // [{ date: '2025-01-01', count: 42 }, ...]
|
|
77
66
|
*/
|
|
78
67
|
async timeSeries(options) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
granularity: options?.granularity,
|
|
82
|
-
dateRange: options?.dateRange
|
|
83
|
-
}, options);
|
|
68
|
+
const { granularity, dateRange, ...rest } = options;
|
|
69
|
+
return this.query({ queryType: "timeSeries", granularity, dateRange }, rest);
|
|
84
70
|
}
|
|
85
71
|
// ── fieldTimeSeries ────────────────────────────────────────────────────────
|
|
86
72
|
/**
|
|
87
73
|
* Aggregate a numeric field over time.
|
|
88
|
-
* Server `queryType`: **"fieldTimeSeries"**
|
|
89
74
|
*
|
|
90
75
|
* @example
|
|
91
76
|
* const { data } = await db.analytics.fieldTimeSeries('revenue', {
|
|
77
|
+
* bucketKey: 'orders',
|
|
92
78
|
* granularity: 'month',
|
|
93
79
|
* aggregation: 'sum',
|
|
94
80
|
* });
|
|
95
81
|
*/
|
|
96
82
|
async fieldTimeSeries(field, options) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
field,
|
|
100
|
-
aggregation: options?.aggregation,
|
|
101
|
-
granularity: options?.granularity,
|
|
102
|
-
dateRange: options?.dateRange
|
|
103
|
-
}, options);
|
|
83
|
+
const { aggregation, granularity, dateRange, ...rest } = options;
|
|
84
|
+
return this.query({ queryType: "fieldTimeSeries", field, aggregation, granularity, dateRange }, rest);
|
|
104
85
|
}
|
|
105
86
|
// ── topN ───────────────────────────────────────────────────────────────────
|
|
106
87
|
/**
|
|
107
88
|
* Top N most frequent values for a field.
|
|
108
|
-
* Server `queryType`: **"topN"**
|
|
109
89
|
*
|
|
110
90
|
* @example
|
|
111
|
-
* const { data } = await db.analytics.topN('country', 5);
|
|
91
|
+
* const { data } = await db.analytics.topN('country', 5, { bucketKey: 'users' });
|
|
112
92
|
* // [{ label: 'US', value: 'US', count: 500 }, ...]
|
|
113
93
|
*/
|
|
114
94
|
async topN(field, n = 10, options) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
field,
|
|
118
|
-
n,
|
|
119
|
-
labelField: options?.labelField,
|
|
120
|
-
order: options?.order,
|
|
121
|
-
dateRange: options?.dateRange
|
|
122
|
-
}, options);
|
|
95
|
+
const { labelField, order, dateRange, ...rest } = options;
|
|
96
|
+
return this.query({ queryType: "topN", field, n, labelField, order, dateRange }, rest);
|
|
123
97
|
}
|
|
124
98
|
// ── stats ──────────────────────────────────────────────────────────────────
|
|
125
99
|
/**
|
|
126
100
|
* Statistical summary for a numeric field: min, max, avg, stddev, p50, p90, p99.
|
|
127
|
-
* Server `queryType`: **"stats"**
|
|
128
101
|
*
|
|
129
102
|
* @example
|
|
130
|
-
* const { data } = await db.analytics.stats('score');
|
|
103
|
+
* const { data } = await db.analytics.stats('score', { bucketKey: 'users' });
|
|
131
104
|
* console.log(data.avg, data.p99);
|
|
132
105
|
*/
|
|
133
106
|
async stats(field, options) {
|
|
134
|
-
|
|
107
|
+
const { dateRange, ...rest } = options;
|
|
108
|
+
return this.query({ queryType: "stats", field, dateRange }, rest);
|
|
135
109
|
}
|
|
136
110
|
// ── records ────────────────────────────────────────────────────────────────
|
|
137
111
|
/**
|
|
138
112
|
* Filtered, paginated raw records with optional field projection.
|
|
139
113
|
* Supports filter ops: == != > < >= <= CONTAINS
|
|
140
|
-
* Server `queryType`: **"records"**
|
|
141
114
|
*
|
|
142
115
|
* @example
|
|
143
116
|
* const { data } = await db.analytics.records({
|
|
117
|
+
* bucketKey: 'users',
|
|
144
118
|
* filters: [{ field: 'role', op: '==', value: 'admin' }],
|
|
145
119
|
* selectFields: ['email', 'createdAt'],
|
|
146
120
|
* limit: 25,
|
|
147
121
|
* });
|
|
148
|
-
* console.log(data.data, data.hasMore);
|
|
149
122
|
*/
|
|
150
123
|
async records(options) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
offset: options?.offset,
|
|
157
|
-
orderBy: options?.orderBy,
|
|
158
|
-
order: options?.order,
|
|
159
|
-
dateRange: options?.dateRange
|
|
160
|
-
}, options);
|
|
124
|
+
const { filters, selectFields, limit, offset, orderBy, order, dateRange, ...rest } = options;
|
|
125
|
+
return this.query(
|
|
126
|
+
{ queryType: "records", filters, selectFields, limit, offset, orderBy, order, dateRange },
|
|
127
|
+
rest
|
|
128
|
+
);
|
|
161
129
|
}
|
|
162
130
|
// ── multiMetric ────────────────────────────────────────────────────────────
|
|
163
131
|
/**
|
|
164
|
-
* Multiple aggregations in a single
|
|
165
|
-
* Server `queryType`: **"multiMetric"**
|
|
132
|
+
* Multiple aggregations in a single call — ideal for dashboard stat cards.
|
|
166
133
|
*
|
|
167
134
|
* @example
|
|
168
|
-
* const { data } = await db.analytics.multiMetric(
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
135
|
+
* const { data } = await db.analytics.multiMetric(
|
|
136
|
+
* [
|
|
137
|
+
* { name: 'totalRevenue', field: 'amount', aggregation: 'sum' },
|
|
138
|
+
* { name: 'avgScore', field: 'score', aggregation: 'avg' },
|
|
139
|
+
* ],
|
|
140
|
+
* { bucketKey: 'orders' }
|
|
141
|
+
* );
|
|
142
|
+
* console.log(data.totalRevenue, data.avgScore);
|
|
174
143
|
*/
|
|
175
144
|
async multiMetric(metrics, options) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
metrics,
|
|
179
|
-
dateRange: options?.dateRange
|
|
180
|
-
}, options);
|
|
145
|
+
const { dateRange, ...rest } = options;
|
|
146
|
+
return this.query({ queryType: "multiMetric", metrics, dateRange }, rest);
|
|
181
147
|
}
|
|
182
148
|
// ── storageStats ───────────────────────────────────────────────────────────
|
|
183
149
|
/**
|
|
184
|
-
* Storage statistics for
|
|
185
|
-
* Server `queryType`: **"storageStats"**
|
|
150
|
+
* Storage statistics for a bucket — total records, bytes, avg/min/max size.
|
|
186
151
|
*
|
|
187
152
|
* @example
|
|
188
|
-
* const { data } = await db.analytics.storageStats();
|
|
153
|
+
* const { data } = await db.analytics.storageStats({ bucketKey: 'orders' });
|
|
189
154
|
* console.log(data.totalRecords, data.totalBytes);
|
|
190
155
|
*/
|
|
191
156
|
async storageStats(options) {
|
|
192
|
-
|
|
157
|
+
const { dateRange, ...rest } = options;
|
|
158
|
+
return this.query({ queryType: "storageStats", dateRange }, rest);
|
|
193
159
|
}
|
|
194
160
|
// ── crossBucket ────────────────────────────────────────────────────────────
|
|
195
161
|
/**
|
|
196
162
|
* Compare a metric across multiple buckets in one query.
|
|
197
|
-
*
|
|
163
|
+
* Note: `bucketKey` here is used only for auth — the actual buckets
|
|
164
|
+
* compared are specified via `bucketKeys`.
|
|
198
165
|
*
|
|
199
166
|
* @example
|
|
200
167
|
* const { data } = await db.analytics.crossBucket({
|
|
201
|
-
*
|
|
168
|
+
* bucketKey: 'sales-2025', // auth bucket
|
|
169
|
+
* bucketKeys: ['sales-2024', 'sales-2025'],
|
|
202
170
|
* field: 'amount',
|
|
203
171
|
* aggregation: 'sum',
|
|
204
172
|
* });
|
|
205
173
|
*/
|
|
206
174
|
async crossBucket(options) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
bucketKeys: options.bucketKeys,
|
|
210
|
-
field: options.field,
|
|
211
|
-
aggregation: options.aggregation,
|
|
212
|
-
dateRange: options.dateRange
|
|
213
|
-
}, options);
|
|
175
|
+
const { bucketKeys, field, aggregation, dateRange, ...rest } = options;
|
|
176
|
+
return this.query({ queryType: "crossBucket", bucketKeys, field, aggregation, dateRange }, rest);
|
|
214
177
|
}
|
|
215
178
|
};
|
|
216
179
|
|