@spfn/core 0.2.0-beta.2 → 0.2.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +262 -1092
  2. package/dist/{boss-D-fGtVgM.d.ts → boss-DI1r4kTS.d.ts} +68 -11
  3. package/dist/codegen/index.d.ts +55 -8
  4. package/dist/codegen/index.js +179 -5
  5. package/dist/codegen/index.js.map +1 -1
  6. package/dist/config/index.d.ts +204 -6
  7. package/dist/config/index.js +44 -11
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/db/index.d.ts +13 -0
  10. package/dist/db/index.js +92 -33
  11. package/dist/db/index.js.map +1 -1
  12. package/dist/env/index.d.ts +83 -3
  13. package/dist/env/index.js +83 -15
  14. package/dist/env/index.js.map +1 -1
  15. package/dist/env/loader.d.ts +95 -0
  16. package/dist/env/loader.js +78 -0
  17. package/dist/env/loader.js.map +1 -0
  18. package/dist/event/index.d.ts +3 -70
  19. package/dist/event/index.js +10 -1
  20. package/dist/event/index.js.map +1 -1
  21. package/dist/event/sse/client.d.ts +131 -0
  22. package/dist/event/sse/client.js +150 -0
  23. package/dist/event/sse/client.js.map +1 -0
  24. package/dist/event/sse/index.d.ts +46 -0
  25. package/dist/event/sse/index.js +205 -0
  26. package/dist/event/sse/index.js.map +1 -0
  27. package/dist/job/index.d.ts +54 -8
  28. package/dist/job/index.js +61 -12
  29. package/dist/job/index.js.map +1 -1
  30. package/dist/middleware/index.d.ts +124 -11
  31. package/dist/middleware/index.js +41 -7
  32. package/dist/middleware/index.js.map +1 -1
  33. package/dist/nextjs/index.d.ts +2 -2
  34. package/dist/nextjs/index.js +37 -5
  35. package/dist/nextjs/index.js.map +1 -1
  36. package/dist/nextjs/server.d.ts +70 -17
  37. package/dist/nextjs/server.js +107 -36
  38. package/dist/nextjs/server.js.map +1 -1
  39. package/dist/route/index.d.ts +207 -14
  40. package/dist/route/index.js +304 -31
  41. package/dist/route/index.js.map +1 -1
  42. package/dist/route/types.d.ts +2 -31
  43. package/dist/router-Di7ENoah.d.ts +151 -0
  44. package/dist/server/index.d.ts +321 -10
  45. package/dist/server/index.js +798 -189
  46. package/dist/server/index.js.map +1 -1
  47. package/dist/{types-DRG2XMTR.d.ts → types-7Mhoxnnt.d.ts} +97 -4
  48. package/dist/types-B-lVqv6b.d.ts +298 -0
  49. package/docs/cache.md +133 -0
  50. package/docs/codegen.md +74 -0
  51. package/docs/database.md +346 -0
  52. package/docs/entity.md +539 -0
  53. package/docs/env.md +499 -0
  54. package/docs/errors.md +319 -0
  55. package/docs/event.md +423 -0
  56. package/docs/file-upload.md +717 -0
  57. package/docs/job.md +131 -0
  58. package/docs/logger.md +108 -0
  59. package/docs/middleware.md +337 -0
  60. package/docs/nextjs.md +241 -0
  61. package/docs/repository.md +496 -0
  62. package/docs/route.md +497 -0
  63. package/docs/server.md +377 -0
  64. package/package.json +19 -3
package/docs/server.md ADDED
@@ -0,0 +1,377 @@
1
+ # Server
2
+
3
+ HTTP server configuration with three-level progressive customization.
4
+
5
+ ## Quick Start
6
+
7
+ ```typescript
8
+ // src/server/index.ts
9
+ import { startServer } from '@spfn/core/server';
10
+
11
+ await startServer();
12
+ ```
13
+
14
+ ---
15
+
16
+ ## Configuration Levels
17
+
18
+ ### Level 1: Zero Config
19
+
20
+ No configuration needed. Uses sensible defaults:
21
+
22
+ ```typescript
23
+ import { startServer } from '@spfn/core/server';
24
+
25
+ await startServer();
26
+ // Port: 4000 (or process.env.PORT)
27
+ // Host: localhost (or process.env.HOST)
28
+ // Middleware: Logger + CORS + ErrorHandler
29
+ // Infrastructure: Auto-init from env vars
30
+ ```
31
+
32
+ ### Level 2: Partial Customization
33
+
34
+ Create `src/server/server.config.ts`:
35
+
36
+ ```typescript
37
+ import { defineServerConfig, defineRouter } from '@spfn/core/server';
38
+ import * as userRoutes from './routes/users';
39
+ import * as postRoutes from './routes/posts';
40
+ import { authMiddleware, rateLimiter } from './middlewares';
41
+
42
+ const appRouter = defineRouter({
43
+ ...userRoutes,
44
+ ...postRoutes
45
+ });
46
+
47
+ export default defineServerConfig()
48
+ .port(8790)
49
+ .host('0.0.0.0')
50
+ .routes(appRouter)
51
+ .middlewares([authMiddleware, rateLimiter])
52
+ .build();
53
+
54
+ export type AppRouter = typeof appRouter;
55
+ ```
56
+
57
+ ### Level 3: Full Control
58
+
59
+ Create `src/server/app.ts` for custom Hono setup:
60
+
61
+ ```typescript
62
+ import { Hono } from 'hono';
63
+ import { timing } from 'hono/timing';
64
+ import { compress } from 'hono/compress';
65
+ import type { AppFactory } from '@spfn/core/server';
66
+
67
+ export default (async () => {
68
+ const app = new Hono();
69
+
70
+ // Custom middleware
71
+ app.use('*', timing());
72
+ app.use('*', compress());
73
+
74
+ // Custom routes
75
+ app.get('/custom', (c) => c.json({ custom: true }));
76
+
77
+ return app;
78
+ }) satisfies AppFactory;
79
+ ```
80
+
81
+ Then in `server.config.ts`:
82
+
83
+ ```typescript
84
+ export default defineServerConfig()
85
+ .routes(appRouter) // Routes registered to your custom app
86
+ .build();
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Configuration Builder
92
+
93
+ ### Basic Options
94
+
95
+ ```typescript
96
+ defineServerConfig()
97
+ .port(8790) // Server port
98
+ .host('0.0.0.0') // Server host
99
+ .routes(appRouter) // Router
100
+ .middlewares([...]) // Global middlewares
101
+ .build();
102
+ ```
103
+
104
+ ### Infrastructure Options
105
+
106
+ ```typescript
107
+ defineServerConfig()
108
+ .database(true) // Enable database (default: auto from env)
109
+ .redis(true) // Enable Redis (default: auto from env)
110
+ .build();
111
+ ```
112
+
113
+ ### CORS Options
114
+
115
+ ```typescript
116
+ defineServerConfig()
117
+ .cors({
118
+ origin: ['https://example.com'],
119
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
120
+ credentials: true
121
+ })
122
+ .build();
123
+ ```
124
+
125
+ ### Lifecycle Hooks
126
+
127
+ ```typescript
128
+ defineServerConfig()
129
+ .beforeStart(async () => {
130
+ // Before server starts
131
+ console.log('Initializing...');
132
+ })
133
+ .afterStart(async (server) => {
134
+ // After server is running
135
+ console.log(`Server running on port ${server.port}`);
136
+ })
137
+ .beforeShutdown(async () => {
138
+ // Before graceful shutdown
139
+ console.log('Shutting down...');
140
+ })
141
+ .build();
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Router
147
+
148
+ ### Define Router
149
+
150
+ ```typescript
151
+ import { defineRouter } from '@spfn/core/route';
152
+
153
+ export const appRouter = defineRouter({
154
+ getUser,
155
+ createUser,
156
+ updateUser,
157
+ deleteUser
158
+ });
159
+ ```
160
+
161
+ ### Nested Routers
162
+
163
+ ```typescript
164
+ export const appRouter = defineRouter({
165
+ users: defineRouter({
166
+ get: getUser,
167
+ create: createUser,
168
+ list: getUsers
169
+ }),
170
+ posts: defineRouter({
171
+ get: getPost,
172
+ create: createPost
173
+ })
174
+ });
175
+ ```
176
+
177
+ ### Type Export
178
+
179
+ ```typescript
180
+ // server.config.ts
181
+ export type AppRouter = typeof appRouter;
182
+
183
+ // Use in Next.js client
184
+ import type { AppRouter } from '@/server/server.config';
185
+ import { createApi } from '@spfn/core/nextjs';
186
+
187
+ const api = createApi<AppRouter>();
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Graceful Shutdown
193
+
194
+ Automatic graceful shutdown with drain behavior (AWS drain style):
195
+
196
+ ```
197
+ SIGTERM
198
+
199
+ ├─ Phase 1: server.close() ← 새 연결 거부, 진행 중 요청 대기
200
+ ├─ Phase 2: stopBoss() ← pg-boss 작업 정리
201
+ ├─ Phase 3: ShutdownManager.execute()
202
+ │ ├─ drain: tracked operations 완료 대기
203
+ │ └─ hooks: 등록된 훅 순서대로 실행
204
+ ├─ Phase 4: beforeShutdown lifecycle ← 기존 lifecycle 호환
205
+ ├─ Phase 5: closeDatabase / closeCache
206
+ └─ process.exit(0)
207
+ ```
208
+
209
+ **Signals handled:** `SIGTERM`, `SIGINT`
210
+
211
+ ### ShutdownManager
212
+
213
+ 모듈별 독립적인 shutdown 훅 등록과 장기 작업 추적:
214
+
215
+ ```typescript
216
+ import { getShutdownManager } from '@spfn/core/server';
217
+
218
+ const shutdown = getShutdownManager();
219
+
220
+ // 1. Shutdown 훅 등록 — 모듈별 독립 cleanup
221
+ shutdown.onShutdown('ai-client', async () =>
222
+ {
223
+ await openaiClient.close();
224
+ }, { timeout: 5000, order: 10 });
225
+
226
+ shutdown.onShutdown('search-index', async () =>
227
+ {
228
+ await elasticClient.close();
229
+ }, { order: 20 });
230
+
231
+ // 2. 장기 작업 추적 — shutdown 시 완료까지 대기
232
+ const result = await shutdown.trackOperation(
233
+ 'ai-generate',
234
+ aiService.generate(prompt)
235
+ );
236
+ // shutdown 중이면 자동으로 throw → 503
237
+
238
+ // 3. Shutdown 상태 확인
239
+ if (shutdown.isShuttingDown())
240
+ {
241
+ return c.json({ error: 'Server is shutting down' }, 503);
242
+ }
243
+ ```
244
+
245
+ ### Timeout Configuration
246
+
247
+ ```typescript
248
+ defineServerConfig()
249
+ .shutdown({
250
+ timeout: 280000, // 280s (default: k8s 300s - 5s preStop - 15s margin)
251
+ })
252
+ .build();
253
+ ```
254
+
255
+ AI 파이프라인 등 장기 작업이 있는 앱은 Helm chart과 함께 조정:
256
+
257
+ ```yaml
258
+ # apps/values.yaml
259
+ gracefulShutdown:
260
+ terminationGracePeriodSeconds: 660 # 11분
261
+ ```
262
+
263
+ ```bash
264
+ SHUTDOWN_TIMEOUT=640000 # 660 - 5 - 15 = 640s
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Health Check
270
+
271
+ Built-in health endpoint at `/health`:
272
+
273
+ ```bash
274
+ # Normal
275
+ curl http://localhost:8790/health
276
+ # { "status": "ok", "timestamp": "2024-..." }
277
+
278
+ # During shutdown → 503
279
+ # { "status": "shutting_down", "timestamp": "2024-..." }
280
+ ```
281
+
282
+ ---
283
+
284
+ ## Environment Variables
285
+
286
+ ```bash
287
+ # Server
288
+ PORT=8790
289
+ HOST=localhost
290
+ NODE_ENV=development
291
+
292
+ # Database
293
+ DATABASE_URL=postgresql://localhost:5432/mydb
294
+ DATABASE_WRITE_URL=postgresql://primary:5432/mydb
295
+ DATABASE_READ_URL=postgresql://replica:5432/mydb
296
+
297
+ # Redis
298
+ REDIS_URL=redis://localhost:6379
299
+
300
+ # Shutdown
301
+ SHUTDOWN_TIMEOUT=280000 # Milliseconds (default: 280s)
302
+ ```
303
+
304
+ ---
305
+
306
+ ## File Structure
307
+
308
+ ```
309
+ src/server/
310
+ ├── server.config.ts # Configuration (Level 2)
311
+ ├── app.ts # Custom Hono app (Level 3, optional)
312
+ ├── index.ts # Entry point
313
+ ├── entities/ # Database schema
314
+ ├── repositories/ # Data access
315
+ ├── routes/ # API routes
316
+ └── middlewares/ # Custom middleware
317
+ ```
318
+
319
+ ---
320
+
321
+ ## Startup Banner
322
+
323
+ On startup, server displays:
324
+
325
+ ```
326
+ ╭──────────────────────────────────────╮
327
+ │ │
328
+ │ SPFN Server v1.0.0 │
329
+ │ │
330
+ │ http://localhost:8790 │
331
+ │ │
332
+ │ Press Ctrl+C to stop │
333
+ │ │
334
+ ╰──────────────────────────────────────╯
335
+
336
+ ✓ Database connected
337
+ ✓ Redis connected
338
+ ✓ 12 routes registered
339
+ ```
340
+
341
+ ---
342
+
343
+ ## Best Practices
344
+
345
+ ### Do
346
+
347
+ ```typescript
348
+ // 1. Export router type for client usage
349
+ export type AppRouter = typeof appRouter;
350
+
351
+ // 2. Use lifecycle hooks for initialization
352
+ .beforeStart(async () => {
353
+ await warmupCache();
354
+ })
355
+
356
+ // 3. Register global middlewares
357
+ .middlewares([authMiddleware, rateLimiter])
358
+
359
+ // 4. Use Level 2 for most projects
360
+ defineServerConfig()
361
+ .port(8790)
362
+ .routes(appRouter)
363
+ .build();
364
+ ```
365
+
366
+ ### Don't
367
+
368
+ ```typescript
369
+ // 1. Don't hardcode port in production
370
+ .port(8790) // Use process.env.PORT instead
371
+
372
+ // 2. Don't skip database auto-init unless needed
373
+ .database(false) // Usually let it auto-detect
374
+
375
+ // 3. Don't use Level 3 unless necessary
376
+ // Level 2 covers most use cases
377
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spfn/core",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.20",
4
4
  "description": "SPFN Framework Core - File-based routing, transactions, repository pattern",
5
5
  "type": "module",
6
6
  "exports": {
@@ -64,6 +64,11 @@
64
64
  "import": "./dist/env/index.js",
65
65
  "require": "./dist/env/index.js"
66
66
  },
67
+ "./env/loader": {
68
+ "types": "./dist/env/loader.d.ts",
69
+ "import": "./dist/env/loader.js",
70
+ "require": "./dist/env/loader.js"
71
+ },
67
72
  "./logger": {
68
73
  "types": "./dist/logger/index.d.ts",
69
74
  "import": "./dist/logger/index.js",
@@ -83,6 +88,16 @@
83
88
  "types": "./dist/event/index.d.ts",
84
89
  "import": "./dist/event/index.js",
85
90
  "require": "./dist/event/index.js"
91
+ },
92
+ "./event/sse": {
93
+ "types": "./dist/event/sse/index.d.ts",
94
+ "import": "./dist/event/sse/index.js",
95
+ "require": "./dist/event/sse/index.js"
96
+ },
97
+ "./event/sse/client": {
98
+ "types": "./dist/event/sse/client.d.ts",
99
+ "import": "./dist/event/sse/client.js",
100
+ "require": "./dist/event/sse/client.js"
86
101
  }
87
102
  },
88
103
  "keywords": [
@@ -137,6 +152,7 @@
137
152
  "pg-boss": "^11.1.2",
138
153
  "postgres": "^3.4.0",
139
154
  "typescript": "^5.3.3",
155
+ "undici": "^7.22.0",
140
156
  "zod": "^4.1.11"
141
157
  },
142
158
  "optionalDependencies": {
@@ -148,7 +164,7 @@
148
164
  "@vitest/coverage-v8": "^4.0.6",
149
165
  "drizzle-kit": "^0.31.6",
150
166
  "madge": "^8.0.0",
151
- "next": "16.0.7",
167
+ "next": "^16.0.0",
152
168
  "tsup": "^8.5.0",
153
169
  "vitest": "^4.0.6"
154
170
  },
@@ -168,7 +184,7 @@
168
184
  ],
169
185
  "publishConfig": {
170
186
  "access": "public",
171
- "tag": "alpha"
187
+ "tag": "beta"
172
188
  },
173
189
  "scripts": {
174
190
  "build": "pnpm check:circular && tsup",