@thednp/rpc 0.3.1 → 0.3.3
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/CHANGELOG.md +26 -0
- package/dist/config/config.d.mts +8 -2
- package/dist/config/config.d.mts.map +1 -1
- package/dist/config/config.mjs.map +1 -1
- package/dist/express/express.d.mts +13 -13
- package/dist/express/express.d.mts.map +1 -1
- package/dist/express/express.mjs.map +1 -1
- package/dist/fastify/fastify.d.mts +27 -9
- package/dist/fastify/fastify.d.mts.map +1 -1
- package/dist/fastify/fastify.mjs +27 -1
- package/dist/fastify/fastify.mjs.map +1 -1
- package/dist/fastify/plugin/fastify/plugin.d.mts +7 -0
- package/dist/fastify/plugin/fastify/plugin.d.mts.map +1 -1
- package/dist/fastify/plugin/fastify/plugin.mjs.map +1 -1
- package/dist/h3/h3.d.mts +11 -11
- package/dist/h3/h3.d.mts.map +1 -1
- package/dist/h3/h3.mjs.map +1 -1
- package/dist/helpers/helpers.d.mts +18 -5
- package/dist/helpers/helpers.d.mts.map +1 -1
- package/dist/helpers/helpers.mjs +18 -1
- package/dist/helpers/helpers.mjs.map +1 -1
- package/dist/hono/hono.d.mts +9 -9
- package/dist/hono/hono.d.mts.map +1 -1
- package/dist/hono/hono.mjs.map +1 -1
- package/dist/index.d.mts +14 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +6 -5
- package/dist/index.mjs.map +1 -1
- package/dist/koa/koa.d.mts +8 -8
- package/dist/koa/koa.d.mts.map +1 -1
- package/dist/koa/koa.mjs.map +1 -1
- package/dist/server/server.d.mts +37 -31
- package/dist/server/server.d.mts.map +1 -1
- package/dist/server/server.mjs +1 -1
- package/dist/server/server.mjs.map +1 -1
- package/llms.txt +5 -3
- package/package.json +11 -11
package/dist/koa/koa.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Koa, { Context, Next } from "koa";
|
|
1
|
+
import Koa, { Context, Next, Next as KoaNext } from "koa";
|
|
2
2
|
import { BodyResult, JsonValue, MiddlewareOptions, RpcPluginOptions } from "@thednp/rpc";
|
|
3
3
|
import { ViteDevServer } from "vite";
|
|
4
4
|
//#region src/koa/types.d.ts
|
|
@@ -40,7 +40,7 @@ type KoaMiddlewareFn = <A extends RpcPluginOptions["adapter"] = "koa">(initialOp
|
|
|
40
40
|
* @param initialOptions - Options for rpcPrefix, path matching, and the handler function
|
|
41
41
|
* @returns A Koa middleware function
|
|
42
42
|
*/
|
|
43
|
-
declare const createMiddleware: KoaMiddlewareFn;
|
|
43
|
+
export declare const createMiddleware: KoaMiddlewareFn;
|
|
44
44
|
/**
|
|
45
45
|
* Creates the Koa RPC middleware that routes incoming requests to registered server functions.
|
|
46
46
|
* Wraps the generic createMiddleware with the RPC handler that reads the body, dispatches
|
|
@@ -48,7 +48,7 @@ declare const createMiddleware: KoaMiddlewareFn;
|
|
|
48
48
|
* @param initialOptions - Options including rpcPrefix for URL routing
|
|
49
49
|
* @returns A Koa middleware function
|
|
50
50
|
*/
|
|
51
|
-
declare const createRPCMiddleware: KoaMiddlewareFn;
|
|
51
|
+
export declare const createRPCMiddleware: KoaMiddlewareFn;
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/koa/helpers.d.ts
|
|
54
54
|
/**
|
|
@@ -56,7 +56,7 @@ declare const createRPCMiddleware: KoaMiddlewareFn;
|
|
|
56
56
|
* Dynamically imports loadRPCConfig and registers the middleware.
|
|
57
57
|
* @param app - Koa application instance
|
|
58
58
|
*/
|
|
59
|
-
declare function attachRPC(app: Koa): Promise<void>;
|
|
59
|
+
export declare function attachRPC(app: Koa): Promise<void>;
|
|
60
60
|
/**
|
|
61
61
|
* Attaches Vite's dev server middlewares to a Koa app for development mode.
|
|
62
62
|
* Bridges Koa's context-based middleware to Vite's Connect-compatible middleware stack
|
|
@@ -64,7 +64,7 @@ declare function attachRPC(app: Koa): Promise<void>;
|
|
|
64
64
|
* @param app - Koa application instance
|
|
65
65
|
* @param vite - Running Vite dev server
|
|
66
66
|
*/
|
|
67
|
-
declare function attachVite(app: Koa, vite: ViteDevServer): void;
|
|
67
|
+
export declare function attachVite(app: Koa, vite: ViteDevServer): void;
|
|
68
68
|
/**
|
|
69
69
|
* Reads and parses the HTTP request body from a Koa context.
|
|
70
70
|
* If koa-body or another body parser already consumed the stream,
|
|
@@ -72,7 +72,7 @@ declare function attachVite(app: Koa, vite: ViteDevServer): void;
|
|
|
72
72
|
* @param ctx - Koa context
|
|
73
73
|
* @returns A promise resolving to the parsed body with its content type
|
|
74
74
|
*/
|
|
75
|
-
declare const readBody: (ctx: KoaContext) => Promise<BodyResult>;
|
|
75
|
+
export declare const readBody: (ctx: KoaContext) => Promise<BodyResult>;
|
|
76
76
|
/**
|
|
77
77
|
* Issues an HTTP redirect on a Koa context. Koa's `ctx.redirect(location)`
|
|
78
78
|
* defaults to `302` and sets the `Location` header; the status code must be
|
|
@@ -83,7 +83,7 @@ declare const readBody: (ctx: KoaContext) => Promise<BodyResult>;
|
|
|
83
83
|
* @param location - The URL to redirect to
|
|
84
84
|
* @param status - HTTP status code, defaults to 303
|
|
85
85
|
*/
|
|
86
|
-
declare const redirect: (ctx: KoaContext, location: string, status?: number) => void;
|
|
86
|
+
export declare const redirect: (ctx: KoaContext, location: string, status?: number) => void;
|
|
87
87
|
//#endregion
|
|
88
|
-
export {
|
|
88
|
+
export type { Koa, KoaContext, KoaMiddlewareFn, KoaMiddlewareHooks, KoaMiddlewareOptions, KoaNext };
|
|
89
89
|
//# sourceMappingURL=koa.d.mts.map
|
package/dist/koa/koa.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"koa.d.mts","names":[],"sources":["../../src/koa/types.d.ts","../../src/koa/createMiddleware.ts","../../src/koa/helpers.ts"],"mappings":";;;;;;;KAWY,uBAAuB;;;;UAKlB,mBAAmB;;EAElC,SAAS;IAAuB,gBAAgB;;;;;;UAMjC;;;;;;EAMf,UAAU,KAAK,SAAS,MAAM,SAAS;;;;;;KAO7B,mBAAmB,UAAU,qCACvC,iBAAiB,QAAQ,0BACtB;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"koa.d.mts","names":[],"sources":["../../src/koa/types.d.ts","../../src/koa/createMiddleware.ts","../../src/koa/helpers.ts"],"mappings":";;;;;;;KAWY,uBAAuB;;;;UAKlB,mBAAmB;;EAElC,SAAS;IAAuB,gBAAgB;;;;;;UAMjC;;;;;;EAMf,UAAU,KAAK,SAAS,MAAM,SAAS;;;;;;KAO7B,mBAAmB,UAAU,qCACvC,iBAAiB,QAAQ,0BACtB;;;;;;;;;;qBCGQ,kBAAkB;;;;;;;;qBA4ElB,qBAAqB;;;;;;;;wBCzGZ,UAAU,KAAK,MAAG;;;;;;;;wBAkBxB,WAAW,KAAK,KAAK,MAAM;;;;;;;;qBAuC9B,WAAQ,KACd,eACJ,QAAQ;;;;;;;;;;;qBA0FE,WAAQ,KACd,YAAU,kBACC"}
|
package/dist/koa/koa.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"koa.mjs","names":[],"sources":["../../src/options.ts","../../src/functionsMap.ts","../../src/constants.ts","../../src/koa/helpers.ts","../../src/koa/createMiddleware.ts"],"sourcesContent":["import type {\n MiddlewareOptions,\n RpcPluginOptions,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\n\nexport const defaultServerFnOptions: ServerFunctionOptions = {\n contentType: \"application/json\",\n credentials: \"same-origin\",\n method: \"POST\",\n};\n\nexport const defaultPrefix = \"__rpc\";\n\nexport const defaultRPCOptions: RpcPluginOptions = {\n rpcPrefix: defaultPrefix,\n adapter: \"express\",\n serverFiles: \"exact\",\n scanRoot: undefined,\n};\n\nexport const defaultMiddlewareOptions: MiddlewareOptions = {\n rpcPrefix: undefined,\n path: undefined,\n origin: undefined,\n};\n","import type { ServerFnEntry } from \"./types.d.ts\";\nimport { defaultPrefix } from \"./options.ts\";\n\n/**\n * Global symbol under which the shared `serverFunctionsByPrefix` map is stored\n * on `globalThis`. Keeping it on a `Symbol.for` key makes it instance-stable\n * across the bundled entry copies (`index.mjs`, `server.mjs`, `express.mjs`,\n * ...) and dev-server hot reloads, exactly like the request-context storage in\n * `context.ts`. Without this, `scanForServerFiles` (bundled into the plugin)\n * would populate a map copy the adapter middleware could not read.\n */\nconst functionsMapSymbol = Symbol.for(\"thednp.rpc.functionsMap\");\n\n/**\n * Map of rpcPrefix -> Map of function names -> ServerFnEntry\n * Enables multiple RPC instances with different prefixes to coexist\n * without name collisions.\n */\nexport const serverFunctionsByPrefix: Map<\n string,\n Map<string, ServerFnEntry>\n> = ((globalThis as Record<symbol, Map<string, Map<string, ServerFnEntry>>>)[\n functionsMapSymbol\n] ??= new Map());\n\n/**\n * Gets or creates the function map for a specific prefix.\n * @param prefix - The RPC prefix (e.g., \"__rpc\", \"v1:rpc\", \"admin:rpc\")\n * @returns Map of function names to ServerFnEntry for that prefix\n */\nexport const getFunctionsForPrefix = (\n prefix: string,\n): Map<string, ServerFnEntry> => {\n if (!serverFunctionsByPrefix.has(prefix)) {\n serverFunctionsByPrefix.set(prefix, new Map());\n }\n return serverFunctionsByPrefix.get(prefix)!;\n};\n\n/**\n * Backward compatibility: default map for the default prefix.\n * Legacy code can still use serverFunctionsMap.set(name, entry).\n */\nexport const serverFunctionsMap: Map<string, ServerFnEntry> = {\n get: (key: string) => getFunctionsForPrefix(defaultPrefix).get(key),\n set: (key: string, value: ServerFnEntry) =>\n getFunctionsForPrefix(defaultPrefix).set(key, value),\n has: (key: string) => getFunctionsForPrefix(defaultPrefix).has(key),\n delete: (key: string) => getFunctionsForPrefix(defaultPrefix).delete(key),\n clear: () => getFunctionsForPrefix(defaultPrefix).clear(),\n get size() {\n return getFunctionsForPrefix(defaultPrefix).size;\n },\n entries: () => getFunctionsForPrefix(defaultPrefix).entries(),\n keys: () => getFunctionsForPrefix(defaultPrefix).keys(),\n values: () => getFunctionsForPrefix(defaultPrefix).values(),\n forEach: (\n callback: (\n value: ServerFnEntry,\n key: string,\n map: Map<string, ServerFnEntry>,\n ) => void,\n ) => getFunctionsForPrefix(defaultPrefix).forEach(callback),\n [Symbol.iterator]: () =>\n getFunctionsForPrefix(defaultPrefix)[Symbol.iterator](),\n} as unknown as Map<string, ServerFnEntry>;\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 UNSUPPORTED_MEDIA_TYPE = \"Unsupported Media Type\";\n\nexport const BAD_REQUEST = \"Bad Request\";\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","// src/koa/helpers.ts\nimport type { Buffer } from \"node:buffer\";\nimport type { ViteDevServer } from \"vite\";\nimport { createRPCMiddleware } from \"./createMiddleware.ts\";\nimport type { BodyResult } from \"@thednp/rpc\";\nimport type { Koa } from \"./index.ts\";\nimport type { KoaContext } from \"./types.d.ts\";\n\n/**\n * Convenience function to load RPC config and attach the RPC middleware to a Koa app.\n * Dynamically imports loadRPCConfig and registers the middleware.\n * @param app - Koa application instance\n */\nexport async function attachRPC(app: Koa) {\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\n const config = await loadRPCConfig();\n const { adapter: _adapter, ...options } = config;\n app.use(createRPCMiddleware(options));\n}\n\n/**\n * Attaches Vite's dev server middlewares to a Koa app for development mode.\n * Bridges Koa's context-based middleware to Vite's Connect-compatible middleware stack\n * by forwarding Koa body, wrapping res.end, and delegating back to Koa on 404 or unhandled routes.\n * @param app - Koa application instance\n * @param vite - Running Vite dev server\n */\nexport function attachVite(app: Koa, vite: ViteDevServer): void {\n app.use(async (ctx: KoaContext, next) => {\n const req = ctx.req;\n const res = ctx.res;\n\n // Forward Koa body to req.body for Express/RPC middleware compatibility\n const requestBody = ctx.request?.body;\n if (requestBody !== undefined) {\n Object.assign(req, { body: requestBody });\n }\n\n const originalEnd = res.end.bind(res);\n let viteHandled = false;\n // @ts-ignore - Koa res.end type mismatch with Node's\n res.end = function (...args: unknown[]) {\n viteHandled = true;\n return originalEnd(args[0]);\n };\n\n await new Promise<void>((resolve) => {\n vite.middlewares(req, res, () => resolve(undefined));\n });\n\n // @ts-ignore - Koa res.end type mismatch with Node's\n res.end = originalEnd;\n\n if (!viteHandled || res.statusCode === 404) {\n await next();\n }\n });\n}\n\n/**\n * Reads and parses the HTTP request body from a Koa context.\n * If koa-body or another body parser already consumed the stream,\n * uses the pre-parsed body from `ctx.request.body`.\n * @param ctx - Koa context\n * @returns A promise resolving to the parsed body with its content type\n */\nexport const readBody = (\n ctx: KoaContext,\n): Promise<BodyResult> => {\n const contentType = ctx.request.headers[\"content-type\"]?.toLowerCase() || \"\";\n\n return new Promise((resolve, reject) => {\n // If an koa-body already consumed the stream\n // via app.use(koaBody()), use ctx.request.body directly\n const isJSON = contentType.includes(\"json\");\n const isMultipart = contentType.includes(\"multipart/form-data\");\n const isUrlEncoded = contentType.includes(\"urlencoded\");\n const reqBody = ctx.request.body;\n if (reqBody !== undefined) {\n resolve({\n contentType: isMultipart\n ? \"multipart/form-data\"\n : isJSON\n ? \"application/json\"\n : isUrlEncoded\n ? \"application/x-www-form-urlencoded\"\n : \"text/plain\",\n data: isMultipart\n ? (reqBody as Record<string, unknown>)\n : isJSON\n ? reqBody\n : isUrlEncoded\n ? (reqBody as Record<string, unknown>)\n : String(reqBody),\n } as BodyResult);\n return;\n }\n\n // OR read the body normally\n let body = \"\";\n\n const toggleListeners = (add?: boolean) => {\n const method = add ? \"on\" : \"off\";\n ctx.req[method](\"data\", onData);\n ctx.req[method](\"end\", onEnd);\n ctx.req[method](\"error\", onError);\n };\n\n const onData = (chunk: Buffer) => {\n // chunks.push(chunk);\n body += chunk.toString();\n };\n\n const onEnd = () => {\n toggleListeners();\n const isJSON = contentType.includes(\"json\");\n const isMultipart = contentType.includes(\"multipart/form-data\");\n const isUrlEncoded = contentType.includes(\"urlencoded\");\n try {\n const data = isMultipart\n ? { raw: body }\n : isUrlEncoded\n ? Object.fromEntries(new URLSearchParams(body))\n : JSON.parse(body);\n resolve({\n contentType: isMultipart\n ? \"multipart/form-data\"\n : isJSON\n ? \"application/json\"\n : isUrlEncoded\n ? \"application/x-www-form-urlencoded\"\n : \"text/plain\",\n data: isMultipart ? (data as Record<string, unknown>) : data,\n } as BodyResult);\n } catch (_er) {\n resolve({ contentType: \"text/plain\", data: String(body) });\n }\n };\n\n const onError = (err: Error) => {\n toggleListeners();\n reject(err);\n };\n\n toggleListeners(true);\n });\n};\n\n/**\n * Issues an HTTP redirect on a Koa context. Koa's `ctx.redirect(location)`\n * defaults to `302` and sets the `Location` header; the status code must be\n * overridden *after* the call (setting it before is ignored, see\n * koajs/koa#857). Defaults to `303 See Other` for convention\n * (Post/Redirect/Get).\n * @param ctx - Koa context\n * @param location - The URL to redirect to\n * @param status - HTTP status code, defaults to 303\n */\nexport const redirect = (\n ctx: KoaContext,\n location: string,\n status = 303,\n): void => {\n ctx.redirect(location);\n ctx.status = status;\n};\n","// src/koa/createMiddleware.ts\nimport type { Context, Next } from \"koa\";\nimport type { KoaMiddlewareFn, KoaMiddlewareOptions } from \"./types.d.ts\";\nimport type { JsonValue } from \"@thednp/rpc\";\nimport type { RequestEvent } from \"@thednp/rpc/server\";\nimport {\n escapeRegExp,\n formatError,\n getGlobalPrefix,\n hasContentTypeMismatch,\n provideRequestContext,\n safeURL,\n scanForServerFiles,\n} from \"@thednp/rpc/server\";\nimport { getFunctionsForPrefix } from \"../functionsMap.ts\";\nimport {\n BAD_REQUEST,\n CLIENT_DISCONNECTED,\n FUNCTION_NOT_FOUND,\n METHOD_NOT_ALLOWED,\n MIDDLEWARE_NAME_USED,\n REQUEST_FORBIDDEN,\n UNSUPPORTED_MEDIA_TYPE,\n} from \"../constants.ts\";\n\nimport {\n defaultMiddlewareOptions,\n defaultPrefix,\n defaultRPCOptions,\n} from \"../options.ts\";\nimport { readBody, redirect as koaRedirect } from \"./helpers.ts\";\n\nlet middlewareCount = 0;\nconst middlewareStack = new Set<string>();\n\n/**\n * Creates a Koa middleware with optional path and rpcPrefix filtering.\n * Middleware names are deduplicated. Prefix and path regexes are compiled once at creation time.\n * Koa URL is normalized via `new URL()` to strip query strings before matching.\n * @param initialOptions - Options for rpcPrefix, path matching, and the handler function\n * @returns A Koa middleware function\n */\nexport const createMiddleware: KoaMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n initialOptions,\n ) as KoaMiddlewareOptions;\n\n const middlewareName = options.name;\n let 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 = async (ctx: Context, next: Next) => {\n const url = safeURL(ctx.url).pathname;\n\n // No need to continue when no handler provided\n if (!handler) {\n return next();\n }\n\n if (pathMatcher && !pathMatcher.test(url)) {\n return next();\n }\n\n if (prefixRegex && !prefixRegex.test(url)) {\n return next();\n }\n\n rpcPrefix = (rpcPrefix ?? defaultPrefix) as string;\n\n // When serving from production server, scan for server files\n if (getFunctionsForPrefix(rpcPrefix).size === 0) {\n await scanForServerFiles({\n rpcPrefix,\n serverFiles: (options as unknown as { serverFiles?: \"exact\" | \"glob\" })\n .serverFiles,\n scanRoot: (options as unknown as { scanRoot?: string }).scanRoot,\n } as never);\n }\n\n await handler(ctx, next);\n };\n\n Object.defineProperty(middlewareHandler, \"name\", {\n value: name,\n });\n\n return middlewareHandler;\n};\n\n/**\n * Creates the Koa 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 sets the JSON-serialized result on ctx.body.\n * @param initialOptions - Options including rpcPrefix for URL routing\n * @returns A Koa middleware function\n */\nexport const createRPCMiddleware: KoaMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n { rpcPrefix: defaultRPCOptions.rpcPrefix },\n initialOptions,\n ) as KoaMiddlewareOptions;\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 prefix = rpcPrefix || getGlobalPrefix() || defaultPrefix;\n const prefixRegex = rpcPrefix\n ? new RegExp(`^/${escapeRegExp(rpcPrefix)}/`)\n : /* istanbul ignore next */ null;\n const prefixReplace = `/${prefix}/`;\n\n return createMiddleware({\n ...options,\n handler: async (ctx: Context, _next: Next) => {\n const reqUrl = safeURL(ctx.url);\n const url = reqUrl.pathname;\n // const { rpcPrefix } = 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 next\n if (prefixRegex && !prefixRegex.test(url)) {\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 = ctx.headers.origin;\n if (origin && requestOrigin && requestOrigin !== origin) {\n ctx.status = 403;\n ctx.body = { error: REQUEST_FORBIDDEN };\n return;\n }\n\n const functionName = url.replace(prefixReplace, \"\");\n const serverFunction = getFunctionsForPrefix(prefix).get(functionName);\n\n if (!serverFunction) {\n ctx.status = 404;\n ctx.body = { error: FUNCTION_NOT_FOUND };\n return;\n }\n\n try {\n const method = serverFunction.options?.method || \"POST\";\n if (ctx.method.toUpperCase() !== method) {\n ctx.status = 405;\n ctx.body = { error: METHOD_NOT_ALLOWED };\n return;\n }\n\n let args: JsonValue[] = [];\n if (method === \"GET\") {\n const raw = reqUrl.searchParams.get(\"args\");\n if (raw) {\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n ctx.status = 400;\n ctx.body = { error: BAD_REQUEST };\n return;\n }\n args = parsed as JsonValue[];\n }\n } else {\n // Content-type enforcement: strict for json/text, lenient between forms.\n // Requests without a Content-Type header are exempt (curl/GET compat).\n // Checked BEFORE readBody so mismatched bodies are never buffered.\n if (\n hasContentTypeMismatch(\n serverFunction.options?.contentType ?? \"application/json\",\n ctx.headers[\"content-type\"],\n )\n ) {\n ctx.status = 415;\n ctx.body = { error: UNSUPPORTED_MEDIA_TYPE };\n return;\n }\n const body = await readBody(ctx);\n args = Array.isArray(body.data)\n ? body.data as JsonValue[]\n : [body.data as JsonValue];\n }\n const requestEvent: RequestEvent = {\n request: ctx.req,\n response: ctx,\n nativeEvent: ctx,\n locals: ctx.state,\n functionName,\n redirect: (location, status = 303) => {\n requestEvent.redirected = { location, status };\n koaRedirect(ctx, location, status);\n },\n send: (status, body, headers) => {\n requestEvent.sent = { status, body, headers };\n if (headers) {\n for (const [name, value] of Object.entries(headers)) {\n ctx.set(name, value);\n }\n }\n ctx.status = status;\n ctx.body = body;\n },\n };\n const { data: resultData, cancel } = provideRequestContext(\n requestEvent,\n () => serverFunction.handler(...args),\n );\n const onClose = () => cancel(CLIENT_DISCONNECTED);\n ctx.req.on(\"close\", onClose);\n const result = await resultData;\n ctx.req.off(\"close\", onClose);\n\n // Skip the JSON send when the server function issued a redirect or\n // short-circuited with `send`; the bound Koa adapter already set\n // ctx.status/ctx.body.\n // istanbul ignore else\n if (!requestEvent.redirected && !requestEvent.sent) {\n ctx.status = 200;\n ctx.body = { data: result };\n }\n } catch (err) {\n console.error(String(err));\n const isProduction = process.env.NODE_ENV === \"production\";\n ctx.status = 500;\n ctx.body = formatError(err, isProduction);\n }\n },\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"koa.mjs","names":[],"sources":["../../src/options.ts","../../src/functionsMap.ts","../../src/constants.ts","../../src/koa/helpers.ts","../../src/koa/createMiddleware.ts"],"sourcesContent":["import type {\n MiddlewareOptions,\n RpcPluginOptions,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\n\nexport const defaultServerFnOptions: ServerFunctionOptions = {\n contentType: \"application/json\",\n credentials: \"same-origin\",\n method: \"POST\",\n};\n\nexport const defaultPrefix = \"__rpc\";\n\nexport const defaultRPCOptions: RpcPluginOptions = {\n rpcPrefix: defaultPrefix,\n adapter: \"express\",\n serverFiles: \"exact\",\n scanRoot: undefined,\n};\n\nexport const defaultMiddlewareOptions: MiddlewareOptions = {\n rpcPrefix: undefined,\n path: undefined,\n origin: undefined,\n};\n","import type { ServerFnEntry } from \"./types.d.ts\";\nimport { defaultPrefix } from \"./options.ts\";\n\n/**\n * Global symbol under which the shared `serverFunctionsByPrefix` map is stored\n * on `globalThis`. Keeping it on a `Symbol.for` key makes it instance-stable\n * across the bundled entry copies (`index.mjs`, `server.mjs`, `express.mjs`,\n * ...) and dev-server hot reloads, exactly like the request-context storage in\n * `context.ts`. Without this, `scanForServerFiles` (bundled into the plugin)\n * would populate a map copy the adapter middleware could not read.\n */\nconst functionsMapSymbol = Symbol.for(\"thednp.rpc.functionsMap\");\n\n/**\n * Map of rpcPrefix -> Map of function names -> ServerFnEntry\n * Enables multiple RPC instances with different prefixes to coexist\n * without name collisions.\n */\nexport const serverFunctionsByPrefix: Map<\n string,\n Map<string, ServerFnEntry>\n> = ((globalThis as Record<symbol, Map<string, Map<string, ServerFnEntry>>>)[\n functionsMapSymbol\n] ??= new Map());\n\n/**\n * Gets or creates the function map for a specific prefix.\n * @param prefix - The RPC prefix (e.g., \"__rpc\", \"v1:rpc\", \"admin:rpc\")\n * @returns Map of function names to ServerFnEntry for that prefix\n */\nexport const getFunctionsForPrefix = (\n prefix: string,\n): Map<string, ServerFnEntry> => {\n if (!serverFunctionsByPrefix.has(prefix)) {\n serverFunctionsByPrefix.set(prefix, new Map());\n }\n return serverFunctionsByPrefix.get(prefix)!;\n};\n\n/**\n * Backward compatibility: default map for the default prefix.\n * Legacy code can still use serverFunctionsMap.set(name, entry).\n */\nexport const serverFunctionsMap: Map<string, ServerFnEntry> = {\n get: (key: string) => getFunctionsForPrefix(defaultPrefix).get(key),\n set: (key: string, value: ServerFnEntry) =>\n getFunctionsForPrefix(defaultPrefix).set(key, value),\n has: (key: string) => getFunctionsForPrefix(defaultPrefix).has(key),\n delete: (key: string) => getFunctionsForPrefix(defaultPrefix).delete(key),\n clear: () => getFunctionsForPrefix(defaultPrefix).clear(),\n get size() {\n return getFunctionsForPrefix(defaultPrefix).size;\n },\n entries: () => getFunctionsForPrefix(defaultPrefix).entries(),\n keys: () => getFunctionsForPrefix(defaultPrefix).keys(),\n values: () => getFunctionsForPrefix(defaultPrefix).values(),\n forEach: (\n callback: (\n value: ServerFnEntry,\n key: string,\n map: Map<string, ServerFnEntry>,\n ) => void,\n ) => getFunctionsForPrefix(defaultPrefix).forEach(callback),\n [Symbol.iterator]: () =>\n getFunctionsForPrefix(defaultPrefix)[Symbol.iterator](),\n} as unknown as Map<string, ServerFnEntry>;\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 UNSUPPORTED_MEDIA_TYPE = \"Unsupported Media Type\";\n\nexport const BAD_REQUEST = \"Bad Request\";\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 =\n ` ⚡︎ 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","// src/koa/helpers.ts\nimport type { Buffer } from \"node:buffer\";\nimport type { ViteDevServer } from \"vite\";\nimport { createRPCMiddleware } from \"./createMiddleware.ts\";\nimport type { BodyResult } from \"@thednp/rpc\";\nimport type { Koa } from \"./index.ts\";\nimport type { KoaContext } from \"./types.d.ts\";\n\n/**\n * Convenience function to load RPC config and attach the RPC middleware to a Koa app.\n * Dynamically imports loadRPCConfig and registers the middleware.\n * @param app - Koa application instance\n */\nexport async function attachRPC(app: Koa) {\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\n const config = await loadRPCConfig();\n const { adapter: _adapter, ...options } = config;\n app.use(createRPCMiddleware(options));\n}\n\n/**\n * Attaches Vite's dev server middlewares to a Koa app for development mode.\n * Bridges Koa's context-based middleware to Vite's Connect-compatible middleware stack\n * by forwarding Koa body, wrapping res.end, and delegating back to Koa on 404 or unhandled routes.\n * @param app - Koa application instance\n * @param vite - Running Vite dev server\n */\nexport function attachVite(app: Koa, vite: ViteDevServer): void {\n app.use(async (ctx: KoaContext, next) => {\n const req = ctx.req;\n const res = ctx.res;\n\n // Forward Koa body to req.body for Express/RPC middleware compatibility\n const requestBody = ctx.request?.body;\n if (requestBody !== undefined) {\n Object.assign(req, { body: requestBody });\n }\n\n const originalEnd = res.end.bind(res);\n let viteHandled = false;\n // @ts-ignore - Koa res.end type mismatch with Node's\n res.end = function (...args: unknown[]) {\n viteHandled = true;\n return originalEnd(args[0]);\n };\n\n await new Promise<void>((resolve) => {\n vite.middlewares(req, res, () => resolve(undefined));\n });\n\n // @ts-ignore - Koa res.end type mismatch with Node's\n res.end = originalEnd;\n\n if (!viteHandled || res.statusCode === 404) {\n await next();\n }\n });\n}\n\n/**\n * Reads and parses the HTTP request body from a Koa context.\n * If koa-body or another body parser already consumed the stream,\n * uses the pre-parsed body from `ctx.request.body`.\n * @param ctx - Koa context\n * @returns A promise resolving to the parsed body with its content type\n */\nexport const readBody = (\n ctx: KoaContext,\n): Promise<BodyResult> => {\n const contentType = ctx.request.headers[\"content-type\"]?.toLowerCase() || \"\";\n\n return new Promise((resolve, reject) => {\n // If an koa-body already consumed the stream\n // via app.use(koaBody()), use ctx.request.body directly\n const isJSON = contentType.includes(\"json\");\n const isMultipart = contentType.includes(\"multipart/form-data\");\n const isUrlEncoded = contentType.includes(\"urlencoded\");\n const reqBody = ctx.request.body;\n if (reqBody !== undefined) {\n resolve({\n contentType: isMultipart\n ? \"multipart/form-data\"\n : isJSON\n ? \"application/json\"\n : isUrlEncoded\n ? \"application/x-www-form-urlencoded\"\n : \"text/plain\",\n data: isMultipart\n ? (reqBody as Record<string, unknown>)\n : isJSON\n ? reqBody\n : isUrlEncoded\n ? (reqBody as Record<string, unknown>)\n : String(reqBody),\n } as BodyResult);\n return;\n }\n\n // OR read the body normally\n let body = \"\";\n\n const toggleListeners = (add?: boolean) => {\n const method = add ? \"on\" : \"off\";\n ctx.req[method](\"data\", onData);\n ctx.req[method](\"end\", onEnd);\n ctx.req[method](\"error\", onError);\n };\n\n const onData = (chunk: Buffer) => {\n // chunks.push(chunk);\n body += chunk.toString();\n };\n\n const onEnd = () => {\n toggleListeners();\n const isJSON = contentType.includes(\"json\");\n const isMultipart = contentType.includes(\"multipart/form-data\");\n const isUrlEncoded = contentType.includes(\"urlencoded\");\n try {\n const data = isMultipart\n ? { raw: body }\n : isUrlEncoded\n ? Object.fromEntries(new URLSearchParams(body))\n : JSON.parse(body);\n resolve({\n contentType: isMultipart\n ? \"multipart/form-data\"\n : isJSON\n ? \"application/json\"\n : isUrlEncoded\n ? \"application/x-www-form-urlencoded\"\n : \"text/plain\",\n data: isMultipart ? (data as Record<string, unknown>) : data,\n } as BodyResult);\n } catch (_er) {\n resolve({ contentType: \"text/plain\", data: String(body) });\n }\n };\n\n const onError = (err: Error) => {\n toggleListeners();\n reject(err);\n };\n\n toggleListeners(true);\n });\n};\n\n/**\n * Issues an HTTP redirect on a Koa context. Koa's `ctx.redirect(location)`\n * defaults to `302` and sets the `Location` header; the status code must be\n * overridden *after* the call (setting it before is ignored, see\n * koajs/koa#857). Defaults to `303 See Other` for convention\n * (Post/Redirect/Get).\n * @param ctx - Koa context\n * @param location - The URL to redirect to\n * @param status - HTTP status code, defaults to 303\n */\nexport const redirect = (\n ctx: KoaContext,\n location: string,\n status = 303,\n): void => {\n ctx.redirect(location);\n ctx.status = status;\n};\n","// src/koa/createMiddleware.ts\nimport type { Context, Next } from \"koa\";\nimport type { KoaMiddlewareFn, KoaMiddlewareOptions } from \"./types.d.ts\";\nimport type { JsonValue } from \"@thednp/rpc\";\nimport type { RequestEvent } from \"@thednp/rpc/server\";\nimport {\n escapeRegExp,\n formatError,\n getGlobalPrefix,\n hasContentTypeMismatch,\n provideRequestContext,\n safeURL,\n scanForServerFiles,\n} from \"@thednp/rpc/server\";\nimport { getFunctionsForPrefix } from \"../functionsMap.ts\";\nimport {\n BAD_REQUEST,\n CLIENT_DISCONNECTED,\n FUNCTION_NOT_FOUND,\n METHOD_NOT_ALLOWED,\n MIDDLEWARE_NAME_USED,\n REQUEST_FORBIDDEN,\n UNSUPPORTED_MEDIA_TYPE,\n} from \"../constants.ts\";\n\nimport {\n defaultMiddlewareOptions,\n defaultPrefix,\n defaultRPCOptions,\n} from \"../options.ts\";\nimport { readBody, redirect as koaRedirect } from \"./helpers.ts\";\n\nlet middlewareCount = 0;\nconst middlewareStack = new Set<string>();\n\n/**\n * Creates a Koa middleware with optional path and rpcPrefix filtering.\n * Middleware names are deduplicated. Prefix and path regexes are compiled once at creation time.\n * Koa URL is normalized via `new URL()` to strip query strings before matching.\n * @param initialOptions - Options for rpcPrefix, path matching, and the handler function\n * @returns A Koa middleware function\n */\nexport const createMiddleware: KoaMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n initialOptions,\n ) as KoaMiddlewareOptions;\n\n const middlewareName = options.name;\n let 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 = async (ctx: Context, next: Next) => {\n const url = safeURL(ctx.url).pathname;\n\n // No need to continue when no handler provided\n if (!handler) {\n return next();\n }\n\n if (pathMatcher && !pathMatcher.test(url)) {\n return next();\n }\n\n if (prefixRegex && !prefixRegex.test(url)) {\n return next();\n }\n\n rpcPrefix = (rpcPrefix ?? defaultPrefix) as string;\n\n // When serving from production server, scan for server files\n if (getFunctionsForPrefix(rpcPrefix).size === 0) {\n await scanForServerFiles({\n rpcPrefix,\n serverFiles: (options as unknown as { serverFiles?: \"exact\" | \"glob\" })\n .serverFiles,\n scanRoot: (options as unknown as { scanRoot?: string }).scanRoot,\n } as never);\n }\n\n await handler(ctx, next);\n };\n\n Object.defineProperty(middlewareHandler, \"name\", {\n value: name,\n });\n\n return middlewareHandler;\n};\n\n/**\n * Creates the Koa 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 sets the JSON-serialized result on ctx.body.\n * @param initialOptions - Options including rpcPrefix for URL routing\n * @returns A Koa middleware function\n */\nexport const createRPCMiddleware: KoaMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n { rpcPrefix: defaultRPCOptions.rpcPrefix },\n initialOptions,\n ) as KoaMiddlewareOptions;\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 prefix = rpcPrefix || getGlobalPrefix() || defaultPrefix;\n const prefixRegex = rpcPrefix\n ? new RegExp(`^/${escapeRegExp(rpcPrefix)}/`)\n : /* istanbul ignore next */ null;\n const prefixReplace = `/${prefix}/`;\n\n return createMiddleware({\n ...options,\n handler: async (ctx: Context, _next: Next) => {\n const reqUrl = safeURL(ctx.url);\n const url = reqUrl.pathname;\n // const { rpcPrefix } = 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 next\n if (prefixRegex && !prefixRegex.test(url)) {\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 = ctx.headers.origin;\n if (origin && requestOrigin && requestOrigin !== origin) {\n ctx.status = 403;\n ctx.body = { error: REQUEST_FORBIDDEN };\n return;\n }\n\n const functionName = url.replace(prefixReplace, \"\");\n const serverFunction = getFunctionsForPrefix(prefix).get(functionName);\n\n if (!serverFunction) {\n ctx.status = 404;\n ctx.body = { error: FUNCTION_NOT_FOUND };\n return;\n }\n\n try {\n const method = serverFunction.options?.method || \"POST\";\n if (ctx.method.toUpperCase() !== method) {\n ctx.status = 405;\n ctx.body = { error: METHOD_NOT_ALLOWED };\n return;\n }\n\n let args: JsonValue[] = [];\n if (method === \"GET\") {\n const raw = reqUrl.searchParams.get(\"args\");\n if (raw) {\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n ctx.status = 400;\n ctx.body = { error: BAD_REQUEST };\n return;\n }\n args = parsed as JsonValue[];\n }\n } else {\n // Content-type enforcement: strict for json/text, lenient between forms.\n // Requests without a Content-Type header are exempt (curl/GET compat).\n // Checked BEFORE readBody so mismatched bodies are never buffered.\n if (\n hasContentTypeMismatch(\n serverFunction.options?.contentType ?? \"application/json\",\n ctx.headers[\"content-type\"],\n )\n ) {\n ctx.status = 415;\n ctx.body = { error: UNSUPPORTED_MEDIA_TYPE };\n return;\n }\n const body = await readBody(ctx);\n args = Array.isArray(body.data)\n ? body.data as JsonValue[]\n : [body.data as JsonValue];\n }\n const requestEvent: RequestEvent = {\n request: ctx.req,\n response: ctx,\n nativeEvent: ctx,\n locals: ctx.state,\n functionName,\n redirect: (location, status = 303) => {\n requestEvent.redirected = { location, status };\n koaRedirect(ctx, location, status);\n },\n send: (status, body, headers) => {\n requestEvent.sent = { status, body, headers };\n if (headers) {\n for (const [name, value] of Object.entries(headers)) {\n ctx.set(name, value);\n }\n }\n ctx.status = status;\n ctx.body = body;\n },\n };\n const { data: resultData, cancel } = provideRequestContext(\n requestEvent,\n () => serverFunction.handler(...args),\n );\n const onClose = () => cancel(CLIENT_DISCONNECTED);\n ctx.req.on(\"close\", onClose);\n const result = await resultData;\n ctx.req.off(\"close\", onClose);\n\n // Skip the JSON send when the server function issued a redirect or\n // short-circuited with `send`; the bound Koa adapter already set\n // ctx.status/ctx.body.\n // istanbul ignore else\n if (!requestEvent.redirected && !requestEvent.sent) {\n ctx.status = 200;\n ctx.body = { data: result };\n }\n } catch (err) {\n console.error(String(err));\n const isProduction = process.env.NODE_ENV === \"production\";\n ctx.status = 500;\n ctx.body = formatError(err, isProduction);\n }\n },\n });\n};\n"],"mappings":"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"}
|
package/dist/server/server.d.mts
CHANGED
|
@@ -149,13 +149,13 @@ type JsonValue = JsonPrimitive | JsonArray | JsonObject;
|
|
|
149
149
|
* Server function initialization signature, identical to `ServerFunction`.
|
|
150
150
|
* Used when registering a function with `createServerFunction`.
|
|
151
151
|
*/
|
|
152
|
-
type ServerFunctionInit<TArgs extends FormData | JsonArray = JsonArray, TResult
|
|
152
|
+
type ServerFunctionInit<TArgs extends FormData | JsonArray = JsonArray, TResult = JsonValue> = (signal: AbortSignal, ...args: TArgs) => Promise<TResult>;
|
|
153
153
|
/**
|
|
154
154
|
* Client-side stub signature generated for each server function.
|
|
155
155
|
* Returns a promise-backed `data` handle plus a `cancel` function
|
|
156
156
|
* that aborts the underlying fetch request.
|
|
157
157
|
*/
|
|
158
|
-
type ClientFunction<TArgs extends JsonArray = JsonArray, TResult
|
|
158
|
+
type ClientFunction<TArgs extends JsonArray = JsonArray, TResult = JsonValue> = (...args: TArgs) => {
|
|
159
159
|
/** Promise resolving to the server response data */
|
|
160
160
|
data: Promise<TResult>;
|
|
161
161
|
/** Aborts the in-flight request with the given reason */
|
|
@@ -244,6 +244,13 @@ interface RpcPluginOptions {
|
|
|
244
244
|
* @default "exact"
|
|
245
245
|
*/
|
|
246
246
|
serverFiles?: "exact" | "glob";
|
|
247
|
+
/**
|
|
248
|
+
* Suppress the "no RPC config found" warning when no config file is
|
|
249
|
+
* discovered. Useful when the plugin is wrapped by another tool that
|
|
250
|
+
* provides configuration externally (e.g. a meta-framework adapter).
|
|
251
|
+
* @default false
|
|
252
|
+
*/
|
|
253
|
+
silent?: boolean;
|
|
247
254
|
}
|
|
248
255
|
interface MiddlewareOptions<A extends RpcPluginOptions["adapter"] = "express"> {
|
|
249
256
|
/**
|
|
@@ -319,22 +326,22 @@ interface MiddlewareOptions<A extends RpcPluginOptions["adapter"] = "express"> {
|
|
|
319
326
|
* Enables multiple RPC instances with different prefixes to coexist
|
|
320
327
|
* without name collisions.
|
|
321
328
|
*/
|
|
322
|
-
declare const serverFunctionsByPrefix: Map<string, Map<string, ServerFnEntry>>;
|
|
329
|
+
export declare const serverFunctionsByPrefix: Map<string, Map<string, ServerFnEntry>>;
|
|
323
330
|
/**
|
|
324
331
|
* Gets or creates the function map for a specific prefix.
|
|
325
332
|
* @param prefix - The RPC prefix (e.g., "__rpc", "v1:rpc", "admin:rpc")
|
|
326
333
|
* @returns Map of function names to ServerFnEntry for that prefix
|
|
327
334
|
*/
|
|
328
|
-
declare const getFunctionsForPrefix: (prefix: string) => Map<string, ServerFnEntry>;
|
|
335
|
+
export declare const getFunctionsForPrefix: (prefix: string) => Map<string, ServerFnEntry>;
|
|
329
336
|
/**
|
|
330
337
|
* Backward compatibility: default map for the default prefix.
|
|
331
338
|
* Legacy code can still use serverFunctionsMap.set(name, entry).
|
|
332
339
|
*/
|
|
333
|
-
declare const serverFunctionsMap: Map<string, ServerFnEntry>;
|
|
340
|
+
export declare const serverFunctionsMap: Map<string, ServerFnEntry>;
|
|
334
341
|
//#endregion
|
|
335
342
|
//#region src/scanForServerFiles.d.ts
|
|
336
343
|
/** Absolute ids (normalized) of the scanned server function files. */
|
|
337
|
-
declare const scannedServerFiles: Set<string>;
|
|
344
|
+
export declare const scannedServerFiles: Set<string>;
|
|
338
345
|
/**
|
|
339
346
|
* Scans `src/api/` (or an explicit `scanRoot`) for server function files
|
|
340
347
|
* and populates the server functions map (scoped by rpcPrefix) with their exported functions.
|
|
@@ -346,13 +353,13 @@ declare const scannedServerFiles: Set<string>;
|
|
|
346
353
|
* @param initialCfg - Optional Vite config overrides (root, base, server, serverFiles, scanRoot)
|
|
347
354
|
* @param devServer - Optional running Vite dev server instance; when provided, skips creating a new one
|
|
348
355
|
*/
|
|
349
|
-
declare const scanForServerFiles: (initialCfg?: ScanConfig, devServer?: ViteDevServer) => Promise<void>;
|
|
356
|
+
export declare const scanForServerFiles: (initialCfg?: ScanConfig, devServer?: ViteDevServer) => Promise<void>;
|
|
350
357
|
//#endregion
|
|
351
358
|
//#region src/createFunction.d.ts
|
|
352
359
|
/**
|
|
353
360
|
* Extended options for createServerFunction, including rpcPrefix for multi-instance support.
|
|
354
361
|
*/
|
|
355
|
-
interface CreateServerFunctionOptions extends Partial<ServerFunctionOptions> {
|
|
362
|
+
export interface CreateServerFunctionOptions extends Partial<ServerFunctionOptions> {
|
|
356
363
|
/**
|
|
357
364
|
* RPC prefix for this function. Enables multiple RPC instances with different prefixes.
|
|
358
365
|
* When using multi-prefix setup, functions with the same name but different prefixes
|
|
@@ -385,7 +392,7 @@ interface CreateServerFunctionOptions extends Partial<ServerFunctionOptions> {
|
|
|
385
392
|
* @param fnOptions - Optional contentType, credentials, and rpcPrefix settings
|
|
386
393
|
* @returns A client stub with `data` promise and `cancel` method, auto-registered in the server map
|
|
387
394
|
*/
|
|
388
|
-
declare function createServerFunction<TArgs extends JsonArray = JsonArray, TResult
|
|
395
|
+
export declare function createServerFunction<TArgs extends JsonArray = JsonArray, TResult = JsonValue>(name: string, handler: ServerFunctionInit<TArgs, TResult>, fnOptions?: CreateServerFunctionOptions): ClientFunction<TArgs, TResult>;
|
|
389
396
|
//#endregion
|
|
390
397
|
//#region src/getClientModules.d.ts
|
|
391
398
|
/**
|
|
@@ -395,20 +402,20 @@ declare function createServerFunction<TArgs extends JsonArray = JsonArray, TResu
|
|
|
395
402
|
* @param initialOptions - Plugin options containing rpcPrefix and optional adapter
|
|
396
403
|
* @returns A string of JavaScript code with all client RPC modules and their import dependencies
|
|
397
404
|
*/
|
|
398
|
-
declare const getClientModules: (initialOptions: RpcPluginOptionsInternal) => string;
|
|
405
|
+
export declare const getClientModules: (initialOptions: RpcPluginOptionsInternal) => string;
|
|
399
406
|
//#endregion
|
|
400
407
|
//#region src/server-helpers.d.ts
|
|
401
408
|
/**
|
|
402
409
|
* Recursively walks `dir` and collects absolute paths to files whose
|
|
403
410
|
* basename matches the `*.server.{ts,js,mjs,mts}` glob pattern.
|
|
404
411
|
*/
|
|
405
|
-
declare const walkGlobFiles: (dir: string) => Promise<string[]>;
|
|
412
|
+
export declare const walkGlobFiles: (dir: string) => Promise<string[]>;
|
|
406
413
|
/**
|
|
407
414
|
* A typed error thrown from server functions.
|
|
408
415
|
* The middleware serializes the `message` and `code` in the response,
|
|
409
416
|
* allowing clients to recognise and handle specific error conditions.
|
|
410
417
|
*/
|
|
411
|
-
declare class RPCError extends Error {
|
|
418
|
+
export declare class RPCError extends Error {
|
|
412
419
|
/** Machine-readable error code (e.g. "VALIDATION_FAILED", "UNAUTHORIZED") */
|
|
413
420
|
code: string;
|
|
414
421
|
/** Optional diagnostic payload */
|
|
@@ -423,14 +430,14 @@ declare class RPCError extends Error {
|
|
|
423
430
|
* information disclosure; server-side diagnostics are preserved via the
|
|
424
431
|
* middleware's `console.error` logging.
|
|
425
432
|
*/
|
|
426
|
-
declare const formatError: (err: unknown, isProduction: boolean) => JsonObject;
|
|
433
|
+
export declare const formatError: (err: unknown, isProduction: boolean) => JsonObject;
|
|
427
434
|
/**
|
|
428
435
|
* Checks whether a content type maps to a form encoding
|
|
429
436
|
* (`multipart/form-data` or `application/x-www-form-urlencoded`).
|
|
430
437
|
* Form-declared functions accept either encoding so native browser
|
|
431
438
|
* submissions (urlencoded) keep working without JavaScript.
|
|
432
439
|
*/
|
|
433
|
-
declare const isFormContentType: (contentType: string) => boolean;
|
|
440
|
+
export declare const isFormContentType: (contentType: string) => boolean;
|
|
434
441
|
/**
|
|
435
442
|
* Detects whether an incoming request's `Content-Type` header conflicts
|
|
436
443
|
* with the function's declared content type. JSON and text functions are
|
|
@@ -441,7 +448,7 @@ declare const isFormContentType: (contentType: string) => boolean;
|
|
|
441
448
|
* @param declared - The declared `contentType` from the server function options
|
|
442
449
|
* @param rawHeader - The raw `Content-Type` request header, if present
|
|
443
450
|
*/
|
|
444
|
-
declare const hasContentTypeMismatch: (declared: ContentType, rawHeader: string | undefined) => boolean;
|
|
451
|
+
export declare const hasContentTypeMismatch: (declared: ContentType, rawHeader: string | undefined) => boolean;
|
|
445
452
|
/**
|
|
446
453
|
* Escapes special regex metacharacters in a string.
|
|
447
454
|
* Used to safely embed user-configurable values (like rpcPrefix) into regular expressions,
|
|
@@ -449,7 +456,7 @@ declare const hasContentTypeMismatch: (declared: ContentType, rawHeader: string
|
|
|
449
456
|
* @param s - The raw string to escape
|
|
450
457
|
* @returns The escaped string safe for use in new RegExp()
|
|
451
458
|
*/
|
|
452
|
-
declare function escapeRegExp(s: string): string;
|
|
459
|
+
export declare function escapeRegExp(s: string): string;
|
|
453
460
|
/**
|
|
454
461
|
* Parses a raw request URL against a fixed base without ever throwing.
|
|
455
462
|
* Malformed request-targets (e.g. `/\`, `//`, `/\/`) make the WHATWG URL
|
|
@@ -463,10 +470,10 @@ declare function escapeRegExp(s: string): string;
|
|
|
463
470
|
* @param base - Optional base URL, defaults to a fixed localhost origin
|
|
464
471
|
* @returns A URL object; never throws
|
|
465
472
|
*/
|
|
466
|
-
declare const safeURL: (rawUrl: string, base?: string) => URL;
|
|
473
|
+
export declare const safeURL: (rawUrl: string, base?: string) => URL;
|
|
467
474
|
/** Global rpcPrefix from the last loaded config / middleware — fallback for functions without explicit prefix. */
|
|
468
|
-
declare const getGlobalPrefix: () => string | undefined;
|
|
469
|
-
declare const setGlobalPrefix: (prefix: string | undefined) => void;
|
|
475
|
+
export declare const getGlobalPrefix: () => string | undefined;
|
|
476
|
+
export declare const setGlobalPrefix: (prefix: string | undefined) => void;
|
|
470
477
|
//#endregion
|
|
471
478
|
//#region src/context.d.ts
|
|
472
479
|
/**
|
|
@@ -484,7 +491,7 @@ declare const setGlobalPrefix: (prefix: string | undefined) => void;
|
|
|
484
491
|
* - Hono: `c` (the Hono `Context`) plus `nativeEvent = c`
|
|
485
492
|
* - h3: `event` (the h3 `H3Event`) plus `nativeEvent = event`
|
|
486
493
|
*/
|
|
487
|
-
interface RequestEvent {
|
|
494
|
+
export interface RequestEvent {
|
|
488
495
|
/** Adapter-specific native event kept for deep framework access */
|
|
489
496
|
nativeEvent?: unknown;
|
|
490
497
|
/** Adapter request object */
|
|
@@ -541,13 +548,13 @@ interface RequestEvent {
|
|
|
541
548
|
* @param init - The request context for the duration of `cb`
|
|
542
549
|
* @param cb - The work that needs access to the request context
|
|
543
550
|
*/
|
|
544
|
-
declare const provideRequestContext: <T>(init: RequestEvent, cb: () => T) => T;
|
|
551
|
+
export declare const provideRequestContext: <T>(init: RequestEvent, cb: () => T) => T;
|
|
545
552
|
/**
|
|
546
553
|
* Returns the current request context, or throws when called outside of a
|
|
547
554
|
* request (e.g. module scope or a background task).
|
|
548
555
|
* @throws When no request context is established
|
|
549
556
|
*/
|
|
550
|
-
declare const getRequestContext: () => RequestEvent;
|
|
557
|
+
export declare const getRequestContext: () => RequestEvent;
|
|
551
558
|
/**
|
|
552
559
|
* Redirects the current request to `location`. Reads the adapter-bound
|
|
553
560
|
* `redirect` from the current request context — callable from anywhere inside
|
|
@@ -556,7 +563,7 @@ declare const getRequestContext: () => RequestEvent;
|
|
|
556
563
|
* @param status - HTTP status code, defaults to `303 See Other`
|
|
557
564
|
* @throws When called outside of a request
|
|
558
565
|
*/
|
|
559
|
-
declare const redirect: (location: string, status?: number) => void;
|
|
566
|
+
export declare const redirect: (location: string, status?: number) => void;
|
|
560
567
|
/**
|
|
561
568
|
* Sends a raw JSON response for the current request, bypassing the standard
|
|
562
569
|
* `{ data }` shape. Reads the adapter-bound `send` from the current request
|
|
@@ -568,14 +575,14 @@ declare const redirect: (location: string, status?: number) => void;
|
|
|
568
575
|
* @param headers - Optional response headers
|
|
569
576
|
* @throws When called outside of a request
|
|
570
577
|
*/
|
|
571
|
-
declare const sendResponse: (status: number, body: JsonValue, headers?: Record<string, string>) => void;
|
|
578
|
+
export declare const sendResponse: (status: number, body: JsonValue, headers?: Record<string, string>) => void;
|
|
572
579
|
/**
|
|
573
580
|
* Normalized, adapter-agnostic view of the current request. Reads the request
|
|
574
581
|
* object off the current request context and normalizes it across the five
|
|
575
582
|
* adapter request shapes (Express `req`, Fastify `req`, Koa `ctx.req`,
|
|
576
583
|
* Hono `c.req`, h3 `event.req`) so middleware can be written once.
|
|
577
584
|
*/
|
|
578
|
-
interface RequestMeta {
|
|
585
|
+
export interface RequestMeta {
|
|
579
586
|
/** HTTP method, upper-cased (e.g. "GET", "POST") */
|
|
580
587
|
method: string;
|
|
581
588
|
/** URL pathname (e.g. "/__rpc/greet") */
|
|
@@ -601,13 +608,12 @@ interface RequestMeta {
|
|
|
601
608
|
* @param event - The request context to read, typically the result of
|
|
602
609
|
* {@link getRequestContext}
|
|
603
610
|
*/
|
|
604
|
-
declare const getRequestMeta: (event: RequestEvent) => RequestMeta;
|
|
611
|
+
export declare const getRequestMeta: (event: RequestEvent) => RequestMeta;
|
|
605
612
|
//#endregion
|
|
606
613
|
//#region src/options.d.ts
|
|
607
|
-
declare const defaultServerFnOptions: ServerFunctionOptions;
|
|
608
|
-
declare const defaultPrefix = "__rpc";
|
|
609
|
-
declare const defaultRPCOptions: RpcPluginOptions;
|
|
610
|
-
declare const defaultMiddlewareOptions: MiddlewareOptions;
|
|
614
|
+
export declare const defaultServerFnOptions: ServerFunctionOptions;
|
|
615
|
+
export declare const defaultPrefix = "__rpc";
|
|
616
|
+
export declare const defaultRPCOptions: RpcPluginOptions;
|
|
617
|
+
export declare const defaultMiddlewareOptions: MiddlewareOptions;
|
|
611
618
|
//#endregion
|
|
612
|
-
export { CreateServerFunctionOptions, RPCError, RequestEvent, RequestMeta, createServerFunction, defaultMiddlewareOptions, defaultPrefix, defaultRPCOptions, defaultServerFnOptions, escapeRegExp, formatError, getClientModules, getFunctionsForPrefix, getGlobalPrefix, getRequestContext, getRequestMeta, hasContentTypeMismatch, isFormContentType, provideRequestContext, redirect, safeURL, scanForServerFiles, scannedServerFiles, sendResponse, serverFunctionsByPrefix, serverFunctionsMap, setGlobalPrefix, walkGlobFiles };
|
|
613
619
|
//# sourceMappingURL=server.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.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/h3/types.d.ts","../../src/types.d.ts","../../src/functionsMap.ts","../../src/scanForServerFiles.ts","../../src/createFunction.ts","../../src/getClientModules.ts","../../src/server-helpers.ts","../../src/context.ts","../../src/options.ts"],"mappings":";;;;;;;;;;;;;;;;UA+BiB;;;;;;;EAOf,UACE,KAAK,kBAAkB,SACvB,KAAK,iBAAiB,YACtB,MAAM,QAAQ,eAAe,iBAC1B;;;;;;;UCzBU;;EAEf,SAAS;;;;;;;UCmCM;;;;;;;EAOf,UACE,KAAK,gBACL,KAAK,cACL,MAAM,4BACH;;;;;;;UCzCU;;;;;;EAMf,UAAU,KAAK,SAAS,MAAM,SAAS;;;;;;;UClBxB;;;;;;EAMf,SAAS;;;;;;;;UCKM;;EAEf,SAAS;;EAET,MAAM;;EAEN,SAAS;;EAET,KAAK;;EAEL,IAAI;;;;;KAiCM;;;;KASA;;;;;UAkBK;;;;;EAKf,aAAa;;;;;EAKb,cAAc;;;;;;;EAOd;;;;;EAKA;;;;;;KAOU;;;;KAIA;GAAgB,cAAc,YAAY;;;;;KAI1C,aAAa,WAAW;;;;KAIxB,YAAY,gBAAgB,YAAY;;;;;KAsCxC,mBACV,cAAc,WAAW,YAAY,WACrC,
|
|
1
|
+
{"version":3,"file":"server.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/h3/types.d.ts","../../src/types.d.ts","../../src/functionsMap.ts","../../src/scanForServerFiles.ts","../../src/createFunction.ts","../../src/getClientModules.ts","../../src/server-helpers.ts","../../src/context.ts","../../src/options.ts"],"mappings":"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"}
|
package/dist/server/server.mjs
CHANGED
|
@@ -532,7 +532,7 @@ const getRequestMeta = (event) => {
|
|
|
532
532
|
const method = (req?.method ?? "GET").toUpperCase();
|
|
533
533
|
const rawUrl = req?.originalUrl ?? req?.url ?? req?.path ?? "";
|
|
534
534
|
const url = safeURL(rawUrl);
|
|
535
|
-
const headers = toHeaderRecord(req?.headers);
|
|
535
|
+
const headers = toHeaderRecord(req?.headers ?? req?.raw?.headers);
|
|
536
536
|
const hostHeader = pickHeader(headers, "host");
|
|
537
537
|
return {
|
|
538
538
|
method,
|