@thednp/rpc 0.0.13 → 0.1.1
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/AGENTS.md +16 -14
- package/CHANGELOG.md +276 -0
- package/README.md +27 -8
- package/dist/express/express.d.mts.map +1 -1
- package/dist/express/express.mjs +55 -9
- package/dist/express/express.mjs.map +1 -1
- package/dist/fastify/fastify.d.mts +4 -1
- package/dist/fastify/fastify.d.mts.map +1 -1
- package/dist/fastify/fastify.mjs +51 -7
- package/dist/fastify/fastify.mjs.map +1 -1
- package/dist/fastify/plugin/fastify/plugin.d.mts +14 -0
- package/dist/fastify/plugin/fastify/plugin.d.mts.map +1 -1
- package/dist/fastify/plugin/fastify/plugin.mjs +51 -7
- package/dist/fastify/plugin/fastify/plugin.mjs.map +1 -1
- package/dist/helpers/helpers.d.mts +2 -2
- package/dist/helpers/helpers.d.mts.map +1 -1
- package/dist/helpers/helpers.mjs +1 -1
- package/dist/helpers/helpers.mjs.map +1 -1
- package/dist/hono/hono.d.mts +4 -1
- package/dist/hono/hono.d.mts.map +1 -1
- package/dist/hono/hono.mjs +51 -7
- package/dist/hono/hono.mjs.map +1 -1
- package/dist/index.d.mts +25 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +257 -226
- package/dist/index.mjs.map +1 -1
- package/dist/koa/koa.d.mts.map +1 -1
- package/dist/koa/koa.mjs +51 -7
- package/dist/koa/koa.mjs.map +1 -1
- package/dist/server/server.d.mts +58 -8
- package/dist/server/server.d.mts.map +1 -1
- package/dist/server/server.mjs +124 -29
- package/dist/server/server.mjs.map +1 -1
- package/package.json +11 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.mjs","names":[],"sources":["../../src/constants.ts","../../src/helpers.ts"],"sourcesContent":["export const OPERATION_ABORTED = \"Operation aborted\";\n\nexport const REQUEST_CANCELLED = \"Request was cancelled\";\n\nexport const FETCH_ERROR_PREFIX = \"Fetch error: \";\n\nexport const NO_SERVER_FUNCTION_FOUND = \"No server function found.\";\n\nexport const ERROR_LOADING_FILE = \"Error loading file:\";\n\nexport const FUNCTION_NOT_FOUND = \"Function not found\";\n\nexport const METHOD_NOT_ALLOWED = \"Method Not Allowed\";\n\nexport const REQUEST_FORBIDDEN = \"Forbidden\";\n\nexport const INTERNAL_SERVER_ERROR = \"Internal Server Error\";\n\nexport const CLIENT_DISCONNECTED = \"client disconnected\";\n\n/** Returns a warning when a middleware name is reused, preventing registration conflicts. @param name - The duplicate middleware name */\nexport const MIDDLEWARE_NAME_USED = (name: string) =>\n `The middleware name \"${name}\" is already used.`;\n\n/** Error message when a value fails the safe-identifier validation. @param label - What kind of value was being validated. @param name - The rejected value */\nexport const INVALID_IDENTIFIER = (label: string, name: string) =>\n `Invalid ${label}: \"${name}\" must match /^[A-Za-z_$][A-Za-z0-9_$]*$/`;\n\n/** Error message when a value fails the safe-path-segment validation. @param label - What kind of value was being validated. @param segment - The rejected value */\nexport const INVALID_PATH_SEGMENT = (label: string, segment: string) =>\n `Invalid ${label}: \"${segment}\" must match /^[A-Za-z0-9_$][A-Za-z0-9_$/-]*$/`;\n\n/** Warning message when a specified RPC config file cannot be resolved on disk. @param configFile - The requested config filename. @param configFilePath - The resolved absolute path */\nexport const CONFIG_FILE_NOT_FOUND = (\n configFile: string,\n configFilePath: string,\n) =>\n ` ⚠︎ The specified RPC config file ${configFile} cannot be found at ${configFilePath}, loading the defaults..`;\n\nexport const NO_CONFIG_FOUND = ` ⚡︎ No RPC config found, loading the defaults..`;\n\nexport const FAILED_LOAD_CONFIG = ` ⚠︎ Failed to load RPC config:`;\n","/** @module Client-side helper utilities. Exports `handleResponse` for processing fetch responses and `innerModule` for creating AbortController-bound RPC fetch calls. */\nimport type { Credentials, InnerModReturn, JsonValue } from \"./types.d.ts\";\nimport { FETCH_ERROR_PREFIX, REQUEST_CANCELLED } from \"./constants.ts\";\n\n/**\n * Processes an HTTP fetch response from the RPC server.\n * On HTTP 499 or 408 (client cancellation), logs a warning and returns undefined.\n * On other error statuses, throws a Fetch error.\n * On success, parses JSON and returns `result.data` — or throws if `result.error` is set.\n * @param response - Fetch Response object from the RPC endpoint\n * @returns The response data, or void on cancellation\n */\nexport const handleResponse = async (\n response: Response,\n): Promise<JsonValue | void> => {\n if (!response.ok) {\n if (response.status === 499 || response.status === 408) {\n return console.warn(REQUEST_CANCELLED);\n }\n throw new Error(FETCH_ERROR_PREFIX + response.statusText);\n }\n const result = await response.json();\n if (result.error) throw new Error(result.error);\n return result.data;\n};\n\n/**\n * Creates an AbortController-bound fetch call for a single RPC function.\n * Used by the auto-generated client modules to issue HTTP requests with cancellation support.\n * GET requests carry arguments as an `?args=` JSON query parameter, since a fetch\n * request body is not allowed on GET.\n * @param body - Serialized request body (JSON string or raw text)\n * @param headers - HTTP headers (Content-Type, etc.)\n * @param credentials - Fetch credentials policy (\"same-origin\", \"include\", or \"omit\")\n * @param prefix - RPC endpoint prefix (e.g. \"__rpc\")\n * @param name - Registered server function name\n * @param method - HTTP method to use, \"POST\" by default\n * @returns An object with `data` (promise resolving to the server response) and `cancel` (abort function)\n */\nexport const innerModule = (\n body: BodyInit,\n headers: HeadersInit,\n credentials: Credentials,\n prefix: string,\n name: string,\n method?: \"GET\" | \"POST\",\n): InnerModReturn => {\n const controller = new AbortController();\n const cancel = (reason: string) => controller.abort(reason);\n\n const fetcher = async () => {\n try {\n const isGet = method === \"GET\";\n const url = isGet\n ? `/${prefix}/${name}?args=${encodeURIComponent(String(body))}`\n : `/${prefix}/${name}`;\n const response = await fetch(url, {\n method: isGet ? \"GET\" : \"POST\",\n headers,\n credentials,\n body: isGet ? undefined : body,\n signal: controller.signal,\n });\n return await handleResponse(response);\n } catch (err) {\n throw err;\n }\n };\n\n return {\n data: fetcher(),\n cancel,\n };\n};\n"],"mappings":";AAEA,MAAa,oBAAoB;AAEjC,MAAa,qBAAqB;;;;;;;;;;;ACQlC,MAAa,iBAAiB,OAC5B,aAC8B;CAC9B,IAAI,CAAC,SAAS,IAAI;EAChB,IAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KACjD,OAAO,QAAQ,KAAK,iBAAiB;EAEvC,MAAM,IAAI,MAAM,qBAAqB,SAAS,UAAU;CAC1D;CACA,MAAM,SAAS,MAAM,SAAS,KAAK;CACnC,IAAI,OAAO,OAAO,MAAM,IAAI,MAAM,OAAO,KAAK;CAC9C,OAAO,OAAO;AAChB;;;;;;;;;;;;;;AAeA,MAAa,eACX,MACA,SACA,aACA,QACA,MACA,WACmB;CACnB,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,WAAmB,WAAW,MAAM,MAAM;CAE1D,MAAM,UAAU,YAAY;EAC1B,IAAI;GACF,MAAM,QAAQ,WAAW;GACzB,MAAM,MAAM,QACR,IAAI,OAAO,GAAG,KAAK,QAAQ,mBAAmB,OAAO,IAAI,CAAC,MAC1D,IAAI,OAAO,GAAG;GAClB,MAAM,WAAW,MAAM,MAAM,KAAK;IAChC,QAAQ,QAAQ,QAAQ;IACxB;IACA;IACA,MAAM,QAAQ,KAAA,IAAY;IAC1B,QAAQ,WAAW;GACrB,CAAC;GACD,OAAO,MAAM,eAAe,QAAQ;EACtC,SAAS,KAAK;GACZ,MAAM;EACR;CACF;CAEA,OAAO;EACL,MAAM,QAAQ;EACd;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"helpers.mjs","names":[],"sources":["../../src/constants.ts","../../src/client-helpers.ts"],"sourcesContent":["export const OPERATION_ABORTED = \"Operation aborted\";\n\nexport const REQUEST_CANCELLED = \"Request was cancelled\";\n\nexport const FETCH_ERROR_PREFIX = \"Fetch error: \";\n\nexport const NO_SERVER_FUNCTION_FOUND = \"No server function found.\";\n\nexport const ERROR_LOADING_FILE = \"Error loading file:\";\n\nexport const FUNCTION_NOT_FOUND = \"Function not found\";\n\nexport const METHOD_NOT_ALLOWED = \"Method Not Allowed\";\n\nexport const REQUEST_FORBIDDEN = \"Forbidden\";\n\nexport const INTERNAL_SERVER_ERROR = \"Internal Server Error\";\n\nexport const CLIENT_DISCONNECTED = \"client disconnected\";\n\n/** Returns a warning when a middleware name is reused, preventing registration conflicts. @param name - The duplicate middleware name */\nexport const MIDDLEWARE_NAME_USED = (name: string) =>\n `The middleware name \"${name}\" is already used.`;\n\n/** Error message when a value fails the safe-identifier validation. @param label - What kind of value was being validated. @param name - The rejected value */\nexport const INVALID_IDENTIFIER = (label: string, name: string) =>\n `Invalid ${label}: \"${name}\" must match /^[A-Za-z_$][A-Za-z0-9_$]*$/`;\n\n/** Error message when a value fails the safe-path-segment validation. @param label - What kind of value was being validated. @param segment - The rejected value */\nexport const INVALID_PATH_SEGMENT = (label: string, segment: string) =>\n `Invalid ${label}: \"${segment}\" must match /^[A-Za-z0-9_$][A-Za-z0-9_$/-]*$/`;\n\n/** Warning message when a specified RPC config file cannot be resolved on disk. @param configFile - The requested config filename. @param configFilePath - The resolved absolute path */\nexport const CONFIG_FILE_NOT_FOUND = (\n configFile: string,\n configFilePath: string,\n) =>\n ` ⚠︎ The specified RPC config file ${configFile} cannot be found at ${configFilePath}, loading the defaults..`;\n\nexport const NO_CONFIG_FOUND = ` ⚡︎ No RPC config found, loading the defaults..`;\n\nexport const FAILED_LOAD_CONFIG = ` ⚠︎ Failed to load RPC config:`;\n\n/** Error template for duplicate server function names across files. @param name - The duplicate registered name */\nexport const DUPLICATE_FUNCTION_NAME = (name: string) =>\n `Duplicate server function \"${name}\" detected. Each server function must have a unique name. Remove or rename the duplicate.`;\n","/** @module Client-side helper utilities. Exports `handleResponse` for processing fetch responses and `innerModule` for creating AbortController-bound RPC fetch calls. This module is bundled into the generated client modules — keep it free of server-only code. */\nimport type { Credentials, InnerModReturn, JsonValue } from \"./types.d.ts\";\nimport { FETCH_ERROR_PREFIX, REQUEST_CANCELLED } from \"./constants.ts\";\n\n/**\n * Processes an HTTP fetch response from the RPC server.\n * On HTTP 499 or 408 (client cancellation), logs a warning and returns undefined.\n * On other error statuses, throws a Fetch error.\n * On success, parses JSON and returns `result.data` — or throws if `result.error` is set.\n * @param response - Fetch Response object from the RPC endpoint\n * @returns The response data, or void on cancellation\n */\nexport const handleResponse = async (\n response: Response,\n): Promise<JsonValue | void> => {\n if (!response.ok) {\n if (response.status === 499 || response.status === 408) {\n return console.warn(REQUEST_CANCELLED);\n }\n throw new Error(FETCH_ERROR_PREFIX + response.statusText);\n }\n const result = await response.json();\n if (result.error) throw new Error(result.error);\n return result.data;\n};\n\n/**\n * Creates an AbortController-bound fetch call for a single RPC function.\n * Used by the auto-generated client modules to issue HTTP requests with cancellation support.\n * GET requests carry arguments as an `?args=` JSON query parameter, since a fetch\n * request body is not allowed on GET.\n * @param body - Serialized request body (JSON string or raw text)\n * @param headers - HTTP headers (Content-Type, etc.)\n * @param credentials - Fetch credentials policy (\"same-origin\", \"include\", or \"omit\")\n * @param prefix - RPC endpoint prefix (e.g. \"__rpc\")\n * @param name - Registered server function name\n * @param method - HTTP method to use, \"POST\" by default\n * @returns An object with `data` (promise resolving to the server response) and `cancel` (abort function)\n */\nexport const innerModule = (\n body: BodyInit,\n headers: HeadersInit,\n credentials: Credentials,\n prefix: string,\n name: string,\n method?: \"GET\" | \"POST\",\n): InnerModReturn => {\n const controller = new AbortController();\n const cancel = (reason: string) => controller.abort(reason);\n\n const fetcher = async () => {\n try {\n const isGet = method === \"GET\";\n const url = isGet\n ? `/${prefix}/${name}?args=${encodeURIComponent(String(body))}`\n : `/${prefix}/${name}`;\n const response = await fetch(url, {\n method: isGet ? \"GET\" : \"POST\",\n headers,\n credentials,\n body: isGet ? undefined : body,\n signal: controller.signal,\n });\n return await handleResponse(response);\n } catch (err) {\n throw err;\n }\n };\n\n return {\n data: fetcher(),\n cancel,\n };\n};\n"],"mappings":";AAEA,MAAa,oBAAoB;AAEjC,MAAa,qBAAqB;;;;;;;;;;;ACQlC,MAAa,iBAAiB,OAC5B,aAC8B;CAC9B,IAAI,CAAC,SAAS,IAAI;EAChB,IAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KACjD,OAAO,QAAQ,KAAK,iBAAiB;EAEvC,MAAM,IAAI,MAAM,qBAAqB,SAAS,UAAU;CAC1D;CACA,MAAM,SAAS,MAAM,SAAS,KAAK;CACnC,IAAI,OAAO,OAAO,MAAM,IAAI,MAAM,OAAO,KAAK;CAC9C,OAAO,OAAO;AAChB;;;;;;;;;;;;;;AAeA,MAAa,eACX,MACA,SACA,aACA,QACA,MACA,WACmB;CACnB,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,WAAmB,WAAW,MAAM,MAAM;CAE1D,MAAM,UAAU,YAAY;EAC1B,IAAI;GACF,MAAM,QAAQ,WAAW;GACzB,MAAM,MAAM,QACR,IAAI,OAAO,GAAG,KAAK,QAAQ,mBAAmB,OAAO,IAAI,CAAC,MAC1D,IAAI,OAAO,GAAG;GAClB,MAAM,WAAW,MAAM,MAAM,KAAK;IAChC,QAAQ,QAAQ,QAAQ;IACxB;IACA;IACA,MAAM,QAAQ,KAAA,IAAY;IAC1B,QAAQ,WAAW;GACrB,CAAC;GACD,OAAO,MAAM,eAAe,QAAQ;EACtC,SAAS,KAAK;GACZ,MAAM;EACR;CACF;CAEA,OAAO;EACL,MAAM,QAAQ;EACd;CACF;AACF"}
|
package/dist/hono/hono.d.mts
CHANGED
|
@@ -60,6 +60,9 @@ type BodyResult = {
|
|
|
60
60
|
} | {
|
|
61
61
|
contentType: "text/plain";
|
|
62
62
|
data: string;
|
|
63
|
+
} | {
|
|
64
|
+
contentType: "multipart/form-data";
|
|
65
|
+
data: Record<string, unknown>;
|
|
63
66
|
};
|
|
64
67
|
// primitives and their compositions
|
|
65
68
|
/**
|
|
@@ -75,7 +78,7 @@ type JsonObject = {
|
|
|
75
78
|
/**
|
|
76
79
|
* A JSON array of JSON values.
|
|
77
80
|
*/
|
|
78
|
-
type JsonArray = JsonValue[];
|
|
81
|
+
type JsonArray = (FormData | JsonValue)[];
|
|
79
82
|
/**
|
|
80
83
|
* Any JSON-serializable value: primitive, array, or object.
|
|
81
84
|
*/
|
package/dist/hono/hono.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.d.mts","names":[],"sources":["../../src/hono/types.d.ts","../../src/hono/createMiddleware.ts","../../src/types.d.ts","../../src/hono/helpers.ts"],"mappings":";;;;;;;;;;;;;;KAOY,mBAAmB;EAAoB;;;;;KAKvC,wBAAwB;;;;UAKnB;;EAEf,SAAS;;;;;;KAOC,oBAAoB,UAAU,sCACxC,iBAAiB,QAAQ,kBAAkB,QACxC;;;;;;;;;;cCAQ,kBAAkB;;;;;;;;cA0ElB,qBAAqB;;;;;;
|
|
1
|
+
{"version":3,"file":"hono.d.mts","names":[],"sources":["../../src/hono/types.d.ts","../../src/hono/createMiddleware.ts","../../src/types.d.ts","../../src/hono/helpers.ts"],"mappings":";;;;;;;;;;;;;;KAOY,mBAAmB;EAAoB;;;;;KAKvC,wBAAwB;;;;UAKnB;;EAEf,SAAS;;;;;;KAOC,oBAAoB,UAAU,sCACxC,iBAAiB,QAAQ,kBAAkB,QACxC;;;;;;;;;;cCAQ,kBAAkB;;;;;;;;cA0ElB,qBAAqB;;;;;;KC9BtB;EACN;EAAiC,MAAM;;EACvC;EAA2B;;EAC3B;EAAoC,MAAM;;;;;;KA8BpC;;;;KAIA;GAAgB,cAAc,YAAY;;;;;KAI1C,aAAa,WAAW;;;;KAIxB,YAAY,gBAAgB,YAAY;;;;;;;;iBCrG9B,UAAU,KAAK,OAAI;;;;;;;cAgB5B,aAAU,KAAS,MAAI,MAAQ;;;;;;;;;cAY/B,iBAAc,MACnB,kBACL,kBAAkB;EAAmB,UAAU;;;;;;;;cAgDrC,WAAQ,GAChB,YACF,QAAQ"}
|
package/dist/hono/hono.mjs
CHANGED
|
@@ -3,7 +3,9 @@ import { scanForServerFiles, serverFunctionsMap } from "@thednp/rpc/server";
|
|
|
3
3
|
//#region src/options.ts
|
|
4
4
|
const defaultRPCOptions = {
|
|
5
5
|
rpcPrefix: "__rpc",
|
|
6
|
-
adapter: "express"
|
|
6
|
+
adapter: "express",
|
|
7
|
+
serverFiles: "exact",
|
|
8
|
+
scanRoot: void 0
|
|
7
9
|
};
|
|
8
10
|
const defaultMiddlewareOptions = {
|
|
9
11
|
rpcPrefix: void 0,
|
|
@@ -32,6 +34,45 @@ const CLIENT_DISCONNECTED = "client disconnected";
|
|
|
32
34
|
/** Returns a warning when a middleware name is reused, preventing registration conflicts. @param name - The duplicate middleware name */
|
|
33
35
|
const MIDDLEWARE_NAME_USED = (name) => `The middleware name "${name}" is already used.`;
|
|
34
36
|
//#endregion
|
|
37
|
+
//#region src/server-helpers.ts
|
|
38
|
+
/**
|
|
39
|
+
* A typed error thrown from server functions.
|
|
40
|
+
* The middleware serializes the `message` and `code` in the response,
|
|
41
|
+
* allowing clients to recognise and handle specific error conditions.
|
|
42
|
+
*/
|
|
43
|
+
var RPCError = class extends Error {
|
|
44
|
+
/** Machine-readable error code (e.g. "VALIDATION_FAILED", "UNAUTHORIZED") */
|
|
45
|
+
code;
|
|
46
|
+
/** Optional diagnostic payload */
|
|
47
|
+
data;
|
|
48
|
+
constructor(message, code = "INTERNAL", data) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = "RPCError";
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.data = data;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Formats an error for the RPC middleware response.
|
|
57
|
+
* In development the full `RPCError` payload is included so developers
|
|
58
|
+
* can quickly identify issues. Unexpected exceptions never expose their
|
|
59
|
+
* message — only the generic "Internal Server Error" is sent, preventing
|
|
60
|
+
* information disclosure; server-side diagnostics are preserved via the
|
|
61
|
+
* middleware's `console.error` logging.
|
|
62
|
+
*/
|
|
63
|
+
const formatError = (err, isProduction) => {
|
|
64
|
+
if (isProduction) return { error: INTERNAL_SERVER_ERROR };
|
|
65
|
+
if (err instanceof RPCError) {
|
|
66
|
+
const payload = {
|
|
67
|
+
error: err.message || "Internal Server Error",
|
|
68
|
+
code: err.code
|
|
69
|
+
};
|
|
70
|
+
if (err.data !== void 0) payload.data = err.data;
|
|
71
|
+
return payload;
|
|
72
|
+
}
|
|
73
|
+
return { error: INTERNAL_SERVER_ERROR };
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
35
76
|
//#region src/hono/helpers.ts
|
|
36
77
|
/**
|
|
37
78
|
* Convenience function to load RPC config and attach the RPC middleware to a Hono app.
|
|
@@ -95,13 +136,15 @@ const viteMiddleware = (vite) => {
|
|
|
95
136
|
* @returns A promise resolving to the parsed body with its content type
|
|
96
137
|
*/
|
|
97
138
|
const readBody = async (c) => {
|
|
98
|
-
const
|
|
139
|
+
const contentType = c.req.header("content-type")?.toLowerCase() || "";
|
|
140
|
+
const isJSON = contentType.includes("json");
|
|
141
|
+
const isMultipart = contentType.includes("multipart/form-data");
|
|
99
142
|
const incoming = c.env.incoming;
|
|
100
143
|
if (incoming?.body !== void 0) {
|
|
101
144
|
const reqBody = incoming.body;
|
|
102
145
|
return {
|
|
103
|
-
contentType: isJSON ? "application/json" : "text/plain",
|
|
104
|
-
data: isJSON ? reqBody : String(reqBody)
|
|
146
|
+
contentType: isMultipart ? "multipart/form-data" : isJSON ? "application/json" : "text/plain",
|
|
147
|
+
data: isMultipart ? reqBody : isJSON ? reqBody : String(reqBody)
|
|
105
148
|
};
|
|
106
149
|
}
|
|
107
150
|
if (isJSON) return {
|
|
@@ -110,8 +153,8 @@ const readBody = async (c) => {
|
|
|
110
153
|
};
|
|
111
154
|
const text = await c.req.text();
|
|
112
155
|
return {
|
|
113
|
-
contentType: "text/plain",
|
|
114
|
-
data: String(text)
|
|
156
|
+
contentType: isMultipart ? "multipart/form-data" : "text/plain",
|
|
157
|
+
data: isMultipart ? { raw: text } : String(text)
|
|
115
158
|
};
|
|
116
159
|
};
|
|
117
160
|
//#endregion
|
|
@@ -202,7 +245,8 @@ const createRPCMiddleware = (initialOptions = {}) => {
|
|
|
202
245
|
return c.json({ data: result }, 200);
|
|
203
246
|
} catch (err) {
|
|
204
247
|
console.error(String(err));
|
|
205
|
-
|
|
248
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
249
|
+
return c.json(formatError(err, isProduction), 500);
|
|
206
250
|
}
|
|
207
251
|
}
|
|
208
252
|
});
|
package/dist/hono/hono.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.mjs","names":["createMiddleware","createHonoMiddleware"],"sources":["../../src/options.ts","../../src/tools.ts","../../src/constants.ts","../../src/hono/helpers.ts","../../src/hono/createMiddleware.ts"],"sourcesContent":["import type {\n MiddlewareOptions,\n RpcPluginOptions,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\n\nexport const defaultServerFnOptions = {\n contentType: \"application/json\",\n credentials: \"same-origin\",\n method: \"POST\",\n} satisfies ServerFunctionOptions;\n\nexport const defaultRPCOptions: RpcPluginOptions = {\n rpcPrefix: \"__rpc\",\n adapter: \"express\",\n};\n\nexport const defaultMiddlewareOptions = {\n rpcPrefix: undefined,\n path: undefined,\n origin: undefined,\n} satisfies MiddlewareOptions;\n","/**\n * Escapes special regex metacharacters in a string.\n * Used to safely embed user-configurable values (like rpcPrefix) into regular expressions,\n * preventing ReDoS and regex injection attacks.\n * @param s - The raw string to escape\n * @returns The escaped string safe for use in new RegExp()\n */\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","export const OPERATION_ABORTED = \"Operation aborted\";\n\nexport const REQUEST_CANCELLED = \"Request was cancelled\";\n\nexport const FETCH_ERROR_PREFIX = \"Fetch error: \";\n\nexport const NO_SERVER_FUNCTION_FOUND = \"No server function found.\";\n\nexport const ERROR_LOADING_FILE = \"Error loading file:\";\n\nexport const FUNCTION_NOT_FOUND = \"Function not found\";\n\nexport const METHOD_NOT_ALLOWED = \"Method Not Allowed\";\n\nexport const REQUEST_FORBIDDEN = \"Forbidden\";\n\nexport const INTERNAL_SERVER_ERROR = \"Internal Server Error\";\n\nexport const CLIENT_DISCONNECTED = \"client disconnected\";\n\n/** Returns a warning when a middleware name is reused, preventing registration conflicts. @param name - The duplicate middleware name */\nexport const MIDDLEWARE_NAME_USED = (name: string) =>\n `The middleware name \"${name}\" is already used.`;\n\n/** Error message when a value fails the safe-identifier validation. @param label - What kind of value was being validated. @param name - The rejected value */\nexport const INVALID_IDENTIFIER = (label: string, name: string) =>\n `Invalid ${label}: \"${name}\" must match /^[A-Za-z_$][A-Za-z0-9_$]*$/`;\n\n/** Error message when a value fails the safe-path-segment validation. @param label - What kind of value was being validated. @param segment - The rejected value */\nexport const INVALID_PATH_SEGMENT = (label: string, segment: string) =>\n `Invalid ${label}: \"${segment}\" must match /^[A-Za-z0-9_$][A-Za-z0-9_$/-]*$/`;\n\n/** Warning message when a specified RPC config file cannot be resolved on disk. @param configFile - The requested config filename. @param configFilePath - The resolved absolute path */\nexport const CONFIG_FILE_NOT_FOUND = (\n configFile: string,\n configFilePath: string,\n) =>\n ` ⚠︎ The specified RPC config file ${configFile} cannot be found at ${configFilePath}, loading the defaults..`;\n\nexport const NO_CONFIG_FOUND = ` ⚡︎ No RPC config found, loading the defaults..`;\n\nexport const FAILED_LOAD_CONFIG = ` ⚠︎ Failed to load RPC config:`;\n","// src/hono/helpers.ts\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { HttpBindings } from \"@hono/node-server\";\nimport type { Context, Hono } from \"hono\";\nimport type { ViteDevServer } from \"vite\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport type { BodyResult } from \"../types.d.ts\";\nimport type { IncomingWithBody } from \"./types.d.ts\";\nimport { createMiddleware } from \"hono/factory\";\nimport { createRPCMiddleware } from \"./createMiddleware.ts\";\n\n/**\n * Convenience function to load RPC config and attach the RPC middleware to a Hono app.\n * Dynamically imports loadRPCConfig and registers the middleware.\n * @param app - Hono application instance\n */\nexport async function attachRPC(app: Hono) {\n const { loadRPCConfig } = await import(\"@thednp/rpc\");\n const { adapter: _adapter, ...options } = await loadRPCConfig();\n\n app.use(createRPCMiddleware(options));\n}\n\n/**\n * Attaches Vite's dev server middlewares to a Hono app for development mode.\n * Uses the viteMiddleware wrapper to bridge Vite's Connect-compatible stack into Hono.\n * @param app - Hono application instance\n * @param vite - Running Vite dev server\n */\nexport const attachVite = (app: Hono, vite: ViteDevServer): void => {\n app.use(viteMiddleware(vite));\n};\n\n/**\n * Creates a Hono-compatible middleware from a Vite dev server middleware stack.\n * Bridges the Connect/Express middleware interface to Hono's context-based request/response model.\n * Supports both Node.js and Bun runtimes with separate polyfill paths.\n * @param vite - Running Vite dev server\n * @returns A Hono middleware function\n * @see https://github.com/honojs/hono/issues/3162#issuecomment-2331118049\n */\nexport const viteMiddleware = (\n vite: ViteDevServer,\n): ReturnType<typeof createMiddleware<{ Bindings: HttpBindings }>> => {\n return createMiddleware<{ Bindings: HttpBindings }>((c, next) => {\n return new Promise((resolve) => {\n // Node.js\n // @ts-expect-error - NodeJS is different\n // istanbul ignore if\n if (typeof Bun === \"undefined\") {\n vite.middlewares(c.env.incoming, c.env.outgoing, () => resolve(next()));\n return;\n }\n\n /* istanbul ignore next */ {\n // Bun\n let sent = false;\n const headers = new Headers();\n // Polyfill the node:http IncommingMessage and ServerResponse\n vite.middlewares(\n {\n url: new URL(c.req.path, \"http://localhost\").pathname,\n method: c.req.raw.method,\n headers: Object.fromEntries(c.req.raw.headers),\n } as IncomingMessage,\n {\n setHeader(name, value: string) {\n headers.set(name, value);\n return this;\n },\n end(body) {\n sent = true;\n resolve(\n // @ts-expect-error - weird\n c.body(body, c.res.status as ContentfulStatusCode, headers),\n );\n },\n } as ServerResponse,\n () => sent || resolve(next()),\n );\n }\n });\n });\n};\n\n/**\n * Reads and parses the HTTP request body from a Hono context.\n * Supports JSON and text content types, with pre-parsed body detection for server-side environments.\n * @param c - Hono request context\n * @returns A promise resolving to the parsed body with its content type\n */\nexport const readBody = async (\n c: Context,\n): Promise<BodyResult> => {\n const contentType = c.req.header(\"content-type\")?.toLowerCase() || \"\";\n const isJSON = contentType.includes(\"json\");\n const incoming = (c.env as HttpBindings).incoming as\n | IncomingWithBody\n | undefined;\n if (incoming?.body !== undefined) {\n const reqBody = incoming.body;\n return {\n contentType: isJSON ? \"application/json\" : \"text/plain\",\n data: isJSON ? reqBody : String(reqBody),\n } as BodyResult;\n }\n if (isJSON) {\n const data = await c.req.json();\n return {\n contentType: \"application/json\",\n data,\n };\n }\n\n const text = await c.req.text();\n return { contentType: \"text/plain\", data: String(text) };\n};\n","// src/hono/createMiddleware.ts\nimport type { Context, Next } from \"hono\";\nimport type { HonoMiddlewareFn, HonoMiddlewareOptions } from \"./types.d.ts\";\nimport type { JsonValue } from \"../types.d.ts\";\nimport { createMiddleware as createHonoMiddleware } from \"hono/factory\";\nimport { scanForServerFiles, serverFunctionsMap } from \"@thednp/rpc/server\";\nimport { defaultMiddlewareOptions, defaultRPCOptions } from \"../options.ts\";\nimport { escapeRegExp } from \"../tools.ts\";\nimport {\n CLIENT_DISCONNECTED,\n FUNCTION_NOT_FOUND,\n INTERNAL_SERVER_ERROR,\n METHOD_NOT_ALLOWED,\n MIDDLEWARE_NAME_USED,\n REQUEST_FORBIDDEN,\n} from \"../constants.ts\";\nimport { readBody } from \"./helpers.ts\";\n\nlet middlewareCount = 0;\nconst middlewareStack = new Set<string>();\n\n/**\n * Creates a Hono middleware with optional path and rpcPrefix filtering.\n * Middleware names are deduplicated. Prefix and path regexes are compiled once at creation time.\n * Uses Hono's factory `createMiddleware` to wrap the handler.\n * @param initialOptions - Options for rpcPrefix, path matching, and the handler function\n * @returns A Hono middleware function\n */\nexport const createMiddleware: HonoMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n initialOptions,\n ) as HonoMiddlewareOptions;\n\n const middlewareName = options.name;\n const rpcPrefix = options.rpcPrefix;\n const path = options.path;\n const handler = options.handler;\n\n let name = middlewareName;\n if (!name) {\n name = \"viteRPCMiddleware-\" + middlewareCount;\n middlewareCount += 1;\n }\n if (middlewareStack.has(name)) {\n throw new Error(MIDDLEWARE_NAME_USED(name));\n }\n middlewareStack.add(name);\n\n // Hoist regex compilation out of per-request path. Escape the prefix to\n // prevent regex injection via metacharacters in the config string.\n const prefixRegex: RegExp | null = rpcPrefix\n ? new RegExp(`^/${escapeRegExp(rpcPrefix)}/`)\n : null;\n const pathMatcher: RegExp | null = path\n ? (typeof path === \"string\" ? new RegExp(path) : path)\n : null;\n\n const middlewareHandler = createHonoMiddleware(\n async (c: Context, next: Next) => {\n const reqUrl = new URL(c.req.path, \"http://localhost\");\n const url = reqUrl.pathname;\n\n if (serverFunctionsMap.size === 0) {\n await scanForServerFiles();\n }\n\n // No need to continue when no handler provided\n if (!handler) {\n await next();\n return;\n }\n\n if (pathMatcher && !pathMatcher.test(url)) {\n await next();\n return;\n }\n\n if (prefixRegex && !prefixRegex.test(url)) {\n await next();\n return;\n }\n\n return await handler(c, next);\n },\n );\n\n Object.defineProperty(middlewareHandler, \"name\", {\n value: name,\n });\n\n return middlewareHandler;\n};\n\n/**\n * Creates the Hono RPC middleware that routes incoming requests to registered server functions.\n * Wraps the generic createMiddleware with the RPC handler that reads the body, dispatches\n * to the matching function, and returns the JSON-serialized result.\n * @param initialOptions - Options including rpcPrefix for URL routing\n * @returns A Hono middleware function\n */\nexport const createRPCMiddleware: HonoMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n { rpcPrefix: defaultRPCOptions.rpcPrefix },\n initialOptions,\n ) as HonoMiddlewareOptions;\n\n // Hoist prefix regex (escaped) and the literal prefix-for-replace out of the\n // per-request handler to avoid regex injection and per-request compilation.\n const rpcPrefix = options.rpcPrefix;\n const prefixRegex = rpcPrefix\n ? new RegExp(`^/${escapeRegExp(rpcPrefix)}/`)\n : /* istanbul ignore next */ null;\n const prefixReplace = `/${rpcPrefix}/`;\n\n return createMiddleware({\n ...options,\n handler: async (c: Context, _next: Next) => {\n const { path: reqPath } = c.req;\n // const { rpcPrefix: prefix } = options;\n\n // Defense-in-depth: validate prefix match via escaped regex even though\n // the outer createMiddleware gates on the same prefix already.\n // istanbul ignore if\n if (prefixRegex && !prefixRegex.test(reqPath)) {\n /* istanbul ignore next */\n return;\n }\n\n // Optional origin check: reject requests whose Origin header does not\n // match the configured origin. Requests without an Origin header\n // (curl, native clients) pass through unchecked.\n const origin = options.origin;\n const requestOrigin = c.req.header(\"origin\");\n if (origin && requestOrigin && requestOrigin !== origin) {\n return c.json({ error: REQUEST_FORBIDDEN }, 403);\n }\n\n const functionName = reqPath.replace(prefixReplace, \"\");\n const serverFunction = serverFunctionsMap.get(functionName);\n\n if (!serverFunction) {\n return c.json({ error: FUNCTION_NOT_FOUND }, 404);\n }\n\n try {\n const method = serverFunction.options?.method || \"POST\";\n if (c.req.method.toUpperCase() !== method) {\n return c.json({ error: METHOD_NOT_ALLOWED }, 405);\n }\n\n let args: JsonValue[] = [];\n if (method === \"GET\") {\n const raw = c.req.query(\"args\");\n // istanbul ignore else\n if (raw) args = JSON.parse(raw);\n } else {\n const body = await readBody(c);\n args = Array.isArray(body.data) ? body.data : [body.data];\n }\n const fnResult = serverFunction.handler(...args);\n const onAbort = () => fnResult.cancel(CLIENT_DISCONNECTED);\n // The runtime adapter may be absent in some Hono environments\n // (e.g. standalone serverless adapters), so guard the close hook.\n c.env.incoming?.on(\"close\", onAbort);\n const result = await fnResult.data;\n c.env.incoming?.off(\"close\", onAbort);\n\n return c.json({ data: result }, 200);\n } catch (err) {\n console.error(String(err));\n return c.json({ error: INTERNAL_SERVER_ERROR }, 500);\n }\n },\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hono.mjs","names":["createMiddleware","createHonoMiddleware"],"sources":["../../src/options.ts","../../src/tools.ts","../../src/constants.ts","../../src/server-helpers.ts","../../src/hono/helpers.ts","../../src/hono/createMiddleware.ts"],"sourcesContent":["import type {\n MiddlewareOptions,\n RpcPluginOptions,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\n\nexport const defaultServerFnOptions = {\n contentType: \"application/json\",\n credentials: \"same-origin\",\n method: \"POST\",\n} satisfies ServerFunctionOptions;\n\nexport const defaultRPCOptions: RpcPluginOptions = {\n rpcPrefix: \"__rpc\",\n adapter: \"express\",\n serverFiles: \"exact\",\n scanRoot: undefined,\n};\n\nexport const defaultMiddlewareOptions = {\n rpcPrefix: undefined,\n path: undefined,\n origin: undefined,\n} satisfies MiddlewareOptions;\n","/**\n * Escapes special regex metacharacters in a string.\n * Used to safely embed user-configurable values (like rpcPrefix) into regular expressions,\n * preventing ReDoS and regex injection attacks.\n * @param s - The raw string to escape\n * @returns The escaped string safe for use in new RegExp()\n */\nexport function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","export const OPERATION_ABORTED = \"Operation aborted\";\n\nexport const REQUEST_CANCELLED = \"Request was cancelled\";\n\nexport const FETCH_ERROR_PREFIX = \"Fetch error: \";\n\nexport const NO_SERVER_FUNCTION_FOUND = \"No server function found.\";\n\nexport const ERROR_LOADING_FILE = \"Error loading file:\";\n\nexport const FUNCTION_NOT_FOUND = \"Function not found\";\n\nexport const METHOD_NOT_ALLOWED = \"Method Not Allowed\";\n\nexport const REQUEST_FORBIDDEN = \"Forbidden\";\n\nexport const INTERNAL_SERVER_ERROR = \"Internal Server Error\";\n\nexport const CLIENT_DISCONNECTED = \"client disconnected\";\n\n/** Returns a warning when a middleware name is reused, preventing registration conflicts. @param name - The duplicate middleware name */\nexport const MIDDLEWARE_NAME_USED = (name: string) =>\n `The middleware name \"${name}\" is already used.`;\n\n/** Error message when a value fails the safe-identifier validation. @param label - What kind of value was being validated. @param name - The rejected value */\nexport const INVALID_IDENTIFIER = (label: string, name: string) =>\n `Invalid ${label}: \"${name}\" must match /^[A-Za-z_$][A-Za-z0-9_$]*$/`;\n\n/** Error message when a value fails the safe-path-segment validation. @param label - What kind of value was being validated. @param segment - The rejected value */\nexport const INVALID_PATH_SEGMENT = (label: string, segment: string) =>\n `Invalid ${label}: \"${segment}\" must match /^[A-Za-z0-9_$][A-Za-z0-9_$/-]*$/`;\n\n/** Warning message when a specified RPC config file cannot be resolved on disk. @param configFile - The requested config filename. @param configFilePath - The resolved absolute path */\nexport const CONFIG_FILE_NOT_FOUND = (\n configFile: string,\n configFilePath: string,\n) =>\n ` ⚠︎ The specified RPC config file ${configFile} cannot be found at ${configFilePath}, loading the defaults..`;\n\nexport const NO_CONFIG_FOUND = ` ⚡︎ No RPC config found, loading the defaults..`;\n\nexport const FAILED_LOAD_CONFIG = ` ⚠︎ Failed to load RPC config:`;\n\n/** Error template for duplicate server function names across files. @param name - The duplicate registered name */\nexport const DUPLICATE_FUNCTION_NAME = (name: string) =>\n `Duplicate server function \"${name}\" detected. Each server function must have a unique name. Remove or rename the duplicate.`;\n","/** @module Server-side utilities. Exports the `RPCError` class for typed server-side errors, `formatError` for middleware error responses, and `walkGlobFiles` for recursively discovering `*.server.*` files. Never import this module in client code — it is server-only. */\nimport type { JsonObject, JsonValue } from \"./types.d.ts\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { INTERNAL_SERVER_ERROR } from \"./constants.ts\";\n\nconst GLOB_REGEX = /^.+\\.server\\.(ts|js|mjs|mts)$/;\n\n/**\n * Recursively walks `dir` and collects absolute paths to files whose\n * basename matches the `*.server.{ts,js,mjs,mts}` glob pattern.\n */\nexport const walkGlobFiles = async (dir: string): Promise<string[]> => {\n const results: string[] = [];\n const stack = [dir];\n while (stack.length) {\n const current = stack.pop()!;\n let entries;\n try {\n entries = await readdir(current, { withFileTypes: true });\n } catch (_e) {\n continue;\n }\n for (const entry of entries) {\n const fullPath = join(current, entry.name);\n if (entry.isFile() && GLOB_REGEX.test(entry.name)) {\n results.push(fullPath);\n } else if (entry.isDirectory()) {\n stack.push(fullPath);\n }\n }\n }\n return results;\n};\n\n/**\n * A typed error thrown from server functions.\n * The middleware serializes the `message` and `code` in the response,\n * allowing clients to recognise and handle specific error conditions.\n */\nexport class RPCError extends Error {\n /** Machine-readable error code (e.g. \"VALIDATION_FAILED\", \"UNAUTHORIZED\") */\n code: string;\n /** Optional diagnostic payload */\n data?: JsonValue;\n constructor(message: string, code = \"INTERNAL\", data?: JsonValue) {\n super(message);\n this.name = \"RPCError\";\n this.code = code;\n this.data = data;\n }\n}\n\n/**\n * Formats an error for the RPC middleware response.\n * In development the full `RPCError` payload is included so developers\n * can quickly identify issues. Unexpected exceptions never expose their\n * message — only the generic \"Internal Server Error\" is sent, preventing\n * information disclosure; server-side diagnostics are preserved via the\n * middleware's `console.error` logging.\n */\nexport const formatError = (\n err: unknown,\n isProduction: boolean,\n): JsonObject => {\n if (isProduction) {\n return { error: INTERNAL_SERVER_ERROR };\n }\n if (err instanceof RPCError) {\n const payload: JsonObject = {\n error: err.message || INTERNAL_SERVER_ERROR,\n code: err.code,\n };\n if (err.data !== undefined) payload.data = err.data;\n return payload;\n }\n return { error: INTERNAL_SERVER_ERROR };\n};\n","// src/hono/helpers.ts\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { HttpBindings } from \"@hono/node-server\";\nimport type { Context, Hono } from \"hono\";\nimport type { ViteDevServer } from \"vite\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport type { BodyResult } from \"../types.d.ts\";\nimport type { IncomingWithBody } from \"./types.d.ts\";\nimport { createMiddleware } from \"hono/factory\";\nimport { createRPCMiddleware } from \"./createMiddleware.ts\";\n\n/**\n * Convenience function to load RPC config and attach the RPC middleware to a Hono app.\n * Dynamically imports loadRPCConfig and registers the middleware.\n * @param app - Hono application instance\n */\nexport async function attachRPC(app: Hono) {\n // The main plugin entry statically imports Vite, so loadRPCConfig is\n // imported lazily: function bundles that never call attachRPC (e.g.\n // serverless functions) keep Vite out of the bundle (or externalized).\n const { loadRPCConfig } = await import(\"@thednp/rpc\");\n const { adapter: _adapter, ...options } = await loadRPCConfig();\n\n app.use(createRPCMiddleware(options));\n}\n\n/**\n * Attaches Vite's dev server middlewares to a Hono app for development mode.\n * Uses the viteMiddleware wrapper to bridge Vite's Connect-compatible stack into Hono.\n * @param app - Hono application instance\n * @param vite - Running Vite dev server\n */\nexport const attachVite = (app: Hono, vite: ViteDevServer): void => {\n app.use(viteMiddleware(vite));\n};\n\n/**\n * Creates a Hono-compatible middleware from a Vite dev server middleware stack.\n * Bridges the Connect/Express middleware interface to Hono's context-based request/response model.\n * Supports both Node.js and Bun runtimes with separate polyfill paths.\n * @param vite - Running Vite dev server\n * @returns A Hono middleware function\n * @see https://github.com/honojs/hono/issues/3162#issuecomment-2331118049\n */\nexport const viteMiddleware = (\n vite: ViteDevServer,\n): ReturnType<typeof createMiddleware<{ Bindings: HttpBindings }>> => {\n return createMiddleware<{ Bindings: HttpBindings }>((c, next) => {\n return new Promise((resolve) => {\n // Node.js\n // @ts-expect-error - NodeJS is different\n // istanbul ignore if\n if (typeof Bun === \"undefined\") {\n vite.middlewares(c.env.incoming, c.env.outgoing, () => resolve(next()));\n return;\n }\n\n /* istanbul ignore next */ {\n // Bun\n let sent = false;\n const headers = new Headers();\n // Polyfill the node:http IncommingMessage and ServerResponse\n vite.middlewares(\n {\n url: new URL(c.req.path, \"http://localhost\").pathname,\n method: c.req.raw.method,\n headers: Object.fromEntries(c.req.raw.headers),\n } as IncomingMessage,\n {\n setHeader(name, value: string) {\n headers.set(name, value);\n return this;\n },\n end(body) {\n sent = true;\n resolve(\n // @ts-expect-error - weird\n c.body(body, c.res.status as ContentfulStatusCode, headers),\n );\n },\n } as ServerResponse,\n () => sent || resolve(next()),\n );\n }\n });\n });\n};\n\n/**\n * Reads and parses the HTTP request body from a Hono context.\n * Supports JSON and text content types, with pre-parsed body detection for server-side environments.\n * @param c - Hono request context\n * @returns A promise resolving to the parsed body with its content type\n */\nexport const readBody = async (\n c: Context,\n): Promise<BodyResult> => {\n const contentType = c.req.header(\"content-type\")?.toLowerCase() || \"\";\n const isJSON = contentType.includes(\"json\");\n const isMultipart = contentType.includes(\"multipart/form-data\");\n const incoming = (c.env as HttpBindings).incoming as\n | IncomingWithBody\n | undefined;\n if (incoming?.body !== undefined) {\n const reqBody = incoming.body;\n return {\n contentType: isMultipart\n ? \"multipart/form-data\"\n : isJSON\n ? \"application/json\"\n : \"text/plain\",\n data: isMultipart\n ? (reqBody as Record<string, unknown>)\n : isJSON\n ? reqBody\n : String(reqBody),\n } as BodyResult;\n }\n if (isJSON) {\n const data = await c.req.json();\n return {\n contentType: \"application/json\",\n data,\n };\n }\n\n const text = await c.req.text();\n return {\n contentType: isMultipart ? \"multipart/form-data\" : \"text/plain\",\n data: isMultipart\n ? ({ raw: text } as Record<string, unknown>)\n : String(text),\n } as BodyResult;\n};\n","// src/hono/createMiddleware.ts\nimport type { Context, Next } from \"hono\";\nimport type { HonoMiddlewareFn, HonoMiddlewareOptions } from \"./types.d.ts\";\nimport type { JsonValue } from \"../types.d.ts\";\nimport { createMiddleware as createHonoMiddleware } from \"hono/factory\";\nimport { scanForServerFiles, serverFunctionsMap } from \"@thednp/rpc/server\";\nimport { defaultMiddlewareOptions, defaultRPCOptions } from \"../options.ts\";\nimport { escapeRegExp } from \"../tools.ts\";\nimport { formatError } from \"../server-helpers.ts\";\nimport {\n CLIENT_DISCONNECTED,\n FUNCTION_NOT_FOUND,\n METHOD_NOT_ALLOWED,\n MIDDLEWARE_NAME_USED,\n REQUEST_FORBIDDEN,\n} from \"../constants.ts\";\nimport { readBody } from \"./helpers.ts\";\n\nlet middlewareCount = 0;\nconst middlewareStack = new Set<string>();\n\n/**\n * Creates a Hono middleware with optional path and rpcPrefix filtering.\n * Middleware names are deduplicated. Prefix and path regexes are compiled once at creation time.\n * Uses Hono's factory `createMiddleware` to wrap the handler.\n * @param initialOptions - Options for rpcPrefix, path matching, and the handler function\n * @returns A Hono middleware function\n */\nexport const createMiddleware: HonoMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n initialOptions,\n ) as HonoMiddlewareOptions;\n\n const middlewareName = options.name;\n const rpcPrefix = options.rpcPrefix;\n const path = options.path;\n const handler = options.handler;\n\n let name = middlewareName;\n if (!name) {\n name = \"viteRPCMiddleware-\" + middlewareCount;\n middlewareCount += 1;\n }\n if (middlewareStack.has(name)) {\n throw new Error(MIDDLEWARE_NAME_USED(name));\n }\n middlewareStack.add(name);\n\n // Hoist regex compilation out of per-request path. Escape the prefix to\n // prevent regex injection via metacharacters in the config string.\n const prefixRegex: RegExp | null = rpcPrefix\n ? new RegExp(`^/${escapeRegExp(rpcPrefix)}/`)\n : null;\n const pathMatcher: RegExp | null = path\n ? (typeof path === \"string\" ? new RegExp(path) : path)\n : null;\n\n const middlewareHandler = createHonoMiddleware(\n async (c: Context, next: Next) => {\n const reqUrl = new URL(c.req.path, \"http://localhost\");\n const url = reqUrl.pathname;\n\n if (serverFunctionsMap.size === 0) {\n await scanForServerFiles();\n }\n\n // No need to continue when no handler provided\n if (!handler) {\n await next();\n return;\n }\n\n if (pathMatcher && !pathMatcher.test(url)) {\n await next();\n return;\n }\n\n if (prefixRegex && !prefixRegex.test(url)) {\n await next();\n return;\n }\n\n return await handler(c, next);\n },\n );\n\n Object.defineProperty(middlewareHandler, \"name\", {\n value: name,\n });\n\n return middlewareHandler;\n};\n\n/**\n * Creates the Hono RPC middleware that routes incoming requests to registered server functions.\n * Wraps the generic createMiddleware with the RPC handler that reads the body, dispatches\n * to the matching function, and returns the JSON-serialized result.\n * @param initialOptions - Options including rpcPrefix for URL routing\n * @returns A Hono middleware function\n */\nexport const createRPCMiddleware: HonoMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n { rpcPrefix: defaultRPCOptions.rpcPrefix },\n initialOptions,\n ) as HonoMiddlewareOptions;\n\n // Hoist prefix regex (escaped) and the literal prefix-for-replace out of the\n // per-request handler to avoid regex injection and per-request compilation.\n const rpcPrefix = options.rpcPrefix;\n const prefixRegex = rpcPrefix\n ? new RegExp(`^/${escapeRegExp(rpcPrefix)}/`)\n : /* istanbul ignore next */ null;\n const prefixReplace = `/${rpcPrefix}/`;\n\n return createMiddleware({\n ...options,\n handler: async (c: Context, _next: Next) => {\n const { path: reqPath } = c.req;\n // const { rpcPrefix: prefix } = options;\n\n // Defense-in-depth: validate prefix match via escaped regex even though\n // the outer createMiddleware gates on the same prefix already.\n // istanbul ignore if\n if (prefixRegex && !prefixRegex.test(reqPath)) {\n /* istanbul ignore next */\n return;\n }\n\n // Optional origin check: reject requests whose Origin header does not\n // match the configured origin. Requests without an Origin header\n // (curl, native clients) pass through unchecked.\n const origin = options.origin;\n const requestOrigin = c.req.header(\"origin\");\n if (origin && requestOrigin && requestOrigin !== origin) {\n return c.json({ error: REQUEST_FORBIDDEN }, 403);\n }\n\n const functionName = reqPath.replace(prefixReplace, \"\");\n const serverFunction = serverFunctionsMap.get(functionName);\n\n if (!serverFunction) {\n return c.json({ error: FUNCTION_NOT_FOUND }, 404);\n }\n\n try {\n const method = serverFunction.options?.method || \"POST\";\n if (c.req.method.toUpperCase() !== method) {\n return c.json({ error: METHOD_NOT_ALLOWED }, 405);\n }\n\n let args: JsonValue[] = [];\n if (method === \"GET\") {\n const raw = c.req.query(\"args\");\n // istanbul ignore else\n if (raw) args = JSON.parse(raw);\n } else {\n const body = await readBody(c);\n args = Array.isArray(body.data)\n ? body.data as JsonValue[]\n : [body.data as JsonValue];\n }\n const fnResult = serverFunction.handler(...args);\n const onAbort = () => fnResult.cancel(CLIENT_DISCONNECTED);\n // The runtime adapter may be absent in some Hono environments\n // (e.g. standalone serverless adapters), so guard the close hook.\n c.env.incoming?.on(\"close\", onAbort);\n const result = await fnResult.data;\n c.env.incoming?.off(\"close\", onAbort);\n\n return c.json({ data: result }, 200);\n } catch (err) {\n console.error(String(err));\n const isProduction = process.env.NODE_ENV === \"production\";\n return c.json(formatError(err, isProduction), 500);\n }\n },\n });\n};\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -126,7 +126,7 @@ type SupportableContentType = "multipart/form-data" | "application/json" | "text
|
|
|
126
126
|
/**
|
|
127
127
|
* Content types the RPC client modules send with each request.
|
|
128
128
|
*/
|
|
129
|
-
type ContentType = "application/json" | "text/plain";
|
|
129
|
+
type ContentType = "application/json" | "text/plain" | "multipart/form-data";
|
|
130
130
|
/**
|
|
131
131
|
* Fetch `credentials` policy used by the generated client modules.
|
|
132
132
|
*/
|
|
@@ -140,6 +140,9 @@ type BodyResult = {
|
|
|
140
140
|
} | {
|
|
141
141
|
contentType: "text/plain";
|
|
142
142
|
data: string;
|
|
143
|
+
} | {
|
|
144
|
+
contentType: "multipart/form-data";
|
|
145
|
+
data: Record<string, unknown>;
|
|
143
146
|
};
|
|
144
147
|
/**
|
|
145
148
|
* Options for a single server function, controlling how the generated
|
|
@@ -178,7 +181,7 @@ type JsonObject = {
|
|
|
178
181
|
/**
|
|
179
182
|
* A JSON array of JSON values.
|
|
180
183
|
*/
|
|
181
|
-
type JsonArray = JsonValue[];
|
|
184
|
+
type JsonArray = (FormData | JsonValue)[];
|
|
182
185
|
/**
|
|
183
186
|
* Any JSON-serializable value: primitive, array, or object.
|
|
184
187
|
*/
|
|
@@ -211,7 +214,7 @@ type ServerFunction<TArgs extends JsonArray = JsonArray, TResult extends JsonVal
|
|
|
211
214
|
* Server function initialization signature, identical to `ServerFunction`.
|
|
212
215
|
* Used when registering a function with `createServerFunction`.
|
|
213
216
|
*/
|
|
214
|
-
type ServerFunctionInit<TArgs extends JsonArray = JsonArray, TResult extends JsonValue = JsonValue> = (signal: AbortSignal, ...args: TArgs) => Promise<TResult>;
|
|
217
|
+
type ServerFunctionInit<TArgs extends FormData | JsonArray = JsonArray, TResult extends JsonValue = JsonValue> = (signal: AbortSignal, ...args: TArgs) => Promise<TResult>;
|
|
215
218
|
/**
|
|
216
219
|
* Client-side stub signature generated for each server function.
|
|
217
220
|
* Returns a promise-backed `data` handle plus a `cancel` function
|
|
@@ -245,9 +248,11 @@ interface RpcPluginOptionsInternal {
|
|
|
245
248
|
/**
|
|
246
249
|
* Partial Vite config used when scanning server files outside a running dev server.
|
|
247
250
|
*/
|
|
248
|
-
type ScanConfig = Pick<ResolvedConfig, "
|
|
249
|
-
|
|
251
|
+
type ScanConfig = Pick<ResolvedConfig, "base"> & {
|
|
252
|
+
root?: string;
|
|
250
253
|
server?: Partial<ResolvedConfig["server"]>;
|
|
254
|
+
serverFiles?: "exact" | "glob";
|
|
255
|
+
scanRoot?: string;
|
|
251
256
|
};
|
|
252
257
|
/**
|
|
253
258
|
* Entry in the server functions map: registered name, client handler,
|
|
@@ -288,6 +293,20 @@ interface RpcPluginOptions {
|
|
|
288
293
|
* @default express
|
|
289
294
|
*/
|
|
290
295
|
adapter: "express" | "hono" | "fastify" | "koa";
|
|
296
|
+
/**
|
|
297
|
+
* Root directory from which the plugin scans for server files.
|
|
298
|
+
* Defaults to `<root>/src/api`. Use this in monorepos where server files
|
|
299
|
+
* live in a shared package outside the current project root.
|
|
300
|
+
* @default undefined (resolves to src/api relative to the Vite root)
|
|
301
|
+
*/
|
|
302
|
+
scanRoot?: string;
|
|
303
|
+
/**
|
|
304
|
+
* Server file matching mode. Use `"exact"` (default) for the classic
|
|
305
|
+
* `server.ts|js|mjs|mts` names, or `"glob"` to match `**\/*.server.{ts,js,mjs,mts}`
|
|
306
|
+
* inside the scan root.
|
|
307
|
+
* @default "exact"
|
|
308
|
+
*/
|
|
309
|
+
serverFiles?: "exact" | "glob";
|
|
291
310
|
}
|
|
292
311
|
interface MiddlewareOptions<A extends RpcPluginOptions["adapter"] = "express"> {
|
|
293
312
|
/**
|
|
@@ -377,7 +396,7 @@ declare const loadRPCConfig: (f?: string) => Promise<RpcPluginOptions>;
|
|
|
377
396
|
* @param devOptions - Development-only overrides (merged on top of config file values)
|
|
378
397
|
* @returns A Vite plugin object
|
|
379
398
|
*/
|
|
380
|
-
declare function rpcPlugin(devOptions?: Partial<RpcPluginOptions>): Plugin
|
|
399
|
+
declare function rpcPlugin(devOptions?: Partial<RpcPluginOptions>): Plugin;
|
|
381
400
|
//#endregion
|
|
382
401
|
export { type BodyResult, type ClientFunction, type ClientFunctionWithOptions, type ContentType, type Credentials, type FrameworkHooks, type FrameworkMiddlewareFn, type InnerModReturn, type JsonArray, type JsonObject, type JsonPrimitive, type JsonValue, type MiddlewareOptions, type RpcPluginOptions, type RpcPluginOptionsInternal, type ScanConfig, type ServerFnArgs, type ServerFnEntry, type ServerFunction, type ServerFunctionInit, type ServerFunctionOptions, type SupportableContentType, rpcPlugin as default, defineConfig, loadRPCConfig };
|
|
383
402
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/express/types.d.ts","../src/hono/types.d.ts","../src/fastify/types.d.ts","../src/koa/types.d.ts","../src/types.d.ts","../src/index.ts"],"mappings":";;;;;;;;;;;;;;KAgBY,2BAA2B;;;;;KAM3B,uBACV,UAAU,2CAEV,iBAAiB,QAAQ,8BACtB;;;;UAKY;;;;;;;EAOf,UACE,KAAK,kBAAkB,SACvB,KAAK,iBAAiB,UACtB,MAAM,QAAQ,eAAe,iBAC1B;;;;;;;UCzBU;;EAEf,SAAS;;;;;;KAOC,oBAAoB,UAAU,wCACxC,iBAAiB,QAAQ,oBAAkB,QACxC;;;;;;KCWO,2BAA2B;;;;;KAM3B,uBACV,UAAU,2CAEV,iBAAiB,QAAQ,8BACtB;;;;UAKY;;;;;;;EAOf,UACE,KAAK,gBACL,KAAK,cACL,MAAM,4BACH;;;;;;;KCtDK,uBAAuB;;;;UAalB;;;;;;EAMf,UAAU,KAAK,SAAS,MAAM,SAAS;;;;;;KAO7B,mBAAmB,UAAU,uCACvC,iBAAiB,QAAQ,0BACtB;;;;;;;UClBY;;EAEf,SAAS;;EAET,MAAM;;EAEN,SAAS;;EAET,KAAK;;;;;UAMU;;EAEf,SAAS;;EAET,MAAM;;EAEN,SAAS;;EAET,KAAK;;;;;;KAOK;;;;KASA;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/express/types.d.ts","../src/hono/types.d.ts","../src/fastify/types.d.ts","../src/koa/types.d.ts","../src/types.d.ts","../src/index.ts"],"mappings":";;;;;;;;;;;;;;KAgBY,2BAA2B;;;;;KAM3B,uBACV,UAAU,2CAEV,iBAAiB,QAAQ,8BACtB;;;;UAKY;;;;;;;EAOf,UACE,KAAK,kBAAkB,SACvB,KAAK,iBAAiB,UACtB,MAAM,QAAQ,eAAe,iBAC1B;;;;;;;UCzBU;;EAEf,SAAS;;;;;;KAOC,oBAAoB,UAAU,wCACxC,iBAAiB,QAAQ,oBAAkB,QACxC;;;;;;KCWO,2BAA2B;;;;;KAM3B,uBACV,UAAU,2CAEV,iBAAiB,QAAQ,8BACtB;;;;UAKY;;;;;;;EAOf,UACE,KAAK,gBACL,KAAK,cACL,MAAM,4BACH;;;;;;;KCtDK,uBAAuB;;;;UAalB;;;;;;EAMf,UAAU,KAAK,SAAS,MAAM,SAAS;;;;;;KAO7B,mBAAmB,UAAU,uCACvC,iBAAiB,QAAQ,0BACtB;;;;;;;UClBY;;EAEf,SAAS;;EAET,MAAM;;EAEN,SAAS;;EAET,KAAK;;;;;UAMU;;EAEf,SAAS;;EAET,MAAM;;EAEN,SAAS;;EAET,KAAK;;;;;;KAOK;;;;KASA;;;;KAQA;;;;KAKA;EACN;EAAiC,MAAM;;EACvC;EAA2B;;EAC3B;EAAoC,MAAM;;;;;;UAM/B;;;;;EAKf,aAAa;;;;;EAKb,cAAc;;;;;;;EAOd;;;;;;KAOU;;;;KAIA;GAAgB,cAAc,YAAY;;;;;KAI1C,aAAa,WAAW;;;;KAIxB,YAAY,gBAAgB,YAAY;;;;;;;;;;;;;;;;;;;KAuBxC,mBAAmB;;;;;KAMnB,eACV,cAAc,YAAY,WAC1B,gBAAgB,YAAY,cACzB,QAAQ,gBAAgB,MAAM,UAAU,QAAQ;;;;;KAMzC,mBACV,cAAc,WAAW,YAAY,WACrC,gBAAgB,YAAY,cACzB,QAAQ,gBAAgB,MAAM,UAAU,QAAQ;;;;;;KAOzC,eACV,cAAc,YAAY,WAC1B,gBAAgB,YAAY,iBACtB,MAAM;;EAEZ,MAAM,QAAQ;;EAEd,SAAS;;;;;;KAOC,4BAA4B;;EAEtC;;EAEA,UAAU;;;;;UAMK;;EAEf;;EAEA;;;;;KAMU,aAAa,KAAK;EAC5B;EACA,SAAS,QAAQ;EACjB;EACA;;;;;;UAOe;;EAEf;;EAEA,SAAS;;EAET,UAAU;;EAEV;;;;;;;;UASe;;;;;;;;;;;EAWf;;;;;;;EAQA;;;;;;;EAQA;;;;;;;EAQA;;UAGe,kBACf,UAAU;;;;EAKV;;;;;;;;;;;;EAaA,gBAAgB;;;;;;;;;;EAWhB;;;;;;;;EASA;;;;;;;;;;;;;;;;;;EAmBA,UAAU,eAAe;;;;;;KAOf;;EAEV,MAAM,QAAQ;;EAEd,SAAS;;;;;;;;;;cChSL,eAAe,GAAG,QAAQ,sBAAsB;;;;;;;;cAehD,gBAAgB,eAAe,QAAQ;;;;;;;;iBA4FpC,UACP,aAAY,QAAQ,oBACnB"}
|