create-celsian 0.3.5 → 0.3.8

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ThenJS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,596 @@
1
+ # CelsianJS
2
+
3
+ TypeScript backend framework built on Web Standard APIs. Ships runtime adapters for Node.js, Bun, Deno, Cloudflare Workers, AWS Lambda, and Vercel.
4
+
5
+ - **Multi-runtime** -- Write once for Web Standard `Request`/`Response`, then deploy with the adapter that matches your target runtime.
6
+ - **Significantly faster than Express** -- Radix-tree router, zero-copy request building, pre-stringified error paths. 1.3x-1.7x faster across all scenarios.
7
+ - **First-party batteries** -- Background tasks, cron, WebSocket, CORS, CSRF protection, security headers, DB analytics, rate limiting, JWT, caching, compression, and OpenAPI docs via core APIs plus first-party packages.
8
+ - **Fastify-style plugin encapsulation** -- Scoped hooks and decorations by default. No accidental middleware leaks.
9
+ - **Schema-agnostic validation** -- Auto-detects Zod, TypeBox, or Valibot. No config, no adapters.
10
+
11
+ ## Local pnpm note
12
+
13
+ This monorepo is pinned to `pnpm@9.15.0`. For local release verification, prefer:
14
+
15
+ ```bash
16
+ npm run setup:pnpm
17
+ # or, if Corepack is unavailable locally:
18
+ npx -y pnpm@9.15.0 <script>
19
+ ```
20
+
21
+ CI and release gates should use the pinned package manager version, not an older global pnpm.
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ npx create-celsian my-api
27
+ cd my-api
28
+ npm install
29
+ npm run dev
30
+ ```
31
+
32
+ Or manually for the core router/runtime:
33
+
34
+ ```bash
35
+ npm install @celsian/core
36
+ ```
37
+
38
+ ```typescript
39
+ import { createApp, serve } from '@celsian/core';
40
+
41
+ const app = createApp({ logger: true });
42
+
43
+ // ─── Background tasks with retries ───
44
+ app.task({
45
+ name: 'sendWelcomeEmail',
46
+ retries: 3,
47
+ async handler(input: { to: string }) {
48
+ await sendEmail(input.to, 'Welcome!');
49
+ },
50
+ });
51
+
52
+ // ─── Cron job: clean up expired sessions every night ───
53
+ app.cron('cleanup', '0 3 * * *', async () => {
54
+ await db.query('DELETE FROM sessions WHERE expires_at < NOW()');
55
+ });
56
+
57
+ // ─── API routes ───
58
+ app.post('/users', async (req, reply) => {
59
+ const body = req.parsedBody as { name: string; email: string };
60
+ const user = await db.createUser(body);
61
+ await app.enqueue('sendWelcomeEmail', { to: body.email });
62
+ return reply.status(201).json(user);
63
+ });
64
+
65
+ app.get('/users/:id', (req, reply) => {
66
+ return reply.json({ id: req.params.id, name: 'Alice' });
67
+ });
68
+
69
+ serve(app, { port: 3000 });
70
+ ```
71
+
72
+ Tasks, cron, and API routes in one file -- no separate worker process needed. On Bun or Deno, `serve()` auto-detects the runtime. No code changes needed.
73
+
74
+ ```bash
75
+ bun run server.ts # Uses Bun.serve() automatically
76
+ deno run server.ts # Uses Deno.serve() automatically
77
+ ```
78
+
79
+ ## Why CelsianJS
80
+
81
+ ### Multi-Runtime
82
+
83
+ Built on Web Standard `Request`/`Response` -- not Node.js `IncomingMessage`/`ServerResponse`. One adapter line deploys anywhere:
84
+
85
+ ```typescript
86
+ export default createCloudflareHandler(app); // Cloudflare Workers
87
+ export const handler = createLambdaHandler(app); // AWS Lambda
88
+ export default createVercelEdgeHandler(app); // Vercel Edge
89
+ ```
90
+
91
+ ### Honest Benchmarks
92
+
93
+ Benchmarked on Node.js v22, Apple Silicon, 10 connections for 10 seconds per scenario:
94
+
95
+ | Scenario | Fastify (req/s) | CelsianJS (req/s) | Express (req/s) |
96
+ | --------------------- | ---------------: | -----------------: | --------------: |
97
+ | JSON response | 45,866 | 27,996 | 16,321 |
98
+ | Route params | 45,440 | 27,026 | 16,288 |
99
+ | Middleware (5 layers) | 41,380 | 24,445 | 15,751 |
100
+ | JSON body parsing | 29,998 | 19,074 | 14,648 |
101
+ | Error handling | 32,398 | 18,542 | 14,765 |
102
+
103
+ **Fastify is faster.** It operates directly on Node.js internals with `fast-json-stringify` — hard to beat. CelsianJS pays a performance tax for Web Standard API compatibility (`Request`/`Response` object creation per request).
104
+
105
+ **CelsianJS is 1.3-1.7x faster than Express** while shipping batteries that neither Fastify nor Express include: background task queues, cron scheduling, multi-runtime deployment, and DB analytics. If raw throughput is your only concern, use Fastify. If you need application infrastructure in a single framework, that's where CelsianJS fits.
106
+
107
+ ### Built-In Everything
108
+
109
+ Celsian's batteries ship as first-party packages. Install the extras you use:
110
+
111
+ ```bash
112
+ npm install @celsian/core @celsian/rate-limit @celsian/jwt @celsian/compress
113
+ ```
114
+
115
+ ```typescript
116
+ import { createApp, security, cors, csrf, openapi } from '@celsian/core';
117
+ import { rateLimit } from '@celsian/rate-limit';
118
+ import { jwt } from '@celsian/jwt';
119
+ import { compress } from '@celsian/compress';
120
+
121
+ const app = createApp();
122
+
123
+ await app.register(security(), { encapsulate: false }); // Helmet-style headers
124
+ await app.register(cors({ origin: 'https://myapp.com' }));
125
+ await app.register(csrf(), { encapsulate: false }); // CSRF token protection
126
+ await app.register(rateLimit({ max: 100, window: 60_000, keyGenerator: req => req.headers.get("x-api-key") ?? "public" }));
127
+ await app.register(compress());
128
+ await app.register(jwt({ secret: process.env.JWT_SECRET! }));
129
+ await app.register(openapi({ title: 'My API' }));
130
+
131
+ app.health(); // /health + /ready
132
+ app.task({ name: 'email', handler, retries: 3 }); // Background tasks
133
+ app.cron('cleanup', '0 3 * * *', cleanupHandler); // Cron jobs
134
+ app.ws('/chat', { open, message, close }); // WebSocket
135
+ ```
136
+
137
+ ### Plugin Encapsulation
138
+
139
+ Plugins get isolated scopes by default. Hooks and decorations registered inside a plugin do not leak to sibling plugins or the parent scope.
140
+
141
+ ```typescript
142
+ // Auth plugin -- hooks only apply to routes registered inside
143
+ async function authPlugin(app) {
144
+ app.addHook('onRequest', async (req, reply) => {
145
+ const token = req.headers.get('authorization');
146
+ if (!token) return reply.unauthorized();
147
+ });
148
+
149
+ app.get('/me', (req, reply) => {
150
+ return reply.json({ user: req.user });
151
+ });
152
+ }
153
+
154
+ // Public routes -- no auth required
155
+ app.get('/health', (req, reply) => reply.json({ status: 'ok' }));
156
+
157
+ // Register auth plugin under /api prefix
158
+ await app.register(authPlugin, { prefix: '/api' });
159
+ ```
160
+
161
+ Use `{ encapsulate: false }` when a plugin should affect all routes (e.g., CORS, database):
162
+
163
+ ```typescript
164
+ await app.register(cors(), { encapsulate: false });
165
+ ```
166
+
167
+ ### Type-Safe Schema Validation
168
+
169
+ Pass any Zod, TypeBox, or Valibot schema. CelsianJS auto-detects the library.
170
+
171
+ ```typescript
172
+ app.route({
173
+ method: 'POST',
174
+ url: '/users',
175
+ schema: {
176
+ body: z.object({ name: z.string().min(1), email: z.string().email() }),
177
+ },
178
+ handler(req, reply) {
179
+ const { name, email } = req.parsedBody as { name: string; email: string };
180
+ return reply.status(201).json({ id: '1', name, email });
181
+ },
182
+ });
183
+ // Invalid input returns 400 with structured issues automatically
184
+ ```
185
+
186
+ ### JSX Server Rendering
187
+
188
+ CelsianJS includes a built-in JSX runtime for server-side HTML rendering -- no React dependency needed. Supports both automatic and classic transforms.
189
+
190
+ **Setup (automatic transform -- recommended):**
191
+
192
+ ```jsonc
193
+ // tsconfig.json
194
+ {
195
+ "compilerOptions": {
196
+ "jsx": "react-jsx",
197
+ "jsxImportSource": "@celsian/core"
198
+ }
199
+ }
200
+ ```
201
+
202
+ **Setup (classic transform):**
203
+
204
+ ```jsonc
205
+ // tsconfig.json
206
+ {
207
+ "compilerOptions": {
208
+ "jsx": "react",
209
+ "jsxFactory": "h",
210
+ "jsxFragmentFactory": "Fragment"
211
+ }
212
+ }
213
+ ```
214
+
215
+ ```typescript
216
+ import { h, Fragment } from '@celsian/core/jsx';
217
+ ```
218
+
219
+ **Usage:**
220
+
221
+ ```tsx
222
+ import { renderToString, renderToDocument } from '@celsian/core';
223
+
224
+ // Function components work like React
225
+ function Layout({ title, children }: { title: string; children: any }) {
226
+ return (
227
+ <html>
228
+ <head><title>{title}</title></head>
229
+ <body>{children}</body>
230
+ </html>
231
+ );
232
+ }
233
+
234
+ function UserCard({ name, email }: { name: string; email: string }) {
235
+ return (
236
+ <div className="card">
237
+ <h2>{name}</h2>
238
+ <p>{email}</p>
239
+ </div>
240
+ );
241
+ }
242
+
243
+ app.get('/page', (req, reply) => {
244
+ const html = renderToDocument(
245
+ <Layout title="Users">
246
+ <UserCard name="Alice" email="alice@example.com" />
247
+ </Layout>
248
+ );
249
+ return reply.html(html);
250
+ });
251
+ ```
252
+
253
+ Supports `className`/`htmlFor` mapping, style objects, boolean attributes, void elements, `dangerouslySetInnerHTML`, Fragments, and XSS-safe escaping.
254
+
255
+ ## Features at a Glance
256
+
257
+ | Category | Features |
258
+ | -------- | -------- |
259
+ | **Routing** | Radix-tree router, params, wildcards, HEAD fallback, 405, route tagging |
260
+ | **Hooks** | 8-hook lifecycle (onRequest through onResponse), route-level hooks |
261
+ | **Plugins** | Scoped encapsulation, app/request/reply decorators |
262
+ | **Validation** | Zod, TypeBox, Valibot auto-detect; body, querystring, params schemas |
263
+ | **Reply** | json, html, stream, redirect, sendFile (Range/ETag/304), download, cookies, 9 error helpers, JSX SSR |
264
+ | **Security** | Helmet-style headers, CORS, CSRF protection, JWT, fixed-window rate limiting |
265
+ | **Background** | Task queue with retries, cron scheduling, Redis queue backend |
266
+ | **Real-time** | WebSocket with broadcast and connection management |
267
+ | **Database** | Connection pool plugin, transactions, query analytics, Server-Timing |
268
+ | **Caching** | Response cache, session management (KV store) |
269
+ | **Infra** | Brotli/gzip/deflate compression, OpenAPI 3.1 + Swagger UI, structured logging, inject() testing |
270
+ | **Deploy** | Node, Bun, Deno, Workers, Lambda, Vercel, Fly.io, Railway, graceful shutdown |
271
+
272
+ ## Core Concepts
273
+
274
+ ### Routes and Handlers
275
+
276
+ ```typescript
277
+ app.get('/users/:id', (req, reply) => {
278
+ return reply.json({ id: req.params.id, include: req.query.include });
279
+ });
280
+
281
+ // Full route options with schema, hooks, and deployment tagging
282
+ app.route({
283
+ method: 'POST',
284
+ url: '/items',
285
+ kind: 'serverless',
286
+ schema: { body: mySchema },
287
+ preHandler: [authHook],
288
+ handler(req, reply) { return reply.status(201).json(req.parsedBody); },
289
+ });
290
+ ```
291
+
292
+ ### Hooks Lifecycle
293
+
294
+ 8 hooks run in order: `onRequest` > `preParsing` > `preValidation` > `preHandler` > `handler` > `preSerialization` > `onSend` > `onResponse`. Plus `onError` for error handling. Any hook can short-circuit by returning a `Response`.
295
+
296
+ ```typescript
297
+ // Global hook
298
+ app.addHook('onRequest', async (req, reply) => {
299
+ reply.header('x-request-id', crypto.randomUUID());
300
+ });
301
+
302
+ // Route-level hook
303
+ app.route({
304
+ method: 'POST',
305
+ url: '/admin/users',
306
+ onRequest: [requireAdmin],
307
+ handler(req, reply) { return reply.json({ created: true }); },
308
+ });
309
+ ```
310
+
311
+ See [Hooks Lifecycle](docs/hooks.md) for the complete guide.
312
+
313
+ ### Reply Helpers
314
+
315
+ ```typescript
316
+ reply.json({ data: [] }); // JSON response
317
+ reply.html('<h1>Hello</h1>'); // HTML response
318
+ reply.stream(readableStream); // Streaming
319
+ reply.redirect('/new-path', 301); // Redirect
320
+ await reply.sendFile('/path/to/report.pdf'); // Serve file
321
+ await reply.download('/path/to/data.csv', 'export'); // Download
322
+
323
+ // Structured error responses
324
+ reply.notFound('User not found'); // 404
325
+ reply.badRequest('Missing email'); // 400
326
+ reply.unauthorized('Token expired'); // 401
327
+ reply.forbidden(); // 403
328
+ reply.conflict(); // 409
329
+ reply.tooManyRequests(); // 429
330
+
331
+ // Cookies + chaining
332
+ reply.cookie('session', token, { httpOnly: true, secure: true });
333
+ return reply.status(201).header('x-custom', 'value').json({ id: '1' });
334
+ ```
335
+
336
+ #### File Serving with Range Requests
337
+
338
+ `sendFile` and `download` support Range requests (HTTP 206), conditional GET (304), ETag, and Last-Modified headers. Pass `options.request` to enable these features:
339
+
340
+ ```typescript
341
+ app.get('/files/:name', async (req, reply) => {
342
+ return reply.sendFile(req.params.name, {
343
+ root: './uploads', // Resolve relative to this directory (with traversal protection)
344
+ request: req, // Enable Range requests + conditional GET (304)
345
+ cacheControl: 'public, max-age=3600', // Set Cache-Control header
346
+ });
347
+ });
348
+
349
+ // Download with Range support (for resumable downloads)
350
+ app.get('/export/:id', async (req, reply) => {
351
+ return reply.download(`./data/${req.params.id}.csv`, 'export.csv', {
352
+ request: req,
353
+ });
354
+ });
355
+ ```
356
+
357
+ Supported Range formats: `bytes=0-499`, `bytes=500-`, `bytes=-500`, and multi-range (`bytes=0-499, 1000-1499`). Invalid ranges return 416 Range Not Satisfiable.
358
+
359
+ #### Compression with Brotli
360
+
361
+ The compression plugin prefers Brotli over gzip when the client supports it, providing better compression ratios for text content:
362
+
363
+ ```typescript
364
+ import { compress } from '@celsian/compress';
365
+
366
+ // Brotli + gzip + deflate (default)
367
+ await app.register(compress());
368
+
369
+ // Custom Brotli quality (0-11, default 4)
370
+ await app.register(compress({ brotliQuality: 6 }));
371
+
372
+ // Gzip/deflate only (disable Brotli)
373
+ await app.register(compress({ encodings: ['gzip', 'deflate'] }));
374
+ ```
375
+
376
+ ### Error Handling
377
+
378
+ Thrown errors are caught and returned as structured JSON. Stack traces are stripped in production.
379
+
380
+ ```typescript
381
+ import { HttpError } from '@celsian/core';
382
+
383
+ // Throw HTTP errors anywhere
384
+ throw new HttpError(403, 'Forbidden');
385
+ // { "error": "Forbidden", "statusCode": 403, "code": "FORBIDDEN" }
386
+
387
+ // Custom error handler
388
+ app.setErrorHandler((error, req, reply) => {
389
+ if (error.message.includes('UNIQUE constraint')) return reply.conflict();
390
+ return reply.internalServerError();
391
+ });
392
+ ```
393
+
394
+ ### Type-Safe RPC
395
+
396
+ `@celsian/rpc` provides tRPC-style procedures with type inference, middleware, and OpenAPI generation.
397
+
398
+ ```typescript
399
+ // server.ts
400
+ import { procedure, router, RPCHandler } from '@celsian/rpc';
401
+
402
+ const appRouter = router({
403
+ users: {
404
+ list: procedure
405
+ .input(z.object({ limit: z.number().optional() }))
406
+ .query(async ({ input }) => [{ id: '1', name: 'Alice' }]),
407
+ create: procedure
408
+ .input(z.object({ name: z.string(), email: z.string().email() }))
409
+ .mutation(async ({ input }) => ({ id: '2', ...input })),
410
+ },
411
+ });
412
+
413
+ const rpc = new RPCHandler(appRouter);
414
+ app.route({ method: ['GET', 'POST'], url: '/_rpc/*path', handler: (req) => rpc.handle(req) });
415
+ export type AppRouter = typeof appRouter;
416
+
417
+ // client.ts
418
+ const client = createRPCClient<AppRouter>({ baseUrl: 'http://localhost:3000/_rpc' });
419
+ const users = await client.users.list.query({ limit: 10 });
420
+ const newUser = await client.users.create.mutate({ name: 'Bob', email: 'bob@example.com' });
421
+ ```
422
+
423
+ ## Try the Demo
424
+
425
+ The [SaaS Demo](examples/saas-demo/) builds a complete backend in one file (~250 lines): JWT auth, users CRUD, background tasks, cron, SSE, and OpenAPI docs.
426
+
427
+ ```bash
428
+ cd examples/saas-demo
429
+ npm install
430
+ npx tsx src/index.ts
431
+ ```
432
+
433
+ Then hit `http://localhost:3000/docs` for the Swagger UI, or:
434
+
435
+ ```bash
436
+ # Register
437
+ curl -X POST http://localhost:3000/register \
438
+ -H 'Content-Type: application/json' \
439
+ -d '{"email":"alice@example.com","password":"secret123","name":"Alice"}'
440
+
441
+ # Login and grab the token
442
+ curl -X POST http://localhost:3000/login \
443
+ -H 'Content-Type: application/json' \
444
+ -d '{"email":"alice@example.com","password":"secret123"}'
445
+ ```
446
+
447
+ ## Ecosystem
448
+
449
+ ### Core Packages
450
+
451
+ | Package | Description |
452
+ | ------- | ----------- |
453
+ | `@celsian/core` | Server runtime, routing, hooks, plugins, task queue, cron, WebSocket, CORS, security, database, OpenAPI |
454
+ | `@celsian/schema` | Standard Schema adapters -- auto-detects Zod, TypeBox, Valibot |
455
+ | `@celsian/rpc` | Type-safe RPC procedures, middleware, OpenAPI generation, typed client |
456
+ | `@celsian/jwt` | JWT sign/verify plugin with route guard helper |
457
+ | `@celsian/cache` | KV store, response caching, session management |
458
+ | `@celsian/rate-limit` | Fixed-window rate limiter with pluggable store |
459
+ | `@celsian/compress` | Response compression (Brotli, gzip, deflate) |
460
+ | `@celsian/queue-redis` | Redis-backed task queue for production |
461
+
462
+ ### Deployment Adapters
463
+
464
+ | Package | Target |
465
+ | ------- | ------ |
466
+ | `@celsian/adapter-cloudflare` | Cloudflare Workers (env bindings, execution context) |
467
+ | `@celsian/adapter-lambda` | AWS Lambda + API Gateway v2 |
468
+ | `@celsian/adapter-vercel` | Vercel Serverless + Edge Functions |
469
+ | `@celsian/adapter-node` | Standalone Node.js server |
470
+ | `@celsian/adapter-fly` | Fly.io (generates fly.toml, Dockerfile, multi-region) |
471
+ | `@celsian/adapter-railway` | Railway (generates railway.json, Procfile) |
472
+
473
+ ### Tooling
474
+
475
+ | Package | Description |
476
+ | ------- | ----------- |
477
+ | `create-celsian` | Project scaffolder (`npx create-celsian my-api`) |
478
+ | `@celsian/cli` | Dev server, route listing, code generation |
479
+ | `celsian` | Meta-package for single-import convenience |
480
+
481
+ ## Production Features
482
+
483
+ ### Graceful Shutdown
484
+
485
+ On SIGTERM/SIGINT: stops accepting connections, drains in-flight requests, stops workers and cron, runs cleanup.
486
+
487
+ ```typescript
488
+ serve(app, {
489
+ shutdownTimeout: 15_000,
490
+ onShutdown: () => db.close(),
491
+ });
492
+ ```
493
+
494
+ ### Health Checks and Route Manifest
495
+
496
+ ```typescript
497
+ app.health({ check: () => pool.isHealthy() }); // /health + /ready
498
+
499
+ // Tag routes for deployment tooling
500
+ app.route({ method: 'GET', url: '/api/users', kind: 'serverless', handler });
501
+ app.route({ method: 'GET', url: '/ws', kind: 'hot', handler });
502
+ const manifest = app.getRouteManifest(); // { serverless: [...], hot: [...], task: [...] }
503
+ ```
504
+
505
+ ### Database Analytics
506
+
507
+ Wrap your pool with `trackedPool()` for per-request query metrics, `Server-Timing` headers, and slow query logging -- zero handler changes. See [Database Plugin](docs/database.md).
508
+
509
+ ```typescript
510
+ const pool = trackedPool(pgPool);
511
+ await app.register(database({ createPool: () => pool }), { encapsulate: false });
512
+ await app.register(dbAnalytics({ slowThreshold: 100 }), { encapsulate: false });
513
+ // Response: Server-Timing: db;dur=12.5;desc="3 queries"
514
+ ```
515
+
516
+ ### Testing Without a Server
517
+
518
+ ```typescript
519
+ const response = await app.inject({ method: 'GET', url: '/hello' });
520
+ const body = await response.json(); // { hello: 'world' }
521
+ ```
522
+
523
+ ## Deployment
524
+
525
+ Swap the entry point for the JavaScript runtime you are deploying to. See [Deployment Guide](docs/deployment.md) for full instructions and provider-specific credential/config requirements.
526
+
527
+ ```typescript
528
+ serve(app, { port: 3000 }); // Node / Bun / Deno
529
+
530
+ export default createCloudflareHandler(app); // Cloudflare Workers
531
+ export const handler = createLambdaHandler(app); // AWS Lambda
532
+ export default createVercelHandler(app); // Vercel Serverless
533
+ export default createVercelEdgeHandler(app); // Vercel Edge
534
+ ```
535
+
536
+ Fly.io and Railway adapters auto-generate deployment configs (`fly.toml`, Dockerfile, `railway.json`). Adapter packages are covered by local generation/package smoke tests; live cloud deployment still depends on each provider's credentials and project configuration.
537
+
538
+ ## Benchmark Results
539
+
540
+ Node.js v22.13.1, macOS Darwin (Apple Silicon), 10 connections, 10s per scenario.
541
+
542
+ | Scenario | Fastify (req/s) | CelsianJS (req/s) | Express (req/s) |
543
+ | --------------------- | ---------------: | -----------------: | --------------: |
544
+ | JSON response | 45,866 | 27,996 | 16,321 |
545
+ | Route params | 45,440 | 27,026 | 16,288 |
546
+ | Middleware (5) | 41,380 | 24,445 | 15,751 |
547
+ | JSON body parsing | 29,998 | 19,074 | 14,648 |
548
+ | Error handling | 32,398 | 18,542 | 14,765 |
549
+
550
+ Fastify is the fastest Node.js framework. CelsianJS is 1.3-1.7x faster than Express. The gap with Fastify comes from Web Standard API overhead (`Request`/`Response` per request). CelsianJS trades some throughput for multi-runtime portability and built-in application infrastructure.
551
+
552
+ ## Configuration
553
+
554
+ CelsianJS loads `celsian.config.ts` (or `.js`/`.mjs`) automatically:
555
+
556
+ ```typescript
557
+ import { defineConfig } from '@celsian/core';
558
+
559
+ export default defineConfig({
560
+ server: { port: 3000, host: 'localhost', trustProxy: true },
561
+ schema: { provider: 'auto' }, // or 'zod' | 'typebox' | 'valibot'
562
+ });
563
+ ```
564
+
565
+ ## Documentation
566
+
567
+ - [Quick Start Guide](docs/quickstart.md)
568
+ - [Hooks Lifecycle](docs/hooks.md)
569
+ - [Plugins and Encapsulation](docs/plugins.md)
570
+ - [Deployment Guide](docs/deployment.md)
571
+ - [Database Plugin](docs/database.md)
572
+
573
+ ## WhatStack
574
+
575
+ CelsianJS is the backend half of [WhatStack](https://whatfw.com) — the agent-first full-stack framework:
576
+
577
+ | Layer | Framework | What It Does |
578
+ |-------|-----------|-------------|
579
+ | Frontend | [WhatFW](https://whatfw.com) | Signals, fine-grained rendering, MCP DevTools |
580
+ | Backend | **CelsianJS** | Hooks, plugins, tasks, cron, RPC, multi-runtime |
581
+ | Deploy | [Vura](https://github.com/zvndev/vura) | Platform deployment (coming soon) |
582
+
583
+ ## Contributing
584
+
585
+ ```bash
586
+ git clone https://github.com/CelsianJs/celsian.git
587
+ cd celsian
588
+ pnpm install
589
+ pnpm test
590
+ ```
591
+
592
+ The project uses pnpm workspaces. All packages are in `packages/`. Tests use Vitest.
593
+
594
+ ## License
595
+
596
+ MIT
package/dist/index.js CHANGED
File without changes
@@ -9,7 +9,7 @@ export const basicTemplate = {
9
9
  start: "node dist/index.js",
10
10
  },
11
11
  dependencies: {
12
- celsian: "latest",
12
+ celsian: "^0.3.6",
13
13
  },
14
14
  devDependencies: {
15
15
  typescript: "^5.7.0",
@@ -11,11 +11,11 @@ export const fullTemplate = {
11
11
  lint: "npx tsc --noEmit",
12
12
  },
13
13
  dependencies: {
14
- celsian: "latest",
15
- "@celsian/core": "latest",
16
- "@celsian/jwt": "latest",
17
- "@celsian/rpc": "latest",
18
- "@celsian/rate-limit": "latest",
14
+ celsian: "^0.3.6",
15
+ "@celsian/core": "^0.3.6",
16
+ "@celsian/jwt": "^0.3.6",
17
+ "@celsian/rpc": "^0.3.6",
18
+ "@celsian/rate-limit": "^0.3.6",
19
19
  "@sinclair/typebox": "^0.34.0",
20
20
  },
21
21
  devDependencies: {
@@ -9,7 +9,7 @@ export const restApiTemplate = {
9
9
  start: "node dist/index.js",
10
10
  },
11
11
  dependencies: {
12
- celsian: "latest",
12
+ celsian: "^0.3.6",
13
13
  "@sinclair/typebox": "^0.34.0",
14
14
  },
15
15
  devDependencies: {
@@ -9,8 +9,8 @@ export const rpcApiTemplate = {
9
9
  start: "node dist/index.js",
10
10
  },
11
11
  dependencies: {
12
- celsian: "latest",
13
- "@celsian/rpc": "latest",
12
+ celsian: "^0.3.6",
13
+ "@celsian/rpc": "^0.3.6",
14
14
  "@sinclair/typebox": "^0.34.0",
15
15
  },
16
16
  devDependencies: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-celsian",
3
- "version": "0.3.5",
3
+ "version": "0.3.8",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-celsian": "dist/index.js"
@@ -10,9 +10,6 @@
10
10
  "files": [
11
11
  "dist"
12
12
  ],
13
- "scripts": {
14
- "build": "tsc -b"
15
- },
16
13
  "license": "MIT",
17
14
  "engines": {
18
15
  "node": ">=20"
@@ -33,5 +30,8 @@
33
30
  "scaffold",
34
31
  "cli",
35
32
  "typescript"
36
- ]
37
- }
33
+ ],
34
+ "scripts": {
35
+ "build": "tsc -b"
36
+ }
37
+ }