@wowsql/sdk 3.4.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.
@@ -0,0 +1,404 @@
1
+ "use strict";
2
+ /**
3
+ * WowSQL Storage SDK - S3 Storage management with automatic quota validation
4
+ *
5
+ * @version 2.1.0
6
+ * @license MIT
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __importDefault = (this && this.__importDefault) || function (mod) {
42
+ return (mod && mod.__esModule) ? mod : { "default": mod };
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.WowSQLStorage = exports.StorageLimitExceededError = exports.StorageError = void 0;
46
+ const axios_1 = __importDefault(require("axios"));
47
+ const fs = __importStar(require("fs"));
48
+ class StorageError extends Error {
49
+ constructor(message, statusCode, response) {
50
+ super(message);
51
+ this.statusCode = statusCode;
52
+ this.response = response;
53
+ this.name = 'StorageError';
54
+ }
55
+ }
56
+ exports.StorageError = StorageError;
57
+ class StorageLimitExceededError extends StorageError {
58
+ constructor(message, statusCode, response) {
59
+ super(message, statusCode, response);
60
+ this.name = 'StorageLimitExceededError';
61
+ }
62
+ }
63
+ exports.StorageLimitExceededError = StorageLimitExceededError;
64
+ // ==================== Storage Client ====================
65
+ /**
66
+ * WowSQL Storage Client - Manage S3 storage with automatic quota validation
67
+ *
68
+ * Features:
69
+ * - Automatic storage limit validation before upload
70
+ * - Real-time quota checking
71
+ * - File upload/download/delete operations
72
+ * - Presigned URL generation
73
+ * - Storage provisioning and management
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * const storage = new WowSQLStorage({
78
+ * projectSlug: 'myproject',
79
+ * apiKey: 'your_api_key'
80
+ * });
81
+ *
82
+ * // Check quota
83
+ * const quota = await storage.getQuota();
84
+ * console.log(`Available: ${quota.storage_available_gb.toFixed(2)} GB`);
85
+ *
86
+ * // Upload file (auto-validates limits)
87
+ * const fileBuffer = fs.readFileSync('document.pdf');
88
+ * const result = await storage.uploadFile(fileBuffer, 'document.pdf', {
89
+ * folder: 'documents'
90
+ * });
91
+ *
92
+ * // List files
93
+ * const files = await storage.listFiles({ prefix: 'documents/' });
94
+ * ```
95
+ */
96
+ class WowSQLStorage {
97
+ constructor(config) {
98
+ this.projectSlug = config.projectSlug;
99
+ this.autoCheckQuota = config.autoCheckQuota !== false;
100
+ const baseUrl = config.baseUrl || 'https://api.wowsql.com';
101
+ // Create axios instance
102
+ this.client = axios_1.default.create({
103
+ baseURL: baseUrl,
104
+ headers: {
105
+ 'Authorization': `Bearer ${config.apiKey}`,
106
+ },
107
+ timeout: config.timeout || 60000, // 60s default for file uploads
108
+ });
109
+ // Add error interceptor
110
+ this.client.interceptors.response.use((response) => response, (error) => {
111
+ if (error.response) {
112
+ const errorData = error.response.data;
113
+ const errorMessage = errorData?.detail || errorData?.message || error.message;
114
+ // Check for storage limit exceeded
115
+ if (error.response.status === 413) {
116
+ throw new StorageLimitExceededError(errorMessage, error.response.status, errorData);
117
+ }
118
+ throw new StorageError(errorMessage, error.response.status, errorData);
119
+ }
120
+ throw new StorageError(error.message);
121
+ });
122
+ }
123
+ /**
124
+ * Get storage quota and usage information
125
+ *
126
+ * @param forceRefresh - Force refresh quota from server (default: false)
127
+ * @returns Storage quota details
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const quota = await storage.getQuota();
132
+ * console.log(`Used: ${quota.storage_used_gb} GB`);
133
+ * console.log(`Available: ${quota.storage_available_gb} GB`);
134
+ * console.log(`Usage: ${quota.usage_percentage}%`);
135
+ * ```
136
+ */
137
+ async getQuota(forceRefresh = false) {
138
+ if (this.quotaCache && !forceRefresh) {
139
+ return this.quotaCache;
140
+ }
141
+ const response = await this.client.get(`/api/v1/storage/s3/projects/${this.projectSlug}/quota`);
142
+ this.quotaCache = response.data;
143
+ return response.data;
144
+ }
145
+ /**
146
+ * Check if file upload is allowed based on storage quota
147
+ *
148
+ * @param fileSizeBytes - Size of file to upload in bytes
149
+ * @returns Object with allowed status and message
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * const fileSize = 1024 * 1024 * 500; // 500 MB
154
+ * const check = await storage.checkUploadAllowed(fileSize);
155
+ * if (!check.allowed) {
156
+ * console.error(check.message);
157
+ * }
158
+ * ```
159
+ */
160
+ async checkUploadAllowed(fileSizeBytes) {
161
+ const quota = await this.getQuota(true);
162
+ const fileSizeGB = fileSizeBytes / (1024 ** 3);
163
+ if (fileSizeGB > quota.storage_available_gb) {
164
+ return {
165
+ allowed: false,
166
+ message: `Storage limit exceeded! File size: ${fileSizeGB.toFixed(4)} GB, ` +
167
+ `Available: ${quota.storage_available_gb.toFixed(4)} GB. ` +
168
+ `Upgrade your plan to get more storage.`
169
+ };
170
+ }
171
+ return {
172
+ allowed: true,
173
+ message: `Upload allowed. ${quota.storage_available_gb.toFixed(4)} GB available.`
174
+ };
175
+ }
176
+ /**
177
+ * Upload a file to S3 storage with automatic quota validation
178
+ *
179
+ * @param fileData - File data as Buffer or Blob
180
+ * @param fileName - File name
181
+ * @param options - Upload options
182
+ * @returns Upload result
183
+ *
184
+ * @throws {StorageLimitExceededError} If storage quota would be exceeded
185
+ * @throws {StorageError} If upload fails
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * // Node.js - from file
190
+ * const fileBuffer = fs.readFileSync('photo.jpg');
191
+ * const result = await storage.uploadFile(fileBuffer, 'photo.jpg', {
192
+ * folder: 'images',
193
+ * contentType: 'image/jpeg'
194
+ * });
195
+ *
196
+ * // Browser - from File input
197
+ * const file = document.querySelector('input[type="file"]').files[0];
198
+ * const arrayBuffer = await file.arrayBuffer();
199
+ * const result = await storage.uploadFile(
200
+ * Buffer.from(arrayBuffer),
201
+ * file.name,
202
+ * { folder: 'uploads' }
203
+ * );
204
+ * ```
205
+ */
206
+ async uploadFile(fileData, fileName, options) {
207
+ // Get file size
208
+ const fileSize = fileData instanceof Buffer ? fileData.length : fileData.size;
209
+ // Check quota if enabled
210
+ const shouldCheck = options?.checkQuota !== undefined ? options.checkQuota : this.autoCheckQuota;
211
+ if (shouldCheck) {
212
+ const check = await this.checkUploadAllowed(fileSize);
213
+ if (!check.allowed) {
214
+ throw new StorageLimitExceededError(check.message, 413);
215
+ }
216
+ }
217
+ // Prepare form data
218
+ const formData = new FormData();
219
+ const blob = fileData instanceof Buffer ? new Blob([fileData]) : fileData;
220
+ formData.append('file', blob, fileName);
221
+ // Build URL with query params
222
+ const params = new URLSearchParams();
223
+ if (options?.folder) {
224
+ params.append('folder', options.folder);
225
+ }
226
+ const url = `/api/v1/storage/s3/projects/${this.projectSlug}/upload${params.toString() ? '?' + params.toString() : ''}`;
227
+ // Upload
228
+ const response = await this.client.post(url, formData, {
229
+ headers: {
230
+ 'Content-Type': 'multipart/form-data',
231
+ },
232
+ });
233
+ // Clear quota cache after upload
234
+ this.quotaCache = undefined;
235
+ return response.data;
236
+ }
237
+ /**
238
+ * Upload a file from filesystem path (Node.js only)
239
+ *
240
+ * @param filePath - Path to local file
241
+ * @param fileName - Optional file name in bucket (defaults to filename)
242
+ * @param options - Upload options
243
+ * @returns Upload result
244
+ *
245
+ * @example
246
+ * ```typescript
247
+ * const result = await storage.uploadFromPath(
248
+ * 'documents/report.pdf',
249
+ * 'report.pdf',
250
+ * { folder: 'reports' }
251
+ * );
252
+ * ```
253
+ */
254
+ async uploadFromPath(filePath, fileName, options) {
255
+ if (!fs.existsSync(filePath)) {
256
+ throw new Error(`File not found: ${filePath}`);
257
+ }
258
+ const fileBuffer = fs.readFileSync(filePath);
259
+ const name = fileName || filePath.split('/').pop() || 'file';
260
+ return this.uploadFile(fileBuffer, name, options);
261
+ }
262
+ /**
263
+ * List files in S3 bucket
264
+ *
265
+ * @param options - List options
266
+ * @returns Array of storage files
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * const files = await storage.listFiles({ prefix: 'documents/' });
271
+ * for (const file of files) {
272
+ * console.log(`${file.key}: ${(file.size / 1024 / 1024).toFixed(2)} MB`);
273
+ * }
274
+ * ```
275
+ */
276
+ async listFiles(options) {
277
+ const params = {};
278
+ if (options?.prefix)
279
+ params.prefix = options.prefix;
280
+ if (options?.maxKeys)
281
+ params.max_keys = options.maxKeys;
282
+ const response = await this.client.get(`/api/v1/storage/s3/projects/${this.projectSlug}/files`, { params });
283
+ return response.data.files || [];
284
+ }
285
+ /**
286
+ * Delete a file from S3 bucket
287
+ *
288
+ * @param fileKey - Path to file in bucket
289
+ * @returns Deletion result
290
+ *
291
+ * @example
292
+ * ```typescript
293
+ * const result = await storage.deleteFile('documents/old-file.pdf');
294
+ * console.log(result.message);
295
+ * ```
296
+ */
297
+ async deleteFile(fileKey) {
298
+ const response = await this.client.delete(`/api/v1/storage/s3/projects/${this.projectSlug}/files/${fileKey}`);
299
+ // Clear quota cache after delete
300
+ this.quotaCache = undefined;
301
+ return response.data;
302
+ }
303
+ /**
304
+ * Get presigned URL for file access
305
+ *
306
+ * @param fileKey - Path to file in bucket
307
+ * @param expiresIn - URL validity in seconds (default: 3600 = 1 hour)
308
+ * @returns File URL and metadata
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * const urlData = await storage.getFileUrl('photo.jpg', 7200);
313
+ * console.log(urlData.file_url); // Use this for downloads
314
+ * ```
315
+ */
316
+ async getFileUrl(fileKey, expiresIn = 3600) {
317
+ const response = await this.client.get(`/api/v1/storage/s3/projects/${this.projectSlug}/files/${fileKey}/url`, { params: { expires_in: expiresIn } });
318
+ return response.data;
319
+ }
320
+ /**
321
+ * Generate presigned URL for file operations
322
+ *
323
+ * @param fileKey - Path to file in bucket
324
+ * @param options - Presigned URL options
325
+ * @returns Presigned URL string
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * // Download URL
330
+ * const downloadUrl = await storage.getPresignedUrl('file.pdf');
331
+ *
332
+ * // Upload URL
333
+ * const uploadUrl = await storage.getPresignedUrl('new-file.pdf', {
334
+ * operation: 'put_object',
335
+ * expiresIn: 1800
336
+ * });
337
+ * ```
338
+ */
339
+ async getPresignedUrl(fileKey, options) {
340
+ const response = await this.client.post(`/api/v1/storage/s3/projects/${this.projectSlug}/presigned-url`, {
341
+ file_key: fileKey,
342
+ expires_in: options?.expiresIn || 3600,
343
+ operation: options?.operation || 'get_object',
344
+ });
345
+ return response.data.url;
346
+ }
347
+ /**
348
+ * Get S3 storage information for the project
349
+ *
350
+ * @returns Storage information
351
+ *
352
+ * @example
353
+ * ```typescript
354
+ * const info = await storage.getStorageInfo();
355
+ * console.log(`Bucket: ${info.bucket_name}`);
356
+ * console.log(`Region: ${info.region}`);
357
+ * console.log(`Objects: ${info.total_objects}`);
358
+ * console.log(`Size: ${info.total_size_gb.toFixed(2)} GB`);
359
+ * ```
360
+ */
361
+ async getStorageInfo() {
362
+ const response = await this.client.get(`/api/v1/storage/s3/projects/${this.projectSlug}/info`);
363
+ return response.data;
364
+ }
365
+ /**
366
+ * Provision S3 storage for the project
367
+ *
368
+ * **IMPORTANT**: Save the credentials returned! They're only shown once.
369
+ *
370
+ * @param region - AWS region (default: 'us-east-1')
371
+ * @returns Provisioning result with credentials
372
+ *
373
+ * @example
374
+ * ```typescript
375
+ * const result = await storage.provisionStorage('us-west-2');
376
+ * console.log(`Bucket: ${result.bucket_name}`);
377
+ * console.log(`Access Key: ${result.credentials.access_key_id}`);
378
+ * // SAVE THESE CREDENTIALS SECURELY!
379
+ * ```
380
+ */
381
+ async provisionStorage(region = 'us-east-1') {
382
+ const response = await this.client.post(`/api/v1/storage/s3/projects/${this.projectSlug}/provision`, { region });
383
+ return response.data;
384
+ }
385
+ /**
386
+ * Get list of available S3 regions with pricing
387
+ *
388
+ * @returns Array of available regions
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * const regions = await storage.getAvailableRegions();
393
+ * for (const region of regions) {
394
+ * console.log(`${region.name}: $${region.storage_price_gb}/GB/month`);
395
+ * }
396
+ * ```
397
+ */
398
+ async getAvailableRegions() {
399
+ const response = await this.client.get('/api/v1/storage/s3/regions');
400
+ return response.data;
401
+ }
402
+ }
403
+ exports.WowSQLStorage = WowSQLStorage;
404
+ exports.default = WowSQLStorage;
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@wowsql/sdk",
3
+ "version": "3.4.0",
4
+ "description": "Official TypeScript/JavaScript SDK for WowSQL - MySQL Backend-as-a-Service with S3 Storage, type-safe queries and fluent API",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "LICENSE",
11
+ "CHANGELOG.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "jest",
16
+ "prepublishOnly": "npm run build",
17
+ "publish:patch": "npm version patch && npm publish",
18
+ "publish:minor": "npm version minor && npm publish",
19
+ "publish:major": "npm version major && npm publish"
20
+ },
21
+ "keywords": [
22
+ "wowsql",
23
+ "mysql",
24
+ "database",
25
+ "backend-as-a-service",
26
+ "baas",
27
+ "api",
28
+ "rest",
29
+ "typescript",
30
+ "javascript",
31
+ "sdk",
32
+ "client",
33
+ "query-builder",
34
+ "orm",
35
+ "sql",
36
+ "aws-rds",
37
+ "cloud-database",
38
+ "s3",
39
+ "storage",
40
+ "file-upload",
41
+ "object-storage"
42
+ ],
43
+ "author": "WowSQL Team <support@wowsql.com>",
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/wowsql/wowsql.git"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/wowsql/wowsql/issues"
51
+ },
52
+ "homepage": "https://wowsql.com",
53
+ "engines": {
54
+ "node": ">=14.0.0"
55
+ },
56
+ "dependencies": {
57
+ "axios": "^1.6.0"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "^20.0.0",
61
+ "typescript": "^5.0.0",
62
+ "jest": "^29.0.0",
63
+ "@types/jest": "^29.0.0"
64
+ }
65
+ }