@mionjs/router 0.8.7 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,108 @@
1
+ /* ########
2
+ * 2023 mion
3
+ * Author: Ma-jerez
4
+ * License: MIT
5
+ * The software is provided "as is", without warranty of any kind.
6
+ * ######## */
7
+
8
+ import {AnyObject, Mutable, RpcError, MION_ROUTES, SerializableMethodsData, SerializerModes} from '@mionjs/core';
9
+ import {
10
+ getMiddleFnExecutable,
11
+ getRouteExecutable,
12
+ isPrivateExecutable,
13
+ getRouterOptions,
14
+ getTotalExecutables,
15
+ getAllExecutablesIds,
16
+ getAnyExecutable,
17
+ } from '../router.ts';
18
+ import {middleFn, route} from '../lib/handlers.ts';
19
+ import {RouterOptions, Routes} from '../types/general.ts';
20
+ import {MiddleFnsCollection} from '../types/publicMethods.ts';
21
+ import {getSerializableMethod, serializeMethodDeps} from '../lib/remoteMethods.ts';
22
+ import {RemoteMethod} from '../types/remoteMethods.ts';
23
+ import {CallContext, MionResponse} from '../types/context.ts';
24
+
25
+ export interface ClientRouteOptions extends RouterOptions {
26
+ getAllRemoteMethodsMaxNumber?: number;
27
+ }
28
+
29
+ export const defaultClientRouteOptions = {
30
+ getAllRemoteMethodsMaxNumber: 100,
31
+ };
32
+
33
+ // Internal mion routes that should not be exposed to clients
34
+ const mionInternalRoutes = Object.values(MION_ROUTES) as string[];
35
+
36
+ /**
37
+ * Returns the metadata for the given method ids.
38
+ * If getAllRemoteMethods is true, all public methods and middleFns are returned.
39
+ * @mion:route
40
+ */
41
+ function mionGetRemoteMethodsDataById(
42
+ ctx,
43
+ methodsIds: string[],
44
+ getAllRemoteMethods?: boolean
45
+ ): SerializableMethodsData | RpcError<'rpc-metadata-not-found'> {
46
+ const resp: SerializableMethodsData = {
47
+ methods: {},
48
+ deps: {},
49
+ purFnDeps: {},
50
+ };
51
+ const errorData = {};
52
+ const maxMethods =
53
+ getRouterOptions<ClientRouteOptions>().getAllRemoteMethodsMaxNumber ||
54
+ defaultClientRouteOptions.getAllRemoteMethodsMaxNumber;
55
+ const shouldReturnAll = getAllRemoteMethods && getTotalExecutables() <= maxMethods;
56
+ const idsToReturn = shouldReturnAll
57
+ ? getAllExecutablesIds().filter(
58
+ (id) => !mionInternalRoutes.includes(id) && !isPrivateExecutable(getAnyExecutable(id) as RemoteMethod)
59
+ )
60
+ : methodsIds;
61
+ idsToReturn.forEach((id) => addRequiredRemoteMethodsToResponse(id, resp, errorData));
62
+
63
+ if (Object.keys(errorData).length)
64
+ return new RpcError({
65
+ type: 'rpc-metadata-not-found',
66
+ publicMessage: 'Errors getting Remote Methods Metadata',
67
+ errorData,
68
+ });
69
+ return resp;
70
+ }
71
+
72
+ /** Middleware wrapper: delegates to mionGetRemoteMethodsDataById when params are provided */
73
+ function mionMethodsMetadata(
74
+ ctx: CallContext,
75
+ methodsIds?: string[],
76
+ getAllRemoteMethods?: boolean
77
+ ): SerializableMethodsData | RpcError<'rpc-metadata-not-found'> | void {
78
+ if (!methodsIds || methodsIds.length === 0) return;
79
+ // Force JSON serialization so optimistic client can parse the response
80
+ (ctx.response as Mutable<MionResponse>).serializer = SerializerModes.stringifyJson;
81
+ return mionGetRemoteMethodsDataById(ctx, methodsIds, getAllRemoteMethods);
82
+ }
83
+
84
+ function addRequiredRemoteMethodsToResponse(id: string, resp: SerializableMethodsData, errorData: AnyObject): void {
85
+ const {methods, deps, purFnDeps} = resp;
86
+ if (methods[id]) return;
87
+ if (mionInternalRoutes.includes(id)) return;
88
+ const executable = getMiddleFnExecutable(id) || getRouteExecutable(id);
89
+ if (!executable) {
90
+ errorData[id] = `Remote Method ${id} not found`;
91
+ return;
92
+ }
93
+ if (isPrivateExecutable(executable)) return;
94
+ const method = getSerializableMethod(executable as RemoteMethod);
95
+ methods[id] = method;
96
+ method.middleFnIds?.forEach((middleFnId) => addRequiredRemoteMethodsToResponse(middleFnId, resp, errorData));
97
+ serializeMethodDeps(method, deps, purFnDeps);
98
+ }
99
+
100
+ export const mionClientMiddleFns = {
101
+ [MION_ROUTES.methodsMetadata]: middleFn(mionMethodsMetadata, {runOnError: true}),
102
+ } as const satisfies MiddleFnsCollection;
103
+
104
+ export const mionClientRoutes = {
105
+ // Client routes always use stringifyJson serialization to avoid mutating data as is cached
106
+ // These routes are used by the client to fetch metadata and must work regardless of router's default serialization
107
+ [MION_ROUTES.methodsMetadataById]: route(mionGetRemoteMethodsDataById, {serializer: 'stringifyJson'}),
108
+ } as const satisfies Routes;
@@ -0,0 +1,51 @@
1
+ /* ########
2
+ * 2023 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 {Routes} from '../types/general.ts';
9
+ import type {CallContext} from '../types/context.ts';
10
+ import {RpcError, MION_ROUTES, StatusCodes} from '@mionjs/core';
11
+ import {route} from '../lib/handlers.ts';
12
+
13
+ export const mionErrorsRoutes = {
14
+ /**
15
+ * !IMPORTANT!
16
+ * This is declared as route mostly to reuse existing router serialization/deserialization functionality.
17
+ * But "@thrownErrors" is expected to be a field in response body that contain all thrown errors from other executables.
18
+ * thrown Errors are not strongly typed and are all serialized/deserialized as RpcError<string>.
19
+ * this also prevents users to register a route with the same name.
20
+ */
21
+ [MION_ROUTES.thrownErrors]: route((ctx: CallContext): Record<string, RpcError<string>> => {
22
+ return ctx.request.thrownErrors || {};
23
+ }),
24
+ /**
25
+ * Route that handles not-found scenarios when a requested route doesn't exist.
26
+ * This route is registered as an internal mion route.
27
+ * The route is called by dispatch logic when no matching route is found.
28
+ * Throws an RpcError that will be caught and stored in thrownErrors by the router.
29
+ */
30
+ [MION_ROUTES.notFound]: route((ctx: CallContext): RpcError<'route-not-found'> => {
31
+ throw new RpcError({
32
+ statusCode: StatusCodes.NOT_FOUND,
33
+ publicMessage: `Route not found`,
34
+ type: 'route-not-found',
35
+ });
36
+ }),
37
+ /**
38
+ * Platform error route for strongly typing platform/adapter errors.
39
+ * Platform errors occur before reaching the router or outside the router
40
+ * and are platform/adapter related (e.g., HTTP server errors, connection issues).
41
+ * This route is used for serialization/deserialization of platform errors.
42
+ * This also prevents users to register a route with the same name.
43
+ */
44
+ [MION_ROUTES.platformError]: route((_ctx: CallContext): RpcError<string> => {
45
+ // Platform errors are passed through context, this route is for type serialization
46
+ return new RpcError({
47
+ publicMessage: 'Platform error',
48
+ type: 'platform-error',
49
+ });
50
+ }),
51
+ } as const satisfies Routes;
@@ -0,0 +1,11 @@
1
+ import {Routes} from '../types/general.ts';
2
+ import {PublicApi} from '../types/publicMethods.ts';
3
+ import {mionClientRoutes} from './client.routes.ts';
4
+ import {mionErrorsRoutes} from './errors.routes.ts';
5
+
6
+ export const mionRoutes = {
7
+ ...mionClientRoutes,
8
+ ...mionErrorsRoutes,
9
+ } as const satisfies Routes;
10
+
11
+ export type MionRoutes = PublicApi<typeof mionRoutes>;
@@ -0,0 +1,225 @@
1
+ /* ########
2
+ * 2023 mion
3
+ * Author: Ma-jerez
4
+ * License: MIT
5
+ * The software is provided "as is", without warranty of any kind.
6
+ * ######## */
7
+
8
+ import {MionResponse, MionRequest, CallContext, ResponseBody} from '../types/context.ts';
9
+ import {RouterOptions} from '../types/general.ts';
10
+ import {MiddleFnsCollection, MayReturnError} from '../types/publicMethods.ts';
11
+ import {
12
+ AnyObject,
13
+ Mutable,
14
+ MION_ROUTES,
15
+ StatusCodes,
16
+ serializeBinaryBody as coreSerializeBinaryBody,
17
+ deserializeBinaryBody as coreDeserializeBinaryBody,
18
+ SerializerModes,
19
+ } from '@mionjs/core';
20
+ import {rawMiddleFn} from '../lib/handlers.ts';
21
+ import {getRouteExecutableFromPath, getRouteExecutable} from '../router.ts';
22
+ import {RpcError} from '@mionjs/core';
23
+ import {RemoteMethod} from '../types/remoteMethods.ts';
24
+ import {onExecutableError} from '../lib/dispatchError.ts';
25
+
26
+ // ############# PUBLIC METHODS #############
27
+
28
+ /**
29
+ * Deserializes the request body and stores it in the request body property.
30
+ * This method is called before any other middleFn or route handler.
31
+ * For binary requests, the body is parsed lazily per-method in dispatch.ts.
32
+ * @mion:middleFn
33
+ */
34
+ export function deserializeRequestBody(context: CallContext): MayReturnError {
35
+ if (!context.request.rawBody) return; // empty body
36
+ let parsedBody: any;
37
+ switch (context.request.bodyType) {
38
+ case SerializerModes.stringifyJson: // jit stringify json
39
+ try {
40
+ parsedBody = JSON.parse(context.request.rawBody as string);
41
+ } catch (err: any) {
42
+ throw new RpcError({
43
+ statusCode: StatusCodes.UNEXPECTED_ERROR,
44
+ type: 'parsing-json-request-error',
45
+ publicMessage: `Invalid json request body: ${err?.message || 'unknown parsing error.'}`,
46
+ });
47
+ }
48
+ break;
49
+ case SerializerModes.binary: {
50
+ // binary
51
+ const rawBody = context.request.rawBody as Uint8Array;
52
+ const {body} = coreDeserializeBinaryBody(context.path, rawBody, false);
53
+ parsedBody = body;
54
+ break;
55
+ }
56
+ case SerializerModes.json: // Object (pre-parsed body from platforms like Google Cloud Functions where Express auto-parses JSON)
57
+ parsedBody = context.request.rawBody;
58
+ break;
59
+ default:
60
+ throw new Error(`Invalid body type ${context.request.bodyType}`);
61
+ }
62
+ if (parsedBody) {
63
+ if (Array.isArray(parsedBody)) {
64
+ // when the body is an array we assume it's a single route call and we have to reconstruct the body
65
+ // http://my-api.com/route1 [p1, p2, p3] => {route1: [p1, p2, p3]}
66
+ parsedBody = {[getRouteExecutableFromPath(context.path).id]: parsedBody};
67
+ }
68
+ if (typeof parsedBody !== 'object')
69
+ throw new RpcError({
70
+ statusCode: StatusCodes.UNEXPECTED_ERROR,
71
+ type: 'invalid-request-body',
72
+ publicMessage: 'Wrong request body. Expecting a body containing the route name and parameters.',
73
+ });
74
+ (context.request as Mutable<MionRequest>).body = parsedBody;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Serializes the response body and stores it in the response rawBody property.
80
+ * This method is called after any other middleFn or route handler.
81
+ * @mion:middleFn
82
+ */
83
+ export function serializeResponseBody(context: CallContext, opts: RouterOptions): MayReturnError {
84
+ const response = context.response as Mutable<MionResponse>;
85
+ const respBody: AnyObject = response.body;
86
+ const bodyType = context.response.serializer;
87
+ const thrownErrors = context.request.thrownErrors as Record<string, RpcError<string>> | undefined;
88
+ // Add thrownErrors to response body before the serializer runs
89
+ if (thrownErrors) (response.body as Mutable<AnyObject>)['@thrownErrors'] = thrownErrors;
90
+ switch (bodyType) {
91
+ case SerializerModes.stringifyJson: {
92
+ // json - use stringifyJson JIT function
93
+ response.headers.set('content-type', 'application/json; charset=utf-8');
94
+ const body = stringifyBody(context, context.executionChain.methods, respBody);
95
+ response.rawBody = body;
96
+ break;
97
+ }
98
+ case SerializerModes.json: {
99
+ // pre-serialized object - only prepare for JSON, don't stringify
100
+ // Platform adapters will handle the actual JSON stringification
101
+ // prepareForJson mutates response.body in place, so we don't set rawBody
102
+ response.headers.set('content-type', 'application/json; charset=utf-8');
103
+ prepareBodyForJson(context, context.executionChain.methods, respBody);
104
+ break;
105
+ }
106
+ case SerializerModes.binary: {
107
+ // binary - use toBinary JIT function
108
+ response.headers.set('content-type', 'application/octet-stream');
109
+ serializeBinaryBody(context, context.executionChain.methods, respBody);
110
+ break;
111
+ }
112
+ default:
113
+ throw new Error(`Invalid body type ${context.request.bodyType}`);
114
+ }
115
+ }
116
+
117
+ /** Serializes response body to binary format using the core serializeBinaryBody function */
118
+ function serializeBinaryBody(context: CallContext, executionChain: RemoteMethod[], respBody: ResponseBody): void {
119
+ const response = context.response as Mutable<MionResponse>;
120
+ // For routesFlow, use routesFlowRouteIds from context for proper buffer sizing
121
+ const {serializer, buffer} = coreSerializeBinaryBody(
122
+ context.path,
123
+ executionChain,
124
+ respBody,
125
+ true,
126
+ context.routesFlowRouteIds
127
+ );
128
+ response.binSerializer = serializer;
129
+ response.rawBody = new Uint8Array(buffer);
130
+ }
131
+
132
+ function stringifyBody(context: CallContext, executionChain: RemoteMethod[], respBody: ResponseBody): string {
133
+ const props: string[] = [];
134
+ for (let i = 0; i < executionChain.length; i++) {
135
+ const method = executionChain[i];
136
+ const returnValue = respBody[method.id];
137
+ if (!method.hasReturnData || typeof returnValue === 'undefined') continue;
138
+ try {
139
+ const jsonValue = stringifyHandlerReturnValue(method, returnValue);
140
+ if (!jsonValue) continue;
141
+ props.push(`${JSON.stringify(method.id)}:${jsonValue}`);
142
+ } catch (e: any) {
143
+ onStringifyExecutableError(context, method, e);
144
+ }
145
+ }
146
+
147
+ // Serialize thrownErrors if they exist
148
+ const thrownErrors = respBody['@thrownErrors'];
149
+ if (thrownErrors) {
150
+ const method = getRouteExecutable(MION_ROUTES.thrownErrors)!;
151
+ try {
152
+ const jsonValue = stringifyHandlerReturnValue(method, thrownErrors);
153
+ if (jsonValue) props.push(`${JSON.stringify(method.id)}:${jsonValue}`);
154
+ } catch (e: any) {
155
+ onStringifyExecutableError(context, method, e);
156
+ }
157
+ }
158
+ return `{${props.join(',')}}`;
159
+ }
160
+
161
+ function onStringifyExecutableError(context: CallContext, method: RemoteMethod, e: any) {
162
+ const err = new RpcError({
163
+ statusCode: StatusCodes.UNEXPECTED_ERROR,
164
+ type: 'json-stringify-response-error',
165
+ publicMessage: `Failed to stringify return value for handler ${method.id}, expected response type: ${method.returnJitFns.stringifyJson.typeName}`,
166
+ originalError: e,
167
+ errorData: {methodId: method.id},
168
+ });
169
+ onExecutableError(context, method, err);
170
+ }
171
+
172
+ function stringifyHandlerReturnValue(method: RemoteMethod, returnValue: any): string {
173
+ if (!method.hasReturnData) return '';
174
+ // id data does not require custom encoding then we use native json
175
+ if (method.returnJitFns.prepareForJson.isNoop) JSON.stringify(returnValue);
176
+ return method.returnJitFns.stringifyJson.fn(returnValue);
177
+ }
178
+
179
+ function prepareBodyForJson(context: CallContext, executionChain: RemoteMethod[], respBody: ResponseBody): void {
180
+ // prepareForJson mutates the response body in place
181
+ for (let i = 0; i < executionChain.length; i++) {
182
+ const method = executionChain[i];
183
+ const returnValue = respBody[method.id];
184
+ if (!method.hasReturnData || typeof returnValue === 'undefined') continue;
185
+ try {
186
+ const preparedValue = prepareHandlerReturnValue(method, returnValue);
187
+ if (preparedValue !== undefined) (respBody as Mutable<ResponseBody>)[method.id] = preparedValue;
188
+ } catch (e: any) {
189
+ onPrepareForJsonExecutableError(context, method, e);
190
+ }
191
+ }
192
+ // Prepare thrownErrors if they exist
193
+ const thrownErrors = respBody['@thrownErrors'];
194
+ if (thrownErrors) {
195
+ const method = getRouteExecutable(MION_ROUTES.thrownErrors)!;
196
+ try {
197
+ const preparedValue = prepareHandlerReturnValue(method, thrownErrors);
198
+ if (preparedValue !== undefined) (respBody as Mutable<ResponseBody>)[method.id] = preparedValue;
199
+ } catch (e: any) {
200
+ onPrepareForJsonExecutableError(context, method, e);
201
+ }
202
+ }
203
+ }
204
+
205
+ function onPrepareForJsonExecutableError(context: CallContext, method: RemoteMethod, e: any) {
206
+ const err = new RpcError({
207
+ statusCode: StatusCodes.UNEXPECTED_ERROR,
208
+ type: 'prepare-for-json-response-error',
209
+ publicMessage: `Failed to prepare return value for JSON for handler ${method.id}, expected response type: ${method.returnJitFns.prepareForJson.typeName}`,
210
+ originalError: e,
211
+ errorData: {methodId: method.id},
212
+ });
213
+ onExecutableError(context, method, err);
214
+ }
215
+
216
+ function prepareHandlerReturnValue(method: RemoteMethod, returnValue: any): any {
217
+ if (!method.hasReturnData) return undefined;
218
+ if (method.returnJitFns.prepareForJson.isNoop) return returnValue;
219
+ return method.returnJitFns.prepareForJson.fn(returnValue);
220
+ }
221
+
222
+ export const serializerMiddleFns = {
223
+ mionDeserializeRequest: rawMiddleFn(deserializeRequestBody, {runOnError: true}),
224
+ mionSerializeResponse: rawMiddleFn(serializeResponseBody, {runOnError: true}),
225
+ } satisfies MiddleFnsCollection;