voltjs-framework 1.0.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1265 -0
  3. package/bin/volt.js +139 -0
  4. package/package.json +56 -0
  5. package/src/api/graphql.js +399 -0
  6. package/src/api/rest.js +204 -0
  7. package/src/api/websocket.js +285 -0
  8. package/src/cli/build.js +111 -0
  9. package/src/cli/create.js +371 -0
  10. package/src/cli/db.js +106 -0
  11. package/src/cli/dev.js +114 -0
  12. package/src/cli/generate.js +278 -0
  13. package/src/cli/lint.js +172 -0
  14. package/src/cli/routes.js +118 -0
  15. package/src/cli/start.js +42 -0
  16. package/src/cli/test.js +138 -0
  17. package/src/core/app.js +701 -0
  18. package/src/core/config.js +232 -0
  19. package/src/core/middleware.js +133 -0
  20. package/src/core/plugins.js +88 -0
  21. package/src/core/react-renderer.js +244 -0
  22. package/src/core/renderer.js +337 -0
  23. package/src/core/router.js +183 -0
  24. package/src/database/index.js +461 -0
  25. package/src/database/migration.js +192 -0
  26. package/src/database/model.js +285 -0
  27. package/src/database/query.js +394 -0
  28. package/src/database/seeder.js +89 -0
  29. package/src/index.js +156 -0
  30. package/src/security/auth.js +425 -0
  31. package/src/security/cors.js +80 -0
  32. package/src/security/csrf.js +125 -0
  33. package/src/security/encryption.js +110 -0
  34. package/src/security/helmet.js +103 -0
  35. package/src/security/index.js +75 -0
  36. package/src/security/rateLimit.js +119 -0
  37. package/src/security/sanitizer.js +113 -0
  38. package/src/security/xss.js +110 -0
  39. package/src/ui/component.js +224 -0
  40. package/src/ui/reactive.js +503 -0
  41. package/src/ui/template.js +448 -0
  42. package/src/utils/cache.js +216 -0
  43. package/src/utils/collection.js +772 -0
  44. package/src/utils/cron.js +213 -0
  45. package/src/utils/date.js +223 -0
  46. package/src/utils/events.js +181 -0
  47. package/src/utils/excel.js +482 -0
  48. package/src/utils/form.js +547 -0
  49. package/src/utils/hash.js +121 -0
  50. package/src/utils/http.js +461 -0
  51. package/src/utils/logger.js +186 -0
  52. package/src/utils/mail.js +347 -0
  53. package/src/utils/paginator.js +179 -0
  54. package/src/utils/pdf.js +417 -0
  55. package/src/utils/queue.js +199 -0
  56. package/src/utils/schema.js +985 -0
  57. package/src/utils/sms.js +243 -0
  58. package/src/utils/storage.js +348 -0
  59. package/src/utils/string.js +236 -0
  60. package/src/utils/validation.js +318 -0
@@ -0,0 +1,482 @@
1
+ /**
2
+ * VoltJS Excel Module
3
+ *
4
+ * Import and export Excel files (XLSX, CSV) with zero external dependencies.
5
+ * Uses native Node.js buffers to read/write Excel format.
6
+ *
7
+ * @example
8
+ * const { Excel } = require('voltjs');
9
+ *
10
+ * // Export to CSV
11
+ * const csv = Excel.toCSV([
12
+ * { name: 'John', age: 30, email: 'john@test.com' },
13
+ * { name: 'Jane', age: 25, email: 'jane@test.com' },
14
+ * ]);
15
+ *
16
+ * // Import from CSV
17
+ * const data = Excel.fromCSV(csvString);
18
+ *
19
+ * // Generate XLSX
20
+ * const buffer = Excel.createXLSX(data, { sheetName: 'Users' });
21
+ * fs.writeFileSync('users.xlsx', buffer);
22
+ *
23
+ * // Parse XLSX
24
+ * const rows = Excel.parseXLSX(buffer);
25
+ */
26
+
27
+ 'use strict';
28
+
29
+ const fs = require('fs');
30
+ const path = require('path');
31
+ const zlib = require('zlib');
32
+
33
+ class Excel {
34
+ // ===== CSV OPERATIONS =====
35
+
36
+ /** Convert array of objects to CSV string */
37
+ static toCSV(data, options = {}) {
38
+ if (!Array.isArray(data) || data.length === 0) return '';
39
+
40
+ const delimiter = options.delimiter || ',';
41
+ const lineBreak = options.lineBreak || '\n';
42
+ const headers = options.headers || Object.keys(data[0]);
43
+ const includeHeaders = options.includeHeaders !== false;
44
+
45
+ const escapeValue = (val) => {
46
+ if (val === null || val === undefined) return '';
47
+ const str = String(val);
48
+ if (str.includes(delimiter) || str.includes('"') || str.includes('\n') || str.includes('\r')) {
49
+ return `"${str.replace(/"/g, '""')}"`;
50
+ }
51
+ return str;
52
+ };
53
+
54
+ const lines = [];
55
+ if (includeHeaders) {
56
+ lines.push(headers.map(escapeValue).join(delimiter));
57
+ }
58
+
59
+ for (const row of data) {
60
+ lines.push(headers.map(h => escapeValue(row[h])).join(delimiter));
61
+ }
62
+
63
+ return lines.join(lineBreak);
64
+ }
65
+
66
+ /** Parse CSV string to array of objects */
67
+ static fromCSV(csvString, options = {}) {
68
+ const delimiter = options.delimiter || ',';
69
+ const hasHeaders = options.hasHeaders !== false;
70
+
71
+ const lines = Excel._parseCSVLines(csvString, delimiter);
72
+ if (lines.length === 0) return [];
73
+
74
+ if (hasHeaders) {
75
+ const headers = lines[0];
76
+ return lines.slice(1).map(row => {
77
+ const obj = {};
78
+ headers.forEach((h, i) => {
79
+ obj[h] = row[i] !== undefined ? row[i] : '';
80
+ });
81
+ return obj;
82
+ });
83
+ }
84
+
85
+ return lines;
86
+ }
87
+
88
+ /** Parse CSV with proper quote handling */
89
+ static _parseCSVLines(text, delimiter = ',') {
90
+ const lines = [];
91
+ let current = [];
92
+ let field = '';
93
+ let inQuotes = false;
94
+
95
+ for (let i = 0; i < text.length; i++) {
96
+ const ch = text[i];
97
+ const next = text[i + 1];
98
+
99
+ if (inQuotes) {
100
+ if (ch === '"' && next === '"') {
101
+ field += '"';
102
+ i++;
103
+ } else if (ch === '"') {
104
+ inQuotes = false;
105
+ } else {
106
+ field += ch;
107
+ }
108
+ } else {
109
+ if (ch === '"') {
110
+ inQuotes = true;
111
+ } else if (ch === delimiter) {
112
+ current.push(field.trim());
113
+ field = '';
114
+ } else if (ch === '\n' || (ch === '\r' && next === '\n')) {
115
+ current.push(field.trim());
116
+ if (current.some(f => f !== '')) lines.push(current);
117
+ current = [];
118
+ field = '';
119
+ if (ch === '\r') i++;
120
+ } else {
121
+ field += ch;
122
+ }
123
+ }
124
+ }
125
+
126
+ // Last field
127
+ current.push(field.trim());
128
+ if (current.some(f => f !== '')) lines.push(current);
129
+
130
+ return lines;
131
+ }
132
+
133
+ /** Read CSV file */
134
+ static readCSV(filePath, options = {}) {
135
+ const content = fs.readFileSync(filePath, options.encoding || 'utf-8');
136
+ return Excel.fromCSV(content, options);
137
+ }
138
+
139
+ /** Write CSV file */
140
+ static writeCSV(filePath, data, options = {}) {
141
+ const csv = Excel.toCSV(data, options);
142
+ fs.writeFileSync(filePath, csv, options.encoding || 'utf-8');
143
+ return filePath;
144
+ }
145
+
146
+ // ===== XLSX OPERATIONS =====
147
+
148
+ /** Create a simple XLSX file (ZIP-based XML spreadsheet) */
149
+ static createXLSX(data, options = {}) {
150
+ if (!Array.isArray(data) || data.length === 0) {
151
+ throw new Error('Data must be a non-empty array of objects');
152
+ }
153
+
154
+ const sheetName = options.sheetName || 'Sheet1';
155
+ const headers = options.headers || Object.keys(data[0]);
156
+
157
+ // Build the sheet XML
158
+ let sheetData = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
159
+ sheetData += '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">\n';
160
+ sheetData += '<sheetData>\n';
161
+
162
+ // Header row
163
+ sheetData += '<row r="1">\n';
164
+ headers.forEach((h, i) => {
165
+ const col = Excel._columnLetter(i);
166
+ sheetData += `<c r="${col}1" t="inlineStr"><is><t>${Excel._escapeXML(String(h))}</t></is></c>\n`;
167
+ });
168
+ sheetData += '</row>\n';
169
+
170
+ // Data rows
171
+ data.forEach((row, rowIndex) => {
172
+ const r = rowIndex + 2;
173
+ sheetData += `<row r="${r}">\n`;
174
+ headers.forEach((h, colIndex) => {
175
+ const col = Excel._columnLetter(colIndex);
176
+ const val = row[h];
177
+ if (val === null || val === undefined) {
178
+ sheetData += `<c r="${col}${r}" t="inlineStr"><is><t></t></is></c>\n`;
179
+ } else if (typeof val === 'number') {
180
+ sheetData += `<c r="${col}${r}"><v>${val}</v></c>\n`;
181
+ } else {
182
+ sheetData += `<c r="${col}${r}" t="inlineStr"><is><t>${Excel._escapeXML(String(val))}</t></is></c>\n`;
183
+ }
184
+ });
185
+ sheetData += '</row>\n';
186
+ });
187
+
188
+ sheetData += '</sheetData>\n';
189
+ sheetData += '</worksheet>';
190
+
191
+ // Build XLSX (ZIP format with required XML files)
192
+ const files = {
193
+ '[Content_Types].xml': Excel._contentTypes(),
194
+ '_rels/.rels': Excel._rootRels(),
195
+ 'xl/_rels/workbook.xml.rels': Excel._workbookRels(),
196
+ 'xl/workbook.xml': Excel._workbook(sheetName),
197
+ 'xl/worksheets/sheet1.xml': sheetData,
198
+ };
199
+
200
+ return Excel._createZip(files);
201
+ }
202
+
203
+ /** Parse XLSX file to array of objects */
204
+ static parseXLSX(bufferOrPath) {
205
+ let buffer;
206
+ if (typeof bufferOrPath === 'string') {
207
+ buffer = fs.readFileSync(bufferOrPath);
208
+ } else {
209
+ buffer = bufferOrPath;
210
+ }
211
+
212
+ // Extract ZIP entries
213
+ const entries = Excel._extractZip(buffer);
214
+
215
+ // Find the sheet XML
216
+ const sheetEntry = entries['xl/worksheets/sheet1.xml'] || entries['xl/worksheets/Sheet1.xml'];
217
+ if (!sheetEntry) {
218
+ throw new Error('No worksheet found in XLSX file');
219
+ }
220
+
221
+ const sheetXML = sheetEntry.toString('utf-8');
222
+
223
+ // Check for shared strings
224
+ let sharedStrings = [];
225
+ const sharedStringsEntry = entries['xl/sharedStrings.xml'];
226
+ if (sharedStringsEntry) {
227
+ const ssXML = sharedStringsEntry.toString('utf-8');
228
+ const ssMatches = ssXML.matchAll(/<t[^>]*>([^<]*)<\/t>/g);
229
+ for (const m of ssMatches) {
230
+ sharedStrings.push(m[1]);
231
+ }
232
+ }
233
+
234
+ // Parse rows
235
+ const rows = [];
236
+ const rowMatches = sheetXML.matchAll(/<row[^>]*>([\s\S]*?)<\/row>/g);
237
+
238
+ for (const rowMatch of rowMatches) {
239
+ const row = [];
240
+ const cellMatches = rowMatch[1].matchAll(/<c[^>]*(?:t="([^"]*)")?[^>]*>(?:<(?:is|v)>(?:<t>)?([^<]*)(?:<\/t>)?<\/(?:is|v)>)?<\/c>/g);
241
+
242
+ for (const cellMatch of cellMatches) {
243
+ const type = cellMatch[1];
244
+ let value = cellMatch[2] || '';
245
+
246
+ if (type === 's' && sharedStrings.length > 0) {
247
+ value = sharedStrings[parseInt(value)] || value;
248
+ }
249
+
250
+ value = Excel._unescapeXML(value);
251
+ row.push(value);
252
+ }
253
+ rows.push(row);
254
+ }
255
+
256
+ if (rows.length === 0) return [];
257
+
258
+ // First row as headers
259
+ const headers = rows[0];
260
+ return rows.slice(1).map(row => {
261
+ const obj = {};
262
+ headers.forEach((h, i) => {
263
+ obj[h] = row[i] !== undefined ? row[i] : '';
264
+ });
265
+ return obj;
266
+ });
267
+ }
268
+
269
+ /** Write XLSX file */
270
+ static writeXLSX(filePath, data, options = {}) {
271
+ const buffer = Excel.createXLSX(data, options);
272
+ fs.writeFileSync(filePath, buffer);
273
+ return filePath;
274
+ }
275
+
276
+ // ===== TSV OPERATIONS =====
277
+
278
+ /** Convert to TSV (Tab-Separated Values) */
279
+ static toTSV(data, options = {}) {
280
+ return Excel.toCSV(data, { ...options, delimiter: '\t' });
281
+ }
282
+
283
+ /** Parse TSV */
284
+ static fromTSV(tsvString, options = {}) {
285
+ return Excel.fromCSV(tsvString, { ...options, delimiter: '\t' });
286
+ }
287
+
288
+ // ===== JSON OPERATIONS =====
289
+
290
+ /** Convert to formatted JSON */
291
+ static toJSON(data, pretty = true) {
292
+ return JSON.stringify(data, null, pretty ? 2 : 0);
293
+ }
294
+
295
+ /** Convert to JSON file */
296
+ static writeJSON(filePath, data, pretty = true) {
297
+ fs.writeFileSync(filePath, Excel.toJSON(data, pretty));
298
+ return filePath;
299
+ }
300
+
301
+ /** Read JSON file */
302
+ static readJSON(filePath) {
303
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
304
+ }
305
+
306
+ // ===== HELPERS =====
307
+
308
+ static _columnLetter(index) {
309
+ let letter = '';
310
+ while (index >= 0) {
311
+ letter = String.fromCharCode(65 + (index % 26)) + letter;
312
+ index = Math.floor(index / 26) - 1;
313
+ }
314
+ return letter;
315
+ }
316
+
317
+ static _escapeXML(str) {
318
+ return str
319
+ .replace(/&/g, '&amp;')
320
+ .replace(/</g, '&lt;')
321
+ .replace(/>/g, '&gt;')
322
+ .replace(/"/g, '&quot;')
323
+ .replace(/'/g, '&apos;');
324
+ }
325
+
326
+ static _unescapeXML(str) {
327
+ return str
328
+ .replace(/&amp;/g, '&')
329
+ .replace(/&lt;/g, '<')
330
+ .replace(/&gt;/g, '>')
331
+ .replace(/&quot;/g, '"')
332
+ .replace(/&apos;/g, "'");
333
+ }
334
+
335
+ static _contentTypes() {
336
+ return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
337
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\n' +
338
+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\n' +
339
+ '<Default Extension="xml" ContentType="application/xml"/>\n' +
340
+ '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>\n' +
341
+ '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>\n' +
342
+ '</Types>';
343
+ }
344
+
345
+ static _rootRels() {
346
+ return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
347
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
348
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>\n' +
349
+ '</Relationships>';
350
+ }
351
+
352
+ static _workbookRels() {
353
+ return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
354
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
355
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>\n' +
356
+ '</Relationships>';
357
+ }
358
+
359
+ static _workbook(sheetName) {
360
+ return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
361
+ '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">\n' +
362
+ '<sheets>\n' +
363
+ `<sheet name="${sheetName}" sheetId="1" r:id="rId1"/>\n` +
364
+ '</sheets>\n' +
365
+ '</workbook>';
366
+ }
367
+
368
+ /** Create a simple ZIP file from an object of {path: content} */
369
+ static _createZip(files) {
370
+ // Simple ZIP file creator (PKZIP format)
371
+ const entries = [];
372
+ const centralDir = [];
373
+ let offset = 0;
374
+
375
+ for (const [filepath, content] of Object.entries(files)) {
376
+ const data = Buffer.from(content, 'utf-8');
377
+ const compressed = zlib.deflateRawSync(data);
378
+
379
+ const filenameBuffer = Buffer.from(filepath, 'utf-8');
380
+
381
+ // Local file header
382
+ const localHeader = Buffer.alloc(30);
383
+ localHeader.writeUInt32LE(0x04034b50, 0); // Local file header signature
384
+ localHeader.writeUInt16LE(20, 4); // Version needed
385
+ localHeader.writeUInt16LE(0, 6); // General purpose flag
386
+ localHeader.writeUInt16LE(8, 8); // Compression method (deflate)
387
+ localHeader.writeUInt16LE(0, 10); // Last mod time
388
+ localHeader.writeUInt16LE(0, 12); // Last mod date
389
+ localHeader.writeUInt32LE(Excel._crc32(data), 14); // CRC-32
390
+ localHeader.writeUInt32LE(compressed.length, 18); // Compressed size
391
+ localHeader.writeUInt32LE(data.length, 22); // Uncompressed size
392
+ localHeader.writeUInt16LE(filenameBuffer.length, 26); // Filename length
393
+
394
+ const entry = Buffer.concat([localHeader, filenameBuffer, compressed]);
395
+ entries.push(entry);
396
+
397
+ // Central directory header
398
+ const cdHeader = Buffer.alloc(46);
399
+ cdHeader.writeUInt32LE(0x02014b50, 0); // Central directory signature
400
+ cdHeader.writeUInt16LE(20, 4); // Version made by
401
+ cdHeader.writeUInt16LE(20, 6); // Version needed
402
+ cdHeader.writeUInt16LE(0, 8); // General purpose flag
403
+ cdHeader.writeUInt16LE(8, 10); // Compression method
404
+ cdHeader.writeUInt16LE(0, 12); // Last mod time
405
+ cdHeader.writeUInt16LE(0, 14); // Last mod date
406
+ cdHeader.writeUInt32LE(Excel._crc32(data), 16);
407
+ cdHeader.writeUInt32LE(compressed.length, 20);
408
+ cdHeader.writeUInt32LE(data.length, 24);
409
+ cdHeader.writeUInt16LE(filenameBuffer.length, 28);
410
+ cdHeader.writeUInt32LE(offset, 42); // Relative offset
411
+
412
+ centralDir.push(Buffer.concat([cdHeader, filenameBuffer]));
413
+ offset += entry.length;
414
+ }
415
+
416
+ const centralDirBuffer = Buffer.concat(centralDir);
417
+ const centralDirOffset = offset;
418
+
419
+ // End of central directory
420
+ const eocd = Buffer.alloc(22);
421
+ eocd.writeUInt32LE(0x06054b50, 0);
422
+ eocd.writeUInt16LE(Object.keys(files).length, 8); // Total entries
423
+ eocd.writeUInt16LE(Object.keys(files).length, 10);
424
+ eocd.writeUInt32LE(centralDirBuffer.length, 12);
425
+ eocd.writeUInt32LE(centralDirOffset, 16);
426
+
427
+ return Buffer.concat([...entries, centralDirBuffer, eocd]);
428
+ }
429
+
430
+ /** Extract files from a ZIP buffer */
431
+ static _extractZip(buffer) {
432
+ const entries = {};
433
+ let offset = 0;
434
+
435
+ while (offset < buffer.length - 4) {
436
+ const signature = buffer.readUInt32LE(offset);
437
+
438
+ if (signature === 0x04034b50) { // Local file header
439
+ const compressionMethod = buffer.readUInt16LE(offset + 8);
440
+ const compressedSize = buffer.readUInt32LE(offset + 18);
441
+ const uncompressedSize = buffer.readUInt32LE(offset + 22);
442
+ const filenameLen = buffer.readUInt16LE(offset + 26);
443
+ const extraLen = buffer.readUInt16LE(offset + 28);
444
+
445
+ const filename = buffer.slice(offset + 30, offset + 30 + filenameLen).toString('utf-8');
446
+ const dataStart = offset + 30 + filenameLen + extraLen;
447
+ const data = buffer.slice(dataStart, dataStart + compressedSize);
448
+
449
+ try {
450
+ if (compressionMethod === 8) {
451
+ entries[filename] = zlib.inflateRawSync(data);
452
+ } else {
453
+ entries[filename] = data;
454
+ }
455
+ } catch {
456
+ entries[filename] = data;
457
+ }
458
+
459
+ offset = dataStart + compressedSize;
460
+ } else {
461
+ break;
462
+ }
463
+ }
464
+
465
+ return entries;
466
+ }
467
+
468
+ /** CRC-32 calculation */
469
+ static _crc32(data) {
470
+ let crc = 0xFFFFFFFF;
471
+ for (let i = 0; i < data.length; i++) {
472
+ crc ^= data[i];
473
+ for (let j = 0; j < 8; j++) {
474
+ if (crc & 1) crc = (crc >>> 1) ^ 0xEDB88320;
475
+ else crc >>>= 1;
476
+ }
477
+ }
478
+ return (crc ^ 0xFFFFFFFF) >>> 0;
479
+ }
480
+ }
481
+
482
+ module.exports = { Excel };