@spfn/core 0.2.0-beta.1 → 0.2.0-beta.10

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 +159 -5
  5. package/dist/codegen/index.js.map +1 -1
  6. package/dist/config/index.d.ts +36 -0
  7. package/dist/config/index.js +15 -6
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/db/index.d.ts +13 -0
  10. package/dist/db/index.js +40 -6
  11. package/dist/db/index.js.map +1 -1
  12. package/dist/env/index.d.ts +82 -3
  13. package/dist/env/index.js +81 -14
  14. package/dist/env/index.js.map +1 -1
  15. package/dist/env/loader.d.ts +87 -0
  16. package/dist/env/loader.js +70 -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 +82 -0
  22. package/dist/event/sse/client.js +115 -0
  23. package/dist/event/sse/client.js.map +1 -0
  24. package/dist/event/sse/index.d.ts +40 -0
  25. package/dist/event/sse/index.js +92 -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 +102 -11
  31. package/dist/middleware/index.js +2 -2
  32. package/dist/middleware/index.js.map +1 -1
  33. package/dist/nextjs/index.d.ts +2 -2
  34. package/dist/nextjs/index.js +36 -4
  35. package/dist/nextjs/index.js.map +1 -1
  36. package/dist/nextjs/server.d.ts +62 -15
  37. package/dist/nextjs/server.js +102 -33
  38. package/dist/nextjs/server.js.map +1 -1
  39. package/dist/route/index.d.ts +227 -15
  40. package/dist/route/index.js +307 -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 +153 -6
  45. package/dist/server/index.js +216 -14
  46. package/dist/server/index.js.map +1 -1
  47. package/dist/types-B-e_f2dQ.d.ts +121 -0
  48. package/dist/{types-DRG2XMTR.d.ts → types-BOPTApC2.d.ts} +91 -3
  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 +477 -0
  54. package/docs/errors.md +319 -0
  55. package/docs/event.md +116 -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 +307 -0
  64. package/package.json +18 -3
@@ -0,0 +1,346 @@
1
+ # Database
2
+
3
+ PostgreSQL database layer with Drizzle ORM, automatic transaction management, and read/write separation.
4
+
5
+ ## Setup
6
+
7
+ ### Environment Variables
8
+
9
+ ```bash
10
+ # Single database
11
+ DATABASE_URL=postgresql://localhost:5432/mydb
12
+
13
+ # Primary + Replica (recommended for production)
14
+ DATABASE_WRITE_URL=postgresql://primary:5432/mydb
15
+ DATABASE_READ_URL=postgresql://replica:5432/mydb
16
+ ```
17
+
18
+ ### Initialize
19
+
20
+ ```typescript
21
+ import { initDatabase } from '@spfn/core/db';
22
+
23
+ // Called automatically by startServer()
24
+ // Manual call only needed for scripts
25
+ await initDatabase();
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Helper Functions
31
+
32
+ Standalone functions for simple database operations.
33
+
34
+ ```typescript
35
+ import {
36
+ findOne,
37
+ findMany,
38
+ create,
39
+ createMany,
40
+ upsert,
41
+ updateOne,
42
+ updateMany,
43
+ deleteOne,
44
+ deleteMany,
45
+ count
46
+ } from '@spfn/core/db';
47
+ ```
48
+
49
+ ### findOne
50
+
51
+ Find a single record.
52
+
53
+ ```typescript
54
+ // Object-based where (simple equality)
55
+ const user = await findOne(users, { id: '1' });
56
+ const user = await findOne(users, { email: 'test@example.com' });
57
+
58
+ // SQL-based where (complex conditions)
59
+ import { eq, and, gt } from 'drizzle-orm';
60
+ const user = await findOne(users, and(
61
+ eq(users.email, 'test@example.com'),
62
+ eq(users.isActive, true)
63
+ ));
64
+ ```
65
+
66
+ ### findMany
67
+
68
+ Find multiple records with filtering, ordering, and pagination.
69
+
70
+ ```typescript
71
+ // Simple
72
+ const allUsers = await findMany(users);
73
+
74
+ // With options
75
+ const activeUsers = await findMany(users, {
76
+ where: { isActive: true },
77
+ orderBy: desc(users.createdAt),
78
+ limit: 10,
79
+ offset: 0
80
+ });
81
+
82
+ // Complex where
83
+ const recentAdmins = await findMany(users, {
84
+ where: and(
85
+ eq(users.role, 'admin'),
86
+ gt(users.createdAt, lastWeek)
87
+ ),
88
+ orderBy: [desc(users.createdAt), asc(users.name)],
89
+ limit: 20
90
+ });
91
+ ```
92
+
93
+ ### create
94
+
95
+ Create a single record.
96
+
97
+ ```typescript
98
+ const user = await create(users, {
99
+ email: 'new@example.com',
100
+ name: 'New User'
101
+ });
102
+ ```
103
+
104
+ ### createMany
105
+
106
+ Create multiple records.
107
+
108
+ ```typescript
109
+ const newUsers = await createMany(users, [
110
+ { email: 'user1@example.com', name: 'User 1' },
111
+ { email: 'user2@example.com', name: 'User 2' }
112
+ ]);
113
+ ```
114
+
115
+ ### upsert
116
+
117
+ Insert or update on conflict.
118
+
119
+ ```typescript
120
+ const cache = await upsert(cmsCache, data, {
121
+ target: [cmsCache.section, cmsCache.locale],
122
+ set: {
123
+ content: data.content,
124
+ updatedAt: new Date()
125
+ }
126
+ });
127
+ ```
128
+
129
+ ### updateOne
130
+
131
+ Update a single record. Returns updated record or null.
132
+
133
+ ```typescript
134
+ const updated = await updateOne(users, { id: '1' }, { name: 'Updated Name' });
135
+ if (!updated)
136
+ {
137
+ throw new Error('User not found');
138
+ }
139
+ ```
140
+
141
+ ### updateMany
142
+
143
+ Update multiple records. Returns array of updated records.
144
+
145
+ ```typescript
146
+ const updated = await updateMany(
147
+ users,
148
+ { role: 'guest' },
149
+ { isActive: false }
150
+ );
151
+ ```
152
+
153
+ ### deleteOne
154
+
155
+ Delete a single record. Returns deleted record or null.
156
+
157
+ ```typescript
158
+ const deleted = await deleteOne(users, { id: '1' });
159
+ ```
160
+
161
+ ### deleteMany
162
+
163
+ Delete multiple records. Returns array of deleted records.
164
+
165
+ ```typescript
166
+ const deleted = await deleteMany(users, { isActive: false });
167
+ ```
168
+
169
+ ### count
170
+
171
+ Count records.
172
+
173
+ ```typescript
174
+ const total = await count(users);
175
+ const activeCount = await count(users, { isActive: true });
176
+ const adminCount = await count(users, eq(users.role, 'admin'));
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Transaction
182
+
183
+ ### Transactional Middleware
184
+
185
+ Use in routes for automatic commit/rollback.
186
+
187
+ ```typescript
188
+ import { Transactional } from '@spfn/core/db';
189
+
190
+ route.post('/users')
191
+ .use([Transactional()])
192
+ .handler(async (c) => {
193
+ // Auto commit on success
194
+ // Auto rollback on error
195
+ return userRepo.create(body);
196
+ });
197
+ ```
198
+
199
+ **With options:**
200
+
201
+ ```typescript
202
+ Transactional({
203
+ timeout: 30000, // Transaction timeout (ms)
204
+ logSuccess: false, // Log successful transactions
205
+ logErrors: true // Log failed transactions
206
+ })
207
+ ```
208
+
209
+ ### Manual Transaction
210
+
211
+ For complex multi-operation scenarios.
212
+
213
+ ```typescript
214
+ import { runWithTransaction } from '@spfn/core/db';
215
+
216
+ await runWithTransaction(async () => {
217
+ const user = await userRepo.create(userData);
218
+ await profileRepo.create({ userId: user.id, ...profileData });
219
+ await emailService.sendWelcome(user.email);
220
+ // All succeed or all rollback
221
+ });
222
+ ```
223
+
224
+ ### Get Current Transaction
225
+
226
+ Access the current transaction context.
227
+
228
+ ```typescript
229
+ import { getTransaction } from '@spfn/core/db';
230
+
231
+ async function customDbOperation()
232
+ {
233
+ const tx = getTransaction();
234
+ if (tx)
235
+ {
236
+ // Inside transaction
237
+ await tx.insert(users).values(data);
238
+ }
239
+ else
240
+ {
241
+ // Not in transaction
242
+ const db = getDatabase('write');
243
+ await db.insert(users).values(data);
244
+ }
245
+ }
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Direct Database Access
251
+
252
+ For complex queries not covered by helpers.
253
+
254
+ ```typescript
255
+ import { getDatabase } from '@spfn/core/db';
256
+
257
+ // Read operations (uses replica if available)
258
+ const db = getDatabase('read');
259
+ const results = await db
260
+ .select({
261
+ user: users,
262
+ postsCount: sql`count(${posts.id})`
263
+ })
264
+ .from(users)
265
+ .leftJoin(posts, eq(users.id, posts.authorId))
266
+ .groupBy(users.id);
267
+
268
+ // Write operations (always uses primary)
269
+ const db = getDatabase('write');
270
+ await db.insert(users).values(data);
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Connection Info
276
+
277
+ ```typescript
278
+ import { getDatabaseInfo, checkConnection } from '@spfn/core/db';
279
+
280
+ // Get connection status
281
+ const info = getDatabaseInfo();
282
+ // { hasWriteDb: true, hasReadDb: true, pattern: 'write-read' }
283
+
284
+ // Health check
285
+ const isHealthy = await checkConnection(getDatabase('write'));
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Cleanup
291
+
292
+ ```typescript
293
+ import { closeDatabase } from '@spfn/core/db';
294
+
295
+ // Called automatically on graceful shutdown
296
+ // Manual call for scripts/tests
297
+ await closeDatabase();
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Best Practices
303
+
304
+ ### Do
305
+
306
+ ```typescript
307
+ // 1. Use Transactional for write routes
308
+ route.post('/users')
309
+ .use([Transactional()])
310
+ .handler(...)
311
+
312
+ // 2. Use repository pattern for data access
313
+ const user = await userRepo.findById(id);
314
+
315
+ // 3. Use read database for read operations
316
+ async findAll()
317
+ {
318
+ return this._findMany(users); // BaseRepository uses readDb
319
+ }
320
+
321
+ // 4. Close connections in tests
322
+ afterAll(async () => {
323
+ await closeDatabase();
324
+ });
325
+ ```
326
+
327
+ ### Don't
328
+
329
+ ```typescript
330
+ // 1. Don't forget Transactional for writes
331
+ route.post('/users')
332
+ .handler(async (c) => { // Missing Transactional!
333
+ await userRepo.create(body);
334
+ });
335
+
336
+ // 2. Don't bypass repository in routes
337
+ route.get('/users')
338
+ .handler(async (c) => {
339
+ // Bad - use repository
340
+ return getDatabase('read').select().from(users);
341
+ });
342
+
343
+ // 3. Don't use write database for reads
344
+ const db = getDatabase('write'); // Bad for read queries
345
+ await db.select().from(users);
346
+ ```