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