@mionjs/router 0.8.0-alpha.0
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.cjs +154 -0
- package/.dist/cjs/index.cjs.map +1 -0
- package/.dist/cjs/index.d.ts +19 -0
- package/.dist/cjs/package.json +1 -0
- package/.dist/cjs/src/callContext.cjs +125 -0
- package/.dist/cjs/src/callContext.cjs.map +1 -0
- package/.dist/cjs/src/callContext.d.ts +10 -0
- package/.dist/cjs/src/constants.cjs +45 -0
- package/.dist/cjs/src/constants.cjs.map +1 -0
- package/.dist/cjs/src/constants.d.ts +10 -0
- package/.dist/cjs/src/defaultRoutes.cjs +16 -0
- package/.dist/cjs/src/defaultRoutes.cjs.map +1 -0
- package/.dist/cjs/src/defaultRoutes.d.ts +3 -0
- package/.dist/cjs/src/dispatch.cjs +141 -0
- package/.dist/cjs/src/dispatch.cjs.map +1 -0
- package/.dist/cjs/src/dispatch.d.ts +3 -0
- package/.dist/cjs/src/lib/aotCacheLoader.cjs +13 -0
- package/.dist/cjs/src/lib/aotCacheLoader.cjs.map +1 -0
- package/.dist/cjs/src/lib/aotCacheLoader.d.ts +1 -0
- package/.dist/cjs/src/lib/aotEmitter.cjs +70 -0
- package/.dist/cjs/src/lib/aotEmitter.cjs.map +1 -0
- package/.dist/cjs/src/lib/aotEmitter.d.ts +17 -0
- package/.dist/cjs/src/lib/dispatchError.cjs +39 -0
- package/.dist/cjs/src/lib/dispatchError.cjs.map +1 -0
- package/.dist/cjs/src/lib/dispatchError.d.ts +5 -0
- package/.dist/cjs/src/lib/handlers.cjs +52 -0
- package/.dist/cjs/src/lib/handlers.cjs.map +1 -0
- package/.dist/cjs/src/lib/handlers.d.ts +9 -0
- package/.dist/cjs/src/lib/headers.cjs +64 -0
- package/.dist/cjs/src/lib/headers.cjs.map +1 -0
- package/.dist/cjs/src/lib/headers.d.ts +2 -0
- package/.dist/cjs/src/lib/methodsCache.cjs +55 -0
- package/.dist/cjs/src/lib/methodsCache.cjs.map +1 -0
- package/.dist/cjs/src/lib/methodsCache.d.ts +11 -0
- package/.dist/cjs/src/lib/queryBody.cjs +34 -0
- package/.dist/cjs/src/lib/queryBody.cjs.map +1 -0
- package/.dist/cjs/src/lib/queryBody.d.ts +7 -0
- package/.dist/cjs/src/lib/reflection.cjs +342 -0
- package/.dist/cjs/src/lib/reflection.cjs.map +1 -0
- package/.dist/cjs/src/lib/reflection.d.ts +12 -0
- package/.dist/cjs/src/lib/remoteMethods.cjs +109 -0
- package/.dist/cjs/src/lib/remoteMethods.cjs.map +1 -0
- package/.dist/cjs/src/lib/remoteMethods.d.ts +10 -0
- package/.dist/cjs/src/lib/test/aotEmitter-test-router.cjs +24 -0
- package/.dist/cjs/src/lib/test/aotEmitter-test-router.cjs.map +1 -0
- package/.dist/cjs/src/lib/test/aotEmitter-test-router.d.ts +4 -0
- package/.dist/cjs/src/router.cjs +429 -0
- package/.dist/cjs/src/router.cjs.map +1 -0
- package/.dist/cjs/src/router.d.ts +30 -0
- package/.dist/cjs/src/routes/client.routes.cjs +76 -0
- package/.dist/cjs/src/routes/client.routes.cjs.map +1 -0
- package/.dist/cjs/src/routes/client.routes.d.ts +16 -0
- package/.dist/cjs/src/routes/errors.routes.cjs +49 -0
- package/.dist/cjs/src/routes/errors.routes.cjs.map +1 -0
- package/.dist/cjs/src/routes/errors.routes.d.ts +7 -0
- package/.dist/cjs/src/routes/mion.routes.cjs +13 -0
- package/.dist/cjs/src/routes/mion.routes.cjs.map +1 -0
- package/.dist/cjs/src/routes/mion.routes.d.ts +10 -0
- package/.dist/cjs/src/routes/serializer.routes.cjs +191 -0
- package/.dist/cjs/src/routes/serializer.routes.cjs.map +1 -0
- package/.dist/cjs/src/routes/serializer.routes.d.ts +9 -0
- package/.dist/cjs/src/routesFlow.cjs +227 -0
- package/.dist/cjs/src/routesFlow.cjs.map +1 -0
- package/.dist/cjs/src/routesFlow.d.ts +7 -0
- package/.dist/cjs/src/types/context.cjs +31 -0
- package/.dist/cjs/src/types/context.cjs.map +1 -0
- package/.dist/cjs/src/types/context.d.ts +57 -0
- package/.dist/cjs/src/types/definitions.cjs +15 -0
- package/.dist/cjs/src/types/definitions.cjs.map +1 -0
- package/.dist/cjs/src/types/definitions.d.ts +20 -0
- package/.dist/cjs/src/types/general.cjs +14 -0
- package/.dist/cjs/src/types/general.cjs.map +1 -0
- package/.dist/cjs/src/types/general.d.ts +27 -0
- package/.dist/cjs/src/types/guards.cjs +66 -0
- package/.dist/cjs/src/types/guards.cjs.map +1 -0
- package/.dist/cjs/src/types/guards.d.ts +17 -0
- package/.dist/cjs/src/types/handlers.cjs +13 -0
- package/.dist/cjs/src/types/handlers.cjs.map +1 -0
- package/.dist/cjs/src/types/handlers.d.ts +12 -0
- package/.dist/cjs/src/types/publicMethods.cjs +24 -0
- package/.dist/cjs/src/types/publicMethods.cjs.map +1 -0
- package/.dist/cjs/src/types/publicMethods.d.ts +45 -0
- package/.dist/cjs/src/types/remoteMethods.cjs +25 -0
- package/.dist/cjs/src/types/remoteMethods.cjs.map +1 -0
- package/.dist/cjs/src/types/remoteMethods.d.ts +44 -0
- package/.dist/esm/index.d.ts +19 -0
- package/.dist/esm/index.js +145 -0
- package/.dist/esm/index.js.map +1 -0
- package/.dist/esm/src/callContext.d.ts +10 -0
- package/.dist/esm/src/callContext.js +125 -0
- package/.dist/esm/src/callContext.js.map +1 -0
- package/.dist/esm/src/constants.d.ts +10 -0
- package/.dist/esm/src/constants.js +45 -0
- package/.dist/esm/src/constants.js.map +1 -0
- package/.dist/esm/src/defaultRoutes.d.ts +3 -0
- package/.dist/esm/src/defaultRoutes.js +16 -0
- package/.dist/esm/src/defaultRoutes.js.map +1 -0
- package/.dist/esm/src/dispatch.d.ts +3 -0
- package/.dist/esm/src/dispatch.js +141 -0
- package/.dist/esm/src/dispatch.js.map +1 -0
- package/.dist/esm/src/lib/aotCacheLoader.d.ts +1 -0
- package/.dist/esm/src/lib/aotCacheLoader.js +13 -0
- package/.dist/esm/src/lib/aotCacheLoader.js.map +1 -0
- package/.dist/esm/src/lib/aotEmitter.d.ts +17 -0
- package/.dist/esm/src/lib/aotEmitter.js +70 -0
- package/.dist/esm/src/lib/aotEmitter.js.map +1 -0
- package/.dist/esm/src/lib/dispatchError.d.ts +5 -0
- package/.dist/esm/src/lib/dispatchError.js +39 -0
- package/.dist/esm/src/lib/dispatchError.js.map +1 -0
- package/.dist/esm/src/lib/handlers.d.ts +9 -0
- package/.dist/esm/src/lib/handlers.js +52 -0
- package/.dist/esm/src/lib/handlers.js.map +1 -0
- package/.dist/esm/src/lib/headers.d.ts +2 -0
- package/.dist/esm/src/lib/headers.js +64 -0
- package/.dist/esm/src/lib/headers.js.map +1 -0
- package/.dist/esm/src/lib/methodsCache.d.ts +11 -0
- package/.dist/esm/src/lib/methodsCache.js +56 -0
- package/.dist/esm/src/lib/methodsCache.js.map +1 -0
- package/.dist/esm/src/lib/queryBody.d.ts +7 -0
- package/.dist/esm/src/lib/queryBody.js +34 -0
- package/.dist/esm/src/lib/queryBody.js.map +1 -0
- package/.dist/esm/src/lib/reflection.d.ts +12 -0
- package/.dist/esm/src/lib/reflection.js +320 -0
- package/.dist/esm/src/lib/reflection.js.map +1 -0
- package/.dist/esm/src/lib/remoteMethods.d.ts +10 -0
- package/.dist/esm/src/lib/remoteMethods.js +109 -0
- package/.dist/esm/src/lib/remoteMethods.js.map +1 -0
- package/.dist/esm/src/lib/test/aotEmitter-test-router.d.ts +4 -0
- package/.dist/esm/src/lib/test/aotEmitter-test-router.js +24 -0
- package/.dist/esm/src/lib/test/aotEmitter-test-router.js.map +1 -0
- package/.dist/esm/src/router.d.ts +30 -0
- package/.dist/esm/src/router.js +431 -0
- package/.dist/esm/src/router.js.map +1 -0
- package/.dist/esm/src/routes/client.routes.d.ts +16 -0
- package/.dist/esm/src/routes/client.routes.js +76 -0
- package/.dist/esm/src/routes/client.routes.js.map +1 -0
- package/.dist/esm/src/routes/errors.routes.d.ts +7 -0
- package/.dist/esm/src/routes/errors.routes.js +49 -0
- package/.dist/esm/src/routes/errors.routes.js.map +1 -0
- package/.dist/esm/src/routes/mion.routes.d.ts +10 -0
- package/.dist/esm/src/routes/mion.routes.js +13 -0
- package/.dist/esm/src/routes/mion.routes.js.map +1 -0
- package/.dist/esm/src/routes/serializer.routes.d.ts +9 -0
- package/.dist/esm/src/routes/serializer.routes.js +191 -0
- package/.dist/esm/src/routes/serializer.routes.js.map +1 -0
- package/.dist/esm/src/routesFlow.d.ts +7 -0
- package/.dist/esm/src/routesFlow.js +227 -0
- package/.dist/esm/src/routesFlow.js.map +1 -0
- package/.dist/esm/src/types/context.d.ts +57 -0
- package/.dist/esm/src/types/context.js +31 -0
- package/.dist/esm/src/types/context.js.map +1 -0
- package/.dist/esm/src/types/definitions.d.ts +20 -0
- package/.dist/esm/src/types/definitions.js +15 -0
- package/.dist/esm/src/types/definitions.js.map +1 -0
- package/.dist/esm/src/types/general.d.ts +27 -0
- package/.dist/esm/src/types/general.js +14 -0
- package/.dist/esm/src/types/general.js.map +1 -0
- package/.dist/esm/src/types/guards.d.ts +17 -0
- package/.dist/esm/src/types/guards.js +66 -0
- package/.dist/esm/src/types/guards.js.map +1 -0
- package/.dist/esm/src/types/handlers.d.ts +12 -0
- package/.dist/esm/src/types/handlers.js +13 -0
- package/.dist/esm/src/types/handlers.js.map +1 -0
- package/.dist/esm/src/types/publicMethods.d.ts +45 -0
- package/.dist/esm/src/types/publicMethods.js +24 -0
- package/.dist/esm/src/types/publicMethods.js.map +1 -0
- package/.dist/esm/src/types/remoteMethods.d.ts +44 -0
- package/.dist/esm/src/types/remoteMethods.js +25 -0
- package/.dist/esm/src/types/remoteMethods.js.map +1 -0
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/package.json +67 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { MION_ROUTES, RpcError, StatusCodes } from "@mionjs/core";
|
|
2
|
+
import { route } from "../lib/handlers.js";
|
|
3
|
+
const __ΩRecord = ["K", "T", "Record", `l'e#"Rb!b"Pde"!N#!w#y`];
|
|
4
|
+
function __assignType(fn, args) {
|
|
5
|
+
fn.__type = args;
|
|
6
|
+
return fn;
|
|
7
|
+
}
|
|
8
|
+
const mionErrorsRoutes = {
|
|
9
|
+
/**
|
|
10
|
+
* !IMPORTANT!
|
|
11
|
+
* This is declared as route mostly to reuse existing router serialization/deserialization functionality.
|
|
12
|
+
* But "@thrownErrors" is expected to be a field in response body that contain all thrown errors from other executables.
|
|
13
|
+
* thrown Errors are not strongly typed and are all serialized/deserialized as RpcError<string>.
|
|
14
|
+
* this also prevents users to register a route with the same name.
|
|
15
|
+
*/
|
|
16
|
+
[MION_ROUTES.thrownErrors]: route(__assignType((ctx) => {
|
|
17
|
+
return ctx.request.thrownErrors || {};
|
|
18
|
+
}, ["CallContext", "ctx", () => __ΩRecord, () => RpcError, "", 'P"w!2"&P&7$o##/%'])),
|
|
19
|
+
/**
|
|
20
|
+
* Route that handles not-found scenarios when a requested route doesn't exist.
|
|
21
|
+
* This route is registered as an internal mion route.
|
|
22
|
+
* The route is called by dispatch logic when no matching route is found.
|
|
23
|
+
* Throws an RpcError that will be caught and stored in thrownErrors by the router.
|
|
24
|
+
*/
|
|
25
|
+
[MION_ROUTES.notFound]: route(__assignType((ctx) => {
|
|
26
|
+
throw new RpcError({
|
|
27
|
+
statusCode: StatusCodes.NOT_FOUND,
|
|
28
|
+
publicMessage: `Route not found`,
|
|
29
|
+
type: "route-not-found"
|
|
30
|
+
});
|
|
31
|
+
}, ["CallContext", "ctx", "route-not-found", () => RpcError, "", 'P"w!2"P.#7$/%'])),
|
|
32
|
+
/**
|
|
33
|
+
* Platform error route for strongly typing platform/adapter errors.
|
|
34
|
+
* Platform errors occur before reaching the router or outside the router
|
|
35
|
+
* and are platform/adapter related (e.g., HTTP server errors, connection issues).
|
|
36
|
+
* This route is used for serialization/deserialization of platform errors.
|
|
37
|
+
* This also prevents users to register a route with the same name.
|
|
38
|
+
*/
|
|
39
|
+
[MION_ROUTES.platformError]: route(__assignType((_ctx) => {
|
|
40
|
+
return new RpcError({
|
|
41
|
+
publicMessage: "Platform error",
|
|
42
|
+
type: "platform-error"
|
|
43
|
+
});
|
|
44
|
+
}, ["CallContext", "_ctx", () => RpcError, "", 'P"w!2"P&7#/$']))
|
|
45
|
+
};
|
|
46
|
+
export {
|
|
47
|
+
mionErrorsRoutes
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=errors.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.routes.js","sources":["../../../../src/routes/errors.routes.ts"],"sourcesContent":["/* ########\n * 2023 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport type {Routes} from '../types/general.ts';\nimport type {CallContext} from '../types/context.ts';\nimport {RpcError, MION_ROUTES, StatusCodes} from '@mionjs/core';\nimport {route} from '../lib/handlers.ts';\n\nexport const mionErrorsRoutes = {\n /**\n * !IMPORTANT!\n * This is declared as route mostly to reuse existing router serialization/deserialization functionality.\n * But \"@thrownErrors\" is expected to be a field in response body that contain all thrown errors from other executables.\n * thrown Errors are not strongly typed and are all serialized/deserialized as RpcError<string>.\n * this also prevents users to register a route with the same name.\n */\n [MION_ROUTES.thrownErrors]: route((ctx: CallContext): Record<string, RpcError<string>> => {\n return ctx.request.thrownErrors || {};\n }),\n /**\n * Route that handles not-found scenarios when a requested route doesn't exist.\n * This route is registered as an internal mion route.\n * The route is called by dispatch logic when no matching route is found.\n * Throws an RpcError that will be caught and stored in thrownErrors by the router.\n */\n [MION_ROUTES.notFound]: route((ctx: CallContext): RpcError<'route-not-found'> => {\n throw new RpcError({\n statusCode: StatusCodes.NOT_FOUND,\n publicMessage: `Route not found`,\n type: 'route-not-found',\n });\n }),\n /**\n * Platform error route for strongly typing platform/adapter errors.\n * Platform errors occur before reaching the router or outside the router\n * and are platform/adapter related (e.g., HTTP server errors, connection issues).\n * This route is used for serialization/deserialization of platform errors.\n * This also prevents users to register a route with the same name.\n */\n [MION_ROUTES.platformError]: route((_ctx: CallContext): RpcError<string> => {\n // Platform errors are passed through context, this route is for type serialization\n return new RpcError({\n publicMessage: 'Platform error',\n type: 'platform-error',\n });\n }),\n} as const satisfies Routes;\n"],"names":[],"mappings":";;;;;;;AAYO,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5B,CAAC,YAAY,YAAY,GAAG,MAAK,aAAC,CAAC,QAAsD;AACrF,WAAO,IAAI,QAAQ,gBAAgB,CAAA;AAAA,EACvC,GAAC,CAAA,eAAA,OAAA,MAAA,WAAA,MAAA,UAAA,IAAA,kBAAA,CAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOD,CAAC,YAAY,QAAQ,GAAG,MAAK,aAAC,CAAC,QAAiD;AAC5E,UAAM,IAAI,SAAS;AAAA,MACf,YAAY,YAAY;AAAA,MACxB,eAAe;AAAA,MACf,MAAM;AAAA,IAAA,CACT;AAAA,EACL,GAAC,CAAA,eAAA,OAAA,mBAAA,MAAA,UAAA,IAAA,eAAA,CAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQD,CAAC,YAAY,aAAa,GAAG,MAAK,aAAC,CAAC,SAAuC;AAEvE,WAAO,IAAI,SAAS;AAAA,MAChB,eAAe;AAAA,MACf,MAAM;AAAA,IAAA,CACT;AAAA,EACL,GAAC,CAAA,eAAA,QAAA,MAAA,UAAA,IAAA,cAAA,CAAA,CAAA;;"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PublicApi } from '../types/publicMethods.ts';
|
|
2
|
+
export declare const mionRoutes: {
|
|
3
|
+
readonly "@thrownErrors": import('../types/definitions.ts').RouteDef<(ctx: import('../types/context.ts').CallContext) => Record<string, import('@mionjs/core/.dist/esm/index.js').RpcError<string>>>;
|
|
4
|
+
readonly "mion@notFound": import('../types/definitions.ts').RouteDef<(ctx: import('../types/context.ts').CallContext) => import('@mionjs/core/.dist/esm/index.js').RpcError<"route-not-found">>;
|
|
5
|
+
readonly "mion@platformError": import('../types/definitions.ts').RouteDef<(_ctx: import('../types/context.ts').CallContext) => import('@mionjs/core/.dist/esm/index.js').RpcError<string>>;
|
|
6
|
+
readonly "mion@methodsMetadataById": import('../types/definitions.ts').RouteDef<(ctx: any, methodsIds: string[], getAllRemoteMethods?: boolean) => import('@mionjs/core/.dist/esm/index.js').SerializableMethodsData | import('@mionjs/core/.dist/esm/index.js').RpcError<"rpc-metadata-not-found">>;
|
|
7
|
+
readonly "mion@methodsMetadataByPath": import('../types/definitions.ts').RouteDef<(ctx: any, path: string, getAllRemoteMethods?: boolean) => import('@mionjs/core/.dist/esm/index.js').SerializableMethodsData | import('@mionjs/core/.dist/esm/index.js').RpcError<"rpc-metadata-not-found">>;
|
|
8
|
+
};
|
|
9
|
+
export type MionRoutes = PublicApi<typeof mionRoutes>;
|
|
10
|
+
export declare type __ΩMionRoutes = any[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { __ΩPublicApi as ___PublicApi } from "../types/publicMethods.js";
|
|
2
|
+
import { mionClientRoutes } from "./client.routes.js";
|
|
3
|
+
import { mionErrorsRoutes } from "./errors.routes.js";
|
|
4
|
+
const mionRoutes = {
|
|
5
|
+
...mionClientRoutes,
|
|
6
|
+
...mionErrorsRoutes
|
|
7
|
+
};
|
|
8
|
+
const __ΩMionRoutes = [() => ___PublicApi, () => mionRoutes, "MionRoutes", 'i"o!"w#y'];
|
|
9
|
+
export {
|
|
10
|
+
__ΩMionRoutes,
|
|
11
|
+
mionRoutes
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=mion.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mion.routes.js","sources":["../../../../src/routes/mion.routes.ts"],"sourcesContent":["import {Routes} from '../types/general.ts';\nimport {PublicApi} from '../types/publicMethods.ts';\nimport {mionClientRoutes} from './client.routes.ts';\nimport {mionErrorsRoutes} from './errors.routes.ts';\n\nexport const mionRoutes = {\n ...mionClientRoutes,\n ...mionErrorsRoutes,\n} as const satisfies Routes;\n\nexport type MionRoutes = PublicApi<typeof mionRoutes>;\n"],"names":[],"mappings":";;;AAKO,MAAM,aAAa;AAAA,EACtB,GAAG;AAAA,EACH,GAAG;;;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CallContext } from '../types/context.ts';
|
|
2
|
+
import { RouterOptions } from '../types/general.ts';
|
|
3
|
+
import { MayReturnError } from '../types/publicMethods.ts';
|
|
4
|
+
export declare function deserializeRequestBody(context: CallContext): MayReturnError;
|
|
5
|
+
export declare function serializeResponseBody(context: CallContext, opts: RouterOptions): MayReturnError;
|
|
6
|
+
export declare const serializerMiddleFns: {
|
|
7
|
+
mionDeserializeRequest: import('../types/definitions.ts').RawMiddleFnDef<typeof deserializeRequestBody>;
|
|
8
|
+
mionSerializeResponse: import('../types/definitions.ts').RawMiddleFnDef<typeof serializeResponseBody>;
|
|
9
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { __ΩCallContext as ___CallContext, __ΩResponseBody as ___ResponseBody } from "../types/context.js";
|
|
2
|
+
import { __ΩRouterOptions as ___RouterOptions } from "../types/general.js";
|
|
3
|
+
import { __ΩMayReturnError as ___MayReturnError } from "../types/publicMethods.js";
|
|
4
|
+
import { SerializerModes, deserializeBinaryBody, RpcError, StatusCodes, serializeBinaryBody as serializeBinaryBody$1, MION_ROUTES } from "@mionjs/core";
|
|
5
|
+
import { rawMiddleFn } from "../lib/handlers.js";
|
|
6
|
+
import { getRouteExecutableFromPath, getRouteExecutable } from "../router.js";
|
|
7
|
+
import { __ΩRemoteMethod as ___RemoteMethod } from "../types/remoteMethods.js";
|
|
8
|
+
import { onExecutableError } from "../lib/dispatchError.js";
|
|
9
|
+
function deserializeRequestBody(context) {
|
|
10
|
+
if (!context.request.rawBody)
|
|
11
|
+
return;
|
|
12
|
+
let parsedBody;
|
|
13
|
+
switch (context.request.bodyType) {
|
|
14
|
+
case SerializerModes.stringifyJson:
|
|
15
|
+
try {
|
|
16
|
+
parsedBody = JSON.parse(context.request.rawBody);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
throw new RpcError({
|
|
19
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
20
|
+
type: "parsing-json-request-error",
|
|
21
|
+
publicMessage: `Invalid json request body: ${err?.message || "unknown parsing error."}`
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
break;
|
|
25
|
+
case SerializerModes.binary: {
|
|
26
|
+
const rawBody = context.request.rawBody;
|
|
27
|
+
const { body } = deserializeBinaryBody(context.path, rawBody, false);
|
|
28
|
+
parsedBody = body;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case SerializerModes.json:
|
|
32
|
+
parsedBody = context.request.rawBody;
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Invalid body type ${context.request.bodyType}`);
|
|
36
|
+
}
|
|
37
|
+
if (parsedBody) {
|
|
38
|
+
if (Array.isArray(parsedBody)) {
|
|
39
|
+
parsedBody = { [getRouteExecutableFromPath(context.path).id]: parsedBody };
|
|
40
|
+
}
|
|
41
|
+
if (typeof parsedBody !== "object")
|
|
42
|
+
throw new RpcError({
|
|
43
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
44
|
+
type: "invalid-request-body",
|
|
45
|
+
publicMessage: "Wrong request body. Expecting a body containing the route name and parameters."
|
|
46
|
+
});
|
|
47
|
+
context.request.body = parsedBody;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
deserializeRequestBody.__type = [() => ___CallContext, "context", () => ___MayReturnError, "deserializeRequestBody", 'Pn!2"n#/$'];
|
|
51
|
+
function serializeResponseBody(context, opts) {
|
|
52
|
+
const response = context.response;
|
|
53
|
+
const respBody = response.body;
|
|
54
|
+
const bodyType = context.response.serializer;
|
|
55
|
+
const thrownErrors = context.request.thrownErrors;
|
|
56
|
+
if (thrownErrors)
|
|
57
|
+
response.body["@thrownErrors"] = thrownErrors;
|
|
58
|
+
switch (bodyType) {
|
|
59
|
+
case SerializerModes.stringifyJson: {
|
|
60
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
61
|
+
const body = stringifyBody(context, context.executionChain.methods, respBody);
|
|
62
|
+
response.rawBody = body;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case SerializerModes.json: {
|
|
66
|
+
response.headers.set("content-type", "application/json; charset=utf-8");
|
|
67
|
+
prepareBodyForJson(context, context.executionChain.methods, respBody);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case SerializerModes.binary: {
|
|
71
|
+
response.headers.set("content-type", "application/octet-stream");
|
|
72
|
+
serializeBinaryBody(context, context.executionChain.methods, respBody);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
default:
|
|
76
|
+
throw new Error(`Invalid body type ${context.request.bodyType}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
serializeResponseBody.__type = [() => ___CallContext, "context", () => ___RouterOptions, "opts", () => ___MayReturnError, "serializeResponseBody", 'Pn!2"n#2$n%/&'];
|
|
80
|
+
function serializeBinaryBody(context, executionChain, respBody) {
|
|
81
|
+
const response = context.response;
|
|
82
|
+
const { serializer, buffer } = serializeBinaryBody$1(context.path, executionChain, respBody, true, context.routesFlowRouteIds);
|
|
83
|
+
response.binSerializer = serializer;
|
|
84
|
+
response.rawBody = new Uint8Array(buffer);
|
|
85
|
+
}
|
|
86
|
+
serializeBinaryBody.__type = [() => ___CallContext, "context", () => ___RemoteMethod, "executionChain", () => ___ResponseBody, "respBody", "serializeBinaryBody", `Pn!2"n#F2$n%2&$/'`];
|
|
87
|
+
function stringifyBody(context, executionChain, respBody) {
|
|
88
|
+
const props = [];
|
|
89
|
+
for (let i = 0; i < executionChain.length; i++) {
|
|
90
|
+
const method = executionChain[i];
|
|
91
|
+
const returnValue = respBody[method.id];
|
|
92
|
+
if (!method.hasReturnData || typeof returnValue === "undefined")
|
|
93
|
+
continue;
|
|
94
|
+
try {
|
|
95
|
+
const jsonValue = stringifyHandlerReturnValue(method, returnValue);
|
|
96
|
+
if (!jsonValue)
|
|
97
|
+
continue;
|
|
98
|
+
props.push(`${JSON.stringify(method.id)}:${jsonValue}`);
|
|
99
|
+
} catch (e) {
|
|
100
|
+
onStringifyExecutableError(context, method, e);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const thrownErrors = respBody["@thrownErrors"];
|
|
104
|
+
if (thrownErrors) {
|
|
105
|
+
const method = getRouteExecutable(MION_ROUTES.thrownErrors);
|
|
106
|
+
try {
|
|
107
|
+
const jsonValue = stringifyHandlerReturnValue(method, thrownErrors);
|
|
108
|
+
if (jsonValue)
|
|
109
|
+
props.push(`${JSON.stringify(method.id)}:${jsonValue}`);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
onStringifyExecutableError(context, method, e);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return `{${props.join(",")}}`;
|
|
115
|
+
}
|
|
116
|
+
stringifyBody.__type = [() => ___CallContext, "context", () => ___RemoteMethod, "executionChain", () => ___ResponseBody, "respBody", "stringifyBody", `Pn!2"n#F2$n%2&&/'`];
|
|
117
|
+
function onStringifyExecutableError(context, method, e) {
|
|
118
|
+
const err = new RpcError({
|
|
119
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
120
|
+
type: "json-stringify-response-error",
|
|
121
|
+
publicMessage: `Failed to stringify return value for handler ${method.id}, expected response type: ${method.returnJitFns.stringifyJson.typeName}`,
|
|
122
|
+
originalError: e,
|
|
123
|
+
errorData: { methodId: method.id }
|
|
124
|
+
});
|
|
125
|
+
onExecutableError(context, method, err);
|
|
126
|
+
}
|
|
127
|
+
onStringifyExecutableError.__type = [() => ___CallContext, "context", () => ___RemoteMethod, "method", "e", "onStringifyExecutableError", 'Pn!2"n#2$"2%"/&'];
|
|
128
|
+
function stringifyHandlerReturnValue(method, returnValue) {
|
|
129
|
+
if (!method.hasReturnData)
|
|
130
|
+
return "";
|
|
131
|
+
if (method.returnJitFns.prepareForJson.isNoop)
|
|
132
|
+
JSON.stringify(returnValue);
|
|
133
|
+
return method.returnJitFns.stringifyJson.fn(returnValue);
|
|
134
|
+
}
|
|
135
|
+
stringifyHandlerReturnValue.__type = [() => ___RemoteMethod, "method", "returnValue", "stringifyHandlerReturnValue", 'Pn!2""2#&/$'];
|
|
136
|
+
function prepareBodyForJson(context, executionChain, respBody) {
|
|
137
|
+
for (let i = 0; i < executionChain.length; i++) {
|
|
138
|
+
const method = executionChain[i];
|
|
139
|
+
const returnValue = respBody[method.id];
|
|
140
|
+
if (!method.hasReturnData || typeof returnValue === "undefined")
|
|
141
|
+
continue;
|
|
142
|
+
try {
|
|
143
|
+
const preparedValue = prepareHandlerReturnValue(method, returnValue);
|
|
144
|
+
if (preparedValue !== void 0)
|
|
145
|
+
respBody[method.id] = preparedValue;
|
|
146
|
+
} catch (e) {
|
|
147
|
+
onPrepareForJsonExecutableError(context, method, e);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const thrownErrors = respBody["@thrownErrors"];
|
|
151
|
+
if (thrownErrors) {
|
|
152
|
+
const method = getRouteExecutable(MION_ROUTES.thrownErrors);
|
|
153
|
+
try {
|
|
154
|
+
const preparedValue = prepareHandlerReturnValue(method, thrownErrors);
|
|
155
|
+
if (preparedValue !== void 0)
|
|
156
|
+
respBody[method.id] = preparedValue;
|
|
157
|
+
} catch (e) {
|
|
158
|
+
onPrepareForJsonExecutableError(context, method, e);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
prepareBodyForJson.__type = [() => ___CallContext, "context", () => ___RemoteMethod, "executionChain", () => ___ResponseBody, "respBody", "prepareBodyForJson", `Pn!2"n#F2$n%2&$/'`];
|
|
163
|
+
function onPrepareForJsonExecutableError(context, method, e) {
|
|
164
|
+
const err = new RpcError({
|
|
165
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
166
|
+
type: "prepare-for-json-response-error",
|
|
167
|
+
publicMessage: `Failed to prepare return value for JSON for handler ${method.id}, expected response type: ${method.returnJitFns.prepareForJson.typeName}`,
|
|
168
|
+
originalError: e,
|
|
169
|
+
errorData: { methodId: method.id }
|
|
170
|
+
});
|
|
171
|
+
onExecutableError(context, method, err);
|
|
172
|
+
}
|
|
173
|
+
onPrepareForJsonExecutableError.__type = [() => ___CallContext, "context", () => ___RemoteMethod, "method", "e", "onPrepareForJsonExecutableError", 'Pn!2"n#2$"2%"/&'];
|
|
174
|
+
function prepareHandlerReturnValue(method, returnValue) {
|
|
175
|
+
if (!method.hasReturnData)
|
|
176
|
+
return void 0;
|
|
177
|
+
if (method.returnJitFns.prepareForJson.isNoop)
|
|
178
|
+
return returnValue;
|
|
179
|
+
return method.returnJitFns.prepareForJson.fn(returnValue);
|
|
180
|
+
}
|
|
181
|
+
prepareHandlerReturnValue.__type = [() => ___RemoteMethod, "method", "returnValue", "prepareHandlerReturnValue", 'Pn!2""2#"/$'];
|
|
182
|
+
const serializerMiddleFns = {
|
|
183
|
+
mionDeserializeRequest: rawMiddleFn(deserializeRequestBody, { runOnError: true }),
|
|
184
|
+
mionSerializeResponse: rawMiddleFn(serializeResponseBody, { runOnError: true })
|
|
185
|
+
};
|
|
186
|
+
export {
|
|
187
|
+
deserializeRequestBody,
|
|
188
|
+
serializeResponseBody,
|
|
189
|
+
serializerMiddleFns
|
|
190
|
+
};
|
|
191
|
+
//# sourceMappingURL=serializer.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.routes.js","sources":["../../../../src/routes/serializer.routes.ts"],"sourcesContent":["/* ########\n * 2023 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {MionResponse, MionRequest, CallContext, ResponseBody} from '../types/context.ts';\nimport {RouterOptions} from '../types/general.ts';\nimport {MiddleFnsCollection, MayReturnError} from '../types/publicMethods.ts';\nimport {\n AnyObject,\n Mutable,\n MION_ROUTES,\n StatusCodes,\n serializeBinaryBody as coreSerializeBinaryBody,\n deserializeBinaryBody as coreDeserializeBinaryBody,\n SerializerModes,\n} from '@mionjs/core';\nimport {rawMiddleFn} from '../lib/handlers.ts';\nimport {getRouteExecutableFromPath, getRouteExecutable} from '../router.ts';\nimport {RpcError} from '@mionjs/core';\nimport {RemoteMethod} from '../types/remoteMethods.ts';\nimport {onExecutableError} from '../lib/dispatchError.ts';\n\n// ############# PUBLIC METHODS #############\n\n/**\n * Deserializes the request body and stores it in the request body property.\n * This method is called before any other middleFn or route handler.\n * For binary requests, the body is parsed lazily per-method in dispatch.ts.\n * @mion:middleFn\n */\nexport function deserializeRequestBody(context: CallContext): MayReturnError {\n if (!context.request.rawBody) return; // empty body\n let parsedBody: any;\n switch (context.request.bodyType) {\n case SerializerModes.stringifyJson: // jit stringify json\n try {\n parsedBody = JSON.parse(context.request.rawBody as string);\n } catch (err: any) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'parsing-json-request-error',\n publicMessage: `Invalid json request body: ${err?.message || 'unknown parsing error.'}`,\n });\n }\n break;\n case SerializerModes.binary: {\n // binary\n const rawBody = context.request.rawBody as Uint8Array;\n const {body} = coreDeserializeBinaryBody(context.path, rawBody, false);\n parsedBody = body;\n break;\n }\n case SerializerModes.json: // Object (pre-parsed body from platforms like Google Cloud Functions where Express auto-parses JSON)\n parsedBody = context.request.rawBody;\n break;\n default:\n throw new Error(`Invalid body type ${context.request.bodyType}`);\n }\n if (parsedBody) {\n if (Array.isArray(parsedBody)) {\n // when the body is an array we assume it's a single route call and we have to reconstruct the body\n // http://my-api.com/route1 [p1, p2, p3] => {route1: [p1, p2, p3]}\n parsedBody = {[getRouteExecutableFromPath(context.path).id]: parsedBody};\n }\n if (typeof parsedBody !== 'object')\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'invalid-request-body',\n publicMessage: 'Wrong request body. Expecting a body containing the route name and parameters.',\n });\n (context.request as Mutable<MionRequest>).body = parsedBody;\n }\n}\n\n/**\n * Serializes the response body and stores it in the response rawBody property.\n * This method is called after any other middleFn or route handler.\n * @mion:middleFn\n */\nexport function serializeResponseBody(context: CallContext, opts: RouterOptions): MayReturnError {\n const response = context.response as Mutable<MionResponse>;\n const respBody: AnyObject = response.body;\n const bodyType = context.response.serializer;\n const thrownErrors = context.request.thrownErrors as Record<string, RpcError<string>> | undefined;\n // Add thrownErrors to response body before the serializer runs\n if (thrownErrors) (response.body as Mutable<AnyObject>)['@thrownErrors'] = thrownErrors;\n switch (bodyType) {\n case SerializerModes.stringifyJson: {\n // json - use stringifyJson JIT function\n response.headers.set('content-type', 'application/json; charset=utf-8');\n const body = stringifyBody(context, context.executionChain.methods, respBody);\n response.rawBody = body;\n break;\n }\n case SerializerModes.json: {\n // pre-serialized object - only prepare for JSON, don't stringify\n // Platform adapters will handle the actual JSON stringification\n // prepareForJson mutates response.body in place, so we don't set rawBody\n response.headers.set('content-type', 'application/json; charset=utf-8');\n prepareBodyForJson(context, context.executionChain.methods, respBody);\n break;\n }\n case SerializerModes.binary: {\n // binary - use toBinary JIT function\n response.headers.set('content-type', 'application/octet-stream');\n serializeBinaryBody(context, context.executionChain.methods, respBody);\n break;\n }\n default:\n throw new Error(`Invalid body type ${context.request.bodyType}`);\n }\n}\n\n/** Serializes response body to binary format using the core serializeBinaryBody function */\nfunction serializeBinaryBody(context: CallContext, executionChain: RemoteMethod[], respBody: ResponseBody): void {\n const response = context.response as Mutable<MionResponse>;\n // For routesFlow, use routesFlowRouteIds from context for proper buffer sizing\n const {serializer, buffer} = coreSerializeBinaryBody(\n context.path,\n executionChain,\n respBody,\n true,\n context.routesFlowRouteIds\n );\n response.binSerializer = serializer;\n response.rawBody = new Uint8Array(buffer);\n}\n\nfunction stringifyBody(context: CallContext, executionChain: RemoteMethod[], respBody: ResponseBody): string {\n const props: string[] = [];\n for (let i = 0; i < executionChain.length; i++) {\n const method = executionChain[i];\n const returnValue = respBody[method.id];\n if (!method.hasReturnData || typeof returnValue === 'undefined') continue;\n try {\n const jsonValue = stringifyHandlerReturnValue(method, returnValue);\n if (!jsonValue) continue;\n props.push(`${JSON.stringify(method.id)}:${jsonValue}`);\n } catch (e: any) {\n onStringifyExecutableError(context, method, e);\n }\n }\n\n // Serialize thrownErrors if they exist\n const thrownErrors = respBody['@thrownErrors'];\n if (thrownErrors) {\n const method = getRouteExecutable(MION_ROUTES.thrownErrors)!;\n try {\n const jsonValue = stringifyHandlerReturnValue(method, thrownErrors);\n if (jsonValue) props.push(`${JSON.stringify(method.id)}:${jsonValue}`);\n } catch (e: any) {\n onStringifyExecutableError(context, method, e);\n }\n }\n return `{${props.join(',')}}`;\n}\n\nfunction onStringifyExecutableError(context: CallContext, method: RemoteMethod, e: any) {\n const err = new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'json-stringify-response-error',\n publicMessage: `Failed to stringify return value for handler ${method.id}, expected response type: ${method.returnJitFns.stringifyJson.typeName}`,\n originalError: e,\n errorData: {methodId: method.id},\n });\n onExecutableError(context, method, err);\n}\n\nfunction stringifyHandlerReturnValue(method: RemoteMethod, returnValue: any): string {\n if (!method.hasReturnData) return '';\n // id data does not require custom encoding then we use native json\n if (method.returnJitFns.prepareForJson.isNoop) JSON.stringify(returnValue);\n return method.returnJitFns.stringifyJson.fn(returnValue);\n}\n\nfunction prepareBodyForJson(context: CallContext, executionChain: RemoteMethod[], respBody: ResponseBody): void {\n // prepareForJson mutates the response body in place\n for (let i = 0; i < executionChain.length; i++) {\n const method = executionChain[i];\n const returnValue = respBody[method.id];\n if (!method.hasReturnData || typeof returnValue === 'undefined') continue;\n try {\n const preparedValue = prepareHandlerReturnValue(method, returnValue);\n if (preparedValue !== undefined) (respBody as Mutable<ResponseBody>)[method.id] = preparedValue;\n } catch (e: any) {\n onPrepareForJsonExecutableError(context, method, e);\n }\n }\n // Prepare thrownErrors if they exist\n const thrownErrors = respBody['@thrownErrors'];\n if (thrownErrors) {\n const method = getRouteExecutable(MION_ROUTES.thrownErrors)!;\n try {\n const preparedValue = prepareHandlerReturnValue(method, thrownErrors);\n if (preparedValue !== undefined) (respBody as Mutable<ResponseBody>)[method.id] = preparedValue;\n } catch (e: any) {\n onPrepareForJsonExecutableError(context, method, e);\n }\n }\n}\n\nfunction onPrepareForJsonExecutableError(context: CallContext, method: RemoteMethod, e: any) {\n const err = new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'prepare-for-json-response-error',\n publicMessage: `Failed to prepare return value for JSON for handler ${method.id}, expected response type: ${method.returnJitFns.prepareForJson.typeName}`,\n originalError: e,\n errorData: {methodId: method.id},\n });\n onExecutableError(context, method, err);\n}\n\nfunction prepareHandlerReturnValue(method: RemoteMethod, returnValue: any): any {\n if (!method.hasReturnData) return undefined;\n if (method.returnJitFns.prepareForJson.isNoop) return returnValue;\n return method.returnJitFns.prepareForJson.fn(returnValue);\n}\n\nexport const serializerMiddleFns = {\n mionDeserializeRequest: rawMiddleFn(deserializeRequestBody, {runOnError: true}),\n mionSerializeResponse: rawMiddleFn(serializeResponseBody, {runOnError: true}),\n} satisfies MiddleFnsCollection;\n"],"names":["coreDeserializeBinaryBody","coreSerializeBinaryBody"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { RouterOptions } from './types/general.ts';
|
|
2
|
+
import { MethodsExecutionChain } from './types/remoteMethods.ts';
|
|
3
|
+
import { RoutesFlowExecutionResult } from './types/context.ts';
|
|
4
|
+
export declare function clearRoutesFlowCache(): void;
|
|
5
|
+
export declare function getRoutesFlowCacheSize(): number;
|
|
6
|
+
export declare function getCachedRoutesFlow(query: string): MethodsExecutionChain | undefined;
|
|
7
|
+
export declare function getRoutesFlowExecutionChain(rawRequest: unknown, opts: RouterOptions, urlQuery?: string): RoutesFlowExecutionResult;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { RpcError, StatusCodes, fromBase64Url, SerializerModes, PURE_SERVER_FN_NAMESPACE, getNoopJitFns, HandlerType } from "@mionjs/core";
|
|
2
|
+
import { serverPureFnsCache } from "virtual:mion-server-pure-fns";
|
|
3
|
+
import { startMiddleFns, endMiddleFns, getRouteExecutionChain, getRouterOptions } from "./router.js";
|
|
4
|
+
import { __ΩRouterOptions as ___RouterOptions } from "./types/general.js";
|
|
5
|
+
import { __ΩMethodsExecutionChain as ___MethodsExecutionChain, __ΩRemoteMethod as ___RemoteMethod } from "./types/remoteMethods.js";
|
|
6
|
+
import { __ΩRoutesFlowExecutionResult as ___RoutesFlowExecutionResult } from "./types/context.js";
|
|
7
|
+
function __assignType(fn, args) {
|
|
8
|
+
fn.__type = args;
|
|
9
|
+
return fn;
|
|
10
|
+
}
|
|
11
|
+
const routesFlowCache = (Map.Ω = [["&"], [() => ___MethodsExecutionChain, "n!"]], /* @__PURE__ */ new Map());
|
|
12
|
+
const cacheOrder = [];
|
|
13
|
+
const mappingMethodCache = (Map.Ω = [["&"], [() => ___RemoteMethod, "n!"]], /* @__PURE__ */ new Map());
|
|
14
|
+
function clearRoutesFlowCache() {
|
|
15
|
+
routesFlowCache.clear();
|
|
16
|
+
cacheOrder.length = 0;
|
|
17
|
+
mappingMethodCache.clear();
|
|
18
|
+
}
|
|
19
|
+
clearRoutesFlowCache.__type = ["clearRoutesFlowCache", "P$/!"];
|
|
20
|
+
function getRoutesFlowCacheSize() {
|
|
21
|
+
return routesFlowCache.size;
|
|
22
|
+
}
|
|
23
|
+
getRoutesFlowCacheSize.__type = ["getRoutesFlowCacheSize", "P'/!"];
|
|
24
|
+
function getCachedRoutesFlow(query) {
|
|
25
|
+
return routesFlowCache.get(query);
|
|
26
|
+
}
|
|
27
|
+
getCachedRoutesFlow.__type = ["query", () => ___MethodsExecutionChain, "getCachedRoutesFlow", 'P&2!Pn"-J/#'];
|
|
28
|
+
function addToRoutesFlowCache(query, chain) {
|
|
29
|
+
const routerOpts = getRouterOptions();
|
|
30
|
+
const maxSize = routerOpts.maxRoutesFlowsCacheSize;
|
|
31
|
+
if (maxSize <= 0)
|
|
32
|
+
return;
|
|
33
|
+
while (cacheOrder.length >= maxSize) {
|
|
34
|
+
const oldestKey = cacheOrder.shift();
|
|
35
|
+
if (oldestKey)
|
|
36
|
+
routesFlowCache.delete(oldestKey);
|
|
37
|
+
}
|
|
38
|
+
routesFlowCache.set(query, chain);
|
|
39
|
+
cacheOrder.push(query);
|
|
40
|
+
}
|
|
41
|
+
addToRoutesFlowCache.__type = ["query", () => ___MethodsExecutionChain, "chain", "addToRoutesFlowCache", 'P&2!n"2#$/$'];
|
|
42
|
+
function decodeRoutesFlowQuery(urlQuery) {
|
|
43
|
+
try {
|
|
44
|
+
const dataParam = urlQuery.startsWith("data=") ? urlQuery.slice(5) : urlQuery;
|
|
45
|
+
const jsonString = fromBase64Url(dataParam);
|
|
46
|
+
return JSON.parse(jsonString);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
throw new RpcError({
|
|
49
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
50
|
+
type: "routesFlow-invalid-query",
|
|
51
|
+
publicMessage: "RoutesFlow query string is not valid base64url-encoded JSON.",
|
|
52
|
+
errorData: { parseError: e?.message || "Unknown error" }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
decodeRoutesFlowQuery.__type = ["urlQuery", "RoutesFlowQuery", "decodeRoutesFlowQuery", 'P&2!"w"/#'];
|
|
57
|
+
function getRoutesFlowExecutionChain(rawRequest, opts, urlQuery) {
|
|
58
|
+
if (!urlQuery) {
|
|
59
|
+
throw new RpcError({
|
|
60
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
61
|
+
type: "routesFlow-missing-query",
|
|
62
|
+
publicMessage: "RoutesFlow request requires a query string with route paths."
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const query = decodeRoutesFlowQuery(urlQuery);
|
|
66
|
+
const routePaths = query.routes;
|
|
67
|
+
const mappings = query.mappings;
|
|
68
|
+
if (!routePaths || routePaths.length === 0) {
|
|
69
|
+
throw new RpcError({
|
|
70
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
71
|
+
type: "routesFlow-empty-routes",
|
|
72
|
+
publicMessage: "RoutesFlow request requires at least one route path in query string."
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const routeIds = routePaths.map(__assignType((path) => path.startsWith("/") ? path.slice(1) : path, ["path", "", 'P"2!"/"']));
|
|
76
|
+
let executionChain = routesFlowCache.get(urlQuery);
|
|
77
|
+
if (executionChain)
|
|
78
|
+
return { executionChain, routesFlowRouteIds: routeIds, mappings };
|
|
79
|
+
executionChain = buildMergedExecutionChain(routePaths, rawRequest, opts, mappings);
|
|
80
|
+
addToRoutesFlowCache(urlQuery, executionChain);
|
|
81
|
+
return { executionChain, routesFlowRouteIds: routeIds, mappings };
|
|
82
|
+
}
|
|
83
|
+
getRoutesFlowExecutionChain.__type = ["rawRequest", () => ___RouterOptions, "opts", "urlQuery", () => ___RoutesFlowExecutionResult, "getRoutesFlowExecutionChain", 'P#2!n"2#&2$8n%/&'];
|
|
84
|
+
function buildMergedExecutionChain(routePaths, rawRequest, opts, mappings) {
|
|
85
|
+
const seenIds = (Set.Ω = [["&"]], /* @__PURE__ */ new Set());
|
|
86
|
+
const middleMethods = [];
|
|
87
|
+
let resolvedSerializer;
|
|
88
|
+
let firstRouteIndex = -1;
|
|
89
|
+
const defaultSerializerCode = SerializerModes[opts.serializer];
|
|
90
|
+
const startMiddleFnIds = new Set(startMiddleFns.map(__assignType((m) => m.id, ["m", "", 'P"2!"/"'])));
|
|
91
|
+
const endMiddleFnIds = new Set(endMiddleFns.map(__assignType((m) => m.id, ["m", "", 'P"2!"/"'])));
|
|
92
|
+
for (const routePath of routePaths) {
|
|
93
|
+
const transformedPath = opts.pathTransform?.(rawRequest, routePath) || routePath;
|
|
94
|
+
const chain = getRouteExecutionChain(transformedPath);
|
|
95
|
+
if (!chain) {
|
|
96
|
+
throw new RpcError({
|
|
97
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
98
|
+
type: "routesFlow-route-not-found",
|
|
99
|
+
publicMessage: `Route not found in routesFlow: ${routePath}`,
|
|
100
|
+
errorData: { routePath }
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (!resolvedSerializer) {
|
|
104
|
+
resolvedSerializer = chain.serializer;
|
|
105
|
+
firstRouteIndex = chain.routeIndex;
|
|
106
|
+
} else if (resolvedSerializer !== chain.serializer) {
|
|
107
|
+
resolvedSerializer = defaultSerializerCode;
|
|
108
|
+
}
|
|
109
|
+
for (const method of chain.methods) {
|
|
110
|
+
if (seenIds.has(method.id))
|
|
111
|
+
continue;
|
|
112
|
+
if (startMiddleFnIds.has(method.id))
|
|
113
|
+
continue;
|
|
114
|
+
if (endMiddleFnIds.has(method.id))
|
|
115
|
+
continue;
|
|
116
|
+
seenIds.add(method.id);
|
|
117
|
+
middleMethods.push(method);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (mappings && mappings.length > 0) {
|
|
121
|
+
insertMappingMethods(middleMethods, mappings);
|
|
122
|
+
}
|
|
123
|
+
const mergedMethods = [...startMiddleFns, ...middleMethods, ...endMiddleFns];
|
|
124
|
+
return {
|
|
125
|
+
// Use the first route's routeIndex since that's where the first route handler is
|
|
126
|
+
routeIndex: firstRouteIndex,
|
|
127
|
+
methods: mergedMethods,
|
|
128
|
+
serializer: resolvedSerializer ?? defaultSerializerCode
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
buildMergedExecutionChain.__type = ["routePaths", "rawRequest", () => ___RouterOptions, "opts", "RoutesFlowMapping", "mappings", () => ___MethodsExecutionChain, "buildMergedExecutionChain", `P&F2!#2"n#2$"w%F2&8n'/(`];
|
|
132
|
+
function insertMappingMethods(middleMethods, mappings) {
|
|
133
|
+
const idToIndex = (Map.Ω = [["&"], ["'"]], /* @__PURE__ */ new Map());
|
|
134
|
+
for (let i = 0; i < middleMethods.length; i++) {
|
|
135
|
+
idToIndex.set(middleMethods[i].id, i);
|
|
136
|
+
}
|
|
137
|
+
const insertions = [];
|
|
138
|
+
for (const mapping of mappings) {
|
|
139
|
+
const fromIndex = idToIndex.get(mapping.fromId);
|
|
140
|
+
const toIndex = idToIndex.get(mapping.toId);
|
|
141
|
+
if (fromIndex === void 0) {
|
|
142
|
+
throw new RpcError({
|
|
143
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
144
|
+
type: "routesFlow-mapping-invalid-source",
|
|
145
|
+
publicMessage: `Mapping source route '${mapping.fromId}' not found in routesFlow execution chain.`,
|
|
146
|
+
errorData: { mapping }
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (toIndex === void 0) {
|
|
150
|
+
throw new RpcError({
|
|
151
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
152
|
+
type: "routesFlow-mapping-invalid-target",
|
|
153
|
+
publicMessage: `Mapping target route '${mapping.toId}' not found in routesFlow execution chain.`,
|
|
154
|
+
errorData: { mapping }
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (!serverPureFnsCache[PURE_SERVER_FN_NAMESPACE]?.[mapping.bodyHash]?.fn) {
|
|
158
|
+
throw new RpcError({
|
|
159
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
160
|
+
type: "routesFlow-mapping-missing-pure-fn",
|
|
161
|
+
publicMessage: `Mapping pure function '${mapping.bodyHash}' not found. Ensure the function is registered on the server.`,
|
|
162
|
+
errorData: { mapping }
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
insertions.push({
|
|
166
|
+
index: fromIndex + 1,
|
|
167
|
+
method: createMappingMethod(mapping)
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
insertions.sort(__assignType((a, b) => b.index - a.index, ["a", "b", "", 'P"2!"2""/#']));
|
|
171
|
+
for (const { index, method } of insertions) {
|
|
172
|
+
middleMethods.splice(index, 0, method);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
insertMappingMethods.__type = [() => ___RemoteMethod, "middleMethods", "RoutesFlowMapping", "mappings", "insertMappingMethods", 'Pn!F2""w#F2$$/%'];
|
|
176
|
+
function createMappingMethod(mapping) {
|
|
177
|
+
const id = `mionMapFrom_${mapping.fromId}_${mapping.bodyHash}_to_${mapping.toId}`;
|
|
178
|
+
const cached = mappingMethodCache.get(id);
|
|
179
|
+
if (cached)
|
|
180
|
+
return cached;
|
|
181
|
+
const noopJitFns = getNoopJitFns();
|
|
182
|
+
const method = {
|
|
183
|
+
type: HandlerType.rawMiddleFn,
|
|
184
|
+
id,
|
|
185
|
+
isAsync: false,
|
|
186
|
+
hasReturnData: false,
|
|
187
|
+
paramsJitHash: "",
|
|
188
|
+
returnJitHash: "",
|
|
189
|
+
paramsJitFns: noopJitFns,
|
|
190
|
+
returnJitFns: noopJitFns,
|
|
191
|
+
handler: createMappingHandler(mapping),
|
|
192
|
+
options: { runOnError: false, validateParams: false },
|
|
193
|
+
methodCaller: runMappingHandler
|
|
194
|
+
};
|
|
195
|
+
mappingMethodCache.set(id, method);
|
|
196
|
+
return method;
|
|
197
|
+
}
|
|
198
|
+
createMappingMethod.__type = ["RoutesFlowMapping", "mapping", () => ___RemoteMethod, "createMappingMethod", 'P"w!2"n#/$'];
|
|
199
|
+
function createMappingHandler(mapping) {
|
|
200
|
+
return __assignType((ctx) => {
|
|
201
|
+
const sourceOutput = ctx.response.body[mapping.fromId];
|
|
202
|
+
const entry = serverPureFnsCache[PURE_SERVER_FN_NAMESPACE]?.[mapping.bodyHash];
|
|
203
|
+
if (!entry?.fn) {
|
|
204
|
+
throw new RpcError({
|
|
205
|
+
statusCode: StatusCodes.UNEXPECTED_ERROR,
|
|
206
|
+
type: "routesFlow-mapping-missing-pure-fn",
|
|
207
|
+
publicMessage: `Mapping pure function '${mapping.bodyHash}' not found at runtime.`
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
const mappedValue = entry.fn(sourceOutput);
|
|
211
|
+
const targetParams = ctx.request.body[mapping.toId];
|
|
212
|
+
if (targetParams)
|
|
213
|
+
targetParams[mapping.paramIndex] = mappedValue;
|
|
214
|
+
}, ["CallContext", "ctx", "", 'P"w!2""/#']);
|
|
215
|
+
}
|
|
216
|
+
createMappingHandler.__type = ["RoutesFlowMapping", "mapping", "createMappingHandler", 'P"w!2""/#'];
|
|
217
|
+
async function runMappingHandler(context, executable, ...args) {
|
|
218
|
+
return executable.handler(context);
|
|
219
|
+
}
|
|
220
|
+
runMappingHandler.__type = ["CallContext", "context", () => ___RemoteMethod, "executable", "args", "runMappingHandler", 'P"w!2"n#2$#@2%"/&'];
|
|
221
|
+
export {
|
|
222
|
+
clearRoutesFlowCache,
|
|
223
|
+
getCachedRoutesFlow,
|
|
224
|
+
getRoutesFlowCacheSize,
|
|
225
|
+
getRoutesFlowExecutionChain
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=routesFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routesFlow.js","sources":["../../../src/routesFlow.ts"],"sourcesContent":["/* ########\n * 2023 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {\n RpcError,\n SerializerCode,\n SerializerModes,\n StatusCodes,\n HandlerType,\n getNoopJitFns,\n PURE_SERVER_FN_NAMESPACE,\n fromBase64Url,\n} from '@mionjs/core';\nimport {serverPureFnsCache} from 'virtual:mion-server-pure-fns';\nimport {getRouteExecutionChain, getRouterOptions, startMiddleFns, endMiddleFns} from './router.ts';\nimport {RouterOptions} from './types/general.ts';\nimport {MethodsExecutionChain, RemoteMethod} from './types/remoteMethods.ts';\nimport {RoutesFlowExecutionResult} from './types/context.ts';\nimport type {CallContext} from './types/context.ts';\nimport type {RoutesFlowQuery, RoutesFlowMapping} from '@mionjs/core';\n\n// ############# ROUTES_FLOW CACHE #############\n\n/** FILO cache for merged execution chains. Key is the query string, value is the cached chain. */\nconst routesFlowCache = new Map<string, MethodsExecutionChain>();\nconst cacheOrder: string[] = [];\n/** Cache for mapping RemoteMethods keyed by their unique ID */\nconst mappingMethodCache = new Map<string, RemoteMethod>();\n\n/** Clears the routesFlow cache and mapping method cache - useful for testing */\nexport function clearRoutesFlowCache(): void {\n routesFlowCache.clear();\n cacheOrder.length = 0;\n mappingMethodCache.clear();\n}\n\n/** Returns the current routesFlow cache size */\nexport function getRoutesFlowCacheSize(): number {\n return routesFlowCache.size;\n}\n\n/** Returns a cached routesFlow chain by query string */\nexport function getCachedRoutesFlow(query: string): MethodsExecutionChain | undefined {\n return routesFlowCache.get(query);\n}\n\n/** Adds a merged chain to the cache with FILO eviction */\nfunction addToRoutesFlowCache(query: string, chain: MethodsExecutionChain): void {\n const routerOpts = getRouterOptions();\n const maxSize = routerOpts.maxRoutesFlowsCacheSize;\n // Caching disabled\n if (maxSize <= 0) return;\n // Evict oldest entries if cache is full (FILO - First In, Last Out)\n while (cacheOrder.length >= maxSize) {\n const oldestKey = cacheOrder.shift();\n if (oldestKey) routesFlowCache.delete(oldestKey);\n }\n routesFlowCache.set(query, chain);\n cacheOrder.push(query);\n}\n\n// ############# QUERY PARSING #############\n\n/** Decodes a base64url-encoded JSON routesFlow query string, expects `data=<base64url>` format */\nfunction decodeRoutesFlowQuery(urlQuery: string): RoutesFlowQuery {\n try {\n const dataParam = urlQuery.startsWith('data=') ? urlQuery.slice(5) : urlQuery;\n const jsonString = fromBase64Url(dataParam);\n return JSON.parse(jsonString) as RoutesFlowQuery;\n } catch (e: any) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-invalid-query',\n publicMessage: 'RoutesFlow query string is not valid base64url-encoded JSON.',\n errorData: {parseError: e?.message || 'Unknown error'},\n });\n }\n}\n\n// ############# ROUTES_FLOW #############\n\n/** Builds or retrieves a cached merged execution chain for routesFlow requests */\nexport function getRoutesFlowExecutionChain(\n rawRequest: unknown,\n opts: RouterOptions,\n urlQuery?: string\n): RoutesFlowExecutionResult {\n // Validate urlQuery is provided\n if (!urlQuery) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-missing-query',\n publicMessage: 'RoutesFlow request requires a query string with route paths.',\n });\n }\n\n // Decode base64+JSON query\n const query = decodeRoutesFlowQuery(urlQuery);\n const routePaths = query.routes;\n const mappings = query.mappings;\n\n if (!routePaths || routePaths.length === 0) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-empty-routes',\n publicMessage: 'RoutesFlow request requires at least one route path in query string.',\n });\n }\n\n // Convert paths to route IDs (remove leading slash)\n const routeIds = routePaths.map((path) => (path.startsWith('/') ? path.slice(1) : path));\n\n // Check cache first\n let executionChain = routesFlowCache.get(urlQuery);\n if (executionChain) return {executionChain, routesFlowRouteIds: routeIds, mappings};\n\n // Build merged execution chain\n executionChain = buildMergedExecutionChain(routePaths, rawRequest, opts, mappings);\n addToRoutesFlowCache(urlQuery, executionChain);\n return {executionChain, routesFlowRouteIds: routeIds, mappings};\n}\n\n/**\n * Builds a merged execution chain from multiple route paths.\n * The merged chain includes all methods from all routes, with deduplication by ID.\n *\n * The chain is structured as:\n * 1. Start middleFns (e.g., mionDeserializeRequest) - from first route, at the beginning\n * 2. Middle methods (routes and their middleFns) - merged from all routes, with mapping steps inserted\n * 3. End middleFns (e.g., mionSerializeResponse) - from first route, at the end\n *\n * When mappings are provided, mapping steps are inserted after the source route\n * and before the target route to transform output → input.\n */\nfunction buildMergedExecutionChain(\n routePaths: string[],\n rawRequest: unknown,\n opts: RouterOptions,\n mappings?: RoutesFlowMapping[]\n): MethodsExecutionChain {\n const seenIds = new Set<string>();\n const middleMethods: RemoteMethod[] = [];\n let resolvedSerializer: SerializerCode | undefined;\n let firstRouteIndex = -1;\n const defaultSerializerCode = SerializerModes[opts.serializer];\n\n // Build sets of start and end middleFn IDs for filtering\n const startMiddleFnIds = new Set(startMiddleFns.map((m) => m.id));\n const endMiddleFnIds = new Set(endMiddleFns.map((m) => m.id));\n\n // Process each route path\n for (const routePath of routePaths) {\n // Apply path transform if configured\n const transformedPath = opts.pathTransform?.(rawRequest, routePath) || routePath;\n const chain = getRouteExecutionChain(transformedPath);\n if (!chain) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-route-not-found',\n publicMessage: `Route not found in routesFlow: ${routePath}`,\n errorData: {routePath},\n });\n }\n\n // Resolve serializer - use first route's serializer, or fall back to default if conflicting\n if (!resolvedSerializer) {\n resolvedSerializer = chain.serializer;\n // Track the route index from the first route (relative to start middleFns)\n firstRouteIndex = chain.routeIndex;\n } else if (resolvedSerializer !== chain.serializer) {\n resolvedSerializer = defaultSerializerCode;\n }\n\n // Add middle methods from this route's chain, deduplicating by ID\n // Skip start and end middleFns - they will be added separately\n for (const method of chain.methods) {\n if (seenIds.has(method.id)) continue;\n if (startMiddleFnIds.has(method.id)) continue;\n if (endMiddleFnIds.has(method.id)) continue;\n seenIds.add(method.id);\n middleMethods.push(method);\n }\n }\n\n // Insert mapping methods between source and target routes\n if (mappings && mappings.length > 0) {\n insertMappingMethods(middleMethods, mappings);\n }\n\n // Build final chain: start middleFns + middle methods + end middleFns\n const mergedMethods = [...startMiddleFns, ...middleMethods, ...endMiddleFns];\n\n return {\n // Use the first route's routeIndex since that's where the first route handler is\n routeIndex: firstRouteIndex,\n methods: mergedMethods,\n serializer: resolvedSerializer ?? defaultSerializerCode,\n };\n}\n\n// ############# MAPPING METHODS #############\n\n/**\n * Inserts mapping methods into the middleMethods array in the correct position.\n * Each mapping method is inserted after the source route (fromId) and before the target route (toId).\n * Mappings are processed in reverse insertion order to maintain correct indices.\n */\nfunction insertMappingMethods(middleMethods: RemoteMethod[], mappings: RoutesFlowMapping[]): void {\n // Build a map of route ID → index in middleMethods for quick lookup\n const idToIndex = new Map<string, number>();\n for (let i = 0; i < middleMethods.length; i++) {\n idToIndex.set(middleMethods[i].id, i);\n }\n\n // Collect insertions: each mapping creates one insertion point\n const insertions: Array<{index: number; method: RemoteMethod}> = [];\n\n for (const mapping of mappings) {\n const fromIndex = idToIndex.get(mapping.fromId);\n const toIndex = idToIndex.get(mapping.toId);\n\n if (fromIndex === undefined) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-mapping-invalid-source',\n publicMessage: `Mapping source route '${mapping.fromId}' not found in routesFlow execution chain.`,\n errorData: {mapping},\n });\n }\n if (toIndex === undefined) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-mapping-invalid-target',\n publicMessage: `Mapping target route '${mapping.toId}' not found in routesFlow execution chain.`,\n errorData: {mapping},\n });\n }\n\n // Validate the pure function exists in the serverPureFnsCache (populated by mion vite plugin)\n if (!serverPureFnsCache[PURE_SERVER_FN_NAMESPACE]?.[mapping.bodyHash]?.fn) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-mapping-missing-pure-fn',\n publicMessage: `Mapping pure function '${mapping.bodyHash}' not found. Ensure the function is registered on the server.`,\n errorData: {mapping},\n });\n }\n\n // Insert after the source route (fromIndex + 1)\n insertions.push({\n index: fromIndex + 1,\n method: createMappingMethod(mapping),\n });\n }\n\n // Sort insertions by index descending so splice doesn't shift subsequent indices\n insertions.sort((a, b) => b.index - a.index);\n\n for (const {index, method} of insertions) {\n middleMethods.splice(index, 0, method);\n }\n}\n\n/** Creates or retrieves a cached RemoteMethod that acts as a raw middleFn to execute a mapping between routes */\nfunction createMappingMethod(mapping: RoutesFlowMapping): RemoteMethod {\n const id = `mionMapFrom_${mapping.fromId}_${mapping.bodyHash}_to_${mapping.toId}`;\n const cached = mappingMethodCache.get(id);\n if (cached) return cached;\n\n const noopJitFns = getNoopJitFns();\n const method = {\n type: HandlerType.rawMiddleFn,\n id,\n isAsync: false,\n hasReturnData: false,\n paramsJitHash: '',\n returnJitHash: '',\n paramsJitFns: noopJitFns,\n returnJitFns: noopJitFns,\n handler: createMappingHandler(mapping),\n options: {runOnError: false, validateParams: false},\n methodCaller: runMappingHandler,\n } as RemoteMethod;\n\n mappingMethodCache.set(id, method);\n return method;\n}\n\n/** Creates the handler function for a mapping step */\nfunction createMappingHandler(mapping: RoutesFlowMapping) {\n return (ctx: CallContext) => {\n // Get the output from the source route\n const sourceOutput = ctx.response.body[mapping.fromId];\n\n // Resolve and execute the pure function from serverPureFnsCache (populated by mion vite plugin)\n const entry = serverPureFnsCache[PURE_SERVER_FN_NAMESPACE]?.[mapping.bodyHash];\n if (!entry?.fn) {\n throw new RpcError({\n statusCode: StatusCodes.UNEXPECTED_ERROR,\n type: 'routesFlow-mapping-missing-pure-fn',\n publicMessage: `Mapping pure function '${mapping.bodyHash}' not found at runtime.`,\n });\n }\n const mappedValue = entry.fn(sourceOutput);\n\n // Replace null at paramIndex in target route's params\n const targetParams = ctx.request.body[mapping.toId] as any[];\n if (targetParams) (targetParams as any[])[mapping.paramIndex] = mappedValue;\n };\n}\n\n/** Custom method caller for mapping handlers — only passes the context */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nasync function runMappingHandler(context: CallContext, executable: RemoteMethod, ...args: unknown[]) {\n return executable.handler(context);\n}\n"],"names":["__ΩMethodsExecutionChain","__ΩRemoteMethod"],"mappings":"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"}
|