@spfn/core 0.1.0-alpha.88 → 0.2.0-beta.2

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 (71) hide show
  1. package/README.md +1046 -384
  2. package/dist/boss-D-fGtVgM.d.ts +187 -0
  3. package/dist/cache/index.d.ts +13 -33
  4. package/dist/cache/index.js +14 -703
  5. package/dist/cache/index.js.map +1 -1
  6. package/dist/codegen/index.d.ts +167 -17
  7. package/dist/codegen/index.js +76 -1419
  8. package/dist/codegen/index.js.map +1 -1
  9. package/dist/config/index.d.ts +1191 -0
  10. package/dist/config/index.js +264 -0
  11. package/dist/config/index.js.map +1 -0
  12. package/dist/db/index.d.ts +728 -59
  13. package/dist/db/index.js +1028 -1225
  14. package/dist/db/index.js.map +1 -1
  15. package/dist/env/index.d.ts +579 -308
  16. package/dist/env/index.js +438 -930
  17. package/dist/env/index.js.map +1 -1
  18. package/dist/errors/index.d.ts +417 -29
  19. package/dist/errors/index.js +359 -98
  20. package/dist/errors/index.js.map +1 -1
  21. package/dist/event/index.d.ts +108 -0
  22. package/dist/event/index.js +122 -0
  23. package/dist/event/index.js.map +1 -0
  24. package/dist/job/index.d.ts +172 -0
  25. package/dist/job/index.js +361 -0
  26. package/dist/job/index.js.map +1 -0
  27. package/dist/logger/index.d.ts +20 -79
  28. package/dist/logger/index.js +82 -387
  29. package/dist/logger/index.js.map +1 -1
  30. package/dist/middleware/index.d.ts +2 -11
  31. package/dist/middleware/index.js +49 -703
  32. package/dist/middleware/index.js.map +1 -1
  33. package/dist/nextjs/index.d.ts +120 -0
  34. package/dist/nextjs/index.js +416 -0
  35. package/dist/nextjs/index.js.map +1 -0
  36. package/dist/{client/nextjs/index.d.ts → nextjs/server.d.ts} +288 -262
  37. package/dist/nextjs/server.js +568 -0
  38. package/dist/nextjs/server.js.map +1 -0
  39. package/dist/route/index.d.ts +686 -25
  40. package/dist/route/index.js +440 -1287
  41. package/dist/route/index.js.map +1 -1
  42. package/dist/route/types.d.ts +38 -0
  43. package/dist/route/types.js +3 -0
  44. package/dist/route/types.js.map +1 -0
  45. package/dist/server/index.d.ts +201 -67
  46. package/dist/server/index.js +921 -3182
  47. package/dist/server/index.js.map +1 -1
  48. package/dist/types-BGl4QL1w.d.ts +77 -0
  49. package/dist/types-DRG2XMTR.d.ts +157 -0
  50. package/package.json +52 -47
  51. package/dist/auto-loader-JFaZ9gON.d.ts +0 -80
  52. package/dist/client/index.d.ts +0 -358
  53. package/dist/client/index.js +0 -357
  54. package/dist/client/index.js.map +0 -1
  55. package/dist/client/nextjs/index.js +0 -371
  56. package/dist/client/nextjs/index.js.map +0 -1
  57. package/dist/codegen/generators/index.d.ts +0 -19
  58. package/dist/codegen/generators/index.js +0 -1404
  59. package/dist/codegen/generators/index.js.map +0 -1
  60. package/dist/database-errors-BNNmLTJE.d.ts +0 -86
  61. package/dist/events/index.d.ts +0 -183
  62. package/dist/events/index.js +0 -77
  63. package/dist/events/index.js.map +0 -1
  64. package/dist/index-DHiAqhKv.d.ts +0 -101
  65. package/dist/index.d.ts +0 -8
  66. package/dist/index.js +0 -3674
  67. package/dist/index.js.map +0 -1
  68. package/dist/types/index.d.ts +0 -121
  69. package/dist/types/index.js +0 -38
  70. package/dist/types/index.js.map +0 -1
  71. package/dist/types-BXibIEyj.d.ts +0 -60
@@ -1,40 +1,701 @@
1
- export { A as AutoRouteLoader, R as RouteInfo, a as RouteStats, l as loadRoutes } from '../auto-loader-JFaZ9gON.js';
2
- import { Context, Hono, MiddlewareHandler } from 'hono';
3
- import { a as RouteContract, R as RouteContext, b as RouteHandler } from '../types-BXibIEyj.js';
4
- export { c as HeaderRecord, H as HttpMethod, I as InferContract, d as RouteMeta, i as isHttpMethod } from '../types-BXibIEyj.js';
5
- export { ApiErrorResponse, ApiErrorSchema, ApiResponse, ApiResponseSchema, ApiSuccessResponse, ApiSuccessSchema } from '../types/index.js';
6
- import 'hono/utils/http-status';
7
- import '@sinclair/typebox';
1
+ import * as _sinclair_typebox from '@sinclair/typebox';
2
+ import { TSchema, Static } from '@sinclair/typebox';
3
+ import { Context, MiddlewareHandler, Hono } from 'hono';
4
+ import { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status';
5
+ import { HttpMethod } from './types.js';
6
+ export { HeaderRecord, InferResponseData, RouteMeta, RouteMetadata, RouterMetadata } from './types.js';
8
7
 
9
8
  /**
10
- * Contract-based Route Handler Wrapper
9
+ * Route Input Types
11
10
  *
12
- * Binds a contract to a route handler, providing automatic validation
13
- * and type-safe context creation.
11
+ * Defines the structure for route input validation schemas
12
+ */
13
+
14
+ /**
15
+ * Route input schemas
16
+ *
17
+ * Defines validation schemas for different parts of an HTTP request
18
+ */
19
+ type RouteInput = {
20
+ /** Path parameters (e.g., /users/:id) */
21
+ params?: TSchema;
22
+ /** Query string parameters (e.g., ?page=1&limit=20) */
23
+ query?: TSchema;
24
+ /** Request body (JSON) */
25
+ body?: TSchema;
26
+ /** HTTP headers */
27
+ headers?: TSchema;
28
+ /** Cookies */
29
+ cookies?: TSchema;
30
+ };
31
+
32
+ /**
33
+ * Route Builder Context
34
+ *
35
+ * Provides structured input access and response helpers for route handlers
36
+ */
37
+
38
+ /**
39
+ * Merge input with interceptor-injected fields
40
+ * Server receives both client input and interceptor-injected fields
14
41
  *
15
- * Features:
16
- * - Automatic params/query/body validation using TypeBox
17
- * - Type-safe RouteContext with contract-based inference
18
- * - Clean separation: bind() for validation, Hono for middleware
42
+ * @example
43
+ * ```ts
44
+ * type ClientInput = { body: { email: string, password: string } };
45
+ * type InterceptorInput = { body: { publicKey: string, keyId: string } };
46
+ * // MergedInput = { body: { email: string, password: string, publicKey: string, keyId: string } }
47
+ * ```
19
48
  */
20
- declare function bind<TContract extends RouteContract>(contract: TContract, handler: (c: RouteContext<TContract>) => Response | Promise<Response>): (rawContext: Context) => Promise<Response>;
49
+ type MergedInput<TInput extends RouteInput, TInterceptor extends RouteInput> = {
50
+ params: (TInput['params'] extends TSchema ? Static<TInput['params']> : {}) & (TInterceptor['params'] extends TSchema ? Static<TInterceptor['params']> : {});
51
+ query: (TInput['query'] extends TSchema ? Static<TInput['query']> : {}) & (TInterceptor['query'] extends TSchema ? Static<TInterceptor['query']> : {});
52
+ body: (TInput['body'] extends TSchema ? Static<TInput['body']> : {}) & (TInterceptor['body'] extends TSchema ? Static<TInterceptor['body']> : {});
53
+ headers: (TInput['headers'] extends TSchema ? Static<TInput['headers']> : {}) & (TInterceptor['headers'] extends TSchema ? Static<TInterceptor['headers']> : {});
54
+ cookies: (TInput['cookies'] extends TSchema ? Static<TInput['cookies']> : {}) & (TInterceptor['cookies'] extends TSchema ? Static<TInterceptor['cookies']> : {});
55
+ };
56
+ /**
57
+ * RouteBuilderContext - define-route dedicated context
58
+ *
59
+ * Provides structured input access through data() method
60
+ */
61
+ type RouteBuilderContext<TInput extends RouteInput = RouteInput, TInterceptor extends RouteInput = {}> = {
62
+ /**
63
+ * Get structured input data
64
+ *
65
+ * Returns an object with separate params, query, body, headers, cookies
66
+ * If interceptor fields are defined, they are merged with input fields
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * // GET /users/:id?page=1
71
+ * const { params, query } = await c.data();
72
+ * // params = { id: string }
73
+ * // query = { page: number }
74
+ *
75
+ * // POST /users with headers
76
+ * const { body, headers } = await c.data();
77
+ * // body = { name: string }
78
+ * // headers = { authorization: string }
79
+ *
80
+ * // With interceptor-injected fields
81
+ * const { body } = await c.data();
82
+ * // body = { email: string, password: string, publicKey: string, keyId: string }
83
+ * ```
84
+ */
85
+ data(): Promise<MergedInput<TInput, TInterceptor>>;
86
+ /**
87
+ * Return JSON response with custom status and headers
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * return c.json({ message: 'Custom response' }, 200);
92
+ * ```
93
+ */
94
+ json(data: unknown, status?: ContentfulStatusCode, headers?: Record<string, string | string[]>): Response;
95
+ /**
96
+ * Return 201 Created response with optional Location header
97
+ * Returns data directly (no wrapper)
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const user = await createUser(body);
102
+ * return c.created(user, `/users/${user.id}`);
103
+ * // Response: 201 Created
104
+ * // Header: Location: /users/123
105
+ * // Body: { id: '123', name: 'John' }
106
+ * ```
107
+ */
108
+ created(data: unknown, location?: string): Response;
109
+ /**
110
+ * Return 202 Accepted response
111
+ * Returns data directly (no wrapper), or empty body if no data
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * // With data
116
+ * return c.accepted({ jobId: '123' });
117
+ * // Response: 202 Accepted, Body: { jobId: '123' }
118
+ *
119
+ * // Without data
120
+ * return c.accepted();
121
+ * // Response: 202 Accepted, Body: (empty)
122
+ * ```
123
+ */
124
+ accepted(data?: unknown): Response;
125
+ /**
126
+ * Return 204 No Content response (empty body)
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * await deleteUser(id);
131
+ * return c.noContent();
132
+ * // Response: 204 No Content, Body: (empty)
133
+ * ```
134
+ */
135
+ noContent(): Response;
136
+ /**
137
+ * Return 304 Not Modified response (empty body)
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * if (etag === requestEtag) {
142
+ * return c.notModified();
143
+ * }
144
+ * // Response: 304 Not Modified, Body: (empty)
145
+ * ```
146
+ */
147
+ notModified(): Response;
148
+ /**
149
+ * Return paginated response with metadata
150
+ * Returns `{ items: [...], pagination: {...} }` format
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const users = await getUsers(page, limit);
155
+ * const total = await countUsers();
156
+ * return c.paginated(users, page, limit, total);
157
+ * // Response: {
158
+ * // items: [...],
159
+ * // pagination: {
160
+ * // page: 1,
161
+ * // limit: 20,
162
+ * // total: 100,
163
+ * // totalPages: 5
164
+ * // }
165
+ * // }
166
+ * ```
167
+ */
168
+ paginated(data: unknown[], page: number, limit: number, total: number): Response;
169
+ /**
170
+ * Redirect to another URL
171
+ *
172
+ * @param url - Target URL to redirect to
173
+ * @param status - HTTP status code (301, 302, 303, 307, 308). Default: 302
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * // Temporary redirect (302)
178
+ * return c.redirect('/login');
179
+ *
180
+ * // Permanent redirect (301)
181
+ * return c.redirect('/new-path', 301);
182
+ *
183
+ * // See Other (303) - useful after POST
184
+ * return c.redirect('/success', 303);
185
+ * ```
186
+ */
187
+ redirect(url: string, status?: RedirectStatusCode): Response;
188
+ raw: Context;
189
+ };
21
190
 
22
191
  /**
23
- * Create App - Hono Wrapper for Contract-based Routing
192
+ * Middleware Definition Helper
24
193
  *
25
- * Provides a cleaner API for registering routes with contracts
194
+ * Provides type-safe middleware definition with name inference
26
195
  */
27
196
 
28
- type SPFNApp = Hono & {
29
- bind<TContract extends RouteContract>(contract: TContract, handler: RouteHandler<TContract>): void;
30
- bind<TContract extends RouteContract>(contract: TContract, middlewares: MiddlewareHandler[], handler: RouteHandler<TContract>): void;
31
- _contractMetas?: Map<string, RouteContract['meta']>;
197
+ /**
198
+ * Named middleware with type inference
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * export const authMiddleware = defineMiddleware('auth', async (c, next) => {
203
+ * // auth logic
204
+ * c.set('user', { id: 1 });
205
+ * await next();
206
+ * });
207
+ *
208
+ * export const rateLimitMiddleware = defineMiddleware('rateLimit', async (c, next) => {
209
+ * // rate limit logic
210
+ * await next();
211
+ * });
212
+ * ```
213
+ */
214
+ type NamedMiddleware<TName extends string = string> = {
215
+ name: TName;
216
+ handler: MiddlewareHandler;
217
+ _name: TName;
32
218
  };
33
219
  /**
34
- * Create SPFN app instance
220
+ * Named middleware factory with type inference
221
+ *
222
+ * Factory function that creates middleware instances with parameters
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * export const requirePermissions = defineMiddleware('permission',
227
+ * (...permissions: string[]) => async (c, next) => {
228
+ * // permission check logic
229
+ * await next();
230
+ * }
231
+ * );
232
+ * ```
233
+ */
234
+ type NamedMiddlewareFactory<TName extends string = string, TArgs extends any[] = any[]> = {
235
+ name: TName;
236
+ _name: TName;
237
+ } & ((...args: TArgs) => MiddlewareHandler);
238
+ /**
239
+ * Define a named middleware
240
+ *
241
+ * Creates a middleware with a unique name that can be referenced
242
+ * in route-level skip() calls with full type safety.
243
+ *
244
+ * Supports two patterns:
245
+ * 1. Regular middleware: Direct middleware handler
246
+ * 2. Factory middleware: Function that creates middleware with parameters
247
+ *
248
+ * @param name - Unique middleware name
249
+ * @param handler - Middleware handler function
250
+ * @returns Named middleware with type inference
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * // Regular middleware
255
+ * export const authMiddleware = defineMiddleware('auth', async (c, next) => {
256
+ * const token = c.req.header('authorization');
257
+ * if (!token) {
258
+ * return c.json({ error: 'Unauthorized' }, 401);
259
+ * }
260
+ * c.set('user', await verifyToken(token));
261
+ * await next();
262
+ * });
263
+ *
264
+ * // Factory middleware
265
+ * export const requirePermissions = defineMiddleware('permission',
266
+ * (...permissions: string[]) => async (c, next) => {
267
+ * const user = c.get('user');
268
+ * if (!hasPermissions(user, permissions)) {
269
+ * return c.json({ error: 'Forbidden' }, 403);
270
+ * }
271
+ * await next();
272
+ * }
273
+ * );
274
+ *
275
+ * // server.config.ts
276
+ * export default defineServerConfig()
277
+ * .middlewares([authMiddleware])
278
+ * .routes(appRouter)
279
+ * .build();
280
+ *
281
+ * // routes.ts - skip by name
282
+ * export const publicRoute = route.get('/health')
283
+ * .skip(['auth']) // ✅ Type-safe! Autocomplete!
284
+ * .handler(async (c) => c.success({ status: 'ok' }));
285
+ *
286
+ * // Use factory middleware inline
287
+ * export const protectedRoute = route.get('/admin')
288
+ * .use([requirePermissions('admin:write')])
289
+ * .handler(async (c) => { ... });
290
+ * ```
291
+ */
292
+ declare function defineMiddleware<TName extends string>(name: TName, handler: MiddlewareHandler): NamedMiddleware<TName>;
293
+ declare function defineMiddleware<TName extends string, TArgs extends any[]>(name: TName, factory: (...args: TArgs) => MiddlewareHandler): NamedMiddlewareFactory<TName, TArgs>;
294
+ /**
295
+ * Define a middleware factory explicitly
296
+ *
297
+ * Use this when your factory function has exactly 2 parameters,
298
+ * which would be incorrectly detected as a regular middleware handler.
299
+ *
300
+ * @param name - Unique middleware name
301
+ * @param factory - Factory function that returns a middleware handler
302
+ * @returns Named middleware factory with type inference
303
+ *
304
+ * @example
305
+ * ```ts
306
+ * // Factory with 2 params (would be misdetected by defineMiddleware)
307
+ * export const rateLimiter = defineMiddlewareFactory('rateLimit',
308
+ * (limit: number, window: number) => async (c, next) => {
309
+ * // rate limit logic using limit and window
310
+ * await next();
311
+ * }
312
+ * );
313
+ *
314
+ * // Usage
315
+ * route.get('/api')
316
+ * .use([rateLimiter(100, 60000)]) // 100 requests per minute
317
+ * .handler(...)
318
+ * ```
319
+ */
320
+ declare function defineMiddlewareFactory<TName extends string, TArgs extends unknown[]>(name: TName, factory: (...args: TArgs) => MiddlewareHandler): NamedMiddlewareFactory<TName, TArgs>;
321
+ /**
322
+ * Extract middleware names from an array of named middlewares
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * type Middlewares = [
327
+ * NamedMiddleware<'auth'>,
328
+ * NamedMiddleware<'rateLimit'>
329
+ * ];
330
+ * type Names = ExtractMiddlewareNames<Middlewares>; // 'auth' | 'rateLimit'
331
+ * ```
332
+ */
333
+ type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> = T[number]['_name'];
334
+
335
+ /**
336
+ * Route Builder
337
+ *
338
+ * Provides tRPC-style chainable API for route definition
339
+ */
340
+
341
+ /**
342
+ * Route handler function
343
+ */
344
+ type RouteHandlerFn<TInput extends RouteInput = RouteInput, TInterceptor extends RouteInput = {}, TResponse = unknown> = (c: RouteBuilderContext<TInput, TInterceptor>) => Response | Promise<Response> | TResponse | Promise<TResponse>;
345
+ /**
346
+ * Route definition result
347
+ *
348
+ * Contains all information needed for type inference and registration
349
+ */
350
+ type RouteDef<TInput extends RouteInput = RouteInput, TInterceptor extends RouteInput = {}, TResponse = unknown> = {
351
+ method?: HttpMethod;
352
+ path?: string;
353
+ input?: TInput;
354
+ interceptor?: TInterceptor;
355
+ middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];
356
+ skipMiddlewares?: string[] | '*';
357
+ handler: RouteHandlerFn<TInput, TInterceptor, TResponse>;
358
+ _input: TInput;
359
+ _interceptor: TInterceptor;
360
+ _response: TResponse;
361
+ };
362
+ /**
363
+ * Route builder with chainable API (tRPC-style)
364
+ */
365
+ declare class RouteBuilder<TInput extends RouteInput = {}, TInterceptor extends RouteInput = {}, TResponse = never> {
366
+ _method?: HttpMethod;
367
+ _path?: string;
368
+ _input?: TInput;
369
+ _interceptor?: TInterceptor;
370
+ _middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];
371
+ _skipMiddlewares?: string[] | '*';
372
+ /**
373
+ * Create a new RouteBuilder with copied properties and optional overrides
374
+ */
375
+ private clone;
376
+ /**
377
+ * Define input schemas
378
+ *
379
+ * @example
380
+ * ```ts
381
+ * route.get('/users/:id')
382
+ * .input({
383
+ * params: Type.Object({ id: Type.String() }),
384
+ * query: Type.Object({ page: Type.Number() }),
385
+ * headers: Type.Object({ authorization: Type.String() })
386
+ * })
387
+ * .handler(async (c) => {
388
+ * const { params, query, headers } = await c.data();
389
+ * // params = { id: string }
390
+ * // query = { page: number }
391
+ * // headers = { authorization: string }
392
+ * })
393
+ * ```
394
+ */
395
+ input<TNewInput extends RouteInput>(input: TNewInput): RouteBuilder<TNewInput, TInterceptor, TResponse>;
396
+ /**
397
+ * Define fields injected by interceptors
398
+ *
399
+ * These fields are:
400
+ * - Available in the handler (merged with input)
401
+ * - Excluded from client types (codegen uses only input)
402
+ * - Not validated by route input schema (injected by middleware)
403
+ *
404
+ * Use this when middleware/interceptors add fields to the request
405
+ * before it reaches the handler.
406
+ *
407
+ * @example
408
+ * ```ts
409
+ * // Auth interceptor injects crypto key fields
410
+ * route.post('/_auth/login')
411
+ * .input({
412
+ * body: Type.Object({
413
+ * email: Type.String(),
414
+ * password: Type.String()
415
+ * })
416
+ * })
417
+ * .interceptor({
418
+ * body: Type.Object({
419
+ * publicKey: Type.String(),
420
+ * keyId: Type.String(),
421
+ * fingerprint: Type.String()
422
+ * })
423
+ * })
424
+ * .handler(async (c) => {
425
+ * const { body } = await c.data();
426
+ * // body type: { email, password, publicKey, keyId, fingerprint }
427
+ * // Client only sees: { email, password }
428
+ * return loginService(body);
429
+ * });
430
+ * ```
431
+ */
432
+ interceptor<TNewInterceptor extends RouteInput>(interceptor: TNewInterceptor): RouteBuilder<TInput, TNewInterceptor, TResponse>;
433
+ /**
434
+ * Add middlewares to the route
435
+ *
436
+ * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).
437
+ * Named middlewares that are already registered globally will be automatically
438
+ * deduplicated to prevent double execution.
439
+ *
440
+ * @example
441
+ * ```ts
442
+ * import { authenticate } from '@spfn/auth/server/middleware';
443
+ *
444
+ * // With NamedMiddleware (auto-deduped if registered globally)
445
+ * route.get('/users')
446
+ * .use([authenticate, RateLimitMiddleware()])
447
+ *
448
+ * // With regular middleware handlers
449
+ * route.get('/users')
450
+ * .use([AuthMiddleware(), RateLimitMiddleware()])
451
+ * ```
452
+ */
453
+ middleware(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>;
454
+ /**
455
+ * Add middlewares to the route (alias for `.middleware()`)
456
+ *
457
+ * Accepts both regular middleware handlers and named middlewares (NamedMiddleware).
458
+ * Named middlewares that are already registered globally will be automatically
459
+ * deduplicated to prevent double execution.
460
+ *
461
+ * @example
462
+ * ```ts
463
+ * import { authenticate } from '@spfn/auth/server/middleware';
464
+ *
465
+ * // With NamedMiddleware (auto-deduped if registered globally)
466
+ * route.get('/users')
467
+ * .use([authenticate, RateLimitMiddleware()])
468
+ *
469
+ * // With regular middleware handlers
470
+ * route.get('/users')
471
+ * .use([AuthMiddleware(), RateLimitMiddleware()])
472
+ * ```
473
+ */
474
+ use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>;
475
+ /**
476
+ * Skip server-level named middlewares
477
+ *
478
+ * Useful for public endpoints that should bypass auth or rate limiting
479
+ *
480
+ * @param middlewareNames - Array of middleware names to skip, or '*' to skip all
481
+ *
482
+ * @example
483
+ * ```ts
484
+ * // Skip specific middlewares
485
+ * route.get('/health')
486
+ * .skip(['auth', 'rateLimit'])
487
+ * .handler(async (c) => c.json({ status: 'ok' }));
488
+ *
489
+ * // Skip only auth (still apply rate limiting)
490
+ * route.get('/public-data')
491
+ * .skip(['auth'])
492
+ * .handler(async (c) => { ... });
493
+ *
494
+ * // Skip all middlewares
495
+ * route.get('/public-health')
496
+ * .skip('*')
497
+ * .handler(async (c) => c.json({ status: 'ok' }));
498
+ * ```
499
+ */
500
+ skip(middlewareNames: string[] | '*'): RouteBuilder<TInput, TInterceptor, TResponse>;
501
+ /**
502
+ * Define handler function
503
+ *
504
+ * @example
505
+ * ```ts
506
+ * route.get('/users/:id')
507
+ * .input({ params: Type.Object({ id: Type.String() }) })
508
+ * .handler(async (c) => {
509
+ * const { params } = await c.data();
510
+ * const user = await getUser(params.id);
511
+ * return user; // Type inferred!
512
+ * })
513
+ * ```
514
+ */
515
+ handler<THandlerResponse>(fn: RouteHandlerFn<TInput, TInterceptor, THandlerResponse>): RouteDef<TInput, TInterceptor, THandlerResponse>;
516
+ }
517
+ /**
518
+ * Route builder entry point
519
+ *
520
+ * @example
521
+ * ```ts
522
+ * // GET request
523
+ * export const getUser = route.get('/users/:id')
524
+ * .input({ params: Type.Object({ id: Type.String() }) })
525
+ * .handler(async (c) => {
526
+ * const { params } = await c.data();
527
+ * return await db.user.findUnique({ where: { id: params.id } });
528
+ * });
529
+ *
530
+ * // POST request
531
+ * export const createUser = route.post('/users')
532
+ * .input({ body: Type.Object({ name: Type.String(), email: Type.String() }) })
533
+ * .handler(async (c) => {
534
+ * const { body } = await c.data();
535
+ * return c.created(await db.user.create({ data: body }));
536
+ * });
537
+ * ```
538
+ */
539
+ declare const route: {
540
+ get: (path: string) => RouteBuilder;
541
+ post: (path: string) => RouteBuilder;
542
+ put: (path: string) => RouteBuilder;
543
+ patch: (path: string) => RouteBuilder;
544
+ delete: (path: string) => RouteBuilder;
545
+ };
546
+
547
+ /**
548
+ * Router Definition
549
+ *
550
+ * Provides router composition and middleware management
551
+ */
552
+
553
+ /**
554
+ * Router definition - holds all routes
555
+ */
556
+ interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>> {
557
+ routes: TRoutes;
558
+ _routes: TRoutes;
559
+ _packageRouters: Router<any>[];
560
+ _globalMiddlewares: NamedMiddleware<string>[];
561
+ /**
562
+ * Register package routers (type-hidden)
563
+ *
564
+ * Package routes are:
565
+ * - Recognized by RPC proxy and backend
566
+ * - NOT exposed in client types (use package's own API like authApi, cmsApi)
567
+ *
568
+ * @example
569
+ * ```ts
570
+ * import { authRouter } from '@spfn/auth/server';
571
+ * import { cmsAppRouter } from '@spfn/cms/server';
572
+ *
573
+ * export const appRouter = defineRouter({
574
+ * getRoot,
575
+ * getHealth,
576
+ * })
577
+ * .packages([authRouter, cmsAppRouter]);
578
+ *
579
+ * // Client usage:
580
+ * // api.getRoot.call({}) - app routes
581
+ * // authApi.login.call({}) - package API
582
+ * ```
583
+ */
584
+ packages(routers: Router<any>[]): Router<TRoutes>;
585
+ /**
586
+ * Register global middlewares
587
+ *
588
+ * Applied to all routes unless explicitly skipped via .skip()
589
+ *
590
+ * @example
591
+ * ```ts
592
+ * import { authMiddleware, loggingMiddleware } from './middlewares';
593
+ *
594
+ * export const appRouter = defineRouter({
595
+ * getRoot,
596
+ * getHealth,
597
+ * })
598
+ * .packages([authRouter])
599
+ * .use([authMiddleware, loggingMiddleware]);
600
+ * ```
601
+ */
602
+ use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;
603
+ }
604
+ /**
605
+ * Define a router with multiple routes (tRPC-style)
606
+ *
607
+ * Supports chainable API for packages and middlewares:
608
+ *
609
+ * @example
610
+ * ```ts
611
+ * // Basic usage
612
+ * export const appRouter = defineRouter({
613
+ * getRoot,
614
+ * getHealth,
615
+ * listExamples,
616
+ * });
617
+ *
618
+ * // With package routers (type-hidden)
619
+ * export const appRouter = defineRouter({
620
+ * getRoot,
621
+ * getHealth,
622
+ * })
623
+ * .packages([authRouter, cmsAppRouter]);
624
+ *
625
+ * // With global middlewares
626
+ * export const appRouter = defineRouter({
627
+ * getRoot,
628
+ * getHealth,
629
+ * })
630
+ * .packages([authRouter])
631
+ * .use([authMiddleware, loggingMiddleware]);
632
+ *
633
+ * export type AppRouter = typeof appRouter;
634
+ * ```
635
+ *
636
+ * Package routes:
637
+ * - Recognized by RPC proxy and backend for routing
638
+ * - NOT included in AppRouter type (use authApi, cmsApi instead)
639
+ * - Prevents confusion between app API and package APIs
640
+ */
641
+ declare function defineRouter<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(routes: TRoutes): Router<TRoutes>;
642
+
643
+ /**
644
+ * Route Registration for define-route style routing
645
+ *
646
+ * Registers routes defined with route.get()...handler() to Hono app
647
+ */
648
+
649
+ /**
650
+ * Register routes from defineRouter() to Hono app
651
+ *
652
+ * @param app - Hono app instance
653
+ * @param router - Router definition
654
+ * @param namedMiddlewares - Optional server-level named middlewares
655
+ *
656
+ * @example
657
+ * ```ts
658
+ * const appRouter = defineRouter({
659
+ * getUser: route.get('/users/:id')...
660
+ * createUser: route.post('/users')...
661
+ * });
662
+ *
663
+ * const app = new Hono();
664
+ * const namedMiddlewares = [
665
+ * { name: 'auth', handler: AuthMiddleware() },
666
+ * { name: 'rateLimit', handler: RateLimitMiddleware() },
667
+ * ];
668
+ * registerRoutes(app, appRouter, namedMiddlewares);
669
+ * ```
670
+ */
671
+ declare function registerRoutes<TRoutes extends Record<string, RouteDef<any> | Router<any>>>(app: Hono, router: Router<TRoutes>, namedMiddlewares?: ReadonlyArray<{
672
+ name: string;
673
+ handler: MiddlewareHandler;
674
+ }>): void;
675
+
676
+ /**
677
+ * Type guard for HttpMethod
678
+ */
679
+ declare function isHttpMethod(value: unknown): value is HttpMethod;
680
+ /**
681
+ * Nullable - Creates a union of T | null
682
+ *
683
+ * @example
684
+ * ```typescript
685
+ * // string | null
686
+ * firstName: Nullable(Type.String())
687
+ * ```
688
+ */
689
+ declare const Nullable: <T extends TSchema>(schema: T) => _sinclair_typebox.TUnion<[T, _sinclair_typebox.TNull]>;
690
+ /**
691
+ * OptionalNullable - Creates a union of T | null | undefined
35
692
  *
36
- * Wraps Hono with contract-based routing support
693
+ * @example
694
+ * ```typescript
695
+ * // string | null | undefined
696
+ * lastName: OptionalNullable(Type.String())
697
+ * ```
37
698
  */
38
- declare function createApp(): SPFNApp;
699
+ declare const OptionalNullable: <T extends TSchema>(schema: T) => _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[T, _sinclair_typebox.TNull]>>;
39
700
 
40
- export { RouteContext, RouteContract, RouteHandler, type SPFNApp, bind, createApp };
701
+ export { type ExtractMiddlewareNames, HttpMethod, type MergedInput, type NamedMiddleware, type NamedMiddlewareFactory, Nullable, OptionalNullable, RouteBuilder, type RouteBuilderContext, type RouteDef, type RouteHandlerFn, type RouteInput, type Router, defineMiddleware, defineMiddlewareFactory, defineRouter, isHttpMethod, registerRoutes, route };