@thednp/rpc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/AGENTS.md +142 -0
  2. package/LICENSE +21 -0
  3. package/README.md +178 -0
  4. package/dist/express/express.d.mts +39 -0
  5. package/dist/express/express.d.mts.map +1 -0
  6. package/dist/express/express.mjs +179 -0
  7. package/dist/express/express.mjs.map +1 -0
  8. package/dist/fastify/fastify.d.mts +45 -0
  9. package/dist/fastify/fastify.d.mts.map +1 -0
  10. package/dist/fastify/fastify.mjs +164 -0
  11. package/dist/fastify/fastify.mjs.map +1 -0
  12. package/dist/fastify/plugin/fastify/plugin.d.mts +114 -0
  13. package/dist/fastify/plugin/fastify/plugin.d.mts.map +1 -0
  14. package/dist/fastify/plugin/fastify/plugin.mjs +151 -0
  15. package/dist/fastify/plugin/fastify/plugin.mjs.map +1 -0
  16. package/dist/helpers/helpers.d.mts +9 -0
  17. package/dist/helpers/helpers.d.mts.map +1 -0
  18. package/dist/helpers/helpers.mjs +36 -0
  19. package/dist/helpers/helpers.mjs.map +1 -0
  20. package/dist/hono/hono.d.mts +49 -0
  21. package/dist/hono/hono.d.mts.map +1 -0
  22. package/dist/hono/hono.mjs +153 -0
  23. package/dist/hono/hono.mjs.map +1 -0
  24. package/dist/index.d.mts +192 -0
  25. package/dist/index.d.mts.map +1 -0
  26. package/dist/index.mjs +296 -0
  27. package/dist/index.mjs.map +1 -0
  28. package/dist/koa/koa.d.mts +26 -0
  29. package/dist/koa/koa.d.mts.map +1 -0
  30. package/dist/koa/koa.mjs +155 -0
  31. package/dist/koa/koa.mjs.map +1 -0
  32. package/dist/server/server.d.mts +92 -0
  33. package/dist/server/server.d.mts.map +1 -0
  34. package/dist/server/server.mjs +161 -0
  35. package/dist/server/server.mjs.map +1 -0
  36. package/package.json +78 -0
  37. package/wiki/adapters.md +143 -0
  38. package/wiki/best-practices.md +201 -0
  39. package/wiki/client-usage.md +62 -0
  40. package/wiki/configuration.md +77 -0
  41. package/wiki/getting-started.md +76 -0
  42. package/wiki/index.md +26 -0
  43. package/wiki/security.md +54 -0
  44. package/wiki/server-functions.md +93 -0
  45. package/wiki/setup.md +77 -0
@@ -0,0 +1,153 @@
1
+ import { scanForServerFiles, serverFunctionsMap } from "@thednp/rpc/server";
2
+ //#region node_modules/.pnpm/hono@4.12.32/node_modules/hono/dist/helper/factory/index.js
3
+ var createMiddleware$1 = (middleware) => middleware;
4
+ //#endregion
5
+ //#region src/options.ts
6
+ const defaultRPCOptions = {
7
+ rpcPreffix: "__rpc",
8
+ adapter: "express"
9
+ };
10
+ const defaultMiddlewareOptions = {
11
+ rpcPreffix: void 0,
12
+ path: void 0
13
+ };
14
+ //#endregion
15
+ //#region src/tools.ts
16
+ function escapeRegExp(s) {
17
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18
+ }
19
+ //#endregion
20
+ //#region src/hono/helpers.ts
21
+ async function attachRPC(app) {
22
+ const { loadRPCConfig } = await import("@thednp/rpc");
23
+ const { adapter: _adapter, ...options } = await loadRPCConfig();
24
+ app.use(createRPCMiddleware(options));
25
+ }
26
+ function attachVite(app, vite) {
27
+ app.use(viteMiddleware(vite));
28
+ }
29
+ /**
30
+ * Creates a hono compatible middleware for a given vite development server.
31
+ * @see https://github.com/honojs/hono/issues/3162#issuecomment-2331118049
32
+ * @param vite the vite development server
33
+ */
34
+ const viteMiddleware = (vite) => {
35
+ return createMiddleware$1((c, next) => {
36
+ return new Promise((resolve) => {
37
+ if (typeof Bun === "undefined") {
38
+ vite.middlewares(c.env.incoming, c.env.outgoing, () => resolve(next()));
39
+ return;
40
+ }
41
+ {
42
+ let sent = false;
43
+ const headers = new Headers();
44
+ vite.middlewares({
45
+ url: new URL(c.req.path, "http://localhost").pathname,
46
+ method: c.req.raw.method,
47
+ headers: Object.fromEntries(c.req.raw.headers)
48
+ }, {
49
+ setHeader(name, value) {
50
+ headers.set(name, value);
51
+ return this;
52
+ },
53
+ end(body) {
54
+ sent = true;
55
+ resolve(c.body(body, c.res.status, headers));
56
+ }
57
+ }, () => sent || resolve(next()));
58
+ }
59
+ });
60
+ });
61
+ };
62
+ const readBody = async (c) => {
63
+ const isJSON = (c.req.header("content-type")?.toLowerCase() || "").includes("json");
64
+ const incoming = c.env.incoming;
65
+ if (incoming?.body !== void 0) {
66
+ const reqBody = incoming.body;
67
+ return {
68
+ contentType: isJSON ? "application/json" : "text/plain",
69
+ data: isJSON ? reqBody : String(reqBody)
70
+ };
71
+ }
72
+ if (isJSON) return {
73
+ contentType: "application/json",
74
+ data: await c.req.json()
75
+ };
76
+ const text = await c.req.text();
77
+ return {
78
+ contentType: "text/plain",
79
+ data: String(text)
80
+ };
81
+ };
82
+ //#endregion
83
+ //#region src/hono/createMiddleware.ts
84
+ let middlewareCount = 0;
85
+ const middlewareStack = /* @__PURE__ */ new Set();
86
+ const createMiddleware = (initialOptions = {}) => {
87
+ const options = Object.assign({}, defaultMiddlewareOptions, initialOptions);
88
+ const middlewareName = options.name;
89
+ const rpcPreffix = options.rpcPreffix;
90
+ const path = options.path;
91
+ const handler = options.handler;
92
+ let name = middlewareName;
93
+ if (!name) {
94
+ name = "viteRPCMiddleware-" + middlewareCount;
95
+ middlewareCount += 1;
96
+ }
97
+ if (middlewareStack.has(name)) throw new Error(`The middleware name "${name}" is already used.`);
98
+ middlewareStack.add(name);
99
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
100
+ const pathMatcher = path ? typeof path === "string" ? new RegExp(path) : path : null;
101
+ const middlewareHandler = createMiddleware$1(async (c, next) => {
102
+ const url = new URL(c.req.path, "http://localhost").pathname;
103
+ if (serverFunctionsMap.size === 0) await scanForServerFiles();
104
+ if (!handler) {
105
+ await next();
106
+ return;
107
+ }
108
+ if (pathMatcher && !pathMatcher.test(url)) {
109
+ await next();
110
+ return;
111
+ }
112
+ if (prefixRegex && !prefixRegex.test(url)) {
113
+ await next();
114
+ return;
115
+ }
116
+ return await handler(c, next);
117
+ });
118
+ Object.defineProperty(middlewareHandler, "name", { value: name });
119
+ return middlewareHandler;
120
+ };
121
+ const createRPCMiddleware = (initialOptions = {}) => {
122
+ const options = Object.assign({}, defaultMiddlewareOptions, { rpcPreffix: defaultRPCOptions.rpcPreffix }, initialOptions);
123
+ const rpcPreffix = options.rpcPreffix;
124
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
125
+ const prefixReplace = `/${rpcPreffix}/`;
126
+ return createMiddleware({
127
+ ...options,
128
+ handler: async (c, _next) => {
129
+ const { path: reqPath } = c.req;
130
+ if (prefixRegex && !prefixRegex.test(reqPath)) return;
131
+ const functionName = reqPath.replace(prefixReplace, "");
132
+ const serverFunction = serverFunctionsMap.get(functionName);
133
+ if (!serverFunction) return c.json({ error: "Function not found" }, 404);
134
+ try {
135
+ const body = await readBody(c);
136
+ const args = Array.isArray(body.data) ? body.data : [body.data];
137
+ const fnResult = serverFunction.handler(...args);
138
+ const onAbort = () => fnResult.cancel("client disconnected");
139
+ c.env.incoming.on("close", onAbort);
140
+ const result = await fnResult.data;
141
+ c.env.incoming.off("close", onAbort);
142
+ return c.json({ data: result }, 200);
143
+ } catch (err) {
144
+ console.error(String(err));
145
+ return c.json({ error: "Internal Server Error" }, 500);
146
+ }
147
+ }
148
+ });
149
+ };
150
+ //#endregion
151
+ export { attachRPC, attachVite, createMiddleware, createRPCMiddleware, readBody, viteMiddleware };
152
+
153
+ //# sourceMappingURL=hono.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.mjs","names":["createMiddleware","createMiddleware","createHonoMiddleware"],"sources":["../../node_modules/.pnpm/hono@4.12.32/node_modules/hono/dist/helper/factory/index.js","../../src/options.ts","../../src/tools.ts","../../src/hono/helpers.ts","../../src/hono/createMiddleware.ts"],"sourcesContent":["// src/helper/factory/index.ts\nimport { Hono } from \"../../hono.js\";\nvar Factory = class {\n initApp;\n #defaultAppOptions;\n constructor(init) {\n this.initApp = init?.initApp;\n this.#defaultAppOptions = init?.defaultAppOptions;\n }\n createApp = (options) => {\n const app = new Hono(\n options && this.#defaultAppOptions ? { ...this.#defaultAppOptions, ...options } : options ?? this.#defaultAppOptions\n );\n if (this.initApp) {\n this.initApp(app);\n }\n return app;\n };\n createMiddleware = (middleware) => middleware;\n createHandlers = (...handlers) => {\n return handlers.filter((handler) => handler !== void 0);\n };\n};\nvar createFactory = (init) => new Factory(init);\nvar createMiddleware = (middleware) => middleware;\nexport {\n Factory,\n createFactory,\n createMiddleware\n};\n","import type {\n MiddlewareOptions,\n RpcPluginOptions,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\n\nexport const defaultServerFnOptions = {\n contentType: \"application/json\",\n} satisfies ServerFunctionOptions;\n\nexport const defaultRPCOptions: RpcPluginOptions = {\n rpcPreffix: \"__rpc\",\n adapter: \"express\",\n};\n\nexport const defaultMiddlewareOptions = {\n rpcPreffix: undefined,\n path: undefined,\n} satisfies MiddlewareOptions;\n","export function escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","// src/hono/helpers.ts\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { type HttpBindings } from \"@hono/node-server\";\nimport type { Context, Hono } from \"hono\";\nimport { createMiddleware } from \"hono/factory\";\nimport { type ViteDevServer } from \"vite\";\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\";\nimport { createRPCMiddleware } from \"./createMiddleware.ts\";\nimport type { BodyResult } from \"../types.d.ts\";\n\nexport async function attachRPC(app: Hono) {\n const { loadRPCConfig } = await import(\"@thednp/rpc\");\n const { adapter: _adapter, ...options } = await loadRPCConfig();\n\n app.use(createRPCMiddleware(options));\n}\n\nexport function attachVite(app: Hono, vite: ViteDevServer): void {\n app.use(viteMiddleware(vite));\n}\n\n/**\n * Creates a hono compatible middleware for a given vite development server.\n * @see https://github.com/honojs/hono/issues/3162#issuecomment-2331118049\n * @param vite the vite development server\n */\nexport const viteMiddleware = (vite: ViteDevServer) => {\n return createMiddleware<{ Bindings: HttpBindings }>((c, next) => {\n return new Promise((resolve) => {\n // Node.js\n // @ts-expect-error - NodeJS is different\n // istanbul ignore if\n if (typeof Bun === \"undefined\") {\n vite.middlewares(c.env.incoming, c.env.outgoing, () => resolve(next()));\n return;\n }\n\n /* istanbul ignore next */ {\n // Bun\n let sent = false;\n const headers = new Headers();\n // Polyfill the node:http IncommingMessage and ServerResponse\n vite.middlewares(\n {\n url: new URL(c.req.path, \"http://localhost\").pathname,\n method: c.req.raw.method,\n headers: Object.fromEntries(c.req.raw.headers),\n } as IncomingMessage,\n {\n setHeader(name, value: string) {\n headers.set(name, value);\n return this;\n },\n end(body) {\n sent = true;\n resolve(\n // @ts-expect-error - weird\n c.body(body, c.res.status as ContentfulStatusCode, headers),\n );\n },\n } as ServerResponse,\n () => sent || resolve(next()),\n );\n }\n });\n });\n};\n\nexport const readBody = async (\n c: Context,\n): Promise<BodyResult> => {\n const contentType = c.req.header(\"content-type\")?.toLowerCase() || \"\";\n const isJSON = contentType.includes(\"json\");\n type IncomingWithBody = IncomingMessage & { body?: unknown };\n const incoming = (c.env as HttpBindings).incoming as\n | IncomingWithBody\n | undefined;\n if (incoming?.body !== undefined) {\n const reqBody = incoming.body;\n return {\n contentType: isJSON ? \"application/json\" : \"text/plain\",\n data: isJSON ? reqBody : String(reqBody),\n } as BodyResult;\n }\n if (isJSON) {\n const data = await c.req.json();\n return {\n contentType: \"application/json\",\n data,\n };\n }\n\n const text = await c.req.text();\n return { contentType: \"text/plain\", data: String(text) };\n};\n","// src/hono/createMiddleware.ts\nimport type { Context, Next } from \"hono\";\n// import type { JsonArray, JsonValue } from \"@thednp/rpc\";\nimport type { HonoMiddlewareFn, HonoMiddlewareOptions } from \"./types.d.ts\";\nimport { createMiddleware as createHonoMiddleware } from \"hono/factory\";\nimport { scanForServerFiles, serverFunctionsMap } from \"@thednp/rpc/server\";\nimport { defaultMiddlewareOptions, defaultRPCOptions } from \"../options.ts\";\nimport { escapeRegExp } from \"../tools.ts\";\nimport { readBody } from \"./helpers.ts\";\n\nlet middlewareCount = 0;\nconst middlewareStack = new Set<string>();\n\nexport const createMiddleware: HonoMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n initialOptions,\n ) as HonoMiddlewareOptions;\n\n const middlewareName = options.name;\n const rpcPreffix = options.rpcPreffix;\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(`The middleware name \"${name}\" is already used.`);\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 = rpcPreffix\n ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`)\n : null;\n const pathMatcher: RegExp | null = path\n ? (typeof path === \"string\" ? new RegExp(path) : path)\n : null;\n\n const middlewareHandler = createHonoMiddleware(\n async (c: Context, next: Next) => {\n const reqUrl = new URL(c.req.path, \"http://localhost\");\n const url = reqUrl.pathname;\n\n if (serverFunctionsMap.size === 0) {\n await scanForServerFiles();\n }\n\n // No need to continue when no handler provided\n if (!handler) {\n await next();\n return;\n }\n\n if (pathMatcher && !pathMatcher.test(url)) {\n await next();\n return;\n }\n\n if (prefixRegex && !prefixRegex.test(url)) {\n await next();\n return;\n }\n\n return await handler(c, next);\n },\n );\n\n Object.defineProperty(middlewareHandler, \"name\", {\n value: name,\n });\n\n return middlewareHandler;\n};\n\nexport const createRPCMiddleware: HonoMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n { rpcPreffix: defaultRPCOptions.rpcPreffix },\n initialOptions,\n ) as HonoMiddlewareOptions;\n\n // Hoist prefix regex (escaped) and the literal prefix-for-replace out of the\n // per-request handler to avoid regex injection and per-request compilation.\n const rpcPreffix = options.rpcPreffix;\n const prefixRegex = rpcPreffix\n ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`)\n : /* istanbul ignore next */ null;\n const prefixReplace = `/${rpcPreffix}/`;\n\n return createMiddleware({\n ...options,\n handler: async (c: Context, _next: Next) => {\n const { path: reqPath } = c.req;\n // const { rpcPreffix: prefix } = options;\n\n // Defense-in-depth: validate prefix match via escaped regex even though\n // the outer createMiddleware gates on the same prefix already.\n // istanbul ignore if\n if (prefixRegex && !prefixRegex.test(reqPath)) {\n /* istanbul ignore next */\n return;\n }\n\n const functionName = reqPath.replace(prefixReplace, \"\");\n const serverFunction = serverFunctionsMap.get(functionName);\n\n if (!serverFunction) {\n return c.json({ error: \"Function not found\" }, 404);\n }\n\n try {\n const body = await readBody(c);\n const args = Array.isArray(body.data) ? body.data : [body.data];\n const fnResult = serverFunction.handler(...args);\n const onAbort = () => fnResult.cancel(\"client disconnected\");\n c.env.incoming.on(\"close\", onAbort);\n const result = await fnResult.data;\n c.env.incoming.off(\"close\", onAbort);\n\n return c.json({ data: result }, 200);\n } catch (err) {\n console.error(String(err));\n return c.json({ error: \"Internal Server Error\" }, 500);\n }\n },\n });\n};\n"],"x_google_ignoreList":[0],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ import { Connect, Plugin } from "vite";
2
+ import { MiddlewareOptions as MiddlewareOptions$1, RpcPluginOptions as RpcPluginOptions$1 } from "@thednp/rpc";
3
+ import { IncomingMessage, ServerResponse } from "node:http";
4
+ import { NextFunction, Request, Response as Response$1 } from "express";
5
+ import { MiddlewareHandler } from "hono";
6
+ import "@hono/node-server";
7
+ import { FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
8
+ import { Context, Next } from "koa";
9
+ //#region src/express/types.d.ts
10
+ type ExpressMiddlewareOptions = MiddlewareOptions$1<"express">;
11
+ type ExpressMiddlewareFn = <A extends RpcPluginOptions$1["adapter"] = "express">(initialOptions?: Partial<ExpressMiddlewareOptions>) => ExpressMiddlewareHooks["handler"];
12
+ interface ExpressMiddlewareHooks {
13
+ handler: (req: IncomingMessage | Request, res: ServerResponse | Response$1, next: Connect.NextFunction | NextFunction) => Promise<void>;
14
+ }
15
+ //#endregion
16
+ //#region src/hono/types.d.ts
17
+ interface HonoMiddlewareHooks {
18
+ handler: MiddlewareHandler;
19
+ }
20
+ type HonoMiddlewareFn = <A extends RpcPluginOptions$1["adapter"] = "hono">(initialOptions?: Partial<MiddlewareOptions$1<A>>) => HonoMiddlewareHooks["handler"];
21
+ //#endregion
22
+ //#region src/fastify/types.d.ts
23
+ type FastifyMiddlewareOptions = MiddlewareOptions$1<"fastify">;
24
+ type FastifyMiddlewareFn = <A extends RpcPluginOptions$1["adapter"] = "fastify">(initialOptions?: Partial<FastifyMiddlewareOptions>) => FastifyMiddlewareHooks["handler"];
25
+ interface FastifyMiddlewareHooks {
26
+ handler: (req: FastifyRequest, res: FastifyReply, done: HookHandlerDoneFunction) => Promise<void>;
27
+ }
28
+ //#endregion
29
+ //#region src/koa/types.d.ts
30
+ type KoaMiddlewareOptions = MiddlewareOptions$1<"koa">;
31
+ interface KoaMiddlewareHooks {
32
+ handler: (ctx: Context, next: Next) => Promise<void>;
33
+ }
34
+ type KoaMiddlewareFn = <A extends RpcPluginOptions$1["adapter"] = "koa">(initialOptions?: Partial<KoaMiddlewareOptions>) => KoaMiddlewareHooks["handler"];
35
+ //#endregion
36
+ //#region src/types.d.ts
37
+ interface FrameworkHooks {
38
+ express: ExpressMiddlewareHooks;
39
+ hono: HonoMiddlewareHooks;
40
+ fastify: FastifyMiddlewareHooks;
41
+ koa: KoaMiddlewareHooks;
42
+ }
43
+ interface FrameworkMiddlewareFn {
44
+ express: ExpressMiddlewareFn;
45
+ hono: HonoMiddlewareFn;
46
+ fastify: FastifyMiddlewareFn;
47
+ koa: KoaMiddlewareFn;
48
+ }
49
+ type SupportableContentType = "multipart/form-data" | "application/json" | "text/plain" | "application/octet-stream";
50
+ type ContentType = "application/json" | "text/plain";
51
+ type BodyResult = {
52
+ contentType: "application/json";
53
+ data: JsonValue;
54
+ } | {
55
+ contentType: "text/plain";
56
+ data: string;
57
+ };
58
+ interface ServerFunctionOptions {
59
+ contentType: ContentType;
60
+ }
61
+ // primitives and their compositions
62
+ type JsonPrimitive = string | number | boolean | null | undefined;
63
+ type JsonObject = {
64
+ [key: string]: JsonValue | JsonArray;
65
+ };
66
+ type JsonArray = JsonValue[];
67
+ type JsonValue = JsonPrimitive | JsonArray | JsonObject;
68
+ // Keep these as a refference
69
+ // Date strings are common in APIs
70
+ // export type ISODateString = string; // for dates in ISO format
71
+ // Special types that might be useful
72
+ // export type Base64String = string; // for binary data encoded as base64
73
+ // export type URLString = string; // for URLs
74
+ // export type EmailString = string; // for email addresses
75
+ // export type RPCValue =
76
+ // | JsonValue
77
+ // | Date // will be serialized as ISOString
78
+ // | Uint8Array // will be serialized as base64
79
+ // | File // for file uploads
80
+ // | Blob // for binary data
81
+ // | URLSearchParams; // for query parameters
82
+ // export type ServerFnArgs = [JsonObject | JsonPrimitive, ...JsonArray];
83
+ type ServerFnArgs = [...JsonArray];
84
+ type ServerFunction<TArgs extends JsonArray = JsonArray, TResult extends JsonValue = JsonValue> = (signal: AbortSignal, ...args: TArgs) => Promise<TResult>;
85
+ type ServerFunctionInit<TArgs extends JsonArray = JsonArray, TResult extends JsonValue = JsonValue> = (signal: AbortSignal, ...args: TArgs) => Promise<TResult>;
86
+ type ClientFunction<TArgs extends JsonArray = JsonArray, TResult extends JsonValue = JsonValue> = (...args: TArgs) => {
87
+ data: Promise<TResult>;
88
+ cancel: (reason: string) => void;
89
+ };
90
+ type ClientFunctionWithOptions = ClientFunction & {
91
+ name: string;
92
+ options?: ServerFunctionOptions;
93
+ };
94
+ interface ServerFnEntry {
95
+ name: string;
96
+ handler: ClientFunctionWithOptions;
97
+ options?: ServerFunctionOptions;
98
+ exportName?: string;
99
+ }
100
+ interface CacheEntry<T> {
101
+ data?: T;
102
+ timestamp: number;
103
+ promise?: Promise<T>;
104
+ }
105
+ /**
106
+ * ### @thednp/rpc
107
+ * The plugin configuration allows for granular control of your
108
+ * application RPC calls. The default settings are optimized for development
109
+ * environments while providing a secure foundation for production use.
110
+ */
111
+ interface RpcPluginOptions {
112
+ // RPC Middleware Options
113
+ /**
114
+ * RPC prefix without leading slash (e.g. "__rpc")
115
+ * Leading slash will be added automatically by the middleware.
116
+ * This prefix defines the base path for all RPC endpoints.
117
+ * @default "__rpc"
118
+ * @example
119
+ * // Results in endpoints like: /api/rpc/myFunction
120
+ * rpcPreffix: "api/rpc"
121
+ */
122
+ rpcPreffix: "__rpc" | string;
123
+ /**
124
+ * Option to set an adapter for the middleware connection. The default is _express_,
125
+ * which is the most popular and battle tested server app. The _express_ adapter is
126
+ * also compatible with the vite's Connect development server.
127
+ * @default express
128
+ */
129
+ adapter: "express" | "hono" | "fastify" | "koa";
130
+ }
131
+ interface MiddlewareOptions<A extends RpcPluginOptions["adapter"] = "express"> {
132
+ /**
133
+ * Name for the middleware (used for identification in Express stack)
134
+ */
135
+ name?: string;
136
+ /**
137
+ * Path pattern to match for middleware execution.
138
+ * Accepts string or RegExp to filter requests based on URL path.
139
+ *
140
+ * @example
141
+ * // String path
142
+ * path: "/api/v1"
143
+ *
144
+ * // RegExp pattern
145
+ * path: /^\/api\/v[0-9]+/
146
+ */
147
+ path?: string | RegExp;
148
+ /**
149
+ * RPC prefix without leading slash (e.g. "__rpc")
150
+ * Leading slash will be added automatically by the middleware.
151
+ * This prefix defines the base path for all RPC endpoints.
152
+ * @default string
153
+ * @example
154
+ * // Results in endpoints like: /api/rpc/myFunction
155
+ * rpcPreffix: "api/rpc"
156
+ */
157
+ rpcPreffix?: string | false;
158
+ /**
159
+ * Async handler for request processing.
160
+ * Core middleware function that processes incoming requests.
161
+ *
162
+ * @param req - The incoming request object
163
+ * @param res - The server response object
164
+ * @param next - Function to pass control to the next middleware
165
+ *
166
+ * @example
167
+ * handler: async (req, res, next) => {
168
+ * // Process request
169
+ * const data = await processRequest(req);
170
+ *
171
+ * // Send response
172
+ * sendResponse(res, { data }, 200);
173
+ * }
174
+ */
175
+ handler?: FrameworkHooks[A]["handler"];
176
+ }
177
+ //#endregion
178
+ //#region src/index.d.ts
179
+ /**
180
+ * Utility to define `@thednp/rpc` configuration file similar to vite.
181
+ * @param uniConfig a system wide RPC configuration
182
+ */
183
+ declare const defineConfig: (uniConfig: Partial<RpcPluginOptions>) => RpcPluginOptions;
184
+ /**
185
+ * Utility to load `@thednp/rpc` configuration file system wide.
186
+ * @param configFile an optional parameter to specify a file within your project scope
187
+ */
188
+ declare function loadRPCConfig(configFile?: string): Promise<RpcPluginOptions>;
189
+ declare function rpcPlugin(devOptions?: Partial<RpcPluginOptions>): Plugin<unknown>;
190
+ //#endregion
191
+ export { type BodyResult, type CacheEntry, type ClientFunction, type ClientFunctionWithOptions, type ContentType, type FrameworkHooks, type FrameworkMiddlewareFn, type JsonArray, type JsonObject, type JsonPrimitive, type JsonValue, type MiddlewareOptions, type RpcPluginOptions, type ServerFnArgs, type ServerFnEntry, type ServerFunction, type ServerFunctionInit, type ServerFunctionOptions, type SupportableContentType, rpcPlugin as default, defineConfig, loadRPCConfig };
192
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/express/types.d.ts","../src/hono/types.d.ts","../src/fastify/types.d.ts","../src/koa/types.d.ts","../src/types.d.ts","../src/index.ts"],"mappings":";;;;;;;;;KAKY,2BAA2B;KAE3B,uBACV,UAAU,2CAEV,iBAAiB,QAAQ,8BACtB;UAEY;EACf,UACE,KAAK,kBAAkB,SACvB,KAAK,iBAAiB,YACtB,MAAM,QAAQ,eAAe,iBAC1B;;;;UCbU;EACf,SAAS;;KAGC,oBAAoB,UAAU,wCACxC,iBAAiB,QAAQ,oBAAkB,QACxC;;;KCJO,2BAA2B;KAE3B,uBACV,UAAU,2CAEV,iBAAiB,QAAQ,8BACtB;UAEY;EACf,UACE,KAAK,gBACL,KAAK,cACL,MAAM,4BACH;;;;KCZK,uBAAuB;UAMlB;EACf,UAAU,KAAK,SAAS,MAAM,SAAS;;KAG7B,mBAAmB,UAAU,uCACvC,iBAAiB,QAAQ,0BACtB;;;UCLY;EACf,SAAS;EACT,MAAM;EACN,SAAS;EACT,KAAK;;UAGU;EACf,SAAS;EACT,MAAM;EACN,SAAS;EACT,KAAK;;KAGK;KAMA;KAEA;EACN;EAAiC,MAAM;;EACvC;EAA2B;;UAEhB;EACf,aAAa;;;KAIH;KACA;GAAgB,cAAc,YAAY;;KAC1C,YAAY;KACZ,YAAY,gBAAgB,YAAY;;;;;;;;;;;;;;;;KAoBxC,mBAAmB;KAEnB,eACV,cAAc,YAAY,WAC1B,gBAAgB,YAAY,cACzB,QAAQ,gBAAgB,MAAM,UAAU,QAAQ;KAEzC,mBACV,cAAc,YAAY,WAC1B,gBAAgB,YAAY,cACzB,QAAQ,gBAAgB,MAAM,UAAU,QAAQ;KAEzC,eACV,cAAc,YAAY,WAC1B,gBAAgB,YAAY,iBACtB,MAAM;EACZ,MAAM,QAAQ;EACd,SAAS;;KAGC,4BAA4B;EACtC;EACA,UAAU;;UAGK;EACf;EACA,SAAS;EACT,UAAU;EACV;;UAGe,WAAW;EAC1B,OAAO;EACP;EACA,UAAU,QAAQ;;;;;;;;UASH;;;;;;;;;;;EAWf;;;;;;;EAQA;;UAGe,kBACf,UAAU;;;;EAKV;;;;;;;;;;;;EAaA,gBAAgB;;;;;;;;;;EAWhB;;;;;;;;;;;;;;;;;;EAmBA,UAAU,eAAe;;;;;;;;cC1JrB,eAAY,WAAe,QAAQ,sBACa;;;;;iBASvC,cAAc,sBAAmB,QAAA;iBA0FvC,UACP,aAAY,QAAQ,oBACnB"}
package/dist/index.mjs ADDED
@@ -0,0 +1,296 @@
1
+ import { loadConfigFromFile, mergeConfig } from "vite";
2
+ import { resolve } from "node:path";
3
+ import process from "node:process";
4
+ import { existsSync } from "node:fs";
5
+ import { getClientModules, scanForServerFiles, serverFunctionsMap } from "@thednp/rpc/server";
6
+ //#region src/options.ts
7
+ const defaultRPCOptions = {
8
+ rpcPreffix: "__rpc",
9
+ adapter: "express"
10
+ };
11
+ const defaultMiddlewareOptions = {
12
+ rpcPreffix: void 0,
13
+ path: void 0
14
+ };
15
+ //#endregion
16
+ //#region src/express/helpers.ts
17
+ const readBody = (req) => {
18
+ return new Promise((resolve, reject) => {
19
+ if (hasPreParsedBody(req) && req.body !== void 0) {
20
+ const isJSON = (req.headers["content-type"]?.toLowerCase() || "").includes("json");
21
+ resolve({
22
+ contentType: isJSON ? "application/json" : "text/plain",
23
+ data: isJSON ? req.body : String(req.body)
24
+ });
25
+ return;
26
+ }
27
+ let body = "";
28
+ const toggleListeners = (add) => {
29
+ const method = add ? "on" : "off";
30
+ req[method]("data", onData);
31
+ req[method]("end", onEnd);
32
+ req[method]("error", onError);
33
+ };
34
+ const onData = (chunk) => {
35
+ body += chunk.toString();
36
+ };
37
+ const onEnd = () => {
38
+ toggleListeners();
39
+ const isJSON = (req.headers["content-type"]?.toLowerCase() || "").includes("json");
40
+ try {
41
+ const data = JSON.parse(body);
42
+ resolve({
43
+ contentType: isJSON ? "application/json" : "text/plain",
44
+ data
45
+ });
46
+ } catch (_e) {
47
+ resolve({
48
+ contentType: "text/plain",
49
+ data: String(body)
50
+ });
51
+ }
52
+ };
53
+ const onError = (err) => {
54
+ toggleListeners();
55
+ reject(err);
56
+ };
57
+ toggleListeners(true);
58
+ });
59
+ };
60
+ const isExpressRequest = (req) => {
61
+ return "originalUrl" in req;
62
+ };
63
+ const isExpressResponse = (res) => {
64
+ return "json" in res && "send" in res;
65
+ };
66
+ const hasPreParsedBody = (req) => {
67
+ return "body" in req;
68
+ };
69
+ const getRequestDetails = (request) => {
70
+ const rawUrl = isExpressRequest(request) ? request.originalUrl : request.url;
71
+ const url = new URL(rawUrl, "http://localhost");
72
+ return {
73
+ url: url.pathname,
74
+ search: url.search,
75
+ searchParams: url.searchParams,
76
+ headers: request.headers,
77
+ method: request.method
78
+ };
79
+ };
80
+ const getResponseDetails = (response) => {
81
+ const isResponseSent = response.headersSent || response.writableEnded;
82
+ const setHeader = (name, value) => {
83
+ if (isExpressResponse(response)) response.header(name, value);
84
+ else response.setHeader(name, value);
85
+ };
86
+ const setStatusCode = (code) => {
87
+ if (isExpressResponse(response)) response.status(code);
88
+ else response.statusCode = code;
89
+ };
90
+ const sendResponse = (code, output) => {
91
+ setStatusCode(code);
92
+ setHeader("Content-Type", "application/json");
93
+ if (isExpressResponse(response)) response.send(JSON.stringify(output));
94
+ else response.end(JSON.stringify(output));
95
+ };
96
+ return {
97
+ isResponseSent,
98
+ setHeader,
99
+ statusCode: response.statusCode,
100
+ setStatusCode,
101
+ sendResponse
102
+ };
103
+ };
104
+ //#endregion
105
+ //#region src/tools.ts
106
+ function escapeRegExp(s) {
107
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
108
+ }
109
+ //#endregion
110
+ //#region src/express/createMiddleware.ts
111
+ let middlewareCount = 0;
112
+ const middlewareStack = /* @__PURE__ */ new Set();
113
+ const createMiddleware = (initialOptions = {}) => {
114
+ const options = Object.assign({}, defaultMiddlewareOptions, initialOptions);
115
+ const middlewareName = options.name;
116
+ const rpcPreffix = options.rpcPreffix;
117
+ const path = options.path;
118
+ const handler = options.handler;
119
+ let name = middlewareName;
120
+ if (!name) {
121
+ name = "viteRPCMiddleware-" + middlewareCount;
122
+ middlewareCount += 1;
123
+ }
124
+ if (middlewareStack.has(name)) throw new Error(`The middleware name "${name}" is already used.`);
125
+ middlewareStack.add(name);
126
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
127
+ const pathMatcher = path ? typeof path === "string" ? new RegExp(path) : path : null;
128
+ const middlewareHandler = async (req, _res, next) => {
129
+ const { url } = getRequestDetails(req);
130
+ if (serverFunctionsMap.size === 0) await scanForServerFiles();
131
+ if (!handler) return next?.();
132
+ if (pathMatcher && !pathMatcher.test(url)) return next?.();
133
+ if (prefixRegex && !prefixRegex.test(url)) return next?.();
134
+ await handler(req, _res, next);
135
+ };
136
+ Object.defineProperty(middlewareHandler, "name", { value: name });
137
+ return middlewareHandler;
138
+ };
139
+ const createRPCMiddleware = (initialOptions = {}) => {
140
+ const options = Object.assign({}, defaultMiddlewareOptions, { rpcPreffix: defaultRPCOptions.rpcPreffix }, initialOptions);
141
+ const rpcPreffix = options.rpcPreffix;
142
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
143
+ const prefixReplace = `/${rpcPreffix}/`;
144
+ return createMiddleware({
145
+ ...options,
146
+ handler: async (req, res, _next) => {
147
+ const { url } = getRequestDetails(req);
148
+ const { sendResponse } = getResponseDetails(res);
149
+ if (prefixRegex && !prefixRegex.test(url)) return;
150
+ const functionName = url.replace(prefixReplace, "");
151
+ const serverFunction = serverFunctionsMap.get(functionName);
152
+ if (!serverFunction) {
153
+ sendResponse(404, { error: "Function not found" });
154
+ return;
155
+ }
156
+ try {
157
+ const body = await readBody(req);
158
+ const args = Array.isArray(body.data) ? body.data : [body.data];
159
+ const { data, cancel } = serverFunction.handler(...args);
160
+ const onClose = () => cancel("client disconnected");
161
+ req.on("close", onClose);
162
+ const result = await data;
163
+ req.off("close", onClose);
164
+ if (!res.headersSent) sendResponse(200, { data: result });
165
+ } catch (err) {
166
+ console.error(String(err));
167
+ sendResponse(500, { error: "Internal Server Error" });
168
+ }
169
+ }
170
+ });
171
+ };
172
+ //#endregion
173
+ //#region src/index.ts
174
+ const loadConfigFile = async (env, file) => {
175
+ const result = await loadConfigFromFile(env, file);
176
+ return result ? {
177
+ ...result,
178
+ config: {
179
+ ...result.config,
180
+ configFile: file
181
+ }
182
+ } : null;
183
+ };
184
+ /**
185
+ * Utility to define `@thednp/rpc` configuration file similar to vite.
186
+ * @param uniConfig a system wide RPC configuration
187
+ */
188
+ const defineConfig = (uniConfig) => {
189
+ return mergeConfig(defaultRPCOptions, uniConfig);
190
+ };
191
+ let RPCConfig;
192
+ /**
193
+ * Utility to load `@thednp/rpc` configuration file system wide.
194
+ * @param configFile an optional parameter to specify a file within your project scope
195
+ */
196
+ async function loadRPCConfig(configFile) {
197
+ try {
198
+ const env = {
199
+ command: "serve",
200
+ root: process.cwd(),
201
+ mode: process.env.NODE_ENV || "development"
202
+ };
203
+ const defaultConfigFiles = [
204
+ "rpc.config.ts",
205
+ "rpc.config.js",
206
+ "rpc.config.mjs",
207
+ "rpc.config.mts",
208
+ ".rpcrc.ts",
209
+ ".rpcrc.js"
210
+ ];
211
+ if (configFile) {
212
+ const configFilePath = resolve(env.root, configFile);
213
+ if (!existsSync(configFilePath)) {
214
+ console.warn(` ⚠︎ The specified RPC config file ${configFile} cannot be found at ${configFilePath}, loading the defaults..`);
215
+ RPCConfig = defaultRPCOptions;
216
+ return defaultRPCOptions;
217
+ }
218
+ const result = await loadConfigFile(env, configFile);
219
+ if (result && typeof result === "object") {
220
+ RPCConfig = mergeConfig({
221
+ ...defaultRPCOptions,
222
+ configFile: configFilePath
223
+ }, result.config);
224
+ return RPCConfig;
225
+ }
226
+ RPCConfig = defaultRPCOptions;
227
+ }
228
+ if (RPCConfig !== void 0) return RPCConfig;
229
+ for (const file of defaultConfigFiles) {
230
+ const configFilePath = resolve(env.root, file);
231
+ if (!existsSync(configFilePath)) continue;
232
+ const result = await loadConfigFile(env, file);
233
+ if (result) {
234
+ RPCConfig = mergeConfig({
235
+ ...defaultRPCOptions,
236
+ configFile: configFilePath
237
+ }, result.config);
238
+ return RPCConfig;
239
+ }
240
+ }
241
+ RPCConfig = defaultRPCOptions;
242
+ console.warn(` ⚡︎ No RPC config found, loading the defaults..`);
243
+ } catch (error) {
244
+ RPCConfig = defaultRPCOptions;
245
+ console.warn(` ⚠︎ Failed to load RPC config:`, error);
246
+ }
247
+ return RPCConfig;
248
+ }
249
+ function rpcPlugin(devOptions = {}) {
250
+ let options;
251
+ let config;
252
+ let viteServer;
253
+ let isOxc = true;
254
+ return {
255
+ name: "vite-plugin-universal-rpc",
256
+ enforce: "pre",
257
+ async configResolved(resolvedConfig) {
258
+ options = mergeConfig(await loadRPCConfig(), devOptions);
259
+ config = resolvedConfig;
260
+ },
261
+ async configureServer(server) {
262
+ viteServer = server;
263
+ const { adapter: _adapter, ...rest } = options;
264
+ if (serverFunctionsMap.size === 0) await scanForServerFiles(config, viteServer);
265
+ server.middlewares.use(createRPCMiddleware(rest));
266
+ },
267
+ async buildStart() {
268
+ const viteVersion = this.meta?.viteVersion;
269
+ isOxc = Number(viteVersion[0]) >= 8;
270
+ if (!viteServer && config) await scanForServerFiles(config);
271
+ },
272
+ async transform(code, id, ops) {
273
+ if (!code.includes("createServerFunction") || ops?.ssr || code.includes("createServerFunction") && typeof process === "undefined") return null;
274
+ if (serverFunctionsMap.size === 0) await scanForServerFiles(config);
275
+ const vite = await import("vite");
276
+ const transformer = isOxc ? "transformWithOxc" : "transformWithEsbuild";
277
+ const langProp = isOxc ? "lang" : "loader";
278
+ const source = getClientModules({
279
+ rpcPreffix: options.rpcPreffix,
280
+ adapter: options.adapter
281
+ });
282
+ const result = await vite[transformer](source, id, {
283
+ [langProp]: "js",
284
+ sourcemap: true
285
+ });
286
+ return {
287
+ code: result.code,
288
+ map: result.map ? typeof result.map === "string" ? JSON.parse(result.map) : result.map : null
289
+ };
290
+ }
291
+ };
292
+ }
293
+ //#endregion
294
+ export { rpcPlugin as default, defineConfig, loadRPCConfig };
295
+
296
+ //# sourceMappingURL=index.mjs.map