hydrousdb 2.0.1 → 3.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/dist/index.d.ts CHANGED
@@ -1,168 +1,188 @@
1
- interface HydrousConfig {
2
- /** Your Hydrous project URL e.g. https://api.yourapp.hydrous.app */
3
- url: string;
4
- /** Your project API key */
5
- apiKey: string;
6
- /** Optional default request timeout in milliseconds (default: 30_000) */
7
- timeout?: number;
8
- }
9
- interface HydrousResponse<T = unknown> {
10
- data: T | null;
11
- error: HydrousError | null;
12
- }
13
- interface HydrousError {
14
- message: string;
15
- code: string;
16
- status?: number;
17
- }
18
- type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in' | 'nin' | 'is' | 'not';
19
- interface Filter {
20
- field: string;
21
- operator: FilterOperator;
22
- value: unknown;
1
+ interface RequestOptions {
2
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
3
+ body?: unknown;
4
+ headers?: Record<string, string>;
5
+ rawBody?: string | FormData | Uint8Array<ArrayBuffer>;
6
+ contentType?: string;
23
7
  }
24
- interface QueryOptions {
25
- where?: Filter | Filter[];
26
- orderBy?: {
27
- field: string;
28
- direction?: 'asc' | 'desc';
29
- };
30
- limit?: number;
31
- offset?: number;
32
- select?: string[];
8
+ declare class HttpClient {
9
+ private readonly baseUrl;
10
+ private readonly securityKey;
11
+ constructor(baseUrl: string, securityKey: string);
12
+ request<T = unknown>(path: string, opts?: RequestOptions): Promise<T>;
13
+ get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
14
+ post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
15
+ put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
16
+ patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
17
+ delete<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
18
+ putToSignedUrl(signedUrl: string, data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
33
19
  }
34
- interface RecordResponse<T = Record<string, unknown>> {
35
- data: T[];
36
- count: number;
37
- error: HydrousError | null;
20
+
21
+ interface HydrousConfig {
22
+ /**
23
+ * Your project's Security Key (starts with `sk_`).
24
+ * Obtain this from your dashboard at https://hydrousdb.com/dashboard.
25
+ * This is your most important credential — keep it secret.
26
+ */
27
+ securityKey: string;
28
+ /**
29
+ * Override the API base URL. Defaults to the official HydrousDB endpoint.
30
+ * You almost never need to set this.
31
+ */
32
+ baseUrl?: string;
38
33
  }
39
- interface SingleRecordResponse<T = Record<string, unknown>> {
40
- data: T | null;
41
- error: HydrousError | null;
34
+ interface SignupOptions {
35
+ email: string;
36
+ password: string;
37
+ fullName?: string;
38
+ [key: string]: unknown;
42
39
  }
43
- interface AuthUser {
40
+ interface LoginOptions {
41
+ email: string;
42
+ password: string;
43
+ }
44
+ interface UserRecord {
44
45
  id: string;
45
46
  email: string;
46
- createdAt: string;
47
- updatedAt: string;
47
+ fullName?: string | null;
48
+ emailVerified: boolean;
49
+ accountStatus: 'active' | 'locked' | 'suspended';
50
+ role: 'user' | 'admin';
51
+ createdAt: number;
52
+ updatedAt: number;
48
53
  metadata?: Record<string, unknown>;
54
+ [key: string]: unknown;
49
55
  }
50
- interface AuthSession {
51
- accessToken: string;
52
- refreshToken: string;
56
+ interface Session {
57
+ sessionId: string;
58
+ userId: string;
59
+ bucketId: string;
60
+ createdAt: number;
53
61
  expiresAt: number;
54
- user: AuthUser;
62
+ refreshToken: string;
63
+ refreshExpiresAt: number;
55
64
  }
56
- interface SignUpOptions {
57
- email: string;
58
- password: string;
59
- metadata?: Record<string, unknown>;
65
+ interface AuthResult {
66
+ user: UserRecord;
67
+ session: Session;
60
68
  }
61
- interface SignInOptions {
62
- email: string;
63
- password: string;
69
+ interface UpdateUserOptions {
70
+ sessionId: string;
71
+ userId: string;
72
+ data: Partial<Omit<UserRecord, 'id' | 'email' | 'createdAt'>>;
64
73
  }
65
- interface TrackEventOptions {
66
- event: string;
67
- properties?: Record<string, unknown>;
68
- userId?: string;
69
- sessionId?: string;
70
- timestamp?: number;
71
- }
72
- interface AnalyticsQueryOptions {
73
- event?: string;
74
- from?: string;
75
- to?: string;
74
+ interface ChangePasswordOptions {
75
+ sessionId: string;
76
+ userId: string;
77
+ currentPassword: string;
78
+ newPassword: string;
79
+ }
80
+ interface ListUsersOptions {
81
+ sessionId: string;
76
82
  limit?: number;
77
- groupBy?: string;
83
+ offset?: number;
78
84
  }
79
- interface AnalyticsEvent {
80
- id: string;
81
- event: string;
82
- properties: Record<string, unknown>;
83
- userId?: string;
84
- sessionId?: string;
85
- timestamp: number;
86
- }
87
- /** The bucket key (ssk_…) that authorizes access to a specific storage bucket */
88
- type BucketKey = string;
89
- type UploadStage = 'pending' | 'compressing' | 'uploading' | 'done' | 'error';
90
- /** Fired for every progress tick during an upload */
91
- interface UploadProgress {
92
- /** 0-based index (always 0 for single uploads) */
93
- index: number;
94
- /** Total files in this operation */
95
- total: number;
96
- /** Destination path (relative to your bucket root) */
97
- path: string;
98
- /** Current lifecycle stage */
99
- stage: UploadStage;
100
- /** Bytes sent to the server so far */
101
- bytesUploaded: number;
102
- /** Total bytes to send */
103
- totalBytes: number;
104
- /** 0–100 integer */
105
- percent: number;
106
- /** Upload speed in bytes/second – null before the first tick */
107
- bytesPerSecond: number | null;
108
- /** Estimated seconds remaining – null until speed is known */
109
- eta: number | null;
110
- /** Set when stage === 'done' */
111
- result?: UploadResult;
112
- /** Set when stage === 'error' */
113
- error?: string;
114
- /** Set when stage === 'error' */
115
- code?: string;
116
- }
117
- /** Per-file progress during a batch download */
118
- interface DownloadProgress {
119
- index: number;
85
+ interface ListUsersResult {
86
+ users: UserRecord[];
120
87
  total: number;
121
- path: string;
122
- status: 'pending' | 'success' | 'error';
123
- size?: number;
88
+ limit: number;
89
+ offset: number;
90
+ }
91
+ type RecordData = Record<string, unknown>;
92
+ interface RecordResult {
93
+ id: string;
94
+ createdAt?: number;
95
+ updatedAt?: number;
96
+ [key: string]: unknown;
97
+ }
98
+ interface QueryFilter {
99
+ field: string;
100
+ op: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'CONTAINS';
101
+ value: string | number | boolean;
102
+ }
103
+ interface QueryOptions {
104
+ filters?: QueryFilter[];
105
+ fields?: string;
106
+ limit?: number;
107
+ offset?: number;
108
+ orderBy?: string;
109
+ order?: 'asc' | 'desc';
110
+ startAfter?: string;
111
+ startAt?: string;
112
+ endAt?: string;
113
+ dateRange?: DateRange;
114
+ }
115
+ interface QueryResult<T = RecordData> {
116
+ records: (T & RecordResult)[];
117
+ total?: number;
118
+ hasMore?: boolean;
119
+ nextCursor?: string;
120
+ }
121
+ interface PatchRecordOptions {
122
+ merge?: boolean;
123
+ }
124
+ interface RecordHistoryEntry {
125
+ id: string;
126
+ version: number;
127
+ createdAt: number;
128
+ data: RecordData;
129
+ }
130
+ interface UploadOptions {
131
+ /** Set to true to make the file publicly accessible without authentication. */
132
+ isPublic?: boolean;
133
+ /** Set to true to overwrite an existing file at the same path. */
134
+ overwrite?: boolean;
135
+ /** MIME type of the file. Auto-detected if omitted. */
124
136
  mimeType?: string;
125
- error?: string;
137
+ /** TTL in seconds for the signed upload URL (default 900 = 15 min). */
138
+ expiresInSeconds?: number;
126
139
  }
127
140
  interface UploadResult {
128
141
  path: string;
129
- compressed: boolean;
130
- originalSize: number;
131
- storedSize: number;
132
- spaceSaved: number;
133
142
  mimeType: string;
143
+ size: number;
144
+ isPublic: boolean;
145
+ publicUrl: string | null;
146
+ downloadUrl: string | null;
134
147
  }
135
- interface FileMetadata {
148
+ interface UploadUrlResult {
149
+ uploadUrl: string;
136
150
  path: string;
137
- name: string;
138
- size: number | null;
139
- originalSize: number | null;
140
- mimeType: string | null;
141
- isCompressed: boolean;
142
- md5Hash: string | null;
143
- updatedAt: string | null;
144
- createdAt: string | null;
145
- }
146
- interface StorageItem {
151
+ mimeType: string;
152
+ expiresAt: string;
153
+ expiresIn: number;
154
+ }
155
+ interface ListOptions {
156
+ prefix?: string;
157
+ limit?: number;
158
+ cursor?: string;
159
+ recursive?: boolean;
160
+ }
161
+ interface FileEntry {
147
162
  name: string;
148
163
  path: string;
149
- type: 'file' | 'folder';
150
- size?: number | null;
151
- storedSize?: number | null;
152
- mimeType?: string | null;
153
- isCompressed?: boolean;
154
- updatedAt?: string | null;
155
- createdAt?: string | null;
156
- md5Hash?: string | null;
164
+ size?: number;
165
+ mimeType?: string;
166
+ isPublic?: boolean;
167
+ publicUrl?: string | null;
168
+ downloadUrl?: string | null;
169
+ updatedAt?: string;
157
170
  }
158
171
  interface ListResult {
159
- items: StorageItem[];
160
- pagination: {
161
- limit: number;
162
- count: number;
163
- hasNextPage: boolean;
164
- nextCursor: string | null;
165
- };
172
+ files: FileEntry[];
173
+ folders: string[];
174
+ hasMore: boolean;
175
+ nextCursor?: string;
176
+ }
177
+ interface FileMetadata {
178
+ path: string;
179
+ size: number;
180
+ mimeType: string;
181
+ isPublic: boolean;
182
+ publicUrl: string | null;
183
+ downloadUrl: string | null;
184
+ createdAt?: string;
185
+ updatedAt?: string;
166
186
  }
167
187
  interface SignedUrlResult {
168
188
  signedUrl: string;
@@ -170,542 +190,1351 @@ interface SignedUrlResult {
170
190
  expiresIn: number;
171
191
  path: string;
172
192
  }
173
- interface BatchUploadResult {
174
- succeeded: UploadResult[];
175
- failed: Array<{
176
- path: string;
177
- error: string;
178
- code: string;
179
- }>;
193
+ interface StorageStats {
194
+ totalFiles: number;
195
+ totalBytes: number;
196
+ uploadCount: number;
197
+ downloadCount: number;
198
+ deleteCount: number;
180
199
  }
181
- interface BatchDownloadFile {
200
+ interface BatchUploadItem {
182
201
  path: string;
183
- content: ArrayBuffer;
184
202
  mimeType: string;
185
203
  size: number;
204
+ isPublic?: boolean;
205
+ overwrite?: boolean;
186
206
  }
187
- interface StorageStats {
188
- totalFiles: number;
189
- totalSizeBytes: number;
190
- totalOriginalSizeBytes: number;
191
- spaceSavedBytes: number;
192
- uploadsCount: number;
193
- downloadsCount: number;
194
- deletesCount: number;
195
- creditsUsedUpload: number;
196
- creditsUsedDownload: number;
197
- creditsTotalUsed: number;
198
- bytesDelivered: number;
199
- compressionRatio: string;
207
+ interface BatchUploadUrlResult {
208
+ files: Array<UploadUrlResult & {
209
+ index: number;
210
+ }>;
200
211
  }
201
- interface UploadOptions {
212
+ type QueryType = 'count' | 'distribution' | 'sum' | 'timeSeries' | 'fieldTimeSeries' | 'topN' | 'stats' | 'records' | 'multiMetric' | 'storageStats' | 'crossBucket';
213
+ type Aggregation = 'sum' | 'avg' | 'min' | 'max' | 'count';
214
+ type Granularity = 'hour' | 'day' | 'week' | 'month' | 'year';
215
+ type SortOrder = 'asc' | 'desc';
216
+ interface DateRange {
217
+ /** Start timestamp in milliseconds (Unix epoch). */
218
+ start?: number;
219
+ /** End timestamp in milliseconds (Unix epoch). */
220
+ end?: number;
221
+ }
222
+ interface AnalyticsFilter {
223
+ field: string;
224
+ op: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'CONTAINS';
225
+ value: string | number | boolean;
226
+ }
227
+ interface MetricDefinition {
228
+ /** Field name in your records to aggregate. */
229
+ field: string;
230
+ /** Alias used in the result object. Must be a safe identifier. */
231
+ name: string;
232
+ /** Aggregation function. Defaults to 'count'. */
233
+ aggregation?: Aggregation;
234
+ }
235
+ interface AnalyticsQuery {
236
+ queryType: QueryType;
237
+ dateRange?: DateRange;
238
+ field?: string;
239
+ groupBy?: string;
240
+ labelField?: string;
241
+ aggregation?: Aggregation;
242
+ granularity?: Granularity;
243
+ filters?: AnalyticsFilter[];
244
+ selectFields?: string[];
245
+ limit?: number;
246
+ offset?: number;
247
+ orderBy?: string;
248
+ order?: SortOrder;
249
+ n?: number;
250
+ metrics?: MetricDefinition[];
251
+ /** For crossBucket queries: list of bucket names to compare. */
252
+ bucketKeys?: string[];
253
+ }
254
+ interface AnalyticsResult<T = unknown> {
255
+ queryType: QueryType;
256
+ data: T;
257
+ }
258
+ interface CountResult {
259
+ count: number;
260
+ }
261
+ interface DistributionRow {
262
+ value: string | number;
263
+ count: number;
264
+ }
265
+ interface SumRow {
266
+ group?: string | number;
267
+ sum: number;
268
+ }
269
+ interface TimeSeriesRow {
270
+ date: string;
271
+ count: number;
272
+ }
273
+ interface FieldTimeSeriesRow {
274
+ date: string;
275
+ value: number;
276
+ }
277
+ interface TopNRow {
278
+ value: string | number;
279
+ label?: string;
280
+ count: number;
281
+ }
282
+ interface FieldStats {
283
+ min: number;
284
+ max: number;
285
+ avg: number;
286
+ sum: number;
287
+ count: number;
288
+ stddev?: number;
289
+ }
290
+ interface MultiMetricResult {
291
+ [metricName: string]: number;
292
+ }
293
+ interface StorageStatsResult {
294
+ totalRecords: number;
295
+ totalBytes: number;
296
+ avgBytes: number;
297
+ minBytes: number;
298
+ maxBytes: number;
299
+ }
300
+ interface CrossBucketRow {
301
+ bucket: string;
302
+ value: number;
303
+ }
304
+
305
+ /**
306
+ * AuthClient — full user authentication for a single bucket.
307
+ *
308
+ * Every method maps directly to a route in the server's `auth.js` router.
309
+ * The bucket key is the name of your user bucket (e.g. "users" or "customers").
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * const db = createClient({ securityKey: 'sk_...' });
314
+ * const auth = db.auth('my-app-users');
315
+ *
316
+ * const { user, session } = await auth.signup({
317
+ * email: 'alice@example.com',
318
+ * password: 'hunter2',
319
+ * fullName: 'Alice',
320
+ * });
321
+ * ```
322
+ */
323
+ declare class AuthClient {
324
+ private readonly http;
325
+ private readonly bucketKey;
326
+ private readonly basePath;
327
+ constructor(http: HttpClient, bucketKey: string);
202
328
  /**
203
- * Destination path within your bucket (e.g. "avatars/photo.jpg").
204
- * Defaults to the file's original name.
329
+ * Register a new user in this bucket.
330
+ *
331
+ * @example
332
+ * ```ts
333
+ * const { user, session } = await auth.signup({
334
+ * email: 'alice@example.com',
335
+ * password: 'hunter2',
336
+ * fullName: 'Alice Wonderland',
337
+ * // Any extra fields are stored on the user record:
338
+ * plan: 'pro',
339
+ * });
340
+ * ```
205
341
  */
206
- path?: string;
342
+ signup(options: SignupOptions): Promise<AuthResult>;
207
343
  /**
208
- * Replace the file if it already exists at the given path.
209
- * @default false
344
+ * Authenticate an existing user and create a session.
345
+ * Sessions are valid for 24 hours; use `refreshSession` to extend.
346
+ *
347
+ * @example
348
+ * ```ts
349
+ * const { user, session } = await auth.login({
350
+ * email: 'alice@example.com',
351
+ * password: 'hunter2',
352
+ * });
353
+ * // Store session.sessionId safely — you'll need it for other calls.
354
+ * ```
210
355
  */
211
- overwrite?: boolean;
356
+ login(options: LoginOptions): Promise<AuthResult>;
212
357
  /**
213
- * Called on every progress tick while the file is being transferred.
214
- * Use this to drive progress bars, speed indicators, and ETAs.
358
+ * Invalidate a session (sign out).
359
+ *
360
+ * @example
361
+ * ```ts
362
+ * await auth.logout({ sessionId: session.sessionId });
363
+ * ```
215
364
  */
216
- onProgress?: (progress: UploadProgress) => void;
217
- }
218
- interface BatchUploadOptions {
365
+ logout({ sessionId }: {
366
+ sessionId: string;
367
+ }): Promise<void>;
219
368
  /**
220
- * Folder prefix to prepend to all file names (e.g. "uploads/2024/").
221
- * Individual path overrides take precedence if provided.
369
+ * Extend a session using its refresh token.
370
+ * Returns a new session object.
371
+ *
372
+ * @example
373
+ * ```ts
374
+ * const newSession = await auth.refreshSession({ refreshToken: session.refreshToken });
375
+ * ```
222
376
  */
223
- prefix?: string;
377
+ refreshSession({ refreshToken }: {
378
+ refreshToken: string;
379
+ }): Promise<Session>;
224
380
  /**
225
- * Per-file destination paths in the same order as the `files` array.
226
- * If omitted, `prefix + file.name` is used.
381
+ * Fetch a user by their ID.
382
+ *
383
+ * @example
384
+ * ```ts
385
+ * const user = await auth.getUser({ userId: 'usr_abc123' });
386
+ * ```
227
387
  */
228
- paths?: string[];
229
- /** @default false */
230
- overwrite?: boolean;
231
- /** Max simultaneous uploads (1–10). @default 5 */
232
- concurrency?: number;
388
+ getUser({ userId }: {
389
+ userId: string;
390
+ }): Promise<UserRecord>;
233
391
  /**
234
- * Called on every progress tick for every file.
235
- * The `index` field identifies which file the event belongs to.
392
+ * Update fields on a user record. Requires a valid session.
393
+ *
394
+ * @example
395
+ * ```ts
396
+ * const updated = await auth.updateUser({
397
+ * sessionId: session.sessionId,
398
+ * userId: user.id,
399
+ * data: { fullName: 'Alice Smith', plan: 'enterprise' },
400
+ * });
401
+ * ```
236
402
  */
237
- onProgress?: (progress: UploadProgress) => void;
238
- }
239
- interface BatchDownloadOptions {
240
- /** Max simultaneous downloads (1–10). @default 5 */
241
- concurrency?: number;
403
+ updateUser(options: UpdateUserOptions): Promise<UserRecord>;
242
404
  /**
243
- * Called when each file's download status changes.
405
+ * Soft-delete a user. The record is marked deleted but not removed from storage.
406
+ * Requires a valid session (the user can delete themselves, or an admin can delete any user).
407
+ *
408
+ * @example
409
+ * ```ts
410
+ * await auth.deleteUser({ sessionId: session.sessionId, userId: user.id });
411
+ * ```
244
412
  */
245
- onProgress?: (progress: DownloadProgress) => void;
413
+ deleteUser({ sessionId, userId }: {
414
+ sessionId: string;
415
+ userId: string;
416
+ }): Promise<void>;
246
417
  /**
247
- * When true, automatically triggers browser download for each file as
248
- * it arrives. Only works in browser environments.
249
- * @default false
418
+ * List all users in the bucket. **Admin session required.**
419
+ *
420
+ * @example
421
+ * ```ts
422
+ * const { users, total } = await auth.listUsers({
423
+ * sessionId: adminSession.sessionId,
424
+ * limit: 50,
425
+ * offset: 0,
426
+ * });
427
+ * ```
250
428
  */
251
- autoSave?: boolean;
252
- }
253
- interface ListOptions {
254
- /** Folder prefix to list (e.g. "avatars/"). Empty string = root. */
255
- prefix?: string;
256
- /** Max items per page (1–100). @default 50 */
257
- limit?: number;
258
- /** Pagination cursor returned from the previous call. */
259
- cursor?: string;
260
- }
261
- interface SignedUrlOptions {
262
- /** How long the URL stays valid, in seconds. @default 3600 (1 hour) */
263
- expiresIn?: number;
264
- }
265
-
266
- declare class AuthClient {
267
- private readonly baseUrl;
268
- private readonly headers;
269
- private session;
270
- constructor(config: HydrousConfig);
429
+ listUsers(options: ListUsersOptions): Promise<ListUsersResult>;
271
430
  /**
272
- * Create a new user account and return a session.
431
+ * Permanently hard-delete a user and all their data. **Admin session required.**
432
+ * This action is irreversible.
273
433
  *
274
434
  * @example
275
- * const { data, error } = await hydrous.auth.signUp({
276
- * email: 'user@example.com',
277
- * password: 'supersecret',
435
+ * ```ts
436
+ * await auth.hardDeleteUser({ sessionId: adminSession.sessionId, userId: user.id });
437
+ * ```
438
+ */
439
+ hardDeleteUser({ sessionId, userId }: {
440
+ sessionId: string;
441
+ userId: string;
442
+ }): Promise<void>;
443
+ /**
444
+ * Bulk delete multiple users. **Admin session required.**
445
+ *
446
+ * @example
447
+ * ```ts
448
+ * const result = await auth.bulkDeleteUsers({
449
+ * sessionId: adminSession.sessionId,
450
+ * userIds: ['usr_a', 'usr_b'],
278
451
  * });
452
+ * ```
279
453
  */
280
- signUp(options: SignUpOptions): Promise<HydrousResponse<AuthSession>>;
454
+ bulkDeleteUsers({ sessionId, userIds, }: {
455
+ sessionId: string;
456
+ userIds: string[];
457
+ }): Promise<{
458
+ deleted: number;
459
+ failed: string[];
460
+ }>;
281
461
  /**
282
- * Sign in with email and password.
462
+ * Lock a user account, preventing login. **Admin session required.**
463
+ *
464
+ * @param options.duration Lock duration in milliseconds. Defaults to 15 minutes.
283
465
  *
284
466
  * @example
285
- * const { data, error } = await hydrous.auth.signIn({
286
- * email: 'user@example.com',
287
- * password: 'supersecret',
467
+ * ```ts
468
+ * await auth.lockAccount({
469
+ * sessionId: adminSession.sessionId,
470
+ * userId: user.id,
471
+ * duration: 60 * 60 * 1000, // 1 hour
288
472
  * });
289
- * if (data) console.log('Signed in as', data.user.email);
473
+ * ```
290
474
  */
291
- signIn(options: SignInOptions): Promise<HydrousResponse<AuthSession>>;
475
+ lockAccount({ sessionId, userId, duration, }: {
476
+ sessionId: string;
477
+ userId: string;
478
+ duration?: number;
479
+ }): Promise<{
480
+ lockedUntil: number;
481
+ unlockTime: string;
482
+ }>;
292
483
  /**
293
- * Sign out the current user and invalidate their session.
484
+ * Unlock a previously locked user account. **Admin session required.**
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * await auth.unlockAccount({ sessionId: adminSession.sessionId, userId: user.id });
489
+ * ```
294
490
  */
295
- signOut(): Promise<HydrousResponse<void>>;
296
- /** Return the currently authenticated user, or null if not signed in. */
297
- getUser(): Promise<HydrousResponse<AuthUser>>;
491
+ unlockAccount({ sessionId, userId, }: {
492
+ sessionId: string;
493
+ userId: string;
494
+ }): Promise<void>;
298
495
  /**
299
- * Refresh the access token using the stored refresh token.
300
- * Called automatically by the SDK when a 401 is received.
496
+ * Change a user's password. The user must supply their current password.
497
+ *
498
+ * @example
499
+ * ```ts
500
+ * await auth.changePassword({
501
+ * sessionId: session.sessionId,
502
+ * userId: user.id,
503
+ * currentPassword: 'hunter2',
504
+ * newPassword: 'correcthorsebatterystaple',
505
+ * });
506
+ * ```
301
507
  */
302
- refreshSession(): Promise<HydrousResponse<AuthSession>>;
303
- /** Return the current in-memory session (may be null). */
304
- getSession(): AuthSession | null;
305
- private _sessionHeader;
306
- }
307
-
308
- declare class RecordsClient {
309
- private readonly baseUrl;
310
- private readonly headers;
311
- constructor(config: HydrousConfig);
508
+ changePassword(options: ChangePasswordOptions): Promise<void>;
312
509
  /**
313
- * Query records from a collection.
510
+ * Request a password reset email for a user.
511
+ * Always returns success to prevent user enumeration.
314
512
  *
315
- * @param collection - Collection name (e.g. "users")
316
- * @param options - Filters, ordering, pagination
513
+ * @example
514
+ * ```ts
515
+ * await auth.requestPasswordReset({ email: 'alice@example.com' });
516
+ * ```
517
+ */
518
+ requestPasswordReset({ email }: {
519
+ email: string;
520
+ }): Promise<void>;
521
+ /**
522
+ * Complete a password reset using the token from the reset email.
317
523
  *
318
524
  * @example
319
- * const { data, error } = await hydrous.records.select('users', {
320
- * where: { field: 'role', operator: 'eq', value: 'admin' },
321
- * orderBy: { field: 'createdAt', direction: 'desc' },
322
- * limit: 20,
525
+ * ```ts
526
+ * await auth.confirmPasswordReset({
527
+ * resetToken: 'tok_from_email',
528
+ * newPassword: 'correcthorsebatterystaple',
323
529
  * });
530
+ * ```
324
531
  */
325
- select<T = Record<string, unknown>>(collection: string, options?: QueryOptions): Promise<RecordResponse<T>>;
532
+ confirmPasswordReset({ resetToken, newPassword, }: {
533
+ resetToken: string;
534
+ newPassword: string;
535
+ }): Promise<void>;
536
+ /**
537
+ * Send (or resend) an email verification message to a user.
538
+ *
539
+ * @example
540
+ * ```ts
541
+ * await auth.requestEmailVerification({ userId: user.id });
542
+ * ```
543
+ */
544
+ requestEmailVerification({ userId }: {
545
+ userId: string;
546
+ }): Promise<void>;
547
+ /**
548
+ * Confirm an email address using the token from the verification email.
549
+ *
550
+ * @example
551
+ * ```ts
552
+ * await auth.confirmEmailVerification({ verifyToken: 'tok_from_email' });
553
+ * ```
554
+ */
555
+ confirmEmailVerification({ verifyToken }: {
556
+ verifyToken: string;
557
+ }): Promise<void>;
558
+ }
559
+
560
+ /**
561
+ * RecordsClient — create, read, update, delete, and query records in a bucket.
562
+ *
563
+ * A bucket is simply a named collection of JSON records. You create buckets
564
+ * from your HydrousDB dashboard. Each record is a JSON object with any fields
565
+ * you want; HydrousDB automatically adds `id`, `createdAt`, and `updatedAt`.
566
+ *
567
+ * @example
568
+ * ```ts
569
+ * const db = createClient({ securityKey: 'sk_...' });
570
+ * const posts = db.records('blog-posts');
571
+ *
572
+ * // Create a record
573
+ * const post = await posts.create({ title: 'Hello World', status: 'draft' });
574
+ *
575
+ * // Query records
576
+ * const { records } = await posts.query({
577
+ * filters: [{ field: 'status', op: '==', value: 'published' }],
578
+ * limit: 20,
579
+ * });
580
+ * ```
581
+ */
582
+ declare class RecordsClient<T extends RecordData = RecordData> {
583
+ private readonly http;
584
+ private readonly bucketKey;
585
+ private readonly basePath;
586
+ constructor(http: HttpClient, bucketKey: string);
326
587
  /**
327
- * Fetch a single record by its ID.
588
+ * Create a new record.
328
589
  *
329
590
  * @example
330
- * const { data, error } = await hydrous.records.get('users', 'user_abc123');
591
+ * ```ts
592
+ * const user = await records.create({
593
+ * name: 'Alice',
594
+ * email: 'alice@example.com',
595
+ * score: 100,
596
+ * });
597
+ * console.log(user.id); // "rec_xxxxxxxx"
598
+ * ```
331
599
  */
332
- get<T = Record<string, unknown>>(collection: string, id: string): Promise<SingleRecordResponse<T>>;
600
+ create(data: T): Promise<T & RecordResult>;
333
601
  /**
334
- * Insert one or more records into a collection.
602
+ * Fetch a single record by ID.
335
603
  *
336
- * @param collection - Collection name
337
- * @param payload - A single record object or an array of record objects
604
+ * @example
605
+ * ```ts
606
+ * const post = await records.get('rec_abc123');
607
+ * ```
608
+ */
609
+ get(id: string): Promise<T & RecordResult>;
610
+ /**
611
+ * Overwrite a record entirely (full replace).
338
612
  *
339
613
  * @example
340
- * // Single insert
341
- * const { data, error } = await hydrous.records.insert('users', {
342
- * name: 'Alice', email: 'alice@example.com'
614
+ * ```ts
615
+ * const updated = await records.set('rec_abc123', {
616
+ * name: 'Alice Updated',
617
+ * email: 'alice2@example.com',
343
618
  * });
619
+ * ```
620
+ */
621
+ set(id: string, data: T): Promise<T & RecordResult>;
622
+ /**
623
+ * Partially update a record (merge by default).
624
+ *
625
+ * @example
626
+ * ```ts
627
+ * // Merge: only the provided fields are updated
628
+ * const updated = await records.patch('rec_abc123', { score: 200 });
344
629
  *
345
- * // Bulk insert
346
- * const { data, error } = await hydrous.records.insert('users', [
347
- * { name: 'Alice' }, { name: 'Bob' }
630
+ * // Replace: equivalent to set()
631
+ * const replaced = await records.patch('rec_abc123', { score: 200 }, { merge: false });
632
+ * ```
633
+ */
634
+ patch(id: string, data: Partial<T>, options?: PatchRecordOptions): Promise<T & RecordResult>;
635
+ /**
636
+ * Delete a record permanently.
637
+ *
638
+ * @example
639
+ * ```ts
640
+ * await records.delete('rec_abc123');
641
+ * ```
642
+ */
643
+ delete(id: string): Promise<void>;
644
+ /**
645
+ * Create multiple records in one request.
646
+ *
647
+ * @example
648
+ * ```ts
649
+ * const created = await records.batchCreate([
650
+ * { name: 'Alice', score: 100 },
651
+ * { name: 'Bob', score: 200 },
348
652
  * ]);
653
+ * ```
654
+ */
655
+ batchCreate(items: T[]): Promise<(T & RecordResult)[]>;
656
+ /**
657
+ * Delete multiple records by ID in one request.
658
+ *
659
+ * @example
660
+ * ```ts
661
+ * await records.batchDelete(['rec_a', 'rec_b', 'rec_c']);
662
+ * ```
349
663
  */
350
- insert<T = Record<string, unknown>>(collection: string, payload: Partial<T> | Partial<T>[]): Promise<RecordResponse<T>>;
664
+ batchDelete(ids: string[]): Promise<{
665
+ deleted: number;
666
+ failed: string[];
667
+ }>;
351
668
  /**
352
- * Update a record by ID.
669
+ * Query records with optional filters, sorting, and pagination.
353
670
  *
354
671
  * @example
355
- * const { data, error } = await hydrous.records.update('users', 'user_abc123', {
356
- * name: 'Alice Smith'
672
+ * ```ts
673
+ * // Simple query
674
+ * const { records } = await posts.query({ limit: 10 });
675
+ *
676
+ * // Filtered query with cursor pagination
677
+ * const page1 = await posts.query({
678
+ * filters: [
679
+ * { field: 'status', op: '==', value: 'published' },
680
+ * { field: 'views', op: '>', value: 1000 },
681
+ * ],
682
+ * orderBy: 'createdAt',
683
+ * order: 'desc',
684
+ * limit: 20,
685
+ * });
686
+ *
687
+ * const page2 = await posts.query({
688
+ * filters: [{ field: 'status', op: '==', value: 'published' }],
689
+ * limit: 20,
690
+ * startAfter: page1.nextCursor,
357
691
  * });
692
+ * ```
693
+ */
694
+ query(options?: QueryOptions): Promise<QueryResult<T>>;
695
+ /**
696
+ * Convenience alias: get all records up to `limit` (default 100).
697
+ *
698
+ * @example
699
+ * ```ts
700
+ * const allPosts = await posts.getAll({ limit: 500 });
701
+ * ```
358
702
  */
359
- update<T = Record<string, unknown>>(collection: string, id: string, payload: Partial<T>): Promise<SingleRecordResponse<T>>;
703
+ getAll(options?: Omit<QueryOptions, 'filters'>): Promise<(T & RecordResult)[]>;
360
704
  /**
361
- * Delete a record by ID.
705
+ * Count records matching optional filters.
362
706
  *
363
707
  * @example
364
- * const { error } = await hydrous.records.delete('users', 'user_abc123');
708
+ * ```ts
709
+ * const total = await posts.count([{ field: 'status', op: '==', value: 'published' }]);
710
+ * ```
365
711
  */
366
- delete(collection: string, id: string): Promise<SingleRecordResponse<void>>;
712
+ count(filters?: QueryOptions['filters']): Promise<number>;
713
+ /**
714
+ * Retrieve the full version history of a record.
715
+ * Each write creates a new version stored in GCS.
716
+ *
717
+ * @example
718
+ * ```ts
719
+ * const history = await records.getHistory('rec_abc123');
720
+ * console.log(history[0].data); // latest version
721
+ * ```
722
+ */
723
+ getHistory(id: string): Promise<RecordHistoryEntry[]>;
724
+ /**
725
+ * Restore a record to a specific historical version.
726
+ *
727
+ * @example
728
+ * ```ts
729
+ * const history = await records.getHistory('rec_abc123');
730
+ * const restored = await records.restoreVersion('rec_abc123', history[2].version);
731
+ * ```
732
+ */
733
+ restoreVersion(id: string, version: number): Promise<T & RecordResult>;
367
734
  }
368
735
 
736
+ /**
737
+ * AnalyticsClient — run powerful aggregation and time-series queries against
738
+ * your bucket data using BigQuery under the hood.
739
+ *
740
+ * All query types accept an optional `dateRange` to filter by time.
741
+ * The security key is sent automatically — you never pass it manually.
742
+ *
743
+ * @example
744
+ * ```ts
745
+ * const db = createClient({ securityKey: 'sk_...' });
746
+ * const analytics = db.analytics('orders');
747
+ *
748
+ * // How many orders in the last 30 days?
749
+ * const { count } = await analytics.count({
750
+ * dateRange: { start: Date.now() - 30 * 24 * 60 * 60 * 1000, end: Date.now() },
751
+ * });
752
+ * ```
753
+ */
369
754
  declare class AnalyticsClient {
370
- private readonly baseUrl;
371
- private readonly headers;
372
- constructor(config: HydrousConfig);
755
+ private readonly http;
756
+ private readonly bucketKey;
757
+ private readonly basePath;
758
+ constructor(http: HttpClient, bucketKey: string);
759
+ /** Internal dispatcher — all queries POST to the same endpoint. */
760
+ private run;
373
761
  /**
374
- * Track an analytics event.
762
+ * Count the total number of records in the bucket, with optional date filter.
375
763
  *
376
764
  * @example
377
- * await hydrous.analytics.track({
378
- * event: 'page_view',
379
- * properties: { page: '/home', referrer: 'google.com' },
380
- * userId: 'user_abc123',
765
+ * ```ts
766
+ * const { count } = await analytics.count();
767
+ * // { count: 4821 }
768
+ *
769
+ * // Count only this month's records
770
+ * const { count } = await analytics.count({
771
+ * dateRange: { start: new Date('2025-01-01').getTime(), end: Date.now() },
381
772
  * });
773
+ * ```
382
774
  */
383
- track(options: TrackEventOptions): Promise<HydrousResponse<void>>;
775
+ count(opts?: {
776
+ dateRange?: DateRange;
777
+ }): Promise<CountResult>;
384
778
  /**
385
- * Query recorded analytics events.
779
+ * Count how many records have each unique value for a given field.
780
+ * Great for pie charts and bar charts.
386
781
  *
387
782
  * @example
388
- * const { data } = await hydrous.analytics.query({
389
- * event: 'page_view',
390
- * from: '2024-01-01',
391
- * to: '2024-01-31',
392
- * limit: 100,
783
+ * ```ts
784
+ * const rows = await analytics.distribution({
785
+ * field: 'status',
786
+ * limit: 10,
787
+ * order: 'desc',
393
788
  * });
789
+ * // → [{ value: 'completed', count: 312 }, { value: 'pending', count: 88 }, ...]
790
+ * ```
394
791
  */
395
- query(options?: AnalyticsQueryOptions): Promise<RecordResponse<AnalyticsEvent>>;
792
+ distribution(opts: {
793
+ field: string;
794
+ limit?: number;
795
+ order?: SortOrder;
796
+ dateRange?: DateRange;
797
+ }): Promise<DistributionRow[]>;
396
798
  /**
397
- * Track multiple events in a single request (more efficient than
398
- * calling `track` in a loop).
799
+ * Sum a numeric field, optionally grouped by another field.
399
800
  *
400
801
  * @example
401
- * await hydrous.analytics.trackBatch([
402
- * { event: 'signup', userId: 'u1' },
403
- * { event: 'onboarded', userId: 'u1' },
404
- * ]);
802
+ * ```ts
803
+ * // Total revenue
804
+ * const rows = await analytics.sum({ field: 'amount' });
805
+ * // → [{ sum: 198432.50 }]
806
+ *
807
+ * // Revenue by country
808
+ * const rows = await analytics.sum({ field: 'amount', groupBy: 'country', limit: 10 });
809
+ * // → [{ group: 'US', sum: 120000 }, { group: 'UK', sum: 45000 }, ...]
810
+ * ```
405
811
  */
406
- trackBatch(events: TrackEventOptions[]): Promise<HydrousResponse<void>>;
407
- }
408
-
409
- declare class StorageClient {
410
- private readonly baseUrl;
411
- constructor(config: HydrousConfig);
812
+ sum(opts: {
813
+ field: string;
814
+ groupBy?: string;
815
+ limit?: number;
816
+ dateRange?: DateRange;
817
+ }): Promise<SumRow[]>;
412
818
  /**
413
- * Upload a single file to a bucket.
414
- *
415
- * The bucket key **always comes first**.
416
- * Supply an `onProgress` callback to receive live upload progress including
417
- * bytes transferred, speed (bytes/sec), ETA, and lifecycle stage.
418
- *
419
- * ### Stages fired via `onProgress`
420
- * | Stage | Meaning |
421
- * |-------------|------------------------------------------|
422
- * | `pending` | Queued, not yet started |
423
- * | `compressing` | Server is compressing the file |
424
- * | `uploading` | Bytes flowing to cloud storage |
425
- * | `done` | Confirmed written to cloud storage |
426
- * | `error` | Something went wrong |
427
- *
428
- * @param bucketKey Your storage bucket key (`ssk_…`)
429
- * @param file A `File`, `Blob`, or `Buffer` (Node)
430
- * @param options Path, overwrite flag, progress callback
431
- *
432
- * @example
433
- * const { data, error } = await hydrous.storage.upload(
434
- * 'ssk_my_bucket_key',
435
- * file,
436
- * {
437
- * path: 'avatars/alice.jpg',
438
- * overwrite: true,
439
- * onProgress: (p) => {
440
- * console.log(`${p.stage} — ${p.percent}% ${p.bytesPerSecond} B/s ETA ${p.eta}s`);
441
- * },
442
- * }
443
- * );
819
+ * Count records created over time, grouped by a time granularity.
820
+ * Perfect for line charts and activity graphs.
821
+ *
822
+ * @example
823
+ * ```ts
824
+ * const rows = await analytics.timeSeries({
825
+ * granularity: 'day',
826
+ * dateRange: { start: Date.now() - 7 * 86400000, end: Date.now() },
827
+ * });
828
+ * // [{ date: '2025-06-01', count: 42 }, { date: '2025-06-02', count: 67 }, ...]
829
+ * ```
444
830
  */
445
- upload(bucketKey: BucketKey, file: File | Blob | Uint8Array | ArrayBuffer, options?: UploadOptions): Promise<HydrousResponse<UploadResult>>;
831
+ timeSeries(opts?: {
832
+ granularity?: Granularity;
833
+ dateRange?: DateRange;
834
+ }): Promise<TimeSeriesRow[]>;
446
835
  /**
447
- * Upload raw text or JSON content directly no `File` object needed.
448
- * Great for saving generated content, config files, or JSON records.
836
+ * Aggregate a numeric field over time (e.g. daily revenue, hourly signups).
449
837
  *
450
- * @param bucketKey Your storage bucket key (`ssk_…`)
451
- * @param path Destination path (e.g. `"configs/settings.json"`)
452
- * @param content String content to store
453
- * @param options `mimeType`, `overwrite`, `onProgress`
838
+ * @example
839
+ * ```ts
840
+ * const rows = await analytics.fieldTimeSeries({
841
+ * field: 'amount',
842
+ * aggregation: 'sum',
843
+ * granularity: 'week',
844
+ * });
845
+ * // → [{ date: '2025-W22', value: 14230.50 }, ...]
846
+ * ```
847
+ */
848
+ fieldTimeSeries(opts: {
849
+ field: string;
850
+ aggregation?: Aggregation;
851
+ granularity?: Granularity;
852
+ dateRange?: DateRange;
853
+ }): Promise<FieldTimeSeriesRow[]>;
854
+ /**
855
+ * Get the top N values by frequency for a field.
856
+ * Optionally pair with a `labelField` for human-readable labels.
454
857
  *
455
858
  * @example
456
- * await hydrous.storage.uploadText(
457
- * 'ssk_my_bucket_key',
458
- * 'reports/summary.txt',
459
- * 'Hello from Hydrous!',
460
- * { mimeType: 'text/plain' }
461
- * );
859
+ * ```ts
860
+ * // Top 5 most purchased products
861
+ * const rows = await analytics.topN({
862
+ * field: 'productId',
863
+ * labelField: 'productName',
864
+ * n: 5,
865
+ * });
866
+ * // → [{ value: 'prod_123', label: 'Widget Pro', count: 892 }, ...]
867
+ * ```
462
868
  */
463
- uploadText(bucketKey: BucketKey, path: string, content: string, options?: {
464
- mimeType?: string;
465
- overwrite?: boolean;
466
- onProgress?: UploadOptions['onProgress'];
467
- }): Promise<HydrousResponse<UploadResult>>;
468
- /**
469
- * Upload multiple files in one request.
470
- *
471
- * `onProgress` fires for **every file individually** the `index` field
472
- * tells you which file the event belongs to (0-based, same order as `files`).
473
- * All files receive a `pending` event upfront before any uploads start,
474
- * so you can render all progress bars immediately.
475
- *
476
- * @param bucketKey Your storage bucket key (`ssk_…`)
477
- * @param files Array of `File` objects (browser) or `{ name, data }` objects (Node)
478
- * @param options Prefix, per-file paths, overwrite, concurrency, onProgress
479
- *
480
- * @example
481
- * await hydrous.storage.batchUpload(
482
- * 'ssk_my_bucket_key',
483
- * fileArray,
484
- * {
485
- * prefix: 'uploads/2024/',
486
- * onProgress: (p) => {
487
- * console.log(`File ${p.index}: ${p.stage} ${p.percent}%`);
488
- * },
489
- * }
490
- * );
869
+ topN(opts: {
870
+ field: string;
871
+ n?: number;
872
+ labelField?: string;
873
+ order?: SortOrder;
874
+ dateRange?: DateRange;
875
+ }): Promise<TopNRow[]>;
876
+ /**
877
+ * Get statistical summary (min, max, avg, sum, count, stddev) for a numeric field.
878
+ *
879
+ * @example
880
+ * ```ts
881
+ * const stats = await analytics.stats({ field: 'orderValue' });
882
+ * // { min: 4.99, max: 9999.99, avg: 87.23, sum: 420948.27, count: 4823, stddev: 143.2 }
883
+ * ```
491
884
  */
492
- batchUpload(bucketKey: BucketKey, files: File[], options?: BatchUploadOptions): Promise<HydrousResponse<BatchUploadResult>>;
885
+ stats(opts: {
886
+ field: string;
887
+ dateRange?: DateRange;
888
+ }): Promise<FieldStats>;
493
889
  /**
494
- * Download a single file and return its content as an `ArrayBuffer`.
890
+ * Query raw records with filters, field selection, and pagination.
891
+ * This is the analytics version of `records.query()` but powered by BigQuery.
495
892
  *
496
- * @param bucketKey Your storage bucket key (`ssk_…`)
497
- * @param filePath Path of the file within your bucket
893
+ * @example
894
+ * ```ts
895
+ * const { records } = await analytics.records({
896
+ * filters: [{ field: 'status', op: '==', value: 'refunded' }],
897
+ * selectFields: ['orderId', 'amount', 'createdAt'],
898
+ * limit: 50,
899
+ * orderBy: 'amount',
900
+ * order: 'desc',
901
+ * });
902
+ * ```
903
+ */
904
+ records<T extends RecordData = RecordData>(opts?: {
905
+ filters?: AnalyticsFilter[];
906
+ selectFields?: string[];
907
+ limit?: number;
908
+ offset?: number;
909
+ orderBy?: string;
910
+ order?: SortOrder;
911
+ dateRange?: DateRange;
912
+ }): Promise<(T & RecordResult)[]>;
913
+ /**
914
+ * Calculate multiple aggregations in a single query.
915
+ * Ideal for dashboards that need several numbers at once.
498
916
  *
499
917
  * @example
500
- * const { data, error } = await hydrous.storage.download(
501
- * 'ssk_my_bucket_key',
502
- * 'avatars/alice.jpg'
503
- * );
504
- * if (data) {
505
- * const blob = new Blob([data]);
506
- * const url = URL.createObjectURL(blob);
507
- * }
918
+ * ```ts
919
+ * const result = await analytics.multiMetric({
920
+ * metrics: [
921
+ * { field: 'amount', name: 'totalRevenue', aggregation: 'sum' },
922
+ * { field: 'amount', name: 'avgOrderValue', aggregation: 'avg' },
923
+ * { field: 'userId', name: 'uniqueCustomers', aggregation: 'count' },
924
+ * ],
925
+ * });
926
+ * // → { totalRevenue: 198432.50, avgOrderValue: 87.23, uniqueCustomers: 2275 }
927
+ * ```
508
928
  */
509
- download(bucketKey: BucketKey, filePath: string): Promise<HydrousResponse<ArrayBuffer>>;
929
+ multiMetric(opts: {
930
+ metrics: MetricDefinition[];
931
+ dateRange?: DateRange;
932
+ }): Promise<MultiMetricResult>;
510
933
  /**
511
- * Download multiple files in one request.
934
+ * Get storage statistics for this bucket: record counts, byte sizes.
512
935
  *
513
- * When `autoSave: true` (browser only) each file is automatically saved
514
- * to the user's Downloads folder as it arrives.
936
+ * @example
937
+ * ```ts
938
+ * const stats = await analytics.storageStats();
939
+ * // → { totalRecords: 4821, totalBytes: 48293820, avgBytes: 10015, ... }
940
+ * ```
941
+ */
942
+ storageStats(opts?: {
943
+ dateRange?: DateRange;
944
+ }): Promise<StorageStatsResult>;
945
+ /**
946
+ * Compare the same field aggregation across multiple buckets in one query.
947
+ * Your security key must have read access to ALL listed buckets.
515
948
  *
516
- * @param bucketKey Your storage bucket key (`ssk_…`)
517
- * @param filePaths Array of file paths within your bucket
518
- * @param options Concurrency, onProgress, autoSave
949
+ * @example
950
+ * ```ts
951
+ * const rows = await analytics.crossBucket({
952
+ * bucketKeys: ['orders-us', 'orders-eu', 'orders-apac'],
953
+ * field: 'amount',
954
+ * aggregation: 'sum',
955
+ * });
956
+ * // → [
957
+ * // { bucket: 'orders-us', value: 120000 },
958
+ * // { bucket: 'orders-eu', value: 45000 },
959
+ * // { bucket: 'orders-apac', value: 33000 },
960
+ * // ]
961
+ * ```
962
+ */
963
+ crossBucket(opts: {
964
+ bucketKeys: string[];
965
+ field: string;
966
+ aggregation?: Aggregation;
967
+ dateRange?: DateRange;
968
+ }): Promise<CrossBucketRow[]>;
969
+ /**
970
+ * Send a raw analytics query object. Use this when you need full control
971
+ * over the query shape or want to use a queryType not covered by the helpers.
519
972
  *
520
973
  * @example
521
- * const { data } = await hydrous.storage.batchDownload(
522
- * 'ssk_my_bucket_key',
523
- * ['reports/jan.pdf', 'reports/feb.pdf'],
524
- * {
525
- * onProgress: (p) => console.log(p.path, p.status),
526
- * autoSave: true, // triggers browser file-save dialog per file
527
- * }
528
- * );
974
+ * ```ts
975
+ * const result = await analytics.query({
976
+ * queryType: 'topN',
977
+ * field: 'category',
978
+ * n: 3,
979
+ * order: 'asc',
980
+ * });
981
+ * ```
529
982
  */
530
- batchDownload(bucketKey: BucketKey, filePaths: string[], options?: BatchDownloadOptions): Promise<HydrousResponse<BatchDownloadFile[]>>;
983
+ query<T = unknown>(query: AnalyticsQuery): Promise<AnalyticsResult<T>>;
984
+ }
985
+
986
+ /**
987
+ * StorageManager — upload, download, list, move, copy, and delete files.
988
+ *
989
+ * Authentication is handled automatically via the X-Storage-Key header.
990
+ * Files are scoped to your owner ID — you can never access another owner's files.
991
+ *
992
+ * **Recommended upload flow** (for progress tracking and large files):
993
+ * 1. Call `getUploadUrl()` to get a signed PUT URL.
994
+ * 2. Upload directly to GCS using XHR (supports progress events).
995
+ * 3. Call `confirmUpload()` to finalize metadata.
996
+ *
997
+ * **Simple upload** (small files, no progress needed):
998
+ * - Call `upload()` — it handles everything in one call.
999
+ *
1000
+ * @example
1001
+ * ```ts
1002
+ * const db = createClient({ securityKey: 'sk_...' });
1003
+ *
1004
+ * // Simple upload
1005
+ * const file = await db.storage.upload(fileBlob, 'avatars/alice.jpg', { isPublic: true });
1006
+ * console.log(file.publicUrl); // CDN URL, usable anywhere
1007
+ * ```
1008
+ */
1009
+ declare class StorageManager {
1010
+ private readonly http;
1011
+ private readonly storageKey;
1012
+ private readonly basePath;
1013
+ constructor(http: HttpClient, storageKey: string);
1014
+ /** Headers for all storage requests — uses X-Storage-Key, not X-Api-Key. */
1015
+ private get authHeaders();
531
1016
  /**
532
- * List files and folders inside a bucket (or a folder within it).
1017
+ * Upload a file to storage in one step (server-buffered, up to 500 MB).
1018
+ * For files >10 MB or when you need upload progress, use `getUploadUrl()` instead.
1019
+ *
1020
+ * @param data File data as a Blob, Buffer, Uint8Array, or ArrayBuffer.
1021
+ * @param path Destination path in your storage (e.g. `"avatars/alice.jpg"`).
1022
+ * @param options Upload options: isPublic, overwrite, mimeType.
533
1023
  *
534
- * Results are paginated — use `pagination.nextCursor` to fetch the next page.
1024
+ * @example
1025
+ * ```ts
1026
+ * // Upload a public avatar
1027
+ * const result = await storage.upload(file, 'avatars/alice.jpg', { isPublic: true });
1028
+ * console.log(result.publicUrl); // → https://...
535
1029
  *
536
- * @param bucketKey Your storage bucket key (`ssk_…`)
537
- * @param options `prefix`, `limit`, `cursor`
1030
+ * // Upload a private document
1031
+ * const result = await storage.upload(pdfBuffer, 'docs/contract.pdf');
1032
+ * console.log(result.downloadUrl); // → /storage/download/docs/contract.pdf
1033
+ * ```
1034
+ */
1035
+ upload(data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
1036
+ /**
1037
+ * Upload raw JSON or plain text data as a file.
538
1038
  *
539
1039
  * @example
540
- * const { data } = await hydrous.storage.list('ssk_my_bucket_key', {
541
- * prefix: 'avatars/',
542
- * limit: 50,
1040
+ * ```ts
1041
+ * const result = await storage.uploadRaw(
1042
+ * { config: { theme: 'dark' } },
1043
+ * 'settings/user-config.json',
1044
+ * { isPublic: false },
1045
+ * );
1046
+ * ```
1047
+ */
1048
+ uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
1049
+ /**
1050
+ * Step 1 of the recommended upload flow.
1051
+ * Get a signed URL to upload directly to GCS from the client (supports progress).
1052
+ *
1053
+ * @example
1054
+ * ```ts
1055
+ * const { uploadUrl, path: confirmedPath } = await storage.getUploadUrl({
1056
+ * path: 'videos/intro.mp4',
1057
+ * mimeType: 'video/mp4',
1058
+ * size: file.size,
1059
+ * isPublic: true,
543
1060
  * });
544
- * for (const item of data.items) {
545
- * console.log(item.type, item.path);
546
- * }
1061
+ *
1062
+ * // Upload using XHR for progress tracking
1063
+ * await storage.uploadToSignedUrl(uploadUrl, file, 'video/mp4', (pct) => {
1064
+ * console.log(`${pct}% uploaded`);
1065
+ * });
1066
+ *
1067
+ * // Step 3: confirm
1068
+ * const result = await storage.confirmUpload({ path: confirmedPath, mimeType: 'video/mp4', isPublic: true });
1069
+ * ```
547
1070
  */
548
- list(bucketKey: BucketKey, options?: ListOptions): Promise<HydrousResponse<ListResult>>;
1071
+ getUploadUrl(opts: {
1072
+ path: string;
1073
+ mimeType: string;
1074
+ size: number;
1075
+ isPublic?: boolean;
1076
+ overwrite?: boolean;
1077
+ expiresInSeconds?: number;
1078
+ }): Promise<UploadUrlResult>;
549
1079
  /**
550
- * Get metadata for a specific file (size, MIME type, compression info, etc.)
1080
+ * Upload data directly to a signed GCS URL (no auth headers needed).
1081
+ * Optionally tracks progress via a callback.
551
1082
  *
552
- * @param bucketKey Your storage bucket key (`ssk_…`)
553
- * @param filePath Path of the file within your bucket
1083
+ * @param signedUrl The URL returned by `getUploadUrl()`.
1084
+ * @param data File data.
1085
+ * @param mimeType Must match what was used in `getUploadUrl()`.
1086
+ * @param onProgress Optional callback called with 0–100 progress percentage.
1087
+ */
1088
+ uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
1089
+ /**
1090
+ * Step 3 of the recommended upload flow.
1091
+ * Confirm a direct upload and register metadata on the server.
554
1092
  *
555
1093
  * @example
556
- * const { data } = await hydrous.storage.metadata(
557
- * 'ssk_my_bucket_key',
558
- * 'avatars/alice.jpg'
559
- * );
560
- * console.log(data.size, data.mimeType);
1094
+ * ```ts
1095
+ * const result = await storage.confirmUpload({
1096
+ * path: 'videos/intro.mp4',
1097
+ * mimeType: 'video/mp4',
1098
+ * isPublic: true,
1099
+ * });
1100
+ * console.log(result.publicUrl);
1101
+ * ```
561
1102
  */
562
- metadata(bucketKey: BucketKey, filePath: string): Promise<HydrousResponse<FileMetadata>>;
1103
+ confirmUpload(opts: {
1104
+ path: string;
1105
+ mimeType: string;
1106
+ isPublic?: boolean;
1107
+ }): Promise<UploadResult>;
563
1108
  /**
564
- * Delete a single file.
1109
+ * Get signed upload URLs for multiple files at once.
1110
+ *
1111
+ * @example
1112
+ * ```ts
1113
+ * const { files } = await storage.getBatchUploadUrls([
1114
+ * { path: 'images/photo1.jpg', mimeType: 'image/jpeg', size: 204800 },
1115
+ * { path: 'images/photo2.jpg', mimeType: 'image/jpeg', size: 153600 },
1116
+ * ]);
1117
+ *
1118
+ * // Upload each file and confirm
1119
+ * for (const f of files) {
1120
+ * await storage.uploadToSignedUrl(f.uploadUrl, blobs[f.index], f.mimeType);
1121
+ * await storage.confirmUpload({ path: f.path, mimeType: f.mimeType });
1122
+ * }
1123
+ * ```
1124
+ */
1125
+ getBatchUploadUrls(files: BatchUploadItem[]): Promise<BatchUploadUrlResult>;
1126
+ /**
1127
+ * Confirm multiple direct uploads at once.
1128
+ */
1129
+ batchConfirmUploads(items: Array<{
1130
+ path: string;
1131
+ mimeType: string;
1132
+ isPublic?: boolean;
1133
+ }>): Promise<UploadResult[]>;
1134
+ /**
1135
+ * Download a private file as an ArrayBuffer.
1136
+ * For public files, use the `publicUrl` directly — no SDK needed.
565
1137
  *
566
- * @param bucketKey Your storage bucket key (`ssk_…`)
567
- * @param filePath Path of the file to delete
1138
+ * @example
1139
+ * ```ts
1140
+ * const buffer = await storage.download('docs/contract.pdf');
1141
+ * const blob = new Blob([buffer], { type: 'application/pdf' });
1142
+ * // Open in browser:
1143
+ * window.open(URL.createObjectURL(blob));
1144
+ * ```
1145
+ */
1146
+ download(path: string): Promise<ArrayBuffer>;
1147
+ /**
1148
+ * Download multiple files at once, returned as a JSON map of `{ path: base64 }`.
568
1149
  *
569
1150
  * @example
570
- * await hydrous.storage.deleteFile('ssk_my_bucket_key', 'avatars/old.jpg');
1151
+ * ```ts
1152
+ * const files = await storage.batchDownload(['docs/a.pdf', 'docs/b.pdf']);
1153
+ * ```
571
1154
  */
572
- deleteFile(bucketKey: BucketKey, filePath: string): Promise<HydrousResponse<void>>;
1155
+ batchDownload(paths: string[]): Promise<Record<string, string>>;
573
1156
  /**
574
- * Recursively delete a folder and all of its contents.
1157
+ * List files and folders at a given path prefix.
575
1158
  *
576
- * @param bucketKey Your storage bucket key (`ssk_…`)
577
- * @param folderPath Folder path to delete (e.g. `"old-uploads/"`)
1159
+ * @example
1160
+ * ```ts
1161
+ * // List everything in the root
1162
+ * const { files, folders } = await storage.list();
1163
+ *
1164
+ * // List a specific folder
1165
+ * const { files, folders, hasMore, nextCursor } = await storage.list({
1166
+ * prefix: 'avatars/',
1167
+ * limit: 20,
1168
+ * });
1169
+ *
1170
+ * // Next page
1171
+ * const page2 = await storage.list({ prefix: 'avatars/', cursor: nextCursor });
1172
+ * ```
1173
+ */
1174
+ list(opts?: ListOptions): Promise<ListResult>;
1175
+ /**
1176
+ * Get metadata for a file (size, MIME type, visibility, URLs).
578
1177
  *
579
1178
  * @example
580
- * await hydrous.storage.deleteFolder('ssk_my_bucket_key', 'temp/');
1179
+ * ```ts
1180
+ * const meta = await storage.getMetadata('avatars/alice.jpg');
1181
+ * console.log(meta.size, meta.isPublic, meta.publicUrl);
1182
+ * ```
581
1183
  */
582
- deleteFolder(bucketKey: BucketKey, folderPath: string): Promise<HydrousResponse<void>>;
1184
+ getMetadata(path: string): Promise<FileMetadata>;
583
1185
  /**
584
- * Create an empty folder.
1186
+ * Generate a time-limited download URL for a private file.
1187
+ * The URL can be shared externally without requiring an `X-Storage-Key`.
1188
+ *
1189
+ * > **Note:** Downloads via signed URLs bypass the server, so download stats
1190
+ * > are NOT tracked. Use `downloadUrl` for tracked downloads.
585
1191
  *
586
- * @param bucketKey Your storage bucket key (`ssk_…`)
587
- * @param folderPath Path for the new folder (e.g. `"avatars/2024/"`)
1192
+ * @param path Path to the file.
1193
+ * @param expiresIn URL lifetime in seconds (default 3600 = 1 hour).
588
1194
  *
589
1195
  * @example
590
- * await hydrous.storage.createFolder('ssk_my_bucket_key', 'avatars/2024/');
1196
+ * ```ts
1197
+ * const { signedUrl, expiresAt } = await storage.getSignedUrl('docs/invoice.pdf', 1800);
1198
+ * // Share signedUrl with the recipient — it expires in 30 minutes.
1199
+ * ```
591
1200
  */
592
- createFolder(bucketKey: BucketKey, folderPath: string): Promise<HydrousResponse<void>>;
1201
+ getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
593
1202
  /**
594
- * Move (rename) a file to a new path.
1203
+ * Change a file's visibility between public and private after upload.
595
1204
  *
596
- * @param bucketKey Your storage bucket key (`ssk_…`)
597
- * @param fromPath Current path of the file
598
- * @param toPath New path for the file
1205
+ * @example
1206
+ * ```ts
1207
+ * // Make a file public
1208
+ * const result = await storage.setVisibility('avatars/alice.jpg', true);
1209
+ * console.log(result.publicUrl); // CDN URL
1210
+ *
1211
+ * // Make a file private
1212
+ * const result = await storage.setVisibility('avatars/alice.jpg', false);
1213
+ * console.log(result.downloadUrl); // Auth-required URL
1214
+ * ```
1215
+ */
1216
+ setVisibility(path: string, isPublic: boolean): Promise<{
1217
+ path: string;
1218
+ isPublic: boolean;
1219
+ publicUrl: string | null;
1220
+ downloadUrl: string | null;
1221
+ }>;
1222
+ /**
1223
+ * Create a folder (a GCS prefix placeholder).
599
1224
  *
600
1225
  * @example
601
- * await hydrous.storage.move(
602
- * 'ssk_my_bucket_key',
603
- * 'drafts/report.pdf',
604
- * 'published/report.pdf'
605
- * );
1226
+ * ```ts
1227
+ * await storage.createFolder('uploads/2025/');
1228
+ * ```
606
1229
  */
607
- move(bucketKey: BucketKey, fromPath: string, toPath: string): Promise<HydrousResponse<void>>;
1230
+ createFolder(path: string): Promise<{
1231
+ path: string;
1232
+ }>;
608
1233
  /**
609
- * Copy a file to a new path (original is kept).
1234
+ * Delete a single file.
610
1235
  *
611
- * @param bucketKey Your storage bucket key (`ssk_…`)
612
- * @param fromPath Source path
613
- * @param toPath Destination path
1236
+ * @example
1237
+ * ```ts
1238
+ * await storage.deleteFile('avatars/old-avatar.jpg');
1239
+ * ```
1240
+ */
1241
+ deleteFile(path: string): Promise<void>;
1242
+ /**
1243
+ * Delete a folder and all its contents recursively.
614
1244
  *
615
1245
  * @example
616
- * await hydrous.storage.copy(
617
- * 'ssk_my_bucket_key',
618
- * 'templates/invoice.pdf',
619
- * 'invoices/invoice-001.pdf'
620
- * );
1246
+ * ```ts
1247
+ * await storage.deleteFolder('temp/');
1248
+ * ```
621
1249
  */
622
- copy(bucketKey: BucketKey, fromPath: string, toPath: string): Promise<HydrousResponse<void>>;
1250
+ deleteFolder(path: string): Promise<void>;
623
1251
  /**
624
- * Generate a time-limited public URL for a private file.
1252
+ * Move or rename a file.
625
1253
  *
626
- * @param bucketKey Your storage bucket key (`ssk_…`)
627
- * @param filePath Path of the file
628
- * @param options `expiresIn` seconds (default: 3600)
1254
+ * @example
1255
+ * ```ts
1256
+ * // Rename
1257
+ * await storage.move('docs/draft.pdf', 'docs/final.pdf');
1258
+ * // Move to a different folder
1259
+ * await storage.move('inbox/report.xlsx', 'archive/2025/report.xlsx');
1260
+ * ```
1261
+ */
1262
+ move(from: string, to: string): Promise<{
1263
+ from: string;
1264
+ to: string;
1265
+ }>;
1266
+ /**
1267
+ * Copy a file to a new path.
629
1268
  *
630
1269
  * @example
631
- * const { data } = await hydrous.storage.signedUrl(
632
- * 'ssk_my_bucket_key',
633
- * 'private/contract.pdf',
634
- * { expiresIn: 300 } // 5 minutes
635
- * );
636
- * console.log(data.signedUrl); // share this URL
1270
+ * ```ts
1271
+ * await storage.copy('templates/base.html', 'sites/my-site/index.html');
1272
+ * ```
637
1273
  */
638
- signedUrl(bucketKey: BucketKey, filePath: string, options?: SignedUrlOptions): Promise<HydrousResponse<SignedUrlResult>>;
1274
+ copy(from: string, to: string): Promise<{
1275
+ from: string;
1276
+ to: string;
1277
+ }>;
639
1278
  /**
640
- * Get usage and billing statistics for this bucket key.
1279
+ * Get storage statistics for your key: total files, bytes, operation counts.
641
1280
  *
642
- * @param bucketKey Your storage bucket key (`ssk_…`)
1281
+ * @example
1282
+ * ```ts
1283
+ * const stats = await storage.getStats();
1284
+ * console.log(`${stats.totalFiles} files, ${(stats.totalBytes / 1e6).toFixed(1)} MB`);
1285
+ * ```
1286
+ */
1287
+ getStats(): Promise<StorageStats>;
1288
+ /**
1289
+ * Ping the storage service. No authentication required.
643
1290
  *
644
1291
  * @example
645
- * const { data } = await hydrous.storage.stats('ssk_my_bucket_key');
646
- * console.log(`${data.totalFiles} files, ${data.totalSizeBytes} bytes stored`);
1292
+ * ```ts
1293
+ * const info = await storage.info();
1294
+ * // → { ok: true, storageRoot: 'hydrous-storage' }
1295
+ * ```
647
1296
  */
648
- stats(bucketKey: BucketKey): Promise<HydrousResponse<StorageStats>>;
1297
+ info(): Promise<{
1298
+ ok: boolean;
1299
+ storageRoot: string;
1300
+ }>;
649
1301
  }
650
1302
 
1303
+ /** Accepted data types for file uploads. */
1304
+ type UploadData = Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | Buffer;
651
1305
  /**
652
- * The root Hydrous client.
1306
+ * ScopedStorage a StorageManager pre-scoped to a specific folder prefix.
653
1307
  *
654
- * Instantiate once and reuse throughout your app.
1308
+ * All paths are automatically prepended with the scope, so you never
1309
+ * have to repeat the folder name.
655
1310
  *
656
1311
  * @example
657
- * import { HydrousClient } from 'hydrous';
1312
+ * ```ts
1313
+ * const db = createClient({ securityKey: 'sk_...' });
658
1314
  *
659
- * const hydrous = new HydrousClient({
660
- * url: 'https://api.myapp.hydrous.app',
661
- * apiKey: 'hk_live_…',
662
- * });
1315
+ * // All operations in the "avatars/" folder
1316
+ * const avatars = db.storage.scope('avatars');
1317
+ *
1318
+ * await avatars.upload(file, 'alice.jpg'); // → "avatars/alice.jpg"
1319
+ * const list = await avatars.list(); // → files under "avatars/"
1320
+ * await avatars.deleteFile('alice.jpg'); // → deletes "avatars/alice.jpg"
1321
+ * ```
663
1322
  */
664
- declare class HydrousClient {
665
- /** Authentication — sign up, sign in, sign out, session management */
666
- readonly auth: AuthClient;
667
- /** Records — insert, select, update, delete structured data */
668
- readonly records: RecordsClient;
669
- /** Analytics track events, query event history */
670
- readonly analytics: AnalyticsClient;
1323
+ declare class ScopedStorage {
1324
+ private readonly manager;
1325
+ private readonly prefix;
1326
+ constructor(manager: StorageManager, prefix: string);
1327
+ private scopedPath;
1328
+ /** Upload a file within the scoped folder. */
1329
+ upload(data: UploadData, path: string, options?: UploadOptions): Promise<UploadResult>;
1330
+ /** Upload raw JSON or text within the scoped folder. */
1331
+ uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
1332
+ /** Get a signed upload URL for a file within the scoped folder. */
1333
+ getUploadUrl(opts: {
1334
+ path: string;
1335
+ mimeType: string;
1336
+ size: number;
1337
+ isPublic?: boolean;
1338
+ overwrite?: boolean;
1339
+ }): Promise<UploadUrlResult>;
1340
+ /** Upload data directly to a signed GCS URL with optional progress tracking. */
1341
+ uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
1342
+ /** Confirm a direct upload within the scoped folder. */
1343
+ confirmUpload(opts: {
1344
+ path: string;
1345
+ mimeType: string;
1346
+ isPublic?: boolean;
1347
+ }): Promise<UploadResult>;
1348
+ /** Download a file within the scoped folder. */
1349
+ download(path: string): Promise<ArrayBuffer>;
1350
+ /** List files within the scoped folder. */
1351
+ list(opts?: ListOptions): Promise<ListResult>;
1352
+ /** Get metadata for a file within the scoped folder. */
1353
+ getMetadata(path: string): Promise<FileMetadata>;
1354
+ /** Get a time-limited signed URL for a file within the scoped folder. */
1355
+ getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
1356
+ /** Change visibility of a file within the scoped folder. */
1357
+ setVisibility(path: string, isPublic: boolean): Promise<{
1358
+ path: string;
1359
+ isPublic: boolean;
1360
+ publicUrl: string | null;
1361
+ downloadUrl: string | null;
1362
+ }>;
1363
+ /** Delete a file within the scoped folder. */
1364
+ deleteFile(path: string): Promise<void>;
1365
+ /** Delete a sub-folder within the scoped folder. */
1366
+ deleteFolder(path: string): Promise<void>;
1367
+ /** Move a file within the scoped folder. */
1368
+ move(from: string, to: string): Promise<{
1369
+ from: string;
1370
+ to: string;
1371
+ }>;
1372
+ /** Copy a file within the scoped folder. */
1373
+ copy(from: string, to: string): Promise<{
1374
+ from: string;
1375
+ to: string;
1376
+ }>;
1377
+ /** Create a sub-folder within the scoped folder. */
1378
+ createFolder(path: string): Promise<{
1379
+ path: string;
1380
+ }>;
671
1381
  /**
672
- * Storage upload, download, list, move, delete files.
1382
+ * Create a further-scoped instance nested within this scope.
673
1383
  *
674
- * Every method takes a **bucket key** (`ssk_…`) as its **first argument**.
1384
+ * @example
1385
+ * ```ts
1386
+ * const uploads = db.storage.scope('user-uploads');
1387
+ * const images = uploads.scope('images'); // → "user-uploads/images/"
1388
+ * ```
675
1389
  */
676
- readonly storage: StorageClient;
677
- constructor(config: HydrousConfig);
1390
+ scope(subPrefix: string): ScopedStorage;
678
1391
  }
679
1392
 
680
- /** Shorthand helper: creates a simple equality filter */
681
- declare function eq(field: string, value: unknown): Filter;
682
- /** Shorthand helper: creates a "not equal" filter */
683
- declare function neq(field: string, value: unknown): Filter;
684
- /** Shorthand helper: creates a "greater than" filter */
685
- declare function gt(field: string, value: unknown): Filter;
686
- /** Shorthand helper: creates a "less than" filter */
687
- declare function lt(field: string, value: unknown): Filter;
688
- /** Shorthand helper: creates an "in array" filter */
689
- declare function inArray(field: string, value: unknown[]): Filter;
690
-
691
- declare class HydrousSDKError extends Error {
692
- readonly code: string;
693
- readonly status: number | undefined;
694
- constructor(message: string, code?: string, status?: number);
1393
+ /**
1394
+ * HydrousClient the main entry point for the HydrousDB SDK.
1395
+ *
1396
+ * Create one instance per application (it is safe to share globally).
1397
+ * Every sub-client (`auth`, `records`, `analytics`, `storage`) is lazily
1398
+ * instantiated and cached on first access.
1399
+ *
1400
+ * @example
1401
+ * ```ts
1402
+ * import { createClient } from 'hydrousdb';
1403
+ *
1404
+ * const db = createClient({ securityKey: 'sk_live_...' });
1405
+ *
1406
+ * // Records
1407
+ * const posts = db.records('blog-posts');
1408
+ * const users = db.records('app-users');
1409
+ *
1410
+ * // Auth
1411
+ * const auth = db.auth('app-users');
1412
+ *
1413
+ * // Analytics
1414
+ * const stats = db.analytics('orders');
1415
+ *
1416
+ * // Storage (flat)
1417
+ * const storage = db.storage; // StorageManager
1418
+ *
1419
+ * // Storage (scoped to a folder)
1420
+ * const avatars = db.storage.scope('avatars');
1421
+ * ```
1422
+ */
1423
+ declare class HydrousClient {
1424
+ private readonly http;
1425
+ private readonly _storageKey;
1426
+ private _storage;
1427
+ private readonly _recordsCache;
1428
+ private readonly _authCache;
1429
+ private readonly _analyticsCache;
1430
+ constructor(config: HydrousConfig);
1431
+ /**
1432
+ * Get a typed records client for the given bucket.
1433
+ *
1434
+ * The generic type parameter `T` describes the shape of records in this
1435
+ * bucket. Leave it unset for a generic `Record<string, unknown>` shape.
1436
+ *
1437
+ * @param bucketKey The name of your bucket (must match what you created in the dashboard).
1438
+ *
1439
+ * @example
1440
+ * ```ts
1441
+ * interface Post { title: string; body: string; published: boolean }
1442
+ * const posts = db.records<Post>('blog-posts');
1443
+ *
1444
+ * const post = await posts.create({ title: 'Hello', body: '...', published: false });
1445
+ * // post.id, post.createdAt, post.updatedAt are added automatically
1446
+ * ```
1447
+ */
1448
+ records<T extends RecordData = RecordData>(bucketKey: string): RecordsClient<T>;
1449
+ /**
1450
+ * Get an auth client for the given user bucket.
1451
+ *
1452
+ * @param bucketKey The name of your user bucket (e.g. `"app-users"`).
1453
+ *
1454
+ * @example
1455
+ * ```ts
1456
+ * const auth = db.auth('app-users');
1457
+ * const { user, session } = await auth.login({ email: '...', password: '...' });
1458
+ * ```
1459
+ */
1460
+ auth(bucketKey: string): AuthClient;
1461
+ /**
1462
+ * Get an analytics client for the given bucket.
1463
+ *
1464
+ * @param bucketKey The name of the bucket to analyse.
1465
+ *
1466
+ * @example
1467
+ * ```ts
1468
+ * const analytics = db.analytics('orders');
1469
+ * const { count } = await analytics.count();
1470
+ * ```
1471
+ */
1472
+ analytics(bucketKey: string): AnalyticsClient;
1473
+ /**
1474
+ * The storage manager for uploading, downloading, listing, and managing files.
1475
+ *
1476
+ * Scoped to your project — you can never access another project's files.
1477
+ *
1478
+ * @example
1479
+ * ```ts
1480
+ * // Upload a file
1481
+ * const result = await db.storage.upload(file, 'images/photo.jpg', { isPublic: true });
1482
+ *
1483
+ * // Scope to a folder
1484
+ * const avatars = db.storage.scope('user-avatars');
1485
+ * await avatars.upload(blob, `${userId}.jpg`, { isPublic: true });
1486
+ * ```
1487
+ */
1488
+ get storage(): StorageManager & {
1489
+ scope: (prefix: string) => ScopedStorage;
1490
+ };
695
1491
  }
696
- declare function isHydrousError(err: unknown): err is HydrousSDKError;
697
-
698
1492
  /**
699
- * Create and return a `HydrousClient` instance.
1493
+ * Create a new HydrousDB client.
1494
+ *
1495
+ * This is the **only** export you need to get started.
1496
+ * The API URL is pre-configured — you only need your Security Key.
1497
+ *
1498
+ * @param config.securityKey Your project's Security Key from https://hydrousdb.com/dashboard
1499
+ * @param config.baseUrl (Optional) Override the API base URL.
700
1500
  *
701
1501
  * @example
702
- * import { createClient } from 'hydrous';
1502
+ * ```ts
1503
+ * import { createClient } from 'hydrousdb';
703
1504
  *
704
- * const hydrous = createClient({
705
- * url: 'https://api.myapp.hydrous.app',
706
- * apiKey: 'hk_live_…',
1505
+ * const db = createClient({
1506
+ * securityKey: process.env.HYDROUS_SECURITY_KEY!,
707
1507
  * });
1508
+ * ```
708
1509
  */
709
1510
  declare function createClient(config: HydrousConfig): HydrousClient;
710
1511
 
711
- export { AnalyticsClient, type AnalyticsEvent, type AnalyticsQueryOptions, AuthClient, type AuthSession, type AuthUser, type BatchDownloadFile, type BatchDownloadOptions, type BatchUploadOptions, type BatchUploadResult, type BucketKey, type DownloadProgress, type FileMetadata, type Filter, type FilterOperator, HydrousClient, type HydrousConfig, type HydrousError, type HydrousResponse, HydrousSDKError, type ListOptions, type ListResult, type QueryOptions, type RecordResponse, RecordsClient, type SignInOptions, type SignUpOptions, type SignedUrlOptions, type SignedUrlResult, type SingleRecordResponse, StorageClient, type StorageItem, type StorageStats, type TrackEventOptions, type UploadOptions, type UploadProgress, type UploadResult, type UploadStage, createClient, eq, gt, inArray, isHydrousError, lt, neq };
1512
+ declare class HydrousError extends Error {
1513
+ readonly code: string;
1514
+ readonly status?: number;
1515
+ readonly requestId?: string;
1516
+ readonly details?: string[];
1517
+ constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
1518
+ toString(): string;
1519
+ }
1520
+ declare class AuthError extends HydrousError {
1521
+ constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
1522
+ }
1523
+ declare class RecordError extends HydrousError {
1524
+ constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
1525
+ }
1526
+ declare class StorageError extends HydrousError {
1527
+ constructor(message: string, code: string, status?: number, requestId?: string);
1528
+ }
1529
+ declare class AnalyticsError extends HydrousError {
1530
+ constructor(message: string, code: string, status?: number, requestId?: string);
1531
+ }
1532
+ declare class ValidationError extends HydrousError {
1533
+ constructor(message: string, details?: string[]);
1534
+ }
1535
+ declare class NetworkError extends HydrousError {
1536
+ readonly cause?: unknown;
1537
+ constructor(message: string, cause?: unknown);
1538
+ }
1539
+
1540
+ export { type Aggregation, AnalyticsClient, AnalyticsError, type AnalyticsFilter, type AnalyticsQuery, type AnalyticsResult, AuthClient, AuthError, type AuthResult, type BatchUploadItem, type BatchUploadUrlResult, type ChangePasswordOptions, type CountResult, type CrossBucketRow, type DateRange, type DistributionRow, type FieldStats, type FieldTimeSeriesRow, type FileEntry, type FileMetadata, type Granularity, HydrousClient, type HydrousConfig, HydrousError, type ListOptions, type ListResult, type ListUsersOptions, type ListUsersResult, type LoginOptions, type MetricDefinition, type MultiMetricResult, NetworkError, type PatchRecordOptions, type QueryFilter, type QueryOptions, type QueryResult, type QueryType, type RecordData, RecordError, type RecordHistoryEntry, type RecordResult, RecordsClient, ScopedStorage, type Session, type SignedUrlResult, type SignupOptions, type SortOrder, StorageError, StorageManager, type StorageStats, type StorageStatsResult, type SumRow, type TimeSeriesRow, type TopNRow, type UpdateUserOptions, type UploadOptions, type UploadResult, type UploadUrlResult, type UserRecord, ValidationError, createClient };