@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.
- package/.dist/cjs/index.d.ts +1 -0
- package/.dist/cjs/index.d.ts.map +1 -0
- package/.dist/cjs/src/aot/aotCacheLoader.d.ts +1 -0
- package/.dist/cjs/src/aot/aotCacheLoader.d.ts.map +1 -0
- package/.dist/cjs/src/callContext.d.ts +1 -0
- package/.dist/cjs/src/callContext.d.ts.map +1 -0
- package/.dist/cjs/src/constants.d.ts +1 -0
- package/.dist/cjs/src/constants.d.ts.map +1 -0
- package/.dist/cjs/src/defaultRoutes.d.ts +1 -0
- package/.dist/cjs/src/defaultRoutes.d.ts.map +1 -0
- package/.dist/cjs/src/dispatch.d.ts +1 -0
- package/.dist/cjs/src/dispatch.d.ts.map +1 -0
- package/.dist/cjs/src/lib/aotEmitter.d.ts +1 -0
- package/.dist/cjs/src/lib/aotEmitter.d.ts.map +1 -0
- package/.dist/cjs/src/lib/dispatchError.d.ts +1 -0
- package/.dist/cjs/src/lib/dispatchError.d.ts.map +1 -0
- package/.dist/cjs/src/lib/handlers.d.ts +1 -0
- package/.dist/cjs/src/lib/handlers.d.ts.map +1 -0
- package/.dist/cjs/src/lib/headers.d.ts +1 -0
- package/.dist/cjs/src/lib/headers.d.ts.map +1 -0
- package/.dist/cjs/src/lib/methodsCache.d.ts +1 -0
- package/.dist/cjs/src/lib/methodsCache.d.ts.map +1 -0
- package/.dist/cjs/src/lib/queryBody.d.ts +1 -0
- package/.dist/cjs/src/lib/queryBody.d.ts.map +1 -0
- package/.dist/cjs/src/lib/reflection.d.ts +1 -0
- package/.dist/cjs/src/lib/reflection.d.ts.map +1 -0
- package/.dist/cjs/src/lib/remoteMethods.d.ts +1 -0
- package/.dist/cjs/src/lib/remoteMethods.d.ts.map +1 -0
- package/.dist/cjs/src/lib/test/aotEmitter-test-router.d.ts +1 -0
- package/.dist/cjs/src/lib/test/aotEmitter-test-router.d.ts.map +1 -0
- package/.dist/cjs/src/router.d.ts +1 -0
- package/.dist/cjs/src/router.d.ts.map +1 -0
- package/.dist/cjs/src/routes/client.routes.d.ts +1 -0
- package/.dist/cjs/src/routes/client.routes.d.ts.map +1 -0
- package/.dist/cjs/src/routes/errors.routes.d.ts +1 -0
- package/.dist/cjs/src/routes/errors.routes.d.ts.map +1 -0
- package/.dist/cjs/src/routes/mion.routes.d.ts +1 -0
- package/.dist/cjs/src/routes/mion.routes.d.ts.map +1 -0
- package/.dist/cjs/src/routes/serializer.routes.d.ts +1 -0
- package/.dist/cjs/src/routes/serializer.routes.d.ts.map +1 -0
- package/.dist/cjs/src/routesFlow.d.ts +1 -0
- package/.dist/cjs/src/routesFlow.d.ts.map +1 -0
- package/.dist/cjs/src/types/context.d.ts +1 -0
- package/.dist/cjs/src/types/context.d.ts.map +1 -0
- package/.dist/cjs/src/types/definitions.d.ts +1 -0
- package/.dist/cjs/src/types/definitions.d.ts.map +1 -0
- package/.dist/cjs/src/types/general.d.ts +1 -0
- package/.dist/cjs/src/types/general.d.ts.map +1 -0
- package/.dist/cjs/src/types/guards.d.ts +1 -0
- package/.dist/cjs/src/types/guards.d.ts.map +1 -0
- package/.dist/cjs/src/types/handlers.d.ts +1 -0
- package/.dist/cjs/src/types/handlers.d.ts.map +1 -0
- package/.dist/cjs/src/types/publicMethods.d.ts +1 -0
- package/.dist/cjs/src/types/publicMethods.d.ts.map +1 -0
- package/.dist/cjs/src/types/remoteMethods.d.ts +1 -0
- package/.dist/cjs/src/types/remoteMethods.d.ts.map +1 -0
- package/.dist/esm/index.d.ts +1 -0
- package/.dist/esm/index.d.ts.map +1 -0
- package/.dist/esm/src/aot/aotCacheLoader.d.ts +1 -0
- package/.dist/esm/src/aot/aotCacheLoader.d.ts.map +1 -0
- package/.dist/esm/src/callContext.d.ts +1 -0
- package/.dist/esm/src/callContext.d.ts.map +1 -0
- package/.dist/esm/src/constants.d.ts +1 -0
- package/.dist/esm/src/constants.d.ts.map +1 -0
- package/.dist/esm/src/defaultRoutes.d.ts +1 -0
- package/.dist/esm/src/defaultRoutes.d.ts.map +1 -0
- package/.dist/esm/src/dispatch.d.ts +1 -0
- package/.dist/esm/src/dispatch.d.ts.map +1 -0
- package/.dist/esm/src/lib/aotEmitter.d.ts +1 -0
- package/.dist/esm/src/lib/aotEmitter.d.ts.map +1 -0
- package/.dist/esm/src/lib/dispatchError.d.ts +1 -0
- package/.dist/esm/src/lib/dispatchError.d.ts.map +1 -0
- package/.dist/esm/src/lib/handlers.d.ts +1 -0
- package/.dist/esm/src/lib/handlers.d.ts.map +1 -0
- package/.dist/esm/src/lib/headers.d.ts +1 -0
- package/.dist/esm/src/lib/headers.d.ts.map +1 -0
- package/.dist/esm/src/lib/methodsCache.d.ts +1 -0
- package/.dist/esm/src/lib/methodsCache.d.ts.map +1 -0
- package/.dist/esm/src/lib/queryBody.d.ts +1 -0
- package/.dist/esm/src/lib/queryBody.d.ts.map +1 -0
- package/.dist/esm/src/lib/reflection.d.ts +1 -0
- package/.dist/esm/src/lib/reflection.d.ts.map +1 -0
- package/.dist/esm/src/lib/remoteMethods.d.ts +1 -0
- package/.dist/esm/src/lib/remoteMethods.d.ts.map +1 -0
- package/.dist/esm/src/lib/test/aotEmitter-test-router.d.ts +1 -0
- package/.dist/esm/src/lib/test/aotEmitter-test-router.d.ts.map +1 -0
- package/.dist/esm/src/router.d.ts +1 -0
- package/.dist/esm/src/router.d.ts.map +1 -0
- package/.dist/esm/src/routes/client.routes.d.ts +1 -0
- package/.dist/esm/src/routes/client.routes.d.ts.map +1 -0
- package/.dist/esm/src/routes/errors.routes.d.ts +1 -0
- package/.dist/esm/src/routes/errors.routes.d.ts.map +1 -0
- package/.dist/esm/src/routes/mion.routes.d.ts +1 -0
- package/.dist/esm/src/routes/mion.routes.d.ts.map +1 -0
- package/.dist/esm/src/routes/serializer.routes.d.ts +1 -0
- package/.dist/esm/src/routes/serializer.routes.d.ts.map +1 -0
- package/.dist/esm/src/routesFlow.d.ts +1 -0
- package/.dist/esm/src/routesFlow.d.ts.map +1 -0
- package/.dist/esm/src/types/context.d.ts +1 -0
- package/.dist/esm/src/types/context.d.ts.map +1 -0
- package/.dist/esm/src/types/definitions.d.ts +1 -0
- package/.dist/esm/src/types/definitions.d.ts.map +1 -0
- package/.dist/esm/src/types/general.d.ts +1 -0
- package/.dist/esm/src/types/general.d.ts.map +1 -0
- package/.dist/esm/src/types/guards.d.ts +1 -0
- package/.dist/esm/src/types/guards.d.ts.map +1 -0
- package/.dist/esm/src/types/handlers.d.ts +1 -0
- package/.dist/esm/src/types/handlers.d.ts.map +1 -0
- package/.dist/esm/src/types/publicMethods.d.ts +1 -0
- package/.dist/esm/src/types/publicMethods.d.ts.map +1 -0
- package/.dist/esm/src/types/remoteMethods.d.ts +1 -0
- package/.dist/esm/src/types/remoteMethods.d.ts.map +1 -0
- package/index.ts +29 -0
- package/package.json +9 -5
- package/src/aot/aotCacheLoader.ts +15 -0
- package/src/callContext.ts +193 -0
- package/src/constants.ts +45 -0
- package/src/defaultRoutes.ts +37 -0
- package/src/dispatch.ts +204 -0
- package/src/lib/aotEmitter.ts +140 -0
- package/src/lib/dispatchError.ts +60 -0
- package/src/lib/handlers.ts +75 -0
- package/src/lib/headers.ts +102 -0
- package/src/lib/methodsCache.ts +72 -0
- package/src/lib/queryBody.ts +40 -0
- package/src/lib/reflection.ts +517 -0
- package/src/lib/remoteMethods.ts +180 -0
- package/src/lib/test/aotEmitter-test-router.ts +40 -0
- package/src/router.ts +656 -0
- package/src/routes/client.routes.ts +108 -0
- package/src/routes/errors.routes.ts +51 -0
- package/src/routes/mion.routes.ts +11 -0
- package/src/routes/serializer.routes.ts +225 -0
- package/src/routesFlow.ts +320 -0
- package/src/types/context.ts +119 -0
- package/src/types/definitions.ts +53 -0
- package/src/types/general.ts +84 -0
- package/src/types/guards.ts +71 -0
- package/src/types/handlers.ts +49 -0
- package/src/types/publicMethods.ts +83 -0
- package/src/types/remoteMethods.ts +54 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/* ########
|
|
2
|
+
* 2025 mion
|
|
3
|
+
* Author: Ma-jerez
|
|
4
|
+
* License: MIT
|
|
5
|
+
* The software is provided "as is", without warranty of any kind.
|
|
6
|
+
* ######## */
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
getJitFnCaches,
|
|
10
|
+
getENV,
|
|
11
|
+
isMionAOTEmitMode,
|
|
12
|
+
JitFunctionsCache,
|
|
13
|
+
PureFunctionsCache,
|
|
14
|
+
MethodsCache,
|
|
15
|
+
SrcCodeJITCompiledFnsCache,
|
|
16
|
+
SrcCodePureFunctionsCache,
|
|
17
|
+
ClientSrcCodeJITCompiledFnsCache,
|
|
18
|
+
ClientSrcCodePureFunctionsCache,
|
|
19
|
+
JIT_FUNCTION_IDS,
|
|
20
|
+
} from '@mionjs/core';
|
|
21
|
+
import {getPersistedMethods} from './methodsCache.ts';
|
|
22
|
+
import {createToJavascriptFn} from '@mionjs/run-types';
|
|
23
|
+
|
|
24
|
+
/** IPC message type for AOT cache emission */
|
|
25
|
+
export interface AOTCacheMessage {
|
|
26
|
+
type: 'mion-aot-caches';
|
|
27
|
+
jitFnsCode: string;
|
|
28
|
+
pureFnsCode: string;
|
|
29
|
+
routerCacheCode: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** IPC message sent by setPlatformConfig() to signal server readiness */
|
|
33
|
+
export interface PlatformReadyMessage {
|
|
34
|
+
type: 'mion-platform-ready';
|
|
35
|
+
routerConfig: Record<string, unknown>;
|
|
36
|
+
platformConfig: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Serialized cache data before converting to JS code */
|
|
40
|
+
export interface SerializedCaches {
|
|
41
|
+
jitFnsCode: string;
|
|
42
|
+
pureFnsCode: string;
|
|
43
|
+
routerCacheCode: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** JIT function IDs to exclude from AOT caches (toJSCode is only needed at compile time) */
|
|
47
|
+
const EXCLUDED_JIT_FN_IDS = [JIT_FUNCTION_IDS.toJSCode];
|
|
48
|
+
|
|
49
|
+
/** Pure function names to exclude from AOT caches */
|
|
50
|
+
const EXCLUDED_PURE_FN_NAMES = ['sanitizeCompiledFn'];
|
|
51
|
+
|
|
52
|
+
/** Returns serialized caches for in-process AOT generation (no IPC needed). Call after initMionRouter(). */
|
|
53
|
+
export async function getSerializedCaches(): Promise<SerializedCaches> {
|
|
54
|
+
const {jitFnsCache, pureFnsCache} = getJitFnCaches();
|
|
55
|
+
const routerCache = getPersistedMethods();
|
|
56
|
+
return serializeCachesToCode(jitFnsCache, pureFnsCache, routerCache);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Emits AOT caches to the parent process via IPC when running in MION_COMPILE mode.
|
|
61
|
+
* This function is called automatically at the end of initMionRouter() and can also
|
|
62
|
+
* be called manually for multi-step route registration patterns.
|
|
63
|
+
*/
|
|
64
|
+
export async function emitAOTCaches(): Promise<void> {
|
|
65
|
+
// Only emit in AOT generation mode (compile, SSR, or serve)
|
|
66
|
+
if (!isMionAOTEmitMode()) return;
|
|
67
|
+
// middleware mode: caches remain in global state, read directly via getSerializedCaches()
|
|
68
|
+
if (getENV('MION_COMPILE') === 'middleware') return;
|
|
69
|
+
|
|
70
|
+
// IPC mode: send to parent process
|
|
71
|
+
if (typeof process.send !== 'function') return;
|
|
72
|
+
|
|
73
|
+
// Get the caches
|
|
74
|
+
const {jitFnsCache, pureFnsCache} = getJitFnCaches();
|
|
75
|
+
const routerCache = getPersistedMethods();
|
|
76
|
+
|
|
77
|
+
// Serialize caches to JS code (filtering happens inside, after createToJavascriptFn)
|
|
78
|
+
const serialized = await serializeCachesToCode(jitFnsCache, pureFnsCache, routerCache);
|
|
79
|
+
|
|
80
|
+
// Send to parent process
|
|
81
|
+
const message: AOTCacheMessage = {
|
|
82
|
+
type: 'mion-aot-caches',
|
|
83
|
+
...serialized,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
process.send(message);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Serializes the caches to JavaScript code strings using run-types toJSCode.
|
|
91
|
+
* Filtering is done AFTER createToJavascriptFn because it adds compile-time-only JIT functions
|
|
92
|
+
* to the global caches that should be excluded from the serialized output.
|
|
93
|
+
*/
|
|
94
|
+
export async function serializeCachesToCode(
|
|
95
|
+
jitFnsCache: JitFunctionsCache,
|
|
96
|
+
pureFnsCache: PureFunctionsCache,
|
|
97
|
+
routerCache: MethodsCache
|
|
98
|
+
): Promise<SerializedCaches> {
|
|
99
|
+
const isClient = getENV('MION_AOT_IS_CLIENT') === 'true';
|
|
100
|
+
const jitToJSCode = isClient
|
|
101
|
+
? createToJavascriptFn<ClientSrcCodeJITCompiledFnsCache>()
|
|
102
|
+
: createToJavascriptFn<SrcCodeJITCompiledFnsCache>();
|
|
103
|
+
const pureToJSCode = isClient
|
|
104
|
+
? createToJavascriptFn<ClientSrcCodePureFunctionsCache>()
|
|
105
|
+
: createToJavascriptFn<SrcCodePureFunctionsCache>();
|
|
106
|
+
const routerToJSCode = createToJavascriptFn<MethodsCache>();
|
|
107
|
+
// Filter AFTER createToJavascriptFn to exclude compile-time-only items (including those just added)
|
|
108
|
+
const finalJitFns = filterExcludedJitFns(jitFnsCache, EXCLUDED_JIT_FN_IDS);
|
|
109
|
+
const finalPureFns = filterExcludedPureFns(pureFnsCache, EXCLUDED_PURE_FN_NAMES);
|
|
110
|
+
return {
|
|
111
|
+
jitFnsCode: jitToJSCode(finalJitFns as unknown as SrcCodeJITCompiledFnsCache),
|
|
112
|
+
pureFnsCode: pureToJSCode(finalPureFns as unknown as SrcCodePureFunctionsCache),
|
|
113
|
+
routerCacheCode: routerToJSCode(routerCache),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Filters out excluded JIT functions by their function ID.
|
|
119
|
+
* toJSCode is excluded because it's only needed at compile time.
|
|
120
|
+
*/
|
|
121
|
+
function filterExcludedJitFns(jitFnsCache: JitFunctionsCache, excludedFnIds: string[]): JitFunctionsCache {
|
|
122
|
+
if (!excludedFnIds.length) return jitFnsCache;
|
|
123
|
+
return Object.fromEntries(
|
|
124
|
+
Object.entries(jitFnsCache).filter(([, value]) => !excludedFnIds.includes(value.fnID as string))
|
|
125
|
+
) as JitFunctionsCache;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Filters out excluded pure functions by their function name.
|
|
130
|
+
* sanitizeCompiledFn is excluded because it's only needed at compile time.
|
|
131
|
+
*/
|
|
132
|
+
function filterExcludedPureFns(pureFnsCache: PureFunctionsCache, excludedFnNames: string[]): PureFunctionsCache {
|
|
133
|
+
if (!excludedFnNames.length) return pureFnsCache;
|
|
134
|
+
return Object.fromEntries(
|
|
135
|
+
Object.entries(pureFnsCache).map(([namespace, nsCache]) => [
|
|
136
|
+
namespace,
|
|
137
|
+
Object.fromEntries(Object.entries(nsCache).filter(([, value]) => !excludedFnNames.includes(value.fnName))),
|
|
138
|
+
])
|
|
139
|
+
) as PureFunctionsCache;
|
|
140
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {RpcError, MION_ROUTES, Mutable, StatusCodes, SerializerModes} from '@mionjs/core';
|
|
2
|
+
import type {CallContext, MionHeaders, MionRequest, MionResponse, ResponseBody} from '../types/context.ts';
|
|
3
|
+
import type {RemoteMethod} from '../types/remoteMethods.ts';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Return a Response mion response for any error that happens before or outside the router.
|
|
7
|
+
* to be used by any adapter layer. ie: node/http, aws/lambda, bun, etc.
|
|
8
|
+
* Uses thrownErrors with a special platformError key to maintain consistency with route errors.
|
|
9
|
+
* @param returnErr
|
|
10
|
+
* @param respHeaders
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export function getRouterFatalErrorResponse(returnErr: RpcError<string>, respHeaders: MionHeaders): MionResponse {
|
|
15
|
+
// Store platform error in thrownErrors with special platformError key
|
|
16
|
+
const body: ResponseBody = {
|
|
17
|
+
'@thrownErrors': {[MION_ROUTES.platformError]: returnErr},
|
|
18
|
+
};
|
|
19
|
+
respHeaders.set('content-type', 'application/json; charset=utf-8');
|
|
20
|
+
const response: Mutable<MionResponse> = {
|
|
21
|
+
statusCode: returnErr.statusCode || StatusCodes.SERVER_ERROR, // Global errors are always unexpected
|
|
22
|
+
hasErrors: true,
|
|
23
|
+
headers: respHeaders,
|
|
24
|
+
body,
|
|
25
|
+
rawBody: JSON.stringify(body),
|
|
26
|
+
serializer: SerializerModes.json, // global errors are always json
|
|
27
|
+
};
|
|
28
|
+
return response;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Handles errors during route dispatch.
|
|
33
|
+
* All errors passed to this function are unexpected (thrown errors).
|
|
34
|
+
* Expected errors are returned from handlers and added directly to response.body.
|
|
35
|
+
* @param context
|
|
36
|
+
* @param executable
|
|
37
|
+
* @param err
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
export function onExecutableError(context: CallContext, executable: RemoteMethod, err: any | RpcError<string> | Error) {
|
|
41
|
+
const response = context.response as Mutable<MionResponse>;
|
|
42
|
+
const path = executable.id;
|
|
43
|
+
const rpcError: RpcError<string> =
|
|
44
|
+
err instanceof RpcError
|
|
45
|
+
? err
|
|
46
|
+
: new RpcError({
|
|
47
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
48
|
+
publicMessage: `Unknown error in handler "${path}" of route ExecutionChain.`,
|
|
49
|
+
originalError: err,
|
|
50
|
+
type: 'unknown-error',
|
|
51
|
+
});
|
|
52
|
+
// only first error sets the error header
|
|
53
|
+
if (!response.hasErrors) response.headers.set('x-rpc-error', rpcError.type);
|
|
54
|
+
response.statusCode = rpcError.statusCode ?? StatusCodes.UNEXPECTED_ERROR;
|
|
55
|
+
response.hasErrors = true;
|
|
56
|
+
// Store unexpected errors for serialization
|
|
57
|
+
const thrownErrors = context.request.thrownErrors || ({} as Record<string, RpcError<string>>);
|
|
58
|
+
thrownErrors[path] = rpcError;
|
|
59
|
+
(context.request as Mutable<MionRequest>).thrownErrors = thrownErrors;
|
|
60
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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 {HeadersMiddleFnOptions, MiddleFnOptions, RawMiddleFnOptions, RouteOptions} from '../types/remoteMethods.ts';
|
|
9
|
+
import {HandlerType} from '@mionjs/core';
|
|
10
|
+
import {Handler, HeaderHandler, RawMiddleFnHandler} from '../types/handlers.ts';
|
|
11
|
+
import {HeadersMiddleFnDef, MiddleFnDef, RawMiddleFnDef, RouteDef} from '../types/definitions.ts';
|
|
12
|
+
|
|
13
|
+
// ############# Route & MiddleFns initialization #############
|
|
14
|
+
// these functions are just helpers to initialize the route & middleFns objects and keep route definitions clean
|
|
15
|
+
|
|
16
|
+
export function route<H extends Handler>(handler: H, opts?: RouteOptions): RouteDef<H> {
|
|
17
|
+
return {
|
|
18
|
+
type: HandlerType.route,
|
|
19
|
+
handler,
|
|
20
|
+
options: opts,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Route handler for read-only queries. Uses GET with ?data=base64url on the client when payload fits. */
|
|
25
|
+
export function query<H extends Handler>(handler: H, opts?: RouteOptions): RouteDef<H> {
|
|
26
|
+
return {
|
|
27
|
+
type: HandlerType.route,
|
|
28
|
+
handler,
|
|
29
|
+
options: {...opts, isMutation: false},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Route handler for mutations. Explicit alias for route() with isMutation: true. */
|
|
34
|
+
export function mutation<H extends Handler>(handler: H, opts?: RouteOptions): RouteDef<H> {
|
|
35
|
+
return {
|
|
36
|
+
type: HandlerType.route,
|
|
37
|
+
handler,
|
|
38
|
+
options: {...opts, isMutation: true},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function middleFn<H extends Handler>(handler: H, opts?: MiddleFnOptions): MiddleFnDef<H> {
|
|
43
|
+
return {
|
|
44
|
+
type: HandlerType.middleFn,
|
|
45
|
+
handler,
|
|
46
|
+
options: opts,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* MiddleFn for handling HTTP header parameters
|
|
52
|
+
* Used to define middleFns that receive values from HTTP headers
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* headersFn(['authorization'], (ctx, token: string): void => {
|
|
57
|
+
* // token contains the value of the 'authorization' header
|
|
58
|
+
* })
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function headersFn<H extends HeaderHandler>(handler: H, opts?: HeadersMiddleFnOptions): HeadersMiddleFnDef<H> {
|
|
62
|
+
return {
|
|
63
|
+
type: HandlerType.headersMiddleFn,
|
|
64
|
+
handler,
|
|
65
|
+
options: opts,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function rawMiddleFn<H extends RawMiddleFnHandler>(handler: H, opts?: RawMiddleFnOptions): RawMiddleFnDef<H> {
|
|
70
|
+
return {
|
|
71
|
+
type: HandlerType.rawMiddleFn,
|
|
72
|
+
handler,
|
|
73
|
+
options: opts,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/* ########
|
|
2
|
+
* 2022 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 {MionHeaders} from '../types/context.ts';
|
|
9
|
+
|
|
10
|
+
// ############# PUBLIC METHODS #############
|
|
11
|
+
|
|
12
|
+
type HeadersRecord = Record<string, string>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reusable class for managing HTTP headers with case-insensitive access
|
|
16
|
+
* Similar to the fetch Headers API but optimized for performance
|
|
17
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
|
18
|
+
*/
|
|
19
|
+
class MionHeadersImpl implements MionHeaders {
|
|
20
|
+
constructor(private headers: HeadersRecord) {}
|
|
21
|
+
|
|
22
|
+
append(name: string, value: string): void {
|
|
23
|
+
const nl = name.toLowerCase();
|
|
24
|
+
const existing = this.headers[nl];
|
|
25
|
+
const headerValue = toSingleHeader(value);
|
|
26
|
+
if (existing) {
|
|
27
|
+
this.headers[nl] = `${existing}, ${headerValue}`;
|
|
28
|
+
} else {
|
|
29
|
+
this.headers[nl] = headerValue;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
delete(name: string): void {
|
|
34
|
+
const nl = name.toLowerCase();
|
|
35
|
+
delete this.headers[nl];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get(name: string): string | undefined | null {
|
|
39
|
+
const nl = name.toLowerCase();
|
|
40
|
+
return this.headers[nl];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
set(name: string, value: string): void {
|
|
44
|
+
const ln = name.toLowerCase();
|
|
45
|
+
this.headers[ln] = value as string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
has(name: string): boolean {
|
|
49
|
+
const nl = name.toLowerCase();
|
|
50
|
+
return !!this.headers[nl];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
entries(): IterableIterator<[string, string]> {
|
|
54
|
+
return new Map(Object.entries(this.headers)).entries();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
keys(): IterableIterator<string> {
|
|
58
|
+
return new Map(Object.entries(this.headers)).keys();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
values(): IterableIterator<string> {
|
|
62
|
+
return new Map(Object.entries(this.headers)).values();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Return a Headers Like object from a Headers Record structure (Record<string, string | string[]>)
|
|
68
|
+
* Returned Headers object is similar to the fetch Headers object but not exactly the same
|
|
69
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
|
70
|
+
*
|
|
71
|
+
* This is optimized to avoid creating the Headers Map if it's not strictly needed.
|
|
72
|
+
* ie. for incoming header that only use get method, the Headers object is never created and instead the HeadersRecord is used directly.
|
|
73
|
+
*
|
|
74
|
+
* This function can be used to create a Headers object from incoming request that has the headers in an object structure.
|
|
75
|
+
* ie IncomingMessage.headers or ApiGatewayEvent.headers
|
|
76
|
+
*
|
|
77
|
+
* @param headersObj
|
|
78
|
+
* @returns
|
|
79
|
+
*/
|
|
80
|
+
export function headersFromRecord(headersObj: Record<string, string>, skipToLower = false): MionHeaders {
|
|
81
|
+
// lazy load headers map
|
|
82
|
+
const headers = parseHeaders(headersObj, skipToLower);
|
|
83
|
+
return new MionHeadersImpl(headers);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function toSingleHeader(value: string | number): string {
|
|
87
|
+
if (Array.isArray(value)) return value.join(', ');
|
|
88
|
+
return value as string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function parseHeaders(headersObj: Record<string, string>, skipToLower = false): HeadersRecord {
|
|
92
|
+
if (skipToLower) return headersObj;
|
|
93
|
+
const entries = Object.entries(headersObj);
|
|
94
|
+
const headers: HeadersRecord = {};
|
|
95
|
+
for (let i = 0; i < entries.length; i++) {
|
|
96
|
+
const [name, value] = entries[i];
|
|
97
|
+
if (!value) continue;
|
|
98
|
+
const ln = name.toLowerCase();
|
|
99
|
+
headers[ln] = toSingleHeader(value);
|
|
100
|
+
}
|
|
101
|
+
return headers;
|
|
102
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
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 {MethodsCache, MethodMetadata, isMionAOTEmitMode, getJitFunctionsFromHash} from '@mionjs/core';
|
|
9
|
+
import {RemoteMethod} from '../types/remoteMethods.ts';
|
|
10
|
+
import {AnyHandler} from '../types/handlers.ts';
|
|
11
|
+
import {IS_TEST_ENV} from '../constants.ts';
|
|
12
|
+
|
|
13
|
+
export let persistedMethods: MethodsCache = {};
|
|
14
|
+
|
|
15
|
+
// ############# PUBLIC METHODS #############
|
|
16
|
+
|
|
17
|
+
export function addToPersistedMethods(id: string, method: RemoteMethod) {
|
|
18
|
+
if (!shouldCompile() || !!persistedMethods[id]) return;
|
|
19
|
+
persistedMethods[id] = method;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getPersistedMethod(id: string, handler: AnyHandler): RemoteMethod | undefined {
|
|
23
|
+
const method = persistedMethods?.[id];
|
|
24
|
+
if (!method) return;
|
|
25
|
+
return restorePersistedMethod(method, handler);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Gets method metadata from the persisted methods cache by id. */
|
|
29
|
+
export function getPersistedMethodMetadata(id: string): MethodMetadata | undefined {
|
|
30
|
+
const method = persistedMethods[id];
|
|
31
|
+
return method;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getPersistedMethods(): Readonly<MethodsCache> {
|
|
35
|
+
return persistedMethods;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function setPersistedMethods(newCompiled: MethodsCache) {
|
|
39
|
+
persistedMethods = newCompiled;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resetPersistedMethods() {
|
|
43
|
+
persistedMethods = {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function restorePersistedMethod(method: MethodMetadata, handler: AnyHandler): RemoteMethod {
|
|
47
|
+
const restored = method as any as RemoteMethod;
|
|
48
|
+
if (restored.paramsJitFns && restored.returnJitFns && restored.paramNames && !!restored.handler)
|
|
49
|
+
return method as RemoteMethod;
|
|
50
|
+
restored.handler = handler;
|
|
51
|
+
restored.paramsJitFns = getJitFunctionsFromHash(method.paramsJitHash);
|
|
52
|
+
restored.returnJitFns = getJitFunctionsFromHash(method.returnJitHash);
|
|
53
|
+
if (IS_TEST_ENV) (restored as any).isRestored = true;
|
|
54
|
+
return restored;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function shouldCompile() {
|
|
58
|
+
return isMionAOTEmitMode();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Loads compiled methods data into the persistedMethods cache.
|
|
63
|
+
* This function merges the provided methods data into the existing persistedMethods without overwriting existing entries.
|
|
64
|
+
* @param compiledMethods - Object containing compiled methods data to merge into the cache
|
|
65
|
+
*/
|
|
66
|
+
export function loadCompiledMethods(compiledMethods: MethodsCache) {
|
|
67
|
+
for (const [key, value] of Object.entries(compiledMethods)) {
|
|
68
|
+
if (!(key in persistedMethods)) {
|
|
69
|
+
persistedMethods[key] = value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* ########
|
|
2
|
+
* 2025 mion
|
|
3
|
+
* Author: Ma-jerez
|
|
4
|
+
* License: MIT
|
|
5
|
+
* The software is provided "as is", without warranty of any kind.
|
|
6
|
+
* ######## */
|
|
7
|
+
|
|
8
|
+
import {fromBase64Url, SerializerModes} from '@mionjs/core';
|
|
9
|
+
import type {SerializerCode} from '@mionjs/core';
|
|
10
|
+
|
|
11
|
+
/** Result of decoding a base64url query body from ?data= */
|
|
12
|
+
export interface QueryBodyResult {
|
|
13
|
+
rawBody: string;
|
|
14
|
+
bodyType: SerializerCode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Detects and decodes base64url-encoded request body from ?data= query param.
|
|
18
|
+
* Returns decoded body + bodyType if found, undefined otherwise. */
|
|
19
|
+
export function decodeQueryBody(urlQuery: string | undefined, rawBody: unknown): QueryBodyResult | undefined {
|
|
20
|
+
if (rawBody) return undefined;
|
|
21
|
+
if (!urlQuery) return undefined;
|
|
22
|
+
const dataValue = extractDataParam(urlQuery);
|
|
23
|
+
if (!dataValue) return undefined;
|
|
24
|
+
return {
|
|
25
|
+
rawBody: fromBase64Url(dataValue),
|
|
26
|
+
bodyType: SerializerModes.stringifyJson,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function extractDataParam(urlQuery: string): string | undefined {
|
|
31
|
+
if (urlQuery.startsWith('data=')) {
|
|
32
|
+
const ampIndex = urlQuery.indexOf('&', 5);
|
|
33
|
+
return ampIndex === -1 ? urlQuery.slice(5) : urlQuery.slice(5, ampIndex);
|
|
34
|
+
}
|
|
35
|
+
const idx = urlQuery.indexOf('&data=');
|
|
36
|
+
if (idx === -1) return undefined;
|
|
37
|
+
const start = idx + 6;
|
|
38
|
+
const ampIndex = urlQuery.indexOf('&', start);
|
|
39
|
+
return ampIndex === -1 ? urlQuery.slice(start) : urlQuery.slice(start, ampIndex);
|
|
40
|
+
}
|