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.
- package/LICENSE +21 -0
- package/README.md +1265 -0
- package/bin/volt.js +139 -0
- package/package.json +56 -0
- package/src/api/graphql.js +399 -0
- package/src/api/rest.js +204 -0
- package/src/api/websocket.js +285 -0
- package/src/cli/build.js +111 -0
- package/src/cli/create.js +371 -0
- package/src/cli/db.js +106 -0
- package/src/cli/dev.js +114 -0
- package/src/cli/generate.js +278 -0
- package/src/cli/lint.js +172 -0
- package/src/cli/routes.js +118 -0
- package/src/cli/start.js +42 -0
- package/src/cli/test.js +138 -0
- package/src/core/app.js +701 -0
- package/src/core/config.js +232 -0
- package/src/core/middleware.js +133 -0
- package/src/core/plugins.js +88 -0
- package/src/core/react-renderer.js +244 -0
- package/src/core/renderer.js +337 -0
- package/src/core/router.js +183 -0
- package/src/database/index.js +461 -0
- package/src/database/migration.js +192 -0
- package/src/database/model.js +285 -0
- package/src/database/query.js +394 -0
- package/src/database/seeder.js +89 -0
- package/src/index.js +156 -0
- package/src/security/auth.js +425 -0
- package/src/security/cors.js +80 -0
- package/src/security/csrf.js +125 -0
- package/src/security/encryption.js +110 -0
- package/src/security/helmet.js +103 -0
- package/src/security/index.js +75 -0
- package/src/security/rateLimit.js +119 -0
- package/src/security/sanitizer.js +113 -0
- package/src/security/xss.js +110 -0
- package/src/ui/component.js +224 -0
- package/src/ui/reactive.js +503 -0
- package/src/ui/template.js +448 -0
- package/src/utils/cache.js +216 -0
- package/src/utils/collection.js +772 -0
- package/src/utils/cron.js +213 -0
- package/src/utils/date.js +223 -0
- package/src/utils/events.js +181 -0
- package/src/utils/excel.js +482 -0
- package/src/utils/form.js +547 -0
- package/src/utils/hash.js +121 -0
- package/src/utils/http.js +461 -0
- package/src/utils/logger.js +186 -0
- package/src/utils/mail.js +347 -0
- package/src/utils/paginator.js +179 -0
- package/src/utils/pdf.js +417 -0
- package/src/utils/queue.js +199 -0
- package/src/utils/schema.js +985 -0
- package/src/utils/sms.js +243 -0
- package/src/utils/storage.js +348 -0
- package/src/utils/string.js +236 -0
- package/src/utils/validation.js +318 -0
package/README.md
ADDED
|
@@ -0,0 +1,1265 @@
|
|
|
1
|
+
# âš¡ VoltJS
|
|
2
|
+
|
|
3
|
+
**The batteries-included, security-first JavaScript framework.**
|
|
4
|
+
|
|
5
|
+
Zero boilerplate. Zero config. Everything you need — out of the box.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx voltjs create my-app
|
|
9
|
+
cd my-app
|
|
10
|
+
npm install
|
|
11
|
+
npm run dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Why VoltJS?
|
|
17
|
+
|
|
18
|
+
| Problem | VoltJS Solution |
|
|
19
|
+
|---------|----------------|
|
|
20
|
+
| Express needs 20+ packages to be production-ready | **Everything built-in** — security, ORM, mail, PDF, Excel, SMS, caching, queues |
|
|
21
|
+
| CSRF, XSS, CORS require manual setup | **All security ON by default** — zero-config protection |
|
|
22
|
+
| Next.js / Nuxt are heavy and opinionated | **Lightweight & flexible** — use what you need |
|
|
23
|
+
| Setting up auth takes hours | **Auth in 2 lines** — JWT, sessions, API keys, RBAC, 2FA |
|
|
24
|
+
| Need separate packages for email, PDF, Excel | **Built-in utilities** — no extra dependencies |
|
|
25
|
+
| File-based routing OR programmatic? | **Both** — choose your style |
|
|
26
|
+
|
|
27
|
+
### Single Import, Everything Available
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const {
|
|
31
|
+
Volt, Router, Auth, Mail, Excel, PDF, SMS,
|
|
32
|
+
Database, Model, Cache, Queue, Cron, Logger,
|
|
33
|
+
Validator, HttpClient, Storage, Hash, EventBus,
|
|
34
|
+
Component, Reactive, RestAPI, WebSocketServer, GraphQLHandler,
|
|
35
|
+
Form, Schema, _, Collection,
|
|
36
|
+
} = require('voltjs-framework');
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Table of Contents
|
|
42
|
+
|
|
43
|
+
- [Quick Start](#quick-start)
|
|
44
|
+
- [Routing](#routing)
|
|
45
|
+
- [Security](#security)
|
|
46
|
+
- [Database & ORM](#database--orm)
|
|
47
|
+
- [Authentication](#authentication)
|
|
48
|
+
- [Validation](#validation)
|
|
49
|
+
- [Email](#email)
|
|
50
|
+
- [SMS](#sms)
|
|
51
|
+
- [Excel Import/Export](#excel-importexport)
|
|
52
|
+
- [PDF Generation](#pdf-generation)
|
|
53
|
+
- [File Storage](#file-storage)
|
|
54
|
+
- [Caching](#caching)
|
|
55
|
+
- [Job Queues](#job-queues)
|
|
56
|
+
- [Scheduled Tasks (Cron)](#scheduled-tasks-cron)
|
|
57
|
+
- [WebSockets](#websockets)
|
|
58
|
+
- [GraphQL](#graphql)
|
|
59
|
+
- [REST API Builder](#rest-api-builder)
|
|
60
|
+
- [Reactive State](#reactive-state)
|
|
61
|
+
- [Components](#components)
|
|
62
|
+
- [Template Engine](#template-engine)
|
|
63
|
+
- [Form Handling](#form-handling)
|
|
64
|
+
- [Schema Validation (Zod)](#schema-validation-zod)
|
|
65
|
+
- [Collection Utilities (Lodash)](#collection-utilities-lodash)
|
|
66
|
+
- [classNames / clsx](#classnames--clsx)
|
|
67
|
+
- [Logging](#logging)
|
|
68
|
+
- [HTTP Client](#http-client)
|
|
69
|
+
- [CLI Commands](#cli-commands)
|
|
70
|
+
- [Configuration](#configuration)
|
|
71
|
+
- [Project Structure](#project-structure)
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
### Create a New Project
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx voltjs create my-app
|
|
81
|
+
cd my-app
|
|
82
|
+
npm install
|
|
83
|
+
npm run dev
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Minimal App (2 files)
|
|
87
|
+
|
|
88
|
+
**app.js:**
|
|
89
|
+
```js
|
|
90
|
+
const { Volt } = require('voltjs-framework');
|
|
91
|
+
const app = new Volt();
|
|
92
|
+
|
|
93
|
+
app.get('/', (req, res) => {
|
|
94
|
+
res.json({ message: 'Hello VoltJS! âš¡' });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
app.listen(3000);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**That's it.** CSRF, XSS protection, CORS, rate limiting, and security headers are all active.
|
|
101
|
+
|
|
102
|
+
### What's Built In (No Extra Packages Needed)
|
|
103
|
+
|
|
104
|
+
| Category | Replaces | VoltJS Module |
|
|
105
|
+
|----------|----------|---------------|
|
|
106
|
+
| State Management | Redux, Zustand, MobX | `Reactive.createStore()` — signals, actions, undo/redo, persist |
|
|
107
|
+
| HTTP Client | Axios, Fetch wrappers | `HttpClient` — interceptors, retries, gzip |
|
|
108
|
+
| Data Fetching | SWR, React Query | `HttpClient.swr()` — stale-while-revalidate, dedup, polling |
|
|
109
|
+
| Routing | Express Router, Next.js | `Router` — file-based + programmatic, groups, params |
|
|
110
|
+
| Form Handling | React Hook Form, Formik | `Form` — dirty/touched, field errors, arrays, submit |
|
|
111
|
+
| Schema Validation | Zod, Yup, Joi | `Schema` — chainable types, transforms, coercion, unions |
|
|
112
|
+
| String Validation | Validator.js | `Validator` — 25+ rules, middleware, static checks |
|
|
113
|
+
| Utilities | Lodash, Underscore | `_` — chunk, debounce, groupBy, merge, cloneDeep, 80+ utils |
|
|
114
|
+
| Date Utils | Moment.js, Day.js, date-fns | `DateHelper` — format, ago, add, diff, range |
|
|
115
|
+
| UUID | uuid, nanoid | `Hash.uuid()` — crypto.randomUUID |
|
|
116
|
+
| CSS Classes | classnames, clsx | `_.classNames()` — conditional class merging |
|
|
117
|
+
| Email | Nodemailer | `Mail` — raw SMTP, attachments, templates |
|
|
118
|
+
| PDF | PDFKit, jsPDF | `PDF` — raw PDF generation |
|
|
119
|
+
| Excel | ExcelJS, SheetJS | `Excel` — CSV/XLSX read/write |
|
|
120
|
+
| Caching | node-cache, ioredis | `Cache` — LRU, TTL, middleware |
|
|
121
|
+
| Job Queues | Bull, BullMQ | `Queue` — retries, priorities, concurrency |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Routing
|
|
126
|
+
|
|
127
|
+
### Programmatic Routes
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
app.get('/users', listUsers);
|
|
131
|
+
app.post('/users', createUser);
|
|
132
|
+
app.get('/users/:id', showUser);
|
|
133
|
+
app.put('/users/:id', updateUser);
|
|
134
|
+
app.delete('/users/:id', deleteUser);
|
|
135
|
+
|
|
136
|
+
// Resource routes (auto CRUD)
|
|
137
|
+
app.resource('/api/posts', {
|
|
138
|
+
index: listPosts,
|
|
139
|
+
show: showPost,
|
|
140
|
+
store: createPost,
|
|
141
|
+
update: updatePost,
|
|
142
|
+
destroy: deletePost,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Route groups
|
|
146
|
+
app.group('/admin', [authMiddleware], (group) => {
|
|
147
|
+
group.get('/dashboard', dashboard);
|
|
148
|
+
group.get('/settings', settings);
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### File-Based Routing
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
pages/
|
|
156
|
+
├── index.js → GET /
|
|
157
|
+
├── about.js → GET /about
|
|
158
|
+
├── blog/
|
|
159
|
+
│ ├── index.js → GET /blog
|
|
160
|
+
│ └── [id].js → GET /blog/:id
|
|
161
|
+
api/
|
|
162
|
+
├── users.js → GET/POST /api/users
|
|
163
|
+
└── users/
|
|
164
|
+
└── [id].js → GET/PUT/DELETE /api/users/:id
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**pages/about.js:**
|
|
168
|
+
```js
|
|
169
|
+
module.exports = {
|
|
170
|
+
get(req, res) {
|
|
171
|
+
res.render('about', { title: 'About Us' });
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Security
|
|
179
|
+
|
|
180
|
+
All protections are **enabled by default**. No setup required.
|
|
181
|
+
|
|
182
|
+
```js
|
|
183
|
+
const { Volt } = require('voltjs-framework');
|
|
184
|
+
const app = new Volt();
|
|
185
|
+
// ✅ CSRF protection — active
|
|
186
|
+
// ✅ XSS sanitization — active
|
|
187
|
+
// ✅ CORS handling — active
|
|
188
|
+
// ✅ Rate limiting — active
|
|
189
|
+
// ✅ Security headers (helmet) — active
|
|
190
|
+
// ✅ Input sanitization — active
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Customize Security
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
// volt.config.js
|
|
197
|
+
module.exports = {
|
|
198
|
+
security: {
|
|
199
|
+
csrf: true,
|
|
200
|
+
cors: { origin: 'https://mysite.com', credentials: true },
|
|
201
|
+
rateLimit: { windowMs: 15 * 60 * 1000, max: 100 },
|
|
202
|
+
helmet: true,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Manual Security Usage
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
const { CSRF, XSS, CORS, RateLimiter, Encryption } = require('voltjs-framework');
|
|
211
|
+
|
|
212
|
+
// Encrypt/decrypt data
|
|
213
|
+
const encrypted = Encryption.encrypt('sensitive data', 'secret-key');
|
|
214
|
+
const decrypted = Encryption.decrypt(encrypted, 'secret-key');
|
|
215
|
+
|
|
216
|
+
// Hash comparison (timing-safe)
|
|
217
|
+
Encryption.timingSafeCompare(hash1, hash2);
|
|
218
|
+
|
|
219
|
+
// Generate OTP
|
|
220
|
+
const otp = Encryption.generateOTP(6); // "482916"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Database & ORM
|
|
226
|
+
|
|
227
|
+
### Quick Setup (In-Memory — Zero Config)
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
const { Database, Model } = require('voltjs-framework');
|
|
231
|
+
|
|
232
|
+
const db = new Database(); // In-memory by default
|
|
233
|
+
await db.connect();
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Adapters
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
// SQLite
|
|
240
|
+
const db = new Database({ driver: 'sqlite', database: './app.db' });
|
|
241
|
+
|
|
242
|
+
// MySQL
|
|
243
|
+
const db = new Database({ driver: 'mysql', host: 'localhost', database: 'myapp', user: 'root' });
|
|
244
|
+
|
|
245
|
+
// PostgreSQL
|
|
246
|
+
const db = new Database({ driver: 'postgres', host: 'localhost', database: 'myapp', user: 'postgres' });
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Active Record ORM
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
class User extends Model {
|
|
253
|
+
static table = 'users';
|
|
254
|
+
static schema = {
|
|
255
|
+
name: { type: 'string', required: true, maxLength: 100 },
|
|
256
|
+
email: { type: 'string', required: true },
|
|
257
|
+
age: { type: 'number', min: 0 },
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// CRUD
|
|
262
|
+
const user = await User.create({ name: 'Jane', email: 'jane@example.com' });
|
|
263
|
+
const users = await User.all();
|
|
264
|
+
const found = await User.find(1);
|
|
265
|
+
const filtered = await User.where({ age: 25 });
|
|
266
|
+
await User.updateById(1, { name: 'Jane Doe' });
|
|
267
|
+
await User.deleteById(1);
|
|
268
|
+
|
|
269
|
+
// Pagination
|
|
270
|
+
const page = await User.paginate(1, 20); // { data: [...], meta: { ... } }
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Fluent Query Builder
|
|
274
|
+
|
|
275
|
+
```js
|
|
276
|
+
const { QueryBuilder } = require('voltjs-framework');
|
|
277
|
+
|
|
278
|
+
const results = await new QueryBuilder(db)
|
|
279
|
+
.table('users')
|
|
280
|
+
.select('name', 'email')
|
|
281
|
+
.where('age', '>', 18)
|
|
282
|
+
.whereLike('name', '%john%')
|
|
283
|
+
.orderBy('created_at', 'DESC')
|
|
284
|
+
.limit(10)
|
|
285
|
+
.get();
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Migrations
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
volt generate migration create_users_table
|
|
292
|
+
volt db:migrate
|
|
293
|
+
volt db:rollback
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Authentication
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
const { Auth } = require('voltjs-framework');
|
|
302
|
+
|
|
303
|
+
// JWT
|
|
304
|
+
const token = Auth.generateToken({ userId: 1 }, 'secret', '24h');
|
|
305
|
+
const payload = Auth.verifyToken(token, 'secret');
|
|
306
|
+
const { accessToken, refreshToken } = Auth.generateTokenPair({ userId: 1 }, 'secret');
|
|
307
|
+
|
|
308
|
+
// Password hashing
|
|
309
|
+
const hash = await Auth.hashPassword('mypassword');
|
|
310
|
+
const valid = await Auth.verifyPassword('mypassword', hash);
|
|
311
|
+
|
|
312
|
+
// Middleware
|
|
313
|
+
app.get('/protected', Auth.requireAuth('secret'), handler);
|
|
314
|
+
app.get('/admin', Auth.requireRole('admin'), adminHandler);
|
|
315
|
+
|
|
316
|
+
// Sessions
|
|
317
|
+
const sessionId = Auth.createSession({ userId: 1, role: 'admin' });
|
|
318
|
+
const session = Auth.getSession(sessionId);
|
|
319
|
+
|
|
320
|
+
// API Keys
|
|
321
|
+
const key = Auth.generateApiKey();
|
|
322
|
+
app.get('/api/data', Auth.requireApiKey(['key1', 'key2']), handler);
|
|
323
|
+
|
|
324
|
+
// 2FA (TOTP)
|
|
325
|
+
const { secret, uri } = Auth.generateTOTPSecret('MyApp', 'user@email.com');
|
|
326
|
+
const isValid = Auth.verifyTOTP('123456', secret);
|
|
327
|
+
|
|
328
|
+
// RBAC
|
|
329
|
+
Auth.defineRole('editor', ['posts:read', 'posts:write', 'posts:delete']);
|
|
330
|
+
app.post('/posts', Auth.requirePermission('posts:write'), createPost);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Validation
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
const { Validator } = require('voltjs-framework');
|
|
339
|
+
|
|
340
|
+
const result = Validator.validate(req.body, {
|
|
341
|
+
name: 'required|string|min:2|max:50',
|
|
342
|
+
email: 'required|email',
|
|
343
|
+
age: 'required|integer|min:18|max:120',
|
|
344
|
+
password: 'required|min:8|strongPassword',
|
|
345
|
+
website: 'url',
|
|
346
|
+
role: 'in:admin,user,editor',
|
|
347
|
+
phone: 'phone',
|
|
348
|
+
tags: 'array',
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
if (!result.valid) {
|
|
352
|
+
console.log(result.errors);
|
|
353
|
+
// { email: ['email must be a valid email'], age: ['age must be at least 18'] }
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// As middleware
|
|
357
|
+
app.post('/register', Validator.body({
|
|
358
|
+
name: 'required|string|min:2',
|
|
359
|
+
email: 'required|email',
|
|
360
|
+
password: 'required|min:8|strongPassword',
|
|
361
|
+
}), registerHandler);
|
|
362
|
+
|
|
363
|
+
// Individual validators
|
|
364
|
+
Validator.isEmail('test@example.com'); // true
|
|
365
|
+
Validator.isURL('https://example.com'); // true
|
|
366
|
+
Validator.isCreditCard('4111111111111111'); // true
|
|
367
|
+
Validator.isStrongPassword('Abc123!@#'); // true
|
|
368
|
+
Validator.isUUID('550e8400-e29b-41d4-a716-446655440000'); // true
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Email
|
|
374
|
+
|
|
375
|
+
```js
|
|
376
|
+
const { Mail } = require('voltjs-framework');
|
|
377
|
+
|
|
378
|
+
const mail = new Mail({
|
|
379
|
+
host: 'smtp.gmail.com',
|
|
380
|
+
port: 587,
|
|
381
|
+
user: 'you@gmail.com',
|
|
382
|
+
pass: 'app-password',
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Send email
|
|
386
|
+
await mail.send({
|
|
387
|
+
to: 'user@example.com',
|
|
388
|
+
subject: 'Welcome!',
|
|
389
|
+
html: '<h1>Hello {{name}}</h1>',
|
|
390
|
+
data: { name: 'John' },
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// With attachments
|
|
394
|
+
await mail.send({
|
|
395
|
+
to: 'user@example.com',
|
|
396
|
+
subject: 'Report',
|
|
397
|
+
text: 'Please find attached.',
|
|
398
|
+
attachments: [
|
|
399
|
+
{ filename: 'report.pdf', content: pdfBuffer },
|
|
400
|
+
],
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Quick send (static)
|
|
404
|
+
await Mail.quickSend(config, { to, subject, html });
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## SMS
|
|
410
|
+
|
|
411
|
+
```js
|
|
412
|
+
const { SMS } = require('voltjs-framework');
|
|
413
|
+
|
|
414
|
+
const sms = new SMS({
|
|
415
|
+
provider: 'twilio',
|
|
416
|
+
accountSid: process.env.TWILIO_SID,
|
|
417
|
+
authToken: process.env.TWILIO_TOKEN,
|
|
418
|
+
from: '+1234567890',
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
await sms.send('+1987654321', 'Hello from VoltJS!');
|
|
422
|
+
|
|
423
|
+
// Send OTP
|
|
424
|
+
await sms.sendOTP('+1987654321', '5829');
|
|
425
|
+
|
|
426
|
+
// Bulk send
|
|
427
|
+
await sms.sendBulk(['+111111', '+222222'], 'Announcement!');
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Excel Import/Export
|
|
433
|
+
|
|
434
|
+
```js
|
|
435
|
+
const { Excel } = require('voltjs-framework');
|
|
436
|
+
|
|
437
|
+
// Export to CSV
|
|
438
|
+
Excel.writeCSV('users.csv', [
|
|
439
|
+
{ name: 'John', email: 'john@example.com', age: 30 },
|
|
440
|
+
{ name: 'Jane', email: 'jane@example.com', age: 25 },
|
|
441
|
+
]);
|
|
442
|
+
|
|
443
|
+
// Import from CSV
|
|
444
|
+
const data = Excel.readCSV('users.csv');
|
|
445
|
+
|
|
446
|
+
// Export to XLSX (real Excel file, zero dependencies!)
|
|
447
|
+
Excel.writeXLSX('report.xlsx', data, { sheetName: 'Users' });
|
|
448
|
+
|
|
449
|
+
// Import from XLSX
|
|
450
|
+
const xlsxData = Excel.readXLSX('report.xlsx');
|
|
451
|
+
|
|
452
|
+
// JSON import/export
|
|
453
|
+
Excel.writeJSON('data.json', data);
|
|
454
|
+
const jsonData = Excel.readJSON('data.json');
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## PDF Generation
|
|
460
|
+
|
|
461
|
+
```js
|
|
462
|
+
const { PDF } = require('voltjs-framework');
|
|
463
|
+
|
|
464
|
+
const pdf = new PDF();
|
|
465
|
+
pdf.addPage();
|
|
466
|
+
pdf.setFont('Helvetica', 24);
|
|
467
|
+
pdf.text(50, 50, 'Hello VoltJS!');
|
|
468
|
+
pdf.setFont('Helvetica', 12);
|
|
469
|
+
pdf.paragraph(50, 100, 'This PDF was generated from scratch with zero dependencies.', 500);
|
|
470
|
+
|
|
471
|
+
// Tables
|
|
472
|
+
pdf.table(50, 200, {
|
|
473
|
+
headers: ['Name', 'Email', 'Role'],
|
|
474
|
+
rows: [
|
|
475
|
+
['John Doe', 'john@example.com', 'Admin'],
|
|
476
|
+
['Jane Smith', 'jane@example.com', 'User'],
|
|
477
|
+
],
|
|
478
|
+
columnWidths: [150, 200, 100],
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
pdf.save('output.pdf');
|
|
482
|
+
|
|
483
|
+
// Quick report from data
|
|
484
|
+
PDF.fromData('Report Title', columns, rows).save('report.pdf');
|
|
485
|
+
|
|
486
|
+
// Generate invoice
|
|
487
|
+
PDF.invoice({ company, customer, items, tax }).save('invoice.pdf');
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## File Storage
|
|
493
|
+
|
|
494
|
+
```js
|
|
495
|
+
const { Storage } = require('voltjs-framework');
|
|
496
|
+
|
|
497
|
+
const storage = new Storage({ root: './uploads' });
|
|
498
|
+
|
|
499
|
+
await storage.put('avatars/user1.png', imageBuffer);
|
|
500
|
+
const file = await storage.get('avatars/user1.png');
|
|
501
|
+
const exists = await storage.exists('avatars/user1.png');
|
|
502
|
+
await storage.delete('avatars/user1.png');
|
|
503
|
+
const url = storage.url('avatars/user1.png');
|
|
504
|
+
|
|
505
|
+
// S3-compatible storage
|
|
506
|
+
const s3 = new Storage({
|
|
507
|
+
driver: 's3',
|
|
508
|
+
bucket: 'my-bucket',
|
|
509
|
+
region: 'us-east-1',
|
|
510
|
+
accessKey: process.env.S3_ACCESS_KEY,
|
|
511
|
+
secretKey: process.env.S3_SECRET_KEY,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// Upload middleware
|
|
515
|
+
app.post('/upload', Storage.upload({
|
|
516
|
+
dest: 'uploads',
|
|
517
|
+
maxSize: 5 * 1024 * 1024, // 5MB
|
|
518
|
+
allowedTypes: ['image/png', 'image/jpeg'],
|
|
519
|
+
}), handler);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Caching
|
|
525
|
+
|
|
526
|
+
```js
|
|
527
|
+
const { Cache } = require('voltjs-framework');
|
|
528
|
+
|
|
529
|
+
const cache = new Cache({ maxSize: 10000, defaultTTL: 300 });
|
|
530
|
+
|
|
531
|
+
cache.set('user:1', userData, 60); // 60 second TTL
|
|
532
|
+
const user = cache.get('user:1');
|
|
533
|
+
|
|
534
|
+
// Get-or-set pattern
|
|
535
|
+
const data = await cache.getOrSet('expensive-query', async () => {
|
|
536
|
+
return await db.query('SELECT ...');
|
|
537
|
+
}, 120);
|
|
538
|
+
|
|
539
|
+
// Response caching middleware
|
|
540
|
+
app.get('/api/data', Cache.middleware(60), handler);
|
|
541
|
+
|
|
542
|
+
// Stats
|
|
543
|
+
cache.stats(); // { size: 42, hits: 150, misses: 12, hitRate: '92.6%' }
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Job Queues
|
|
549
|
+
|
|
550
|
+
```js
|
|
551
|
+
const { Queue } = require('voltjs-framework');
|
|
552
|
+
|
|
553
|
+
const emailQueue = new Queue('emails', { concurrency: 3, retries: 3 });
|
|
554
|
+
|
|
555
|
+
emailQueue.process(async (job) => {
|
|
556
|
+
await sendEmail(job.data);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
emailQueue.add({ to: 'user@example.com', subject: 'Hello' });
|
|
560
|
+
emailQueue.add(urgentJob, { priority: 'high', delay: 5000 });
|
|
561
|
+
|
|
562
|
+
emailQueue.on('completed', (job) => console.log(`Done: ${job.id}`));
|
|
563
|
+
emailQueue.on('failed', (job, err) => console.error(`Failed: ${err.message}`));
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Scheduled Tasks (Cron)
|
|
569
|
+
|
|
570
|
+
```js
|
|
571
|
+
const { Cron } = require('voltjs-framework');
|
|
572
|
+
|
|
573
|
+
const cron = new Cron();
|
|
574
|
+
|
|
575
|
+
cron.schedule('cleanup', '0 * * * *', cleanupOldFiles); // Every hour
|
|
576
|
+
cron.schedule('report', '0 9 * * 1', sendWeeklyReport); // Monday 9am
|
|
577
|
+
cron.schedule('backup', '0 2 * * *', backupDatabase); // Daily 2am
|
|
578
|
+
|
|
579
|
+
// Convenience methods
|
|
580
|
+
cron.everyMinute('health-check', checkHealth);
|
|
581
|
+
cron.daily('cleanup', cleanupTask);
|
|
582
|
+
cron.weekly('report', generateReport);
|
|
583
|
+
|
|
584
|
+
cron.start();
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## WebSockets
|
|
590
|
+
|
|
591
|
+
```js
|
|
592
|
+
const { Volt, WebSocketServer } = require('voltjs-framework');
|
|
593
|
+
const app = new Volt();
|
|
594
|
+
|
|
595
|
+
const wss = new WebSocketServer(app, { path: '/ws' });
|
|
596
|
+
|
|
597
|
+
wss.on('connection', (client) => {
|
|
598
|
+
console.log(`Client connected: ${client.id}`);
|
|
599
|
+
client.join('lobby');
|
|
600
|
+
|
|
601
|
+
client.on('chat', (data) => {
|
|
602
|
+
wss.to('lobby').emit('chat', {
|
|
603
|
+
user: client.id,
|
|
604
|
+
message: data.message,
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
wss.requireAuth(async (req, client) => {
|
|
610
|
+
const token = req.headers['authorization'];
|
|
611
|
+
return Auth.verifyToken(token, 'secret');
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
app.listen(3000);
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## GraphQL
|
|
620
|
+
|
|
621
|
+
```js
|
|
622
|
+
const { GraphQLHandler } = require('voltjs-framework');
|
|
623
|
+
|
|
624
|
+
const gql = new GraphQLHandler();
|
|
625
|
+
|
|
626
|
+
gql.type('User', {
|
|
627
|
+
id: 'ID!',
|
|
628
|
+
name: 'String!',
|
|
629
|
+
email: 'String',
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
gql.query('users', {}, async () => await User.all());
|
|
633
|
+
gql.query('user', { id: 'ID!' }, async ({ id }) => await User.find(id));
|
|
634
|
+
|
|
635
|
+
gql.mutation('createUser', { name: 'String!', email: 'String!' }, async (args) => {
|
|
636
|
+
return await User.create(args);
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
app.post('/graphql', gql.middleware());
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
## REST API Builder
|
|
645
|
+
|
|
646
|
+
```js
|
|
647
|
+
const { RestAPI } = require('voltjs-framework');
|
|
648
|
+
|
|
649
|
+
const api = new RestAPI(app, { prefix: '/api/v1' });
|
|
650
|
+
|
|
651
|
+
api.resource('users', {
|
|
652
|
+
async index(req, res) { return await User.all(); },
|
|
653
|
+
async show(req, res) { return await User.find(req.params.id); },
|
|
654
|
+
async store(req, res) { return await User.create(req.body); },
|
|
655
|
+
async update(req, res) { return await User.updateById(req.params.id, req.body); },
|
|
656
|
+
async destroy(req, res) { await User.deleteById(req.params.id); res.noContent(); },
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
// Versioned API groups
|
|
660
|
+
api.version(2, (v2) => {
|
|
661
|
+
v2.get('/status', (req, res) => res.json({ version: 2, status: 'ok' }));
|
|
662
|
+
});
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## Reactive State
|
|
668
|
+
|
|
669
|
+
```js
|
|
670
|
+
const { Reactive } = require('voltjs-framework');
|
|
671
|
+
|
|
672
|
+
const count = Reactive.signal(0);
|
|
673
|
+
const doubled = Reactive.computed(() => count.value * 2);
|
|
674
|
+
|
|
675
|
+
Reactive.effect(() => {
|
|
676
|
+
console.log(`Count: ${count.value}, Doubled: ${doubled.value}`);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
count.value = 5; // Logs: "Count: 5, Doubled: 10"
|
|
680
|
+
|
|
681
|
+
// Reactive store
|
|
682
|
+
const store = Reactive.store({
|
|
683
|
+
user: null,
|
|
684
|
+
theme: 'dark',
|
|
685
|
+
count: 0,
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
store.$watch('count', (val) => console.log('Count changed:', val));
|
|
689
|
+
store.count = 42;
|
|
690
|
+
|
|
691
|
+
// Batch updates
|
|
692
|
+
Reactive.batch(() => {
|
|
693
|
+
store.count = 1;
|
|
694
|
+
store.theme = 'light';
|
|
695
|
+
}); // Single re-render
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### Advanced Store (Redux-like Actions, Undo/Redo, Persist)
|
|
699
|
+
|
|
700
|
+
```js
|
|
701
|
+
const counter = Reactive.createStore({
|
|
702
|
+
state: { count: 0, todos: [] },
|
|
703
|
+
actions: {
|
|
704
|
+
increment(state) { state.count++; },
|
|
705
|
+
add(state, amount) { state.count += amount; },
|
|
706
|
+
addTodo(state, text) { state.todos = [...state.todos, text]; },
|
|
707
|
+
},
|
|
708
|
+
middleware: [Reactive.loggerMiddleware],
|
|
709
|
+
persist: { storage: 'file', path: './.store.json' },
|
|
710
|
+
history: true,
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
counter.dispatch('increment');
|
|
714
|
+
counter.dispatch('add', 5);
|
|
715
|
+
|
|
716
|
+
// Memoized selectors
|
|
717
|
+
const total = counter.select(s => s.count);
|
|
718
|
+
console.log(total.value); // 6
|
|
719
|
+
|
|
720
|
+
// Undo / Redo
|
|
721
|
+
counter.$undo();
|
|
722
|
+
console.log(counter.count); // 1
|
|
723
|
+
counter.$redo();
|
|
724
|
+
console.log(counter.count); // 6
|
|
725
|
+
|
|
726
|
+
// Subscribe to all changes
|
|
727
|
+
counter.$subscribe((key, value, snapshot) => {
|
|
728
|
+
console.log(`${key} changed to`, value);
|
|
729
|
+
});
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## Components
|
|
735
|
+
|
|
736
|
+
```js
|
|
737
|
+
const { Component } = require('voltjs-framework');
|
|
738
|
+
|
|
739
|
+
class Card extends Component {
|
|
740
|
+
setup() {
|
|
741
|
+
this.state = { likes: 0 };
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
render() {
|
|
745
|
+
return `
|
|
746
|
+
<div class="${this.$class({ card: true, featured: this.props.featured })}">
|
|
747
|
+
<h3>${this.$escape(this.props.title)}</h3>
|
|
748
|
+
<p>${this.$escape(this.props.body)}</p>
|
|
749
|
+
<span>Likes: ${this.state.likes}</span>
|
|
750
|
+
</div>
|
|
751
|
+
`;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Usage in route
|
|
756
|
+
app.get('/cards', (req, res) => {
|
|
757
|
+
const html = Component.render(Card, { title: 'Hello', body: 'World' });
|
|
758
|
+
res.html(html);
|
|
759
|
+
});
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
---
|
|
763
|
+
|
|
764
|
+
## Template Engine
|
|
765
|
+
|
|
766
|
+
VoltJS includes a Mustache-like template engine with layouts, partials, loops, and conditionals.
|
|
767
|
+
|
|
768
|
+
```html
|
|
769
|
+
<!-- views/layouts/main.volt -->
|
|
770
|
+
<!DOCTYPE html>
|
|
771
|
+
<html>
|
|
772
|
+
<head><title>{{ title }}</title></head>
|
|
773
|
+
<body>
|
|
774
|
+
{> header}
|
|
775
|
+
<main>{#slot content}</main>
|
|
776
|
+
{> footer}
|
|
777
|
+
</body>
|
|
778
|
+
</html>
|
|
779
|
+
|
|
780
|
+
<!-- views/index.volt -->
|
|
781
|
+
{#layout layouts/main}
|
|
782
|
+
|
|
783
|
+
{#block content}
|
|
784
|
+
<h1>{{ title }}</h1>
|
|
785
|
+
|
|
786
|
+
{#if user}
|
|
787
|
+
<p>Welcome, {{ user.name }}!</p>
|
|
788
|
+
{#else}
|
|
789
|
+
<p>Please log in.</p>
|
|
790
|
+
{/if}
|
|
791
|
+
|
|
792
|
+
<ul>
|
|
793
|
+
{#each items as item}
|
|
794
|
+
<li>{{ item.name }} - ${{ item.price }}</li>
|
|
795
|
+
{/each}
|
|
796
|
+
</ul>
|
|
797
|
+
{/block}
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
```js
|
|
801
|
+
app.get('/', (req, res) => {
|
|
802
|
+
res.render('index', { title: 'Home', user, items });
|
|
803
|
+
});
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## Form Handling
|
|
809
|
+
|
|
810
|
+
Built-in form state management — like **React Hook Form** with dirty/touched tracking,
|
|
811
|
+
field-level errors, array fields, watchers, and submission handling.
|
|
812
|
+
|
|
813
|
+
```js
|
|
814
|
+
const { Form } = require('voltjs-framework');
|
|
815
|
+
|
|
816
|
+
const form = new Form({
|
|
817
|
+
defaults: { name: '', email: '', tags: [] },
|
|
818
|
+
rules: {
|
|
819
|
+
name: 'required|string|min:2',
|
|
820
|
+
email: 'required|email',
|
|
821
|
+
},
|
|
822
|
+
validateOn: 'change', // 'change' | 'blur' | 'submit'
|
|
823
|
+
onSubmit: async (data) => await User.create(data),
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
form.set('name', 'John');
|
|
827
|
+
form.set('email', 'john@example.com');
|
|
828
|
+
|
|
829
|
+
// State tracking
|
|
830
|
+
form.isDirty; // true
|
|
831
|
+
form.isFieldDirty('name'); // true
|
|
832
|
+
form.isFieldTouched('name'); // false
|
|
833
|
+
form.touch('name'); // mark as touched
|
|
834
|
+
form.dirtyValues; // { name: 'John', email: 'john@example.com' }
|
|
835
|
+
|
|
836
|
+
// Array fields
|
|
837
|
+
form.append('tags', 'javascript');
|
|
838
|
+
form.append('tags', 'nodejs');
|
|
839
|
+
form.remove('tags', 0);
|
|
840
|
+
form.move('tags', 0, 1);
|
|
841
|
+
|
|
842
|
+
// Nested fields (dot notation)
|
|
843
|
+
form.set('address.city', 'NYC');
|
|
844
|
+
form.get('address.city'); // 'NYC'
|
|
845
|
+
|
|
846
|
+
// Watch field changes
|
|
847
|
+
form.watch('email', (newVal, oldVal) => console.log('Email changed'));
|
|
848
|
+
|
|
849
|
+
// Submit
|
|
850
|
+
const result = await form.submit();
|
|
851
|
+
// { success: true, data: { ... } } or { success: false, errors: { ... } }
|
|
852
|
+
|
|
853
|
+
// Reset
|
|
854
|
+
form.reset();
|
|
855
|
+
|
|
856
|
+
// As middleware
|
|
857
|
+
app.post('/register', Form.handle({
|
|
858
|
+
rules: { name: 'required', email: 'required|email' },
|
|
859
|
+
onSubmit: (data) => User.create(data),
|
|
860
|
+
}), (req, res) => {
|
|
861
|
+
res.json(req.formResult);
|
|
862
|
+
});
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
|
|
867
|
+
## Schema Validation (Zod)
|
|
868
|
+
|
|
869
|
+
Type-safe, chainable schema builder — like **Zod/Yup** with transforms, coercion,
|
|
870
|
+
nested objects, arrays, unions, and enums.
|
|
871
|
+
|
|
872
|
+
```js
|
|
873
|
+
const { Schema } = require('voltjs-framework');
|
|
874
|
+
|
|
875
|
+
const userSchema = Schema.object({
|
|
876
|
+
name: Schema.string().min(2).max(50).trim().required(),
|
|
877
|
+
email: Schema.string().email().toLowerCase().required(),
|
|
878
|
+
age: Schema.number().int().min(18).max(120).optional(),
|
|
879
|
+
role: Schema.enum(['admin', 'user', 'editor']).default('user'),
|
|
880
|
+
tags: Schema.array(Schema.string()).min(1).unique(),
|
|
881
|
+
website: Schema.string().url().optional(),
|
|
882
|
+
address: Schema.object({
|
|
883
|
+
street: Schema.string(),
|
|
884
|
+
city: Schema.string().required(),
|
|
885
|
+
zip: Schema.string().regex(/^\d{5}$/),
|
|
886
|
+
}).optional(),
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
// Validate (returns result)
|
|
890
|
+
const result = userSchema.validate(data);
|
|
891
|
+
if (!result.valid) console.log(result.errors);
|
|
892
|
+
|
|
893
|
+
// Parse (throws on invalid)
|
|
894
|
+
const parsed = userSchema.parse(data);
|
|
895
|
+
|
|
896
|
+
// Safe parse (never throws)
|
|
897
|
+
const safe = userSchema.safeParse(data);
|
|
898
|
+
|
|
899
|
+
// Schema composition
|
|
900
|
+
const updateSchema = userSchema.partial(); // All fields optional
|
|
901
|
+
const loginSchema = userSchema.pick(['email']); // Only email
|
|
902
|
+
const publicSchema = userSchema.omit(['age']); // Remove age
|
|
903
|
+
|
|
904
|
+
// Coercion (auto-convert types)
|
|
905
|
+
const configSchema = Schema.object({
|
|
906
|
+
port: Schema.number().coerce().int().min(0).max(65535),
|
|
907
|
+
debug: Schema.boolean().coerce(),
|
|
908
|
+
});
|
|
909
|
+
configSchema.parse({ port: '3000', debug: 'true' });
|
|
910
|
+
// { port: 3000, debug: true }
|
|
911
|
+
|
|
912
|
+
// Unions, literals, tuples
|
|
913
|
+
const statusSchema = Schema.union([
|
|
914
|
+
Schema.literal('active'),
|
|
915
|
+
Schema.literal('inactive'),
|
|
916
|
+
]);
|
|
917
|
+
|
|
918
|
+
const pointSchema = Schema.tuple([
|
|
919
|
+
Schema.number(), Schema.number(),
|
|
920
|
+
]);
|
|
921
|
+
|
|
922
|
+
// Custom refinements
|
|
923
|
+
const passwordSchema = Schema.string()
|
|
924
|
+
.min(8)
|
|
925
|
+
.refine(v => /[A-Z]/.test(v), 'Must have uppercase')
|
|
926
|
+
.refine(v => /\d/.test(v), 'Must have a number');
|
|
927
|
+
|
|
928
|
+
// Record (key-value maps)
|
|
929
|
+
const envSchema = Schema.record(Schema.string(), Schema.string());
|
|
930
|
+
|
|
931
|
+
// Recursive schemas
|
|
932
|
+
const categorySchema = Schema.object({
|
|
933
|
+
name: Schema.string().required(),
|
|
934
|
+
children: Schema.lazy(() => Schema.array(categorySchema)),
|
|
935
|
+
});
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
---
|
|
939
|
+
|
|
940
|
+
## Collection Utilities (Lodash)
|
|
941
|
+
|
|
942
|
+
Full **Lodash-equivalent** utilities — arrays, objects, functions — zero dependencies.
|
|
943
|
+
|
|
944
|
+
```js
|
|
945
|
+
const { _ } = require('voltjs-framework');
|
|
946
|
+
|
|
947
|
+
// Arrays
|
|
948
|
+
_.chunk([1,2,3,4,5], 2); // [[1,2],[3,4],[5]]
|
|
949
|
+
_.compact([0, 1, false, 2, '']); // [1, 2]
|
|
950
|
+
_.flatten([[1,[2]],[3]]); // [1,[2],3]
|
|
951
|
+
_.flattenDeep([[1,[2,[3]]]]); // [1,2,3]
|
|
952
|
+
_.uniq([1, 2, 2, 3]); // [1, 2, 3]
|
|
953
|
+
_.uniqBy(users, 'email'); // Unique by email
|
|
954
|
+
_.intersection([1,2], [2,3]); // [2]
|
|
955
|
+
_.difference([1,2,3], [2,3]); // [1]
|
|
956
|
+
_.union([1,2], [2,3]); // [1,2,3]
|
|
957
|
+
_.zip([1,2], ['a','b']); // [[1,'a'],[2,'b']]
|
|
958
|
+
|
|
959
|
+
// Grouping & sorting
|
|
960
|
+
_.groupBy(users, 'role'); // { admin: [...], user: [...] }
|
|
961
|
+
_.keyBy(users, 'id'); // { 1: {...}, 2: {...} }
|
|
962
|
+
_.sortBy(users, 'name'); // Sorted by name
|
|
963
|
+
_.orderBy(users, ['age','name'], ['desc','asc']);
|
|
964
|
+
_.countBy(users, 'role'); // { admin: 2, user: 5 }
|
|
965
|
+
_.partition(nums, n => n > 0); // [[positives], [negatives]]
|
|
966
|
+
|
|
967
|
+
// Aggregation
|
|
968
|
+
_.sum([1, 2, 3]); // 6
|
|
969
|
+
_.sumBy(items, 'price'); // Total price
|
|
970
|
+
_.mean([1, 2, 3, 4]); // 2.5
|
|
971
|
+
_.minBy(users, 'age'); // Youngest user
|
|
972
|
+
_.maxBy(users, 'age'); // Oldest user
|
|
973
|
+
|
|
974
|
+
// Random
|
|
975
|
+
_.sample([1, 2, 3]); // Random element
|
|
976
|
+
_.sampleSize([1,2,3,4], 2); // 2 random elements
|
|
977
|
+
_.shuffle([1, 2, 3, 4, 5]); // Shuffled
|
|
978
|
+
_.range(0, 10, 2); // [0, 2, 4, 6, 8]
|
|
979
|
+
_.times(5, i => i * 2); // [0, 2, 4, 6, 8]
|
|
980
|
+
|
|
981
|
+
// Objects
|
|
982
|
+
_.get(obj, 'a.b[0].c', 'default'); // Deep path access
|
|
983
|
+
_.set(obj, 'a.b.c', 42); // Deep path set
|
|
984
|
+
_.has(obj, 'a.b.c'); // true/false
|
|
985
|
+
_.pick(obj, ['name', 'email']); // { name, email }
|
|
986
|
+
_.omit(obj, ['password']); // Without password
|
|
987
|
+
_.merge(defaults, overrides); // Deep merge
|
|
988
|
+
_.cloneDeep(obj); // Deep clone
|
|
989
|
+
_.isEqual(objA, objB); // Deep equality
|
|
990
|
+
_.isEmpty({}); // true
|
|
991
|
+
_.mapValues(obj, v => v * 2); // Transform values
|
|
992
|
+
_.mapKeys(obj, k => k.toUpperCase());
|
|
993
|
+
_.invert({ a: 1, b: 2 }); // { 1: 'a', 2: 'b' }
|
|
994
|
+
_.freezeDeep(obj); // Deep freeze (immutable)
|
|
995
|
+
|
|
996
|
+
// Functions
|
|
997
|
+
const save = _.debounce(saveData, 300); // Debounce
|
|
998
|
+
const scroll = _.throttle(onScroll, 100); // Throttle
|
|
999
|
+
const loadOnce = _.once(loadConfig); // Execute only once
|
|
1000
|
+
const factorial = _.memoize(calcFactorial);// Memoize results
|
|
1001
|
+
_.curry(fn); // Curry
|
|
1002
|
+
_.pipe(fn1, fn2, fn3)(input); // Pipe (left to right)
|
|
1003
|
+
_.compose(fn3, fn2, fn1)(input); // Compose (right to left)
|
|
1004
|
+
await _.retry(fetchData, { attempts: 3 }); // Retry with backoff
|
|
1005
|
+
await _.delay(fn, 1000); // Delayed execution
|
|
1006
|
+
await _.sleep(500); // Sleep/wait
|
|
1007
|
+
|
|
1008
|
+
// Type checks
|
|
1009
|
+
_.isString('hello'); // true
|
|
1010
|
+
_.isNumber(42); // true
|
|
1011
|
+
_.isArray([]); // true
|
|
1012
|
+
_.isObject({}); // true
|
|
1013
|
+
_.isNil(null); // true
|
|
1014
|
+
_.isPlainObject({}); // true
|
|
1015
|
+
_.isEmpty([]); // true
|
|
1016
|
+
|
|
1017
|
+
// Misc
|
|
1018
|
+
_.toQueryString({ page: 1, q: 'hello' }); // 'page=1&q=hello'
|
|
1019
|
+
_.parseQueryString('page=1&q=hello'); // { page: '1', q: 'hello' }
|
|
1020
|
+
_.tryParse('{"a":1}'); // { a: 1 }
|
|
1021
|
+
_.uniqueId('user_'); // 'user_1'
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
## classNames / clsx
|
|
1027
|
+
|
|
1028
|
+
```js
|
|
1029
|
+
const { _ } = require('voltjs-framework');
|
|
1030
|
+
|
|
1031
|
+
_.classNames('btn', { active: true, disabled: false }, 'primary');
|
|
1032
|
+
// => 'btn active primary'
|
|
1033
|
+
|
|
1034
|
+
_.classNames('flex', condition && 'hidden', ['p-4', { 'bg-red': error }]);
|
|
1035
|
+
// => 'flex hidden p-4 bg-red' (if condition and error are truthy)
|
|
1036
|
+
|
|
1037
|
+
_.clsx('text-lg', { bold: isBold }); // Alias for classNames
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
---
|
|
1041
|
+
|
|
1042
|
+
## Logging
|
|
1043
|
+
|
|
1044
|
+
```js
|
|
1045
|
+
const { Logger } = require('voltjs-framework');
|
|
1046
|
+
|
|
1047
|
+
const log = new Logger({ level: 'debug', file: 'logs/app.log' });
|
|
1048
|
+
|
|
1049
|
+
log.info('Server started', { port: 3000 });
|
|
1050
|
+
log.debug('Query executed', { sql: '...', duration: '12ms' });
|
|
1051
|
+
log.warn('Deprecated API used');
|
|
1052
|
+
log.error('Connection failed', { error: err.message });
|
|
1053
|
+
|
|
1054
|
+
// Request logging middleware
|
|
1055
|
+
app.use(Logger.requestLogger());
|
|
1056
|
+
|
|
1057
|
+
// Child loggers
|
|
1058
|
+
const dbLog = log.child('database');
|
|
1059
|
+
dbLog.info('Connected'); // [database] Connected
|
|
1060
|
+
|
|
1061
|
+
// Timing
|
|
1062
|
+
const timer = log.time('query');
|
|
1063
|
+
await db.query('...');
|
|
1064
|
+
timer.end(); // "query completed in 12.34ms"
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
---
|
|
1068
|
+
|
|
1069
|
+
## HTTP Client
|
|
1070
|
+
|
|
1071
|
+
```js
|
|
1072
|
+
const { HttpClient } = require('voltjs-framework');
|
|
1073
|
+
|
|
1074
|
+
// Quick usage
|
|
1075
|
+
const response = await HttpClient.get('https://api.example.com/users');
|
|
1076
|
+
console.log(response.data);
|
|
1077
|
+
|
|
1078
|
+
// Instance with defaults
|
|
1079
|
+
const api = new HttpClient({
|
|
1080
|
+
baseURL: 'https://api.example.com',
|
|
1081
|
+
timeout: 10000,
|
|
1082
|
+
retries: 2,
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
api.setBearerToken('my-token');
|
|
1086
|
+
|
|
1087
|
+
const users = await api.get('/users');
|
|
1088
|
+
const created = await api.post('/users', { name: 'John' });
|
|
1089
|
+
const updated = await api.put('/users/1', { name: 'Jane' });
|
|
1090
|
+
await api.delete('/users/1');
|
|
1091
|
+
|
|
1092
|
+
// Download files
|
|
1093
|
+
await api.download('https://example.com/file.pdf', './downloads/file.pdf');
|
|
1094
|
+
|
|
1095
|
+
// SWR (stale-while-revalidate) — like SWR/React Query
|
|
1096
|
+
const fetcher = HttpClient.swr({ ttl: 30, revalidate: 120 });
|
|
1097
|
+
const data = await fetcher.get('/api/users'); // Cached + background refresh
|
|
1098
|
+
fetcher.mutate('/api/users', newData); // Optimistic update
|
|
1099
|
+
fetcher.invalidate('/api/users'); // Force refetch
|
|
1100
|
+
|
|
1101
|
+
// Polling
|
|
1102
|
+
const poller = HttpClient.poll('https://api.example.com/status', {
|
|
1103
|
+
interval: 5000,
|
|
1104
|
+
onData: (data) => console.log(data),
|
|
1105
|
+
until: (data) => data.status === 'complete',
|
|
1106
|
+
});
|
|
1107
|
+
poller.stop();
|
|
1108
|
+
|
|
1109
|
+
// Concurrent requests
|
|
1110
|
+
const [users, posts] = await HttpClient.all([
|
|
1111
|
+
HttpClient.get('/api/users'),
|
|
1112
|
+
HttpClient.get('/api/posts'),
|
|
1113
|
+
]);
|
|
1114
|
+
|
|
1115
|
+
// Abortable requests
|
|
1116
|
+
const { promise, abort } = HttpClient.abortable('https://api.example.com/data');
|
|
1117
|
+
setTimeout(() => abort(), 5000);
|
|
1118
|
+
const result = await promise;
|
|
1119
|
+
```
|
|
1120
|
+
|
|
1121
|
+
---
|
|
1122
|
+
|
|
1123
|
+
## CLI Commands
|
|
1124
|
+
|
|
1125
|
+
```bash
|
|
1126
|
+
volt create <name> # Create a new project
|
|
1127
|
+
volt dev # Start dev server with hot reload
|
|
1128
|
+
volt build # Build for production
|
|
1129
|
+
volt start # Start production server
|
|
1130
|
+
|
|
1131
|
+
volt generate page <name> # Generate a page + view
|
|
1132
|
+
volt generate api <name> # Generate an API route
|
|
1133
|
+
volt generate component <name> # Generate a component
|
|
1134
|
+
volt generate model <name> # Generate a model
|
|
1135
|
+
volt generate middleware <name> # Generate middleware
|
|
1136
|
+
volt generate migration <name> # Generate a migration
|
|
1137
|
+
volt generate seeder <name> # Generate a seeder
|
|
1138
|
+
|
|
1139
|
+
volt db:migrate # Run migrations
|
|
1140
|
+
volt db:rollback # Rollback last migration
|
|
1141
|
+
volt db:seed # Run seeders
|
|
1142
|
+
|
|
1143
|
+
volt routes # List all routes
|
|
1144
|
+
volt lint # Lint project
|
|
1145
|
+
volt test # Run tests
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
---
|
|
1149
|
+
|
|
1150
|
+
## Configuration
|
|
1151
|
+
|
|
1152
|
+
**volt.config.js:**
|
|
1153
|
+
```js
|
|
1154
|
+
module.exports = {
|
|
1155
|
+
port: 3000,
|
|
1156
|
+
|
|
1157
|
+
security: {
|
|
1158
|
+
csrf: true,
|
|
1159
|
+
cors: { origin: '*', credentials: true },
|
|
1160
|
+
rateLimit: { windowMs: 900000, max: 100 },
|
|
1161
|
+
helmet: true,
|
|
1162
|
+
},
|
|
1163
|
+
|
|
1164
|
+
database: {
|
|
1165
|
+
driver: 'sqlite', // 'memory' | 'sqlite' | 'mysql' | 'postgres'
|
|
1166
|
+
database: './app.db',
|
|
1167
|
+
},
|
|
1168
|
+
|
|
1169
|
+
views: {
|
|
1170
|
+
dir: './views',
|
|
1171
|
+
engine: 'volt',
|
|
1172
|
+
},
|
|
1173
|
+
|
|
1174
|
+
mail: {
|
|
1175
|
+
host: process.env.MAIL_HOST,
|
|
1176
|
+
port: 587,
|
|
1177
|
+
user: process.env.MAIL_USER,
|
|
1178
|
+
pass: process.env.MAIL_PASS,
|
|
1179
|
+
},
|
|
1180
|
+
|
|
1181
|
+
logging: {
|
|
1182
|
+
level: 'info',
|
|
1183
|
+
file: './logs/app.log',
|
|
1184
|
+
},
|
|
1185
|
+
};
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
All settings can be overridden via environment variables:
|
|
1189
|
+
- `PORT`, `NODE_ENV`
|
|
1190
|
+
- `DB_DRIVER`, `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USER`, `DB_PASSWORD`
|
|
1191
|
+
- `MAIL_HOST`, `MAIL_PORT`, `MAIL_USER`, `MAIL_PASS`
|
|
1192
|
+
- `APP_SECRET`
|
|
1193
|
+
|
|
1194
|
+
---
|
|
1195
|
+
|
|
1196
|
+
## Project Structure
|
|
1197
|
+
|
|
1198
|
+
```
|
|
1199
|
+
my-app/
|
|
1200
|
+
├── app.js # Application entry point
|
|
1201
|
+
├── volt.config.js # Configuration
|
|
1202
|
+
├── package.json
|
|
1203
|
+
├── .env # Environment variables
|
|
1204
|
+
├── pages/ # File-based routes (auto-discovered)
|
|
1205
|
+
│ ├── index.js # → GET /
|
|
1206
|
+
│ └── about.js # → GET /about
|
|
1207
|
+
├── api/ # API routes (auto-discovered)
|
|
1208
|
+
│ └── users.js # → /api/users
|
|
1209
|
+
├── views/ # Templates
|
|
1210
|
+
│ ├── layouts/
|
|
1211
|
+
│ │ └── main.volt
|
|
1212
|
+
│ ├── partials/
|
|
1213
|
+
│ │ ├── header.volt
|
|
1214
|
+
│ │ └── footer.volt
|
|
1215
|
+
│ └── index.volt
|
|
1216
|
+
├── components/ # Reusable components
|
|
1217
|
+
├── models/ # Database models
|
|
1218
|
+
├── middleware/ # Custom middleware
|
|
1219
|
+
├── public/ # Static assets (served automatically)
|
|
1220
|
+
│ ├── css/
|
|
1221
|
+
│ ├── js/
|
|
1222
|
+
│ └── images/
|
|
1223
|
+
├── database/
|
|
1224
|
+
│ ├── migrations/
|
|
1225
|
+
│ └── seeders/
|
|
1226
|
+
├── storage/ # File uploads
|
|
1227
|
+
├── tests/ # Test files
|
|
1228
|
+
└── logs/ # Log files
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
---
|
|
1232
|
+
|
|
1233
|
+
## Design Principles
|
|
1234
|
+
|
|
1235
|
+
1. **Zero Config** — Works out of the box with sensible defaults
|
|
1236
|
+
2. **Security First** — All protections enabled by default
|
|
1237
|
+
3. **Batteries Included** — No hunting for packages
|
|
1238
|
+
4. **Minimal Boilerplate** — 2 files to start a full app
|
|
1239
|
+
5. **Progressive** — Use only what you need
|
|
1240
|
+
6. **Zero External Dependencies** — Only `ws` for WebSocket support; everything else uses native Node.js
|
|
1241
|
+
|
|
1242
|
+
---
|
|
1243
|
+
|
|
1244
|
+
## Requirements
|
|
1245
|
+
|
|
1246
|
+
- **Node.js** >= 18.0.0
|
|
1247
|
+
|
|
1248
|
+
## Optional Dependencies
|
|
1249
|
+
|
|
1250
|
+
| Package | For |
|
|
1251
|
+
|---------|-----|
|
|
1252
|
+
| `ws` | WebSocket support |
|
|
1253
|
+
| `better-sqlite3` | SQLite database |
|
|
1254
|
+
| `mysql2` | MySQL database |
|
|
1255
|
+
| `pg` | PostgreSQL database |
|
|
1256
|
+
|
|
1257
|
+
---
|
|
1258
|
+
|
|
1259
|
+
## License
|
|
1260
|
+
|
|
1261
|
+
MIT
|
|
1262
|
+
|
|
1263
|
+
---
|
|
1264
|
+
|
|
1265
|
+
**Built with âš¡ by the VoltJS team.**
|