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