hydrousdb 2.0.3 → 3.0.1

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.
@@ -0,0 +1,1080 @@
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;
7
+ }
8
+ declare class HttpClient {
9
+ private readonly baseUrl;
10
+ constructor(baseUrl: string);
11
+ request<T = unknown>(path: string, apiKeyOrOpts?: string | RequestOptions, opts?: RequestOptions): Promise<T>;
12
+ get<T = unknown>(path: string, apiKey?: string, headers?: Record<string, string>): Promise<T>;
13
+ post<T = unknown>(path: string, apiKey?: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
14
+ put<T = unknown>(path: string, apiKey?: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
15
+ patch<T = unknown>(path: string, apiKey?: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
16
+ delete<T = unknown>(path: string, apiKey?: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
17
+ putToSignedUrl(signedUrl: string, data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
18
+ }
19
+
20
+ /**
21
+ * Storage keys map — one named key per storage bucket/permission scope.
22
+ * Keys start with `ssk_`. You can define as many as you need.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * storageKeys: {
27
+ * main: 'ssk_main_…',
28
+ * avatars: 'ssk_avatars_…',
29
+ * documents: 'ssk_docs_…',
30
+ * }
31
+ * ```
32
+ */
33
+ type StorageKeys = Record<string, string>;
34
+ interface HydrousConfig {
35
+ /**
36
+ * Auth service key (starts with `hk_auth_`).
37
+ * Used exclusively for all `/auth/*` routes — signup, login, sessions, etc.
38
+ * Obtain from your dashboard at https://hydrousdb.com/dashboard.
39
+ */
40
+ authKey: string;
41
+ /**
42
+ * Bucket security key (starts with `hk_bucket_`).
43
+ * Used for all `/records/*` and `/analytics/*` routes.
44
+ * Obtain from your dashboard at https://hydrousdb.com/dashboard.
45
+ */
46
+ bucketSecurityKey: string;
47
+ /**
48
+ * Named storage keys (each starts with `ssk_`).
49
+ * Define one key per storage bucket/permission scope.
50
+ * Pass the name of the key you want when calling `db.storage('keyName')`.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * storageKeys: {
55
+ * main: 'ssk_main_…', // default general-purpose bucket
56
+ * avatars: 'ssk_avatars_…', // user avatar uploads
57
+ * documents: 'ssk_docs_…', // private documents
58
+ * }
59
+ * ```
60
+ */
61
+ storageKeys: StorageKeys;
62
+ /**
63
+ * Override the API base URL. Defaults to the official HydrousDB endpoint.
64
+ * You almost never need to set this.
65
+ */
66
+ baseUrl?: string;
67
+ }
68
+ interface SignupOptions {
69
+ email: string;
70
+ password: string;
71
+ fullName?: string;
72
+ [key: string]: unknown;
73
+ }
74
+ interface LoginOptions {
75
+ email: string;
76
+ password: string;
77
+ }
78
+ interface UserRecord {
79
+ id: string;
80
+ email: string;
81
+ fullName?: string | null;
82
+ emailVerified: boolean;
83
+ accountStatus: 'active' | 'locked' | 'suspended';
84
+ role: 'user' | 'admin';
85
+ createdAt: number;
86
+ updatedAt: number;
87
+ metadata?: Record<string, unknown>;
88
+ [key: string]: unknown;
89
+ }
90
+ interface Session {
91
+ sessionId: string;
92
+ userId: string;
93
+ bucketId: string;
94
+ createdAt: number;
95
+ expiresAt: number;
96
+ refreshToken: string;
97
+ refreshExpiresAt: number;
98
+ }
99
+ interface AuthResult {
100
+ user: UserRecord;
101
+ session: Session;
102
+ }
103
+ interface UpdateUserOptions {
104
+ sessionId: string;
105
+ userId: string;
106
+ data: Partial<Omit<UserRecord, 'id' | 'email' | 'createdAt'>>;
107
+ }
108
+ interface ChangePasswordOptions {
109
+ sessionId: string;
110
+ userId: string;
111
+ currentPassword: string;
112
+ newPassword: string;
113
+ }
114
+ interface ListUsersOptions {
115
+ sessionId: string;
116
+ limit?: number;
117
+ offset?: number;
118
+ }
119
+ interface ListUsersResult {
120
+ users: UserRecord[];
121
+ total: number;
122
+ limit: number;
123
+ offset: number;
124
+ }
125
+ type RecordData = Record<string, unknown>;
126
+ interface RecordResult {
127
+ id: string;
128
+ createdAt?: number;
129
+ updatedAt?: number;
130
+ [key: string]: unknown;
131
+ }
132
+ interface QueryFilter {
133
+ field: string;
134
+ op: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'CONTAINS';
135
+ value: string | number | boolean;
136
+ }
137
+ interface QueryOptions {
138
+ filters?: QueryFilter[];
139
+ fields?: string;
140
+ limit?: number;
141
+ offset?: number;
142
+ orderBy?: string;
143
+ order?: 'asc' | 'desc';
144
+ startAfter?: string;
145
+ startAt?: string;
146
+ endAt?: string;
147
+ dateRange?: DateRange;
148
+ }
149
+ interface QueryResult<T = RecordData> {
150
+ records: (T & RecordResult)[];
151
+ total?: number;
152
+ hasMore?: boolean;
153
+ nextCursor?: string;
154
+ }
155
+ interface PatchRecordOptions {
156
+ merge?: boolean;
157
+ }
158
+ interface RecordHistoryEntry {
159
+ id: string;
160
+ version: number;
161
+ createdAt: number;
162
+ data: RecordData;
163
+ }
164
+ interface UploadOptions {
165
+ /** Set to true to make the file publicly accessible without authentication. */
166
+ isPublic?: boolean;
167
+ /** Set to true to overwrite an existing file at the same path. */
168
+ overwrite?: boolean;
169
+ /** MIME type of the file. Auto-detected if omitted. */
170
+ mimeType?: string;
171
+ /** TTL in seconds for the signed upload URL (default 900 = 15 min). */
172
+ expiresInSeconds?: number;
173
+ }
174
+ interface UploadResult {
175
+ path: string;
176
+ mimeType: string;
177
+ size: number;
178
+ isPublic: boolean;
179
+ publicUrl: string | null;
180
+ downloadUrl: string | null;
181
+ }
182
+ interface UploadUrlResult {
183
+ uploadUrl: string;
184
+ path: string;
185
+ mimeType: string;
186
+ expiresAt: string;
187
+ expiresIn: number;
188
+ }
189
+ interface ListOptions {
190
+ prefix?: string;
191
+ limit?: number;
192
+ cursor?: string;
193
+ recursive?: boolean;
194
+ }
195
+ interface FileEntry {
196
+ name: string;
197
+ path: string;
198
+ size?: number;
199
+ mimeType?: string;
200
+ isPublic?: boolean;
201
+ publicUrl?: string | null;
202
+ downloadUrl?: string | null;
203
+ updatedAt?: string;
204
+ }
205
+ interface ListResult {
206
+ files: FileEntry[];
207
+ folders: string[];
208
+ hasMore: boolean;
209
+ nextCursor?: string;
210
+ }
211
+ interface FileMetadata {
212
+ path: string;
213
+ size: number;
214
+ mimeType: string;
215
+ isPublic: boolean;
216
+ publicUrl: string | null;
217
+ downloadUrl: string | null;
218
+ createdAt?: string;
219
+ updatedAt?: string;
220
+ }
221
+ interface SignedUrlResult {
222
+ signedUrl: string;
223
+ expiresAt: string;
224
+ expiresIn: number;
225
+ path: string;
226
+ }
227
+ interface StorageStats {
228
+ totalFiles: number;
229
+ totalBytes: number;
230
+ uploadCount: number;
231
+ downloadCount: number;
232
+ deleteCount: number;
233
+ }
234
+ interface BatchUploadItem {
235
+ path: string;
236
+ mimeType: string;
237
+ size: number;
238
+ isPublic?: boolean;
239
+ overwrite?: boolean;
240
+ }
241
+ interface BatchUploadUrlResult {
242
+ files: Array<UploadUrlResult & {
243
+ index: number;
244
+ }>;
245
+ }
246
+ type QueryType = 'count' | 'distribution' | 'sum' | 'timeSeries' | 'fieldTimeSeries' | 'topN' | 'stats' | 'records' | 'multiMetric' | 'storageStats' | 'crossBucket';
247
+ type Aggregation = 'sum' | 'avg' | 'min' | 'max' | 'count';
248
+ type Granularity = 'hour' | 'day' | 'week' | 'month' | 'year';
249
+ type SortOrder = 'asc' | 'desc';
250
+ interface DateRange {
251
+ /** Start timestamp in milliseconds (Unix epoch). */
252
+ start?: number;
253
+ /** End timestamp in milliseconds (Unix epoch). */
254
+ end?: number;
255
+ }
256
+ interface AnalyticsFilter {
257
+ field: string;
258
+ op: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'CONTAINS';
259
+ value: string | number | boolean;
260
+ }
261
+ interface MetricDefinition {
262
+ /** Field name in your records to aggregate. */
263
+ field: string;
264
+ /** Alias used in the result object. Must be a safe identifier. */
265
+ name: string;
266
+ /** Aggregation function. Defaults to 'count'. */
267
+ aggregation?: Aggregation;
268
+ }
269
+ interface AnalyticsQuery {
270
+ queryType: QueryType;
271
+ dateRange?: DateRange;
272
+ field?: string;
273
+ groupBy?: string;
274
+ labelField?: string;
275
+ aggregation?: Aggregation;
276
+ granularity?: Granularity;
277
+ filters?: AnalyticsFilter[];
278
+ selectFields?: string[];
279
+ limit?: number;
280
+ offset?: number;
281
+ orderBy?: string;
282
+ order?: SortOrder;
283
+ n?: number;
284
+ metrics?: MetricDefinition[];
285
+ /** For crossBucket queries: list of bucket names to compare. */
286
+ bucketKeys?: string[];
287
+ }
288
+ interface AnalyticsResult<T = unknown> {
289
+ queryType: QueryType;
290
+ data: T;
291
+ }
292
+ interface CountResult {
293
+ count: number;
294
+ }
295
+ interface DistributionRow {
296
+ value: string | number;
297
+ count: number;
298
+ }
299
+ interface SumRow {
300
+ group?: string | number;
301
+ sum: number;
302
+ }
303
+ interface TimeSeriesRow {
304
+ date: string;
305
+ count: number;
306
+ }
307
+ interface FieldTimeSeriesRow {
308
+ date: string;
309
+ value: number;
310
+ }
311
+ interface TopNRow {
312
+ value: string | number;
313
+ label?: string;
314
+ count: number;
315
+ }
316
+ interface FieldStats {
317
+ min: number;
318
+ max: number;
319
+ avg: number;
320
+ sum: number;
321
+ count: number;
322
+ stddev?: number;
323
+ }
324
+ interface MultiMetricResult {
325
+ [metricName: string]: number;
326
+ }
327
+ interface StorageStatsResult {
328
+ totalRecords: number;
329
+ totalBytes: number;
330
+ avgBytes: number;
331
+ minBytes: number;
332
+ maxBytes: number;
333
+ }
334
+ interface CrossBucketRow {
335
+ bucket: string;
336
+ value: number;
337
+ }
338
+
339
+ /**
340
+ * AuthClient — full user authentication for a single bucket.
341
+ * Uses the `authKey` (`hk_auth_…`) sent via `X-Api-Key` header.
342
+ *
343
+ * @example
344
+ * ```ts
345
+ * const db = createClient({ authKey: 'hk_auth_…', bucketSecurityKey: '…', storageKeys: { main: '…' } });
346
+ * const auth = db.auth('my-app-users');
347
+ * const { user, session } = await auth.signup({ email: 'alice@example.com', password: 'hunter2' });
348
+ * ```
349
+ */
350
+ declare class AuthClient {
351
+ private readonly http;
352
+ private readonly authKey;
353
+ private readonly basePath;
354
+ constructor(http: HttpClient, authKey: string, bucketKey: string);
355
+ private post;
356
+ private get;
357
+ private patch;
358
+ private delete;
359
+ signup(options: SignupOptions): Promise<AuthResult>;
360
+ login(options: LoginOptions): Promise<AuthResult>;
361
+ logout({ sessionId }: {
362
+ sessionId: string;
363
+ }): Promise<void>;
364
+ refreshSession({ refreshToken }: {
365
+ refreshToken: string;
366
+ }): Promise<Session>;
367
+ getUser({ userId }: {
368
+ userId: string;
369
+ }): Promise<UserRecord>;
370
+ updateUser(options: UpdateUserOptions): Promise<UserRecord>;
371
+ deleteUser({ sessionId, userId }: {
372
+ sessionId: string;
373
+ userId: string;
374
+ }): Promise<void>;
375
+ listUsers(options: ListUsersOptions): Promise<ListUsersResult>;
376
+ hardDeleteUser({ sessionId, userId }: {
377
+ sessionId: string;
378
+ userId: string;
379
+ }): Promise<void>;
380
+ bulkDeleteUsers({ sessionId, userIds }: {
381
+ sessionId: string;
382
+ userIds: string[];
383
+ }): Promise<{
384
+ deleted: number;
385
+ failed: string[];
386
+ }>;
387
+ lockAccount({ sessionId, userId, duration }: {
388
+ sessionId: string;
389
+ userId: string;
390
+ duration?: number;
391
+ }): Promise<{
392
+ lockedUntil: number;
393
+ unlockTime: string;
394
+ }>;
395
+ unlockAccount({ sessionId, userId }: {
396
+ sessionId: string;
397
+ userId: string;
398
+ }): Promise<void>;
399
+ changePassword(options: ChangePasswordOptions): Promise<void>;
400
+ requestPasswordReset({ email }: {
401
+ email: string;
402
+ }): Promise<void>;
403
+ confirmPasswordReset({ resetToken, newPassword }: {
404
+ resetToken: string;
405
+ newPassword: string;
406
+ }): Promise<void>;
407
+ requestEmailVerification({ userId }: {
408
+ userId: string;
409
+ }): Promise<void>;
410
+ confirmEmailVerification({ verifyToken }: {
411
+ verifyToken: string;
412
+ }): Promise<void>;
413
+ }
414
+
415
+ /**
416
+ * RecordsClient — CRUD + query for a single bucket.
417
+ * Uses the `bucketSecurityKey` (`hk_bucket_…`) sent via `X-Api-Key` header.
418
+ *
419
+ * @example
420
+ * ```ts
421
+ * const db = createClient({ authKey: '…', bucketSecurityKey: 'hk_bucket_…', storageKeys: { main: '…' } });
422
+ * const posts = db.records('blog-posts');
423
+ * const post = await posts.create({ title: 'Hello World', status: 'draft' });
424
+ * ```
425
+ */
426
+ declare class RecordsClient<T extends RecordData = RecordData> {
427
+ private readonly http;
428
+ private readonly bucketKey;
429
+ private readonly basePath;
430
+ private readonly bucketKey_;
431
+ constructor(http: HttpClient, bucketSecurityKey: string, bucketKey: string);
432
+ private get key();
433
+ create(data: T): Promise<T & RecordResult>;
434
+ get(id: string): Promise<T & RecordResult>;
435
+ set(id: string, data: T): Promise<T & RecordResult>;
436
+ patch(id: string, data: Partial<T>, options?: PatchRecordOptions): Promise<T & RecordResult>;
437
+ delete(id: string): Promise<void>;
438
+ batchCreate(items: T[]): Promise<(T & RecordResult)[]>;
439
+ batchDelete(ids: string[]): Promise<{
440
+ deleted: number;
441
+ failed: string[];
442
+ }>;
443
+ query(options?: QueryOptions): Promise<QueryResult<T>>;
444
+ getAll(options?: Omit<QueryOptions, 'filters'>): Promise<(T & RecordResult)[]>;
445
+ count(filters?: QueryOptions['filters']): Promise<number>;
446
+ getHistory(id: string): Promise<RecordHistoryEntry[]>;
447
+ restoreVersion(id: string, version: number): Promise<T & RecordResult>;
448
+ }
449
+
450
+ /**
451
+ * AnalyticsClient — BigQuery-powered aggregations for a bucket.
452
+ * Uses the `bucketSecurityKey` (`hk_bucket_…`) sent via `X-Api-Key` header.
453
+ *
454
+ * @example
455
+ * ```ts
456
+ * const db = createClient({ authKey: '…', bucketSecurityKey: 'hk_bucket_…', storageKeys: { main: '…' } });
457
+ * const analytics = db.analytics('orders');
458
+ * const { count } = await analytics.count();
459
+ * ```
460
+ */
461
+ declare class AnalyticsClient {
462
+ private readonly http;
463
+ private readonly bucketSecurityKey;
464
+ private readonly basePath;
465
+ constructor(http: HttpClient, bucketSecurityKey: string, bucketKey: string);
466
+ private run;
467
+ count(opts?: {
468
+ dateRange?: DateRange;
469
+ }): Promise<CountResult>;
470
+ distribution(opts: {
471
+ field: string;
472
+ limit?: number;
473
+ order?: SortOrder;
474
+ dateRange?: DateRange;
475
+ }): Promise<DistributionRow[]>;
476
+ sum(opts: {
477
+ field: string;
478
+ groupBy?: string;
479
+ limit?: number;
480
+ dateRange?: DateRange;
481
+ }): Promise<SumRow[]>;
482
+ timeSeries(opts?: {
483
+ granularity?: Granularity;
484
+ dateRange?: DateRange;
485
+ }): Promise<TimeSeriesRow[]>;
486
+ fieldTimeSeries(opts: {
487
+ field: string;
488
+ aggregation?: Aggregation;
489
+ granularity?: Granularity;
490
+ dateRange?: DateRange;
491
+ }): Promise<FieldTimeSeriesRow[]>;
492
+ topN(opts: {
493
+ field: string;
494
+ n?: number;
495
+ labelField?: string;
496
+ order?: SortOrder;
497
+ dateRange?: DateRange;
498
+ }): Promise<TopNRow[]>;
499
+ stats(opts: {
500
+ field: string;
501
+ dateRange?: DateRange;
502
+ }): Promise<FieldStats>;
503
+ records<T extends RecordData = RecordData>(opts?: {
504
+ filters?: AnalyticsFilter[];
505
+ selectFields?: string[];
506
+ limit?: number;
507
+ offset?: number;
508
+ orderBy?: string;
509
+ order?: SortOrder;
510
+ dateRange?: DateRange;
511
+ }): Promise<(T & RecordResult)[]>;
512
+ multiMetric(opts: {
513
+ metrics: MetricDefinition[];
514
+ dateRange?: DateRange;
515
+ }): Promise<MultiMetricResult>;
516
+ storageStats(opts?: {
517
+ dateRange?: DateRange;
518
+ }): Promise<StorageStatsResult>;
519
+ crossBucket(opts: {
520
+ bucketKeys: string[];
521
+ field: string;
522
+ aggregation?: Aggregation;
523
+ dateRange?: DateRange;
524
+ }): Promise<CrossBucketRow[]>;
525
+ query<T = unknown>(query: AnalyticsQuery): Promise<AnalyticsResult<T>>;
526
+ }
527
+
528
+ /**
529
+ * StorageManager — upload, download, list, move, copy, and delete files.
530
+ * Uses an `X-Storage-Key` (`ssk_…`) header — separate from auth and bucket keys.
531
+ * Each key is scoped to a specific storage bucket and permission set.
532
+ *
533
+ * Get a StorageManager via `db.storage('keyName')` where the key name
534
+ * matches one of the keys you defined in `storageKeys` when calling `createClient`.
535
+ *
536
+ * @example
537
+ * ```ts
538
+ * const db = createClient({ authKey: '…', bucketSecurityKey: '…', storageKeys: { avatars: 'ssk_avatars_…' } });
539
+ * const avatars = db.storage('avatars');
540
+ * const result = await avatars.upload(file, 'alice.jpg', { isPublic: true });
541
+ * ```
542
+ */
543
+ declare class StorageManager {
544
+ private readonly http;
545
+ private readonly storageKey;
546
+ private readonly basePath;
547
+ constructor(http: HttpClient, storageKey: string);
548
+ /** Headers for all storage requests — uses X-Storage-Key, not X-Api-Key. */
549
+ private get authHeaders();
550
+ /**
551
+ * Upload a file to storage in one step (server-buffered, up to 500 MB).
552
+ * For files >10 MB or when you need upload progress, use `getUploadUrl()` instead.
553
+ *
554
+ * @param data File data as a Blob, Buffer, Uint8Array, or ArrayBuffer.
555
+ * @param path Destination path in your storage (e.g. `"avatars/alice.jpg"`).
556
+ * @param options Upload options: isPublic, overwrite, mimeType.
557
+ *
558
+ * @example
559
+ * ```ts
560
+ * // Upload a public avatar
561
+ * const result = await storage.upload(file, 'avatars/alice.jpg', { isPublic: true });
562
+ * console.log(result.publicUrl); // → https://...
563
+ *
564
+ * // Upload a private document
565
+ * const result = await storage.upload(pdfBuffer, 'docs/contract.pdf');
566
+ * console.log(result.downloadUrl); // → /storage/download/docs/contract.pdf
567
+ * ```
568
+ */
569
+ upload(data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
570
+ /**
571
+ * Upload raw JSON or plain text data as a file.
572
+ *
573
+ * @example
574
+ * ```ts
575
+ * const result = await storage.uploadRaw(
576
+ * { config: { theme: 'dark' } },
577
+ * 'settings/user-config.json',
578
+ * { isPublic: false },
579
+ * );
580
+ * ```
581
+ */
582
+ uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
583
+ /**
584
+ * Step 1 of the recommended upload flow.
585
+ * Get a signed URL to upload directly to GCS from the client (supports progress).
586
+ *
587
+ * @example
588
+ * ```ts
589
+ * const { uploadUrl, path: confirmedPath } = await storage.getUploadUrl({
590
+ * path: 'videos/intro.mp4',
591
+ * mimeType: 'video/mp4',
592
+ * size: file.size,
593
+ * isPublic: true,
594
+ * });
595
+ *
596
+ * // Upload using XHR for progress tracking
597
+ * await storage.uploadToSignedUrl(uploadUrl, file, 'video/mp4', (pct) => {
598
+ * console.log(`${pct}% uploaded`);
599
+ * });
600
+ *
601
+ * // Step 3: confirm
602
+ * const result = await storage.confirmUpload({ path: confirmedPath, mimeType: 'video/mp4', isPublic: true });
603
+ * ```
604
+ */
605
+ getUploadUrl(opts: {
606
+ path: string;
607
+ mimeType: string;
608
+ size: number;
609
+ isPublic?: boolean;
610
+ overwrite?: boolean;
611
+ expiresInSeconds?: number;
612
+ }): Promise<UploadUrlResult>;
613
+ /**
614
+ * Upload data directly to a signed GCS URL (no auth headers needed).
615
+ * Optionally tracks progress via a callback.
616
+ *
617
+ * @param signedUrl The URL returned by `getUploadUrl()`.
618
+ * @param data File data.
619
+ * @param mimeType Must match what was used in `getUploadUrl()`.
620
+ * @param onProgress Optional callback called with 0–100 progress percentage.
621
+ */
622
+ uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
623
+ /**
624
+ * Step 3 of the recommended upload flow.
625
+ * Confirm a direct upload and register metadata on the server.
626
+ *
627
+ * @example
628
+ * ```ts
629
+ * const result = await storage.confirmUpload({
630
+ * path: 'videos/intro.mp4',
631
+ * mimeType: 'video/mp4',
632
+ * isPublic: true,
633
+ * });
634
+ * console.log(result.publicUrl);
635
+ * ```
636
+ */
637
+ confirmUpload(opts: {
638
+ path: string;
639
+ mimeType: string;
640
+ isPublic?: boolean;
641
+ }): Promise<UploadResult>;
642
+ /**
643
+ * Get signed upload URLs for multiple files at once.
644
+ *
645
+ * @example
646
+ * ```ts
647
+ * const { files } = await storage.getBatchUploadUrls([
648
+ * { path: 'images/photo1.jpg', mimeType: 'image/jpeg', size: 204800 },
649
+ * { path: 'images/photo2.jpg', mimeType: 'image/jpeg', size: 153600 },
650
+ * ]);
651
+ *
652
+ * // Upload each file and confirm
653
+ * for (const f of files) {
654
+ * await storage.uploadToSignedUrl(f.uploadUrl, blobs[f.index], f.mimeType);
655
+ * await storage.confirmUpload({ path: f.path, mimeType: f.mimeType });
656
+ * }
657
+ * ```
658
+ */
659
+ getBatchUploadUrls(files: BatchUploadItem[]): Promise<BatchUploadUrlResult>;
660
+ /**
661
+ * Confirm multiple direct uploads at once.
662
+ */
663
+ batchConfirmUploads(items: Array<{
664
+ path: string;
665
+ mimeType: string;
666
+ isPublic?: boolean;
667
+ }>): Promise<UploadResult[]>;
668
+ /**
669
+ * Download a private file as an ArrayBuffer.
670
+ * For public files, use the `publicUrl` directly — no SDK needed.
671
+ *
672
+ * @example
673
+ * ```ts
674
+ * const buffer = await storage.download('docs/contract.pdf');
675
+ * const blob = new Blob([buffer], { type: 'application/pdf' });
676
+ * // Open in browser:
677
+ * window.open(URL.createObjectURL(blob));
678
+ * ```
679
+ */
680
+ download(path: string): Promise<ArrayBuffer>;
681
+ /**
682
+ * Download multiple files at once, returned as a JSON map of `{ path: base64 }`.
683
+ *
684
+ * @example
685
+ * ```ts
686
+ * const files = await storage.batchDownload(['docs/a.pdf', 'docs/b.pdf']);
687
+ * ```
688
+ */
689
+ batchDownload(paths: string[]): Promise<Record<string, string>>;
690
+ /**
691
+ * List files and folders at a given path prefix.
692
+ *
693
+ * @example
694
+ * ```ts
695
+ * // List everything in the root
696
+ * const { files, folders } = await storage.list();
697
+ *
698
+ * // List a specific folder
699
+ * const { files, folders, hasMore, nextCursor } = await storage.list({
700
+ * prefix: 'avatars/',
701
+ * limit: 20,
702
+ * });
703
+ *
704
+ * // Next page
705
+ * const page2 = await storage.list({ prefix: 'avatars/', cursor: nextCursor });
706
+ * ```
707
+ */
708
+ list(opts?: ListOptions): Promise<ListResult>;
709
+ /**
710
+ * Get metadata for a file (size, MIME type, visibility, URLs).
711
+ *
712
+ * @example
713
+ * ```ts
714
+ * const meta = await storage.getMetadata('avatars/alice.jpg');
715
+ * console.log(meta.size, meta.isPublic, meta.publicUrl);
716
+ * ```
717
+ */
718
+ getMetadata(path: string): Promise<FileMetadata>;
719
+ /**
720
+ * Generate a time-limited download URL for a private file.
721
+ * The URL can be shared externally without requiring an `X-Storage-Key`.
722
+ *
723
+ * > **Note:** Downloads via signed URLs bypass the server, so download stats
724
+ * > are NOT tracked. Use `downloadUrl` for tracked downloads.
725
+ *
726
+ * @param path Path to the file.
727
+ * @param expiresIn URL lifetime in seconds (default 3600 = 1 hour).
728
+ *
729
+ * @example
730
+ * ```ts
731
+ * const { signedUrl, expiresAt } = await storage.getSignedUrl('docs/invoice.pdf', 1800);
732
+ * // Share signedUrl with the recipient — it expires in 30 minutes.
733
+ * ```
734
+ */
735
+ getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
736
+ /**
737
+ * Change a file's visibility between public and private after upload.
738
+ *
739
+ * @example
740
+ * ```ts
741
+ * // Make a file public
742
+ * const result = await storage.setVisibility('avatars/alice.jpg', true);
743
+ * console.log(result.publicUrl); // CDN URL
744
+ *
745
+ * // Make a file private
746
+ * const result = await storage.setVisibility('avatars/alice.jpg', false);
747
+ * console.log(result.downloadUrl); // Auth-required URL
748
+ * ```
749
+ */
750
+ setVisibility(path: string, isPublic: boolean): Promise<{
751
+ path: string;
752
+ isPublic: boolean;
753
+ publicUrl: string | null;
754
+ downloadUrl: string | null;
755
+ }>;
756
+ /**
757
+ * Create a folder (a GCS prefix placeholder).
758
+ *
759
+ * @example
760
+ * ```ts
761
+ * await storage.createFolder('uploads/2025/');
762
+ * ```
763
+ */
764
+ createFolder(path: string): Promise<{
765
+ path: string;
766
+ }>;
767
+ /**
768
+ * Delete a single file.
769
+ *
770
+ * @example
771
+ * ```ts
772
+ * await storage.deleteFile('avatars/old-avatar.jpg');
773
+ * ```
774
+ */
775
+ deleteFile(path: string): Promise<void>;
776
+ /**
777
+ * Delete a folder and all its contents recursively.
778
+ *
779
+ * @example
780
+ * ```ts
781
+ * await storage.deleteFolder('temp/');
782
+ * ```
783
+ */
784
+ deleteFolder(path: string): Promise<void>;
785
+ /**
786
+ * Move or rename a file.
787
+ *
788
+ * @example
789
+ * ```ts
790
+ * // Rename
791
+ * await storage.move('docs/draft.pdf', 'docs/final.pdf');
792
+ * // Move to a different folder
793
+ * await storage.move('inbox/report.xlsx', 'archive/2025/report.xlsx');
794
+ * ```
795
+ */
796
+ move(from: string, to: string): Promise<{
797
+ from: string;
798
+ to: string;
799
+ }>;
800
+ /**
801
+ * Copy a file to a new path.
802
+ *
803
+ * @example
804
+ * ```ts
805
+ * await storage.copy('templates/base.html', 'sites/my-site/index.html');
806
+ * ```
807
+ */
808
+ copy(from: string, to: string): Promise<{
809
+ from: string;
810
+ to: string;
811
+ }>;
812
+ /**
813
+ * Get storage statistics for your key: total files, bytes, operation counts.
814
+ *
815
+ * @example
816
+ * ```ts
817
+ * const stats = await storage.getStats();
818
+ * console.log(`${stats.totalFiles} files, ${(stats.totalBytes / 1e6).toFixed(1)} MB`);
819
+ * ```
820
+ */
821
+ getStats(): Promise<StorageStats>;
822
+ /**
823
+ * Ping the storage service. No authentication required.
824
+ *
825
+ * @example
826
+ * ```ts
827
+ * const info = await storage.info();
828
+ * // → { ok: true, storageRoot: 'hydrous-storage' }
829
+ * ```
830
+ */
831
+ info(): Promise<{
832
+ ok: boolean;
833
+ storageRoot: string;
834
+ }>;
835
+ }
836
+
837
+ /**
838
+ * ScopedStorage — a StorageManager pre-scoped to a specific folder prefix.
839
+ *
840
+ * All paths are automatically prepended with the scope, so you never
841
+ * have to repeat the folder name.
842
+ *
843
+ * @example
844
+ * ```ts
845
+ * const db = createClient({ securityKey: 'sk_...' });
846
+ *
847
+ * // All operations in the "avatars/" folder
848
+ * const avatars = db.storage.scope('avatars');
849
+ *
850
+ * await avatars.upload(file, 'alice.jpg'); // → "avatars/alice.jpg"
851
+ * const list = await avatars.list(); // → files under "avatars/"
852
+ * await avatars.deleteFile('alice.jpg'); // → deletes "avatars/alice.jpg"
853
+ * ```
854
+ */
855
+ declare class ScopedStorage {
856
+ private readonly manager;
857
+ private readonly prefix;
858
+ constructor(manager: StorageManager, prefix: string);
859
+ private scopedPath;
860
+ /** Upload a file within the scoped folder. */
861
+ upload(data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
862
+ /** Upload raw JSON or text within the scoped folder. */
863
+ uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
864
+ /** Get a signed upload URL for a file within the scoped folder. */
865
+ getUploadUrl(opts: {
866
+ path: string;
867
+ mimeType: string;
868
+ size: number;
869
+ isPublic?: boolean;
870
+ overwrite?: boolean;
871
+ }): Promise<UploadUrlResult>;
872
+ /** Confirm a direct upload within the scoped folder. */
873
+ confirmUpload(opts: {
874
+ path: string;
875
+ mimeType: string;
876
+ isPublic?: boolean;
877
+ }): Promise<UploadResult>;
878
+ /** Download a file within the scoped folder. */
879
+ download(path: string): Promise<ArrayBuffer>;
880
+ /**
881
+ * List files within the scoped folder.
882
+ * `prefix` in options is relative to the scope.
883
+ */
884
+ list(opts?: ListOptions): Promise<ListResult>;
885
+ /** Get metadata for a file within the scoped folder. */
886
+ getMetadata(path: string): Promise<FileMetadata>;
887
+ /** Get a time-limited signed URL for a file within the scoped folder. */
888
+ getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
889
+ /** Change visibility of a file within the scoped folder. */
890
+ setVisibility(path: string, isPublic: boolean): Promise<{
891
+ path: string;
892
+ isPublic: boolean;
893
+ publicUrl: string | null;
894
+ downloadUrl: string | null;
895
+ }>;
896
+ /** Delete a file within the scoped folder. */
897
+ deleteFile(path: string): Promise<void>;
898
+ /** Delete a sub-folder within the scoped folder. */
899
+ deleteFolder(path: string): Promise<void>;
900
+ /** Move a file within the scoped folder. */
901
+ move(from: string, to: string): Promise<{
902
+ from: string;
903
+ to: string;
904
+ }>;
905
+ /** Copy a file within the scoped folder. */
906
+ copy(from: string, to: string): Promise<{
907
+ from: string;
908
+ to: string;
909
+ }>;
910
+ /** Create a sub-folder within the scoped folder. */
911
+ createFolder(path: string): Promise<{
912
+ path: string;
913
+ }>;
914
+ /**
915
+ * Create a further-scoped instance nested within this scope.
916
+ *
917
+ * @example
918
+ * ```ts
919
+ * const uploads = db.storage.scope('user-uploads');
920
+ * const images = uploads.scope('images'); // → "user-uploads/images/"
921
+ * ```
922
+ */
923
+ scope(subPrefix: string): ScopedStorage;
924
+ }
925
+
926
+ /**
927
+ * HydrousClient — the main entry point for the HydrousDB SDK.
928
+ *
929
+ * Each service uses its own dedicated key:
930
+ * - `authKey` (`hk_auth_…`) → `db.auth('bucket')`
931
+ * - `bucketSecurityKey` (`hk_bucket_…`) → `db.records('bucket')` + `db.analytics('bucket')`
932
+ * - `storageKeys` (`ssk_…`) → `db.storage('keyName')`
933
+ *
934
+ * @example
935
+ * ```ts
936
+ * import { createClient } from 'hydrousdb';
937
+ *
938
+ * const db = createClient({
939
+ * authKey: 'hk_auth_…',
940
+ * bucketSecurityKey: 'hk_bucket_…',
941
+ * storageKeys: {
942
+ * main: 'ssk_main_…',
943
+ * avatars: 'ssk_avatars_…',
944
+ * documents: 'ssk_docs_…',
945
+ * },
946
+ * });
947
+ *
948
+ * // Records & Analytics — use bucketSecurityKey automatically
949
+ * const posts = db.records('blog-posts');
950
+ * const analytics = db.analytics('orders');
951
+ *
952
+ * // Auth — uses authKey automatically
953
+ * const auth = db.auth('app-users');
954
+ *
955
+ * // Storage — pick which key to use by name
956
+ * const avatarStorage = db.storage('avatars');
957
+ * const documentStorage = db.storage('documents');
958
+ * ```
959
+ */
960
+ declare class HydrousClient {
961
+ private readonly http;
962
+ private readonly authKey_;
963
+ private readonly bucketSecurityKey_;
964
+ private readonly storageKeys_;
965
+ private readonly _recordsCache;
966
+ private readonly _authCache;
967
+ private readonly _analyticsCache;
968
+ private readonly _storageCache;
969
+ constructor(config: HydrousConfig);
970
+ /**
971
+ * Get a typed records client for the named bucket.
972
+ * Uses your `bucketSecurityKey` automatically.
973
+ *
974
+ * @example
975
+ * ```ts
976
+ * interface Post { title: string; published: boolean }
977
+ * const posts = db.records<Post>('blog-posts');
978
+ * const post = await posts.create({ title: 'Hello', published: false });
979
+ * ```
980
+ */
981
+ records<T extends RecordData = RecordData>(bucketKey: string): RecordsClient<T>;
982
+ /**
983
+ * Get an auth client for the named user bucket.
984
+ * Uses your `authKey` automatically.
985
+ *
986
+ * @example
987
+ * ```ts
988
+ * const auth = db.auth('app-users');
989
+ * const { user, session } = await auth.login({ email: '…', password: '…' });
990
+ * ```
991
+ */
992
+ auth(bucketKey: string): AuthClient;
993
+ /**
994
+ * Get an analytics client for the named bucket.
995
+ * Uses your `bucketSecurityKey` automatically.
996
+ *
997
+ * @example
998
+ * ```ts
999
+ * const analytics = db.analytics('orders');
1000
+ * const { count } = await analytics.count();
1001
+ * ```
1002
+ */
1003
+ analytics(bucketKey: string): AnalyticsClient;
1004
+ /**
1005
+ * Get a storage manager for the named storage key.
1006
+ * The name must match a key you defined in `storageKeys` when calling `createClient`.
1007
+ * Uses the corresponding `ssk_…` key automatically via `X-Storage-Key` header.
1008
+ *
1009
+ * @param keyName The name of the storage key (e.g. `"avatars"`, `"documents"`, `"main"`).
1010
+ *
1011
+ * @example
1012
+ * ```ts
1013
+ * const avatars = db.storage('avatars');
1014
+ * const documents = db.storage('documents');
1015
+ *
1016
+ * // Upload to avatars bucket
1017
+ * await avatars.upload(file, `${userId}.jpg`, { isPublic: true });
1018
+ *
1019
+ * // Scope to a sub-folder
1020
+ * const userDocs = db.storage('documents').scope(`users/${userId}`);
1021
+ * await userDocs.upload(pdfBuffer, 'contract.pdf');
1022
+ * ```
1023
+ */
1024
+ storage(keyName: string): StorageManager & {
1025
+ scope: (prefix: string) => ScopedStorage;
1026
+ };
1027
+ }
1028
+ /**
1029
+ * Create a new HydrousDB client.
1030
+ *
1031
+ * @example
1032
+ * ```ts
1033
+ * import { createClient } from 'hydrousdb';
1034
+ *
1035
+ * const db = createClient({
1036
+ * authKey: process.env.HYDROUS_AUTH_KEY!,
1037
+ * bucketSecurityKey: process.env.HYDROUS_BUCKET_KEY!,
1038
+ * storageKeys: {
1039
+ * main: process.env.HYDROUS_STORAGE_MAIN!,
1040
+ * avatars: process.env.HYDROUS_STORAGE_AVATARS!,
1041
+ * documents: process.env.HYDROUS_STORAGE_DOCS!,
1042
+ * },
1043
+ * });
1044
+ * ```
1045
+ */
1046
+ declare function createClient(config: HydrousConfig): HydrousClient;
1047
+
1048
+ declare class HydrousError extends Error {
1049
+ /** Machine-readable error code returned by the API. */
1050
+ readonly code: string;
1051
+ /** HTTP status code (if applicable). */
1052
+ readonly status?: number;
1053
+ /** The original request ID for support tracing. */
1054
+ readonly requestId?: string;
1055
+ /** Additional validation details. */
1056
+ readonly details?: string[];
1057
+ constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
1058
+ toString(): string;
1059
+ }
1060
+ declare class AuthError extends HydrousError {
1061
+ constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
1062
+ }
1063
+ declare class RecordError extends HydrousError {
1064
+ constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
1065
+ }
1066
+ declare class StorageError extends HydrousError {
1067
+ constructor(message: string, code: string, status?: number, requestId?: string);
1068
+ }
1069
+ declare class AnalyticsError extends HydrousError {
1070
+ constructor(message: string, code: string, status?: number, requestId?: string);
1071
+ }
1072
+ declare class ValidationError extends HydrousError {
1073
+ constructor(message: string, details?: string[]);
1074
+ }
1075
+ declare class NetworkError extends HydrousError {
1076
+ readonly cause?: unknown;
1077
+ constructor(message: string, cause?: unknown);
1078
+ }
1079
+
1080
+ 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 };