hydrousdb 3.5.0 → 3.5.2
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 +1097 -195
- package/dist/index.cjs +154 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +203 -73
- package/dist/index.d.ts +203 -73
- package/dist/index.mjs +154 -68
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -179,98 +179,74 @@ var HttpClient = class {
|
|
|
179
179
|
|
|
180
180
|
// src/routes.ts
|
|
181
181
|
var RECORDS = {
|
|
182
|
-
/** GET|POST
|
|
182
|
+
/** Base path for a bucket: GET|POST /api/:bucketKey */
|
|
183
183
|
bucket: (bucketKey) => `/api/${bucketKey}`,
|
|
184
|
-
/** POST /api/:bucketKey/batch/insert */
|
|
184
|
+
/** Batch insert: POST /api/:bucketKey/batch/insert */
|
|
185
185
|
batchInsert: (bucketKey) => `/api/${bucketKey}/batch/insert`,
|
|
186
|
-
/** POST /api/:bucketKey/batch/update */
|
|
186
|
+
/** Batch update: POST /api/:bucketKey/batch/update */
|
|
187
187
|
batchUpdate: (bucketKey) => `/api/${bucketKey}/batch/update`,
|
|
188
|
-
/** POST /api/:bucketKey/batch/delete */
|
|
188
|
+
/** Batch delete: POST /api/:bucketKey/batch/delete */
|
|
189
189
|
batchDelete: (bucketKey) => `/api/${bucketKey}/batch/delete`
|
|
190
190
|
};
|
|
191
191
|
var ANALYTICS = {
|
|
192
|
-
/** POST /api/analytics/:bucketKey */
|
|
192
|
+
/** Analytics query: POST /api/analytics/:bucketKey */
|
|
193
193
|
query: (bucketKey) => `/api/analytics/${bucketKey}`
|
|
194
194
|
};
|
|
195
195
|
var AUTH = {
|
|
196
|
-
|
|
196
|
+
// ── Core ────────────────────────────────────────────────────────────────
|
|
197
197
|
signup: "/api/auth/signup",
|
|
198
|
-
/** POST /api/auth/signin body: { email, password } */
|
|
199
198
|
signin: "/api/auth/signin",
|
|
200
|
-
/** POST /api/auth/signout body: { sessionId, allDevices? } */
|
|
201
199
|
signout: "/api/auth/signout",
|
|
202
|
-
|
|
200
|
+
// ── Google OAuth ─────────────────────────────────────────────────────────
|
|
201
|
+
// POST /api/auth/google — sign in or create account via Google ID token
|
|
202
|
+
// POST /api/auth/google/link — link Google to an existing email/password account
|
|
203
|
+
// DELETE /api/auth/google/unlink — remove Google (only if a password is set)
|
|
204
|
+
googleSignIn: "/api/auth/google",
|
|
205
|
+
googleLink: "/api/auth/google/link",
|
|
206
|
+
googleUnlink: "/api/auth/google/unlink",
|
|
207
|
+
// ── Session ──────────────────────────────────────────────────────────────
|
|
203
208
|
sessionValidate: "/api/auth/session/validate",
|
|
204
|
-
/** POST /api/auth/session/refresh body: { refreshToken } */
|
|
205
209
|
sessionRefresh: "/api/auth/session/refresh",
|
|
206
|
-
|
|
210
|
+
// ── User ─────────────────────────────────────────────────────────────────
|
|
207
211
|
getUser: "/api/auth/user",
|
|
208
|
-
/** GET /api/auth/users?limit=&cursor= */
|
|
209
|
-
listUsers: "/api/auth/users",
|
|
210
|
-
/** PATCH /api/auth/user body: { sessionId, userId, updates: {...} } */
|
|
211
212
|
updateUser: "/api/auth/user",
|
|
212
|
-
/** DELETE /api/auth/user?userId=... body: { sessionId } */
|
|
213
213
|
deleteUser: "/api/auth/user",
|
|
214
|
-
|
|
214
|
+
// ── Admin ─────────────────────────────────────────────────────────────────
|
|
215
|
+
listUsers: "/api/auth/users",
|
|
215
216
|
hardDeleteUser: "/api/auth/user/hard",
|
|
216
|
-
/** DELETE /api/auth/users/bulk body: { userIds, hard?, sessionId } */
|
|
217
217
|
bulkDeleteUsers: "/api/auth/users/bulk",
|
|
218
|
-
|
|
218
|
+
// ── Account ───────────────────────────────────────────────────────────────
|
|
219
|
+
accountLock: "/api/auth/account/lock",
|
|
220
|
+
accountUnlock: "/api/auth/account/unlock",
|
|
221
|
+
// ── Password ──────────────────────────────────────────────────────────────
|
|
219
222
|
passwordChange: "/api/auth/password/change",
|
|
220
|
-
/** POST /api/auth/password/reset/request body: { email } */
|
|
221
223
|
passwordResetRequest: "/api/auth/password/reset/request",
|
|
222
|
-
/** POST /api/auth/password/reset/confirm body: { resetToken, newPassword } */
|
|
223
224
|
passwordResetConfirm: "/api/auth/password/reset/confirm",
|
|
224
|
-
|
|
225
|
+
// ── Email Verification ────────────────────────────────────────────────────
|
|
225
226
|
emailVerifyRequest: "/api/auth/email/verify/request",
|
|
226
|
-
|
|
227
|
-
emailVerifyConfirm: "/api/auth/email/verify/confirm",
|
|
228
|
-
/** POST /api/auth/account/lock body: { sessionId, userId, duration? } */
|
|
229
|
-
accountLock: "/api/auth/account/lock",
|
|
230
|
-
/** POST /api/auth/account/unlock body: { sessionId, userId } */
|
|
231
|
-
accountUnlock: "/api/auth/account/unlock"
|
|
227
|
+
emailVerifyConfirm: "/api/auth/email/verify/confirm"
|
|
232
228
|
};
|
|
233
229
|
var STORAGE = {
|
|
234
|
-
|
|
235
|
-
info: "/storage/info",
|
|
236
|
-
/** GET /storage/public/:fullScopedPath — no auth required */
|
|
237
|
-
publicFile: (fullScopedPath) => `/storage/public/${fullScopedPath}`,
|
|
238
|
-
/** POST /storage/upload-url body: { path, mimeType, size, isPublic?, overwrite?, expiresIn? } */
|
|
239
|
-
uploadUrl: "/storage/upload-url",
|
|
240
|
-
/** POST /storage/batch-upload-urls body: { files: [...], expiresIn? } */
|
|
241
|
-
batchUploadUrls: "/storage/batch-upload-urls",
|
|
242
|
-
/** POST /storage/confirm body: { path, mimeType, isPublic? } */
|
|
243
|
-
confirm: "/storage/confirm",
|
|
244
|
-
/** POST /storage/batch-confirm body: { files: [...] } */
|
|
245
|
-
batchConfirm: "/storage/batch-confirm",
|
|
246
|
-
/** POST /storage/upload multipart/form-data: file, path, mimeType, isPublic, overwrite */
|
|
230
|
+
base: "/storage",
|
|
247
231
|
upload: "/storage/upload",
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
232
|
+
uploadRaw: "/storage/upload/raw",
|
|
233
|
+
uploadUrl: "/storage/upload/url",
|
|
234
|
+
confirm: "/storage/upload/confirm",
|
|
235
|
+
batchUploadUrls: "/storage/upload/batch/urls",
|
|
236
|
+
batchConfirm: "/storage/upload/batch/confirm",
|
|
237
|
+
download: (encodedPath) => `/storage/download/${encodedPath}`,
|
|
238
|
+
batchDownload: "/storage/download/batch",
|
|
251
239
|
list: "/storage/list",
|
|
252
|
-
|
|
253
|
-
download: (filePath) => `/storage/download/${filePath}`,
|
|
254
|
-
/** POST /storage/batch-download body: { paths: [...], concurrency? } */
|
|
255
|
-
batchDownload: "/storage/batch-download",
|
|
256
|
-
/** GET /storage/metadata/:path */
|
|
257
|
-
metadata: (filePath) => `/storage/metadata/${filePath}`,
|
|
258
|
-
/** POST /storage/signed-url body: { path, expiresIn? } */
|
|
240
|
+
metadata: (encodedPath) => `/storage/metadata/${encodedPath}`,
|
|
259
241
|
signedUrl: "/storage/signed-url",
|
|
260
|
-
/** PATCH /storage/visibility body: { path, isPublic } */
|
|
261
242
|
visibility: "/storage/visibility",
|
|
262
|
-
/** POST /storage/folder body: { path } */
|
|
263
243
|
folder: "/storage/folder",
|
|
264
|
-
/** DELETE /storage/file body: { path } */
|
|
265
244
|
file: "/storage/file",
|
|
266
|
-
|
|
267
|
-
folderDelete: "/storage/folder",
|
|
268
|
-
/** POST /storage/move body: { from, to } */
|
|
245
|
+
folderDelete: "/storage/folder/delete",
|
|
269
246
|
move: "/storage/move",
|
|
270
|
-
/** POST /storage/copy body: { from, to } */
|
|
271
247
|
copy: "/storage/copy",
|
|
272
|
-
|
|
273
|
-
|
|
248
|
+
stats: "/storage/stats",
|
|
249
|
+
info: "/storage/info"
|
|
274
250
|
};
|
|
275
251
|
|
|
276
252
|
// src/auth/client.ts
|
|
@@ -298,8 +274,7 @@ var AuthClient = class {
|
|
|
298
274
|
*/
|
|
299
275
|
async signup(options) {
|
|
300
276
|
const result = await this.post(AUTH.signup, options);
|
|
301
|
-
|
|
302
|
-
return this._buildAuthResult(user, result.session);
|
|
277
|
+
return this._buildAuthResult(result.data, result.session, result.isNew);
|
|
303
278
|
}
|
|
304
279
|
/**
|
|
305
280
|
* Sign in with email + password.
|
|
@@ -309,8 +284,7 @@ var AuthClient = class {
|
|
|
309
284
|
*/
|
|
310
285
|
async login(options) {
|
|
311
286
|
const result = await this.post(AUTH.signin, options);
|
|
312
|
-
|
|
313
|
-
return this._buildAuthResult(user, result.session);
|
|
287
|
+
return this._buildAuthResult(result.data, result.session, result.isNew);
|
|
314
288
|
}
|
|
315
289
|
/**
|
|
316
290
|
* Sign out — revoke a session (or all sessions with `allDevices: true`).
|
|
@@ -321,6 +295,111 @@ var AuthClient = class {
|
|
|
321
295
|
async logout(options) {
|
|
322
296
|
await this.post(AUTH.signout, options);
|
|
323
297
|
}
|
|
298
|
+
// ─── Google Sign-In ───────────────────────────────────────────────────────
|
|
299
|
+
/**
|
|
300
|
+
* Sign in or create an account using a Google ID token.
|
|
301
|
+
*
|
|
302
|
+
* Pass the `idToken` from the Google Sign-In SDK. Your server verifies the
|
|
303
|
+
* token against Google's public keys, then either creates a new account or
|
|
304
|
+
* signs in the returning user. No password is ever set or required.
|
|
305
|
+
*
|
|
306
|
+
* The returned `AuthResult` is identical in shape to `login()` and `signup()`.
|
|
307
|
+
* Sessions from Google sign-in work with `validateSession`, `refreshSession`,
|
|
308
|
+
* and `logout` — nothing changes after the initial sign-in.
|
|
309
|
+
*
|
|
310
|
+
* Use `isNew` to drive onboarding flows.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* ```ts
|
|
314
|
+
* // Web — Google Identity Services
|
|
315
|
+
* google.accounts.id.initialize({
|
|
316
|
+
* client_id: 'YOUR_GOOGLE_CLIENT_ID',
|
|
317
|
+
* callback: async ({ credential }) => {
|
|
318
|
+
* const { user, session, isNew } = await db.auth().continueWithGoogle({
|
|
319
|
+
* idToken: credential,
|
|
320
|
+
* });
|
|
321
|
+
* if (isNew) router.push('/onboarding');
|
|
322
|
+
* else router.push('/dashboard');
|
|
323
|
+
* },
|
|
324
|
+
* });
|
|
325
|
+
*
|
|
326
|
+
* // React Native
|
|
327
|
+
* const { idToken } = await GoogleSignin.signIn();
|
|
328
|
+
* const { user, session, isNew } = await db.auth().continueWithGoogle({ idToken });
|
|
329
|
+
* ```
|
|
330
|
+
*
|
|
331
|
+
* Server: POST /api/auth/google
|
|
332
|
+
* Body: { idToken }
|
|
333
|
+
*/
|
|
334
|
+
async continueWithGoogle(options) {
|
|
335
|
+
if (!options.idToken || typeof options.idToken !== "string") {
|
|
336
|
+
throw new Error("[HydrousDB] continueWithGoogle: idToken is required and must be a string.");
|
|
337
|
+
}
|
|
338
|
+
const result = await this.post(AUTH.googleSignIn, {
|
|
339
|
+
idToken: options.idToken
|
|
340
|
+
});
|
|
341
|
+
return this._buildAuthResult(result.data, result.session, result.isNew);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Link a Google account to the currently signed-in user.
|
|
345
|
+
*
|
|
346
|
+
* After linking, the user can sign in with either their email + password
|
|
347
|
+
* or with Google — both produce valid sessions.
|
|
348
|
+
*
|
|
349
|
+
* The server is idempotent — calling this when Google is already linked
|
|
350
|
+
* returns the updated user without creating duplicate entries.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* // User is signed in with email. They click "Connect Google" in settings.
|
|
355
|
+
* const { idToken } = await GoogleSignin.signIn();
|
|
356
|
+
* const updatedUser = await db.auth().linkGoogle({
|
|
357
|
+
* sessionId: currentSession.sessionId,
|
|
358
|
+
* idToken,
|
|
359
|
+
* });
|
|
360
|
+
* ```
|
|
361
|
+
*
|
|
362
|
+
* Server: POST /api/auth/google/link
|
|
363
|
+
* Body: { sessionId, idToken }
|
|
364
|
+
*/
|
|
365
|
+
async linkGoogle(options) {
|
|
366
|
+
if (!options.sessionId) {
|
|
367
|
+
throw new Error("[HydrousDB] linkGoogle: sessionId is required.");
|
|
368
|
+
}
|
|
369
|
+
if (!options.idToken || typeof options.idToken !== "string") {
|
|
370
|
+
throw new Error("[HydrousDB] linkGoogle: idToken is required and must be a string.");
|
|
371
|
+
}
|
|
372
|
+
const result = await this.post(AUTH.googleLink, {
|
|
373
|
+
sessionId: options.sessionId,
|
|
374
|
+
idToken: options.idToken
|
|
375
|
+
});
|
|
376
|
+
return result.data;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Unlink Google from the currently signed-in user's account.
|
|
380
|
+
*
|
|
381
|
+
* Only succeeds if the user has a password set — you cannot remove the only
|
|
382
|
+
* sign-in method. If the account was created via Google and has no password,
|
|
383
|
+
* call `changePassword` first.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```ts
|
|
387
|
+
* await db.auth().unlinkGoogle({ sessionId: currentSession.sessionId });
|
|
388
|
+
* ```
|
|
389
|
+
*
|
|
390
|
+
* Server: DELETE /api/auth/google/unlink
|
|
391
|
+
* Body: { sessionId }
|
|
392
|
+
*/
|
|
393
|
+
async unlinkGoogle(options) {
|
|
394
|
+
if (!options.sessionId) {
|
|
395
|
+
throw new Error("[HydrousDB] unlinkGoogle: sessionId is required.");
|
|
396
|
+
}
|
|
397
|
+
await this.http.request(AUTH.googleUnlink, {
|
|
398
|
+
method: "DELETE",
|
|
399
|
+
body: { sessionId: options.sessionId },
|
|
400
|
+
headers: { "X-Api-Key": this.authKey }
|
|
401
|
+
});
|
|
402
|
+
}
|
|
324
403
|
// ─── Session Management ───────────────────────────────────────────────────
|
|
325
404
|
/**
|
|
326
405
|
* Validate an existing session and retrieve the current user.
|
|
@@ -535,7 +614,7 @@ var AuthClient = class {
|
|
|
535
614
|
await this.post(AUTH.emailVerifyConfirm, { verifyToken });
|
|
536
615
|
}
|
|
537
616
|
// ─── Private helpers ──────────────────────────────────────────────────────
|
|
538
|
-
_buildAuthResult(user, session) {
|
|
617
|
+
_buildAuthResult(user, session, isNew) {
|
|
539
618
|
return {
|
|
540
619
|
user,
|
|
541
620
|
session: {
|
|
@@ -546,7 +625,8 @@ var AuthClient = class {
|
|
|
546
625
|
expiresAt: session.expiresAt,
|
|
547
626
|
refreshToken: session.refreshToken,
|
|
548
627
|
refreshExpiresAt: session.expiresAt
|
|
549
|
-
}
|
|
628
|
+
},
|
|
629
|
+
isNew: isNew ?? false
|
|
550
630
|
};
|
|
551
631
|
}
|
|
552
632
|
};
|
|
@@ -556,15 +636,21 @@ function buildQueryParams(options = {}) {
|
|
|
556
636
|
const params = new URLSearchParams();
|
|
557
637
|
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
558
638
|
if (options.offset !== void 0) params.set("offset", String(options.offset));
|
|
559
|
-
if (options.orderBy !== void 0) params.set("sortBy", options.orderBy);
|
|
560
639
|
if (options.order !== void 0) params.set("sortOrder", options.order);
|
|
561
640
|
if (options.fields !== void 0) params.set("fields", options.fields);
|
|
641
|
+
if (options.orderBy !== void 0) params.set("sortBy", options.orderBy);
|
|
642
|
+
if (options.sortBy !== void 0) params.set("sortBy", options.sortBy);
|
|
562
643
|
if (options.startAfter !== void 0) params.set("cursor", options.startAfter);
|
|
563
644
|
if (options.startAt !== void 0) params.set("cursor", options.startAt);
|
|
564
|
-
if (options.
|
|
645
|
+
if (options.endAt !== void 0) params.set("endAt", options.endAt);
|
|
646
|
+
if (options.timeScope !== void 0) params.set("timeScope", options.timeScope);
|
|
647
|
+
if (options.startDate !== void 0) params.set("startDate", options.startDate);
|
|
648
|
+
if (options.endDate !== void 0) params.set("endDate", options.endDate);
|
|
649
|
+
if (options.dateRange?.start !== void 0 && options.startDate === void 0)
|
|
565
650
|
params.set("startDate", new Date(options.dateRange.start).toISOString().split("T")[0]);
|
|
566
|
-
if (options.dateRange?.end !== void 0)
|
|
651
|
+
if (options.dateRange?.end !== void 0 && options.endDate === void 0)
|
|
567
652
|
params.set("endDate", new Date(options.dateRange.end).toISOString().split("T")[0]);
|
|
653
|
+
if (options.year !== void 0) params.set("year", options.year);
|
|
568
654
|
if (options.filters && options.filters.length > 0) {
|
|
569
655
|
for (const f of options.filters) {
|
|
570
656
|
const key = f.op === "==" ? f.field : `${f.field}[${f.op}]`;
|