speexjs 0.2.1 → 0.2.3

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/README.md CHANGED
@@ -1,152 +1,52 @@
1
- # speedx 🚀
1
+ # SpeexJS
2
2
 
3
- **Fullstack JavaScript/TypeScript Framework — Server + Client + RPC + Schema + Database + Auth**
4
- **🇮🇩 Indonesia First, Built for the World**
3
+ **Fullstack TypeScript Framework — zero dependencies.**
5
4
 
6
5
  ```bash
7
- npm install speedx
6
+ npm install speexjs
8
7
  ```
9
8
 
10
- > Zero external dependencies. Only Node.js built-in modules.
11
-
12
- ---
13
-
14
- ## 📦 Kenapa speedx?
15
-
16
- | Masalah | Solusi speedx |
17
- |---------|---------------|
18
- | Next.js terlalu berat & vendor lock-in | speedx ringan, zero-dep, engine-swappable |
19
- | Express terlalu minimalis | speedx Laravel-like: routing, middleware, controller, DI, ORM |
20
- | Laravel pakai PHP | speedx TypeScript native, end-to-end type safe |
21
- | Fetch API ribet | SuperRequest/SuperResponse wrapper sendiri — lebih mudah |
22
- | Framework asing untuk Indonesia | 🇮🇩 NIK, NPWP, Phone, Rupiah, terbilang built-in |
23
- | React/Vue dependency berat | Signal-based VDOM sendiri — ringan, cepat |
24
-
25
- ---
26
-
27
- ## ✨ Fitur Lengkap
28
-
29
- ### 🔴 Server (Laravel-like, TypeScript Native)
30
-
31
- | Modul | Fitur |
32
- |-------|-------|
33
- | **Routing** | `get()`, `post()`, `put()`, `patch()`, `delete()`, `resource()`, `group()`, named routes |
34
- | **Middleware** | CORS, CSRF, Auth, Throttle, Session, Helmet, Logger, Body Parser, Compress, Static Files |
35
- | **Controller** | Base class + `@controller`/`@get`/`@post` decorators |
36
- | **Container (DI)** | `bind()`, `singleton()`, `instance()`, `resolve()` |
37
- | **HTTP** | SuperRequest + SuperResponse **(BUKAN Fetch API)** — `.json()`, `.html()`, `.redirect()`, `.stream()`, `.file()` |
38
- | **Engine** | Node.js default, bisa ganti ke Bun/Deno via `app.setEngine()` |
39
- | **Validation** | 25+ schema types + 🇮🇩 NIK, NPWP, Phone, Kodepos, Rekening |
40
- | **Error Handling** | `TypedError`, HTTP status codes, exception handler |
41
- | **Logging** | Structured logger — WIB/WITA/WIT timezone |
42
-
43
- ### 🟡 Auth & Security
44
-
45
- | Modul | Fitur |
46
- |-------|-------|
47
- | **Session Guard** | Login/logout via encrypted cookies (AES-256-GCM) |
48
- | **Token Guard** | Bearer token authentication, abilities/permissions |
49
- | **Gate** | Authorization policies — `Gate.define()`, `Gate.allows()` |
50
- | **Middleware** | `authMiddleware()`, `guestMiddleware()`, `authorize()` |
51
- | **Encryption** | AES-256-GCM encrypt/decrypt — Node.js `crypto` native |
52
- | **Hashing** | scrypt (OWASP recommended) + PBKDF2 |
53
-
54
- ### 🟡 Database
55
-
56
- | Modul | Fitur |
57
- |-------|-------|
58
- | **Query Builder** | Laravel-like chain: `.select()`, `.where()`, `.join()`, `.orderBy()` |
59
- | **Pagination** | `.paginate(perPage)` — built-in di Query Builder |
60
- | **Migrations** | `SchemaBuilder`, `TableBlueprint` (30+ column types) |
61
- | **Seeding** | `Seeder` class — insert/truncate data awal |
62
- | **Drivers** | MySQL (default), SQLite, PostgreSQL — runtime dynamic import |
63
-
64
- ### 🎨 Client (Signal-Based VDOM)
65
-
66
- | Modul | Fitur |
67
- |-------|-------|
68
- | **Signals** | `signal()`, `computed()`, `effect()`, `batch()`, `untracked()` |
69
- | **VDOM** | `h()`, `render()`, `patch()`, `hydrate()` — Virtual DOM sendiri |
70
- | **SSR** | `renderToString()`, `renderToStream()`, `ServerRenderer` |
71
- | **JSX** | Full JSX support via `jsxImportSource: \"@SpeedX/vdom\"` |
72
- | **Router** | File-based routing, guards, reactive current/params/query |
73
- | **Adapters** | `FrameworkAdapter` interface — React/Vue integration |
74
-
75
- ### 🔗 RPC (Type-Safe)
76
-
77
- | Modul | Fitur |
78
- |-------|-------|
79
- | **Server** | `rpc.create()` — definisi procedure + schema validation |
80
- | **Client** | `createClient()` — auto-typed queries & mutations |
81
- | **Batch** | Multiple procedures in one request |
82
-
83
- ### 🛠️ CLI (Zero Dependencies)
84
-
85
- | Perintah | Fungsi |
86
- |----------|--------|
87
- | `speedx init [name]` | Scaffold project baru (blank/fullstack/api-only) |
88
- | `speedx make:controller <name>` | Generate controller |
89
- | `speedx make:middleware <name>` | Generate middleware |
90
- | `speedx make:schema <name>` | Generate schema |
91
- | `speedx list-routes` | Lihat semua route yang terdaftar |
92
- | `speedx serve` | Jalankan development server |
93
-
94
- ---
95
-
96
- ## 🚀 Quickstart
97
-
98
- ### 1. Install
99
-
100
- ```bash
101
- npm install speedx
102
- ```
103
-
104
- ### 2. Buat File Server
9
+ ## Quick Start
105
10
 
106
11
  ```typescript
107
- // src/index.ts
108
- import { speedx } from 'speedx/server'
12
+ import { speexjs } from 'speexjs/server'
109
13
 
110
- const app = speedx()
14
+ const app = speexjs()
111
15
 
112
16
  app.get('/', async ({ response }) => {
113
- return response.html('<h1>speedx 🚀</h1>')
114
- })
115
-
116
- app.get('/api/hello', async ({ response }) => {
117
- return response.json({ message: 'Halo Dunia!' })
17
+ return response.html('<h1>SpeexJS 🚀</h1>')
118
18
  })
119
19
 
120
- app.listen(3000, () => console.log('speedx running on http://localhost:3000'))
20
+ app.listen(3000)
121
21
  ```
122
22
 
123
- ### 3. Jalankan
124
-
125
23
  ```bash
126
- npx speedx serve
127
- # atau
128
- node --loader ts-node src/index.ts
24
+ npx speexjs serve
129
25
  ```
130
26
 
131
- ---
27
+ ## Feature Overview
132
28
 
133
- ## 📖 Dokumentasi Lengkap
29
+ | Category | Features |
30
+ |----------|----------|
31
+ | **Server** | Router, Middleware (10 built-in), Controller, DI Container, Laravel-like API |
32
+ | **Database** | Query Builder, Migrations, Pagination, MySQL/SQLite/PostgreSQL |
33
+ | **Auth** | Session Guard, Token Guard, Gate Authorization, Encryption, Hashing |
34
+ | **Validation** | 25+ schema types, NIK/NPWP/Phone/Kodepos/Rupiah |
35
+ | **Client** | Signals, VDOM, JSX, SSR — no React dependency |
36
+ | **RPC** | Type-safe server-client communication |
37
+ | **CLI** | `speexjs init`, `serve`, `make:*`, `list-routes` |
38
+ | **Zero Dep** | 100% native Node.js — zero external dependencies |
134
39
 
135
- ### Server Routing
40
+ ## Examples
136
41
 
137
- ```typescript
138
- import { speedx } from 'speedx/server'
42
+ ### Routing
139
43
 
140
- const app = speedx()
44
+ ```typescript
45
+ import { speexjs, Controller, get, post, controller } from 'speexjs/server'
141
46
 
142
- // Basic routes
143
- app.get('/users', handler)
144
- app.post('/users', handler)
145
- app.put('/users/:id', handler)
146
- app.delete('/users/:id', handler)
147
- app.patch('/users/:id', handler)
47
+ const app = speexjs()
148
48
 
149
- // Route groups
49
+ // Route groups with middleware
150
50
  app.group('/api', (router) => {
151
51
  router.get('/users', [UserController, 'index'])
152
52
  router.post('/users', [UserController, 'store'])
@@ -154,402 +54,77 @@ app.group('/api', (router) => {
154
54
 
155
55
  // Resource routes
156
56
  app.router.resource('/posts', PostController)
157
- // GET /posts index
158
- // GET /posts/create → create
159
- // POST /posts → store
160
- // GET /posts/:id → show
161
- // GET /posts/:id/edit → edit
162
- // PUT /posts/:id → update
163
- // DELETE /posts/:id → destroy
164
-
165
- // Named routes
166
- app.get('/users/:id', handler).name('users.show')
167
- // app.router.route('users.show', { id: 5 }) → '/users/5'
57
+ // GET /posts, POST /posts, GET /posts/:id, PUT /posts/:id, DELETE /posts/:id
168
58
  ```
169
59
 
170
- ### Controller
60
+ ### Validation
171
61
 
172
62
  ```typescript
173
- import { Controller, get, post, put, del } from 'speedx/server'
174
-
175
- @controller('/api/users')
176
- export class UserController extends Controller {
177
- @get('/')
178
- async index({ response }) {
179
- const users = await UserService.all()
180
- return response.json(users)
181
- }
182
-
183
- @post('/')
184
- async store({ request, response }) {
185
- const data = await request.validate(CreateUserSchema)
186
- const user = await UserService.create(data)
187
- return response.json(user, 201)
188
- }
189
-
190
- @get('/:id')
191
- async show({ params, response }) {
192
- const user = await UserService.findOrFail(params.id)
193
- return response.json(user)
194
- }
195
-
196
- @put('/:id')
197
- async update({ params, request, response }) {
198
- const data = await request.validate(UpdateUserSchema)
199
- const user = await UserService.update(params.id, data)
200
- return response.json(user)
201
- }
202
-
203
- @del('/:id')
204
- async destroy({ params, response }) {
205
- await UserService.delete(params.id)
206
- return response.noContent()
207
- }
208
- }
209
- ```
210
-
211
- ### Schema Validation
63
+ import { s } from 'speexjs/schema'
212
64
 
213
- ```typescript
214
- import { s } from 'speedx/schema'
215
-
216
- // Buat schema
217
65
  const UserSchema = s.object({
218
- id: s.number(),
219
66
  name: s.string().min(3).max(100),
220
67
  email: s.string().email(),
221
68
  age: s.number().min(17).max(120).optional(),
222
- phone: s.phone(), // 🇮🇩 Nomor Indonesia
223
- nik: s.nik().optional(), // 🇮🇩 NIK 16 digit
224
- npwp: s.npwp().optional(), // 🇮🇩 NPWP
69
+ phone: s.phone(), // Nomor Indonesia
70
+ nik: s.nik().optional(), // NIK 16 digit
225
71
  })
226
72
 
227
- // Type inference
228
73
  type User = s.Infer<typeof UserSchema>
229
-
230
- // Parse & validate
231
- const result = UserSchema.safeParse(input)
232
- if (!result.success) {
233
- console.log(result.error) // Pesan error Bahasa Indonesia
234
- }
235
74
  ```
236
75
 
237
- ### Database Query Builder
76
+ ### Database
238
77
 
239
78
  ```typescript
240
- import { DatabaseConnection } from 'speedx/server/database'
241
-
242
- const db = new DatabaseConnection({
243
- driver: 'mysql', // mysql | sqlite | postgresql
244
- host: 'localhost',
245
- database: 'myapp',
246
- username: 'root',
247
- password: 'secret',
248
- })
79
+ import { DatabaseConnection } from 'speexjs/server/database'
249
80
 
81
+ const db = new DatabaseConnection({ driver: 'mysql', database: 'myapp' })
250
82
  await db.connect()
251
83
 
252
- // Query dengan chaining
253
84
  const users = await db.table('users')
254
85
  .select('id', 'name', 'email')
255
86
  .where('age', '>', 18)
256
- .whereLike('name', '%john%')
257
87
  .orderByDesc('created_at')
258
- .limit(10)
259
- .get()
260
-
261
- // Pagination
262
- const result = await db.table('posts')
263
- .where('published', true)
264
- .paginate(15, 1)
265
- // result.data, result.total, result.lastPage, ...
266
-
267
- // Insert
268
- const id = await db.table('users').insert({
269
- name: 'John',
270
- email: 'john@test.com',
271
- })
272
-
273
- // Update
274
- await db.table('users')
275
- .where('id', 1)
276
- .update({ name: 'John Doe' })
277
-
278
- // Raw SQL
279
- const result = await db.raw('SELECT * FROM users WHERE age > ?', [18])
88
+ .paginate(10, 1)
280
89
  ```
281
90
 
282
- ### Authentication
91
+ ### Auth
283
92
 
284
93
  ```typescript
285
- import { speedx } from 'speedx/server'
286
- import { AuthManager, SessionGuard } from 'speedx/server/auth'
94
+ import { AuthManager, SessionGuard } from 'speexjs/server/auth'
287
95
 
288
- const app = speedx()
289
-
290
- // Setup auth
291
96
  const auth = new AuthManager()
292
- auth.guard('session', new SessionGuard({
293
- table: 'users',
294
- cookieName: 'speedx_session',
295
- }))
97
+ auth.guard('session', new SessionGuard({ table: 'users' }))
296
98
 
297
- // Login route
298
99
  app.post('/login', async ({ request, response }) => {
299
100
  const { email, password } = await request.json()
300
- const authenticated = await auth.guard('session').attempt({ email, password })
301
-
302
- if (!authenticated) {
303
- return response.json({ error: 'Login gagal' }, 401)
304
- }
101
+ const ok = await auth.guard('session').attempt({ email, password })
102
+ if (!ok) return response.json({ error: 'Login gagal' }, 401)
305
103
  return response.json({ message: 'Login berhasil' })
306
104
  })
307
-
308
- // Protected route
309
- app.get('/profile', async ({ response }, next) => {
310
- const user = await auth.guard('session').user()
311
- return response.json(user)
312
- }).middleware(['auth'])
313
-
314
- // Authorization Gate
315
- import { Gate } from 'speedx/server/gate'
316
-
317
- const gate = new Gate()
318
- gate.define('update-post', (user, post) => user.id === post.user_id)
319
-
320
- app.put('/posts/:id', async ({ params, response }) => {
321
- const post = await Post.find(params.id)
322
- await gate.authorize('update-post', currentUser, post)
323
- // ...
324
- })
325
105
  ```
326
106
 
327
- ### Client — Signals + VDOM
328
-
329
- ```typescript
330
- import { signal, computed, effect, h, render } from 'speedx/client'
331
-
332
- function Counter() {
333
- const count = signal(0)
334
- const doubled = computed(() => count.value * 2)
335
-
336
- effect(() => console.log('Count:', count.value))
337
-
338
- return h('div', { class: 'counter' },
339
- h('p', {}, `Count: ${count.value}`),
340
- h('p', {}, `Doubled: ${doubled.value}`),
341
- h('button', { onClick: () => count.value++ }, '+'),
342
- h('button', { onClick: () => count.value-- }, '-'),
343
- )
344
- }
345
-
346
- // Mount ke DOM
347
- render(h(Counter), document.getElementById('root')!)
348
- ```
107
+ ### CLI
349
108
 
350
- Dengan JSX (`tsconfig.json`):
351
- ```json
352
- {
353
- "compilerOptions": {
354
- "jsx": "react-jsx",
355
- "jsxImportSource": "@SpeedX/vdom"
356
- }
357
- }
358
- ```
359
-
360
- ```tsx
361
- function Counter() {
362
- const count = signal(0)
363
- return (
364
- <div class="counter">
365
- <p>Count: {count}</p>
366
- <button onClick={() => count.value++}>+</button>
367
- </div>
368
- )
369
- }
370
- ```
371
-
372
- ### Cache
373
-
374
- ```typescript
375
- import { Cache } from 'speedx/server/cache'
376
-
377
- const cache = new Cache({ store: 'memory', ttl: 3600 })
378
-
379
- // Set & Get
380
- await cache.set('user:1', { name: 'John' })
381
- const user = await cache.get('user:1') // { name: 'John' }
382
-
383
- // Remember (get or set)
384
- const data = await cache.remember('expensive:data', 300, async () => {
385
- return await fetchExpensiveData()
386
- })
387
-
388
- // Increment/Decrement
389
- await cache.increment('visits')
390
- ```
391
-
392
- ### File Storage
393
-
394
- ```typescript
395
- import { createStorage } from 'speedx/server/storage'
396
-
397
- const storage = createStorage({
398
- defaultDisk: 'local',
399
- disks: {
400
- local: { driver: 'local', root: './storage' },
401
- public: { driver: 'local', root: './public/uploads', url: '/uploads' },
402
- },
403
- })
404
-
405
- // Upload file
406
- await storage.disk('public').put('images/photo.jpg', buffer)
407
-
408
- // Get file URL
409
- const url = storage.disk('public').url('images/photo.jpg')
410
-
411
- // Check exists
412
- const exists = await storage.exists('images/photo.jpg')
413
- ```
414
-
415
- ### Events
416
-
417
- ```typescript
418
- import { event } from 'speedx/server/events'
419
-
420
- // Listen
421
- event.on('user.registered', async (user) => {
422
- await sendWelcomeEmail(user)
423
- })
424
-
425
- // Wildcard pattern
426
- event.onPattern('user.*', async (eventName, data) => {
427
- console.log(`User event: ${eventName}`, data)
428
- })
429
-
430
- // Emit
431
- await event.emit('user.registered', { id: 1, email: 'john@test.com' })
109
+ ```bash
110
+ speexjs init my-app # Buat project baru
111
+ speexjs serve # Jalankan dev server
112
+ speexjs make:controller User # Generate controller
113
+ speexjs make:middleware Auth # Generate middleware
114
+ speexjs list-routes # Lihat semua route
432
115
  ```
433
116
 
434
- ### RPC (Type-Safe)
117
+ ## Architecture
435
118
 
436
- ```typescript
437
- // === SERVER ===
438
- import { speedx } from 'speedx/server'
439
- import { rpc } from 'speedx/rpc'
440
- import { s } from 'speedx/schema'
441
-
442
- const api = rpc.create({
443
- procedures: {
444
- 'users.list': {
445
- input: s.object({ page: s.number().default(1) }),
446
- output: s.array(s.object({ id: s.number(), name: s.string() })),
447
- handler: async ({ page }, ctx) => {
448
- return await db.table('users').paginate(10, page).data
449
- },
450
- },
451
- 'echo': {
452
- input: s.object({ message: s.string() }),
453
- output: s.object({ reply: s.string() }),
454
- handler: async ({ message }) => ({ reply: `You said: ${message}` }),
455
- },
456
- },
457
- })
458
-
459
- const app = speedx()
460
- app.post('/api/rpc', api.toHandler())
461
- app.listen(3000)
462
-
463
- // === CLIENT ===
464
- import { createClient } from 'speedx/rpc'
465
-
466
- const client = createClient({ baseUrl: 'http://localhost:3000/api' })
467
- const users = await client.call('users.list', { page: 1 })
468
- // users typed as { id: number, name: string }[]
469
119
  ```
470
-
471
- ---
472
-
473
- ## 🇮🇩 Fitur Indonesia
474
-
475
- | Fitur | Keterangan |
476
- |-------|-----------|
477
- | `s.nik()` | Validasi NIK 16 digit + tanggal lahir |
478
- | `s.npwp()` | Validasi NPWP + checksum |
479
- | `s.phone()` | Validasi nomor telepon Indonesia (+62, 08xx) |
480
- | `s.kodepos()` | Validasi kode pos 5 digit |
481
- | `s.rekening()` | Validasi nomor rekening bank |
482
- | `s.alamat()` | Validasi alamat Indonesia |
483
- | `Number.formatRupiah()` | Format mata uang Rupiah |
484
- | `Number.terbilang()` | Angka ke kata Bahasa Indonesia |
485
- | **Logger** | Timezone WIB/WITA/WIT |
486
- | **Error messages** | Bahasa Indonesia default |
487
- | **CLI** | Output dengan Bahasa Indonesia |
488
-
489
- ---
490
-
491
- ## 🏗️ Arsitektur
492
-
493
- ```
494
- speedx/
495
- ├── src/
496
- │ ├── index.ts # Barrel export
497
- │ ├── native/ # Zero-dep utilities
498
- │ ├── schema/ # Validation (25+ types)
499
- │ ├── server/
500
- │ │ ├── http/ # SuperRequest/SuperResponse
501
- │ │ ├── router/ # Routing engine
502
- │ │ ├── middleware/ # 10 built-in middleware
503
- │ │ ├── controller/ # Base controller
504
- │ │ ├── container/ # Dependency injection
505
- │ │ ├── engine/ # Swappable engine
506
- │ │ ├── auth/ # Session + Token guards
507
- │ │ ├── gate/ # Authorization
508
- │ │ ├── cache/ # Caching system
509
- │ │ ├── storage/ # File storage
510
- │ │ ├── events/ # Event system
511
- │ │ └── database/ # Query Builder + Migrations
512
- │ ├── client/ # Signals + VDOM + SSR
513
- │ ├── rpc/ # Type-safe RPC
514
- │ └── cli/ # Native CLI (zero dep)
515
- └── tests/
120
+ speexjs/
121
+ ├── server/ (HTTP, Router, Middleware, Auth, Database, Cache, Storage, Events)
122
+ ├── client/ (Signals, VDOM, SSR, JSX)
123
+ ├── schema/ (25+ validation types)
124
+ ├── rpc/ (Type-safe RPC)
125
+ └── cli/ (speexjs init, serve, make:*)
516
126
  ```
517
127
 
518
- ---
519
-
520
- ## 📋 Perbandingan dengan Framework Lain
521
-
522
- | Fitur | speedx | Next.js | Express | AdonisJS | Laravel |
523
- |-------|---------|---------|---------|----------|---------|
524
- | Bahasa | TS/JS | TS/JS | JS | TS/JS | PHP |
525
- | Dependencies | **0** | Ratusan | Puluhan | Puluhan | Ratusan |
526
- | File size | ~175KB | >50MB | ~500KB | >10MB | >20MB |
527
- | Routing | ✅ Laravel-like | ✅ File-based | ❌ Manual | ✅ Programmatic | ✅ Laravel |
528
- | Database | ✅ Query Builder | ❌ Prisma | ❌ None | ✅ Lucid ORM | ✅ Eloquent |
529
- | Auth | ✅ Session+Token | ✅ NextAuth | ❌ Manual | ✅ Built-in | ✅ Built-in |
530
- | Validation | ✅ Schema (25+) | ✅ Zod | ❌ Manual | ✅ Vine | ✅ Form Request |
531
- | VDOM/Signals | ✅ Built-in | ✅ React | ❌ | ❌ Edge | ❌ Blade |
532
- | RPC | ✅ Type-safe | ❌ Server Actions | ❌ | ✅ Tuyau | ❌ |
533
- | 🇮🇩 Indonesia | ✅ Built-in | ❌ | ❌ | ❌ | ❌ |
534
-
535
- ---
536
-
537
- ## 🗺️ Roadmap
538
-
539
- | Version | Fitur |
540
- |---------|-------|
541
- | **v0.1.0** | ✅ Schema, Server (HTTP+Router+Middleware+Controller), Client (Signals+VDOM), RPC, CLI (commander) |
542
- | **v0.2.0** | ✅ Zero dependencies, Native CLI, Auth (Session+Token), Gate, Database (Query Builder+Migrations+Pagination), Cache, Storage, Events, Encryption, Hashing, Helpers |
543
- | **v0.3.0** | 🔜 File-based routing, Server Components, ORM, Queues |
544
- | **v1.0.0** | 🔜 Adapter React/Vue, Dokumentasi lengkap, Publikasi global |
545
-
546
- ---
547
-
548
- ## ⚖️ License
549
-
550
- MIT — bebas digunakan, dimodifikasi, dan didistribusikan.
551
-
552
- ---
128
+ ## License
553
129
 
554
- **speedx — Fullstack JavaScript/TypeScript Framework**
555
- **🇮🇩 Dibuat oleh developer Indonesia, untuk developer dunia.**
130
+ MIT