@spfn/core 0.3.0-beta.2 → 0.3.0-beta.21

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 (94) hide show
  1. package/README.md +343 -16
  2. package/dist/app-config/index.d.ts +134 -0
  3. package/dist/app-config/index.js +58 -0
  4. package/dist/app-config/index.js.map +1 -0
  5. package/dist/authz/index.js +1 -381
  6. package/dist/authz/index.js.map +1 -1
  7. package/dist/{boss-D16fO2oG.d.ts → boss-BiDCbbAQ.d.ts} +60 -7
  8. package/dist/codegen/index.d.ts +93 -15
  9. package/dist/codegen/index.js +626 -165
  10. package/dist/codegen/index.js.map +1 -1
  11. package/dist/config/index.d.ts +0 -63
  12. package/dist/config/index.js +15 -15
  13. package/dist/config/index.js.map +1 -1
  14. package/dist/contract/index.d.ts +40 -17
  15. package/dist/contract/index.js +147 -23
  16. package/dist/contract/index.js.map +1 -1
  17. package/dist/db/index.d.ts +201 -34
  18. package/dist/db/index.js +294 -158
  19. package/dist/db/index.js.map +1 -1
  20. package/dist/{define-middleware-DfDP39Nq.d.ts → define-middleware-CVKgqo8S.d.ts} +1 -1
  21. package/dist/env/index.d.ts +6 -442
  22. package/dist/env/index.js +13 -16
  23. package/dist/env/index.js.map +1 -1
  24. package/dist/env/loader.js +24 -1
  25. package/dist/env/loader.js.map +1 -1
  26. package/dist/error-TsUIjp_x.d.ts +157 -0
  27. package/dist/errors/index.js +1 -381
  28. package/dist/errors/index.js.map +1 -1
  29. package/dist/event/index.d.ts +5 -5
  30. package/dist/event/index.js +32 -1
  31. package/dist/event/index.js.map +1 -1
  32. package/dist/event/sse/client.d.ts +18 -4
  33. package/dist/event/sse/client.js +38 -2
  34. package/dist/event/sse/client.js.map +1 -1
  35. package/dist/event/sse/index.d.ts +5 -4
  36. package/dist/event/ws/client.d.ts +3 -2
  37. package/dist/event/ws/index.d.ts +4 -3
  38. package/dist/job/index.d.ts +48 -3
  39. package/dist/job/index.js +131 -52
  40. package/dist/job/index.js.map +1 -1
  41. package/dist/logger/index.js +0 -12
  42. package/dist/logger/index.js.map +1 -1
  43. package/dist/middleware/index.d.ts +6 -2
  44. package/dist/middleware/index.js +7 -388
  45. package/dist/middleware/index.js.map +1 -1
  46. package/dist/nextjs/index.d.ts +88 -8
  47. package/dist/nextjs/index.js +138 -62
  48. package/dist/nextjs/index.js.map +1 -1
  49. package/dist/nextjs/server.d.ts +45 -7
  50. package/dist/nextjs/server.js +14 -0
  51. package/dist/nextjs/server.js.map +1 -1
  52. package/dist/ops/index.d.ts +62 -43
  53. package/dist/ops/index.js +638 -97
  54. package/dist/ops/index.js.map +1 -1
  55. package/dist/registry-C2KQCh0m.d.ts +473 -0
  56. package/dist/route/index.d.ts +6 -3
  57. package/dist/route/index.js +97 -28
  58. package/dist/route/index.js.map +1 -1
  59. package/dist/route/types.d.ts +7 -1
  60. package/dist/{router-Qbssr11H.d.ts → route-builder-DRW-MSCJ.d.ts} +52 -127
  61. package/dist/router-4YoIy5fN.d.ts +172 -0
  62. package/dist/{token-manager-BT5EnUAR.d.ts → router-CysBmgv3.d.ts} +81 -127
  63. package/dist/server/index.d.ts +185 -14
  64. package/dist/server/index.js +883 -78
  65. package/dist/server/index.js.map +1 -1
  66. package/dist/surface/index.d.ts +115 -0
  67. package/dist/surface/index.js +1003 -0
  68. package/dist/surface/index.js.map +1 -0
  69. package/dist/token-manager-Ckjt2-qq.d.ts +122 -0
  70. package/dist/{types-D1c57Ko-.d.ts → types-5Rl11_Nb.d.ts} +43 -4
  71. package/dist/{types-ZQODsBft.d.ts → types-B8zAxFsE.d.ts} +2 -1
  72. package/dist/types-CxhPH4s_.d.ts +375 -0
  73. package/dist/{types-2AbaW4Ie.d.ts → types-W4u_S0lF.d.ts} +4 -3
  74. package/docs/file-upload.md +195 -333
  75. package/package.json +20 -9
  76. package/src/cache/README.md +330 -0
  77. package/src/codegen/README.md +634 -0
  78. package/src/config/README.md +333 -0
  79. package/src/contract/README.md +440 -0
  80. package/src/db/README.md +589 -0
  81. package/src/db/manager/README.md +500 -0
  82. package/src/db/schema/README.md +354 -0
  83. package/src/db/transaction/README.md +822 -0
  84. package/src/env/README.md +787 -0
  85. package/src/errors/README.md +429 -0
  86. package/src/event/README.md +789 -0
  87. package/src/job/README.md +613 -0
  88. package/src/logger/README.md +321 -0
  89. package/src/middleware/README.md +634 -0
  90. package/src/nextjs/README.md +688 -0
  91. package/src/route/README.md +763 -0
  92. package/src/security/README.md +100 -0
  93. package/src/server/README.md +704 -0
  94. package/dist/types-9oszaJqp.d.ts +0 -250
@@ -0,0 +1,375 @@
1
+ import { TSchema, Static } from '@sinclair/typebox';
2
+ import { ErrorRegistry, ErrorRegistryInput } from '@spfn/core/errors';
3
+ import { RouteDef, RouteInput } from '@spfn/core/route';
4
+
5
+ /**
6
+ * Convert File types in schema to actual File for client usage
7
+ *
8
+ * TypeBox File schemas become actual File objects on the client side.
9
+ */
10
+ type ConvertFileTypes<T> = T extends File ? File : T extends File[] ? File[] : T;
11
+ /**
12
+ * Extract form data input type with File support
13
+ *
14
+ * Maps schema types to runtime types, converting FileSchema to File.
15
+ */
16
+ type FormDataInput<T> = {
17
+ [K in keyof T]: ConvertFileTypes<T[K]>;
18
+ };
19
+ /**
20
+ * Extract structured input from RouteInput
21
+ *
22
+ * Converts TypeBox schemas to their static types for each input field.
23
+ */
24
+ type StructuredInput<TInput extends RouteInput> = {
25
+ params: TInput['params'] extends TSchema ? Static<TInput['params']> : {};
26
+ query: TInput['query'] extends TSchema ? Static<TInput['query']> : {};
27
+ body: TInput['body'] extends TSchema ? Static<TInput['body']> : {};
28
+ formData: TInput['formData'] extends TSchema ? FormDataInput<Static<TInput['formData']>> : {};
29
+ headers: TInput['headers'] extends TSchema ? Static<TInput['headers']> : {};
30
+ cookies: TInput['cookies'] extends TSchema ? Static<TInput['cookies']> : {};
31
+ };
32
+ /**
33
+ * Infer route input type from RouteDef
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Server route definition
38
+ * const getUser = route.get('/users/:id')
39
+ * .input({ params: Type.Object({ id: Type.String() }) })
40
+ * .handler(...);
41
+ *
42
+ * // Client: extract input type
43
+ * type Input = InferRouteInput<typeof getUser>;
44
+ * // { params: { id: string }, query: {}, body: {}, ... }
45
+ * ```
46
+ */
47
+ type InferRouteInput<TRoute> = TRoute extends RouteDef<infer TInput, any, any> ? StructuredInput<TInput> : never;
48
+ /**
49
+ * Infer route output type from RouteDef
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * // Server route definition
54
+ * const getUser = route.get('/users/:id')
55
+ * .handler(async (c) => {
56
+ * return { id: '1', name: 'John' };
57
+ * });
58
+ *
59
+ * // Client: extract output type
60
+ * type Output = InferRouteOutput<typeof getUser>;
61
+ * // { id: string, name: string }
62
+ * ```
63
+ */
64
+ type InferRouteOutput<TRoute> = TRoute extends RouteDef<any, any, infer TResponse> ? TResponse : never;
65
+ /**
66
+ * True only for `any`.
67
+ *
68
+ * `any` satisfies both branches of a conditional type, so a router whose routes are
69
+ * unresolved (`Router<any>`) would otherwise flatten into "every name exists" and
70
+ * accept a misspelled route name. A route record that is not known contributes no
71
+ * names instead.
72
+ */
73
+ type IsAny<T> = 0 extends (1 & T) ? true : false;
74
+ /**
75
+ * True when `T` is a union of two or more members.
76
+ */
77
+ type IsUnion<T, TCopy = T> = T extends unknown ? ([TCopy] extends [T] ? false : true) : false;
78
+ /**
79
+ * One leaf of a router tree: the name a route is registered under, its definition, and
80
+ * the path it was reached by.
81
+ *
82
+ * A route is identified by its own key whatever depth it sits at — `registerRoutes`
83
+ * mounts a nested route under that key, and the contract collector reports a collision
84
+ * on it. Nesting groups the source; it does not qualify the name. The trail is carried
85
+ * so that one name reached through two branches stays two leaves.
86
+ */
87
+ interface RouteLeaf<TName extends PropertyKey, TDef, TTrail extends PropertyKey[]> {
88
+ name: TName;
89
+ def: TDef;
90
+ trail: TTrail;
91
+ }
92
+ /** Decrements the nesting budget; `PrevDepth[0]` is `never`, which ends the walk. */
93
+ type PrevDepth = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
94
+ /**
95
+ * How many levels of nesting are followed.
96
+ *
97
+ * A budget rather than open recursion: a router type that refers to itself would
98
+ * otherwise never terminate, and TypeScript would report an instantiation-depth error
99
+ * in whichever component happened to read a route type. A route below the budget is not
100
+ * visible to these types; ten levels is far past any hand-written tree.
101
+ */
102
+ type MaxNesting = 10;
103
+ /**
104
+ * Collect every route in a route record as a leaf, descending into nested routers
105
+ * exactly as `registerRoutes` does.
106
+ */
107
+ type RouteLeaves<TRoutes, TTrail extends PropertyKey[] = [], TDepth extends number = MaxNesting> = [
108
+ TDepth
109
+ ] extends [never] ? never : IsAny<TRoutes> extends true ? never : {
110
+ [K in keyof TRoutes]-?: RouteLeafOf<K, Exclude<TRoutes[K], undefined>, [...TTrail, K], TDepth>;
111
+ }[keyof TRoutes];
112
+ /**
113
+ * A nested router contributes its own leaves; anything else is a leaf.
114
+ *
115
+ * `{ _routes: ... }` is the runtime's own test for a router (`isRouter`), and a
116
+ * `RouteDef` carries `handler`, `_input` and `_response` but never `_routes`, so a
117
+ * route cannot be mistaken for a router.
118
+ */
119
+ type RouteLeafOf<TName extends PropertyKey, TValue, TTrail extends PropertyKey[], TDepth extends number> = TValue extends {
120
+ _routes: infer TNested;
121
+ } ? RouteLeaves<TNested, TTrail, PrevDepth[TDepth]> : RouteLeaf<TName, TValue, TTrail>;
122
+ /** Every name the tree declares. */
123
+ type LeafNames<TLeaves> = Extract<TLeaves extends RouteLeaf<infer TName, any, any> ? TName : never, PropertyKey>;
124
+ /**
125
+ * The leaves declaring exactly this name.
126
+ *
127
+ * Exactly, not assignably: a record typed `Record<string, RouteDef>` contributes the
128
+ * name `string`, which every literal name is assignable to, and a loose sibling must
129
+ * not make each of its siblings look doubly declared.
130
+ */
131
+ type LeavesNamed<TLeaves, TName extends PropertyKey> = TLeaves extends RouteLeaf<infer TLeafName, any, any> ? [TLeafName] extends [TName] ? [TName] extends [TLeafName] ? TLeaves : never : never : never;
132
+ /** The definition a leaf carries. */
133
+ type LeafDef<TLeaves> = TLeaves extends RouteLeaf<any, infer TDef, any> ? TDef : never;
134
+ /** Where a leaf was found — one distinct trail per place the name is declared. */
135
+ type LeafTrail<TLeaves> = TLeaves extends RouteLeaf<any, any, infer TTrail> ? TTrail : never;
136
+ /**
137
+ * Flatten the leaves into one namespace of name → definition.
138
+ *
139
+ * A name that two branches both declare is left out rather than resolved to one of
140
+ * them: the tree gives it no single meaning, and picking a winner silently would hand
141
+ * a component another route's types. Naming it then fails the `K` constraint on
142
+ * `RouterOutput`/`RouterInput`, on the line that named it. Two places is counted by
143
+ * trail, so one slot holding a union of route definitions stays one route and still
144
+ * infers the union.
145
+ *
146
+ * The result is a fresh mapped type, so no key carries an optional or readonly modifier
147
+ * out of the source record — a route key that turned optional would change what
148
+ * `InferRouteInput` returns for it.
149
+ */
150
+ type FlattenLeaves<TLeaves> = {
151
+ [TName in LeafNames<TLeaves> as UnambiguousName<TLeaves, TName>]: LeafDef<LeavesNamed<TLeaves, TName>>;
152
+ };
153
+ /** The name itself when one place declares it, and nothing when two do. */
154
+ type UnambiguousName<TLeaves, TName extends PropertyKey> = IsUnion<LeafTrail<LeavesNamed<TLeaves, TName>>> extends true ? never : TName;
155
+ /**
156
+ * Extract routes from Router type
157
+ *
158
+ * `Router<TRoutes>` holds its routes in `_routes`, and a value in that record is either
159
+ * a route or another router. Nested routers are flattened away, so every route appears
160
+ * here under its own name at one level — the same set of names the server registers.
161
+ *
162
+ * Exported so that the typed client (`Client`, in `./builder`) names a route the same
163
+ * way `RouterOutput` and `RouterInput` do; the two must not diverge, or a route would
164
+ * type in one place and not the other. It stays internal to the package: the
165
+ * `@spfn/core/nextjs` entry point exports a fixed list of type names, and this is not
166
+ * one of them.
167
+ */
168
+ type ExtractRoutes<TRouter> = TRouter extends {
169
+ _routes: infer TRoutes;
170
+ } ? FlattenLeaves<RouteLeaves<TRoutes>> : FlattenLeaves<RouteLeaves<TRouter>>;
171
+ /**
172
+ * Extract output type for a specific route from router
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * import type { RouterOutput } from '@spfn/core/nextjs';
177
+ * import type { AppRouter } from '@/server/router';
178
+ *
179
+ * // Get output type for a specific route
180
+ * type ListData = RouterOutput<AppRouter, 'listExamples'>;
181
+ *
182
+ * // Use in props
183
+ * interface Props {
184
+ * data: RouterOutput<AppRouter, 'listExamples'>;
185
+ * }
186
+ *
187
+ * // Extract item type from paginated response
188
+ * type Example = RouterOutput<AppRouter, 'listExamples'>['items'][number];
189
+ * ```
190
+ *
191
+ * A route declared inside a nested router is named the same way. The server registers
192
+ * a nested route under its own key, so `K` is the flat set of route names in the tree
193
+ * and the depth the route was declared at never appears here:
194
+ *
195
+ * ```typescript
196
+ * const appRouter = defineRouter({
197
+ * getRoot,
198
+ * examples: defineRouter({ listExamples, createExample }),
199
+ * });
200
+ *
201
+ * type ListData = RouterOutput<typeof appRouter, 'listExamples'>;
202
+ * ```
203
+ *
204
+ * Two branches declaring the same name make that name ambiguous, and it is left out of
205
+ * `K` rather than resolved to one of them — naming it is a compile error here instead
206
+ * of the wrong route's types reaching a component.
207
+ */
208
+ type RouterOutput<TRouter, K extends keyof ExtractRoutes<TRouter>> = InferRouteOutput<ExtractRoutes<TRouter>[K]>;
209
+ /**
210
+ * Extract input type for a specific route from router
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * import type { RouterInput } from '@spfn/core/nextjs';
215
+ * import type { AppRouter } from '@/server/router';
216
+ *
217
+ * // Get input type for a specific route
218
+ * type CreateInput = RouterInput<AppRouter, 'createExample'>;
219
+ *
220
+ * // Use in function parameter
221
+ * function submitForm(data: RouterInput<AppRouter, 'createExample'>['body']) {
222
+ * // ...
223
+ * }
224
+ * ```
225
+ *
226
+ * `K` is the flat set of route names, so a route declared inside a nested router is
227
+ * named by its own key here too — see {@link RouterOutput}.
228
+ */
229
+ type RouterInput<TRouter, K extends keyof ExtractRoutes<TRouter>> = InferRouteInput<ExtractRoutes<TRouter>[K]>;
230
+ /**
231
+ * Cookie options for setCookie
232
+ */
233
+ interface CookieOptions {
234
+ httpOnly?: boolean;
235
+ secure?: boolean;
236
+ sameSite?: 'strict' | 'lax' | 'none';
237
+ maxAge?: number;
238
+ path?: string;
239
+ domain?: string;
240
+ }
241
+ /**
242
+ * Cookie to set in response
243
+ */
244
+ interface SetCookie {
245
+ name: string;
246
+ value: string;
247
+ options?: CookieOptions;
248
+ }
249
+ /**
250
+ * Request interceptor - called before fetch
251
+ */
252
+ type RequestInterceptor = (url: string, init: RequestInit) => Promise<RequestInit> | RequestInit;
253
+ /**
254
+ * Response interceptor - called after fetch
255
+ *
256
+ * It runs outside the client's transport error handling, so throwing from it is a
257
+ * supported way to end the call: the error reaches the caller unchanged, which is what
258
+ * lets `redirect()` and `notFound()` navigate from one (issue #104). Returning
259
+ * `{ response, body }` replaces both for everything after it, error-status handling
260
+ * included.
261
+ */
262
+ type ResponseInterceptor = (response: Response, body: any) => Promise<{
263
+ response: Response;
264
+ body: any;
265
+ }> | {
266
+ response: Response;
267
+ body: any;
268
+ };
269
+ /**
270
+ * Client configuration
271
+ */
272
+ interface ApiConfig {
273
+ /**
274
+ * Base URL for RPC endpoint
275
+ *
276
+ * @default '/api/rpc'
277
+ * @example '/api/rpc', 'http://localhost:3000/api/rpc'
278
+ */
279
+ baseUrl?: string;
280
+ /**
281
+ * Default headers for all requests
282
+ */
283
+ headers?: Record<string, string>;
284
+ /**
285
+ * Request timeout in milliseconds
286
+ *
287
+ * @default env.SERVER_TIMEOUT (120000)
288
+ */
289
+ timeout?: number;
290
+ /**
291
+ * Custom fetch implementation
292
+ */
293
+ fetch?: typeof fetch;
294
+ /**
295
+ * Global request interceptor
296
+ */
297
+ onRequest?: RequestInterceptor;
298
+ /**
299
+ * Global response interceptor
300
+ */
301
+ onResponse?: ResponseInterceptor;
302
+ /**
303
+ * Custom error registry for deserialization
304
+ *
305
+ * Core HTTP errors are automatically registered. Use this to add your custom application errors.
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * import { errorRegistry } from '@spfn/core/errors';
310
+ * import { authErrorRegistry } from '@myapp/auth/errors';
311
+ * import { PaymentFailedError } from '@/server/errors';
312
+ *
313
+ * const api = createApi<AppRouter>({
314
+ * errorRegistry: [errorRegistry, authErrorRegistry, PaymentFailedError]
315
+ * });
316
+ * ```
317
+ */
318
+ errorRegistry?: ErrorRegistry | ErrorRegistryInput[];
319
+ /**
320
+ * Enable debug logging
321
+ *
322
+ * @default false
323
+ */
324
+ debug?: boolean;
325
+ }
326
+ /**
327
+ * Per-call options
328
+ */
329
+ interface CallOptions {
330
+ /**
331
+ * Request timeout in milliseconds, covering the wait for response headers
332
+ * Overrides the global timeout set in ApiConfig; set it with `.timeout(ms)`
333
+ */
334
+ timeout?: number;
335
+ /**
336
+ * Additional headers for this request
337
+ */
338
+ headers?: Record<string, string>;
339
+ /**
340
+ * Override cookies for this request
341
+ *
342
+ * Note: Cookies are automatically forwarded by the proxy.
343
+ * Use this only when you need to override them.
344
+ */
345
+ cookies?: Record<string, string>;
346
+ /**
347
+ * Request-specific interceptor
348
+ */
349
+ onRequest?: RequestInterceptor;
350
+ /**
351
+ * Response-specific interceptor
352
+ */
353
+ onResponse?: ResponseInterceptor;
354
+ /**
355
+ * Next.js-specific fetch options
356
+ *
357
+ * @example
358
+ * // Time-based revalidation
359
+ * { next: { revalidate: 60 } }
360
+ *
361
+ * // Disable cache
362
+ * { cache: 'no-store' }
363
+ *
364
+ * // Tag-based revalidation
365
+ * { next: { tags: ['users'] } }
366
+ */
367
+ fetchOptions?: RequestInit & {
368
+ next?: {
369
+ revalidate?: number | false;
370
+ tags?: string[];
371
+ };
372
+ };
373
+ }
374
+
375
+ export type { ApiConfig as A, CallOptions as C, ExtractRoutes as E, InferRouteInput as I, RequestInterceptor as R, SetCookie as S, ResponseInterceptor as a, InferRouteOutput as b, CookieOptions as c, RouterInput as d, RouterOutput as e, StructuredInput as f };
@@ -1,5 +1,6 @@
1
1
  import { Context } from 'hono';
2
- import { E as EventDef, b as EventRouterDef, I as InferEventNames, e as InferEventPayload, i as SSETokenStore, S as SSETokenManager } from './token-manager-BT5EnUAR.js';
2
+ import { a as EventDef, i as EventRouterBase, I as InferEventNames, g as InferEventPayload } from './router-CysBmgv3.js';
3
+ import { c as SSETokenStore, S as SSETokenManager } from './token-manager-Ckjt2-qq.js';
3
4
 
4
5
  /**
5
6
  * WebSocket Types
@@ -8,9 +9,9 @@ import { E as EventDef, b as EventRouterDef, I as InferEventNames, e as InferEve
8
9
  /**
9
10
  * WebSocket Router Definition
10
11
  *
11
- * Extends EventRouterDef with client→server message handlers.
12
+ * Extends the event router shape with client→server message handlers.
12
13
  */
13
- interface WSRouterDef<TEvents extends Record<string, EventDef<any>>, TMessages extends WSMessageHandlers = WSMessageHandlers> extends EventRouterDef<TEvents> {
14
+ interface WSRouterDef<TEvents extends Record<string, EventDef<any>>, TMessages extends WSMessageHandlers = WSMessageHandlers> extends EventRouterBase<TEvents> {
14
15
  messages: TMessages;
15
16
  }
16
17
  /**