hydrousdb 3.0.2 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +350 -975
- package/dist/index.cjs +942 -534
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +643 -398
- package/dist/index.d.ts +643 -398
- package/dist/index.mjs +1581 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +7 -6
- package/dist/index.js +0 -1179
- package/dist/index.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,67 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The server base URL — no trailing slash, no /api suffix.
|
|
3
|
+
* Routes in routes.ts already include /api, /api/analytics, /api/auth, /storage.
|
|
4
|
+
*/
|
|
5
|
+
declare const DEFAULT_BASE_URL = "https://db-api-82687684612.us-central1.run.app";
|
|
1
6
|
interface RequestOptions {
|
|
2
|
-
method
|
|
7
|
+
method: string;
|
|
3
8
|
body?: unknown;
|
|
9
|
+
rawBody?: FormData | Uint8Array | ArrayBuffer;
|
|
4
10
|
headers?: Record<string, string>;
|
|
5
|
-
rawBody?: string | FormData | Uint8Array<ArrayBuffer>;
|
|
6
|
-
contentType?: string;
|
|
7
11
|
}
|
|
8
12
|
declare class HttpClient {
|
|
9
13
|
private readonly baseUrl;
|
|
10
14
|
constructor(baseUrl: string);
|
|
11
|
-
request<T
|
|
12
|
-
get<T
|
|
13
|
-
post<T
|
|
14
|
-
put<T
|
|
15
|
-
patch<T
|
|
16
|
-
delete<T
|
|
17
|
-
|
|
15
|
+
request<T>(path: string, options: RequestOptions): Promise<T>;
|
|
16
|
+
get<T>(path: string, apiKey?: string, extraHeaders?: Record<string, string>): Promise<T>;
|
|
17
|
+
post<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
|
|
18
|
+
put<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
|
|
19
|
+
patch<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
|
|
20
|
+
delete<T>(path: string, apiKey: string, body?: unknown): Promise<T>;
|
|
21
|
+
/**
|
|
22
|
+
* PUT directly to a signed GCS URL.
|
|
23
|
+
* Uses XHR in browsers for onprogress support; fetch in Node.
|
|
24
|
+
*/
|
|
25
|
+
putToSignedUrl(signedUrl: string, data: Blob | Uint8Array | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
|
|
18
26
|
}
|
|
19
27
|
|
|
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
28
|
type StorageKeys = Record<string, string>;
|
|
34
29
|
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
|
-
*/
|
|
30
|
+
/** Auth service key (starts with `hk_auth_`). Required for auth routes. */
|
|
40
31
|
authKey: string;
|
|
41
32
|
/**
|
|
42
33
|
* Bucket security key (starts with `hk_bucket_`).
|
|
43
|
-
* Used for
|
|
44
|
-
* Obtain from your dashboard at https://hydrousdb.com/dashboard.
|
|
34
|
+
* Used for records and analytics routes.
|
|
45
35
|
*/
|
|
46
36
|
bucketSecurityKey: string;
|
|
47
37
|
/**
|
|
48
|
-
* Named storage keys
|
|
49
|
-
*
|
|
50
|
-
*
|
|
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
|
-
* ```
|
|
38
|
+
* Named storage keys — each value starts with `ssk_`.
|
|
39
|
+
* Key names are arbitrary labels you choose; pass the same label to `db.storage('label')`.
|
|
40
|
+
* Example: { avatars: 'ssk_…', documents: 'ssk_…' }
|
|
60
41
|
*/
|
|
61
42
|
storageKeys: StorageKeys;
|
|
62
43
|
/**
|
|
63
|
-
* Override the API base URL.
|
|
64
|
-
*
|
|
44
|
+
* Override the API base URL.
|
|
45
|
+
* Defaults to the official HydrousDB endpoint.
|
|
46
|
+
* Should NOT include /api — route paths are fully qualified in routes.ts.
|
|
65
47
|
*/
|
|
66
48
|
baseUrl?: string;
|
|
67
49
|
}
|
|
@@ -103,24 +85,26 @@ interface AuthResult {
|
|
|
103
85
|
interface UpdateUserOptions {
|
|
104
86
|
sessionId: string;
|
|
105
87
|
userId: string;
|
|
106
|
-
|
|
88
|
+
/** Fields to update. Sent as `updates: {...}` to the server. */
|
|
89
|
+
updates: Partial<Omit<UserRecord, 'id' | 'email' | 'createdAt'>>;
|
|
107
90
|
}
|
|
108
91
|
interface ChangePasswordOptions {
|
|
109
92
|
sessionId: string;
|
|
110
93
|
userId: string;
|
|
94
|
+
/** Sent to server as `oldPassword`. */
|
|
111
95
|
currentPassword: string;
|
|
112
96
|
newPassword: string;
|
|
113
97
|
}
|
|
114
98
|
interface ListUsersOptions {
|
|
115
99
|
sessionId: string;
|
|
116
100
|
limit?: number;
|
|
117
|
-
|
|
101
|
+
/** URL-encoded cursor returned by the previous page. */
|
|
102
|
+
cursor?: string;
|
|
118
103
|
}
|
|
119
104
|
interface ListUsersResult {
|
|
120
105
|
users: UserRecord[];
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
offset: number;
|
|
106
|
+
hasMore: boolean;
|
|
107
|
+
nextCursor: string | null;
|
|
124
108
|
}
|
|
125
109
|
type RecordData = Record<string, unknown>;
|
|
126
110
|
interface RecordResult {
|
|
@@ -131,7 +115,7 @@ interface RecordResult {
|
|
|
131
115
|
}
|
|
132
116
|
interface QueryFilter {
|
|
133
117
|
field: string;
|
|
134
|
-
op: '==' | '!=' | '>' | '<' | '>=' | '<=' | '
|
|
118
|
+
op: '==' | '!=' | '>' | '<' | '>=' | '<=' | 'contains';
|
|
135
119
|
value: string | number | boolean;
|
|
136
120
|
}
|
|
137
121
|
interface QueryOptions {
|
|
@@ -141,6 +125,10 @@ interface QueryOptions {
|
|
|
141
125
|
offset?: number;
|
|
142
126
|
orderBy?: string;
|
|
143
127
|
order?: 'asc' | 'desc';
|
|
128
|
+
/**
|
|
129
|
+
* Pagination cursor — pass the `nextCursor` from the previous page.
|
|
130
|
+
* Maps to `?cursor=` on the server.
|
|
131
|
+
*/
|
|
144
132
|
startAfter?: string;
|
|
145
133
|
startAt?: string;
|
|
146
134
|
endAt?: string;
|
|
@@ -156,19 +144,36 @@ interface PatchRecordOptions {
|
|
|
156
144
|
merge?: boolean;
|
|
157
145
|
}
|
|
158
146
|
interface RecordHistoryEntry {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
147
|
+
generation: number | null;
|
|
148
|
+
savedAt: number | null;
|
|
149
|
+
savedBy: string | null;
|
|
150
|
+
sizeBytes: number | null;
|
|
151
|
+
}
|
|
152
|
+
/** Options for `records.create()`. */
|
|
153
|
+
interface CreateRecordOptions {
|
|
154
|
+
/**
|
|
155
|
+
* Fields to index for server-side filtering.
|
|
156
|
+
* Only listed fields will be queryable via `query({ filters: [...] })`.
|
|
157
|
+
*/
|
|
158
|
+
queryableFields?: string[];
|
|
159
|
+
/** User email for audit trails. */
|
|
160
|
+
userEmail?: string;
|
|
161
|
+
/**
|
|
162
|
+
* Custom record ID for upsert behaviour.
|
|
163
|
+
* Format: `YYMMDD-segment1__segment2` (e.g. `260307-user_alice__post_1`).
|
|
164
|
+
* If the ID already exists the record is updated in-place.
|
|
165
|
+
*/
|
|
166
|
+
customRecordId?: string;
|
|
167
|
+
}
|
|
168
|
+
/** Options for `records.batchCreate()`. */
|
|
169
|
+
interface BatchCreateOptions {
|
|
170
|
+
queryableFields?: string[];
|
|
171
|
+
userEmail?: string;
|
|
163
172
|
}
|
|
164
173
|
interface UploadOptions {
|
|
165
|
-
/** Set to true to make the file publicly accessible without authentication. */
|
|
166
174
|
isPublic?: boolean;
|
|
167
|
-
/** Set to true to overwrite an existing file at the same path. */
|
|
168
175
|
overwrite?: boolean;
|
|
169
|
-
/** MIME type of the file. Auto-detected if omitted. */
|
|
170
176
|
mimeType?: string;
|
|
171
|
-
/** TTL in seconds for the signed upload URL (default 900 = 15 min). */
|
|
172
177
|
expiresInSeconds?: number;
|
|
173
178
|
}
|
|
174
179
|
interface UploadResult {
|
|
@@ -195,6 +200,7 @@ interface ListOptions {
|
|
|
195
200
|
interface FileEntry {
|
|
196
201
|
name: string;
|
|
197
202
|
path: string;
|
|
203
|
+
type?: 'file' | 'folder';
|
|
198
204
|
size?: number;
|
|
199
205
|
mimeType?: string;
|
|
200
206
|
isPublic?: boolean;
|
|
@@ -243,14 +249,27 @@ interface BatchUploadUrlResult {
|
|
|
243
249
|
index: number;
|
|
244
250
|
}>;
|
|
245
251
|
}
|
|
252
|
+
interface BatchDownloadResult {
|
|
253
|
+
succeeded: Array<{
|
|
254
|
+
index: number;
|
|
255
|
+
path: string;
|
|
256
|
+
mimeType: string;
|
|
257
|
+
size: number;
|
|
258
|
+
content: string;
|
|
259
|
+
}>;
|
|
260
|
+
failed: Array<{
|
|
261
|
+
index: number;
|
|
262
|
+
path: string;
|
|
263
|
+
error: string;
|
|
264
|
+
code?: string;
|
|
265
|
+
}>;
|
|
266
|
+
}
|
|
246
267
|
type QueryType = 'count' | 'distribution' | 'sum' | 'timeSeries' | 'fieldTimeSeries' | 'topN' | 'stats' | 'records' | 'multiMetric' | 'storageStats' | 'crossBucket';
|
|
247
268
|
type Aggregation = 'sum' | 'avg' | 'min' | 'max' | 'count';
|
|
248
269
|
type Granularity = 'hour' | 'day' | 'week' | 'month' | 'year';
|
|
249
270
|
type SortOrder = 'asc' | 'desc';
|
|
250
271
|
interface DateRange {
|
|
251
|
-
/** Start timestamp in milliseconds (Unix epoch). */
|
|
252
272
|
start?: number;
|
|
253
|
-
/** End timestamp in milliseconds (Unix epoch). */
|
|
254
273
|
end?: number;
|
|
255
274
|
}
|
|
256
275
|
interface AnalyticsFilter {
|
|
@@ -259,11 +278,8 @@ interface AnalyticsFilter {
|
|
|
259
278
|
value: string | number | boolean;
|
|
260
279
|
}
|
|
261
280
|
interface MetricDefinition {
|
|
262
|
-
/** Field name in your records to aggregate. */
|
|
263
281
|
field: string;
|
|
264
|
-
/** Alias used in the result object. Must be a safe identifier. */
|
|
265
282
|
name: string;
|
|
266
|
-
/** Aggregation function. Defaults to 'count'. */
|
|
267
283
|
aggregation?: Aggregation;
|
|
268
284
|
}
|
|
269
285
|
interface AnalyticsQuery {
|
|
@@ -282,7 +298,6 @@ interface AnalyticsQuery {
|
|
|
282
298
|
order?: SortOrder;
|
|
283
299
|
n?: number;
|
|
284
300
|
metrics?: MetricDefinition[];
|
|
285
|
-
/** For crossBucket queries: list of bucket names to compare. */
|
|
286
301
|
bucketKeys?: string[];
|
|
287
302
|
}
|
|
288
303
|
interface AnalyticsResult<T = unknown> {
|
|
@@ -337,54 +352,127 @@ interface CrossBucketRow {
|
|
|
337
352
|
}
|
|
338
353
|
|
|
339
354
|
/**
|
|
340
|
-
* AuthClient —
|
|
341
|
-
*
|
|
355
|
+
* AuthClient — user authentication for a project.
|
|
356
|
+
*
|
|
357
|
+
* All routes are under `/api/auth` (NOT `/api/auth/:bucketKey`).
|
|
358
|
+
* The server resolves which user bucket to use from the API key itself.
|
|
359
|
+
* Uses `X-Api-Key: <authKey>`.
|
|
342
360
|
*
|
|
343
361
|
* @example
|
|
344
362
|
* ```ts
|
|
345
|
-
* const
|
|
346
|
-
* const auth = db.auth('my-app-users');
|
|
363
|
+
* const auth = db.auth();
|
|
347
364
|
* const { user, session } = await auth.signup({ email: 'alice@example.com', password: 'hunter2' });
|
|
348
365
|
* ```
|
|
349
366
|
*/
|
|
350
367
|
declare class AuthClient {
|
|
351
368
|
private readonly http;
|
|
352
369
|
private readonly authKey;
|
|
353
|
-
|
|
354
|
-
constructor(http: HttpClient, authKey: string, bucketKey: string);
|
|
370
|
+
constructor(http: HttpClient, authKey: string);
|
|
355
371
|
private post;
|
|
356
372
|
private get;
|
|
357
373
|
private patch;
|
|
358
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Register a new user account.
|
|
376
|
+
*
|
|
377
|
+
* Server: POST /api/auth/signup
|
|
378
|
+
* Body: { email, password, fullName?, ...extraData }
|
|
379
|
+
*/
|
|
359
380
|
signup(options: SignupOptions): Promise<AuthResult>;
|
|
381
|
+
/**
|
|
382
|
+
* Sign in with email + password.
|
|
383
|
+
*
|
|
384
|
+
* Server: POST /api/auth/signin (NOT /login)
|
|
385
|
+
* Body: { email, password }
|
|
386
|
+
*/
|
|
360
387
|
login(options: LoginOptions): Promise<AuthResult>;
|
|
361
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Sign out — revoke a session (or all sessions with `allDevices: true`).
|
|
390
|
+
*
|
|
391
|
+
* Server: POST /api/auth/signout
|
|
392
|
+
* Body: { sessionId, allDevices? }
|
|
393
|
+
*/
|
|
394
|
+
logout(options: {
|
|
362
395
|
sessionId: string;
|
|
396
|
+
allDevices?: boolean;
|
|
363
397
|
}): Promise<void>;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Validate an existing session and retrieve the current user.
|
|
400
|
+
*
|
|
401
|
+
* Server: POST /api/auth/session/validate
|
|
402
|
+
* Body: { sessionId }
|
|
403
|
+
*/
|
|
404
|
+
validateSession(sessionId: string): Promise<{
|
|
405
|
+
user: UserRecord;
|
|
406
|
+
session: {
|
|
407
|
+
sessionId: string;
|
|
408
|
+
expiresAt: number;
|
|
409
|
+
};
|
|
410
|
+
}>;
|
|
411
|
+
/**
|
|
412
|
+
* Rotate a refresh token to get a new session.
|
|
413
|
+
*
|
|
414
|
+
* Server: POST /api/auth/session/refresh
|
|
415
|
+
* Body: { refreshToken }
|
|
416
|
+
*/
|
|
417
|
+
refreshSession(refreshToken: string): Promise<Session>;
|
|
418
|
+
/**
|
|
419
|
+
* Fetch a user by ID.
|
|
420
|
+
*
|
|
421
|
+
* Server: GET /api/auth/user?userId=:userId
|
|
422
|
+
*/
|
|
423
|
+
getUser(userId: string): Promise<UserRecord>;
|
|
424
|
+
/**
|
|
425
|
+
* Update a user's profile fields.
|
|
426
|
+
* Regular users can only update themselves; admins can update any user.
|
|
427
|
+
*
|
|
428
|
+
* Server: PATCH /api/auth/user
|
|
429
|
+
* Body: { sessionId, userId, updates: { ...fields } }
|
|
430
|
+
*/
|
|
370
431
|
updateUser(options: UpdateUserOptions): Promise<UserRecord>;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
432
|
+
/**
|
|
433
|
+
* Soft-delete a user account.
|
|
434
|
+
* Regular users can only delete themselves; admins can delete any user.
|
|
435
|
+
*
|
|
436
|
+
* Server: DELETE /api/auth/user?userId=:userId
|
|
437
|
+
* Body: { sessionId }
|
|
438
|
+
*/
|
|
439
|
+
deleteUser(sessionId: string, userId: string): Promise<void>;
|
|
440
|
+
/**
|
|
441
|
+
* List all users (paginated). Admin only.
|
|
442
|
+
*
|
|
443
|
+
* Server: GET /api/auth/users?limit=&cursor=
|
|
444
|
+
* The sessionId is passed via the X-Session-Id header (GET body is unreliable).
|
|
445
|
+
*/
|
|
375
446
|
listUsers(options: ListUsersOptions): Promise<ListUsersResult>;
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
447
|
+
/**
|
|
448
|
+
* Permanently delete a user (hard delete — cannot be undone).
|
|
449
|
+
* Admin only. Charged at 1 HC per deletion.
|
|
450
|
+
*
|
|
451
|
+
* Server: DELETE /api/auth/user/hard?userId=:userId
|
|
452
|
+
* Body: { sessionId }
|
|
453
|
+
*/
|
|
454
|
+
hardDeleteUser(sessionId: string, userId: string): Promise<void>;
|
|
455
|
+
/**
|
|
456
|
+
* Delete multiple users at once (soft or hard). Admin only.
|
|
457
|
+
*
|
|
458
|
+
* Server: DELETE /api/auth/users/bulk
|
|
459
|
+
* Body: { userIds, hard?, sessionId }
|
|
460
|
+
*/
|
|
461
|
+
bulkDeleteUsers(options: {
|
|
381
462
|
sessionId: string;
|
|
382
463
|
userIds: string[];
|
|
464
|
+
hard?: boolean;
|
|
383
465
|
}): Promise<{
|
|
384
|
-
|
|
385
|
-
failed:
|
|
466
|
+
succeeded: number;
|
|
467
|
+
failed: number;
|
|
386
468
|
}>;
|
|
387
|
-
|
|
469
|
+
/**
|
|
470
|
+
* Lock a user account for a specified duration. Admin only.
|
|
471
|
+
*
|
|
472
|
+
* Server: POST /api/auth/account/lock
|
|
473
|
+
* Body: { sessionId, userId, duration? } — duration in ms, default 15 min
|
|
474
|
+
*/
|
|
475
|
+
lockAccount(options: {
|
|
388
476
|
sessionId: string;
|
|
389
477
|
userId: string;
|
|
390
478
|
duration?: number;
|
|
@@ -392,103 +480,252 @@ declare class AuthClient {
|
|
|
392
480
|
lockedUntil: number;
|
|
393
481
|
unlockTime: string;
|
|
394
482
|
}>;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
483
|
+
/**
|
|
484
|
+
* Unlock a locked user account. Admin only.
|
|
485
|
+
*
|
|
486
|
+
* Server: POST /api/auth/account/unlock
|
|
487
|
+
* Body: { sessionId, userId }
|
|
488
|
+
*/
|
|
489
|
+
unlockAccount(sessionId: string, userId: string): Promise<void>;
|
|
490
|
+
/**
|
|
491
|
+
* Change a user's password. Requires both a valid session AND the old password.
|
|
492
|
+
*
|
|
493
|
+
* Server: POST /api/auth/password/change
|
|
494
|
+
* Body: { sessionId, userId, oldPassword, newPassword }
|
|
495
|
+
*/
|
|
399
496
|
changePassword(options: ChangePasswordOptions): Promise<void>;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Request a password reset email.
|
|
499
|
+
* Always returns success regardless of whether the email exists (prevents enumeration).
|
|
500
|
+
*
|
|
501
|
+
* Server: POST /api/auth/password/reset/request
|
|
502
|
+
* Body: { email }
|
|
503
|
+
*/
|
|
504
|
+
requestPasswordReset(email: string): Promise<void>;
|
|
505
|
+
/**
|
|
506
|
+
* Confirm a password reset using the token from the reset email.
|
|
507
|
+
*
|
|
508
|
+
* Server: POST /api/auth/password/reset/confirm
|
|
509
|
+
* Body: { resetToken, newPassword }
|
|
510
|
+
*/
|
|
511
|
+
confirmPasswordReset(resetToken: string, newPassword: string): Promise<void>;
|
|
512
|
+
/**
|
|
513
|
+
* Request a verification email be sent to a user.
|
|
514
|
+
*
|
|
515
|
+
* Server: POST /api/auth/email/verify/request
|
|
516
|
+
* Body: { userId }
|
|
517
|
+
*/
|
|
518
|
+
requestEmailVerification(userId: string): Promise<void>;
|
|
519
|
+
/**
|
|
520
|
+
* Confirm email ownership using the token from the verification email.
|
|
521
|
+
*
|
|
522
|
+
* Server: POST /api/auth/email/verify/confirm
|
|
523
|
+
* Body: { verifyToken }
|
|
524
|
+
*/
|
|
525
|
+
confirmEmailVerification(verifyToken: string): Promise<void>;
|
|
526
|
+
private _buildAuthResult;
|
|
413
527
|
}
|
|
414
528
|
|
|
415
529
|
/**
|
|
416
|
-
* RecordsClient — CRUD + query for a single bucket.
|
|
417
|
-
*
|
|
530
|
+
* RecordsClient — typed CRUD + batch + query for a single bucket.
|
|
531
|
+
*
|
|
532
|
+
* All requests use `X-Api-Key: <bucketSecurityKey>`.
|
|
533
|
+
* Routes: GET|POST|PATCH|DELETE /api/:bucketKey
|
|
418
534
|
*
|
|
419
535
|
* @example
|
|
420
536
|
* ```ts
|
|
421
|
-
*
|
|
422
|
-
* const posts = db.records('blog-posts');
|
|
423
|
-
* const post
|
|
537
|
+
* interface Post { title: string; published: boolean }
|
|
538
|
+
* const posts = db.records<Post>('blog-posts');
|
|
539
|
+
* const post = await posts.create({ title: 'Hello', published: false });
|
|
424
540
|
* ```
|
|
425
541
|
*/
|
|
426
542
|
declare class RecordsClient<T extends RecordData = RecordData> {
|
|
427
543
|
private readonly http;
|
|
428
544
|
private readonly bucketKey;
|
|
429
|
-
private readonly
|
|
430
|
-
private readonly bucketKey_;
|
|
545
|
+
private readonly apiKey;
|
|
431
546
|
constructor(http: HttpClient, bucketSecurityKey: string, bucketKey: string);
|
|
432
|
-
|
|
433
|
-
|
|
547
|
+
/**
|
|
548
|
+
* Create a new record (auto-generated ID) or upsert by `customRecordId`.
|
|
549
|
+
*
|
|
550
|
+
* Server: POST /api/:bucketKey
|
|
551
|
+
* Body: { values, queryableFields?, userEmail?, customRecordId? }
|
|
552
|
+
* Returns 201 for new records, 200 for upserts.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* const post = await posts.create(
|
|
557
|
+
* { title: 'Hello', status: 'draft', authorId: 'u1' },
|
|
558
|
+
* { queryableFields: ['status', 'authorId'], userEmail: 'alice@example.com' },
|
|
559
|
+
* );
|
|
560
|
+
* ```
|
|
561
|
+
*/
|
|
562
|
+
create(data: T, options?: CreateRecordOptions): Promise<T & RecordResult>;
|
|
563
|
+
/**
|
|
564
|
+
* Get a single record by ID.
|
|
565
|
+
*
|
|
566
|
+
* Server: GET /api/:bucketKey?recordId=:id
|
|
567
|
+
*/
|
|
434
568
|
get(id: string): Promise<T & RecordResult>;
|
|
435
|
-
|
|
436
|
-
|
|
569
|
+
/**
|
|
570
|
+
* Partially update an existing record (PATCH semantics).
|
|
571
|
+
* Supports write-filter sentinels: `{ __op: 'increment', delta: 1 }` etc.
|
|
572
|
+
*
|
|
573
|
+
* Server: PATCH /api/:bucketKey
|
|
574
|
+
* Body: { recordId, values, userEmail?, track_record_history? }
|
|
575
|
+
*/
|
|
576
|
+
patch(id: string, data: Partial<T>, options?: PatchRecordOptions & {
|
|
577
|
+
userEmail?: string;
|
|
578
|
+
trackHistory?: boolean;
|
|
579
|
+
}): Promise<{
|
|
580
|
+
id: string;
|
|
581
|
+
updatedAt?: number;
|
|
582
|
+
}>;
|
|
583
|
+
/**
|
|
584
|
+
* Delete a record permanently.
|
|
585
|
+
*
|
|
586
|
+
* Server: DELETE /api/:bucketKey?recordId=:id
|
|
587
|
+
*/
|
|
437
588
|
delete(id: string): Promise<void>;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
589
|
+
/**
|
|
590
|
+
* Check whether a record exists (HEAD request — very lightweight).
|
|
591
|
+
*
|
|
592
|
+
* Server: HEAD /api/:bucketKey?recordId=:id
|
|
593
|
+
* Returns true if the record exists, false if 404.
|
|
594
|
+
*/
|
|
595
|
+
exists(id: string): Promise<boolean>;
|
|
596
|
+
/**
|
|
597
|
+
* Get a historical snapshot of a record at a specific generation.
|
|
598
|
+
*
|
|
599
|
+
* Server: GET /api/:bucketKey?recordId=:id&generation=:gen
|
|
600
|
+
*/
|
|
601
|
+
getVersion(id: string, generation: string | number): Promise<T & RecordResult>;
|
|
602
|
+
/**
|
|
603
|
+
* Get the version history of a record.
|
|
604
|
+
*
|
|
605
|
+
* Server: GET /api/:bucketKey?recordId=:id&showHistory=true
|
|
606
|
+
*/
|
|
607
|
+
getHistory(id: string): Promise<RecordHistoryEntry[]>;
|
|
608
|
+
/**
|
|
609
|
+
* Create up to 500 records in one request.
|
|
610
|
+
* Each record may include `_customRecordId` for upsert behaviour.
|
|
611
|
+
*
|
|
612
|
+
* Server: POST /api/:bucketKey/batch/insert
|
|
613
|
+
* Body: { records, queryableFields?, userEmail? }
|
|
614
|
+
*
|
|
615
|
+
* @example
|
|
616
|
+
* ```ts
|
|
617
|
+
* const results = await posts.batchCreate(
|
|
618
|
+
* [{ title: 'A' }, { title: 'B' }],
|
|
619
|
+
* { queryableFields: ['title'] },
|
|
620
|
+
* );
|
|
621
|
+
* ```
|
|
622
|
+
*/
|
|
623
|
+
batchCreate(items: T[], options?: BatchCreateOptions): Promise<{
|
|
624
|
+
results: (T & RecordResult)[];
|
|
625
|
+
errors: unknown[];
|
|
626
|
+
successful: number;
|
|
627
|
+
failed: number;
|
|
628
|
+
}>;
|
|
629
|
+
/**
|
|
630
|
+
* Update up to 500 records in one request.
|
|
631
|
+
*
|
|
632
|
+
* Server: POST /api/:bucketKey/batch/update
|
|
633
|
+
* Body: { updates: [{ recordId, values }], userEmail? }
|
|
634
|
+
*/
|
|
635
|
+
batchUpdate(updates: Array<{
|
|
636
|
+
recordId: string;
|
|
637
|
+
values: Partial<T>;
|
|
638
|
+
}>, userEmail?: string): Promise<{
|
|
639
|
+
successful: number;
|
|
441
640
|
failed: string[];
|
|
442
641
|
}>;
|
|
642
|
+
/**
|
|
643
|
+
* Delete up to 500 records in one request.
|
|
644
|
+
*
|
|
645
|
+
* Server: POST /api/:bucketKey/batch/delete
|
|
646
|
+
* Body: { recordIds, userEmail? }
|
|
647
|
+
*/
|
|
648
|
+
batchDelete(ids: string[], userEmail?: string): Promise<{
|
|
649
|
+
successful: number;
|
|
650
|
+
failed: string[];
|
|
651
|
+
}>;
|
|
652
|
+
/**
|
|
653
|
+
* Query records with filters, sorting, and cursor-based pagination.
|
|
654
|
+
*
|
|
655
|
+
* Server: GET /api/:bucketKey?field=value&field[op]=value&limit=&sortBy=&sortOrder=&cursor=
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```ts
|
|
659
|
+
* const { records, hasMore, nextCursor } = await posts.query({
|
|
660
|
+
* filters: [{ field: 'status', op: '==', value: 'published' }],
|
|
661
|
+
* orderBy: 'createdAt',
|
|
662
|
+
* order: 'desc',
|
|
663
|
+
* limit: 20,
|
|
664
|
+
* });
|
|
665
|
+
* ```
|
|
666
|
+
*/
|
|
443
667
|
query(options?: QueryOptions): Promise<QueryResult<T>>;
|
|
668
|
+
/**
|
|
669
|
+
* Retrieve all records matching options (no filter support — use `query()` for filters).
|
|
670
|
+
*/
|
|
444
671
|
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
672
|
}
|
|
449
673
|
|
|
450
674
|
/**
|
|
451
|
-
* AnalyticsClient — BigQuery-powered aggregations for a bucket.
|
|
452
|
-
*
|
|
675
|
+
* AnalyticsClient — BigQuery-powered aggregations for a single bucket.
|
|
676
|
+
*
|
|
677
|
+
* All methods POST a `queryType` body to `POST /api/analytics/:bucketKey`.
|
|
678
|
+
* The `dateRange` is passed as `{ start, end }` ms timestamps — the server
|
|
679
|
+
* converts them to ISO date strings internally.
|
|
680
|
+
* Uses `X-Api-Key: <bucketSecurityKey>`.
|
|
453
681
|
*
|
|
454
682
|
* @example
|
|
455
683
|
* ```ts
|
|
456
|
-
* const db = createClient({ authKey: '…', bucketSecurityKey: 'hk_bucket_…', storageKeys: { main: '…' } });
|
|
457
684
|
* const analytics = db.analytics('orders');
|
|
458
685
|
* const { count } = await analytics.count();
|
|
686
|
+
* const top5 = await analytics.topN({ field: 'country', n: 5 });
|
|
459
687
|
* ```
|
|
460
688
|
*/
|
|
461
689
|
declare class AnalyticsClient {
|
|
462
690
|
private readonly http;
|
|
463
691
|
private readonly bucketSecurityKey;
|
|
464
|
-
private readonly
|
|
692
|
+
private readonly bucketKey;
|
|
465
693
|
constructor(http: HttpClient, bucketSecurityKey: string, bucketKey: string);
|
|
466
694
|
private run;
|
|
695
|
+
/** Count all records, optionally within a date range. */
|
|
467
696
|
count(opts?: {
|
|
468
697
|
dateRange?: DateRange;
|
|
469
698
|
}): Promise<CountResult>;
|
|
699
|
+
/**
|
|
700
|
+
* Get value distribution for a field (e.g. records per status).
|
|
701
|
+
* `order` maps to `sortBy` on the wire (the server param name).
|
|
702
|
+
*/
|
|
470
703
|
distribution(opts: {
|
|
471
704
|
field: string;
|
|
472
705
|
limit?: number;
|
|
473
706
|
order?: SortOrder;
|
|
474
707
|
dateRange?: DateRange;
|
|
475
708
|
}): Promise<DistributionRow[]>;
|
|
709
|
+
/** Sum a numeric field, optionally grouped by another field. */
|
|
476
710
|
sum(opts: {
|
|
477
711
|
field: string;
|
|
478
712
|
groupBy?: string;
|
|
479
713
|
limit?: number;
|
|
480
714
|
dateRange?: DateRange;
|
|
481
715
|
}): Promise<SumRow[]>;
|
|
716
|
+
/** Count of records over time, bucketed by granularity. */
|
|
482
717
|
timeSeries(opts?: {
|
|
483
718
|
granularity?: Granularity;
|
|
484
719
|
dateRange?: DateRange;
|
|
485
720
|
}): Promise<TimeSeriesRow[]>;
|
|
721
|
+
/** Aggregate a numeric field over time. */
|
|
486
722
|
fieldTimeSeries(opts: {
|
|
487
723
|
field: string;
|
|
488
724
|
aggregation?: Aggregation;
|
|
489
725
|
granularity?: Granularity;
|
|
490
726
|
dateRange?: DateRange;
|
|
491
727
|
}): Promise<FieldTimeSeriesRow[]>;
|
|
728
|
+
/** Top N values for a field by count. */
|
|
492
729
|
topN(opts: {
|
|
493
730
|
field: string;
|
|
494
731
|
n?: number;
|
|
@@ -496,10 +733,15 @@ declare class AnalyticsClient {
|
|
|
496
733
|
order?: SortOrder;
|
|
497
734
|
dateRange?: DateRange;
|
|
498
735
|
}): Promise<TopNRow[]>;
|
|
736
|
+
/** Statistical summary (min, max, avg, sum, count, stddev) for a numeric field. */
|
|
499
737
|
stats(opts: {
|
|
500
738
|
field: string;
|
|
501
739
|
dateRange?: DateRange;
|
|
502
740
|
}): Promise<FieldStats>;
|
|
741
|
+
/**
|
|
742
|
+
* Fetch filtered records via the analytics engine (BigQuery).
|
|
743
|
+
* Bypasses Firestore pagination — useful for large result sets.
|
|
744
|
+
*/
|
|
503
745
|
records<T extends RecordData = RecordData>(opts?: {
|
|
504
746
|
filters?: AnalyticsFilter[];
|
|
505
747
|
selectFields?: string[];
|
|
@@ -509,97 +751,96 @@ declare class AnalyticsClient {
|
|
|
509
751
|
order?: SortOrder;
|
|
510
752
|
dateRange?: DateRange;
|
|
511
753
|
}): Promise<(T & RecordResult)[]>;
|
|
754
|
+
/**
|
|
755
|
+
* Compute multiple aggregations in a single request.
|
|
756
|
+
* `metrics` must have valid `field` and `name` (used as BigQuery column aliases).
|
|
757
|
+
*/
|
|
512
758
|
multiMetric(opts: {
|
|
513
759
|
metrics: MetricDefinition[];
|
|
514
760
|
dateRange?: DateRange;
|
|
515
761
|
}): Promise<MultiMetricResult>;
|
|
762
|
+
/** Storage usage stats for the bucket (record count, bytes, avg/min/max size). */
|
|
516
763
|
storageStats(opts?: {
|
|
517
764
|
dateRange?: DateRange;
|
|
518
765
|
}): Promise<StorageStatsResult>;
|
|
766
|
+
/**
|
|
767
|
+
* Compare a metric across multiple buckets in one query.
|
|
768
|
+
* The caller's key must have read access to EVERY bucket in `bucketKeys`.
|
|
769
|
+
* System buckets (`users`, `_sys_*`) are blocked server-side.
|
|
770
|
+
*/
|
|
519
771
|
crossBucket(opts: {
|
|
520
772
|
bucketKeys: string[];
|
|
521
773
|
field: string;
|
|
522
774
|
aggregation?: Aggregation;
|
|
523
775
|
dateRange?: DateRange;
|
|
524
776
|
}): Promise<CrossBucketRow[]>;
|
|
777
|
+
/**
|
|
778
|
+
* Raw query — escape hatch when the typed helpers don't cover your case.
|
|
779
|
+
*/
|
|
525
780
|
query<T = unknown>(query: AnalyticsQuery): Promise<AnalyticsResult<T>>;
|
|
526
781
|
}
|
|
527
782
|
|
|
528
783
|
/**
|
|
529
784
|
* 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
785
|
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
786
|
+
* Uses `X-Storage-Key: <ssk_…>` header — separate from auth and bucket keys.
|
|
787
|
+
* Files are scoped server-side to `hydrous-storage/{ownerId}/{userPath}`.
|
|
788
|
+
* You only ever deal with your own `userPath` — the server handles scoping.
|
|
789
|
+
*
|
|
790
|
+
* Get a StorageManager via `db.storage('keyName')` where the name matches
|
|
791
|
+
* one of the keys defined in `storageKeys` when calling `createClient`.
|
|
535
792
|
*
|
|
536
793
|
* @example
|
|
537
794
|
* ```ts
|
|
538
|
-
* const
|
|
539
|
-
* const
|
|
540
|
-
*
|
|
795
|
+
* const storage = db.storage('avatars');
|
|
796
|
+
* const result = await storage.upload(file, 'alice.jpg', { isPublic: true });
|
|
797
|
+
* console.log(result.publicUrl);
|
|
541
798
|
* ```
|
|
542
799
|
*/
|
|
543
800
|
declare class StorageManager {
|
|
544
801
|
private readonly http;
|
|
545
802
|
private readonly storageKey;
|
|
546
|
-
private readonly basePath;
|
|
547
803
|
constructor(http: HttpClient, storageKey: string);
|
|
548
|
-
/** Headers for all storage requests — uses X-Storage-Key, not X-Api-Key. */
|
|
549
804
|
private get authHeaders();
|
|
550
805
|
/**
|
|
551
|
-
* Upload a file
|
|
552
|
-
* For files >10 MB or when
|
|
806
|
+
* Upload a file in one step (server-buffered, up to 500 MB).
|
|
807
|
+
* For files >10 MB or when upload progress tracking is needed, use the
|
|
808
|
+
* signed URL flow: `getUploadUrl()` → `uploadToSignedUrl()` → `confirmUpload()`.
|
|
553
809
|
*
|
|
554
|
-
*
|
|
555
|
-
* @param path Destination path in your storage (e.g. `"avatars/alice.jpg"`).
|
|
556
|
-
* @param options Upload options: isPublic, overwrite, mimeType.
|
|
810
|
+
* Server: POST /storage/upload (multipart/form-data)
|
|
557
811
|
*
|
|
558
812
|
* @example
|
|
559
813
|
* ```ts
|
|
560
|
-
* // Upload a public avatar
|
|
561
814
|
* const result = await storage.upload(file, 'avatars/alice.jpg', { isPublic: true });
|
|
562
|
-
* console.log(result.publicUrl);
|
|
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
|
|
815
|
+
* console.log(result.publicUrl);
|
|
567
816
|
* ```
|
|
568
817
|
*/
|
|
569
|
-
upload(data: Blob | Uint8Array
|
|
818
|
+
upload(data: Blob | Uint8Array | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
|
|
570
819
|
/**
|
|
571
|
-
* Upload raw
|
|
820
|
+
* Upload raw string or JSON data directly as a file.
|
|
821
|
+
*
|
|
822
|
+
* Server: POST /storage/upload-raw
|
|
823
|
+
* Body: { path, content, mimeType?, isPublic?, overwrite? }
|
|
572
824
|
*
|
|
573
825
|
* @example
|
|
574
826
|
* ```ts
|
|
575
|
-
*
|
|
576
|
-
* { config: { theme: 'dark' } },
|
|
577
|
-
* 'settings/user-config.json',
|
|
578
|
-
* { isPublic: false },
|
|
579
|
-
* );
|
|
827
|
+
* await storage.uploadRaw({ theme: 'dark' }, 'settings/config.json');
|
|
580
828
|
* ```
|
|
581
829
|
*/
|
|
582
830
|
uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
|
|
583
831
|
/**
|
|
584
|
-
* Step 1
|
|
585
|
-
*
|
|
832
|
+
* Step 1 — get a signed GCS PUT URL for direct client-to-GCS upload.
|
|
833
|
+
*
|
|
834
|
+
* Server: POST /storage/upload-url
|
|
835
|
+
* Body: { path, mimeType, size, isPublic?, overwrite?, expiresIn? }
|
|
586
836
|
*
|
|
587
837
|
* @example
|
|
588
838
|
* ```ts
|
|
589
|
-
* const { uploadUrl, path:
|
|
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`);
|
|
839
|
+
* const { uploadUrl, path: p } = await storage.getUploadUrl({
|
|
840
|
+
* path: 'videos/intro.mp4', mimeType: 'video/mp4', size: file.size,
|
|
599
841
|
* });
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
* const result = await storage.confirmUpload({ path: confirmedPath, mimeType: 'video/mp4', isPublic: true });
|
|
842
|
+
* await storage.uploadToSignedUrl(uploadUrl, file, 'video/mp4', pct => setProgress(pct));
|
|
843
|
+
* const result = await storage.confirmUpload({ path: p, mimeType: 'video/mp4' });
|
|
603
844
|
* ```
|
|
604
845
|
*/
|
|
605
846
|
getUploadUrl(opts: {
|
|
@@ -611,28 +852,15 @@ declare class StorageManager {
|
|
|
611
852
|
expiresInSeconds?: number;
|
|
612
853
|
}): Promise<UploadUrlResult>;
|
|
613
854
|
/**
|
|
614
|
-
*
|
|
615
|
-
*
|
|
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.
|
|
855
|
+
* Step 2 — upload data directly to the signed GCS URL.
|
|
856
|
+
* Supports progress tracking in browser environments via XHR.
|
|
621
857
|
*/
|
|
622
|
-
uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array
|
|
858
|
+
uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
|
|
623
859
|
/**
|
|
624
|
-
* Step 3
|
|
625
|
-
* Confirm a direct upload and register metadata on the server.
|
|
860
|
+
* Step 3 — confirm a direct upload and register metadata server-side.
|
|
626
861
|
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
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
|
-
* ```
|
|
862
|
+
* Server: POST /storage/confirm
|
|
863
|
+
* Body: { path, mimeType, isPublic? }
|
|
636
864
|
*/
|
|
637
865
|
confirmUpload(opts: {
|
|
638
866
|
path: string;
|
|
@@ -640,112 +868,79 @@ declare class StorageManager {
|
|
|
640
868
|
isPublic?: boolean;
|
|
641
869
|
}): Promise<UploadResult>;
|
|
642
870
|
/**
|
|
643
|
-
* Get signed upload URLs for
|
|
871
|
+
* Get signed upload URLs for up to 50 files at once.
|
|
644
872
|
*
|
|
645
|
-
*
|
|
646
|
-
*
|
|
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
|
-
* ```
|
|
873
|
+
* Server: POST /storage/batch-upload-urls
|
|
874
|
+
* Body: { files: [...], expiresIn? }
|
|
658
875
|
*/
|
|
659
876
|
getBatchUploadUrls(files: BatchUploadItem[]): Promise<BatchUploadUrlResult>;
|
|
660
877
|
/**
|
|
661
878
|
* Confirm multiple direct uploads at once.
|
|
879
|
+
*
|
|
880
|
+
* Server: POST /storage/batch-confirm
|
|
881
|
+
* Body: { files: [{ path, mimeType, isPublic? }] }
|
|
662
882
|
*/
|
|
663
883
|
batchConfirmUploads(items: Array<{
|
|
664
884
|
path: string;
|
|
665
885
|
mimeType: string;
|
|
666
886
|
isPublic?: boolean;
|
|
667
|
-
}>): Promise<
|
|
887
|
+
}>): Promise<{
|
|
888
|
+
succeeded: UploadResult[];
|
|
889
|
+
failed: Array<{
|
|
890
|
+
path: string;
|
|
891
|
+
error: string;
|
|
892
|
+
}>;
|
|
893
|
+
}>;
|
|
668
894
|
/**
|
|
669
895
|
* Download a private file as an ArrayBuffer.
|
|
670
896
|
* For public files, use the `publicUrl` directly — no SDK needed.
|
|
671
897
|
*
|
|
672
|
-
*
|
|
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
|
-
* ```
|
|
898
|
+
* Server: GET /storage/download/:path (requires X-Storage-Key)
|
|
679
899
|
*/
|
|
680
900
|
download(path: string): Promise<ArrayBuffer>;
|
|
681
901
|
/**
|
|
682
|
-
* Download
|
|
902
|
+
* Download up to 20 files at once. Returns base64-encoded content.
|
|
683
903
|
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
* const files = await storage.batchDownload(['docs/a.pdf', 'docs/b.pdf']);
|
|
687
|
-
* ```
|
|
904
|
+
* Server: POST /storage/batch-download
|
|
905
|
+
* Body: { paths, concurrency? }
|
|
688
906
|
*/
|
|
689
|
-
batchDownload(paths: string[]): Promise<
|
|
907
|
+
batchDownload(paths: string[], concurrency?: number): Promise<BatchDownloadResult>;
|
|
690
908
|
/**
|
|
691
909
|
* List files and folders at a given path prefix.
|
|
692
910
|
*
|
|
911
|
+
* Server: GET /storage/list?prefix=&limit=&cursor=
|
|
912
|
+
*
|
|
693
913
|
* @example
|
|
694
914
|
* ```ts
|
|
695
|
-
*
|
|
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
|
|
915
|
+
* const { files, folders, hasMore, nextCursor } = await storage.list({ prefix: 'avatars/', limit: 20 });
|
|
705
916
|
* const page2 = await storage.list({ prefix: 'avatars/', cursor: nextCursor });
|
|
706
917
|
* ```
|
|
707
918
|
*/
|
|
708
919
|
list(opts?: ListOptions): Promise<ListResult>;
|
|
709
920
|
/**
|
|
710
|
-
* Get metadata
|
|
921
|
+
* Get file metadata: size, MIME type, visibility, URLs.
|
|
711
922
|
*
|
|
712
|
-
*
|
|
713
|
-
* ```ts
|
|
714
|
-
* const meta = await storage.getMetadata('avatars/alice.jpg');
|
|
715
|
-
* console.log(meta.size, meta.isPublic, meta.publicUrl);
|
|
716
|
-
* ```
|
|
923
|
+
* Server: GET /storage/metadata/:path
|
|
717
924
|
*/
|
|
718
925
|
getMetadata(path: string): Promise<FileMetadata>;
|
|
719
926
|
/**
|
|
720
927
|
* Generate a time-limited download URL for a private file.
|
|
721
|
-
*
|
|
928
|
+
* Can be shared externally — no X-Storage-Key required to access.
|
|
722
929
|
*
|
|
723
|
-
*
|
|
724
|
-
* > are NOT tracked. Use `downloadUrl` for tracked downloads.
|
|
930
|
+
* Note: Downloads via signed URLs bypass the server — stats are NOT tracked.
|
|
725
931
|
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
932
|
+
* Server: POST /storage/signed-url
|
|
933
|
+
* Body: { path, expiresIn? }
|
|
728
934
|
*
|
|
729
|
-
* @
|
|
730
|
-
*
|
|
731
|
-
* const { signedUrl, expiresAt } = await storage.getSignedUrl('docs/invoice.pdf', 1800);
|
|
732
|
-
* // Share signedUrl with the recipient — it expires in 30 minutes.
|
|
733
|
-
* ```
|
|
935
|
+
* @param path File path.
|
|
936
|
+
* @param expiresIn URL lifetime in seconds (default 3600 = 1 hour).
|
|
734
937
|
*/
|
|
735
938
|
getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
|
|
736
939
|
/**
|
|
737
|
-
* Change a file's visibility between public and private
|
|
940
|
+
* Change a file's visibility between public and private.
|
|
738
941
|
*
|
|
739
|
-
*
|
|
740
|
-
*
|
|
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
|
-
* ```
|
|
942
|
+
* Server: PATCH /storage/visibility
|
|
943
|
+
* Body: { path, isPublic }
|
|
749
944
|
*/
|
|
750
945
|
setVisibility(path: string, isPublic: boolean): Promise<{
|
|
751
946
|
path: string;
|
|
@@ -754,44 +949,33 @@ declare class StorageManager {
|
|
|
754
949
|
downloadUrl: string | null;
|
|
755
950
|
}>;
|
|
756
951
|
/**
|
|
757
|
-
* Create a folder (
|
|
952
|
+
* Create a folder (empty GCS object used as a prefix marker).
|
|
758
953
|
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* await storage.createFolder('uploads/2025/');
|
|
762
|
-
* ```
|
|
954
|
+
* Server: POST /storage/folder
|
|
955
|
+
* Body: { path }
|
|
763
956
|
*/
|
|
764
957
|
createFolder(path: string): Promise<{
|
|
765
958
|
path: string;
|
|
766
959
|
}>;
|
|
767
960
|
/**
|
|
768
|
-
*
|
|
961
|
+
* Permanently delete a file.
|
|
769
962
|
*
|
|
770
|
-
*
|
|
771
|
-
*
|
|
772
|
-
* await storage.deleteFile('avatars/old-avatar.jpg');
|
|
773
|
-
* ```
|
|
963
|
+
* Server: DELETE /storage/file
|
|
964
|
+
* Body: { path }
|
|
774
965
|
*/
|
|
775
966
|
deleteFile(path: string): Promise<void>;
|
|
776
967
|
/**
|
|
777
|
-
*
|
|
968
|
+
* Recursively delete a folder and all its contents.
|
|
778
969
|
*
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
* await storage.deleteFolder('temp/');
|
|
782
|
-
* ```
|
|
970
|
+
* Server: DELETE /storage/folder
|
|
971
|
+
* Body: { path }
|
|
783
972
|
*/
|
|
784
973
|
deleteFolder(path: string): Promise<void>;
|
|
785
974
|
/**
|
|
786
|
-
* Move
|
|
975
|
+
* Move (rename) a file.
|
|
787
976
|
*
|
|
788
|
-
*
|
|
789
|
-
*
|
|
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
|
-
* ```
|
|
977
|
+
* Server: POST /storage/move
|
|
978
|
+
* Body: { from, to }
|
|
795
979
|
*/
|
|
796
980
|
move(from: string, to: string): Promise<{
|
|
797
981
|
from: string;
|
|
@@ -800,33 +984,23 @@ declare class StorageManager {
|
|
|
800
984
|
/**
|
|
801
985
|
* Copy a file to a new path.
|
|
802
986
|
*
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
* await storage.copy('templates/base.html', 'sites/my-site/index.html');
|
|
806
|
-
* ```
|
|
987
|
+
* Server: POST /storage/copy
|
|
988
|
+
* Body: { from, to }
|
|
807
989
|
*/
|
|
808
990
|
copy(from: string, to: string): Promise<{
|
|
809
991
|
from: string;
|
|
810
992
|
to: string;
|
|
811
993
|
}>;
|
|
812
994
|
/**
|
|
813
|
-
* Get
|
|
995
|
+
* Get usage statistics for this storage key.
|
|
814
996
|
*
|
|
815
|
-
*
|
|
816
|
-
* ```ts
|
|
817
|
-
* const stats = await storage.getStats();
|
|
818
|
-
* console.log(`${stats.totalFiles} files, ${(stats.totalBytes / 1e6).toFixed(1)} MB`);
|
|
819
|
-
* ```
|
|
997
|
+
* Server: GET /storage/stats
|
|
820
998
|
*/
|
|
821
999
|
getStats(): Promise<StorageStats>;
|
|
822
1000
|
/**
|
|
823
|
-
*
|
|
1001
|
+
* Get server info (no auth required).
|
|
824
1002
|
*
|
|
825
|
-
*
|
|
826
|
-
* ```ts
|
|
827
|
-
* const info = await storage.info();
|
|
828
|
-
* // → { ok: true, storageRoot: 'hydrous-storage' }
|
|
829
|
-
* ```
|
|
1003
|
+
* Server: GET /storage/info
|
|
830
1004
|
*/
|
|
831
1005
|
info(): Promise<{
|
|
832
1006
|
ok: boolean;
|
|
@@ -835,190 +1009,159 @@ declare class StorageManager {
|
|
|
835
1009
|
}
|
|
836
1010
|
|
|
837
1011
|
/**
|
|
838
|
-
* ScopedStorage — a
|
|
1012
|
+
* ScopedStorage — a path-prefixed view over a StorageManager.
|
|
839
1013
|
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
1014
|
+
* Every path you pass is automatically prefixed with the scope.
|
|
1015
|
+
* Obtain via `db.storage('keyName').scope('prefix/')`.
|
|
842
1016
|
*
|
|
843
1017
|
* @example
|
|
844
1018
|
* ```ts
|
|
845
|
-
* const
|
|
1019
|
+
* const userDocs = db.storage('documents').scope(`users/${userId}/`);
|
|
846
1020
|
*
|
|
847
|
-
* //
|
|
848
|
-
*
|
|
1021
|
+
* // Uploads to: users/{userId}/contract.pdf
|
|
1022
|
+
* await userDocs.upload(pdfBuffer, 'contract.pdf');
|
|
849
1023
|
*
|
|
850
|
-
*
|
|
851
|
-
* const
|
|
852
|
-
* await avatars.deleteFile('alice.jpg'); // → deletes "avatars/alice.jpg"
|
|
1024
|
+
* // Lists: users/{userId}/
|
|
1025
|
+
* const { files } = await userDocs.list();
|
|
853
1026
|
* ```
|
|
854
1027
|
*/
|
|
855
1028
|
declare class ScopedStorage {
|
|
856
1029
|
private readonly manager;
|
|
857
1030
|
private readonly prefix;
|
|
858
1031
|
constructor(manager: StorageManager, prefix: string);
|
|
859
|
-
private
|
|
860
|
-
|
|
861
|
-
upload(data: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
|
|
862
|
-
/** Upload raw JSON or text within the scoped folder. */
|
|
1032
|
+
private p;
|
|
1033
|
+
upload(data: Blob | Uint8Array | ArrayBuffer | Buffer, path: string, options?: UploadOptions): Promise<UploadResult>;
|
|
863
1034
|
uploadRaw(data: unknown, path: string, options?: UploadOptions): Promise<UploadResult>;
|
|
864
|
-
/** Get a signed upload URL for a file within the scoped folder. */
|
|
865
1035
|
getUploadUrl(opts: {
|
|
866
1036
|
path: string;
|
|
867
1037
|
mimeType: string;
|
|
868
1038
|
size: number;
|
|
869
1039
|
isPublic?: boolean;
|
|
870
1040
|
overwrite?: boolean;
|
|
1041
|
+
expiresInSeconds?: number;
|
|
871
1042
|
}): Promise<UploadUrlResult>;
|
|
872
|
-
|
|
1043
|
+
uploadToSignedUrl(signedUrl: string, data: Blob | Uint8Array | ArrayBuffer, mimeType: string, onProgress?: (percent: number) => void): Promise<void>;
|
|
873
1044
|
confirmUpload(opts: {
|
|
874
1045
|
path: string;
|
|
875
1046
|
mimeType: string;
|
|
876
1047
|
isPublic?: boolean;
|
|
877
1048
|
}): Promise<UploadResult>;
|
|
878
|
-
|
|
1049
|
+
getBatchUploadUrls(files: BatchUploadItem[]): Promise<BatchUploadUrlResult>;
|
|
1050
|
+
batchConfirmUploads(items: Array<{
|
|
1051
|
+
path: string;
|
|
1052
|
+
mimeType: string;
|
|
1053
|
+
isPublic?: boolean;
|
|
1054
|
+
}>): Promise<{
|
|
1055
|
+
succeeded: UploadResult[];
|
|
1056
|
+
failed: Array<{
|
|
1057
|
+
path: string;
|
|
1058
|
+
error: string;
|
|
1059
|
+
}>;
|
|
1060
|
+
}>;
|
|
879
1061
|
download(path: string): Promise<ArrayBuffer>;
|
|
1062
|
+
batchDownload(paths: string[]): Promise<BatchDownloadResult>;
|
|
880
1063
|
/**
|
|
881
|
-
* List files within
|
|
882
|
-
* `prefix`
|
|
1064
|
+
* List files within this scope.
|
|
1065
|
+
* The `prefix` option is relative to the scope root.
|
|
1066
|
+
*
|
|
1067
|
+
* @example
|
|
1068
|
+
* ```ts
|
|
1069
|
+
* const userDocs = storage.scope('users/alice/');
|
|
1070
|
+
* // Lists users/alice/docs/
|
|
1071
|
+
* const { files } = await userDocs.list({ prefix: 'docs/' });
|
|
1072
|
+
* ```
|
|
883
1073
|
*/
|
|
884
1074
|
list(opts?: ListOptions): Promise<ListResult>;
|
|
885
|
-
/** Get metadata for a file within the scoped folder. */
|
|
886
1075
|
getMetadata(path: string): Promise<FileMetadata>;
|
|
887
|
-
/** Get a time-limited signed URL for a file within the scoped folder. */
|
|
888
1076
|
getSignedUrl(path: string, expiresIn?: number): Promise<SignedUrlResult>;
|
|
889
|
-
/** Change visibility of a file within the scoped folder. */
|
|
890
1077
|
setVisibility(path: string, isPublic: boolean): Promise<{
|
|
891
1078
|
path: string;
|
|
892
1079
|
isPublic: boolean;
|
|
893
1080
|
publicUrl: string | null;
|
|
894
1081
|
downloadUrl: string | null;
|
|
895
1082
|
}>;
|
|
896
|
-
|
|
1083
|
+
createFolder(path: string): Promise<{
|
|
1084
|
+
path: string;
|
|
1085
|
+
}>;
|
|
897
1086
|
deleteFile(path: string): Promise<void>;
|
|
898
|
-
/** Delete a sub-folder within the scoped folder. */
|
|
899
1087
|
deleteFolder(path: string): Promise<void>;
|
|
900
|
-
/** Move a file within the scoped folder. */
|
|
901
1088
|
move(from: string, to: string): Promise<{
|
|
902
1089
|
from: string;
|
|
903
1090
|
to: string;
|
|
904
1091
|
}>;
|
|
905
|
-
/** Copy a file within the scoped folder. */
|
|
906
1092
|
copy(from: string, to: string): Promise<{
|
|
907
1093
|
from: string;
|
|
908
1094
|
to: string;
|
|
909
1095
|
}>;
|
|
910
|
-
|
|
911
|
-
createFolder(path: string): Promise<{
|
|
912
|
-
path: string;
|
|
913
|
-
}>;
|
|
1096
|
+
getStats(): Promise<StorageStats>;
|
|
914
1097
|
/**
|
|
915
|
-
* Create a
|
|
1098
|
+
* Create a deeper scope within this one.
|
|
916
1099
|
*
|
|
917
1100
|
* @example
|
|
918
1101
|
* ```ts
|
|
919
|
-
* const
|
|
920
|
-
* const
|
|
1102
|
+
* const user = storage.scope('users/alice/');
|
|
1103
|
+
* const userDocs = user.scope('docs/');
|
|
1104
|
+
* // Effective prefix: users/alice/docs/
|
|
921
1105
|
* ```
|
|
922
1106
|
*/
|
|
923
1107
|
scope(subPrefix: string): ScopedStorage;
|
|
924
1108
|
}
|
|
925
1109
|
|
|
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
1110
|
declare class HydrousClient {
|
|
961
1111
|
private readonly http;
|
|
962
1112
|
private readonly authKey_;
|
|
963
1113
|
private readonly bucketSecurityKey_;
|
|
964
1114
|
private readonly storageKeys_;
|
|
965
1115
|
private readonly _recordsCache;
|
|
966
|
-
private readonly _authCache;
|
|
967
1116
|
private readonly _analyticsCache;
|
|
968
1117
|
private readonly _storageCache;
|
|
1118
|
+
private _authClient?;
|
|
969
1119
|
constructor(config: HydrousConfig);
|
|
970
1120
|
/**
|
|
971
|
-
* Get a typed
|
|
972
|
-
* Uses your `bucketSecurityKey` automatically.
|
|
1121
|
+
* Get a typed RecordsClient for a bucket.
|
|
973
1122
|
*
|
|
974
1123
|
* @example
|
|
975
1124
|
* ```ts
|
|
976
1125
|
* interface Post { title: string; published: boolean }
|
|
977
1126
|
* const posts = db.records<Post>('blog-posts');
|
|
978
|
-
* const post
|
|
1127
|
+
* const post = await posts.create({ title: 'Hello', published: false });
|
|
979
1128
|
* ```
|
|
980
1129
|
*/
|
|
981
1130
|
records<T extends RecordData = RecordData>(bucketKey: string): RecordsClient<T>;
|
|
982
1131
|
/**
|
|
983
|
-
* Get
|
|
984
|
-
*
|
|
1132
|
+
* Get the AuthClient.
|
|
1133
|
+
* Auth routes are NOT bucket-scoped in the URL — the bucket is determined
|
|
1134
|
+
* server-side from the API key itself.
|
|
985
1135
|
*
|
|
986
1136
|
* @example
|
|
987
1137
|
* ```ts
|
|
988
|
-
* const
|
|
989
|
-
* const { user, session } = await auth.login({ email: '…', password: '…' });
|
|
1138
|
+
* const { user, session } = await db.auth().signup({ email: 'alice@example.com', password: 'pw' });
|
|
990
1139
|
* ```
|
|
991
1140
|
*/
|
|
992
|
-
auth(
|
|
1141
|
+
auth(): AuthClient;
|
|
993
1142
|
/**
|
|
994
|
-
* Get an
|
|
995
|
-
* Uses your `bucketSecurityKey` automatically.
|
|
1143
|
+
* Get an AnalyticsClient for a bucket.
|
|
996
1144
|
*
|
|
997
1145
|
* @example
|
|
998
1146
|
* ```ts
|
|
999
|
-
* const
|
|
1000
|
-
* const { count } = await analytics.count();
|
|
1147
|
+
* const { count } = await db.analytics('orders').count();
|
|
1001
1148
|
* ```
|
|
1002
1149
|
*/
|
|
1003
1150
|
analytics(bucketKey: string): AnalyticsClient;
|
|
1004
1151
|
/**
|
|
1005
|
-
* Get a
|
|
1006
|
-
* The name must match a key
|
|
1007
|
-
* Uses the corresponding `ssk_…` key automatically via `X-Storage-Key` header.
|
|
1152
|
+
* Get a StorageManager for a named storage key.
|
|
1153
|
+
* The name must match a key in the `storageKeys` config object.
|
|
1008
1154
|
*
|
|
1009
|
-
*
|
|
1155
|
+
* Attach `.scope('prefix/')` to get a path-prefixed ScopedStorage.
|
|
1010
1156
|
*
|
|
1011
1157
|
* @example
|
|
1012
1158
|
* ```ts
|
|
1013
|
-
* const avatars
|
|
1014
|
-
* const
|
|
1015
|
-
*
|
|
1016
|
-
* // Upload to avatars bucket
|
|
1017
|
-
* await avatars.upload(file, `${userId}.jpg`, { isPublic: true });
|
|
1159
|
+
* const avatars = db.storage('avatars');
|
|
1160
|
+
* const result = await avatars.upload(file, 'alice.jpg', { isPublic: true });
|
|
1018
1161
|
*
|
|
1019
|
-
* //
|
|
1020
|
-
* const
|
|
1021
|
-
* await
|
|
1162
|
+
* // Scoped:
|
|
1163
|
+
* const userFiles = db.storage('documents').scope(`users/${userId}/`);
|
|
1164
|
+
* await userFiles.upload(pdf, 'contract.pdf');
|
|
1022
1165
|
* ```
|
|
1023
1166
|
*/
|
|
1024
1167
|
storage(keyName: string): StorageManager & {
|
|
@@ -1026,33 +1169,135 @@ declare class HydrousClient {
|
|
|
1026
1169
|
};
|
|
1027
1170
|
}
|
|
1028
1171
|
/**
|
|
1029
|
-
* Create a
|
|
1172
|
+
* Create a HydrousDB client.
|
|
1030
1173
|
*
|
|
1031
1174
|
* @example
|
|
1032
1175
|
* ```ts
|
|
1033
|
-
* import { createClient } from '
|
|
1176
|
+
* import { createClient } from 'hydrous-sdk';
|
|
1034
1177
|
*
|
|
1035
1178
|
* const db = createClient({
|
|
1036
|
-
* authKey:
|
|
1037
|
-
* bucketSecurityKey:
|
|
1038
|
-
* storageKeys:
|
|
1039
|
-
* main: process.env.HYDROUS_STORAGE_MAIN!,
|
|
1040
|
-
* avatars: process.env.HYDROUS_STORAGE_AVATARS!,
|
|
1041
|
-
* documents: process.env.HYDROUS_STORAGE_DOCS!,
|
|
1042
|
-
* },
|
|
1179
|
+
* authKey: 'hk_auth_…',
|
|
1180
|
+
* bucketSecurityKey: 'hk_bucket_…',
|
|
1181
|
+
* storageKeys: { avatars: 'ssk_…' },
|
|
1043
1182
|
* });
|
|
1044
1183
|
* ```
|
|
1045
1184
|
*/
|
|
1046
1185
|
declare function createClient(config: HydrousConfig): HydrousClient;
|
|
1047
1186
|
|
|
1187
|
+
/**
|
|
1188
|
+
* routes.ts — Single source of truth for every API path in the HydrousDB SDK.
|
|
1189
|
+
*
|
|
1190
|
+
* Base URL: https://db-api-82687684612.us-central1.run.app
|
|
1191
|
+
*
|
|
1192
|
+
* Mount points (from server.js):
|
|
1193
|
+
* /api → records router (X-Api-Key: bucketSecurityKey)
|
|
1194
|
+
* /api/analytics → analytics router (X-Api-Key: bucketSecurityKey)
|
|
1195
|
+
* /api/auth → auth router (X-Api-Key: authKey)
|
|
1196
|
+
* /storage → storage router (X-Storage-Key: ssk_…)
|
|
1197
|
+
*
|
|
1198
|
+
* ⚠️ Keys MUST be sent in headers — NEVER in URLs or query strings.
|
|
1199
|
+
* Records + Analytics: X-Api-Key (or Authorization: Bearer …)
|
|
1200
|
+
* Storage: X-Storage-Key (or Authorization: Bearer …)
|
|
1201
|
+
*/
|
|
1202
|
+
declare const RECORDS: {
|
|
1203
|
+
/** GET|POST|PATCH|DELETE|HEAD /api/:bucketKey */
|
|
1204
|
+
readonly bucket: (bucketKey: string) => string;
|
|
1205
|
+
/** POST /api/:bucketKey/batch/insert */
|
|
1206
|
+
readonly batchInsert: (bucketKey: string) => string;
|
|
1207
|
+
/** POST /api/:bucketKey/batch/update */
|
|
1208
|
+
readonly batchUpdate: (bucketKey: string) => string;
|
|
1209
|
+
/** POST /api/:bucketKey/batch/delete */
|
|
1210
|
+
readonly batchDelete: (bucketKey: string) => string;
|
|
1211
|
+
};
|
|
1212
|
+
declare const ANALYTICS: {
|
|
1213
|
+
/** POST /api/analytics/:bucketKey */
|
|
1214
|
+
readonly query: (bucketKey: string) => string;
|
|
1215
|
+
};
|
|
1216
|
+
declare const AUTH: {
|
|
1217
|
+
/** POST /api/auth/signup body: { email, password, fullName?, ...extra } */
|
|
1218
|
+
readonly signup: "/api/auth/signup";
|
|
1219
|
+
/** POST /api/auth/signin body: { email, password } */
|
|
1220
|
+
readonly signin: "/api/auth/signin";
|
|
1221
|
+
/** POST /api/auth/signout body: { sessionId, allDevices? } */
|
|
1222
|
+
readonly signout: "/api/auth/signout";
|
|
1223
|
+
/** POST /api/auth/session/validate body: { sessionId } */
|
|
1224
|
+
readonly sessionValidate: "/api/auth/session/validate";
|
|
1225
|
+
/** POST /api/auth/session/refresh body: { refreshToken } */
|
|
1226
|
+
readonly sessionRefresh: "/api/auth/session/refresh";
|
|
1227
|
+
/** GET /api/auth/user?userId=... */
|
|
1228
|
+
readonly getUser: "/api/auth/user";
|
|
1229
|
+
/** GET /api/auth/users?limit=&cursor= */
|
|
1230
|
+
readonly listUsers: "/api/auth/users";
|
|
1231
|
+
/** PATCH /api/auth/user body: { sessionId, userId, updates: {...} } */
|
|
1232
|
+
readonly updateUser: "/api/auth/user";
|
|
1233
|
+
/** DELETE /api/auth/user?userId=... body: { sessionId } */
|
|
1234
|
+
readonly deleteUser: "/api/auth/user";
|
|
1235
|
+
/** DELETE /api/auth/user/hard?userId=... body: { sessionId } */
|
|
1236
|
+
readonly hardDeleteUser: "/api/auth/user/hard";
|
|
1237
|
+
/** DELETE /api/auth/users/bulk body: { userIds, hard?, sessionId } */
|
|
1238
|
+
readonly bulkDeleteUsers: "/api/auth/users/bulk";
|
|
1239
|
+
/** POST /api/auth/password/change body: { sessionId, userId, oldPassword, newPassword } */
|
|
1240
|
+
readonly passwordChange: "/api/auth/password/change";
|
|
1241
|
+
/** POST /api/auth/password/reset/request body: { email } */
|
|
1242
|
+
readonly passwordResetRequest: "/api/auth/password/reset/request";
|
|
1243
|
+
/** POST /api/auth/password/reset/confirm body: { resetToken, newPassword } */
|
|
1244
|
+
readonly passwordResetConfirm: "/api/auth/password/reset/confirm";
|
|
1245
|
+
/** POST /api/auth/email/verify/request body: { userId } */
|
|
1246
|
+
readonly emailVerifyRequest: "/api/auth/email/verify/request";
|
|
1247
|
+
/** POST /api/auth/email/verify/confirm body: { verifyToken } */
|
|
1248
|
+
readonly emailVerifyConfirm: "/api/auth/email/verify/confirm";
|
|
1249
|
+
/** POST /api/auth/account/lock body: { sessionId, userId, duration? } */
|
|
1250
|
+
readonly accountLock: "/api/auth/account/lock";
|
|
1251
|
+
/** POST /api/auth/account/unlock body: { sessionId, userId } */
|
|
1252
|
+
readonly accountUnlock: "/api/auth/account/unlock";
|
|
1253
|
+
};
|
|
1254
|
+
declare const STORAGE: {
|
|
1255
|
+
/** GET /storage/info — no auth required */
|
|
1256
|
+
readonly info: "/storage/info";
|
|
1257
|
+
/** GET /storage/public/:fullScopedPath — no auth required */
|
|
1258
|
+
readonly publicFile: (fullScopedPath: string) => string;
|
|
1259
|
+
/** POST /storage/upload-url body: { path, mimeType, size, isPublic?, overwrite?, expiresIn? } */
|
|
1260
|
+
readonly uploadUrl: "/storage/upload-url";
|
|
1261
|
+
/** POST /storage/batch-upload-urls body: { files: [...], expiresIn? } */
|
|
1262
|
+
readonly batchUploadUrls: "/storage/batch-upload-urls";
|
|
1263
|
+
/** POST /storage/confirm body: { path, mimeType, isPublic? } */
|
|
1264
|
+
readonly confirm: "/storage/confirm";
|
|
1265
|
+
/** POST /storage/batch-confirm body: { files: [...] } */
|
|
1266
|
+
readonly batchConfirm: "/storage/batch-confirm";
|
|
1267
|
+
/** POST /storage/upload multipart/form-data: file, path, mimeType, isPublic, overwrite */
|
|
1268
|
+
readonly upload: "/storage/upload";
|
|
1269
|
+
/** POST /storage/upload-raw body: { path, content, mimeType?, isPublic?, overwrite? } */
|
|
1270
|
+
readonly uploadRaw: "/storage/upload-raw";
|
|
1271
|
+
/** GET /storage/list?prefix=&limit=&cursor= */
|
|
1272
|
+
readonly list: "/storage/list";
|
|
1273
|
+
/** GET /storage/download/:path — requires X-Storage-Key */
|
|
1274
|
+
readonly download: (filePath: string) => string;
|
|
1275
|
+
/** POST /storage/batch-download body: { paths: [...], concurrency? } */
|
|
1276
|
+
readonly batchDownload: "/storage/batch-download";
|
|
1277
|
+
/** GET /storage/metadata/:path */
|
|
1278
|
+
readonly metadata: (filePath: string) => string;
|
|
1279
|
+
/** POST /storage/signed-url body: { path, expiresIn? } */
|
|
1280
|
+
readonly signedUrl: "/storage/signed-url";
|
|
1281
|
+
/** PATCH /storage/visibility body: { path, isPublic } */
|
|
1282
|
+
readonly visibility: "/storage/visibility";
|
|
1283
|
+
/** POST /storage/folder body: { path } */
|
|
1284
|
+
readonly folder: "/storage/folder";
|
|
1285
|
+
/** DELETE /storage/file body: { path } */
|
|
1286
|
+
readonly file: "/storage/file";
|
|
1287
|
+
/** DELETE /storage/folder body: { path } */
|
|
1288
|
+
readonly folderDelete: "/storage/folder";
|
|
1289
|
+
/** POST /storage/move body: { from, to } */
|
|
1290
|
+
readonly move: "/storage/move";
|
|
1291
|
+
/** POST /storage/copy body: { from, to } */
|
|
1292
|
+
readonly copy: "/storage/copy";
|
|
1293
|
+
/** GET /storage/stats */
|
|
1294
|
+
readonly stats: "/storage/stats";
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1048
1297
|
declare class HydrousError extends Error {
|
|
1049
|
-
/** Machine-readable error code returned by the API. */
|
|
1050
1298
|
readonly code: string;
|
|
1051
|
-
/** HTTP status code (if applicable). */
|
|
1052
1299
|
readonly status?: number;
|
|
1053
|
-
/** The original request ID for support tracing. */
|
|
1054
1300
|
readonly requestId?: string;
|
|
1055
|
-
/** Additional validation details. */
|
|
1056
1301
|
readonly details?: string[];
|
|
1057
1302
|
constructor(message: string, code: string, status?: number, requestId?: string, details?: string[]);
|
|
1058
1303
|
toString(): string;
|
|
@@ -1077,4 +1322,4 @@ declare class NetworkError extends HydrousError {
|
|
|
1077
1322
|
constructor(message: string, cause?: unknown);
|
|
1078
1323
|
}
|
|
1079
1324
|
|
|
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 };
|
|
1325
|
+
export { ANALYTICS, AUTH, 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, DEFAULT_BASE_URL, type DateRange, type DistributionRow, type FieldStats, type FieldTimeSeriesRow, type FileEntry, type FileMetadata, type Granularity, HttpClient, 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, RECORDS, type RecordData, RecordError, type RecordHistoryEntry, type RecordResult, RecordsClient, STORAGE, 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 };
|