@thednp/rpc 0.0.4 → 0.0.6

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.
@@ -5,6 +5,7 @@ import "hono";
5
5
  import "@hono/node-server";
6
6
  import "hono/factory";
7
7
  import "fastify";
8
+ import "fastify-plugin";
8
9
  import "koa";
9
10
  //#region src/types.d.ts
10
11
  /**
@@ -83,6 +84,22 @@ type ClientFunctionWithOptions = ClientFunction & {
83
84
  /** Per-function content type and credentials options */
84
85
  options?: ServerFunctionOptions;
85
86
  };
87
+ /**
88
+ * Internal plugin options accepted by `getClientModules`.
89
+ */
90
+ interface RpcPluginOptionsInternal {
91
+ /** RPC endpoint prefix (e.g. "__rpc") */
92
+ rpcPrefix: string;
93
+ /** Framework adapter name */
94
+ adapter?: string | undefined;
95
+ }
96
+ /**
97
+ * Partial Vite config used when scanning server files outside a running dev server.
98
+ */
99
+ type ScanConfig = Pick<ResolvedConfig, "root" | "base"> & {
100
+ /** Vite server options override (e.g. `middlewareMode`) */
101
+ server?: Partial<ResolvedConfig["server"]>;
102
+ };
86
103
  /**
87
104
  * Entry in the server functions map: registered name, client handler,
88
105
  * optional per-function options, and the original export name.
@@ -128,13 +145,6 @@ interface RpcPluginOptions {
128
145
  declare const serverFunctionsMap: Map<string, ServerFnEntry>;
129
146
  //#endregion
130
147
  //#region src/scanForServerFiles.d.ts
131
- /**
132
- * Partial Vite config used when scanning server files outside a running dev server.
133
- */
134
- type ScanConfig = Pick<ResolvedConfig, "root" | "base"> & {
135
- /** Vite server options override (e.g. `middlewareMode`) */
136
- server?: Partial<ResolvedConfig["server"]>;
137
- };
138
148
  /**
139
149
  * Scans `src/api/` for server function files (`server.ts`, `server.js`, `server.mjs`, `server.mts`)
140
150
  * and populates the global `serverFunctionsMap` with their exported functions.
@@ -157,15 +167,6 @@ declare const scanForServerFiles: (initialCfg?: ScanConfig, devServer?: ViteDevS
157
167
  declare function createServerFunction<TArgs extends JsonArray = JsonArray, TResult extends JsonValue = JsonValue>(name: string, handler: ServerFunctionInit<TArgs, TResult>, fnOptions?: Partial<ServerFunctionOptions>): ClientFunction<TArgs, TResult>;
158
168
  //#endregion
159
169
  //#region src/getClientModules.d.ts
160
- /**
161
- * Internal plugin options accepted by `getClientModules`.
162
- */
163
- interface RpcPluginOptionsInternal {
164
- /** RPC endpoint prefix (e.g. "__rpc") */
165
- rpcPrefix: string;
166
- /** Framework adapter name */
167
- adapter?: string | undefined;
168
- }
169
170
  /**
170
171
  * Generates the complete client-side module bundle by iterating all registered server functions
171
172
  * and producing fetch-based stubs for each. The result is transformed by Vite (or Oxc) during
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.mts","names":[],"sources":["../../src/types.d.ts","../../src/functionsMap.ts","../../src/scanForServerFiles.ts","../../src/createFunction.ts","../../src/getClientModules.ts","../../src/options.ts"],"mappings":";;;;;;;;;;;;KAyDY;;;;KAKA;;;;;UAaK;;;;;EAKf,aAAa;;;;;EAKb,cAAc;;;;;;;EAOd;;;;;;KAOU;;;;KAIA;GAAgB,cAAc,YAAY;;;;;KAI1C,YAAY;;;;KAIZ,YAAY,gBAAgB,YAAY;;;;;KAsCxC,mBACV,cAAc,YAAY,WAC1B,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;;;;;;UAOK;;EAEf;;EAEA,SAAS;;EAET,UAAU;;EAEV;;;;;;;;UASe;;;;;;;;;;;EAWf;;;;;;;EAQA;;;;cC1NW,oBAAoB,YAAY;;;;;;KCWxC,aAAa,KAAK;;EAErB,SAAS,QAAQ;;;;;;;;;cAWN,qBAAkB,aAChB,YAAU,YACX,kBACX;;;;;;;;;;;;iBCRa,qBACd,cAAc,YAAY,WAC1B,gBAAgB,YAAY,WAE5B,cACA,SAAS,mBAAmB,OAAO,UACnC,YAAW,QAAQ,yBAClB,eAAe,OAAO;;;;;;UCzBf;;EAER;;EAEA;;;;;;;;;cA2EW,mBAAgB,gBACX;;;cC7EL;;;;;cAMA,mBAAmB;cAKnB"}
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../src/types.d.ts","../../src/functionsMap.ts","../../src/scanForServerFiles.ts","../../src/createFunction.ts","../../src/getClientModules.ts","../../src/options.ts"],"mappings":";;;;;;;;;;;;;KA2DY;;;;KAKA;;;;;UAaK;;;;;EAKf,aAAa;;;;;EAKb,cAAc;;;;;;;EAOd;;;;;;KAOU;;;;KAIA;GAAgB,cAAc,YAAY;;;;;KAI1C,YAAY;;;;KAIZ,YAAY,gBAAgB,YAAY;;;;;KAsCxC,mBACV,cAAc,YAAY,WAC1B,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;;EAE5B,SAAS,QAAQ;;;;;;UAOF;;EAEf;;EAEA,SAAS;;EAET,UAAU;;EAEV;;;;;;;;UASe;;;;;;;;;;;EAWf;;;;;;;EAQA;;;;cC9OW,oBAAoB,YAAY;;;;;;;;;;cCgBhC,qBAAkB,aAChB,YAAU,YACX,kBACX;;;;;;;;;;;;iBCAa,qBACd,cAAc,YAAY,WAC1B,gBAAgB,YAAY,WAE5B,cACA,SAAS,mBAAmB,OAAO,UACnC,YAAW,QAAQ,yBAClB,eAAe,OAAO;;;;;;;;;;cCkDZ,mBAAgB,gBACX;;;cCzEL;;;;;cAMA,mBAAmB;cAKnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":[],"sources":["../../src/functionsMap.ts","../../src/constants.ts","../../src/scanForServerFiles.ts","../../src/options.ts","../../src/createFunction.ts","../../src/validate.ts","../../src/getClientModules.ts"],"sourcesContent":["import type { ServerFnEntry } from \"./types.d.ts\";\n\nexport const serverFunctionsMap: Map<string, ServerFnEntry> = new Map<\n string,\n ServerFnEntry\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","import type { ResolvedConfig, ViteDevServer } from \"vite\";\nimport type { ClientFunctionWithOptions } from \"./types.d.ts\";\nimport { createServer } from \"vite\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\n\nimport { serverFunctionsMap } from \"./functionsMap.ts\";\nimport { ERROR_LOADING_FILE, NO_SERVER_FUNCTION_FOUND } from \"./constants.ts\";\n\n/**\n * Partial Vite config used when scanning server files outside a running dev server.\n */\ntype ScanConfig = Pick<ResolvedConfig, \"root\" | \"base\"> & {\n /** Vite server options override (e.g. `middlewareMode`) */\n server?: Partial<ResolvedConfig[\"server\"]>;\n};\n\nlet isScanned = false;\n/**\n * Scans `src/api/` for server function files (`server.ts`, `server.js`, `server.mjs`, `server.mts`)\n * and populates the global `serverFunctionsMap` with their exported functions.\n * Uses Vite's SSR module loading to resolve and execute each file.\n * @param initialCfg - Optional Vite config overrides (root, base, server)\n * @param devServer - Optional running Vite dev server instance; when provided, skips creating a new one\n */\nexport const scanForServerFiles = async (\n initialCfg?: ScanConfig,\n devServer?: ViteDevServer,\n): Promise<void> => {\n if (isScanned && !devServer) {\n return;\n }\n const config = (!initialCfg && !devServer) || !initialCfg\n ? {\n root: process.cwd(),\n base: process.env.BASE || \"/\",\n server: { middlewareMode: true },\n }\n : {\n ...initialCfg,\n root: process.cwd(),\n };\n\n let server = devServer;\n if (!server) {\n server = await createServer({\n server: config.server,\n appType: \"custom\",\n base: config.base,\n root: config.root,\n });\n }\n\n const svFiles = [\"server.ts\", \"server.js\", \"server.mjs\", \"server.mts\"];\n const apiDir = join(config.root, \"src\", \"api\");\n let files: string[];\n try {\n files = (await readdir(apiDir, { withFileTypes: true }))\n .filter((f) => svFiles.includes(f.name))\n .map((f) => join(apiDir, f.name));\n } catch (_e) {\n files = [];\n }\n\n try {\n for (const file of files) {\n try {\n const moduleExports = (await server.ssrLoadModule(file)) as Record<\n string,\n ClientFunctionWithOptions\n >;\n const moduleEntries = Object.entries(moduleExports);\n if (!moduleEntries.length) {\n console.warn(NO_SERVER_FUNCTION_FOUND);\n return;\n }\n\n for (const [exportName, exportValue] of moduleEntries) {\n // const registeredName = exportValue?.name ?? exportName;\n const registeredName = exportValue.name;\n serverFunctionsMap.set(registeredName, {\n name: registeredName,\n handler: exportValue,\n options: exportValue?.options,\n exportName,\n });\n }\n } catch (error) {\n console.error(ERROR_LOADING_FILE, file, error);\n }\n }\n } finally {\n if (!devServer && server) {\n await server.close();\n }\n isScanned = true;\n }\n};\n","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","// /@thednp/rpc/src/createFn.ts\nimport type {\n ClientFunction,\n JsonArray,\n JsonValue,\n ServerFunctionInit,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\nimport { serverFunctionsMap } from \"./functionsMap.ts\";\nimport { defaultServerFnOptions } from \"./options.ts\";\nimport { OPERATION_ABORTED } from \"./constants.ts\";\n\n/**\n * Creates a server-side RPC function.\n * Registers the function in the server functions map and returns a client-compatible\n * wrapper that exposes `data` (Promise) and `cancel` (function) for request lifecycle control.\n * @param name - Unique identifier used by the RPC router to dispatch requests\n * @param handler - The actual implementation receiving an AbortSignal followed by JSON-serializable arguments\n * @param fnOptions - Optional contentType and credentials settings\n * @returns A client stub with `data` promise and `cancel` method, auto-registered in the server map\n */\nexport function createServerFunction<\n TArgs extends JsonArray = JsonArray,\n TResult extends JsonValue = JsonValue,\n>(\n name: string,\n handler: ServerFunctionInit<TArgs, TResult>,\n fnOptions: Partial<ServerFunctionOptions> = {},\n): ClientFunction<TArgs, TResult> {\n const options = Object.assign({}, defaultServerFnOptions, fnOptions);\n\n const wrappedFunction: ClientFunction<TArgs, TResult> = (...args: TArgs) => {\n const controller = new AbortController();\n const cancel = (reason: string) => controller.abort(reason);\n\n const fetcher = async () => {\n if (controller.signal.aborted) {\n throw new Error(OPERATION_ABORTED);\n }\n\n return await handler(controller.signal, ...args);\n };\n\n return {\n data: fetcher(),\n cancel,\n };\n };\n\n Object.defineProperties(wrappedFunction, {\n name: { value: name, enumerable: true, configurable: false },\n options: { value: options, enumerable: true, configurable: false },\n });\n\n serverFunctionsMap.set(\n name,\n {\n name,\n handler: wrappedFunction as never,\n options,\n },\n );\n\n return wrappedFunction;\n}\n","import type { Credentials } from \"./types.d.ts\";\nimport { INVALID_IDENTIFIER, INVALID_PATH_SEGMENT } from \"./constants.ts\";\n\nconst SAFE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst SAFE_PATH_SEGMENT = /^[A-Za-z0-9_$][A-Za-z0-9_$/-]*$/;\nconst CREDENTIALS_VALUES: readonly Credentials[] = [\n \"same-origin\",\n \"include\",\n \"omit\",\n];\n\n/**\n * Validates that a string is a safe JavaScript identifier.\n * Used to prevent code injection when interpolating export names into generated client code.\n * @param name - The string to validate\n * @param label - Human-readable label for error messages (e.g. \"export name\")\n * @returns The validated name if it passes\n * @throws Error if the name contains characters outside /^[A-Za-z_$][A-Za-z0-9_$]*$/\n */\nexport function validateIdentifier(name: string, label: string): string {\n if (!SAFE_IDENTIFIER.test(name)) {\n throw new Error(INVALID_IDENTIFIER(label, name));\n }\n return name;\n}\n\n/**\n * Validates that a string is a safe path segment for RPC routing.\n * Allows alphanumeric characters, underscores, dollar signs, hyphens, and forward slashes.\n * @param segment - The string to validate\n * @param label - Human-readable label for error messages (e.g. \"rpcPrefix\")\n * @returns The validated segment if it passes\n * @throws Error if the segment contains disallowed characters\n */\nexport function validatePathSegment(segment: string, label: string): string {\n if (!SAFE_PATH_SEGMENT.test(segment)) {\n throw new Error(INVALID_PATH_SEGMENT(label, segment));\n }\n return segment;\n}\n\n/**\n * Validates and normalizes the credentials option.\n * Accepts \"same-origin\", \"include\", or \"omit\"; defaults to \"same-origin\" when undefined.\n * @param value - Credentials value to validate\n * @returns The validated credentials string\n * @throws Error if the value is not one of the accepted credentials\n */\nexport function validateCredentials(value?: string): Credentials {\n const creds = value || \"same-origin\";\n if (!CREDENTIALS_VALUES.includes(creds as Credentials)) {\n throw new Error(\n `Invalid credentials: \"${value}\" must be one of ${\n CREDENTIALS_VALUES.join(\", \")\n }`,\n );\n }\n return creds as Credentials;\n}\n\n/**\n * Validates and normalizes the HTTP method option for a server function.\n * Accepts \"GET\" or \"POST\" (case-insensitive); defaults to \"POST\" when undefined.\n * @param value - Method value to validate\n * @returns The validated uppercase method string\n * @throws Error if the value is not \"GET\" or \"POST\"\n */\nexport function validateMethod(value?: string): \"GET\" | \"POST\" {\n const method = (value || \"POST\").toUpperCase();\n if (method !== \"GET\" && method !== \"POST\") {\n throw new Error(`Invalid method: \"${value}\" must be one of GET, POST`);\n }\n return method;\n}\n","/**\n * Internal plugin options accepted by `getClientModules`.\n */\ninterface RpcPluginOptionsInternal {\n /** RPC endpoint prefix (e.g. \"__rpc\") */\n rpcPrefix: string;\n /** Framework adapter name */\n adapter?: string | undefined;\n}\n\nimport type { ServerFunctionOptions } from \"./types.d.ts\";\nimport { serverFunctionsMap } from \"./functionsMap.ts\";\nimport {\n validateCredentials,\n validateIdentifier,\n validateMethod,\n validatePathSegment,\n} from \"./validate.ts\";\n\n/**\n * Generates a JavaScript client module string for a single server function.\n * All interpolated values are validated to prevent code injection.\n * @param fnName - Registered RPC function name (validated as path segment)\n * @param fnEntry - Export name used in the generated module (validated as identifier)\n * @param options - Content type, credentials, and RPC prefix settings\n * @returns A string of JavaScript code exporting the client stub\n */\nconst getModule = (\n fnName: string,\n fnEntry: string,\n options: Partial<ServerFunctionOptions> & {\n contentType: ServerFunctionOptions[\"contentType\"];\n rpcPrefix: string;\n },\n): string => {\n // Validate all interpolated strings to prevent code injection\n const safeFnName = validatePathSegment(fnName, \"function name\");\n const safeFnEntry = validateIdentifier(fnEntry, \"export name\");\n const safePrefix = validatePathSegment(options.rpcPrefix, \"rpcPrefix\");\n const credentials = validateCredentials(options.credentials);\n const method = validateMethod(options.method);\n let body = \"\";\n let headers = \"{}\";\n switch (options.contentType) {\n case \"text/plain\":\n {\n body = `args[0]`;\n headers = `{ 'Content-Type': 'text/plain' }`;\n }\n break;\n default: {\n body = `JSON.stringify(args)`;\n headers = `{ 'Content-Type': 'application/json' }`;\n }\n }\n // GET requests cannot carry a body: args travel as a JSON query parameter\n if (method === \"GET\") {\n body = `JSON.stringify(args)`;\n headers = `{}`;\n }\n\n const output = `\nexport const ${safeFnEntry} = (...args) => {\n const body = ${body};\n const headers = ${headers};\n const prefix = \"${safePrefix}\";\n const name = \"${safeFnName}\";\n const credentials = \"${credentials}\";\n const method = \"${method}\";\n return innerModule(body, headers, credentials, prefix, name, method);\n}`;\n\n return output.trim();\n};\n\n/**\n * Generates the complete client-side module bundle by iterating all registered server functions\n * and producing fetch-based stubs for each. The result is transformed by Vite (or Oxc) during\n * the dev server or production build.\n * @param initialOptions - Plugin options containing rpcPrefix and optional adapter\n * @returns A string of JavaScript code with all client RPC modules and their import dependencies\n */\nexport const getClientModules = (\n initialOptions: RpcPluginOptionsInternal,\n): string => {\n // Validate prefix once at the top level\n validatePathSegment(initialOptions.rpcPrefix, \"rpcPrefix\");\n\n return `\n// Client-side RPC modules\nimport { innerModule } from \"@thednp/rpc/helpers\";\n${\n Array.from(serverFunctionsMap.entries())\n .filter(([, entry]) => entry.exportName)\n .map(([registeredName, entry]) =>\n getModule(registeredName, entry.exportName!, {\n ...initialOptions,\n ...((entry.options as ServerFunctionOptions) || {}),\n })\n )\n .join(\"\\n\")\n }\n`.trim();\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":[],"sources":["../../src/functionsMap.ts","../../src/constants.ts","../../src/scanForServerFiles.ts","../../src/options.ts","../../src/createFunction.ts","../../src/validate.ts","../../src/getClientModules.ts"],"sourcesContent":["import type { ServerFnEntry } from \"./types.d.ts\";\n\nexport const serverFunctionsMap: Map<string, ServerFnEntry> = new Map<\n string,\n ServerFnEntry\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","import type { ViteDevServer } from \"vite\";\nimport type { ClientFunctionWithOptions, ScanConfig } from \"./types.d.ts\";\nimport { createServer } from \"vite\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\n\nimport { serverFunctionsMap } from \"./functionsMap.ts\";\nimport { ERROR_LOADING_FILE, NO_SERVER_FUNCTION_FOUND } from \"./constants.ts\";\n\nlet isScanned = false;\n/**\n * Scans `src/api/` for server function files (`server.ts`, `server.js`, `server.mjs`, `server.mts`)\n * and populates the global `serverFunctionsMap` with their exported functions.\n * Uses Vite's SSR module loading to resolve and execute each file.\n * @param initialCfg - Optional Vite config overrides (root, base, server)\n * @param devServer - Optional running Vite dev server instance; when provided, skips creating a new one\n */\nexport const scanForServerFiles = async (\n initialCfg?: ScanConfig,\n devServer?: ViteDevServer,\n): Promise<void> => {\n if (isScanned && !devServer) {\n return;\n }\n const config = (!initialCfg && !devServer) || !initialCfg\n ? {\n root: process.cwd(),\n base: process.env.BASE || \"/\",\n server: { middlewareMode: true },\n }\n : {\n ...initialCfg,\n root: process.cwd(),\n };\n\n let server = devServer;\n if (!server) {\n server = await createServer({\n server: config.server,\n appType: \"custom\",\n base: config.base,\n root: config.root,\n });\n }\n\n const svFiles = [\"server.ts\", \"server.js\", \"server.mjs\", \"server.mts\"];\n const apiDir = join(config.root, \"src\", \"api\");\n let files: string[];\n try {\n files = (await readdir(apiDir, { withFileTypes: true }))\n .filter((f) => svFiles.includes(f.name))\n .map((f) => join(apiDir, f.name));\n } catch (_e) {\n files = [];\n }\n\n try {\n for (const file of files) {\n try {\n const moduleExports = (await server.ssrLoadModule(file)) as Record<\n string,\n ClientFunctionWithOptions\n >;\n const moduleEntries = Object.entries(moduleExports);\n if (!moduleEntries.length) {\n console.warn(NO_SERVER_FUNCTION_FOUND);\n return;\n }\n\n for (const [exportName, exportValue] of moduleEntries) {\n // const registeredName = exportValue?.name ?? exportName;\n const registeredName = exportValue.name;\n serverFunctionsMap.set(registeredName, {\n name: registeredName,\n handler: exportValue,\n options: exportValue?.options,\n exportName,\n });\n }\n } catch (error) {\n console.error(ERROR_LOADING_FILE, file, error);\n }\n }\n } finally {\n if (!devServer && server) {\n await server.close();\n }\n isScanned = true;\n }\n};\n","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","// /@thednp/rpc/src/createFn.ts\nimport type {\n ClientFunction,\n JsonArray,\n JsonValue,\n ServerFunctionInit,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\nimport { serverFunctionsMap } from \"./functionsMap.ts\";\nimport { defaultServerFnOptions } from \"./options.ts\";\nimport { OPERATION_ABORTED } from \"./constants.ts\";\n\n/**\n * Creates a server-side RPC function.\n * Registers the function in the server functions map and returns a client-compatible\n * wrapper that exposes `data` (Promise) and `cancel` (function) for request lifecycle control.\n * @param name - Unique identifier used by the RPC router to dispatch requests\n * @param handler - The actual implementation receiving an AbortSignal followed by JSON-serializable arguments\n * @param fnOptions - Optional contentType and credentials settings\n * @returns A client stub with `data` promise and `cancel` method, auto-registered in the server map\n */\nexport function createServerFunction<\n TArgs extends JsonArray = JsonArray,\n TResult extends JsonValue = JsonValue,\n>(\n name: string,\n handler: ServerFunctionInit<TArgs, TResult>,\n fnOptions: Partial<ServerFunctionOptions> = {},\n): ClientFunction<TArgs, TResult> {\n const options = Object.assign({}, defaultServerFnOptions, fnOptions);\n\n const wrappedFunction: ClientFunction<TArgs, TResult> = (...args: TArgs) => {\n const controller = new AbortController();\n const cancel = (reason: string) => controller.abort(reason);\n\n const fetcher = async () => {\n if (controller.signal.aborted) {\n throw new Error(OPERATION_ABORTED);\n }\n\n return await handler(controller.signal, ...args);\n };\n\n return {\n data: fetcher(),\n cancel,\n };\n };\n\n Object.defineProperties(wrappedFunction, {\n name: { value: name, enumerable: true, configurable: false },\n options: { value: options, enumerable: true, configurable: false },\n });\n\n serverFunctionsMap.set(\n name,\n {\n name,\n handler: wrappedFunction as never,\n options,\n },\n );\n\n return wrappedFunction;\n}\n","import type { Credentials } from \"./types.d.ts\";\nimport { INVALID_IDENTIFIER, INVALID_PATH_SEGMENT } from \"./constants.ts\";\n\nconst SAFE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst SAFE_PATH_SEGMENT = /^[A-Za-z0-9_$][A-Za-z0-9_$/-]*$/;\nconst CREDENTIALS_VALUES: readonly Credentials[] = [\n \"same-origin\",\n \"include\",\n \"omit\",\n];\n\n/**\n * Validates that a string is a safe JavaScript identifier.\n * Used to prevent code injection when interpolating export names into generated client code.\n * @param name - The string to validate\n * @param label - Human-readable label for error messages (e.g. \"export name\")\n * @returns The validated name if it passes\n * @throws Error if the name contains characters outside /^[A-Za-z_$][A-Za-z0-9_$]*$/\n */\nexport function validateIdentifier(name: string, label: string): string {\n if (!SAFE_IDENTIFIER.test(name)) {\n throw new Error(INVALID_IDENTIFIER(label, name));\n }\n return name;\n}\n\n/**\n * Validates that a string is a safe path segment for RPC routing.\n * Allows alphanumeric characters, underscores, dollar signs, hyphens, and forward slashes.\n * @param segment - The string to validate\n * @param label - Human-readable label for error messages (e.g. \"rpcPrefix\")\n * @returns The validated segment if it passes\n * @throws Error if the segment contains disallowed characters\n */\nexport function validatePathSegment(segment: string, label: string): string {\n if (!SAFE_PATH_SEGMENT.test(segment)) {\n throw new Error(INVALID_PATH_SEGMENT(label, segment));\n }\n return segment;\n}\n\n/**\n * Validates and normalizes the credentials option.\n * Accepts \"same-origin\", \"include\", or \"omit\"; defaults to \"same-origin\" when undefined.\n * @param value - Credentials value to validate\n * @returns The validated credentials string\n * @throws Error if the value is not one of the accepted credentials\n */\nexport function validateCredentials(value?: string): Credentials {\n const creds = value || \"same-origin\";\n if (!CREDENTIALS_VALUES.includes(creds as Credentials)) {\n throw new Error(\n `Invalid credentials: \"${value}\" must be one of ${\n CREDENTIALS_VALUES.join(\", \")\n }`,\n );\n }\n return creds as Credentials;\n}\n\n/**\n * Validates and normalizes the HTTP method option for a server function.\n * Accepts \"GET\" or \"POST\" (case-insensitive); defaults to \"POST\" when undefined.\n * @param value - Method value to validate\n * @returns The validated uppercase method string\n * @throws Error if the value is not \"GET\" or \"POST\"\n */\nexport function validateMethod(value?: string): \"GET\" | \"POST\" {\n const method = (value || \"POST\").toUpperCase();\n if (method !== \"GET\" && method !== \"POST\") {\n throw new Error(`Invalid method: \"${value}\" must be one of GET, POST`);\n }\n return method;\n}\n","/**\n * @module Client module generation.\n */\nimport type {\n RpcPluginOptionsInternal,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\nimport { serverFunctionsMap } from \"./functionsMap.ts\";\nimport {\n validateCredentials,\n validateIdentifier,\n validateMethod,\n validatePathSegment,\n} from \"./validate.ts\";\n\n/**\n * Generates a JavaScript client module string for a single server function.\n * All interpolated values are validated to prevent code injection.\n * @param fnName - Registered RPC function name (validated as path segment)\n * @param fnEntry - Export name used in the generated module (validated as identifier)\n * @param options - Content type, credentials, and RPC prefix settings\n * @returns A string of JavaScript code exporting the client stub\n */\nconst getModule = (\n fnName: string,\n fnEntry: string,\n options: Partial<ServerFunctionOptions> & {\n contentType: ServerFunctionOptions[\"contentType\"];\n rpcPrefix: string;\n },\n): string => {\n // Validate all interpolated strings to prevent code injection\n const safeFnName = validatePathSegment(fnName, \"function name\");\n const safeFnEntry = validateIdentifier(fnEntry, \"export name\");\n const safePrefix = validatePathSegment(options.rpcPrefix, \"rpcPrefix\");\n const credentials = validateCredentials(options.credentials);\n const method = validateMethod(options.method);\n let body = \"\";\n let headers = \"{}\";\n switch (options.contentType) {\n case \"text/plain\":\n {\n body = `args[0]`;\n headers = `{ 'Content-Type': 'text/plain' }`;\n }\n break;\n default: {\n body = `JSON.stringify(args)`;\n headers = `{ 'Content-Type': 'application/json' }`;\n }\n }\n // GET requests cannot carry a body: args travel as a JSON query parameter\n if (method === \"GET\") {\n body = `JSON.stringify(args)`;\n headers = `{}`;\n }\n\n const output = `\nexport const ${safeFnEntry} = (...args) => {\n const body = ${body};\n const headers = ${headers};\n const prefix = \"${safePrefix}\";\n const name = \"${safeFnName}\";\n const credentials = \"${credentials}\";\n const method = \"${method}\";\n return innerModule(body, headers, credentials, prefix, name, method);\n}`;\n\n return output.trim();\n};\n\n/**\n * Generates the complete client-side module bundle by iterating all registered server functions\n * and producing fetch-based stubs for each. The result is transformed by Vite (or Oxc) during\n * the dev server or production build.\n * @param initialOptions - Plugin options containing rpcPrefix and optional adapter\n * @returns A string of JavaScript code with all client RPC modules and their import dependencies\n */\nexport const getClientModules = (\n initialOptions: RpcPluginOptionsInternal,\n): string => {\n // Validate prefix once at the top level\n validatePathSegment(initialOptions.rpcPrefix, \"rpcPrefix\");\n\n return `\n// Client-side RPC modules\nimport { innerModule } from \"@thednp/rpc/helpers\";\n${\n Array.from(serverFunctionsMap.entries())\n .filter(([, entry]) => entry.exportName)\n .map(([registeredName, entry]) =>\n getModule(registeredName, entry.exportName!, {\n ...initialOptions,\n ...((entry.options as ServerFunctionOptions) || {}),\n })\n )\n .join(\"\\n\")\n }\n`.trim();\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thednp/rpc",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "license": "MIT",
5
5
  "author": "thednp",
6
6
  "description": "⚡ A Vite plugin for creating server functions with automatic Remote Procedure Calls (RPC)",
@@ -23,14 +23,13 @@
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public",
26
- "provenance": true
26
+ "provenance": false
27
27
  },
28
28
  "type": "module",
29
29
  "types": "./dist/index.d.mts",
30
30
  "sideEffects": false,
31
31
  "files": [
32
32
  "dist",
33
- "wiki",
34
33
  "README.md",
35
34
  "AGENTS.md",
36
35
  "CLAUDE.md",
@@ -69,7 +68,7 @@
69
68
  "up:deno": "deno update && deno -A scripts/update-deno.js",
70
69
  "upd": "pnpm up:examples && pnpm up:root",
71
70
  "audit:src": "node scripts/audit-src.js",
72
- "prepublishOnly": "pnpm upd && pnpm lint && pnpm format && pnpm audit:src && pnpm build",
71
+ "prepublishOnly": "pnpm upd && pnpm up:deno && pnpm lint && pnpm format && pnpm audit:src && pnpm build",
73
72
  "build": "tsdown"
74
73
  },
75
74
  "dependencies": {
package/wiki/adapters.md DELETED
@@ -1,172 +0,0 @@
1
- # Adapters
2
-
3
- `@thednp/rpc` provides adapters for ExpressJS, Fastify, Hono, and Koa. Each adapter exports `attachRPC` for production and `attachVite` for development.
4
-
5
- All adapters have an example each, feel free to explore [examples](../examples).
6
-
7
- ## Common Pattern
8
-
9
- All adapters share the same two-function API:
10
-
11
- ```ts
12
- import { attachRPC, attachVite } from '@thednp/rpc/<adapter>';
13
-
14
- // Production: mount RPC middleware
15
- await attachRPC(app);
16
-
17
- // Development: mount RPC + Vite middleware
18
- attachVite(app, vite);
19
- ```
20
-
21
- ## Express
22
-
23
- ### Installation
24
-
25
- ```bash
26
- // jsr registry
27
- pnpm add jsr:@thednp/rpc express
28
- ```
29
-
30
- ```bash
31
- // npmjs registry
32
- pnpm add @thednp/rpc express
33
- ```
34
-
35
- ### Usage
36
-
37
- ```ts
38
- // server.ts
39
- import express from 'express';
40
- import { attachRPC, attachVite } from '@thednp/rpc/express';
41
- import { createServer } from 'vite';
42
-
43
- const app = express();
44
- const isDev = process.env.NODE_ENV !== 'production';
45
-
46
- if (isDev) {
47
- const vite = await createServer({ server: { middlewareMode: true } });
48
- attachVite(app, vite);
49
- } else {
50
- await attachRPC(app);
51
- app.use(express.static('dist'));
52
- }
53
-
54
- app.listen(3000);
55
- ```
56
-
57
- ## Fastify
58
-
59
- ### Installation
60
-
61
- ```bash
62
- // jsr registry
63
- pnpm add jsr:@thednp/rpc fastify
64
- ```
65
-
66
- ```bash
67
- // npmjs registry
68
- pnpm add @thednp/rpc fastify
69
- ```
70
-
71
- ### Usage
72
-
73
- ```ts
74
- // server.ts
75
- import Fastify from 'fastify';
76
- import fastifyStatic from '@fastify/static';
77
- import { attachRPC, attachVite } from '@thednp/rpc/fastify';
78
- import { createServer } from 'vite';
79
-
80
- const fastify = Fastify({ logger: true });
81
- const isDev = process.env.NODE_ENV !== 'production';
82
-
83
- if (isDev) {
84
- const vite = await createServer({ server: { middlewareMode: true } });
85
- attachVite(fastify, vite);
86
- } else {
87
- await attachRPC(fastify);
88
- fastify.register(fastifyStatic, { root: 'dist' });
89
- }
90
-
91
- fastify.listen({ port: 3000 });
92
- ```
93
-
94
- ## Hono
95
-
96
- ### Installation
97
-
98
- ```bash
99
- // jsr registry
100
- pnpm add jsr:@thednp/rpc hono
101
- ```
102
-
103
- ```bash
104
- // npmjs registry
105
- pnpm add @thednp/rpc hono
106
- ```
107
-
108
- ### Usage
109
-
110
- ```ts
111
- // server.ts
112
- import { Hono } from 'hono';
113
- import { serveStatic } from '@hono/node-server/serve-static';
114
- import { attachRPC, attachVite } from '@thednp/rpc/hono';
115
- import { createServer } from 'vite';
116
-
117
- const app = new Hono();
118
- const isDev = process.env.NODE_ENV !== 'production';
119
-
120
- if (isDev) {
121
- const vite = await createServer({ server: { middlewareMode: true } });
122
- attachVite(app, vite);
123
- } else {
124
- await attachRPC(app);
125
- app.use('*', serveStatic({ root: './dist' }));
126
- }
127
-
128
- export default app;
129
- ```
130
-
131
- ## Koa
132
-
133
- ### Installation
134
-
135
- ```bash
136
- // jsr registry
137
- pnpm add jsr:@thednp/rpc koa koa-body
138
- ```
139
-
140
- ```bash
141
- // npmjs registry
142
- pnpm add @thednp/rpc koa koa-body
143
- ```
144
-
145
- ### Usage
146
-
147
- Body parser must be registered before the RPC middleware:
148
-
149
- ```ts
150
- // server.ts
151
- import Koa from 'koa';
152
- import { koaBody } from 'koa-body';
153
- import serve from 'koa-static';
154
- import { attachRPC, attachVite } from '@thednp/rpc/koa';
155
- import { createServer } from 'vite';
156
-
157
- const app = new Koa();
158
- const isDev = process.env.NODE_ENV !== 'production';
159
-
160
- // Body parser must come before RPC middleware
161
- app.use(koaBody({ jsonLimit: 1024 * 1024 }));
162
-
163
- if (isDev) {
164
- const vite = await createServer({ server: { middlewareMode: true } });
165
- attachVite(app, vite);
166
- } else {
167
- await attachRPC(app);
168
- app.use(serve('dist'));
169
- }
170
-
171
- app.listen(3000);
172
- ```