@spfn/core 0.1.0-alpha.88 → 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.
- package/README.md +298 -466
- package/dist/boss-DI1r4kTS.d.ts +244 -0
- package/dist/cache/index.d.ts +13 -33
- package/dist/cache/index.js +14 -703
- package/dist/cache/index.js.map +1 -1
- package/dist/codegen/index.d.ts +214 -17
- package/dist/codegen/index.js +231 -1420
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.d.ts +1227 -0
- package/dist/config/index.js +273 -0
- package/dist/config/index.js.map +1 -0
- package/dist/db/index.d.ts +741 -59
- package/dist/db/index.js +1063 -1226
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.d.ts +658 -308
- package/dist/env/index.js +503 -928
- package/dist/env/index.js.map +1 -1
- package/dist/env/loader.d.ts +87 -0
- package/dist/env/loader.js +70 -0
- package/dist/env/loader.js.map +1 -0
- package/dist/errors/index.d.ts +417 -29
- package/dist/errors/index.js +359 -98
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +41 -0
- package/dist/event/index.js +131 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/sse/client.d.ts +82 -0
- package/dist/event/sse/client.js +115 -0
- package/dist/event/sse/client.js.map +1 -0
- package/dist/event/sse/index.d.ts +40 -0
- package/dist/event/sse/index.js +92 -0
- package/dist/event/sse/index.js.map +1 -0
- package/dist/job/index.d.ts +218 -0
- package/dist/job/index.js +410 -0
- package/dist/job/index.js.map +1 -0
- package/dist/logger/index.d.ts +20 -79
- package/dist/logger/index.js +82 -387
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.d.ts +102 -20
- package/dist/middleware/index.js +51 -705
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +120 -0
- package/dist/nextjs/index.js +448 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/{client/nextjs/index.d.ts → nextjs/server.d.ts} +335 -262
- package/dist/nextjs/server.js +637 -0
- package/dist/nextjs/server.js.map +1 -0
- package/dist/route/index.d.ts +879 -25
- package/dist/route/index.js +697 -1271
- package/dist/route/index.js.map +1 -1
- package/dist/route/types.d.ts +9 -0
- package/dist/route/types.js +3 -0
- package/dist/route/types.js.map +1 -0
- package/dist/router-Di7ENoah.d.ts +151 -0
- package/dist/server/index.d.ts +345 -64
- package/dist/server/index.js +1174 -3233
- package/dist/server/index.js.map +1 -1
- package/dist/types-B-e_f2dQ.d.ts +121 -0
- package/dist/types-BGl4QL1w.d.ts +77 -0
- package/dist/types-BOPTApC2.d.ts +245 -0
- package/docs/cache.md +133 -0
- package/docs/codegen.md +74 -0
- package/docs/database.md +346 -0
- package/docs/entity.md +539 -0
- package/docs/env.md +477 -0
- package/docs/errors.md +319 -0
- package/docs/event.md +116 -0
- package/docs/file-upload.md +717 -0
- package/docs/job.md +131 -0
- package/docs/logger.md +108 -0
- package/docs/middleware.md +337 -0
- package/docs/nextjs.md +241 -0
- package/docs/repository.md +496 -0
- package/docs/route.md +497 -0
- package/docs/server.md +307 -0
- package/package.json +68 -48
- package/dist/auto-loader-JFaZ9gON.d.ts +0 -80
- package/dist/client/index.d.ts +0 -358
- package/dist/client/index.js +0 -357
- package/dist/client/index.js.map +0 -1
- package/dist/client/nextjs/index.js +0 -371
- package/dist/client/nextjs/index.js.map +0 -1
- package/dist/codegen/generators/index.d.ts +0 -19
- package/dist/codegen/generators/index.js +0 -1404
- package/dist/codegen/generators/index.js.map +0 -1
- package/dist/database-errors-BNNmLTJE.d.ts +0 -86
- package/dist/events/index.d.ts +0 -183
- package/dist/events/index.js +0 -77
- package/dist/events/index.js.map +0 -1
- package/dist/index-DHiAqhKv.d.ts +0 -101
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -3674
- package/dist/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -121
- package/dist/types/index.js +0 -38
- package/dist/types/index.js.map +0 -1
- package/dist/types-BXibIEyj.d.ts +0 -60
package/dist/route/index.d.ts
CHANGED
|
@@ -1,40 +1,894 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import 'hono/utils/http-status';
|
|
7
|
-
import '@sinclair/typebox';
|
|
1
|
+
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
2
|
+
import { TSchema, Static, Kind } 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';
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
|
-
*
|
|
8
|
+
* Route Input Types
|
|
11
9
|
*
|
|
12
|
-
*
|
|
13
|
-
|
|
10
|
+
* Defines the structure for route input validation schemas
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Route input schemas
|
|
15
|
+
*
|
|
16
|
+
* Defines validation schemas for different parts of an HTTP request
|
|
17
|
+
*/
|
|
18
|
+
type RouteInput = {
|
|
19
|
+
/** Path parameters (e.g., /users/:id) */
|
|
20
|
+
params?: TSchema;
|
|
21
|
+
/** Query string parameters (e.g., ?page=1&limit=20) */
|
|
22
|
+
query?: TSchema;
|
|
23
|
+
/** Request body (JSON) */
|
|
24
|
+
body?: TSchema;
|
|
25
|
+
/** Form data (multipart/form-data) for file uploads */
|
|
26
|
+
formData?: TSchema;
|
|
27
|
+
/** HTTP headers */
|
|
28
|
+
headers?: TSchema;
|
|
29
|
+
/** Cookies */
|
|
30
|
+
cookies?: TSchema;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Route Builder Context
|
|
35
|
+
*
|
|
36
|
+
* Provides structured input access and response helpers for route handlers
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Paginated response structure
|
|
41
|
+
*/
|
|
42
|
+
type PaginatedResult<T> = {
|
|
43
|
+
items: T[];
|
|
44
|
+
pagination: {
|
|
45
|
+
page: number;
|
|
46
|
+
limit: number;
|
|
47
|
+
total: number;
|
|
48
|
+
totalPages: number;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Merge input with interceptor-injected fields
|
|
53
|
+
* Server receives both client input and interceptor-injected fields
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* type ClientInput = { body: { email: string, password: string } };
|
|
58
|
+
* type InterceptorInput = { body: { publicKey: string, keyId: string } };
|
|
59
|
+
* // MergedInput = { body: { email: string, password: string, publicKey: string, keyId: string } }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
type MergedInput<TInput extends RouteInput, TInterceptor extends RouteInput> = {
|
|
63
|
+
params: (TInput['params'] extends TSchema ? Static<TInput['params']> : {}) & (TInterceptor['params'] extends TSchema ? Static<TInterceptor['params']> : {});
|
|
64
|
+
query: (TInput['query'] extends TSchema ? Static<TInput['query']> : {}) & (TInterceptor['query'] extends TSchema ? Static<TInterceptor['query']> : {});
|
|
65
|
+
body: (TInput['body'] extends TSchema ? Static<TInput['body']> : {}) & (TInterceptor['body'] extends TSchema ? Static<TInterceptor['body']> : {});
|
|
66
|
+
formData: (TInput['formData'] extends TSchema ? Static<TInput['formData']> : {}) & (TInterceptor['formData'] extends TSchema ? Static<TInterceptor['formData']> : {});
|
|
67
|
+
headers: (TInput['headers'] extends TSchema ? Static<TInput['headers']> : {}) & (TInterceptor['headers'] extends TSchema ? Static<TInterceptor['headers']> : {});
|
|
68
|
+
cookies: (TInput['cookies'] extends TSchema ? Static<TInput['cookies']> : {}) & (TInterceptor['cookies'] extends TSchema ? Static<TInterceptor['cookies']> : {});
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* RouteBuilderContext - define-route dedicated context
|
|
72
|
+
*
|
|
73
|
+
* Provides structured input access through data() method
|
|
74
|
+
*/
|
|
75
|
+
type RouteBuilderContext<TInput extends RouteInput = RouteInput, TInterceptor extends RouteInput = {}> = {
|
|
76
|
+
/**
|
|
77
|
+
* Get structured input data
|
|
78
|
+
*
|
|
79
|
+
* Returns an object with separate params, query, body, headers, cookies
|
|
80
|
+
* If interceptor fields are defined, they are merged with input fields
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* // GET /users/:id?page=1
|
|
85
|
+
* const { params, query } = await c.data();
|
|
86
|
+
* // params = { id: string }
|
|
87
|
+
* // query = { page: number }
|
|
88
|
+
*
|
|
89
|
+
* // POST /users with headers
|
|
90
|
+
* const { body, headers } = await c.data();
|
|
91
|
+
* // body = { name: string }
|
|
92
|
+
* // headers = { authorization: string }
|
|
93
|
+
*
|
|
94
|
+
* // With interceptor-injected fields
|
|
95
|
+
* const { body } = await c.data();
|
|
96
|
+
* // body = { email: string, password: string, publicKey: string, keyId: string }
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
data(): Promise<MergedInput<TInput, TInterceptor>>;
|
|
100
|
+
/**
|
|
101
|
+
* Return JSON response with custom status and headers
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* return c.json({ message: 'Custom response' }, 200);
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
json(data: unknown, status?: ContentfulStatusCode, headers?: Record<string, string | string[]>): Response;
|
|
109
|
+
/**
|
|
110
|
+
* Return 201 Created response with optional Location header
|
|
111
|
+
* Returns data directly for type inference
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* const user = await createUser(body);
|
|
116
|
+
* return c.created(user, `/users/${user.id}`);
|
|
117
|
+
* // Response: 201 Created
|
|
118
|
+
* // Header: Location: /users/123
|
|
119
|
+
* // Body: { id: '123', name: 'John' }
|
|
120
|
+
* // Type: User (inferred from data)
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
created<T>(data: T, location?: string): T;
|
|
124
|
+
/**
|
|
125
|
+
* Return 202 Accepted response
|
|
126
|
+
* Returns data directly for type inference
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* // With data
|
|
131
|
+
* return c.accepted({ jobId: '123' });
|
|
132
|
+
* // Response: 202 Accepted, Body: { jobId: '123' }
|
|
133
|
+
* // Type: { jobId: string }
|
|
134
|
+
*
|
|
135
|
+
* // Without data
|
|
136
|
+
* return c.accepted();
|
|
137
|
+
* // Response: 202 Accepted, Body: (empty)
|
|
138
|
+
* // Type: void
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
accepted(): void;
|
|
142
|
+
accepted<T>(data: T): T;
|
|
143
|
+
/**
|
|
144
|
+
* Return 204 No Content response (empty body)
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* await deleteUser(id);
|
|
149
|
+
* return c.noContent();
|
|
150
|
+
* // Response: 204 No Content, Body: (empty)
|
|
151
|
+
* // Type: void
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
noContent(): void;
|
|
155
|
+
/**
|
|
156
|
+
* Return 304 Not Modified response (empty body)
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* if (etag === requestEtag) {
|
|
161
|
+
* return c.notModified();
|
|
162
|
+
* }
|
|
163
|
+
* // Response: 304 Not Modified, Body: (empty)
|
|
164
|
+
* // Type: void
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
notModified(): void;
|
|
168
|
+
/**
|
|
169
|
+
* Return paginated response with metadata
|
|
170
|
+
* Returns `{ items: [...], pagination: {...} }` format with type inference
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* const users = await getUsers(page, limit);
|
|
175
|
+
* const total = await countUsers();
|
|
176
|
+
* return c.paginated(users, page, limit, total);
|
|
177
|
+
* // Response: {
|
|
178
|
+
* // items: [...],
|
|
179
|
+
* // pagination: {
|
|
180
|
+
* // page: 1,
|
|
181
|
+
* // limit: 20,
|
|
182
|
+
* // total: 100,
|
|
183
|
+
* // totalPages: 5
|
|
184
|
+
* // }
|
|
185
|
+
* // }
|
|
186
|
+
* // Type: PaginatedResult<User>
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
paginated<T>(data: T[], page: number, limit: number, total: number): PaginatedResult<T>;
|
|
190
|
+
/**
|
|
191
|
+
* Redirect to another URL
|
|
192
|
+
*
|
|
193
|
+
* @param url - Target URL to redirect to
|
|
194
|
+
* @param status - HTTP status code (301, 302, 303, 307, 308). Default: 302
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* // Temporary redirect (302)
|
|
199
|
+
* return c.redirect('/login');
|
|
200
|
+
*
|
|
201
|
+
* // Permanent redirect (301)
|
|
202
|
+
* return c.redirect('/new-path', 301);
|
|
203
|
+
*
|
|
204
|
+
* // See Other (303) - useful after POST
|
|
205
|
+
* return c.redirect('/success', 303);
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
redirect(url: string, status?: RedirectStatusCode): Response;
|
|
209
|
+
raw: Context;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Middleware Definition Helper
|
|
214
|
+
*
|
|
215
|
+
* Provides type-safe middleware definition with name inference
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Named middleware with type inference
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* export const authMiddleware = defineMiddleware('auth', async (c, next) => {
|
|
224
|
+
* // auth logic
|
|
225
|
+
* c.set('user', { id: 1 });
|
|
226
|
+
* await next();
|
|
227
|
+
* });
|
|
228
|
+
*
|
|
229
|
+
* export const rateLimitMiddleware = defineMiddleware('rateLimit', async (c, next) => {
|
|
230
|
+
* // rate limit logic
|
|
231
|
+
* await next();
|
|
232
|
+
* });
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
type NamedMiddleware<TName extends string = string> = {
|
|
236
|
+
name: TName;
|
|
237
|
+
handler: MiddlewareHandler;
|
|
238
|
+
_name: TName;
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Named middleware factory with type inference
|
|
242
|
+
*
|
|
243
|
+
* Factory function that creates middleware instances with parameters
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```ts
|
|
247
|
+
* export const requirePermissions = defineMiddleware('permission',
|
|
248
|
+
* (...permissions: string[]) => async (c, next) => {
|
|
249
|
+
* // permission check logic
|
|
250
|
+
* await next();
|
|
251
|
+
* }
|
|
252
|
+
* );
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
type NamedMiddlewareFactory<TName extends string = string, TArgs extends any[] = any[]> = {
|
|
256
|
+
name: TName;
|
|
257
|
+
_name: TName;
|
|
258
|
+
} & ((...args: TArgs) => MiddlewareHandler);
|
|
259
|
+
/**
|
|
260
|
+
* Define a named middleware
|
|
261
|
+
*
|
|
262
|
+
* Creates a middleware with a unique name that can be referenced
|
|
263
|
+
* in route-level skip() calls with full type safety.
|
|
264
|
+
*
|
|
265
|
+
* Supports two patterns:
|
|
266
|
+
* 1. Regular middleware: Direct middleware handler
|
|
267
|
+
* 2. Factory middleware: Function that creates middleware with parameters
|
|
268
|
+
*
|
|
269
|
+
* @param name - Unique middleware name
|
|
270
|
+
* @param handler - Middleware handler function
|
|
271
|
+
* @returns Named middleware with type inference
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* // Regular middleware
|
|
276
|
+
* export const authMiddleware = defineMiddleware('auth', async (c, next) => {
|
|
277
|
+
* const token = c.req.header('authorization');
|
|
278
|
+
* if (!token) {
|
|
279
|
+
* return c.json({ error: 'Unauthorized' }, 401);
|
|
280
|
+
* }
|
|
281
|
+
* c.set('user', await verifyToken(token));
|
|
282
|
+
* await next();
|
|
283
|
+
* });
|
|
284
|
+
*
|
|
285
|
+
* // Factory middleware
|
|
286
|
+
* export const requirePermissions = defineMiddleware('permission',
|
|
287
|
+
* (...permissions: string[]) => async (c, next) => {
|
|
288
|
+
* const user = c.get('user');
|
|
289
|
+
* if (!hasPermissions(user, permissions)) {
|
|
290
|
+
* return c.json({ error: 'Forbidden' }, 403);
|
|
291
|
+
* }
|
|
292
|
+
* await next();
|
|
293
|
+
* }
|
|
294
|
+
* );
|
|
295
|
+
*
|
|
296
|
+
* // server.config.ts
|
|
297
|
+
* export default defineServerConfig()
|
|
298
|
+
* .middlewares([authMiddleware])
|
|
299
|
+
* .routes(appRouter)
|
|
300
|
+
* .build();
|
|
301
|
+
*
|
|
302
|
+
* // routes.ts - skip by name
|
|
303
|
+
* export const publicRoute = route.get('/health')
|
|
304
|
+
* .skip(['auth']) // ✅ Type-safe! Autocomplete!
|
|
305
|
+
* .handler(async (c) => c.success({ status: 'ok' }));
|
|
306
|
+
*
|
|
307
|
+
* // Use factory middleware inline
|
|
308
|
+
* export const protectedRoute = route.get('/admin')
|
|
309
|
+
* .use([requirePermissions('admin:write')])
|
|
310
|
+
* .handler(async (c) => { ... });
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
declare function defineMiddleware<TName extends string>(name: TName, handler: MiddlewareHandler): NamedMiddleware<TName>;
|
|
314
|
+
declare function defineMiddleware<TName extends string, TArgs extends any[]>(name: TName, factory: (...args: TArgs) => MiddlewareHandler): NamedMiddlewareFactory<TName, TArgs>;
|
|
315
|
+
/**
|
|
316
|
+
* Define a middleware factory explicitly
|
|
317
|
+
*
|
|
318
|
+
* Use this when your factory function has exactly 2 parameters,
|
|
319
|
+
* which would be incorrectly detected as a regular middleware handler.
|
|
14
320
|
*
|
|
15
|
-
*
|
|
16
|
-
* -
|
|
17
|
-
*
|
|
18
|
-
*
|
|
321
|
+
* @param name - Unique middleware name
|
|
322
|
+
* @param factory - Factory function that returns a middleware handler
|
|
323
|
+
* @returns Named middleware factory with type inference
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```ts
|
|
327
|
+
* // Factory with 2 params (would be misdetected by defineMiddleware)
|
|
328
|
+
* export const rateLimiter = defineMiddlewareFactory('rateLimit',
|
|
329
|
+
* (limit: number, window: number) => async (c, next) => {
|
|
330
|
+
* // rate limit logic using limit and window
|
|
331
|
+
* await next();
|
|
332
|
+
* }
|
|
333
|
+
* );
|
|
334
|
+
*
|
|
335
|
+
* // Usage
|
|
336
|
+
* route.get('/api')
|
|
337
|
+
* .use([rateLimiter(100, 60000)]) // 100 requests per minute
|
|
338
|
+
* .handler(...)
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
declare function defineMiddlewareFactory<TName extends string, TArgs extends unknown[]>(name: TName, factory: (...args: TArgs) => MiddlewareHandler): NamedMiddlewareFactory<TName, TArgs>;
|
|
342
|
+
/**
|
|
343
|
+
* Extract middleware names from an array of named middlewares
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```ts
|
|
347
|
+
* type Middlewares = [
|
|
348
|
+
* NamedMiddleware<'auth'>,
|
|
349
|
+
* NamedMiddleware<'rateLimit'>
|
|
350
|
+
* ];
|
|
351
|
+
* type Names = ExtractMiddlewareNames<Middlewares>; // 'auth' | 'rateLimit'
|
|
352
|
+
* ```
|
|
19
353
|
*/
|
|
20
|
-
|
|
354
|
+
type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> = T[number]['_name'];
|
|
21
355
|
|
|
22
356
|
/**
|
|
23
|
-
*
|
|
357
|
+
* Route Builder
|
|
24
358
|
*
|
|
25
|
-
* Provides
|
|
359
|
+
* Provides tRPC-style chainable API for route definition
|
|
26
360
|
*/
|
|
27
361
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Route handler function
|
|
364
|
+
*/
|
|
365
|
+
type RouteHandlerFn<TInput extends RouteInput = RouteInput, TInterceptor extends RouteInput = {}, TResponse = unknown> = (c: RouteBuilderContext<TInput, TInterceptor>) => Response | Promise<Response> | TResponse | Promise<TResponse>;
|
|
366
|
+
/**
|
|
367
|
+
* Route definition result
|
|
368
|
+
*
|
|
369
|
+
* Contains all information needed for type inference and registration
|
|
370
|
+
*/
|
|
371
|
+
type RouteDef<TInput extends RouteInput = RouteInput, TInterceptor extends RouteInput = {}, TResponse = unknown> = {
|
|
372
|
+
method?: HttpMethod;
|
|
373
|
+
path?: string;
|
|
374
|
+
input?: TInput;
|
|
375
|
+
interceptor?: TInterceptor;
|
|
376
|
+
middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];
|
|
377
|
+
skipMiddlewares?: string[] | '*';
|
|
378
|
+
handler: RouteHandlerFn<TInput, TInterceptor, TResponse>;
|
|
379
|
+
_input: TInput;
|
|
380
|
+
_interceptor: TInterceptor;
|
|
381
|
+
_response: TResponse;
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* Route builder with chainable API (tRPC-style)
|
|
385
|
+
*/
|
|
386
|
+
declare class RouteBuilder<TInput extends RouteInput = {}, TInterceptor extends RouteInput = {}, TResponse = never> {
|
|
387
|
+
_method?: HttpMethod;
|
|
388
|
+
_path?: string;
|
|
389
|
+
_input?: TInput;
|
|
390
|
+
_interceptor?: TInterceptor;
|
|
391
|
+
_middlewares?: (MiddlewareHandler | NamedMiddleware<string>)[];
|
|
392
|
+
_skipMiddlewares?: string[] | '*';
|
|
393
|
+
/**
|
|
394
|
+
* Create a new RouteBuilder with copied properties and optional overrides
|
|
395
|
+
*/
|
|
396
|
+
private clone;
|
|
397
|
+
/**
|
|
398
|
+
* Define input schemas
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* route.get('/users/:id')
|
|
403
|
+
* .input({
|
|
404
|
+
* params: Type.Object({ id: Type.String() }),
|
|
405
|
+
* query: Type.Object({ page: Type.Number() }),
|
|
406
|
+
* headers: Type.Object({ authorization: Type.String() })
|
|
407
|
+
* })
|
|
408
|
+
* .handler(async (c) => {
|
|
409
|
+
* const { params, query, headers } = await c.data();
|
|
410
|
+
* // params = { id: string }
|
|
411
|
+
* // query = { page: number }
|
|
412
|
+
* // headers = { authorization: string }
|
|
413
|
+
* })
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
input<TNewInput extends RouteInput>(input: TNewInput): RouteBuilder<TNewInput, TInterceptor, TResponse>;
|
|
417
|
+
/**
|
|
418
|
+
* Define fields injected by interceptors
|
|
419
|
+
*
|
|
420
|
+
* These fields are:
|
|
421
|
+
* - Available in the handler (merged with input)
|
|
422
|
+
* - Excluded from client types (codegen uses only input)
|
|
423
|
+
* - Not validated by route input schema (injected by middleware)
|
|
424
|
+
*
|
|
425
|
+
* Use this when middleware/interceptors add fields to the request
|
|
426
|
+
* before it reaches the handler.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```ts
|
|
430
|
+
* // Auth interceptor injects crypto key fields
|
|
431
|
+
* route.post('/_auth/login')
|
|
432
|
+
* .input({
|
|
433
|
+
* body: Type.Object({
|
|
434
|
+
* email: Type.String(),
|
|
435
|
+
* password: Type.String()
|
|
436
|
+
* })
|
|
437
|
+
* })
|
|
438
|
+
* .interceptor({
|
|
439
|
+
* body: Type.Object({
|
|
440
|
+
* publicKey: Type.String(),
|
|
441
|
+
* keyId: Type.String(),
|
|
442
|
+
* fingerprint: Type.String()
|
|
443
|
+
* })
|
|
444
|
+
* })
|
|
445
|
+
* .handler(async (c) => {
|
|
446
|
+
* const { body } = await c.data();
|
|
447
|
+
* // body type: { email, password, publicKey, keyId, fingerprint }
|
|
448
|
+
* // Client only sees: { email, password }
|
|
449
|
+
* return loginService(body);
|
|
450
|
+
* });
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
interceptor<TNewInterceptor extends RouteInput>(interceptor: TNewInterceptor): RouteBuilder<TInput, TNewInterceptor, TResponse>;
|
|
454
|
+
/**
|
|
455
|
+
* Add middlewares to the route
|
|
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
|
+
middleware(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>;
|
|
475
|
+
/**
|
|
476
|
+
* Add middlewares to the route (alias for `.middleware()`)
|
|
477
|
+
*
|
|
478
|
+
* Accepts both regular middleware handlers and named middlewares (NamedMiddleware).
|
|
479
|
+
* Named middlewares that are already registered globally will be automatically
|
|
480
|
+
* deduplicated to prevent double execution.
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* import { authenticate } from '@spfn/auth/server/middleware';
|
|
485
|
+
*
|
|
486
|
+
* // With NamedMiddleware (auto-deduped if registered globally)
|
|
487
|
+
* route.get('/users')
|
|
488
|
+
* .use([authenticate, RateLimitMiddleware()])
|
|
489
|
+
*
|
|
490
|
+
* // With regular middleware handlers
|
|
491
|
+
* route.get('/users')
|
|
492
|
+
* .use([AuthMiddleware(), RateLimitMiddleware()])
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
use(middlewares: (MiddlewareHandler | NamedMiddleware<string>)[]): RouteBuilder<TInput, TInterceptor, TResponse>;
|
|
496
|
+
/**
|
|
497
|
+
* Skip server-level named middlewares
|
|
498
|
+
*
|
|
499
|
+
* Useful for public endpoints that should bypass auth or rate limiting
|
|
500
|
+
*
|
|
501
|
+
* @param middlewareNames - Array of middleware names to skip, or '*' to skip all
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* ```ts
|
|
505
|
+
* // Skip specific middlewares
|
|
506
|
+
* route.get('/health')
|
|
507
|
+
* .skip(['auth', 'rateLimit'])
|
|
508
|
+
* .handler(async (c) => c.json({ status: 'ok' }));
|
|
509
|
+
*
|
|
510
|
+
* // Skip only auth (still apply rate limiting)
|
|
511
|
+
* route.get('/public-data')
|
|
512
|
+
* .skip(['auth'])
|
|
513
|
+
* .handler(async (c) => { ... });
|
|
514
|
+
*
|
|
515
|
+
* // Skip all middlewares
|
|
516
|
+
* route.get('/public-health')
|
|
517
|
+
* .skip('*')
|
|
518
|
+
* .handler(async (c) => c.json({ status: 'ok' }));
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
skip(middlewareNames: string[] | '*'): RouteBuilder<TInput, TInterceptor, TResponse>;
|
|
522
|
+
/**
|
|
523
|
+
* Define handler function
|
|
524
|
+
*
|
|
525
|
+
* Response type is automatically inferred from the return value.
|
|
526
|
+
* Use helper methods like `c.created()`, `c.paginated()` for proper type inference.
|
|
527
|
+
*
|
|
528
|
+
* @example
|
|
529
|
+
* ```ts
|
|
530
|
+
* // Direct return - type inferred from data
|
|
531
|
+
* route.get('/users/:id')
|
|
532
|
+
* .input({ params: Type.Object({ id: Type.String() }) })
|
|
533
|
+
* .handler(async (c) => {
|
|
534
|
+
* const { params } = await c.data();
|
|
535
|
+
* return await getUser(params.id); // Type: User
|
|
536
|
+
* })
|
|
537
|
+
*
|
|
538
|
+
* // Using c.created() - returns data with 201 status, type preserved
|
|
539
|
+
* route.post('/users')
|
|
540
|
+
* .input({ body: Type.Object({ name: Type.String() }) })
|
|
541
|
+
* .handler(async (c) => {
|
|
542
|
+
* const { body } = await c.data();
|
|
543
|
+
* return c.created(await createUser(body)); // Type: User
|
|
544
|
+
* })
|
|
545
|
+
*
|
|
546
|
+
* // Using c.paginated() - returns PaginatedResult<T>
|
|
547
|
+
* route.get('/users')
|
|
548
|
+
* .handler(async (c) => {
|
|
549
|
+
* const users = await getUsers();
|
|
550
|
+
* return c.paginated(users, 1, 20, 100); // Type: PaginatedResult<User>
|
|
551
|
+
* })
|
|
552
|
+
*
|
|
553
|
+
* // Using c.noContent() - returns void
|
|
554
|
+
* route.delete('/users/:id')
|
|
555
|
+
* .handler(async (c) => {
|
|
556
|
+
* await deleteUser(params.id);
|
|
557
|
+
* return c.noContent(); // Type: void
|
|
558
|
+
* })
|
|
559
|
+
*
|
|
560
|
+
* // Using c.json() - returns Response (type inference lost)
|
|
561
|
+
* // Use only when you need custom status codes not covered by helpers
|
|
562
|
+
* route.get('/custom')
|
|
563
|
+
* .handler(async (c) => {
|
|
564
|
+
* return c.json({ data }, 418); // Type: Response
|
|
565
|
+
* })
|
|
566
|
+
* ```
|
|
567
|
+
*/
|
|
568
|
+
handler<THandlerResponse>(fn: RouteHandlerFn<TInput, TInterceptor, THandlerResponse>): RouteDef<TInput, TInterceptor, THandlerResponse>;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Route builder entry point
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* // GET request
|
|
576
|
+
* export const getUser = route.get('/users/:id')
|
|
577
|
+
* .input({ params: Type.Object({ id: Type.String() }) })
|
|
578
|
+
* .handler(async (c) => {
|
|
579
|
+
* const { params } = await c.data();
|
|
580
|
+
* return await db.user.findUnique({ where: { id: params.id } });
|
|
581
|
+
* });
|
|
582
|
+
*
|
|
583
|
+
* // POST request
|
|
584
|
+
* export const createUser = route.post('/users')
|
|
585
|
+
* .input({ body: Type.Object({ name: Type.String(), email: Type.String() }) })
|
|
586
|
+
* .handler(async (c) => {
|
|
587
|
+
* const { body } = await c.data();
|
|
588
|
+
* return c.created(await db.user.create({ data: body }));
|
|
589
|
+
* });
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
declare const route: {
|
|
593
|
+
get: (path: string) => RouteBuilder;
|
|
594
|
+
post: (path: string) => RouteBuilder;
|
|
595
|
+
put: (path: string) => RouteBuilder;
|
|
596
|
+
patch: (path: string) => RouteBuilder;
|
|
597
|
+
delete: (path: string) => RouteBuilder;
|
|
32
598
|
};
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Router Definition
|
|
602
|
+
*
|
|
603
|
+
* Provides router composition and middleware management
|
|
604
|
+
*/
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Router definition - holds all routes
|
|
608
|
+
*/
|
|
609
|
+
interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>> {
|
|
610
|
+
routes: TRoutes;
|
|
611
|
+
_routes: TRoutes;
|
|
612
|
+
_packageRouters: Router<any>[];
|
|
613
|
+
_globalMiddlewares: NamedMiddleware<string>[];
|
|
614
|
+
/**
|
|
615
|
+
* Register package routers (type-hidden)
|
|
616
|
+
*
|
|
617
|
+
* Package routes are:
|
|
618
|
+
* - Recognized by RPC proxy and backend
|
|
619
|
+
* - NOT exposed in client types (use package's own API like authApi, cmsApi)
|
|
620
|
+
*
|
|
621
|
+
* @example
|
|
622
|
+
* ```ts
|
|
623
|
+
* import { authRouter } from '@spfn/auth/server';
|
|
624
|
+
* import { cmsAppRouter } from '@spfn/cms/server';
|
|
625
|
+
*
|
|
626
|
+
* export const appRouter = defineRouter({
|
|
627
|
+
* getRoot,
|
|
628
|
+
* getHealth,
|
|
629
|
+
* })
|
|
630
|
+
* .packages([authRouter, cmsAppRouter]);
|
|
631
|
+
*
|
|
632
|
+
* // Client usage:
|
|
633
|
+
* // api.getRoot.call({}) - app routes
|
|
634
|
+
* // authApi.login.call({}) - package API
|
|
635
|
+
* ```
|
|
636
|
+
*/
|
|
637
|
+
packages(routers: Router<any>[]): Router<TRoutes>;
|
|
638
|
+
/**
|
|
639
|
+
* Register global middlewares
|
|
640
|
+
*
|
|
641
|
+
* Applied to all routes unless explicitly skipped via .skip()
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```ts
|
|
645
|
+
* import { authMiddleware, loggingMiddleware } from './middlewares';
|
|
646
|
+
*
|
|
647
|
+
* export const appRouter = defineRouter({
|
|
648
|
+
* getRoot,
|
|
649
|
+
* getHealth,
|
|
650
|
+
* })
|
|
651
|
+
* .packages([authRouter])
|
|
652
|
+
* .use([authMiddleware, loggingMiddleware]);
|
|
653
|
+
* ```
|
|
654
|
+
*/
|
|
655
|
+
use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Define a router with multiple routes (tRPC-style)
|
|
659
|
+
*
|
|
660
|
+
* Supports chainable API for packages and middlewares:
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```ts
|
|
664
|
+
* // Basic usage
|
|
665
|
+
* export const appRouter = defineRouter({
|
|
666
|
+
* getRoot,
|
|
667
|
+
* getHealth,
|
|
668
|
+
* listExamples,
|
|
669
|
+
* });
|
|
670
|
+
*
|
|
671
|
+
* // With package routers (type-hidden)
|
|
672
|
+
* export const appRouter = defineRouter({
|
|
673
|
+
* getRoot,
|
|
674
|
+
* getHealth,
|
|
675
|
+
* })
|
|
676
|
+
* .packages([authRouter, cmsAppRouter]);
|
|
677
|
+
*
|
|
678
|
+
* // With global middlewares
|
|
679
|
+
* export const appRouter = defineRouter({
|
|
680
|
+
* getRoot,
|
|
681
|
+
* getHealth,
|
|
682
|
+
* })
|
|
683
|
+
* .packages([authRouter])
|
|
684
|
+
* .use([authMiddleware, loggingMiddleware]);
|
|
685
|
+
*
|
|
686
|
+
* export type AppRouter = typeof appRouter;
|
|
687
|
+
* ```
|
|
688
|
+
*
|
|
689
|
+
* Package routes:
|
|
690
|
+
* - Recognized by RPC proxy and backend for routing
|
|
691
|
+
* - NOT included in AppRouter type (use authApi, cmsApi instead)
|
|
692
|
+
* - Prevents confusion between app API and package APIs
|
|
693
|
+
*/
|
|
694
|
+
declare function defineRouter<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(routes: TRoutes): Router<TRoutes>;
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Route Registration for define-route style routing
|
|
698
|
+
*
|
|
699
|
+
* Registers routes defined with route.get()...handler() to Hono app
|
|
700
|
+
*/
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Registered route information for logging
|
|
704
|
+
*/
|
|
705
|
+
interface RegisteredRoute {
|
|
706
|
+
method: HttpMethod;
|
|
707
|
+
path: string;
|
|
708
|
+
name: string;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Register routes from defineRouter() to Hono app
|
|
712
|
+
*
|
|
713
|
+
* @param app - Hono app instance
|
|
714
|
+
* @param router - Router definition
|
|
715
|
+
* @param namedMiddlewares - Optional server-level named middlewares
|
|
716
|
+
*
|
|
717
|
+
* @param collectedRoutes
|
|
718
|
+
* @example
|
|
719
|
+
* ```ts
|
|
720
|
+
* const appRouter = defineRouter({
|
|
721
|
+
* getUser: route.get('/users/:id')...
|
|
722
|
+
* createUser: route.post('/users')...
|
|
723
|
+
* });
|
|
724
|
+
*
|
|
725
|
+
* const app = new Hono();
|
|
726
|
+
* const namedMiddlewares = [
|
|
727
|
+
* { name: 'auth', handler: AuthMiddleware() },
|
|
728
|
+
* { name: 'rateLimit', handler: RateLimitMiddleware() },
|
|
729
|
+
* ];
|
|
730
|
+
* registerRoutes(app, appRouter, namedMiddlewares);
|
|
731
|
+
* ```
|
|
732
|
+
*/
|
|
733
|
+
declare function registerRoutes<TRoutes extends Record<string, RouteDef<any> | Router<any>>>(app: Hono, router: Router<TRoutes>, namedMiddlewares?: ReadonlyArray<{
|
|
734
|
+
name: string;
|
|
735
|
+
handler: MiddlewareHandler;
|
|
736
|
+
}>, collectedRoutes?: RegisteredRoute[]): RegisteredRoute[];
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Type guard for HttpMethod
|
|
740
|
+
*/
|
|
741
|
+
declare function isHttpMethod(value: unknown): value is HttpMethod;
|
|
742
|
+
/**
|
|
743
|
+
* Nullable - Creates a union of T | null
|
|
744
|
+
*
|
|
745
|
+
* @example
|
|
746
|
+
* ```typescript
|
|
747
|
+
* // string | null
|
|
748
|
+
* firstName: Nullable(Type.String())
|
|
749
|
+
* ```
|
|
750
|
+
*/
|
|
751
|
+
declare const Nullable: <T extends TSchema>(schema: T) => _sinclair_typebox.TUnion<[T, _sinclair_typebox.TNull]>;
|
|
752
|
+
/**
|
|
753
|
+
* OptionalNullable - Creates a union of T | null | undefined
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```typescript
|
|
757
|
+
* // string | null | undefined
|
|
758
|
+
* lastName: OptionalNullable(Type.String())
|
|
759
|
+
* ```
|
|
760
|
+
*/
|
|
761
|
+
declare const OptionalNullable: <T extends TSchema>(schema: T) => _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[T, _sinclair_typebox.TNull]>>;
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* File Schema Helpers for TypeBox
|
|
765
|
+
*
|
|
766
|
+
* Provides TypeBox schema definitions for file upload handling
|
|
767
|
+
* with optional validation constraints.
|
|
768
|
+
*/
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* File validation options
|
|
772
|
+
*/
|
|
773
|
+
interface FileSchemaOptions {
|
|
774
|
+
/**
|
|
775
|
+
* Maximum file size in bytes
|
|
776
|
+
*
|
|
777
|
+
* @example 5 * 1024 * 1024 // 5MB
|
|
778
|
+
*/
|
|
779
|
+
maxSize?: number;
|
|
780
|
+
/**
|
|
781
|
+
* Allowed MIME types
|
|
782
|
+
*
|
|
783
|
+
* @example ['image/jpeg', 'image/png', 'image/webp']
|
|
784
|
+
*/
|
|
785
|
+
allowedTypes?: string[];
|
|
786
|
+
/**
|
|
787
|
+
* Minimum file size in bytes (optional)
|
|
788
|
+
*
|
|
789
|
+
* @example 1024 // 1KB minimum
|
|
790
|
+
*/
|
|
791
|
+
minSize?: number;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* File array validation options
|
|
795
|
+
*/
|
|
796
|
+
interface FileArraySchemaOptions extends FileSchemaOptions {
|
|
797
|
+
/**
|
|
798
|
+
* Maximum number of files
|
|
799
|
+
*
|
|
800
|
+
* @example 5
|
|
801
|
+
*/
|
|
802
|
+
maxFiles?: number;
|
|
803
|
+
/**
|
|
804
|
+
* Minimum number of files (optional)
|
|
805
|
+
*
|
|
806
|
+
* @example 1
|
|
807
|
+
*/
|
|
808
|
+
minFiles?: number;
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Internal schema type with file validation metadata
|
|
812
|
+
*/
|
|
813
|
+
interface FileSchemaType extends TSchema {
|
|
814
|
+
[Kind]: 'File';
|
|
815
|
+
fileOptions?: FileSchemaOptions;
|
|
816
|
+
}
|
|
817
|
+
interface FileArraySchemaType extends TSchema {
|
|
818
|
+
[Kind]: 'FileArray';
|
|
819
|
+
fileOptions?: FileArraySchemaOptions;
|
|
820
|
+
}
|
|
33
821
|
/**
|
|
34
|
-
* Create
|
|
822
|
+
* Create a File schema with optional validation
|
|
35
823
|
*
|
|
36
|
-
*
|
|
824
|
+
* @example
|
|
825
|
+
* ```ts
|
|
826
|
+
* // Basic usage (no validation)
|
|
827
|
+
* formData: Type.Object({
|
|
828
|
+
* file: FileSchema()
|
|
829
|
+
* })
|
|
830
|
+
*
|
|
831
|
+
* // With validation
|
|
832
|
+
* formData: Type.Object({
|
|
833
|
+
* avatar: FileSchema({
|
|
834
|
+
* maxSize: 5 * 1024 * 1024, // 5MB
|
|
835
|
+
* allowedTypes: ['image/jpeg', 'image/png', 'image/webp']
|
|
836
|
+
* })
|
|
837
|
+
* })
|
|
838
|
+
* ```
|
|
839
|
+
*/
|
|
840
|
+
declare function FileSchema(options?: FileSchemaOptions): FileSchemaType;
|
|
841
|
+
/**
|
|
842
|
+
* Create a File array schema with optional validation
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```ts
|
|
846
|
+
* // Basic usage (no validation)
|
|
847
|
+
* formData: Type.Object({
|
|
848
|
+
* files: FileArraySchema()
|
|
849
|
+
* })
|
|
850
|
+
*
|
|
851
|
+
* // With validation
|
|
852
|
+
* formData: Type.Object({
|
|
853
|
+
* documents: FileArraySchema({
|
|
854
|
+
* maxSize: 10 * 1024 * 1024, // 10MB per file
|
|
855
|
+
* maxFiles: 5,
|
|
856
|
+
* allowedTypes: ['application/pdf', 'application/msword']
|
|
857
|
+
* })
|
|
858
|
+
* })
|
|
859
|
+
* ```
|
|
860
|
+
*/
|
|
861
|
+
declare function FileArraySchema(options?: FileArraySchemaOptions): FileArraySchemaType;
|
|
862
|
+
/**
|
|
863
|
+
* Create an optional File schema with validation
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* ```ts
|
|
867
|
+
* formData: Type.Object({
|
|
868
|
+
* name: Type.String(),
|
|
869
|
+
* avatar: OptionalFileSchema({
|
|
870
|
+
* maxSize: 2 * 1024 * 1024,
|
|
871
|
+
* allowedTypes: ['image/jpeg', 'image/png']
|
|
872
|
+
* })
|
|
873
|
+
* })
|
|
874
|
+
* ```
|
|
875
|
+
*/
|
|
876
|
+
declare function OptionalFileSchema(options?: FileSchemaOptions): TSchema;
|
|
877
|
+
/**
|
|
878
|
+
* Check if a schema is a File schema
|
|
879
|
+
*/
|
|
880
|
+
declare function isFileSchema(schema: TSchema): schema is FileSchemaType;
|
|
881
|
+
/**
|
|
882
|
+
* Check if a schema is a FileArray schema
|
|
883
|
+
*/
|
|
884
|
+
declare function isFileArraySchema(schema: TSchema): schema is FileArraySchemaType;
|
|
885
|
+
/**
|
|
886
|
+
* Get file options from schema
|
|
887
|
+
*/
|
|
888
|
+
declare function getFileOptions(schema: TSchema): FileSchemaOptions | FileArraySchemaOptions | undefined;
|
|
889
|
+
/**
|
|
890
|
+
* Format file size for error messages
|
|
37
891
|
*/
|
|
38
|
-
declare function
|
|
892
|
+
declare function formatFileSize(bytes: number): string;
|
|
39
893
|
|
|
40
|
-
export {
|
|
894
|
+
export { type ExtractMiddlewareNames, FileArraySchema, type FileArraySchemaOptions, type FileArraySchemaType, FileSchema, type FileSchemaOptions, type FileSchemaType, HttpMethod, type MergedInput, type NamedMiddleware, type NamedMiddlewareFactory, Nullable, OptionalFileSchema, OptionalNullable, type PaginatedResult, type RegisteredRoute, type RouteBuilderContext, type RouteDef, type RouteHandlerFn, type RouteInput, type Router, defineMiddleware, defineMiddlewareFactory, defineRouter, formatFileSize, getFileOptions, isFileArraySchema, isFileSchema, isHttpMethod, registerRoutes, route };
|