@wowsql/sdk 3.6.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auth.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { WOWSQLError } from './errors';
2
+ export { WOWSQLError };
1
3
  export interface ProjectAuthClientConfig {
2
4
  /** Project slug or full URL (e.g., `myproject` or `https://myproject.wowsql.com`) */
3
5
  projectUrl: string;
@@ -7,6 +9,8 @@ export interface ProjectAuthClientConfig {
7
9
  secure?: boolean;
8
10
  /** Request timeout in milliseconds */
9
11
  timeout?: number;
12
+ /** Verify SSL certificates (default: true). Set to false for self-signed certs in dev. */
13
+ verifySsl?: boolean;
10
14
  /**
11
15
  * Unified API key - Anonymous Key (wowsql_anon_...) for client-side,
12
16
  * or Service Role Key (wowsql_service_...) for server-side.
@@ -146,6 +150,74 @@ export declare class ProjectAuthClient {
146
150
  success: boolean;
147
151
  message: string;
148
152
  }>;
153
+ /**
154
+ * Send OTP code to user's email.
155
+ *
156
+ * Supports login, signup, and password_reset purposes.
157
+ *
158
+ * @param email - User's email address
159
+ * @param purpose - Purpose of OTP - 'login', 'signup', or 'password_reset' (default: 'login')
160
+ * @returns Object with success status and message
161
+ */
162
+ sendOtp(email: string, purpose?: 'login' | 'signup' | 'password_reset'): Promise<{
163
+ success: boolean;
164
+ message: string;
165
+ }>;
166
+ /**
167
+ * Verify OTP and complete authentication.
168
+ *
169
+ * For signup: Creates new user if doesn't exist
170
+ * For login: Authenticates existing user
171
+ * For password_reset: Updates password if newPassword provided
172
+ *
173
+ * @param email - User's email address
174
+ * @param otp - 6-digit OTP code
175
+ * @param purpose - Purpose of OTP - 'login', 'signup', or 'password_reset' (default: 'login')
176
+ * @param newPassword - Required for password_reset purpose, new password (minimum 8 characters)
177
+ * @returns AuthResponse with session tokens and user info (for login/signup) or success message (for password_reset)
178
+ */
179
+ verifyOtp(email: string, otp: string, purpose?: 'login' | 'signup' | 'password_reset', newPassword?: string): Promise<AuthResponse | {
180
+ success: boolean;
181
+ message: string;
182
+ }>;
183
+ /**
184
+ * Send magic link to user's email.
185
+ *
186
+ * Supports login, signup, and email_verification purposes.
187
+ *
188
+ * @param email - User's email address
189
+ * @param purpose - Purpose of magic link - 'login', 'signup', or 'email_verification' (default: 'login')
190
+ * @returns Object with success status and message
191
+ */
192
+ sendMagicLink(email: string, purpose?: 'login' | 'signup' | 'email_verification'): Promise<{
193
+ success: boolean;
194
+ message: string;
195
+ }>;
196
+ /**
197
+ * Verify email using token (from magic link or OTP verification).
198
+ *
199
+ * Marks email as verified and sends welcome email.
200
+ *
201
+ * @param token - Verification token from email
202
+ * @returns Object with success status, message, and user info
203
+ */
204
+ verifyEmail(token: string): Promise<{
205
+ success: boolean;
206
+ message: string;
207
+ user?: AuthUser;
208
+ }>;
209
+ /**
210
+ * Resend verification email.
211
+ *
212
+ * Always returns success to prevent email enumeration.
213
+ *
214
+ * @param email - User's email address
215
+ * @returns Object with success status and message
216
+ */
217
+ resendVerification(email: string): Promise<{
218
+ success: boolean;
219
+ message: string;
220
+ }>;
149
221
  /**
150
222
  * Get the current session tokens.
151
223
  */
@@ -164,6 +236,48 @@ export declare class ProjectAuthClient {
164
236
  * Clear all stored tokens.
165
237
  */
166
238
  clearSession(): void;
239
+ /**
240
+ * Log out the current user. Invalidates the session on the server.
241
+ *
242
+ * @param accessToken - Optional access token override (uses stored token if not provided)
243
+ * @returns Object with success status and message
244
+ */
245
+ logout(accessToken?: string): Promise<{
246
+ success: boolean;
247
+ message: string;
248
+ }>;
249
+ /**
250
+ * Refresh the access token using a refresh token.
251
+ *
252
+ * @param refreshToken - Optional refresh token override (uses stored token if not provided)
253
+ * @returns AuthResponse with new session tokens
254
+ */
255
+ refreshSession(refreshToken?: string): Promise<AuthResponse>;
256
+ /**
257
+ * Change the current user's password.
258
+ *
259
+ * @param currentPassword - Current password for verification
260
+ * @param newPassword - New password (minimum 8 characters)
261
+ * @param accessToken - Optional access token override
262
+ * @returns Object with success status and message
263
+ */
264
+ changePassword(currentPassword: string, newPassword: string, accessToken?: string): Promise<{
265
+ success: boolean;
266
+ message: string;
267
+ }>;
268
+ /**
269
+ * Update the current user's profile.
270
+ *
271
+ * @param updates - Fields to update
272
+ * @param accessToken - Optional access token override
273
+ * @returns Updated AuthUser
274
+ */
275
+ updateUser(updates: {
276
+ fullName?: string;
277
+ avatarUrl?: string;
278
+ username?: string;
279
+ userMetadata?: Record<string, any>;
280
+ }, accessToken?: string): Promise<AuthUser>;
167
281
  private persistSession;
168
282
  private toWowError;
169
283
  }
package/dist/auth.js CHANGED
@@ -3,9 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ProjectAuthClient = void 0;
6
+ exports.ProjectAuthClient = exports.WOWSQLError = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const errors_1 = require("./errors");
9
+ Object.defineProperty(exports, "WOWSQLError", { enumerable: true, get: function () { return errors_1.WOWSQLError; } });
9
10
  class MemoryAuthTokenStorage {
10
11
  constructor() {
11
12
  this.accessToken = null;
@@ -216,6 +217,145 @@ class ProjectAuthClient {
216
217
  throw this.toWowError(error);
217
218
  }
218
219
  }
220
+ /**
221
+ * Send OTP code to user's email.
222
+ *
223
+ * Supports login, signup, and password_reset purposes.
224
+ *
225
+ * @param email - User's email address
226
+ * @param purpose - Purpose of OTP - 'login', 'signup', or 'password_reset' (default: 'login')
227
+ * @returns Object with success status and message
228
+ */
229
+ async sendOtp(email, purpose = 'login') {
230
+ if (!['login', 'signup', 'password_reset'].includes(purpose)) {
231
+ throw new errors_1.WOWSQLError("Purpose must be 'login', 'signup', or 'password_reset'");
232
+ }
233
+ try {
234
+ const response = await this.client.post('/otp/send', {
235
+ email,
236
+ purpose
237
+ });
238
+ return {
239
+ success: response.data.success ?? true,
240
+ message: response.data.message ?? 'If that email exists, an OTP code has been sent'
241
+ };
242
+ }
243
+ catch (error) {
244
+ throw this.toWowError(error);
245
+ }
246
+ }
247
+ /**
248
+ * Verify OTP and complete authentication.
249
+ *
250
+ * For signup: Creates new user if doesn't exist
251
+ * For login: Authenticates existing user
252
+ * For password_reset: Updates password if newPassword provided
253
+ *
254
+ * @param email - User's email address
255
+ * @param otp - 6-digit OTP code
256
+ * @param purpose - Purpose of OTP - 'login', 'signup', or 'password_reset' (default: 'login')
257
+ * @param newPassword - Required for password_reset purpose, new password (minimum 8 characters)
258
+ * @returns AuthResponse with session tokens and user info (for login/signup) or success message (for password_reset)
259
+ */
260
+ async verifyOtp(email, otp, purpose = 'login', newPassword) {
261
+ if (!['login', 'signup', 'password_reset'].includes(purpose)) {
262
+ throw new errors_1.WOWSQLError("Purpose must be 'login', 'signup', or 'password_reset'");
263
+ }
264
+ if (purpose === 'password_reset' && !newPassword) {
265
+ throw new errors_1.WOWSQLError("newPassword is required for password_reset purpose");
266
+ }
267
+ try {
268
+ const payload = {
269
+ email,
270
+ otp,
271
+ purpose
272
+ };
273
+ if (newPassword) {
274
+ payload.new_password = newPassword;
275
+ }
276
+ const response = await this.client.post('/otp/verify', payload);
277
+ if (purpose === 'password_reset') {
278
+ return {
279
+ success: response.data.success ?? true,
280
+ message: response.data.message ?? 'Password reset successfully! You can now login with your new password'
281
+ };
282
+ }
283
+ const session = this.persistSession(response.data);
284
+ const user = response.data.user ? mapUser(response.data.user) : undefined;
285
+ return { user, session };
286
+ }
287
+ catch (error) {
288
+ throw this.toWowError(error);
289
+ }
290
+ }
291
+ /**
292
+ * Send magic link to user's email.
293
+ *
294
+ * Supports login, signup, and email_verification purposes.
295
+ *
296
+ * @param email - User's email address
297
+ * @param purpose - Purpose of magic link - 'login', 'signup', or 'email_verification' (default: 'login')
298
+ * @returns Object with success status and message
299
+ */
300
+ async sendMagicLink(email, purpose = 'login') {
301
+ if (!['login', 'signup', 'email_verification'].includes(purpose)) {
302
+ throw new errors_1.WOWSQLError("Purpose must be 'login', 'signup', or 'email_verification'");
303
+ }
304
+ try {
305
+ const response = await this.client.post('/magic-link/send', {
306
+ email,
307
+ purpose
308
+ });
309
+ return {
310
+ success: response.data.success ?? true,
311
+ message: response.data.message ?? 'If that email exists, a magic link has been sent'
312
+ };
313
+ }
314
+ catch (error) {
315
+ throw this.toWowError(error);
316
+ }
317
+ }
318
+ /**
319
+ * Verify email using token (from magic link or OTP verification).
320
+ *
321
+ * Marks email as verified and sends welcome email.
322
+ *
323
+ * @param token - Verification token from email
324
+ * @returns Object with success status, message, and user info
325
+ */
326
+ async verifyEmail(token) {
327
+ try {
328
+ const response = await this.client.post('/verify-email', { token });
329
+ return {
330
+ success: response.data.success ?? true,
331
+ message: response.data.message ?? 'Email verified successfully!',
332
+ user: response.data.user ? mapUser(response.data.user) : undefined
333
+ };
334
+ }
335
+ catch (error) {
336
+ throw this.toWowError(error);
337
+ }
338
+ }
339
+ /**
340
+ * Resend verification email.
341
+ *
342
+ * Always returns success to prevent email enumeration.
343
+ *
344
+ * @param email - User's email address
345
+ * @returns Object with success status and message
346
+ */
347
+ async resendVerification(email) {
348
+ try {
349
+ const response = await this.client.post('/resend-verification', { email });
350
+ return {
351
+ success: response.data.success ?? true,
352
+ message: response.data.message ?? 'If that email exists, a verification email has been sent'
353
+ };
354
+ }
355
+ catch (error) {
356
+ throw this.toWowError(error);
357
+ }
358
+ }
219
359
  /**
220
360
  * Get the current session tokens.
221
361
  */
@@ -243,6 +383,111 @@ class ProjectAuthClient {
243
383
  this.storage.setAccessToken(null);
244
384
  this.storage.setRefreshToken(null);
245
385
  }
386
+ /**
387
+ * Log out the current user. Invalidates the session on the server.
388
+ *
389
+ * @param accessToken - Optional access token override (uses stored token if not provided)
390
+ * @returns Object with success status and message
391
+ */
392
+ async logout(accessToken) {
393
+ const token = accessToken || this.accessToken || this.storage.getAccessToken();
394
+ if (!token) {
395
+ throw new errors_1.WOWSQLError('Access token is required. Please sign in first.');
396
+ }
397
+ try {
398
+ const response = await this.client.post('/logout', {}, { headers: { Authorization: `Bearer ${token}` } });
399
+ this.clearSession();
400
+ return {
401
+ success: response.data.success ?? true,
402
+ message: response.data.message ?? 'Logged out successfully',
403
+ };
404
+ }
405
+ catch (error) {
406
+ this.clearSession();
407
+ throw this.toWowError(error);
408
+ }
409
+ }
410
+ /**
411
+ * Refresh the access token using a refresh token.
412
+ *
413
+ * @param refreshToken - Optional refresh token override (uses stored token if not provided)
414
+ * @returns AuthResponse with new session tokens
415
+ */
416
+ async refreshSession(refreshToken) {
417
+ const token = refreshToken || this.refreshToken || this.storage.getRefreshToken();
418
+ if (!token) {
419
+ throw new errors_1.WOWSQLError('Refresh token is required');
420
+ }
421
+ try {
422
+ const response = await this.client.post('/token/refresh', {
423
+ refresh_token: token,
424
+ });
425
+ const session = this.persistSession(response.data);
426
+ const user = response.data.user ? mapUser(response.data.user) : undefined;
427
+ return { user, session };
428
+ }
429
+ catch (error) {
430
+ throw this.toWowError(error);
431
+ }
432
+ }
433
+ /**
434
+ * Change the current user's password.
435
+ *
436
+ * @param currentPassword - Current password for verification
437
+ * @param newPassword - New password (minimum 8 characters)
438
+ * @param accessToken - Optional access token override
439
+ * @returns Object with success status and message
440
+ */
441
+ async changePassword(currentPassword, newPassword, accessToken) {
442
+ const token = accessToken || this.accessToken || this.storage.getAccessToken();
443
+ if (!token) {
444
+ throw new errors_1.WOWSQLError('Access token is required. Please sign in first.');
445
+ }
446
+ try {
447
+ const response = await this.client.post('/change-password', {
448
+ current_password: currentPassword,
449
+ new_password: newPassword,
450
+ }, { headers: { Authorization: `Bearer ${token}` } });
451
+ return {
452
+ success: response.data.success ?? true,
453
+ message: response.data.message ?? 'Password changed successfully',
454
+ };
455
+ }
456
+ catch (error) {
457
+ throw this.toWowError(error);
458
+ }
459
+ }
460
+ /**
461
+ * Update the current user's profile.
462
+ *
463
+ * @param updates - Fields to update
464
+ * @param accessToken - Optional access token override
465
+ * @returns Updated AuthUser
466
+ */
467
+ async updateUser(updates, accessToken) {
468
+ const token = accessToken || this.accessToken || this.storage.getAccessToken();
469
+ if (!token) {
470
+ throw new errors_1.WOWSQLError('Access token is required. Please sign in first.');
471
+ }
472
+ try {
473
+ const body = {};
474
+ if (updates.fullName !== undefined)
475
+ body.full_name = updates.fullName;
476
+ if (updates.avatarUrl !== undefined)
477
+ body.avatar_url = updates.avatarUrl;
478
+ if (updates.username !== undefined)
479
+ body.username = updates.username;
480
+ if (updates.userMetadata !== undefined)
481
+ body.user_metadata = updates.userMetadata;
482
+ const response = await this.client.patch('/me', body, {
483
+ headers: { Authorization: `Bearer ${token}` },
484
+ });
485
+ return mapUser(response.data);
486
+ }
487
+ catch (error) {
488
+ throw this.toWowError(error);
489
+ }
490
+ }
246
491
  persistSession(raw) {
247
492
  const session = mapSession(raw);
248
493
  this.accessToken = session.accessToken;
package/dist/errors.d.ts CHANGED
@@ -3,3 +3,12 @@ export declare class WOWSQLError extends Error {
3
3
  response?: any | undefined;
4
4
  constructor(message: string, statusCode?: number | undefined, response?: any | undefined);
5
5
  }
6
+ export declare class SchemaPermissionError extends WOWSQLError {
7
+ constructor(message?: string, statusCode?: number, response?: any);
8
+ }
9
+ export declare class StorageError extends WOWSQLError {
10
+ constructor(message: string, statusCode?: number, response?: any);
11
+ }
12
+ export declare class StorageLimitExceededError extends StorageError {
13
+ constructor(message?: string, statusCode?: number, response?: any);
14
+ }
package/dist/errors.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WOWSQLError = void 0;
3
+ exports.StorageLimitExceededError = exports.StorageError = exports.SchemaPermissionError = exports.WOWSQLError = void 0;
4
4
  class WOWSQLError extends Error {
5
5
  constructor(message, statusCode, response) {
6
6
  super(message);
@@ -10,3 +10,24 @@ class WOWSQLError extends Error {
10
10
  }
11
11
  }
12
12
  exports.WOWSQLError = WOWSQLError;
13
+ class SchemaPermissionError extends WOWSQLError {
14
+ constructor(message = 'Schema operations require a SERVICE ROLE key. You are using an anonymous key which cannot modify database schema.', statusCode = 403, response) {
15
+ super(message, statusCode, response);
16
+ this.name = 'SchemaPermissionError';
17
+ }
18
+ }
19
+ exports.SchemaPermissionError = SchemaPermissionError;
20
+ class StorageError extends WOWSQLError {
21
+ constructor(message, statusCode, response) {
22
+ super(message, statusCode, response);
23
+ this.name = 'StorageError';
24
+ }
25
+ }
26
+ exports.StorageError = StorageError;
27
+ class StorageLimitExceededError extends StorageError {
28
+ constructor(message = 'Storage limit exceeded', statusCode = 413, response) {
29
+ super(message, statusCode, response);
30
+ this.name = 'StorageLimitExceededError';
31
+ }
32
+ }
33
+ exports.StorageLimitExceededError = StorageLimitExceededError;
package/dist/index.d.ts CHANGED
@@ -18,6 +18,8 @@ export interface WowSQLConfig {
18
18
  secure?: boolean;
19
19
  /** Request timeout in milliseconds (default: 30000) */
20
20
  timeout?: number;
21
+ /** Verify SSL certificates (default: true). Set to false for self-signed certs in dev. */
22
+ verifySsl?: boolean;
21
23
  }
22
24
  export interface QueryOptions {
23
25
  /** Columns to select (comma-separated or array) - can include expressions like "COUNT(*)", "DATE(created_at) as date" */
@@ -111,6 +113,19 @@ export declare class WowSQLClient {
111
113
  status: string;
112
114
  timestamp: string;
113
115
  }>;
116
+ /**
117
+ * Get the underlying axios instance for advanced use cases
118
+ */
119
+ getHttpClient(): AxiosInstance;
120
+ /**
121
+ * Get the base URL for this client
122
+ */
123
+ getBaseUrl(): string;
124
+ /**
125
+ * Close the client and clean up resources.
126
+ * Cancels any pending requests.
127
+ */
128
+ close(): void;
114
129
  }
115
130
  export declare class Table<T = any> {
116
131
  private client;
@@ -144,6 +159,21 @@ export declare class Table<T = any> {
144
159
  * Create a new record
145
160
  */
146
161
  create(data: Partial<T>): Promise<CreateResponse>;
162
+ /**
163
+ * Insert a new record (alias for create)
164
+ */
165
+ insert(data: Partial<T>): Promise<CreateResponse>;
166
+ /**
167
+ * Insert multiple records at once
168
+ */
169
+ bulkInsert(records: Partial<T>[]): Promise<CreateResponse[]>;
170
+ /**
171
+ * Upsert a record — insert or update on conflict.
172
+ *
173
+ * @param data - Record data to upsert
174
+ * @param onConflict - Column to detect conflict on (default: 'id')
175
+ */
176
+ upsert(data: Partial<T>, onConflict?: string): Promise<CreateResponse | UpdateResponse>;
147
177
  /**
148
178
  * Update a record by ID
149
179
  */
@@ -152,6 +182,23 @@ export declare class Table<T = any> {
152
182
  * Delete a record by ID
153
183
  */
154
184
  delete(id: string | number): Promise<DeleteResponse>;
185
+ eq(column: string, value: any): QueryBuilder<T>;
186
+ neq(column: string, value: any): QueryBuilder<T>;
187
+ gt(column: string, value: any): QueryBuilder<T>;
188
+ gte(column: string, value: any): QueryBuilder<T>;
189
+ lt(column: string, value: any): QueryBuilder<T>;
190
+ lte(column: string, value: any): QueryBuilder<T>;
191
+ orderBy(column: string | OrderByItem[], direction?: 'asc' | 'desc'): QueryBuilder<T>;
192
+ count(): Promise<number>;
193
+ paginate(page?: number, perPage?: number): Promise<{
194
+ data: T[];
195
+ page: number;
196
+ perPage: number;
197
+ total: number;
198
+ totalPages: number;
199
+ hasNext: boolean;
200
+ hasPrev: boolean;
201
+ }>;
155
202
  }
156
203
  export declare class QueryBuilder<T = any> {
157
204
  private client;
@@ -202,14 +249,102 @@ export declare class QueryBuilder<T = any> {
202
249
  * Skip records (pagination)
203
250
  */
204
251
  offset(offset: number): this;
252
+ /**
253
+ * Filter where column equals value
254
+ */
255
+ eq(column: string, value: any): this;
256
+ /**
257
+ * Filter where column does not equal value
258
+ */
259
+ neq(column: string, value: any): this;
260
+ /**
261
+ * Filter where column is greater than value
262
+ */
263
+ gt(column: string, value: any): this;
264
+ /**
265
+ * Filter where column is greater than or equal to value
266
+ */
267
+ gte(column: string, value: any): this;
268
+ /**
269
+ * Filter where column is less than value
270
+ */
271
+ lt(column: string, value: any): this;
272
+ /**
273
+ * Filter where column is less than or equal to value
274
+ */
275
+ lte(column: string, value: any): this;
276
+ /**
277
+ * Filter where column matches pattern (SQL LIKE)
278
+ */
279
+ like(column: string, value: string): this;
280
+ /**
281
+ * Filter where column IS NULL
282
+ */
283
+ isNull(column: string): this;
284
+ /**
285
+ * Filter where column IS NOT NULL
286
+ */
287
+ isNotNull(column: string): this;
288
+ /**
289
+ * Filter where column is in list of values
290
+ */
291
+ in(column: string, values: any[]): this;
292
+ /**
293
+ * Filter where column is not in list of values
294
+ */
295
+ notIn(column: string, values: any[]): this;
296
+ /**
297
+ * Filter where column is between min and max values
298
+ */
299
+ between(column: string, minValue: any, maxValue: any): this;
300
+ /**
301
+ * Filter where column is not between min and max values
302
+ */
303
+ notBetween(column: string, minValue: any, maxValue: any): this;
304
+ /**
305
+ * Add an OR filter condition
306
+ */
307
+ or(column: string, operator: FilterExpression['operator'], value: any): this;
205
308
  /**
206
309
  * Execute query - uses POST /{table}/query for advanced features, GET for simple queries
207
310
  */
208
311
  get(additionalOptions?: QueryOptions): Promise<QueryResponse<T>>;
312
+ /**
313
+ * Execute the query (alias for get)
314
+ */
315
+ execute(additionalOptions?: QueryOptions): Promise<QueryResponse<T>>;
209
316
  /**
210
317
  * Get first record
211
318
  */
212
319
  first(): Promise<T | null>;
320
+ /**
321
+ * Get exactly one record. Throws if zero or multiple results.
322
+ */
323
+ single(): Promise<T>;
324
+ /**
325
+ * Get record count matching current filters (uses COUNT(*) aggregate).
326
+ */
327
+ count(): Promise<number>;
328
+ /**
329
+ * Get sum of a column matching current filters.
330
+ */
331
+ sum(column: string): Promise<number>;
332
+ /**
333
+ * Get average of a column matching current filters.
334
+ */
335
+ avg(column: string): Promise<number>;
336
+ /**
337
+ * Paginate results. Returns data along with pagination metadata.
338
+ */
339
+ paginate(page?: number, perPage?: number): Promise<{
340
+ data: T[];
341
+ page: number;
342
+ perPage: number;
343
+ total: number;
344
+ totalPages: number;
345
+ hasNext: boolean;
346
+ hasPrev: boolean;
347
+ }>;
213
348
  }
214
349
  export * from './storage';
215
350
  export * from './auth';