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
package/bin/volt.js ADDED
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * VoltJS CLI - The command center for VoltJS framework
5
+ * Usage:
6
+ * volt create <project-name> Create a new VoltJS project
7
+ * volt dev Start development server with hot reload
8
+ * volt build Build for production
9
+ * volt generate <type> <name> Generate components, pages, APIs
10
+ * volt db:migrate Run database migrations
11
+ * volt db:seed Seed the database
12
+ * volt lint Lint your project
13
+ * volt test Run tests
14
+ */
15
+
16
+ 'use strict';
17
+
18
+ const path = require('path');
19
+ const fs = require('fs');
20
+
21
+ // CLI colors without dependencies
22
+ const c = {
23
+ reset: '\x1b[0m',
24
+ bold: '\x1b[1m',
25
+ dim: '\x1b[2m',
26
+ red: '\x1b[31m',
27
+ green: '\x1b[32m',
28
+ yellow: '\x1b[33m',
29
+ blue: '\x1b[34m',
30
+ magenta: '\x1b[35m',
31
+ cyan: '\x1b[36m',
32
+ white: '\x1b[37m',
33
+ bgBlue: '\x1b[44m',
34
+ bgMagenta: '\x1b[45m',
35
+ };
36
+
37
+ function logo() {
38
+ console.log(`
39
+ ${c.cyan}${c.bold}
40
+ ██╗ ██╗ ██████╗ ██╗ ████████╗ ██╗███████╗
41
+ ██║ ██║██╔═══██╗██║ ╚══██╔══╝ ██║██╔════╝
42
+ ██║ ██║██║ ██║██║ ██║ ██║███████╗
43
+ ╚██╗ ██╔╝██║ ██║██║ ██║ ██ ██║╚════██║
44
+ ╚████╔╝ ╚██████╔╝███████╗██║ ╚█████╔╝███████║
45
+ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚════╝ ╚══════╝
46
+ ${c.reset}
47
+ ${c.dim}Lightning-fast, batteries-included framework${c.reset}
48
+ ${c.dim}Version 1.0.0${c.reset}
49
+ `);
50
+ }
51
+
52
+ const args = process.argv.slice(2);
53
+ const command = args[0];
54
+
55
+ if (!command || command === '--help' || command === '-h') {
56
+ logo();
57
+ console.log(`${c.bold}Usage:${c.reset}
58
+ ${c.cyan}volt create${c.reset} <project-name> Create a new VoltJS project
59
+ ${c.cyan}volt dev${c.reset} Start dev server (port 3000)
60
+ ${c.cyan}volt build${c.reset} Build for production
61
+ ${c.cyan}volt start${c.reset} Start production server
62
+ ${c.cyan}volt generate${c.reset} <type> <name> Generate code (page, api, component, model)
63
+ ${c.cyan}volt db:migrate${c.reset} Run database migrations
64
+ ${c.cyan}volt db:seed${c.reset} Run database seeders
65
+ ${c.cyan}volt db:rollback${c.reset} Rollback last migration
66
+ ${c.cyan}volt lint${c.reset} Lint project files
67
+ ${c.cyan}volt test${c.reset} Run tests
68
+ ${c.cyan}volt routes${c.reset} List all registered routes
69
+ ${c.cyan}volt --version${c.reset} Show version
70
+
71
+ ${c.bold}Examples:${c.reset}
72
+ ${c.dim}$ volt create my-app${c.reset}
73
+ ${c.dim}$ volt generate page dashboard${c.reset}
74
+ ${c.dim}$ volt generate api users${c.reset}
75
+ ${c.dim}$ volt generate component Header${c.reset}
76
+ `);
77
+ process.exit(0);
78
+ }
79
+
80
+ if (command === '--version' || command === '-v') {
81
+ const pkg = require(path.join(__dirname, '..', 'package.json'));
82
+ console.log(`VoltJS v${pkg.version}`);
83
+ process.exit(0);
84
+ }
85
+
86
+ // Route to appropriate CLI handler
87
+ try {
88
+ switch (command) {
89
+ case 'create':
90
+ require('../src/cli/create')(args.slice(1));
91
+ break;
92
+ case 'dev':
93
+ require('../src/cli/dev')(args.slice(1));
94
+ break;
95
+ case 'build':
96
+ require('../src/cli/build')(args.slice(1));
97
+ break;
98
+ case 'start':
99
+ require('../src/cli/start')(args.slice(1));
100
+ break;
101
+ case 'generate':
102
+ case 'g':
103
+ require('../src/cli/generate')(args.slice(1));
104
+ break;
105
+ case 'db:migrate':
106
+ require('../src/cli/db')([ 'migrate', ...args.slice(1) ]);
107
+ break;
108
+ case 'db:seed':
109
+ require('../src/cli/db')([ 'seed', ...args.slice(1) ]);
110
+ break;
111
+ case 'db:rollback':
112
+ require('../src/cli/db')([ 'rollback', ...args.slice(1) ]);
113
+ break;
114
+ case 'routes':
115
+ require('../src/cli/routes')(args.slice(1));
116
+ break;
117
+ case 'lint':
118
+ require('../src/cli/lint')(args.slice(1));
119
+ break;
120
+ case 'test':
121
+ require('../src/cli/test')(args.slice(1));
122
+ break;
123
+ default:
124
+ console.error(`${c.red}Unknown command: ${command}${c.reset}`);
125
+ console.log(`Run ${c.cyan}volt --help${c.reset} for usage information.`);
126
+ process.exit(1);
127
+ }
128
+ } catch (err) {
129
+ if (err.code === 'MODULE_NOT_FOUND') {
130
+ console.error(`${c.red}Error: Command module not found. Please reinstall VoltJS.${c.reset}`);
131
+ console.error(`${c.dim}${err.message}${c.reset}`);
132
+ } else {
133
+ console.error(`${c.red}Error: ${err.message}${c.reset}`);
134
+ if (process.env.VOLT_DEBUG) {
135
+ console.error(err.stack);
136
+ }
137
+ }
138
+ process.exit(1);
139
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "voltjs-framework",
3
+ "version": "1.0.0",
4
+ "description": "VoltJS - Lightning-fast, batteries-included, security-first JavaScript framework. Zero boilerplate, maximum power.",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "volt": "./bin/volt.js"
8
+ },
9
+ "scripts": {
10
+ "test": "node tests/run.js",
11
+ "dev": "node bin/volt.js dev",
12
+ "build": "node bin/volt.js build",
13
+ "lint": "node bin/volt.js lint"
14
+ },
15
+ "keywords": [
16
+ "framework",
17
+ "voltjs",
18
+ "volt-framework",
19
+ "fullstack",
20
+ "ssr",
21
+ "react-ssr",
22
+ "security",
23
+ "batteries-included",
24
+ "minimal",
25
+ "reactive",
26
+ "web-framework",
27
+ "nodejs",
28
+ "rest-api",
29
+ "orm",
30
+ "authentication"
31
+ ],
32
+ "author": "cool_phosphorus",
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "files": [
38
+ "src/",
39
+ "bin/",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "dependencies": {
44
+ "react": "^19.2.4",
45
+ "react-dom": "^19.2.4",
46
+ "ws": "^8.16.0"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": ""
51
+ },
52
+ "homepage": "https://voltjs.dev",
53
+ "bugs": {
54
+ "url": ""
55
+ }
56
+ }
@@ -0,0 +1,399 @@
1
+ /**
2
+ * VoltJS GraphQL Handler
3
+ *
4
+ * Lightweight GraphQL execution engine — no external dependencies.
5
+ * Supports queries, mutations, variables, and introspection basics.
6
+ *
7
+ * @example
8
+ * const { GraphQLHandler } = require('voltjs');
9
+ *
10
+ * const gql = new GraphQLHandler();
11
+ *
12
+ * gql.type('User', {
13
+ * id: 'ID!',
14
+ * name: 'String!',
15
+ * email: 'String',
16
+ * });
17
+ *
18
+ * gql.query('user', { id: 'ID!' }, async ({ id }) => {
19
+ * return await User.find(id);
20
+ * });
21
+ *
22
+ * gql.query('users', {}, async () => {
23
+ * return await User.all();
24
+ * });
25
+ *
26
+ * gql.mutation('createUser', { name: 'String!', email: 'String!' }, async (args) => {
27
+ * return await User.create(args);
28
+ * });
29
+ *
30
+ * // Mount as middleware
31
+ * app.post('/graphql', gql.middleware());
32
+ */
33
+
34
+ 'use strict';
35
+
36
+ class GraphQLHandler {
37
+ constructor(options = {}) {
38
+ this.types = new Map();
39
+ this.queries = new Map();
40
+ this.mutations = new Map();
41
+ this.subscriptions = new Map();
42
+ this.middleware_list = options.middleware || [];
43
+ this.maxDepth = options.maxDepth || 10;
44
+ this.introspection = options.introspection !== false;
45
+ }
46
+
47
+ /** Define a type */
48
+ type(name, fields) {
49
+ this.types.set(name, fields);
50
+ return this;
51
+ }
52
+
53
+ /** Define a query resolver */
54
+ query(name, args, resolver) {
55
+ if (typeof args === 'function') {
56
+ resolver = args;
57
+ args = {};
58
+ }
59
+ this.queries.set(name, { args, resolver });
60
+ return this;
61
+ }
62
+
63
+ /** Define a mutation resolver */
64
+ mutation(name, args, resolver) {
65
+ if (typeof args === 'function') {
66
+ resolver = args;
67
+ args = {};
68
+ }
69
+ this.mutations.set(name, { args, resolver });
70
+ return this;
71
+ }
72
+
73
+ /** Define a subscription resolver */
74
+ subscription(name, args, resolver) {
75
+ if (typeof args === 'function') {
76
+ resolver = args;
77
+ args = {};
78
+ }
79
+ this.subscriptions.set(name, { args, resolver });
80
+ return this;
81
+ }
82
+
83
+ /** Execute a GraphQL request */
84
+ async execute(request, context = {}) {
85
+ try {
86
+ const { query, variables = {}, operationName } = typeof request === 'string'
87
+ ? { query: request }
88
+ : request;
89
+
90
+ if (!query) {
91
+ return { errors: [{ message: 'Query is required' }] };
92
+ }
93
+
94
+ const parsed = this._parse(query);
95
+
96
+ if (!parsed || parsed.length === 0) {
97
+ return { errors: [{ message: 'Failed to parse query' }] };
98
+ }
99
+
100
+ // Find the operation to execute
101
+ let operation = parsed[0];
102
+ if (operationName) {
103
+ operation = parsed.find(op => op.name === operationName) || parsed[0];
104
+ }
105
+
106
+ const resolvers = operation.type === 'mutation' ? this.mutations : this.queries;
107
+ const data = {};
108
+ const errors = [];
109
+
110
+ for (const field of operation.fields) {
111
+ const resolver = resolvers.get(field.name);
112
+
113
+ // Introspection: __schema, __type
114
+ if (field.name === '__schema' && this.introspection) {
115
+ data.__schema = this._introspectSchema();
116
+ continue;
117
+ }
118
+ if (field.name === '__type' && this.introspection) {
119
+ const typeName = this._resolveArgs(field.args, variables).name;
120
+ data.__type = this._introspectType(typeName);
121
+ continue;
122
+ }
123
+
124
+ if (!resolver) {
125
+ errors.push({ message: `Unknown field: ${field.name}`, path: [field.name] });
126
+ continue;
127
+ }
128
+
129
+ try {
130
+ const args = this._resolveArgs(field.args, variables);
131
+ const result = await resolver.resolver(args, context);
132
+ data[field.alias || field.name] = this._filterFields(result, field.fields);
133
+ } catch (err) {
134
+ errors.push({
135
+ message: err.message,
136
+ path: [field.name],
137
+ extensions: { code: err.code || 'INTERNAL_ERROR' },
138
+ });
139
+ }
140
+ }
141
+
142
+ const response = { data };
143
+ if (errors.length > 0) response.errors = errors;
144
+ return response;
145
+
146
+ } catch (error) {
147
+ return {
148
+ errors: [{ message: error.message || 'Internal Error' }],
149
+ };
150
+ }
151
+ }
152
+
153
+ /** Express/Volt middleware */
154
+ middleware() {
155
+ return async (req, res) => {
156
+ // Only handle POST to the GraphQL endpoint
157
+ if (req.method === 'GET' && req.query?.query) {
158
+ const result = await this.execute({
159
+ query: req.query.query,
160
+ variables: req.query.variables ? JSON.parse(req.query.variables) : {},
161
+ operationName: req.query.operationName,
162
+ }, { req, res });
163
+ res.json(result);
164
+ return false;
165
+ }
166
+
167
+ if (req.method === 'POST') {
168
+ const result = await this.execute(req.body, { req, res });
169
+ const status = result.errors && !result.data ? 400 : 200;
170
+ res.json(result, status);
171
+ return false;
172
+ }
173
+ };
174
+ }
175
+
176
+ /** Generate schema SDL string */
177
+ toSDL() {
178
+ const lines = [];
179
+
180
+ for (const [name, fields] of this.types) {
181
+ lines.push(`type ${name} {`);
182
+ for (const [field, type] of Object.entries(fields)) {
183
+ lines.push(` ${field}: ${type}`);
184
+ }
185
+ lines.push('}', '');
186
+ }
187
+
188
+ if (this.queries.size > 0) {
189
+ lines.push('type Query {');
190
+ for (const [name, { args }] of this.queries) {
191
+ const argStr = Object.keys(args).length > 0
192
+ ? `(${Object.entries(args).map(([k, v]) => `${k}: ${v}`).join(', ')})`
193
+ : '';
194
+ lines.push(` ${name}${argStr}: JSON`);
195
+ }
196
+ lines.push('}', '');
197
+ }
198
+
199
+ if (this.mutations.size > 0) {
200
+ lines.push('type Mutation {');
201
+ for (const [name, { args }] of this.mutations) {
202
+ const argStr = Object.keys(args).length > 0
203
+ ? `(${Object.entries(args).map(([k, v]) => `${k}: ${v}`).join(', ')})`
204
+ : '';
205
+ lines.push(` ${name}${argStr}: JSON`);
206
+ }
207
+ lines.push('}', '');
208
+ }
209
+
210
+ return lines.join('\n');
211
+ }
212
+
213
+ // ===== PARSER =====
214
+
215
+ _parse(query) {
216
+ const operations = [];
217
+ const cleaned = query.replace(/#.*/g, '').trim();
218
+
219
+ // Match operation blocks: query/mutation Name { ... } or just { ... }
220
+ const opRegex = /(?:(query|mutation|subscription)\s*(\w*)\s*(?:\(([^)]*)\))?\s*)?{([\s\S]*?)}\s*$/gm;
221
+
222
+ // Simple top-level parse
223
+ let type = 'query';
224
+ let name = null;
225
+ let body = cleaned;
226
+
227
+ const opMatch = cleaned.match(/^(query|mutation|subscription)\s*(\w*)\s*(?:\(([^)]*)\))?\s*\{/);
228
+ if (opMatch) {
229
+ type = opMatch[1];
230
+ name = opMatch[2] || null;
231
+ body = cleaned.slice(opMatch[0].length - 1); // Keep opening {
232
+ }
233
+
234
+ // Remove outer braces
235
+ body = body.trim();
236
+ if (body.startsWith('{')) body = body.slice(1);
237
+ if (body.endsWith('}')) body = body.slice(0, -1);
238
+
239
+ const fields = this._parseFields(body);
240
+
241
+ operations.push({ type, name, fields });
242
+ return operations;
243
+ }
244
+
245
+ _parseFields(body) {
246
+ const fields = [];
247
+ const tokens = body.trim();
248
+ if (!tokens) return fields;
249
+
250
+ let i = 0;
251
+ while (i < tokens.length) {
252
+ // Skip whitespace
253
+ while (i < tokens.length && /\s/.test(tokens[i])) i++;
254
+ if (i >= tokens.length) break;
255
+
256
+ // Read field name (might have alias)
257
+ let fieldName = '';
258
+ let alias = null;
259
+ while (i < tokens.length && /[\w]/.test(tokens[i])) {
260
+ fieldName += tokens[i]; i++;
261
+ }
262
+ while (i < tokens.length && /\s/.test(tokens[i])) i++;
263
+
264
+ // Check for alias
265
+ if (tokens[i] === ':') {
266
+ alias = fieldName;
267
+ i++; // skip :
268
+ while (i < tokens.length && /\s/.test(tokens[i])) i++;
269
+ fieldName = '';
270
+ while (i < tokens.length && /[\w]/.test(tokens[i])) {
271
+ fieldName += tokens[i]; i++;
272
+ }
273
+ while (i < tokens.length && /\s/.test(tokens[i])) i++;
274
+ }
275
+
276
+ if (!fieldName) { i++; continue; }
277
+
278
+ // Parse args
279
+ let args = {};
280
+ if (tokens[i] === '(') {
281
+ const argEnd = this._findClosing(tokens, i, '(', ')');
282
+ const argStr = tokens.slice(i + 1, argEnd);
283
+ args = this._parseArgs(argStr);
284
+ i = argEnd + 1;
285
+ while (i < tokens.length && /\s/.test(tokens[i])) i++;
286
+ }
287
+
288
+ // Parse sub-fields
289
+ let subFields = null;
290
+ if (tokens[i] === '{') {
291
+ const blockEnd = this._findClosing(tokens, i, '{', '}');
292
+ const subBody = tokens.slice(i + 1, blockEnd);
293
+ subFields = this._parseFields(subBody);
294
+ i = blockEnd + 1;
295
+ }
296
+
297
+ fields.push({ name: fieldName, alias, args, fields: subFields });
298
+ }
299
+
300
+ return fields;
301
+ }
302
+
303
+ _parseArgs(argStr) {
304
+ const args = {};
305
+ const parts = argStr.split(',');
306
+ for (const part of parts) {
307
+ const colonIdx = part.indexOf(':');
308
+ if (colonIdx === -1) continue;
309
+ const key = part.slice(0, colonIdx).trim();
310
+ let value = part.slice(colonIdx + 1).trim();
311
+
312
+ // Parse value
313
+ if (value.startsWith('"') && value.endsWith('"')) {
314
+ value = value.slice(1, -1);
315
+ } else if (value.startsWith('$')) {
316
+ value = { $var: value.slice(1) };
317
+ } else if (value === 'true') {
318
+ value = true;
319
+ } else if (value === 'false') {
320
+ value = false;
321
+ } else if (value === 'null') {
322
+ value = null;
323
+ } else if (!isNaN(value)) {
324
+ value = Number(value);
325
+ }
326
+
327
+ args[key] = value;
328
+ }
329
+ return args;
330
+ }
331
+
332
+ _resolveArgs(args, variables) {
333
+ const resolved = {};
334
+ for (const [key, value] of Object.entries(args)) {
335
+ if (value && typeof value === 'object' && value.$var) {
336
+ resolved[key] = variables[value.$var];
337
+ } else {
338
+ resolved[key] = value;
339
+ }
340
+ }
341
+ return resolved;
342
+ }
343
+
344
+ _filterFields(data, fields) {
345
+ if (!fields || !data) return data;
346
+ if (Array.isArray(data)) {
347
+ return data.map(item => this._filterFields(item, fields));
348
+ }
349
+ if (typeof data !== 'object') return data;
350
+
351
+ const result = {};
352
+ for (const field of fields) {
353
+ const key = field.name;
354
+ if (key in data) {
355
+ result[field.alias || key] = field.fields
356
+ ? this._filterFields(data[key], field.fields)
357
+ : data[key];
358
+ }
359
+ }
360
+ return result;
361
+ }
362
+
363
+ _findClosing(str, start, open, close) {
364
+ let depth = 1;
365
+ let i = start + 1;
366
+ while (i < str.length && depth > 0) {
367
+ if (str[i] === open) depth++;
368
+ else if (str[i] === close) depth--;
369
+ if (depth > 0) i++;
370
+ }
371
+ return i;
372
+ }
373
+
374
+ // ===== INTROSPECTION =====
375
+
376
+ _introspectSchema() {
377
+ return {
378
+ queryType: { name: 'Query' },
379
+ mutationType: this.mutations.size > 0 ? { name: 'Mutation' } : null,
380
+ types: [...this.types.keys()].map(name => this._introspectType(name)),
381
+ };
382
+ }
383
+
384
+ _introspectType(name) {
385
+ const fields = this.types.get(name);
386
+ if (!fields) return null;
387
+ return {
388
+ name,
389
+ kind: 'OBJECT',
390
+ fields: Object.entries(fields).map(([fieldName, type]) => ({
391
+ name: fieldName,
392
+ type: { name: type.replace('!', ''), kind: 'SCALAR', ofType: null },
393
+ isDeprecated: false,
394
+ })),
395
+ };
396
+ }
397
+ }
398
+
399
+ module.exports = { GraphQLHandler };