@mionjs/router 0.8.6 → 0.8.8

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 (141) hide show
  1. package/.dist/cjs/index.d.ts +1 -0
  2. package/.dist/cjs/index.d.ts.map +1 -0
  3. package/.dist/cjs/src/aot/aotCacheLoader.d.ts +1 -0
  4. package/.dist/cjs/src/aot/aotCacheLoader.d.ts.map +1 -0
  5. package/.dist/cjs/src/callContext.d.ts +1 -0
  6. package/.dist/cjs/src/callContext.d.ts.map +1 -0
  7. package/.dist/cjs/src/constants.d.ts +1 -0
  8. package/.dist/cjs/src/constants.d.ts.map +1 -0
  9. package/.dist/cjs/src/defaultRoutes.d.ts +1 -0
  10. package/.dist/cjs/src/defaultRoutes.d.ts.map +1 -0
  11. package/.dist/cjs/src/dispatch.d.ts +1 -0
  12. package/.dist/cjs/src/dispatch.d.ts.map +1 -0
  13. package/.dist/cjs/src/lib/aotEmitter.d.ts +1 -0
  14. package/.dist/cjs/src/lib/aotEmitter.d.ts.map +1 -0
  15. package/.dist/cjs/src/lib/dispatchError.d.ts +1 -0
  16. package/.dist/cjs/src/lib/dispatchError.d.ts.map +1 -0
  17. package/.dist/cjs/src/lib/handlers.d.ts +1 -0
  18. package/.dist/cjs/src/lib/handlers.d.ts.map +1 -0
  19. package/.dist/cjs/src/lib/headers.d.ts +1 -0
  20. package/.dist/cjs/src/lib/headers.d.ts.map +1 -0
  21. package/.dist/cjs/src/lib/methodsCache.d.ts +1 -0
  22. package/.dist/cjs/src/lib/methodsCache.d.ts.map +1 -0
  23. package/.dist/cjs/src/lib/queryBody.d.ts +1 -0
  24. package/.dist/cjs/src/lib/queryBody.d.ts.map +1 -0
  25. package/.dist/cjs/src/lib/reflection.d.ts +1 -0
  26. package/.dist/cjs/src/lib/reflection.d.ts.map +1 -0
  27. package/.dist/cjs/src/lib/remoteMethods.d.ts +1 -0
  28. package/.dist/cjs/src/lib/remoteMethods.d.ts.map +1 -0
  29. package/.dist/cjs/src/lib/test/aotEmitter-test-router.d.ts +1 -0
  30. package/.dist/cjs/src/lib/test/aotEmitter-test-router.d.ts.map +1 -0
  31. package/.dist/cjs/src/router.d.ts +1 -0
  32. package/.dist/cjs/src/router.d.ts.map +1 -0
  33. package/.dist/cjs/src/routes/client.routes.d.ts +1 -0
  34. package/.dist/cjs/src/routes/client.routes.d.ts.map +1 -0
  35. package/.dist/cjs/src/routes/errors.routes.d.ts +1 -0
  36. package/.dist/cjs/src/routes/errors.routes.d.ts.map +1 -0
  37. package/.dist/cjs/src/routes/mion.routes.d.ts +1 -0
  38. package/.dist/cjs/src/routes/mion.routes.d.ts.map +1 -0
  39. package/.dist/cjs/src/routes/serializer.routes.d.ts +1 -0
  40. package/.dist/cjs/src/routes/serializer.routes.d.ts.map +1 -0
  41. package/.dist/cjs/src/routesFlow.d.ts +1 -0
  42. package/.dist/cjs/src/routesFlow.d.ts.map +1 -0
  43. package/.dist/cjs/src/types/context.d.ts +1 -0
  44. package/.dist/cjs/src/types/context.d.ts.map +1 -0
  45. package/.dist/cjs/src/types/definitions.d.ts +1 -0
  46. package/.dist/cjs/src/types/definitions.d.ts.map +1 -0
  47. package/.dist/cjs/src/types/general.d.ts +1 -0
  48. package/.dist/cjs/src/types/general.d.ts.map +1 -0
  49. package/.dist/cjs/src/types/guards.d.ts +1 -0
  50. package/.dist/cjs/src/types/guards.d.ts.map +1 -0
  51. package/.dist/cjs/src/types/handlers.d.ts +1 -0
  52. package/.dist/cjs/src/types/handlers.d.ts.map +1 -0
  53. package/.dist/cjs/src/types/publicMethods.d.ts +1 -0
  54. package/.dist/cjs/src/types/publicMethods.d.ts.map +1 -0
  55. package/.dist/cjs/src/types/remoteMethods.d.ts +1 -0
  56. package/.dist/cjs/src/types/remoteMethods.d.ts.map +1 -0
  57. package/.dist/esm/index.d.ts +1 -0
  58. package/.dist/esm/index.d.ts.map +1 -0
  59. package/.dist/esm/src/aot/aotCacheLoader.d.ts +1 -0
  60. package/.dist/esm/src/aot/aotCacheLoader.d.ts.map +1 -0
  61. package/.dist/esm/src/callContext.d.ts +1 -0
  62. package/.dist/esm/src/callContext.d.ts.map +1 -0
  63. package/.dist/esm/src/constants.d.ts +1 -0
  64. package/.dist/esm/src/constants.d.ts.map +1 -0
  65. package/.dist/esm/src/defaultRoutes.d.ts +1 -0
  66. package/.dist/esm/src/defaultRoutes.d.ts.map +1 -0
  67. package/.dist/esm/src/dispatch.d.ts +1 -0
  68. package/.dist/esm/src/dispatch.d.ts.map +1 -0
  69. package/.dist/esm/src/lib/aotEmitter.d.ts +1 -0
  70. package/.dist/esm/src/lib/aotEmitter.d.ts.map +1 -0
  71. package/.dist/esm/src/lib/dispatchError.d.ts +1 -0
  72. package/.dist/esm/src/lib/dispatchError.d.ts.map +1 -0
  73. package/.dist/esm/src/lib/handlers.d.ts +1 -0
  74. package/.dist/esm/src/lib/handlers.d.ts.map +1 -0
  75. package/.dist/esm/src/lib/headers.d.ts +1 -0
  76. package/.dist/esm/src/lib/headers.d.ts.map +1 -0
  77. package/.dist/esm/src/lib/methodsCache.d.ts +1 -0
  78. package/.dist/esm/src/lib/methodsCache.d.ts.map +1 -0
  79. package/.dist/esm/src/lib/queryBody.d.ts +1 -0
  80. package/.dist/esm/src/lib/queryBody.d.ts.map +1 -0
  81. package/.dist/esm/src/lib/reflection.d.ts +1 -0
  82. package/.dist/esm/src/lib/reflection.d.ts.map +1 -0
  83. package/.dist/esm/src/lib/remoteMethods.d.ts +1 -0
  84. package/.dist/esm/src/lib/remoteMethods.d.ts.map +1 -0
  85. package/.dist/esm/src/lib/test/aotEmitter-test-router.d.ts +1 -0
  86. package/.dist/esm/src/lib/test/aotEmitter-test-router.d.ts.map +1 -0
  87. package/.dist/esm/src/router.d.ts +1 -0
  88. package/.dist/esm/src/router.d.ts.map +1 -0
  89. package/.dist/esm/src/routes/client.routes.d.ts +1 -0
  90. package/.dist/esm/src/routes/client.routes.d.ts.map +1 -0
  91. package/.dist/esm/src/routes/errors.routes.d.ts +1 -0
  92. package/.dist/esm/src/routes/errors.routes.d.ts.map +1 -0
  93. package/.dist/esm/src/routes/mion.routes.d.ts +1 -0
  94. package/.dist/esm/src/routes/mion.routes.d.ts.map +1 -0
  95. package/.dist/esm/src/routes/serializer.routes.d.ts +1 -0
  96. package/.dist/esm/src/routes/serializer.routes.d.ts.map +1 -0
  97. package/.dist/esm/src/routesFlow.d.ts +1 -0
  98. package/.dist/esm/src/routesFlow.d.ts.map +1 -0
  99. package/.dist/esm/src/types/context.d.ts +1 -0
  100. package/.dist/esm/src/types/context.d.ts.map +1 -0
  101. package/.dist/esm/src/types/definitions.d.ts +1 -0
  102. package/.dist/esm/src/types/definitions.d.ts.map +1 -0
  103. package/.dist/esm/src/types/general.d.ts +1 -0
  104. package/.dist/esm/src/types/general.d.ts.map +1 -0
  105. package/.dist/esm/src/types/guards.d.ts +1 -0
  106. package/.dist/esm/src/types/guards.d.ts.map +1 -0
  107. package/.dist/esm/src/types/handlers.d.ts +1 -0
  108. package/.dist/esm/src/types/handlers.d.ts.map +1 -0
  109. package/.dist/esm/src/types/publicMethods.d.ts +1 -0
  110. package/.dist/esm/src/types/publicMethods.d.ts.map +1 -0
  111. package/.dist/esm/src/types/remoteMethods.d.ts +1 -0
  112. package/.dist/esm/src/types/remoteMethods.d.ts.map +1 -0
  113. package/index.ts +29 -0
  114. package/package.json +9 -5
  115. package/src/aot/aotCacheLoader.ts +15 -0
  116. package/src/callContext.ts +193 -0
  117. package/src/constants.ts +45 -0
  118. package/src/defaultRoutes.ts +37 -0
  119. package/src/dispatch.ts +204 -0
  120. package/src/lib/aotEmitter.ts +140 -0
  121. package/src/lib/dispatchError.ts +60 -0
  122. package/src/lib/handlers.ts +75 -0
  123. package/src/lib/headers.ts +102 -0
  124. package/src/lib/methodsCache.ts +72 -0
  125. package/src/lib/queryBody.ts +40 -0
  126. package/src/lib/reflection.ts +517 -0
  127. package/src/lib/remoteMethods.ts +180 -0
  128. package/src/lib/test/aotEmitter-test-router.ts +40 -0
  129. package/src/router.ts +656 -0
  130. package/src/routes/client.routes.ts +108 -0
  131. package/src/routes/errors.routes.ts +51 -0
  132. package/src/routes/mion.routes.ts +11 -0
  133. package/src/routes/serializer.routes.ts +225 -0
  134. package/src/routesFlow.ts +320 -0
  135. package/src/types/context.ts +119 -0
  136. package/src/types/definitions.ts +53 -0
  137. package/src/types/general.ts +84 -0
  138. package/src/types/guards.ts +71 -0
  139. package/src/types/handlers.ts +49 -0
  140. package/src/types/publicMethods.ts +83 -0
  141. package/src/types/remoteMethods.ts +54 -0
@@ -0,0 +1,517 @@
1
+ /* ########
2
+ * 2024 mion
3
+ * Author: Ma-jerez
4
+ * License: MIT
5
+ * The software is provided "as is", without warranty of any kind.
6
+ * ######## */
7
+
8
+ import type {MethodWithJitFns, AnyFn, JitCompiledFunctions, MethodMetadata} from '@mionjs/core';
9
+ // Type-only imports from run-types - these don't load the module at runtime
10
+ import type {FunctionRunType, BaseRunType, MemberRunType, RunTypeOptions, JitFnCompiler} from '@mionjs/run-types';
11
+ import {Handler} from '../types/handlers.ts';
12
+ import {RouterOptions} from '../types/general.ts';
13
+ import {DEFAULT_ROUTE_OPTIONS, HEADER_HOOK_DEFAULT_PARAMS, ROUTE_DEFAULT_PARAMS} from '../constants.ts';
14
+ import {EMPTY_HASH, HeadersSubset, getJitFunctionsFromHash, getNoopJitFns, HandlerType} from '@mionjs/core';
15
+ import {getPersistedMethodMetadata} from './methodsCache.ts';
16
+ import {RouteOptions, MiddleFnOptions, HeadersMiddleFnOptions, MiddleFnMethod, HeadersMethod} from '../types/remoteMethods.ts';
17
+
18
+ // ############ This file is the only one importing '@mionjs/run-types' within the router ########
19
+ // In AOT mode, run-types is NOT loaded - all reflection data comes from the AOT cache
20
+
21
+ type MethodReflect = Omit<MethodWithJitFns, 'id' | 'type' | 'nestLevel' | 'pointer' | 'options'>;
22
+
23
+ // ############ AOT Cache Error ############
24
+
25
+ /**
26
+ * Error thrown when AOT mode is enabled but required data is missing from the AOT cache.
27
+ * This indicates that the AOT caches need to be regenerated using 'mion-build-aot' command.
28
+ */
29
+ export class AOTCacheError extends Error {
30
+ constructor(routeId: string, type: 'route' | 'middleFn' | 'rawMiddleFn' = 'route') {
31
+ const typeLabel = type === 'rawMiddleFn' ? 'Raw middleFn' : type === 'middleFn' ? 'MiddleFn' : 'Route/middleFn';
32
+ super(`${typeLabel} "${routeId}" not found in AOT cache.\n` + `Regenerate AOT caches using 'mion-build-aot' command.`);
33
+ this.name = 'AOTCacheError';
34
+ }
35
+ }
36
+
37
+ // ############ Run-Types Module Loading ############
38
+ type RunTypesModule = typeof import('@mionjs/run-types');
39
+ // Type definition for the dynamically imported run-types module
40
+ interface RunTypesFunctions {
41
+ JitFunctions: RunTypesModule['JitFunctions'];
42
+ reflectFunction: RunTypesModule['reflectFunction'];
43
+ isUnionRunType: RunTypesModule['isUnionRunType'];
44
+ isClassRunType: RunTypesModule['isClassRunType'];
45
+ isLiteralRunType: RunTypesModule['isLiteralRunType'];
46
+ isNeverRunType: RunTypesModule['isNeverRunType'];
47
+ }
48
+
49
+ // Cached run-types module - loaded once and reused
50
+ let runTypesModule: RunTypesFunctions | null = null;
51
+ let runTypesLoadPromise: Promise<RunTypesFunctions> | null = null;
52
+
53
+ /** Dynamically loads the @mionjs/run-types module. The module is cached after first load. */
54
+ async function loadRunTypesModule(): Promise<RunTypesFunctions> {
55
+ // Return cached module if already loaded
56
+ if (runTypesModule) return runTypesModule;
57
+
58
+ // Return existing promise if load is in progress
59
+ if (runTypesLoadPromise) return runTypesLoadPromise;
60
+
61
+ // Start loading the module
62
+ runTypesLoadPromise = import('@mionjs/run-types').then((module) => {
63
+ runTypesModule = {
64
+ JitFunctions: module.JitFunctions,
65
+ reflectFunction: module.reflectFunction,
66
+ isUnionRunType: module.isUnionRunType,
67
+ isClassRunType: module.isClassRunType,
68
+ isLiteralRunType: module.isLiteralRunType,
69
+ isNeverRunType: module.isNeverRunType,
70
+ };
71
+ return runTypesModule;
72
+ });
73
+
74
+ return runTypesLoadPromise;
75
+ }
76
+
77
+ /** Resets the run-types module cache. Useful for testing purposes only. */
78
+ export function resetRunTypesCache(): void {
79
+ runTypesModule = null;
80
+ runTypesLoadPromise = null;
81
+ }
82
+
83
+ /** Resets all reflection caches. Useful for testing purposes only. */
84
+ export function resetReflectionCaches(): void {
85
+ rawMiddleFnReflectionCache.clear();
86
+ // Note: functionRunTypeCache uses WeakMap so it doesn't need explicit clearing
87
+ // Note: _cachedReflection on MethodMetadata objects will be cleared when persistedMethods is reset
88
+ }
89
+
90
+ // ############ Raw MiddleFn Reflection Helper ############
91
+
92
+ // Cache for common raw middleFn reflections
93
+ const rawMiddleFnReflectionCache = new Map<string, MethodReflect>();
94
+
95
+ /**
96
+ * Creates a MethodReflect for raw middleFns.
97
+ * Raw middleFns don't need JIT functions - they always use NoopJitFns.
98
+ * Results are cached to avoid creating duplicate objects.
99
+ */
100
+ function createRawMiddleFnReflection(isAsync: boolean, hasReturnData: boolean = false, paramNames: string[] = []): MethodReflect {
101
+ // Create cache key from parameters
102
+ const cacheKey = `${isAsync}_${hasReturnData}_${paramNames.join(',')}`;
103
+
104
+ const cached = rawMiddleFnReflectionCache.get(cacheKey);
105
+ if (cached) return cached;
106
+
107
+ const reflection: MethodReflect = {
108
+ paramNames,
109
+ paramsJitFns: getNoopJitFns(),
110
+ returnJitFns: getNoopJitFns(),
111
+ paramsJitHash: EMPTY_HASH,
112
+ returnJitHash: EMPTY_HASH,
113
+ hasReturnData,
114
+ isAsync,
115
+ };
116
+
117
+ rawMiddleFnReflectionCache.set(cacheKey, reflection);
118
+ return reflection;
119
+ }
120
+
121
+ // ############ AOT Cache Extraction ############
122
+
123
+ // Extend MethodMetadata type to include cached reflection
124
+ type CachedMethodMetadata = MethodMetadata & {
125
+ _cachedReflection?: MethodReflect;
126
+ };
127
+
128
+ /**
129
+ * Extracts reflection data from a cached method.
130
+ * Used in AOT mode to restore method reflection without loading run-types.
131
+ * Results are cached on the metadata object to avoid creating duplicate objects.
132
+ */
133
+ function extractReflectionFromCached(cached: CachedMethodMetadata): MethodReflect {
134
+ // Return cached reflection if available
135
+ if (cached._cachedReflection) return cached._cachedReflection;
136
+
137
+ const reflectionItems: MethodReflect = {
138
+ paramNames: cached.paramNames || [],
139
+ paramsJitFns: getJitFunctionsFromHash(cached.paramsJitHash),
140
+ returnJitFns: getJitFunctionsFromHash(cached.returnJitHash),
141
+ paramsJitHash: cached.paramsJitHash,
142
+ returnJitHash: cached.returnJitHash,
143
+ hasReturnData: cached.hasReturnData,
144
+ isAsync: cached.isAsync,
145
+ };
146
+
147
+ // Restore headers param if present
148
+ if (cached.headersParam) {
149
+ reflectionItems.headersParam = {
150
+ headerNames: cached.headersParam.headerNames,
151
+ jitFns: getJitFunctionsFromHash(cached.headersParam.jitHash) as Pick<JitCompiledFunctions, 'isType' | 'typeErrors'>,
152
+ jitHash: cached.headersParam.jitHash,
153
+ };
154
+ }
155
+
156
+ // Restore headers return if present
157
+ if (cached.headersReturn) {
158
+ reflectionItems.headersReturn = {
159
+ headerNames: cached.headersReturn.headerNames,
160
+ jitFns: getJitFunctionsFromHash(cached.headersReturn.jitHash) as Pick<JitCompiledFunctions, 'isType' | 'typeErrors'>,
161
+ jitHash: cached.headersReturn.jitHash,
162
+ };
163
+ }
164
+
165
+ // Cache for future calls
166
+ cached._cachedReflection = reflectionItems;
167
+ return reflectionItems;
168
+ }
169
+
170
+ // ############ Main Reflection Functions ############
171
+
172
+ /**
173
+ * Gets reflection data for a handler (route or middleFn).
174
+ * In AOT mode, returns cached data without loading run-types.
175
+ * In non-AOT mode, dynamically loads run-types and generates reflection.
176
+ * Throws AOTCacheError if AOT mode is enabled and route is not in cache.
177
+ */
178
+ export async function getHandlerReflection(
179
+ handler: Handler,
180
+ routeId: string,
181
+ routerOptions: RouterOptions,
182
+ handlerOptions: RouteOptions | MiddleFnOptions | HeadersMiddleFnOptions = {},
183
+ isHeadersMiddleFn: boolean = false,
184
+ methodStrictTypes?: boolean
185
+ ): Promise<MethodReflect> {
186
+ // Check AOT cache first
187
+ const cached = getPersistedMethodMetadata(routeId);
188
+ if (cached) return extractReflectionFromCached(cached);
189
+ if (routerOptions.aot) throw new AOTCacheError(routeId, isHeadersMiddleFn ? 'middleFn' : 'route');
190
+ // Non-AOT mode: dynamically load run-types and generate reflection
191
+ const rt = await loadRunTypesModule();
192
+ return generateHandlerReflection(handler, routeId, routerOptions, handlerOptions, isHeadersMiddleFn, rt, methodStrictTypes);
193
+ }
194
+
195
+ /**
196
+ * Gets reflection data for a raw middleFn.
197
+ * Raw middleFns don't use full reflection - they don't need JIT functions.
198
+ * Raw middleFns don't NEED to be in the AOT cache, but if they are, we can use
199
+ * the cached data (especially the isAsync flag).
200
+ * In AOT mode, this function does NOT load run-types.
201
+ */
202
+ export async function getRawMethodReflection(
203
+ handler: Handler,
204
+ routeId: string,
205
+ routerOptions: RouterOptions
206
+ ): Promise<MethodReflect> {
207
+ // Check if raw middleFn is in cache - if so, use cached data (especially isAsync)
208
+ const cached = getPersistedMethodMetadata(routeId);
209
+ if (cached) return createRawMiddleFnReflection(cached.isAsync, cached.hasReturnData, cached.paramNames || []);
210
+ // Raw middleFns don't need JIT functions, so we don't need to load run-types in AOT mode
211
+ if (routerOptions.aot) return createRawMiddleFnReflection(true);
212
+ // Non-AOT mode: dynamically load run-types to properly detect if handler is async
213
+ const rt = await loadRunTypesModule();
214
+ return generateRawMethodReflection(handler, routeId, rt);
215
+ }
216
+
217
+ // ############ Reflection Generation (requires run-types) ############
218
+
219
+ /**
220
+ * Generates reflection data for a handler using run-types.
221
+ * This function is only called in non-AOT mode.
222
+ */
223
+ function generateHandlerReflection(
224
+ handler: Handler,
225
+ routeId: string,
226
+ routerOptions: RouterOptions,
227
+ handlerOptions: RouteOptions | MiddleFnOptions | HeadersMiddleFnOptions,
228
+ isHeadersMiddleFn: boolean,
229
+ rt: RunTypesFunctions,
230
+ methodStrictTypes?: boolean
231
+ ): MethodReflect {
232
+ const reflectionItems: Partial<MethodReflect> = {};
233
+ let handlerRunType: FunctionRunType;
234
+ const needsBinary = ((handlerOptions as RouteOptions)?.serializer ?? routerOptions.serializer) === 'binary';
235
+ const effectiveStrictTypes = methodStrictTypes ?? routerOptions.strictTypes;
236
+ const runTypeOptions: RunTypeOptions = {
237
+ ...(routerOptions?.runTypeOptions || DEFAULT_ROUTE_OPTIONS.runTypeOptions),
238
+ ...(effectiveStrictTypes !== undefined ? {strictTypes: effectiveStrictTypes} : {}),
239
+ };
240
+ try {
241
+ handlerRunType = rt.reflectFunction(handler);
242
+ } catch (error: any) {
243
+ throw new Error(`Can not get RunType of handler for route/middleFn "${routeId}." Error: ${error?.message}`);
244
+ }
245
+ const paramsSlice = isHeadersMiddleFn ? {start: HEADER_HOOK_DEFAULT_PARAMS.length} : {start: ROUTE_DEFAULT_PARAMS.length};
246
+ const paramsOpts: RunTypeOptions = {...runTypeOptions, paramsSlice};
247
+
248
+ try {
249
+ reflectionItems.paramNames = handlerRunType.getParameterNames(paramsOpts);
250
+ // Skip JIT generation if handler has no params (optimization for AOT cache size)
251
+ if (reflectionItems.paramNames.length === 0) {
252
+ reflectionItems.paramsJitHash = EMPTY_HASH;
253
+ reflectionItems.paramsJitFns = getNoopJitFns();
254
+ } else {
255
+ reflectionItems.paramsJitFns = getFunctionJitFns(handler, paramsOpts, rt, false, needsBinary);
256
+ reflectionItems.paramsJitHash = handlerRunType.getParameters().getJitHash(paramsOpts);
257
+ }
258
+ } catch (error: any) {
259
+ throw new Error(`Can not compile Jit Functions for Parameters of route/middleFn "${routeId}." Error: ${error?.message}`);
260
+ }
261
+
262
+ if (isHeadersMiddleFn) {
263
+ const headersRunType = getParamsHeadersRunType(handlerRunType, routeId, routerOptions, rt);
264
+ const headerNames: string[] = getHeaderNames(headersRunType, routeId, rt);
265
+
266
+ try {
267
+ const opts: RunTypeOptions = {
268
+ ...runTypeOptions,
269
+ paramsSlice: undefined,
270
+ };
271
+
272
+ const jitFns: JitCompiledFunctions = getTypeJitFunctions(headersRunType, opts, rt, false);
273
+ const jitHash = headersRunType.getJitHash(opts);
274
+ reflectionItems.headersParam = {headerNames, jitFns, jitHash};
275
+ } catch (error: any) {
276
+ throw new Error(
277
+ `Can not compile Jit Functions for Headers of Headers MiddleFn "${routeId}." Error: ${error?.message}`
278
+ );
279
+ }
280
+ }
281
+
282
+ const returnHeadersRunType = getReturnHeadersRunType(handlerRunType, rt);
283
+ if (returnHeadersRunType) {
284
+ const opts: RunTypeOptions = {};
285
+ const headerNames: string[] = getHeaderNames(returnHeadersRunType, routeId, rt);
286
+ const jitFns: JitCompiledFunctions = getFunctionJitFns(handler, opts, rt, true, false);
287
+ const jitHash: string = returnHeadersRunType.getJitHash(opts);
288
+ reflectionItems.headersReturn = {headerNames, jitFns, jitHash};
289
+ }
290
+
291
+ const returnOpts: RunTypeOptions = runTypeOptions;
292
+ // If the return type is HeadersSubset or if it's a headersFn with array return, don't treat it as return data
293
+ reflectionItems.hasReturnData = handlerRunType.hasReturnData();
294
+
295
+ try {
296
+ // Skip JIT generation if handler has void return (optimization for AOT cache size)
297
+ if (!reflectionItems.hasReturnData) {
298
+ reflectionItems.returnJitFns = getNoopJitFns();
299
+ reflectionItems.returnJitHash = EMPTY_HASH;
300
+ } else {
301
+ // returnJitFns contains all run type functionality for the return value, it compiles when the property is first accessed
302
+ reflectionItems.returnJitFns = getFunctionJitFns(handler, returnOpts, rt, true, needsBinary);
303
+ reflectionItems.returnJitHash = handlerRunType.getResolvedReturnType().getJitHash(returnOpts);
304
+ }
305
+ } catch (error: any) {
306
+ throw new Error(`Can not get Jit Functions for Return of route/middleFn "${routeId}." Error: ${error?.message}`);
307
+ }
308
+
309
+ reflectionItems.isAsync = handlerRunType.isAsync();
310
+
311
+ return reflectionItems as MethodReflect;
312
+ }
313
+
314
+ /**
315
+ * Generates reflection data for a raw middleFn using run-types.
316
+ * This function is only called in non-AOT mode.
317
+ */
318
+ function generateRawMethodReflection(handler: Handler, routeId: string, rt: RunTypesFunctions): MethodReflect {
319
+ let handlerRunType: FunctionRunType;
320
+ try {
321
+ handlerRunType = rt.reflectFunction(handler);
322
+ } catch (error: any) {
323
+ throw new Error(`Can not get RunType of handler for route/middleFn "${routeId}." Error: ${error?.message}`);
324
+ }
325
+ const isAsync = handlerRunType?.isAsync() || true;
326
+ return createRawMiddleFnReflection(isAsync);
327
+ }
328
+
329
+ // ############ Helper Functions (require run-types module) ############
330
+
331
+ function getParamsHeadersRunType(
332
+ handlerRunType: FunctionRunType,
333
+ routeId: string,
334
+ routerOptions: RouterOptions,
335
+ rt: RunTypesFunctions
336
+ ): BaseRunType {
337
+ const paramRunTypes = handlerRunType.getParameters().getParamRunTypes(getFakeCompiler(routerOptions));
338
+ const headersSubset = (paramRunTypes[1] as MemberRunType<any>)?.getMemberType?.(); // HeadersSubset is always index 1 after context
339
+
340
+ if (!isHeaderSubSetRunType(headersSubset, rt)) {
341
+ throw new Error(`Headers MiddleFn '${routeId}' second parameter must be a HeadersSubset.`);
342
+ }
343
+ return headersSubset;
344
+ }
345
+
346
+ function getReturnHeadersRunType(handlerRunType: FunctionRunType, rt: RunTypesFunctions): BaseRunType | undefined {
347
+ const returnRunType = handlerRunType.getReturnType();
348
+ if (rt.isUnionRunType(returnRunType)) {
349
+ const headersSubset = returnRunType.getChildRunTypes().find((child) => isHeaderSubSetRunType(child, rt));
350
+ if (!headersSubset) return undefined;
351
+ return headersSubset;
352
+ }
353
+ if (!isHeaderSubSetRunType(returnRunType, rt)) return undefined;
354
+ return returnRunType;
355
+ }
356
+
357
+ function isHeaderSubSetRunType(runType: BaseRunType | undefined, rt: RunTypesFunctions): runType is BaseRunType {
358
+ if (!runType) return false;
359
+ return rt.isClassRunType(runType, HeadersSubset);
360
+ }
361
+
362
+ function getHeaderNames(runType: BaseRunType, routeId: string, rt: RunTypesFunctions): string[] {
363
+ // HeadersSubset is a generic class: HeadersSubset<Required, Optional>
364
+ // We need to extract the literal string values from the Required and Optional type arguments
365
+ // Use 'typeArguments' (not 'arguments') to get both Required and Optional with their defaults
366
+ const typeArguments = (runType.src as any).typeArguments;
367
+ if (!typeArguments || typeArguments.length === 0) {
368
+ throw new Error(`HeadersSubset must have type arguments in route/middleFn ${routeId}`);
369
+ }
370
+ const headerNames: string[] = [];
371
+ // Extract header names from Required type argument (first argument)
372
+ const requiredArg = typeArguments[0];
373
+ if (requiredArg) {
374
+ const requiredNames = extractLiteralStringsFromType(requiredArg._rt, rt);
375
+ headerNames.push(...requiredNames);
376
+ }
377
+ // Extract header names from Optional type argument (second argument, if present)
378
+ if (typeArguments.length > 1) {
379
+ const optionalArg = typeArguments[1];
380
+ if (optionalArg) {
381
+ const optionalNames = extractLiteralStringsFromType(optionalArg._rt, rt);
382
+ headerNames.push(...optionalNames);
383
+ }
384
+ }
385
+ if (headerNames.length === 0) throw new Error(`Header names array cannot be empty in route/middleFn ${routeId}`);
386
+ return headerNames;
387
+ }
388
+
389
+ /**
390
+ * Internal recursive function to extract literal string values from a type.
391
+ * Handles single literal strings and union types (including nested unions).
392
+ */
393
+ function extractLiteralStringsFromTypeRecursive(runType: BaseRunType, rt: RunTypesFunctions): string[] {
394
+ // Handle single literal string
395
+ if (rt.isLiteralRunType(runType)) {
396
+ const literal = (runType as any).getLiteralValue();
397
+ if (typeof literal === 'string') {
398
+ return [literal];
399
+ }
400
+ return [];
401
+ }
402
+
403
+ // Handle union of literal strings (recursively for nested unions)
404
+ if (rt.isUnionRunType(runType)) {
405
+ const children = runType.getChildRunTypes();
406
+ const literals: string[] = [];
407
+ for (const child of children) {
408
+ // Recursively extract from each child (handles nested unions)
409
+ const childLiterals = extractLiteralStringsFromTypeRecursive(child, rt);
410
+ literals.push(...childLiterals);
411
+ }
412
+ return literals;
413
+ }
414
+
415
+ return [];
416
+ }
417
+
418
+ /**
419
+ * Extracts literal string values from a type.
420
+ * Handles 'never' type only at the root level, then delegates to recursive extraction.
421
+ */
422
+ function extractLiteralStringsFromType(runType: BaseRunType, rt: RunTypesFunctions): string[] {
423
+ // Handle 'never' type at root level only (no headers)
424
+ if (rt.isNeverRunType(runType)) return [];
425
+ return extractLiteralStringsFromTypeRecursive(runType, rt);
426
+ }
427
+
428
+ // Create a fake compiler object with just the opts property needed by getParamRunTypes.
429
+ // getParamRunTypes() requires a JitFnCompiler but we only need the opts property to slice parameters.
430
+ // This is a workaround to avoid updating getParamRunTypes() signature
431
+ function getFakeCompiler(routerOptions: RouterOptions): JitFnCompiler {
432
+ return {opts: routerOptions} as any as JitFnCompiler;
433
+ }
434
+
435
+ function getTypeJitFunctions(
436
+ runType: BaseRunType,
437
+ opts: RunTypeOptions | undefined,
438
+ rtModule: RunTypesFunctions,
439
+ needsBinary: boolean = false
440
+ ): JitCompiledFunctions {
441
+ const jitFns: JitCompiledFunctions = {
442
+ isType: runType.createJitCompiledFunction(rtModule.JitFunctions.isType.id, undefined, opts),
443
+ typeErrors: runType.createJitCompiledFunction(rtModule.JitFunctions.typeErrors.id, undefined, opts),
444
+ prepareForJson: runType.createJitCompiledFunction(rtModule.JitFunctions.prepareForJson.id, undefined, opts),
445
+ restoreFromJson: runType.createJitCompiledFunction(rtModule.JitFunctions.restoreFromJson.id, undefined, opts),
446
+ stringifyJson: runType.createJitCompiledFunction(rtModule.JitFunctions.stringifyJson.id, undefined, opts),
447
+ ...(needsBinary
448
+ ? {
449
+ toBinary: runType.createJitCompiledFunction(rtModule.JitFunctions.toBinary.id, undefined, opts),
450
+ fromBinary: runType.createJitCompiledFunction(rtModule.JitFunctions.fromBinary.id, undefined, opts),
451
+ }
452
+ : {}),
453
+ };
454
+ return jitFns;
455
+ }
456
+
457
+ // Cache for function RunTypes to avoid duplicate reflectFunction calls
458
+ const functionRunTypeCache = new WeakMap<AnyFn, FunctionRunType>();
459
+
460
+ function getFunctionJitFns<Fn extends AnyFn>(
461
+ fn: Fn,
462
+ opts: RunTypeOptions | undefined,
463
+ rtModule: RunTypesFunctions,
464
+ isReturn: boolean,
465
+ needsBinary: boolean = false
466
+ ): JitCompiledFunctions {
467
+ // Check cache first
468
+ let runType = functionRunTypeCache.get(fn);
469
+ if (!runType) {
470
+ runType = rtModule.reflectFunction(fn);
471
+ functionRunTypeCache.set(fn, runType);
472
+ }
473
+
474
+ const createFn = isReturn
475
+ ? runType.createJitCompiledReturnFunction.bind(runType)
476
+ : runType.createJitCompiledParamsFunction.bind(runType);
477
+ const jitFunctions: JitCompiledFunctions = {
478
+ isType: createFn(rtModule.JitFunctions.isType, opts),
479
+ typeErrors: createFn(rtModule.JitFunctions.typeErrors, opts),
480
+ prepareForJson: createFn(rtModule.JitFunctions.prepareForJson, opts),
481
+ restoreFromJson: createFn(rtModule.JitFunctions.restoreFromJson, opts),
482
+ stringifyJson: createFn(rtModule.JitFunctions.stringifyJson, opts),
483
+ ...(needsBinary
484
+ ? {
485
+ toBinary: createFn(rtModule.JitFunctions.toBinary, opts),
486
+ fromBinary: createFn(rtModule.JitFunctions.fromBinary, opts),
487
+ }
488
+ : {}),
489
+ };
490
+ return jitFunctions;
491
+ }
492
+
493
+ // ############ Retroactive Binary JIT Compilation ############
494
+
495
+ /** Compiles toBinary/fromBinary JIT functions for middleware that was initially compiled without binary support */
496
+ export async function ensureBinaryJitFns(method: MiddleFnMethod | HeadersMethod): Promise<void> {
497
+ if (method.paramsJitFns.toBinary && method.returnJitFns.toBinary) return;
498
+ const rt = await loadRunTypesModule();
499
+ const isHeader = method.type === HandlerType.headersMiddleFn;
500
+ const paramsSlice = isHeader ? {start: HEADER_HOOK_DEFAULT_PARAMS.length} : {start: ROUTE_DEFAULT_PARAMS.length};
501
+ const opts: RunTypeOptions = {paramsSlice};
502
+
503
+ if (!method.paramsJitFns.toBinary && method.paramsJitHash !== EMPTY_HASH) {
504
+ const runType = rt.reflectFunction(method.handler);
505
+ const createFn = runType.createJitCompiledParamsFunction.bind(runType);
506
+ method.paramsJitFns.toBinary = createFn(rt.JitFunctions.toBinary, opts);
507
+ method.paramsJitFns.fromBinary = createFn(rt.JitFunctions.fromBinary, opts);
508
+ }
509
+ if (!method.returnJitFns.toBinary && method.returnJitHash !== EMPTY_HASH && method.hasReturnData) {
510
+ const runType = rt.reflectFunction(method.handler);
511
+ const createFn = runType.createJitCompiledReturnFunction.bind(runType);
512
+ method.returnJitFns.toBinary = createFn(rt.JitFunctions.toBinary);
513
+ method.returnJitFns.fromBinary = createFn(rt.JitFunctions.fromBinary);
514
+ }
515
+ }
516
+
517
+ // ############ Null JIT Functions ############