hydrousdb 3.0.2 → 3.2.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.cts CHANGED
@@ -1,68 +1,34 @@
1
1
  interface RequestOptions {
2
- method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2
+ method: string;
3
3
  body?: unknown;
4
+ rawBody?: FormData | Uint8Array | ArrayBuffer;
4
5
  headers?: Record<string, string>;
5
- rawBody?: string | FormData | Uint8Array<ArrayBuffer>;
6
- contentType?: string;
7
6
  }
8
7
  declare class HttpClient {
9
8
  private readonly baseUrl;
10
9
  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>;
10
+ request<T>(path: string, options: RequestOptions): Promise<T>;
11
+ get<T>(path: string, apiKey?: string, extraHeaders?: Record<string, string>): Promise<T>;
12
+ post<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
13
+ put<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
14
+ patch<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
15
+ delete<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
16
+ /**
17
+ * Upload directly to a signed GCS URL (no auth headers — signed URL is self-authenticating).
18
+ * Supports optional progress tracking via XHR in browser environments.
19
+ */
20
+ putToSignedUrl(signedUrl: string, data: Blob | Uint8Array | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
18
21
  }
19
22
 
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
23
  type StorageKeys = Record<string, string>;
34
24
  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
- */
25
+ /** Auth service key (starts with `hk_auth_`). */
40
26
  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
- */
27
+ /** Bucket security key (starts with `hk_bucket_`). Used for records + analytics. */
46
28
  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
- */
29
+ /** Named storage keys (each starts with `ssk_`). */
61
30
  storageKeys: StorageKeys;
62
- /**
63
- * Override the API base URL. Defaults to the official HydrousDB endpoint.
64
- * You almost never need to set this.
65
- */
31
+ /** Override the API base URL. Defaults to the official HydrousDB endpoint. */
66
32
  baseUrl?: string;
67
33
  }
68
34
  interface SignupOptions {
@@ -103,24 +69,26 @@ interface AuthResult {
103
69
  interface UpdateUserOptions {
104
70
  sessionId: string;
105
71
  userId: string;
106
- data: Partial<Omit<UserRecord, 'id' | 'email' | 'createdAt'>>;
72
+ /** Fields to update nested under `updates` when sent to the server. */
73
+ updates: Partial<Omit<UserRecord, 'id' | 'email' | 'createdAt'>>;
107
74
  }
108
75
  interface ChangePasswordOptions {
109
76
  sessionId: string;
110
77
  userId: string;
78
+ /** Maps to `oldPassword` on the server. */
111
79
  currentPassword: string;
112
80
  newPassword: string;
113
81
  }
114
82
  interface ListUsersOptions {
115
83
  sessionId: string;
116
84
  limit?: number;
117
- offset?: number;
85
+ /** Cursor for the next page (URL-encoded value returned by the previous call). */
86
+ cursor?: string;
118
87
  }
119
88
  interface ListUsersResult {
120
89
  users: UserRecord[];
121
- total: number;
122
- limit: number;
123
- offset: number;
90
+ hasMore: boolean;
91
+ nextCursor: string | null;
124
92
  }
125
93
  type RecordData = Record<string, unknown>;
126
94
  interface RecordResult {
@@ -161,12 +129,34 @@ interface RecordHistoryEntry {
161
129
  createdAt: number;
162
130
  data: RecordData;
163
131
  }
132
+ /** Options for creating a record — includes queryable fields for filtering. */
133
+ interface CreateRecordOptions {
134
+ /**
135
+ * Fields that should be indexed for server-side filtering.
136
+ * Only fields listed here will be queryable via `query({ filters: [...] })`.
137
+ */
138
+ queryableFields?: string[];
139
+ /** Email of the user performing the action (for audit trails). */
140
+ userEmail?: string;
141
+ /**
142
+ * Assign a custom record ID instead of an auto-generated one.
143
+ * If the ID already exists, the record will be upserted.
144
+ */
145
+ customRecordId?: string;
146
+ }
147
+ /** Options for batch-creating records. */
148
+ interface BatchCreateOptions {
149
+ /** Fields to index for filtering — applied to all records in the batch. */
150
+ queryableFields?: string[];
151
+ /** Email of the user performing the action. */
152
+ userEmail?: string;
153
+ }
164
154
  interface UploadOptions {
165
- /** Set to true to make the file publicly accessible without authentication. */
155
+ /** Make the file publicly accessible without authentication. */
166
156
  isPublic?: boolean;
167
- /** Set to true to overwrite an existing file at the same path. */
157
+ /** Overwrite an existing file at the same path. */
168
158
  overwrite?: boolean;
169
- /** MIME type of the file. Auto-detected if omitted. */
159
+ /** MIME type of the file. Auto-detected from extension if omitted. */
170
160
  mimeType?: string;
171
161
  /** TTL in seconds for the signed upload URL (default 900 = 15 min). */
172
162
  expiresInSeconds?: number;
@@ -195,6 +185,7 @@ interface ListOptions {
195
185
  interface FileEntry {
196
186
  name: string;
197
187
  path: string;
188
+ type?: 'file' | 'folder';
198
189
  size?: number;
199
190
  mimeType?: string;
200
191
  isPublic?: boolean;
@@ -243,14 +234,27 @@ interface BatchUploadUrlResult {
243
234
  index: number;
244
235
  }>;
245
236
  }
237
+ interface BatchDownloadResult {
238
+ succeeded: Array<{
239
+ index: number;
240
+ path: string;
241
+ mimeType: string;
242
+ size: number;
243
+ content: string;
244
+ }>;
245
+ failed: Array<{
246
+ index: number;
247
+ path: string;
248
+ error: string;
249
+ code?: string;
250
+ }>;
251
+ }
246
252
  type QueryType = 'count' | 'distribution' | 'sum' | 'timeSeries' | 'fieldTimeSeries' | 'topN' | 'stats' | 'records' | 'multiMetric' | 'storageStats' | 'crossBucket';
247
253
  type Aggregation = 'sum' | 'avg' | 'min' | 'max' | 'count';
248
254
  type Granularity = 'hour' | 'day' | 'week' | 'month' | 'year';
249
255
  type SortOrder = 'asc' | 'desc';
250
256
  interface DateRange {
251
- /** Start timestamp in milliseconds (Unix epoch). */
252
257
  start?: number;
253
- /** End timestamp in milliseconds (Unix epoch). */
254
258
  end?: number;
255
259
  }
256
260
  interface AnalyticsFilter {
@@ -259,11 +263,8 @@ interface AnalyticsFilter {
259
263
  value: string | number | boolean;
260
264
  }
261
265
  interface MetricDefinition {
262
- /** Field name in your records to aggregate. */
263
266
  field: string;
264
- /** Alias used in the result object. Must be a safe identifier. */
265
267
  name: string;
266
- /** Aggregation function. Defaults to 'count'. */
267
268
  aggregation?: Aggregation;
268
269
  }
269
270
  interface AnalyticsQuery {
@@ -282,7 +283,6 @@ interface AnalyticsQuery {
282
283
  order?: SortOrder;
283
284
  n?: number;
284
285
  metrics?: MetricDefinition[];
285
- /** For crossBucket queries: list of bucket names to compare. */
286
286
  bucketKeys?: string[];
287
287
  }
288
288
  interface AnalyticsResult<T = unknown> {
@@ -340,10 +340,13 @@ interface CrossBucketRow {
340
340
  * AuthClient — full user authentication for a single bucket.
341
341
  * Uses the `authKey` (`hk_auth_…`) sent via `X-Api-Key` header.
342
342
  *
343
+ * All routes are under `/auth/:bucketKey/` — the bucketKey is the user bucket
344
+ * name you passed when calling `db.auth('bucketKey')`.
345
+ *
343
346
  * @example
344
347
  * ```ts
345
- * const db = createClient({ authKey: 'hk_auth_…', bucketSecurityKey: '', storageKeys: { main: '…' } });
346
- * const auth = db.auth('my-app-users');
348
+ * const db = createClient({ authKey: 'hk_auth_…', … });
349
+ * const auth = db.auth('app-users');
347
350
  * const { user, session } = await auth.signup({ email: 'alice@example.com', password: 'hunter2' });
348
351
  * ```
349
352
  */
@@ -358,12 +361,22 @@ declare class AuthClient {
358
361
  private delete;
359
362
  signup(options: SignupOptions): Promise<AuthResult>;
360
363
  login(options: LoginOptions): Promise<AuthResult>;
361
- logout({ sessionId }: {
364
+ logout(options: {
362
365
  sessionId: string;
366
+ allDevices?: boolean;
363
367
  }): Promise<void>;
364
368
  refreshSession({ refreshToken }: {
365
369
  refreshToken: string;
366
370
  }): Promise<Session>;
371
+ validateSession({ sessionId }: {
372
+ sessionId: string;
373
+ }): Promise<{
374
+ user: UserRecord;
375
+ session: {
376
+ sessionId: string;
377
+ expiresAt: number;
378
+ };
379
+ }>;
367
380
  getUser({ userId }: {
368
381
  userId: string;
369
382
  }): Promise<UserRecord>;
@@ -377,14 +390,15 @@ declare class AuthClient {
377
390
  sessionId: string;
378
391
  userId: string;
379
392
  }): Promise<void>;
380
- bulkDeleteUsers({ sessionId, userIds }: {
393
+ bulkDeleteUsers(options: {
381
394
  sessionId: string;
382
395
  userIds: string[];
396
+ hard?: boolean;
383
397
  }): Promise<{
384
- deleted: number;
385
- failed: string[];
398
+ succeeded: number;
399
+ failed: number;
386
400
  }>;
387
- lockAccount({ sessionId, userId, duration }: {
401
+ lockAccount(options: {
388
402
  sessionId: string;
389
403
  userId: string;
390
404
  duration?: number;
@@ -413,49 +427,121 @@ declare class AuthClient {
413
427
  }
414
428
 
415
429
  /**
416
- * RecordsClient — CRUD + query for a single bucket.
430
+ * RecordsClient — typed CRUD + query + batch for a single bucket.
417
431
  * Uses the `bucketSecurityKey` (`hk_bucket_…`) sent via `X-Api-Key` header.
418
432
  *
419
433
  * @example
420
434
  * ```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' });
435
+ * interface Post { title: string; published: boolean }
436
+ * const posts = db.records<Post>('blog-posts');
437
+ * const post = await posts.create({ title: 'Hello', published: false });
424
438
  * ```
425
439
  */
426
440
  declare class RecordsClient<T extends RecordData = RecordData> {
427
441
  private readonly http;
428
442
  private readonly bucketKey;
443
+ private readonly apiKey;
429
444
  private readonly basePath;
430
- private readonly bucketKey_;
431
445
  constructor(http: HttpClient, bucketSecurityKey: string, bucketKey: string);
432
- private get key();
433
- create(data: T): Promise<T & RecordResult>;
446
+ /**
447
+ * Create a new record.
448
+ *
449
+ * @param data The record fields to store.
450
+ * @param options Optional: queryableFields (for server-side filtering),
451
+ * userEmail (audit trail), customRecordId (upsert by ID).
452
+ *
453
+ * @example
454
+ * ```ts
455
+ * const post = await posts.create(
456
+ * { title: 'Hello', status: 'draft', authorId: 'u1' },
457
+ * { queryableFields: ['status', 'authorId'], userEmail: 'alice@example.com' },
458
+ * );
459
+ * ```
460
+ */
461
+ create(data: T, options?: CreateRecordOptions): Promise<T & RecordResult>;
462
+ /**
463
+ * Get a single record by ID.
464
+ */
434
465
  get(id: string): Promise<T & RecordResult>;
466
+ /**
467
+ * Fully replace a record (PUT semantics).
468
+ */
435
469
  set(id: string, data: T): Promise<T & RecordResult>;
470
+ /**
471
+ * Partially update a record (PATCH semantics).
472
+ * By default merges with existing fields; set `merge: false` to replace.
473
+ * Supports write-filter sentinels: `{ __op: 'increment', delta: 1 }` etc.
474
+ */
436
475
  patch(id: string, data: Partial<T>, options?: PatchRecordOptions): Promise<T & RecordResult>;
476
+ /**
477
+ * Delete a record permanently.
478
+ */
437
479
  delete(id: string): Promise<void>;
438
- batchCreate(items: T[]): Promise<(T & RecordResult)[]>;
480
+ /**
481
+ * Create multiple records in one request (max 500).
482
+ * Each record can include a `_customRecordId` field for upsert behaviour.
483
+ *
484
+ * @example
485
+ * ```ts
486
+ * const created = await posts.batchCreate(
487
+ * [{ title: 'A' }, { title: 'B' }],
488
+ * { queryableFields: ['title'], userEmail: 'alice@example.com' },
489
+ * );
490
+ * ```
491
+ */
492
+ batchCreate(items: T[], options?: BatchCreateOptions): Promise<(T & RecordResult)[]>;
493
+ /**
494
+ * Delete multiple records in one request (max 500).
495
+ */
439
496
  batchDelete(ids: string[]): Promise<{
440
497
  deleted: number;
441
498
  failed: string[];
442
499
  }>;
500
+ /**
501
+ * Query records with optional filters, sorting, and pagination.
502
+ *
503
+ * @example
504
+ * ```ts
505
+ * const { records, hasMore, nextCursor } = await posts.query({
506
+ * filters: [{ field: 'status', op: '==', value: 'published' }],
507
+ * orderBy: 'createdAt',
508
+ * order: 'desc',
509
+ * limit: 20,
510
+ * });
511
+ * ```
512
+ */
443
513
  query(options?: QueryOptions): Promise<QueryResult<T>>;
514
+ /**
515
+ * Get all records matching the given options (no filter support — use `query()` for filters).
516
+ */
444
517
  getAll(options?: Omit<QueryOptions, 'filters'>): Promise<(T & RecordResult)[]>;
518
+ /**
519
+ * Count records matching the given filters.
520
+ */
445
521
  count(filters?: QueryOptions['filters']): Promise<number>;
522
+ /**
523
+ * Get the version history of a record.
524
+ */
446
525
  getHistory(id: string): Promise<RecordHistoryEntry[]>;
526
+ /**
527
+ * Restore a record to a previous version.
528
+ */
447
529
  restoreVersion(id: string, version: number): Promise<T & RecordResult>;
448
530
  }
449
531
 
450
532
  /**
451
- * AnalyticsClient — BigQuery-powered aggregations for a bucket.
533
+ * AnalyticsClient — BigQuery-powered aggregations for a single bucket.
452
534
  * Uses the `bucketSecurityKey` (`hk_bucket_…`) sent via `X-Api-Key` header.
453
535
  *
536
+ * All methods POST a `queryType` body to `POST /analytics/:bucketKey`.
537
+ * The `dateRange` is passed as `{ start, end }` ms timestamps — the server
538
+ * converts them to ISO date strings internally.
539
+ *
454
540
  * @example
455
541
  * ```ts
456
- * const db = createClient({ authKey: '…', bucketSecurityKey: 'hk_bucket_…', storageKeys: { main: '…' } });
457
542
  * const analytics = db.analytics('orders');
458
543
  * const { count } = await analytics.count();
544
+ * const top5 = await analytics.topN({ field: 'country', n: 5 });
459
545
  * ```
460
546
  */
461
547
  declare class AnalyticsClient {
@@ -464,31 +550,37 @@ declare class AnalyticsClient {
464
550
  private readonly basePath;
465
551
  constructor(http: HttpClient, bucketSecurityKey: string, bucketKey: string);
466
552
  private run;
553
+ /** Count all records, optionally within a date range. */
467
554
  count(opts?: {
468
555
  dateRange?: DateRange;
469
556
  }): Promise<CountResult>;
557
+ /** Get value distribution for a field (e.g. how many records per status). */
470
558
  distribution(opts: {
471
559
  field: string;
472
560
  limit?: number;
473
561
  order?: SortOrder;
474
562
  dateRange?: DateRange;
475
563
  }): Promise<DistributionRow[]>;
564
+ /** Sum a numeric field, optionally grouped by another field. */
476
565
  sum(opts: {
477
566
  field: string;
478
567
  groupBy?: string;
479
568
  limit?: number;
480
569
  dateRange?: DateRange;
481
570
  }): Promise<SumRow[]>;
571
+ /** Count of records over time, bucketed by granularity. */
482
572
  timeSeries(opts?: {
483
573
  granularity?: Granularity;
484
574
  dateRange?: DateRange;
485
575
  }): Promise<TimeSeriesRow[]>;
576
+ /** Aggregate a numeric field over time. */
486
577
  fieldTimeSeries(opts: {
487
578
  field: string;
488
579
  aggregation?: Aggregation;
489
580
  granularity?: Granularity;
490
581
  dateRange?: DateRange;
491
582
  }): Promise<FieldTimeSeriesRow[]>;
583
+ /** Top N values for a field by count. */
492
584
  topN(opts: {
493
585
  field: string;
494
586
  n?: number;
@@ -496,10 +588,12 @@ declare class AnalyticsClient {
496
588
  order?: SortOrder;
497
589
  dateRange?: DateRange;
498
590
  }): Promise<TopNRow[]>;
591
+ /** Statistical summary (min, max, avg, sum, count, stddev) for a numeric field. */
499
592
  stats(opts: {
500
593
  field: string;
501
594
  dateRange?: DateRange;
502
595
  }): Promise<FieldStats>;
596
+ /** Fetch filtered records via the analytics engine (bypasses Firestore pagination). */
503
597
  records<T extends RecordData = RecordData>(opts?: {
504
598
  filters?: AnalyticsFilter[];
505
599
  selectFields?: string[];
@@ -509,96 +603,84 @@ declare class AnalyticsClient {
509
603
  order?: SortOrder;
510
604
  dateRange?: DateRange;
511
605
  }): Promise<(T & RecordResult)[]>;
606
+ /** Compute multiple aggregations in a single request. */
512
607
  multiMetric(opts: {
513
608
  metrics: MetricDefinition[];
514
609
  dateRange?: DateRange;
515
610
  }): Promise<MultiMetricResult>;
611
+ /** Storage usage stats for the bucket (record count, bytes, avg/min/max size). */
516
612
  storageStats(opts?: {
517
613
  dateRange?: DateRange;
518
614
  }): Promise<StorageStatsResult>;
615
+ /**
616
+ * Compare a metric across multiple buckets in one query.
617
+ * The caller's key must have read access to every bucket in `bucketKeys`.
618
+ */
519
619
  crossBucket(opts: {
520
620
  bucketKeys: string[];
521
621
  field: string;
522
622
  aggregation?: Aggregation;
523
623
  dateRange?: DateRange;
524
624
  }): Promise<CrossBucketRow[]>;
625
+ /**
626
+ * Raw query — use this when none of the typed helpers cover your use case.
627
+ */
525
628
  query<T = unknown>(query: AnalyticsQuery): Promise<AnalyticsResult<T>>;
526
629
  }
527
630
 
528
631
  /**
529
632
  * StorageManager — upload, download, list, move, copy, and delete files.
530
633
  * 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
634
  *
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`.
635
+ * Get a StorageManager via `db.storage('keyName')` where the name matches
636
+ * one of the keys defined in `storageKeys` when calling `createClient`.
535
637
  *
536
638
  * @example
537
639
  * ```ts
538
- * const db = createClient({ authKey: '…', bucketSecurityKey: '…', storageKeys: { avatars: 'ssk_avatars_…' } });
539
640
  * const avatars = db.storage('avatars');
540
- * const result = await avatars.upload(file, 'alice.jpg', { isPublic: true });
641
+ * const result = await avatars.upload(file, 'alice.jpg', { isPublic: true });
642
+ * console.log(result.publicUrl);
541
643
  * ```
542
644
  */
543
645
  declare class StorageManager {
544
646
  private readonly http;
545
647
  private readonly storageKey;
546
- private readonly basePath;
648
+ readonly basePath = "/storage";
547
649
  constructor(http: HttpClient, storageKey: string);
548
- /** Headers for all storage requests — uses X-Storage-Key, not X-Api-Key. */
549
650
  private get authHeaders();
550
651
  /**
551
652
  * 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.
653
+ * For files >10 MB or when upload progress is needed, use `getUploadUrl()`.
557
654
  *
558
655
  * @example
559
656
  * ```ts
560
- * // Upload a public avatar
561
657
  * 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
658
+ * console.log(result.publicUrl);
567
659
  * ```
568
660
  */
569
- upload(data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
661
+ upload(data: Blob | Uint8Array | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
570
662
  /**
571
- * Upload raw JSON or plain text data as a file.
663
+ * Upload raw JSON or plain-text data as a file.
572
664
  *
573
665
  * @example
574
666
  * ```ts
575
- * const result = await storage.uploadRaw(
576
- * { config: { theme: 'dark' } },
577
- * 'settings/user-config.json',
578
- * { isPublic: false },
579
- * );
667
+ * await storage.uploadRaw({ theme: 'dark' }, 'settings/config.json');
580
668
  * ```
581
669
  */
582
670
  uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
583
671
  /**
584
- * Step 1 of the recommended upload flow.
585
- * Get a signed URL to upload directly to GCS from the client (supports progress).
672
+ * Step 1 get a signed GCS URL to upload directly from the client.
673
+ * Supports upload progress via `uploadToSignedUrl()`.
586
674
  *
587
675
  * @example
588
676
  * ```ts
589
677
  * const { uploadUrl, path: confirmedPath } = await storage.getUploadUrl({
590
- * path: 'videos/intro.mp4',
678
+ * path: 'videos/intro.mp4',
591
679
  * mimeType: 'video/mp4',
592
- * size: file.size,
680
+ * size: file.size,
593
681
  * isPublic: true,
594
682
  * });
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
683
+ * await storage.uploadToSignedUrl(uploadUrl, file, 'video/mp4', pct => console.log(pct + '%'));
602
684
  * const result = await storage.confirmUpload({ path: confirmedPath, mimeType: 'video/mp4', isPublic: true });
603
685
  * ```
604
686
  */
@@ -611,28 +693,12 @@ declare class StorageManager {
611
693
  expiresInSeconds?: number;
612
694
  }): Promise<UploadUrlResult>;
613
695
  /**
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.
696
+ * Step 2 — upload data directly to a signed GCS URL.
697
+ * Supports progress tracking in browser environments.
621
698
  */
622
- uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
699
+ uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
623
700
  /**
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
- * ```
701
+ * Step 3 confirm a direct upload and register metadata on the server.
636
702
  */
637
703
  confirmUpload(opts: {
638
704
  path: string;
@@ -640,16 +706,15 @@ declare class StorageManager {
640
706
  isPublic?: boolean;
641
707
  }): Promise<UploadResult>;
642
708
  /**
643
- * Get signed upload URLs for multiple files at once.
709
+ * Get signed upload URLs for multiple files at once (max 50).
710
+ * Server returns `{ succeeded, failed }` — only succeeded items have URLs.
644
711
  *
645
712
  * @example
646
713
  * ```ts
647
714
  * 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 },
715
+ * { path: 'images/a.jpg', mimeType: 'image/jpeg', size: 204800 },
716
+ * { path: 'images/b.jpg', mimeType: 'image/jpeg', size: 153600 },
650
717
  * ]);
651
- *
652
- * // Upload each file and confirm
653
718
  * for (const f of files) {
654
719
  * await storage.uploadToSignedUrl(f.uploadUrl, blobs[f.index], f.mimeType);
655
720
  * await storage.confirmUpload({ path: f.path, mimeType: f.mimeType });
@@ -659,93 +724,65 @@ declare class StorageManager {
659
724
  getBatchUploadUrls(files: BatchUploadItem[]): Promise<BatchUploadUrlResult>;
660
725
  /**
661
726
  * Confirm multiple direct uploads at once.
727
+ * Returns both succeeded and failed results.
662
728
  */
663
729
  batchConfirmUploads(items: Array<{
664
730
  path: string;
665
731
  mimeType: string;
666
732
  isPublic?: boolean;
667
- }>): Promise<UploadResult[]>;
733
+ }>): Promise<{
734
+ succeeded: UploadResult[];
735
+ failed: Array<{
736
+ path: string;
737
+ error: string;
738
+ }>;
739
+ }>;
668
740
  /**
669
741
  * Download a private file as an ArrayBuffer.
670
742
  * 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
743
  */
680
744
  download(path: string): Promise<ArrayBuffer>;
681
745
  /**
682
- * Download multiple files at once, returned as a JSON map of `{ path: base64 }`.
746
+ * Download multiple files at once (max 20).
747
+ * Returns a structured result with succeeded and failed items.
748
+ * Each succeeded item includes the file content as a base64 string.
683
749
  *
684
750
  * @example
685
751
  * ```ts
686
- * const files = await storage.batchDownload(['docs/a.pdf', 'docs/b.pdf']);
752
+ * const { succeeded, failed } = await storage.batchDownload(['docs/a.pdf', 'docs/b.pdf']);
753
+ * for (const f of succeeded) {
754
+ * const bytes = Buffer.from(f.content, 'base64');
755
+ * }
687
756
  * ```
688
757
  */
689
- batchDownload(paths: string[]): Promise<Record<string, string>>;
758
+ batchDownload(paths: string[]): Promise<BatchDownloadResult>;
690
759
  /**
691
760
  * List files and folders at a given path prefix.
761
+ * Returns separate `files` and `folders` arrays for easy consumption.
692
762
  *
693
763
  * @example
694
764
  * ```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
765
+ * const { files, folders, hasMore, nextCursor } = await storage.list({ prefix: 'avatars/', limit: 20 });
705
766
  * const page2 = await storage.list({ prefix: 'avatars/', cursor: nextCursor });
706
767
  * ```
707
768
  */
708
769
  list(opts?: ListOptions): Promise<ListResult>;
709
770
  /**
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
- * ```
771
+ * Get metadata for a file: size, MIME type, visibility, URLs.
717
772
  */
718
773
  getMetadata(path: string): Promise<FileMetadata>;
719
774
  /**
720
775
  * 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.
776
+ * Can be shared externally without requiring an `X-Storage-Key`.
725
777
  *
726
- * @param path Path to the file.
727
- * @param expiresIn URL lifetime in seconds (default 3600 = 1 hour).
778
+ * > Note: Downloads via signed URLs bypass the server — download stats are NOT tracked.
728
779
  *
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
- * ```
780
+ * @param path File path.
781
+ * @param expiresIn URL lifetime in seconds (default 3600 = 1 hour).
734
782
  */
735
783
  getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
736
784
  /**
737
785
  * 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
786
  */
750
787
  setVisibility(path: string, isPublic: boolean): Promise<{
751
788
  path: string;
@@ -753,81 +790,20 @@ declare class StorageManager {
753
790
  publicUrl: string | null;
754
791
  downloadUrl: string | null;
755
792
  }>;
756
- /**
757
- * Create a folder (a GCS prefix placeholder).
758
- *
759
- * @example
760
- * ```ts
761
- * await storage.createFolder('uploads/2025/');
762
- * ```
763
- */
764
793
  createFolder(path: string): Promise<{
765
794
  path: string;
766
795
  }>;
767
- /**
768
- * Delete a single file.
769
- *
770
- * @example
771
- * ```ts
772
- * await storage.deleteFile('avatars/old-avatar.jpg');
773
- * ```
774
- */
775
796
  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
797
  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
798
  move(from: string, to: string): Promise<{
797
799
  from: string;
798
800
  to: string;
799
801
  }>;
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
802
  copy(from: string, to: string): Promise<{
809
803
  from: string;
810
804
  to: string;
811
805
  }>;
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
806
  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
807
  info(): Promise<{
832
808
  ok: boolean;
833
809
  storageRoot: string;
@@ -835,89 +811,98 @@ declare class StorageManager {
835
811
  }
836
812
 
837
813
  /**
838
- * ScopedStorage — a StorageManager pre-scoped to a specific folder prefix.
814
+ * ScopedStorage — a path-prefixed view over a StorageManager.
815
+ * All paths you pass are automatically prefixed with the scope.
839
816
  *
840
- * All paths are automatically prepended with the scope, so you never
841
- * have to repeat the folder name.
817
+ * Obtain via `db.storage('keyName').scope('prefix/')`.
842
818
  *
843
819
  * @example
844
820
  * ```ts
845
- * const db = createClient({ securityKey: 'sk_...' });
821
+ * const userDocs = db.storage('documents').scope(`users/${userId}/`);
846
822
  *
847
- * // All operations in the "avatars/" folder
848
- * const avatars = db.storage.scope('avatars');
823
+ * // Uploads to: users/{userId}/contract.pdf
824
+ * await userDocs.upload(pdfBuffer, 'contract.pdf');
849
825
  *
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"
826
+ * // Lists: users/{userId}/
827
+ * const { files } = await userDocs.list();
853
828
  * ```
854
829
  */
855
830
  declare class ScopedStorage {
856
831
  private readonly manager;
857
832
  private readonly prefix;
858
833
  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. */
834
+ private p;
835
+ upload(data: Blob | Uint8Array | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
863
836
  uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
864
- /** Get a signed upload URL for a file within the scoped folder. */
865
837
  getUploadUrl(opts: {
866
838
  path: string;
867
839
  mimeType: string;
868
840
  size: number;
869
841
  isPublic?: boolean;
870
842
  overwrite?: boolean;
843
+ expiresInSeconds?: number;
871
844
  }): Promise<UploadUrlResult>;
872
- /** Confirm a direct upload within the scoped folder. */
845
+ uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
873
846
  confirmUpload(opts: {
874
847
  path: string;
875
848
  mimeType: string;
876
849
  isPublic?: boolean;
877
850
  }): Promise<UploadResult>;
878
- /** Download a file within the scoped folder. */
851
+ getBatchUploadUrls(files: BatchUploadItem[]): Promise<BatchUploadUrlResult>;
852
+ batchConfirmUploads(items: Array<{
853
+ path: string;
854
+ mimeType: string;
855
+ isPublic?: boolean;
856
+ }>): Promise<{
857
+ succeeded: UploadResult[];
858
+ failed: Array<{
859
+ path: string;
860
+ error: string;
861
+ }>;
862
+ }>;
879
863
  download(path: string): Promise<ArrayBuffer>;
864
+ batchDownload(paths: string[]): Promise<BatchDownloadResult>;
880
865
  /**
881
- * List files within the scoped folder.
882
- * `prefix` in options is relative to the scope.
866
+ * List files within this scope. The `prefix` option is relative to the scope root.
867
+ *
868
+ * @example
869
+ * ```ts
870
+ * const userDocs = storage.scope('users/alice/');
871
+ * // Lists users/alice/docs/
872
+ * const { files } = await userDocs.list({ prefix: 'docs/' });
873
+ * ```
883
874
  */
884
875
  list(opts?: ListOptions): Promise<ListResult>;
885
- /** Get metadata for a file within the scoped folder. */
886
876
  getMetadata(path: string): Promise<FileMetadata>;
887
- /** Get a time-limited signed URL for a file within the scoped folder. */
888
877
  getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
889
- /** Change visibility of a file within the scoped folder. */
890
878
  setVisibility(path: string, isPublic: boolean): Promise<{
891
879
  path: string;
892
880
  isPublic: boolean;
893
881
  publicUrl: string | null;
894
882
  downloadUrl: string | null;
895
883
  }>;
896
- /** Delete a file within the scoped folder. */
884
+ createFolder(path: string): Promise<{
885
+ path: string;
886
+ }>;
897
887
  deleteFile(path: string): Promise<void>;
898
- /** Delete a sub-folder within the scoped folder. */
899
888
  deleteFolder(path: string): Promise<void>;
900
- /** Move a file within the scoped folder. */
901
889
  move(from: string, to: string): Promise<{
902
890
  from: string;
903
891
  to: string;
904
892
  }>;
905
- /** Copy a file within the scoped folder. */
906
893
  copy(from: string, to: string): Promise<{
907
894
  from: string;
908
895
  to: string;
909
896
  }>;
910
- /** Create a sub-folder within the scoped folder. */
911
- createFolder(path: string): Promise<{
912
- path: string;
913
- }>;
897
+ getStats(): Promise<StorageStats>;
914
898
  /**
915
- * Create a further-scoped instance nested within this scope.
899
+ * Create a deeper scope within this one.
916
900
  *
917
901
  * @example
918
902
  * ```ts
919
- * const uploads = db.storage.scope('user-uploads');
920
- * const images = uploads.scope('images'); // → "user-uploads/images/"
903
+ * const user = storage.scope('users/alice/');
904
+ * const userDocs = user.scope('docs/');
905
+ * // Effective prefix: users/alice/docs/
921
906
  * ```
922
907
  */
923
908
  scope(subPrefix: string): ScopedStorage;
@@ -926,35 +911,30 @@ declare class ScopedStorage {
926
911
  /**
927
912
  * HydrousClient — the main entry point for the HydrousDB SDK.
928
913
  *
929
- * Each service uses its own dedicated key:
914
+ * Each service uses its own dedicated API key:
930
915
  * - `authKey` (`hk_auth_…`) → `db.auth('bucket')`
931
916
  * - `bucketSecurityKey` (`hk_bucket_…`) → `db.records('bucket')` + `db.analytics('bucket')`
932
917
  * - `storageKeys` (`ssk_…`) → `db.storage('keyName')`
933
918
  *
919
+ * Sub-clients are cached — calling `db.records('posts')` twice returns the same instance.
920
+ *
934
921
  * @example
935
922
  * ```ts
936
923
  * import { createClient } from 'hydrousdb';
937
924
  *
938
925
  * const db = createClient({
939
- * authKey: 'hk_auth_…',
940
- * bucketSecurityKey: 'hk_bucket_…',
926
+ * authKey: process.env.HYDROUS_AUTH_KEY!,
927
+ * bucketSecurityKey: process.env.HYDROUS_BUCKET_KEY!,
941
928
  * storageKeys: {
942
- * main: 'ssk_main_…',
943
- * avatars: 'ssk_avatars_…',
944
- * documents: 'ssk_docs_…',
929
+ * avatars: process.env.HYDROUS_STORAGE_AVATARS!,
930
+ * documents: process.env.HYDROUS_STORAGE_DOCS!,
945
931
  * },
946
932
  * });
947
933
  *
948
- * // Records & Analytics — use bucketSecurityKey automatically
949
- * const posts = db.records('blog-posts');
934
+ * const posts = db.records('blog-posts');
935
+ * const auth = db.auth('app-users');
950
936
  * 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');
937
+ * const avatars = db.storage('avatars');
958
938
  * ```
959
939
  */
960
940
  declare class HydrousClient {
@@ -969,19 +949,20 @@ declare class HydrousClient {
969
949
  constructor(config: HydrousConfig);
970
950
  /**
971
951
  * Get a typed records client for the named bucket.
972
- * Uses your `bucketSecurityKey` automatically.
973
952
  *
974
953
  * @example
975
954
  * ```ts
976
955
  * interface Post { title: string; published: boolean }
977
956
  * const posts = db.records<Post>('blog-posts');
978
- * const post = await posts.create({ title: 'Hello', published: false });
957
+ * const post = await posts.create(
958
+ * { title: 'Hello', published: false },
959
+ * { queryableFields: ['published'] },
960
+ * );
979
961
  * ```
980
962
  */
981
963
  records<T extends RecordData = RecordData>(bucketKey: string): RecordsClient<T>;
982
964
  /**
983
965
  * Get an auth client for the named user bucket.
984
- * Uses your `authKey` automatically.
985
966
  *
986
967
  * @example
987
968
  * ```ts
@@ -992,7 +973,6 @@ declare class HydrousClient {
992
973
  auth(bucketKey: string): AuthClient;
993
974
  /**
994
975
  * Get an analytics client for the named bucket.
995
- * Uses your `bucketSecurityKey` automatically.
996
976
  *
997
977
  * @example
998
978
  * ```ts
@@ -1003,21 +983,16 @@ declare class HydrousClient {
1003
983
  analytics(bucketKey: string): AnalyticsClient;
1004
984
  /**
1005
985
  * 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.
986
+ * The name must match a key defined in `storageKeys` when calling `createClient`.
1008
987
  *
1009
- * @param keyName The name of the storage key (e.g. `"avatars"`, `"documents"`, `"main"`).
988
+ * Attach `.scope(prefix)` to namespace all operations under a path prefix.
1010
989
  *
1011
990
  * @example
1012
991
  * ```ts
1013
- * const avatars = db.storage('avatars');
1014
- * const documents = db.storage('documents');
992
+ * const avatars = db.storage('avatars');
993
+ * const userDocs = db.storage('documents').scope(`users/${userId}/`);
1015
994
  *
1016
- * // Upload to avatars bucket
1017
995
  * 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
996
  * await userDocs.upload(pdfBuffer, 'contract.pdf');
1022
997
  * ```
1023
998
  */
@@ -1035,24 +1010,16 @@ declare class HydrousClient {
1035
1010
  * const db = createClient({
1036
1011
  * authKey: process.env.HYDROUS_AUTH_KEY!,
1037
1012
  * 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
- * },
1013
+ * storageKeys: { main: process.env.HYDROUS_STORAGE_MAIN! },
1043
1014
  * });
1044
1015
  * ```
1045
1016
  */
1046
1017
  declare function createClient(config: HydrousConfig): HydrousClient;
1047
1018
 
1048
1019
  declare class HydrousError extends Error {
1049
- /** Machine-readable error code returned by the API. */
1050
1020
  readonly code: string;
1051
- /** HTTP status code (if applicable). */
1052
1021
  readonly status?: number;
1053
- /** The original request ID for support tracing. */
1054
1022
  readonly requestId?: string;
1055
- /** Additional validation details. */
1056
1023
  readonly details?: string[];
1057
1024
  constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
1058
1025
  toString(): string;
@@ -1077,4 +1044,4 @@ declare class NetworkError extends HydrousError {
1077
1044
  constructor(message: string, cause?: unknown);
1078
1045
  }
1079
1046
 
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 };
1047
+ export { type Aggregation, AnalyticsClient, AnalyticsError, type AnalyticsFilter, type AnalyticsQuery, type AnalyticsResult, AuthClient, AuthError, type AuthResult, type BatchCreateOptions, type BatchDownloadResult, type BatchUploadItem, type BatchUploadUrlResult, type ChangePasswordOptions, type CountResult, type CreateRecordOptions, 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, type StorageKeys, StorageManager, type StorageStats, type StorageStatsResult, type SumRow, type TimeSeriesRow, type TopNRow, type UpdateUserOptions, type UploadOptions, type UploadResult, type UploadUrlResult, type UserRecord, ValidationError, createClient };