@urbackend/sdk 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -112,17 +112,90 @@ async function parseApiError(response) {
112
112
 
113
113
  // src/modules/auth.ts
114
114
  var AuthModule = class {
115
+ /**
116
+ * Creates an instance of AuthModule
117
+ *
118
+ * @param {UrBackendClient} client - The urBackend client instance
119
+ *
120
+ * @example
121
+ * const client = new UrBackendClient({ apiKey: 'pk_live_xxx' });
122
+ * const auth = new AuthModule(client);
123
+ */
115
124
  constructor(client) {
116
125
  this.client = client;
117
126
  }
118
127
  /**
119
- * Create a new user account
128
+ * Creates a new user account
129
+ *
130
+ * @param {SignUpPayload} payload - User registration data
131
+ * @param {string} payload.email - User's email address
132
+ * @param {string} payload.password - User's password
133
+ * @param {string} payload.name - User's full name
134
+ * @returns {Promise<AuthUser>} Promise resolving to the created user object
135
+ *
136
+ * @throws {AuthError} If email already exists
137
+ * @throws {AuthError} If password does not meet requirements
138
+ * @throws {AuthError} If validation fails
139
+ *
140
+ * @example
141
+ * // Sign up a new user
142
+ * const user = await auth.signUp({
143
+ * email: 'john@example.com',
144
+ * password: 'StrongP@ss123',
145
+ * name: 'John Doe'
146
+ * });
147
+ * console.log('User created:', user._id);
148
+ *
149
+ * @example
150
+ * // Sign up with error handling
151
+ * try {
152
+ * const user = await auth.signUp({
153
+ * email: 'existing@email.com',
154
+ * password: 'weak',
155
+ * name: 'Test'
156
+ * });
157
+ * } catch (error) {
158
+ * if (error.message.includes('email')) {
159
+ * console.log('Email already registered');
160
+ * } else if (error.message.includes('password')) {
161
+ * console.log('Password too weak');
162
+ * }
163
+ * }
120
164
  */
121
165
  async signUp(payload) {
122
166
  return this.client.request("POST", "/api/userAuth/signup", { body: payload });
123
167
  }
124
168
  /**
125
- * Log in an existing user and store the session token
169
+ * Authenticates an existing user and stores the session token
170
+ *
171
+ * @param {LoginPayload} payload - User login credentials
172
+ * @param {string} payload.email - User's email address
173
+ * @param {string} payload.password - User's password
174
+ * @returns {Promise<AuthResponse>} Promise resolving to authentication response with tokens
175
+ *
176
+ * @throws {AuthError} If credentials are invalid
177
+ * @throws {AuthError} If account is locked or not verified
178
+ *
179
+ * @example
180
+ * // Log in a user
181
+ * const response = await auth.login({
182
+ * email: 'john@example.com',
183
+ * password: 'StrongP@ss123'
184
+ * });
185
+ * console.log('Access token:', response.accessToken);
186
+ *
187
+ * @example
188
+ * // Login with error handling
189
+ * try {
190
+ * const { accessToken, user } = await auth.login({
191
+ * email: 'user@example.com',
192
+ * password: 'wrongpassword'
193
+ * });
194
+ * } catch (error) {
195
+ * if (error.status === 401) {
196
+ * console.log('Invalid email or password');
197
+ * }
198
+ * }
126
199
  */
127
200
  async login(payload) {
128
201
  const response = await this.client.request("POST", "/api/userAuth/login", {
@@ -137,7 +210,33 @@ var AuthModule = class {
137
210
  return response;
138
211
  }
139
212
  /**
140
- * Get the current authenticated user's profile
213
+ * Retrieves the current authenticated user's profile
214
+ *
215
+ * @param {string} [token] - Optional authentication token (overrides stored token)
216
+ * @returns {Promise<AuthUser>} Promise resolving to the authenticated user's profile
217
+ *
218
+ * @throws {AuthError} If no authentication token is provided
219
+ * @throws {AuthError} If token is invalid or expired
220
+ *
221
+ * @example
222
+ * // Get current user profile (uses stored token from login)
223
+ * const user = await auth.me();
224
+ * console.log(`Hello ${user.name}, your email is ${user.email}`);
225
+ *
226
+ * @example
227
+ * // Get profile with custom token
228
+ * const user = await auth.me(customToken);
229
+ *
230
+ * @example
231
+ * // Get profile with error handling
232
+ * try {
233
+ * const user = await auth.me();
234
+ * console.log('Authenticated as:', user.email);
235
+ * } catch (error) {
236
+ * if (error.status === 401) {
237
+ * console.log('Please log in again');
238
+ * }
239
+ * }
141
240
  */
142
241
  async me(token) {
143
242
  const activeToken = token || this.sessionToken;
@@ -151,7 +250,37 @@ var AuthModule = class {
151
250
  return this.client.request("GET", "/api/userAuth/me", { token: activeToken });
152
251
  }
153
252
  /**
154
- * Update the current authenticated user's profile
253
+ * Updates the current authenticated user's profile
254
+ *
255
+ * @param {UpdateProfilePayload} payload - Profile data to update
256
+ * @param {string} [payload.name] - Updated name
257
+ * @param {string} [payload.email] - Updated email (may require re-verification)
258
+ * @param {string} [token] - Optional authentication token (overrides stored token)
259
+ * @returns {Promise<{ message: string }>} Promise resolving to success message
260
+ *
261
+ * @throws {AuthError} If no authentication token is provided
262
+ * @throws {AuthError} If token is invalid or expired
263
+ * @throws {AuthError} If email is already taken
264
+ *
265
+ * @example
266
+ * // Update user's name
267
+ * const result = await auth.updateProfile({ name: 'Jane Smith' });
268
+ * console.log(result.message);
269
+ *
270
+ * @example
271
+ * // Update multiple fields
272
+ * const result = await auth.updateProfile({
273
+ * name: 'Jane Doe',
274
+ * email: 'jane@newemail.com'
275
+ * });
276
+ *
277
+ * @example
278
+ * // Update with error handling
279
+ * try {
280
+ * await auth.updateProfile({ email: 'taken@email.com' });
281
+ * } catch (error) {
282
+ * console.log('Email already in use');
283
+ * }
155
284
  */
156
285
  async updateProfile(payload, token) {
157
286
  const activeToken = token || this.sessionToken;
@@ -164,7 +293,38 @@ var AuthModule = class {
164
293
  });
165
294
  }
166
295
  /**
167
- * Change the current authenticated user's password
296
+ * Changes the current authenticated user's password
297
+ *
298
+ * @param {ChangePasswordPayload} payload - Password change data
299
+ * @param {string} payload.currentPassword - User's current password
300
+ * @param {string} payload.newPassword - Desired new password
301
+ * @param {string} [token] - Optional authentication token (overrides stored token)
302
+ * @returns {Promise<{ message: string }>} Promise resolving to success message
303
+ *
304
+ * @throws {AuthError} If no authentication token is provided
305
+ * @throws {AuthError} If current password is incorrect
306
+ * @throws {AuthError} If new password does not meet requirements
307
+ *
308
+ * @example
309
+ * // Change password
310
+ * const result = await auth.changePassword({
311
+ * currentPassword: 'oldPassword123',
312
+ * newPassword: 'newStrongP@ss456'
313
+ * });
314
+ * console.log(result.message);
315
+ *
316
+ * @example
317
+ * // Change password with error handling
318
+ * try {
319
+ * await auth.changePassword({
320
+ * currentPassword: 'wrong',
321
+ * newPassword: 'newPassword123'
322
+ * });
323
+ * } catch (error) {
324
+ * if (error.message.includes('current password')) {
325
+ * console.log('Current password is incorrect');
326
+ * }
327
+ * }
168
328
  */
169
329
  async changePassword(payload, token) {
170
330
  const activeToken = token || this.sessionToken;
@@ -177,7 +337,31 @@ var AuthModule = class {
177
337
  });
178
338
  }
179
339
  /**
180
- * Verify user email with OTP
340
+ * Verifies user's email address using OTP
341
+ *
342
+ * @param {VerifyEmailPayload} payload - Email verification data
343
+ * @param {string} payload.email - User's email address
344
+ * @param {string} payload.otp - One-time password sent to email
345
+ * @returns {Promise<{ message: string }>} Promise resolving to success message
346
+ *
347
+ * @throws {AuthError} If OTP is invalid or expired
348
+ * @throws {AuthError} If email is not found
349
+ *
350
+ * @example
351
+ * // Verify email with OTP
352
+ * const result = await auth.verifyEmail({
353
+ * email: 'user@example.com',
354
+ * otp: '123456'
355
+ * });
356
+ * console.log('Email verified:', result.message);
357
+ *
358
+ * @example
359
+ * // Verify with error handling
360
+ * try {
361
+ * await auth.verifyEmail({ email: 'user@example.com', otp: '000000' });
362
+ * } catch (error) {
363
+ * console.log('Invalid OTP. Please try again.');
364
+ * }
181
365
  */
182
366
  async verifyEmail(payload) {
183
367
  return this.client.request("POST", "/api/userAuth/verify-email", {
@@ -185,7 +369,21 @@ var AuthModule = class {
185
369
  });
186
370
  }
187
371
  /**
188
- * Resend verification OTP
372
+ * Resends verification OTP to user's email
373
+ *
374
+ * @param {ResendOtpPayload} payload - Resend OTP request data
375
+ * @param {string} payload.email - User's email address
376
+ * @returns {Promise<{ message: string }>} Promise resolving to success message
377
+ *
378
+ * @throws {AuthError} If email is not found
379
+ * @throws {AuthError} If too many attempts
380
+ *
381
+ * @example
382
+ * // Resend verification OTP
383
+ * const result = await auth.resendVerificationOtp({
384
+ * email: 'user@example.com'
385
+ * });
386
+ * console.log('OTP resent:', result.message);
189
387
  */
190
388
  async resendVerificationOtp(payload) {
191
389
  return this.client.request("POST", "/api/userAuth/resend-verification-otp", {
@@ -193,7 +391,21 @@ var AuthModule = class {
193
391
  });
194
392
  }
195
393
  /**
196
- * Request password reset OTP
394
+ * Requests a password reset OTP to be sent to user's email
395
+ *
396
+ * @param {RequestPasswordResetPayload} payload - Password reset request data
397
+ * @param {string} payload.email - User's email address
398
+ * @returns {Promise<{ message: string }>} Promise resolving to success message
399
+ *
400
+ * @throws {AuthError} If email is not found
401
+ * @throws {AuthError} If too many attempts
402
+ *
403
+ * @example
404
+ * // Request password reset
405
+ * const result = await auth.requestPasswordReset({
406
+ * email: 'user@example.com'
407
+ * });
408
+ * console.log('Reset OTP sent:', result.message);
197
409
  */
198
410
  async requestPasswordReset(payload) {
199
411
  return this.client.request("POST", "/api/userAuth/request-password-reset", {
@@ -201,7 +413,38 @@ var AuthModule = class {
201
413
  });
202
414
  }
203
415
  /**
204
- * Reset user password with OTP
416
+ * Resets user's password using OTP
417
+ *
418
+ * @param {ResetPasswordPayload} payload - Password reset data
419
+ * @param {string} payload.email - User's email address
420
+ * @param {string} payload.otp - One-time password sent via email
421
+ * @param {string} payload.newPassword - Desired new password
422
+ * @returns {Promise<{ message: string }>} Promise resolving to success message
423
+ *
424
+ * @throws {AuthError} If OTP is invalid or expired
425
+ * @throws {AuthError} If email is not found
426
+ * @throws {AuthError} If new password does not meet requirements
427
+ *
428
+ * @example
429
+ * // Reset password with OTP
430
+ * const result = await auth.resetPassword({
431
+ * email: 'user@example.com',
432
+ * otp: '123456',
433
+ * newPassword: 'NewStrongP@ss789'
434
+ * });
435
+ * console.log('Password reset:', result.message);
436
+ *
437
+ * @example
438
+ * // Reset password with error handling
439
+ * try {
440
+ * await auth.resetPassword({
441
+ * email: 'user@example.com',
442
+ * otp: 'wrong',
443
+ * newPassword: 'newPass123'
444
+ * });
445
+ * } catch (error) {
446
+ * console.log('Invalid OTP. Please request a new one.');
447
+ * }
205
448
  */
206
449
  async resetPassword(payload) {
207
450
  return this.client.request("POST", "/api/userAuth/reset-password", {
@@ -209,14 +452,57 @@ var AuthModule = class {
209
452
  });
210
453
  }
211
454
  /**
212
- * Get public-safe profile by username
455
+ * Retrieves a public-safe user profile by username
456
+ *
457
+ * @param {string} username - Username of the user to fetch
458
+ * @returns {Promise<AuthUser>} Promise resolving to public user profile (sensitive fields omitted)
459
+ *
460
+ * @throws {AuthError} If user with given username does not exist
461
+ *
462
+ * @example
463
+ * // Get public profile
464
+ * const profile = await auth.publicProfile('john_doe');
465
+ * console.log(`${profile.name} joined on ${profile.createdAt}`);
466
+ *
467
+ * @example
468
+ * // Display user profile on a public page
469
+ * try {
470
+ * const user = await auth.publicProfile('username');
471
+ * // Show user info (no email or private data)
472
+ * } catch (error) {
473
+ * console.log('User not found');
474
+ * }
213
475
  */
214
476
  async publicProfile(username) {
215
477
  return this.client.request("GET", `/api/userAuth/public/${username}`);
216
478
  }
217
479
  /**
218
- * Refresh the access token
219
- * @param refreshToken Optional refresh token for header mode. If omitted, uses cookie mode.
480
+ * Refreshes the access token using refresh token or cookie
481
+ *
482
+ * @param {string} [refreshToken] - Optional refresh token for header mode. If omitted, uses cookie mode.
483
+ * @returns {Promise<AuthResponse>} Promise resolving to new authentication response with fresh tokens
484
+ *
485
+ * @throws {AuthError} If refresh token is invalid or expired
486
+ *
487
+ * @example
488
+ * // Refresh token using cookie (if configured)
489
+ * const newTokens = await auth.refreshToken();
490
+ * console.log('New access token:', newTokens.accessToken);
491
+ *
492
+ * @example
493
+ * // Refresh token using explicit refresh token
494
+ * const newTokens = await auth.refreshToken(storedRefreshToken);
495
+ *
496
+ * @example
497
+ * // Auto-refresh before API calls
498
+ * try {
499
+ * await auth.me();
500
+ * } catch (error) {
501
+ * if (error.status === 401) {
502
+ * await auth.refreshToken();
503
+ * // Retry the original request
504
+ * }
505
+ * }
220
506
  */
221
507
  async refreshToken(refreshToken) {
222
508
  const options = {};
@@ -230,14 +516,45 @@ var AuthModule = class {
230
516
  return response;
231
517
  }
232
518
  /**
233
- * Returns the start URL for social authentication.
234
- * Redirect the user's browser to this URL to begin the flow.
519
+ * Returns the start URL for social authentication
520
+ *
521
+ * @param {('github' | 'google')} provider - The social authentication provider
522
+ * @returns {string} URL to redirect the user's browser to begin the OAuth flow
523
+ *
524
+ * @example
525
+ * // Redirect user to social login page
526
+ * const githubUrl = auth.socialStart('github');
527
+ * window.location.href = githubUrl;
528
+ *
529
+ * @example
530
+ * // For Node.js backend
531
+ * const googleUrl = auth.socialStart('google');
532
+ * res.redirect(googleUrl);
235
533
  */
236
534
  socialStart(provider) {
237
535
  return `${this.client.getBaseUrl()}/api/userAuth/social/${provider}/start?key=${this.client.getApiKey()}`;
238
536
  }
239
537
  /**
240
- * Exchange social auth rtCode for a refresh token
538
+ * Exchanges social authentication rtCode for a refresh token
539
+ *
540
+ * @param {SocialExchangePayload} payload - Social exchange data
541
+ * @param {string} payload.rtCode - Return code from social provider
542
+ * @param {string} payload.provider - Social provider ('github' or 'google')
543
+ * @returns {Promise<SocialExchangeResponse>} Promise resolving to authentication response
544
+ *
545
+ * @throws {AuthError} If rtCode is invalid or expired
546
+ * @throws {AuthError} If social provider fails
547
+ *
548
+ * @example
549
+ * // After user returns from social login
550
+ * const rtCode = new URLSearchParams(window.location.search).get('rtCode');
551
+ * if (rtCode) {
552
+ * const response = await auth.socialExchange({
553
+ * rtCode: rtCode,
554
+ * provider: 'github'
555
+ * });
556
+ * console.log('Social login successful:', response.accessToken);
557
+ * }
241
558
  */
242
559
  async socialExchange(payload) {
243
560
  return this.client.request("POST", "/api/userAuth/social/exchange", {
@@ -245,7 +562,30 @@ var AuthModule = class {
245
562
  });
246
563
  }
247
564
  /**
248
- * Revoke the current session and clear local state
565
+ * Revokes the current session and clears local state
566
+ *
567
+ * @param {string} [token] - Optional authentication token (overrides stored token)
568
+ * @returns {Promise<{ success: boolean; message: string }>} Promise resolving to logout status
569
+ *
570
+ * @example
571
+ * // Log out current user
572
+ * const result = await auth.logout();
573
+ * console.log(result.message);
574
+ * // User is now logged out, session token is cleared
575
+ *
576
+ * @example
577
+ * // Log out with custom token
578
+ * const result = await auth.logout(customToken);
579
+ *
580
+ * @example
581
+ * // Logout after API calls
582
+ * try {
583
+ * await auth.logout();
584
+ * // Redirect to login page
585
+ * window.location.href = '/login';
586
+ * } catch (error) {
587
+ * console.log('Logout failed, but local session cleared');
588
+ * }
249
589
  */
250
590
  async logout(token) {
251
591
  const activeToken = token || this.sessionToken;
@@ -265,13 +605,45 @@ var AuthModule = class {
265
605
  return result;
266
606
  }
267
607
  /**
268
- * Manually set the session token (e.g. after social auth exchange)
608
+ * Manually sets the session token (e.g., after social authentication exchange)
609
+ *
610
+ * @param {string} token - The session/access token to store
611
+ *
612
+ * @example
613
+ * // After successful social exchange
614
+ * const response = await auth.socialExchange({ rtCode, provider: 'github' });
615
+ * auth.setToken(response.accessToken);
616
+ *
617
+ * @example
618
+ * // Restore session from localStorage
619
+ * const savedToken = localStorage.getItem('authToken');
620
+ * if (savedToken) {
621
+ * auth.setToken(savedToken);
622
+ * const user = await auth.me();
623
+ * }
269
624
  */
270
625
  setToken(token) {
271
626
  this.sessionToken = token;
272
627
  }
273
628
  /**
274
- * Get the current stored session token
629
+ * Gets the current stored session token
630
+ *
631
+ * @returns {string | undefined} The current session token, if any
632
+ *
633
+ * @example
634
+ * // Get token for custom API calls
635
+ * const token = auth.getToken();
636
+ * if (token) {
637
+ * // Use token in custom API request
638
+ * fetch('/api/custom', { headers: { Authorization: `Bearer ${token}` } });
639
+ * }
640
+ *
641
+ * @example
642
+ * // Save token to localStorage for persistence
643
+ * const token = auth.getToken();
644
+ * if (token) {
645
+ * localStorage.setItem('authToken', token);
646
+ * }
275
647
  */
276
648
  getToken() {
277
649
  return this.sessionToken;
@@ -280,11 +652,49 @@ var AuthModule = class {
280
652
 
281
653
  // src/modules/database.ts
282
654
  var DatabaseModule = class {
655
+ /**
656
+ * Creates an instance of DatabaseModule
657
+ *
658
+ * @param {UrBackendClient} client - The authenticated urBackend client instance
659
+ *
660
+ * @example
661
+ * const client = new UrBackendClient({ secretKey: 'sk_live_xxx' });
662
+ * const db = new DatabaseModule(client);
663
+ */
283
664
  constructor(client) {
284
665
  this.client = client;
285
666
  }
286
667
  /**
287
- * Fetch all documents from a collection with optional query parameters
668
+ * Fetches all documents from a collection with optional query parameters
669
+ *
670
+ * @template T - The document type (extends DocumentData)
671
+ * @param {string} collection - Name of the collection to query
672
+ * @param {QueryParams} [params={}] - Optional query parameters for filtering, sorting, pagination
673
+ * @returns {Promise<T[]>} Promise resolving to an array of documents (empty array if none found)
674
+ *
675
+ * @throws {Error} If collection name is invalid
676
+ * @throws {Error} If authentication fails
677
+ * @throws {Error} If query parameters are malformed
678
+ *
679
+ * @example
680
+ * // Get all users
681
+ * const users = await db.getAll('users');
682
+ *
683
+ * @example
684
+ * // Get users with filters and pagination
685
+ * const activeUsers = await db.getAll('users', {
686
+ * filter: { status: 'active' },
687
+ * limit: 10,
688
+ * skip: 0,
689
+ * sort: '-createdAt'
690
+ * });
691
+ *
692
+ * @example
693
+ * // Get users with populated relations
694
+ * const usersWithPosts = await db.getAll('users', {
695
+ * populate: ['posts'],
696
+ * expand: ['profile']
697
+ * });
288
698
  */
289
699
  async getAll(collection, params = {}) {
290
700
  const queryString = this.buildQueryString(params);
@@ -299,14 +709,118 @@ var DatabaseModule = class {
299
709
  }
300
710
  }
301
711
  /**
302
- * Fetch a single document by its ID
712
+ * Counts documents in a collection with optional filters
713
+ *
714
+ * @param {string} collection - Name of the collection to count
715
+ * @param {Omit<QueryParams, 'count'>} [params={}] - Optional filter parameters
716
+ * @returns {Promise<number>} Promise resolving to the total count of matching documents
717
+ *
718
+ * @throws {Error} If collection name is invalid
719
+ * @throws {Error} If authentication fails
720
+ *
721
+ * @example
722
+ * // Count all users
723
+ * const totalUsers = await db.count('users');
724
+ * console.log(`Total users: ${totalUsers}`);
725
+ *
726
+ * @example
727
+ * // Count users with filter
728
+ * const activeUsers = await db.count('users', {
729
+ * filter: { status: 'active' }
730
+ * });
731
+ *
732
+ * @example
733
+ * // Count for pagination
734
+ * const total = await db.count('products', {
735
+ * filter: { category: 'electronics' }
736
+ * });
737
+ * const totalPages = Math.ceil(total / 10);
738
+ */
739
+ async count(collection, params = {}) {
740
+ const queryString = this.buildQueryString({ ...params, count: "true" });
741
+ const path = `/api/data/${collection}${queryString}`;
742
+ const result = await this.client.request("GET", path);
743
+ return result.count;
744
+ }
745
+ /**
746
+ * Fetches a single document by its ID
747
+ *
748
+ * @template T - The document type (extends DocumentData)
749
+ * @param {string} collection - Name of the collection
750
+ * @param {string} id - Unique identifier of the document
751
+ * @param {Object} [options={}] - Optional parameters
752
+ * @param {string|string[]} [options.populate] - Fields to populate with related data
753
+ * @param {string|string[]} [options.expand] - Fields to expand with nested data
754
+ * @returns {Promise<T>} Promise resolving to the document
755
+ *
756
+ * @throws {NotFoundError} If document with given ID does not exist
757
+ * @throws {Error} If collection name or ID is invalid
758
+ * @throws {Error} If authentication fails
759
+ *
760
+ * @example
761
+ * // Get user by ID
762
+ * const user = await db.getOne('users', 'user_123');
763
+ * console.log(user.name);
764
+ *
765
+ * @example
766
+ * // Get user with populated posts
767
+ * const userWithPosts = await db.getOne('users', 'user_123', {
768
+ * populate: ['posts', 'comments']
769
+ * });
770
+ *
771
+ * @example
772
+ * // Get with error handling
773
+ * try {
774
+ * const user = await db.getOne('users', 'non_existent_id');
775
+ * } catch (error) {
776
+ * if (error instanceof NotFoundError) {
777
+ * console.log('User not found');
778
+ * }
779
+ * }
303
780
  */
304
781
  async getOne(collection, id, options = {}) {
305
782
  const queryString = this.buildQueryString(options);
306
783
  return this.client.request("GET", `/api/data/${collection}/${id}${queryString}`);
307
784
  }
308
785
  /**
309
- * Insert a new document into a collection
786
+ * Inserts a new document into a collection
787
+ *
788
+ * @template T - The document type (extends DocumentData)
789
+ * @param {string} collection - Name of the collection
790
+ * @param {InsertPayload} data - Document data to insert
791
+ * @param {string} [token] - Optional authentication token (overrides client default)
792
+ * @returns {Promise<T>} Promise resolving to the created document with generated ID
793
+ *
794
+ * @throws {Error} If collection name is invalid
795
+ * @throws {Error} If data validation fails
796
+ * @throws {Error} If authentication fails
797
+ * @throws {Error} If unique constraint violation occurs
798
+ *
799
+ * @example
800
+ * // Insert a new user
801
+ * const newUser = await db.insert('users', {
802
+ * name: 'John Doe',
803
+ * email: 'john@example.com',
804
+ * age: 25
805
+ * });
806
+ * console.log('User created:', newUser._id);
807
+ *
808
+ * @example
809
+ * // Insert with custom token
810
+ * const result = await db.insert('posts', {
811
+ * title: 'My Post',
812
+ * content: 'Hello World'
813
+ * }, customAuthToken);
814
+ *
815
+ * @example
816
+ * // Insert with error handling
817
+ * try {
818
+ * const user = await db.insert('users', { email: 'existing@email.com' });
819
+ * } catch (error) {
820
+ * if (error.message.includes('duplicate')) {
821
+ * console.log('Email already exists');
822
+ * }
823
+ * }
310
824
  */
311
825
  async insert(collection, data, token) {
312
826
  return this.client.request("POST", `/api/data/${collection}`, {
@@ -315,7 +829,38 @@ var DatabaseModule = class {
315
829
  });
316
830
  }
317
831
  /**
318
- * Update an existing document by its ID (Full replacement)
832
+ * Updates an existing document by its ID (full replacement)
833
+ *
834
+ * @template T - The document type (extends DocumentData)
835
+ * @param {string} collection - Name of the collection
836
+ * @param {string} id - Unique identifier of the document
837
+ * @param {UpdatePayload} data - Complete document data for replacement
838
+ * @param {string} [token] - Optional authentication token (overrides client default)
839
+ * @returns {Promise<T>} Promise resolving to the updated document
840
+ *
841
+ * @throws {NotFoundError} If document with given ID does not exist
842
+ * @throws {Error} If collection name or ID is invalid
843
+ * @throws {Error} If data validation fails
844
+ * @throws {Error} If authentication fails
845
+ *
846
+ * @example
847
+ * // Full update of a user
848
+ * const updatedUser = await db.update('users', 'user_123', {
849
+ * name: 'Jane Doe',
850
+ * email: 'jane@example.com',
851
+ * age: 26
852
+ * });
853
+ *
854
+ * @example
855
+ * // Update with error handling
856
+ * try {
857
+ * const result = await db.update('users', 'user_123', updatedData);
858
+ * console.log('Update successful:', result);
859
+ * } catch (error) {
860
+ * if (error instanceof NotFoundError) {
861
+ * console.log('User not found');
862
+ * }
863
+ * }
319
864
  */
320
865
  async update(collection, id, data, token) {
321
866
  return this.client.request("PUT", `/api/data/${collection}/${id}`, {
@@ -324,7 +869,42 @@ var DatabaseModule = class {
324
869
  });
325
870
  }
326
871
  /**
327
- * Partially update an existing document by its ID
872
+ * Partially updates an existing document by its ID (only provided fields)
873
+ *
874
+ * @template T - The document type (extends DocumentData)
875
+ * @param {string} collection - Name of the collection
876
+ * @param {string} id - Unique identifier of the document
877
+ * @param {PatchPayload} data - Partial data to update
878
+ * @param {string} [token] - Optional authentication token (overrides client default)
879
+ * @returns {Promise<T>} Promise resolving to the updated document
880
+ *
881
+ * @throws {NotFoundError} If document with given ID does not exist
882
+ * @throws {Error} If collection name or ID is invalid
883
+ * @throws {Error} If data validation fails
884
+ * @throws {Error} If authentication fails
885
+ *
886
+ * @example
887
+ * // Partial update - only update age
888
+ * const updatedUser = await db.patch('users', 'user_123', {
889
+ * age: 26
890
+ * });
891
+ *
892
+ * @example
893
+ * // Add a new field to document
894
+ * const result = await db.patch('users', 'user_123', {
895
+ * lastLogin: new Date().toISOString()
896
+ * });
897
+ *
898
+ * @example
899
+ * // Partial update with error handling
900
+ * try {
901
+ * const result = await db.patch('products', 'prod_123', {
902
+ * price: 29.99
903
+ * });
904
+ * console.log('Price updated:', result);
905
+ * } catch (error) {
906
+ * console.error('Update failed:', error.message);
907
+ * }
328
908
  */
329
909
  async patch(collection, id, data, token) {
330
910
  return this.client.request("PATCH", `/api/data/${collection}/${id}`, {
@@ -333,7 +913,42 @@ var DatabaseModule = class {
333
913
  });
334
914
  }
335
915
  /**
336
- * Delete a document by its ID
916
+ * Deletes a document by its ID
917
+ *
918
+ * @param {string} collection - Name of the collection
919
+ * @param {string} id - Unique identifier of the document to delete
920
+ * @param {string} [token] - Optional authentication token (overrides client default)
921
+ * @returns {Promise<{ deleted: boolean }>} Promise resolving to deletion status
922
+ *
923
+ * @throws {Error} If collection name or ID is invalid
924
+ * @throws {Error} If authentication fails
925
+ * @throws {Error} If user lacks permission to delete
926
+ *
927
+ * @example
928
+ * // Delete a user
929
+ * const result = await db.delete('users', 'user_123');
930
+ * if (result.deleted) {
931
+ * console.log('User deleted successfully');
932
+ * }
933
+ *
934
+ * @example
935
+ * // Delete with error handling
936
+ * try {
937
+ * const { deleted } = await db.delete('posts', 'post_456');
938
+ * if (deleted) {
939
+ * console.log('Post removed');
940
+ * }
941
+ * } catch (error) {
942
+ * console.error('Deletion failed:', error.message);
943
+ * }
944
+ *
945
+ * @example
946
+ * // Delete after checking existence
947
+ * const exists = await db.getOne('users', 'user_123').catch(() => null);
948
+ * if (exists) {
949
+ * await db.delete('users', 'user_123');
950
+ * console.log('User deleted');
951
+ * }
337
952
  */
338
953
  async delete(collection, id, token) {
339
954
  const result = await this.client.request(
@@ -346,6 +961,20 @@ var DatabaseModule = class {
346
961
  }
347
962
  /**
348
963
  * Internal helper to build query string from QueryParams
964
+ *
965
+ * @param {QueryParams} params - Query parameters to convert
966
+ * @returns {string} URL query string (starting with '?' if parameters exist, otherwise empty)
967
+ *
968
+ * @internal
969
+ * @private
970
+ *
971
+ * @example
972
+ * // Returns "?limit=10&sort=-createdAt"
973
+ * buildQueryString({ limit: 10, sort: '-createdAt' })
974
+ *
975
+ * @example
976
+ * // Returns "?status=active&age=25"
977
+ * buildQueryString({ filter: { status: 'active', age: 25 } })
349
978
  */
350
979
  buildQueryString(params) {
351
980
  const searchParams = new URLSearchParams();
@@ -370,11 +999,59 @@ var DatabaseModule = class {
370
999
 
371
1000
  // src/modules/storage.ts
372
1001
  var StorageModule = class {
1002
+ /**
1003
+ * Creates an instance of StorageModule
1004
+ *
1005
+ * @param {UrBackendClient} client - The authenticated urBackend client instance
1006
+ *
1007
+ * @example
1008
+ * const client = new UrBackendClient({ secretKey: 'sk_live_xxx' });
1009
+ * const storage = new StorageModule(client);
1010
+ */
373
1011
  constructor(client) {
374
1012
  this.client = client;
375
1013
  }
376
1014
  /**
377
- * Upload a file to storage
1015
+ * Uploads a file to the urBackend storage
1016
+ *
1017
+ * @param {unknown} file - The file to upload. Supports:
1018
+ * - Browser: File, Blob
1019
+ * - Node.js: Buffer
1020
+ * @param {string} [filename] - Optional custom filename for the uploaded file
1021
+ * @returns {Promise<UploadResponse>} Promise resolving to upload details including URL and file ID
1022
+ *
1023
+ * @throws {Error} If file is invalid or missing
1024
+ * @throws {Error} If file size exceeds limits
1025
+ * @throws {Error} If authentication fails
1026
+ * @throws {Error} If storage quota is exceeded
1027
+ *
1028
+ * @example
1029
+ * // Browser: Upload from file input
1030
+ * const fileInput = document.querySelector('input[type="file"]');
1031
+ * const file = fileInput.files[0];
1032
+ * const result = await storage.upload(file, 'custom-name.pdf');
1033
+ * console.log('File URL:', result.url);
1034
+ *
1035
+ * @example
1036
+ * // Node.js: Upload Buffer
1037
+ * const fs = require('fs');
1038
+ * const buffer = fs.readFileSync('./image.png');
1039
+ * const result = await storage.upload(buffer, 'image.png');
1040
+ * console.log('Uploaded:', result.fileId);
1041
+ *
1042
+ * @example
1043
+ * // Upload without custom filename (uses original name)
1044
+ * const result = await storage.upload(file);
1045
+ *
1046
+ * @example
1047
+ * // Upload with error handling
1048
+ * try {
1049
+ * const result = await storage.upload(file, 'document.pdf');
1050
+ * console.log('Upload successful:', result.url);
1051
+ * } catch (error) {
1052
+ * console.error('Upload failed:', error.message);
1053
+ * // Handle error: retry, show user message, etc.
1054
+ * }
378
1055
  */
379
1056
  async upload(file, filename) {
380
1057
  const formData = new FormData();
@@ -390,7 +1067,40 @@ var StorageModule = class {
390
1067
  });
391
1068
  }
392
1069
  /**
393
- * Delete a file from storage by its path
1070
+ * Deletes a file from storage by its path
1071
+ *
1072
+ * @param {string} path - The file path or URL of the file to delete
1073
+ * @returns {Promise<{ deleted: boolean }>} Promise resolving to deletion status
1074
+ *
1075
+ * @throws {Error} If path is empty or invalid
1076
+ * @throws {Error} If file does not exist
1077
+ * @throws {Error} If authentication fails
1078
+ * @throws {Error} If user lacks permission to delete the file
1079
+ *
1080
+ * @example
1081
+ * // Delete a file by path
1082
+ * const result = await storage.deleteFile('uploads/document.pdf');
1083
+ * if (result.deleted) {
1084
+ * console.log('File deleted successfully');
1085
+ * }
1086
+ *
1087
+ * @example
1088
+ * // Delete with error handling
1089
+ * try {
1090
+ * const result = await storage.deleteFile('uploads/old-file.jpg');
1091
+ * console.log('Deleted:', result.deleted);
1092
+ * } catch (error) {
1093
+ * console.error('Deletion failed:', error.message);
1094
+ * }
1095
+ *
1096
+ * @example
1097
+ * // Delete after upload
1098
+ * const uploadResult = await storage.upload(file, 'temp-file.pdf');
1099
+ * console.log('Uploaded:', uploadResult.url);
1100
+ *
1101
+ * // Later, delete the file
1102
+ * await storage.deleteFile('temp-file.pdf');
1103
+ * console.log('File cleaned up');
394
1104
  */
395
1105
  async deleteFile(path) {
396
1106
  return this.client.request("DELETE", "/api/storage/file", {
@@ -401,11 +1111,49 @@ var StorageModule = class {
401
1111
 
402
1112
  // src/modules/schema.ts
403
1113
  var SchemaModule = class {
1114
+ /**
1115
+ * Creates an instance of SchemaModule
1116
+ *
1117
+ * @param {UrBackendClient} client - The authenticated urBackend client instance
1118
+ *
1119
+ * @example
1120
+ * const client = new UrBackendClient({ secretKey: 'sk_live_xxx' });
1121
+ * const schema = new SchemaModule(client);
1122
+ */
404
1123
  constructor(client) {
405
1124
  this.client = client;
406
1125
  }
407
1126
  /**
408
- * Fetch the schema definition for a collection
1127
+ * Fetches the schema definition for a specific collection
1128
+ *
1129
+ * @param {string} collection - Name of the collection to fetch schema for
1130
+ * @returns {Promise<CollectionSchema>} Promise resolving to the collection schema definition
1131
+ *
1132
+ * @throws {Error} If collection name is empty or contains only whitespace
1133
+ * @throws {Error} If collection does not exist
1134
+ * @throws {Error} If authentication fails
1135
+ *
1136
+ * @example
1137
+ * // Get schema for users collection
1138
+ * const userSchema = await schema.getSchema('users');
1139
+ * console.log(userSchema.fields);
1140
+ *
1141
+ * @example
1142
+ * // Get schema for products collection with error handling
1143
+ * try {
1144
+ * const productSchema = await schema.getSchema('products');
1145
+ * console.log('Schema fields:', Object.keys(productSchema.fields));
1146
+ * } catch (error) {
1147
+ * console.error('Failed to fetch schema:', error.message);
1148
+ * }
1149
+ *
1150
+ * @example
1151
+ * // Validate collection name before fetching
1152
+ * const collectionName = 'my_collection';
1153
+ * if (collectionName.trim()) {
1154
+ * const schemaDef = await schema.getSchema(collectionName);
1155
+ * // Use schema definition
1156
+ * }
409
1157
  */
410
1158
  async getSchema(collection) {
411
1159
  const trimmedCollection = collection.trim();
@@ -420,12 +1168,65 @@ var SchemaModule = class {
420
1168
 
421
1169
  // src/modules/mail.ts
422
1170
  var MailModule = class {
1171
+ /**
1172
+ * Creates an instance of MailModule
1173
+ *
1174
+ * @param {UrBackendClient} client - The authenticated urBackend client instance
1175
+ *
1176
+ * @example
1177
+ * const client = new UrBackendClient({ secretKey: 'sk_live_xxx' });
1178
+ * const mail = new MailModule(client);
1179
+ */
423
1180
  constructor(client) {
424
1181
  this.client = client;
425
1182
  }
426
1183
  /**
427
- * Send an email using the urBackend mail service.
428
- * Note: This requires a Secret Key (sk_live_...) and should be called from a server environment.
1184
+ * Sends an email using the urBackend mail service
1185
+ *
1186
+ * @param {SendMailPayload} payload - The email content and configuration
1187
+ * @param {string} payload.to - Recipient email address
1188
+ * @param {string} payload.subject - Email subject line
1189
+ * @param {string} payload.html - HTML content of the email
1190
+ * @param {string} [payload.from] - Optional sender email address (defaults to configured sender)
1191
+ * @param {string[]} [payload.cc] - Optional CC recipient email addresses
1192
+ * @param {string[]} [payload.bcc] - Optional BCC recipient email addresses
1193
+ * @returns {Promise<SendMailResponse>} Promise resolving to email sending status and message ID
1194
+ *
1195
+ * @throws {Error} If secret key is missing or invalid
1196
+ * @throws {Error} If email validation fails
1197
+ * @throws {Error} If rate limit is exceeded
1198
+ *
1199
+ * @example
1200
+ * // Send a basic email
1201
+ * const result = await mail.send({
1202
+ * to: 'user@example.com',
1203
+ * subject: 'Welcome to urBackend',
1204
+ * html: '<h1>Welcome!</h1><p>Thanks for joining.</p>'
1205
+ * });
1206
+ * console.log(result.messageId);
1207
+ *
1208
+ * @example
1209
+ * // Send an email with CC and custom sender
1210
+ * const result = await mail.send({
1211
+ * from: 'noreply@myapp.com',
1212
+ * to: 'user@example.com',
1213
+ * cc: ['admin@example.com'],
1214
+ * subject: 'Important Update',
1215
+ * html: '<p>Your account has been updated.</p>'
1216
+ * });
1217
+ *
1218
+ * @example
1219
+ * // Send email with error handling
1220
+ * try {
1221
+ * const result = await mail.send({
1222
+ * to: 'user@example.com',
1223
+ * subject: 'Test',
1224
+ * html: '<p>Test email</p>'
1225
+ * });
1226
+ * console.log('Email sent:', result);
1227
+ * } catch (error) {
1228
+ * console.error('Failed to send email:', error);
1229
+ * }
429
1230
  */
430
1231
  async send(payload) {
431
1232
  return this.client.request("POST", "/api/mail/send", {