@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,164 @@
1
+ import { scanForServerFiles, serverFunctionsMap } from "@thednp/rpc/server";
2
+ import fp from "fastify-plugin";
3
+ //#region src/options.ts
4
+ const defaultRPCOptions = {
5
+ rpcPreffix: "__rpc",
6
+ adapter: "express"
7
+ };
8
+ const defaultMiddlewareOptions = {
9
+ rpcPreffix: void 0,
10
+ path: void 0
11
+ };
12
+ //#endregion
13
+ //#region src/tools.ts
14
+ function escapeRegExp(s) {
15
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16
+ }
17
+ //#endregion
18
+ //#region src/fastify/plugin.ts
19
+ const RpcPlugin = (fastify, initialOptions, done) => {
20
+ const rpcMiddleware = createRPCMiddleware(initialOptions);
21
+ fastify.addHook("preHandler", async (request, reply) => {
22
+ const next = () => new Promise((resolve) => {
23
+ rpcMiddleware(request, reply, resolve);
24
+ });
25
+ await next();
26
+ });
27
+ done();
28
+ };
29
+ const rpcPlugin = fp(RpcPlugin, { name: "uni-rpc-fastify-plugin" });
30
+ //#endregion
31
+ //#region src/fastify/helpers.ts
32
+ async function attachRPC(app) {
33
+ const { loadRPCConfig } = await import("@thednp/rpc");
34
+ const { adapter: _adapter, ...options } = await loadRPCConfig();
35
+ await app.register(rpcPlugin, options);
36
+ }
37
+ function attachVite(app, vite) {
38
+ app.addHook("onRequest", async (request, reply) => {
39
+ const next = () => new Promise((resolve) => {
40
+ vite.middlewares(request.raw, reply.raw, resolve);
41
+ });
42
+ await next();
43
+ });
44
+ }
45
+ const readBody = (req) => {
46
+ return new Promise((resolve, reject) => {
47
+ const contentType = req.headers["content-type"]?.toLowerCase() || "";
48
+ const reqBody = req.body;
49
+ if (reqBody !== void 0) {
50
+ const isJSON = contentType.includes("json");
51
+ resolve({
52
+ contentType: isJSON ? "application/json" : "text/plain",
53
+ data: isJSON ? reqBody : String(reqBody)
54
+ });
55
+ return;
56
+ }
57
+ const toggleListeners = (add) => {
58
+ const method = add ? "on" : "off";
59
+ req.raw[method]("data", onData);
60
+ req.raw[method]("end", onEnd);
61
+ req.raw[method]("error", onError);
62
+ };
63
+ let body = "";
64
+ const onData = (chunk) => {
65
+ body += chunk.toString();
66
+ };
67
+ const onEnd = () => {
68
+ toggleListeners();
69
+ const isJSON = contentType.includes("json");
70
+ try {
71
+ const data = JSON.parse(body);
72
+ resolve({
73
+ contentType: isJSON ? "application/json" : "text/plain",
74
+ data
75
+ });
76
+ } catch (_e) {
77
+ resolve({
78
+ contentType: "text/plain",
79
+ data: String(body)
80
+ });
81
+ }
82
+ };
83
+ const onError = (err) => {
84
+ toggleListeners();
85
+ reject(err);
86
+ };
87
+ toggleListeners(true);
88
+ });
89
+ };
90
+ //#endregion
91
+ //#region src/fastify/createMiddleware.ts
92
+ let middlewareCount = 0;
93
+ const middlewareStack = /* @__PURE__ */ new Set();
94
+ const createMiddleware = (initialOptions = {}) => {
95
+ const options = Object.assign({}, defaultMiddlewareOptions, initialOptions);
96
+ const middlewareName = options.name;
97
+ const rpcPreffix = options.rpcPreffix;
98
+ const path = options.path;
99
+ const handler = options.handler;
100
+ let name = middlewareName;
101
+ if (!name) {
102
+ name = "viteRPCMiddleware-" + middlewareCount;
103
+ middlewareCount += 1;
104
+ }
105
+ if (middlewareStack.has(name)) throw new Error(`The middleware name "${name}" is already used.`);
106
+ middlewareStack.add(name);
107
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
108
+ const pathMatcher = path ? typeof path === "string" ? new RegExp(path) : path : null;
109
+ const middlewareHandler = async (req, _reply, done) => {
110
+ const url = new URL(req.url, "http://localhost").pathname;
111
+ if (serverFunctionsMap.size === 0) await scanForServerFiles();
112
+ if (!handler) {
113
+ done();
114
+ return;
115
+ }
116
+ if (pathMatcher && !pathMatcher.test(url)) {
117
+ done();
118
+ return;
119
+ }
120
+ if (prefixRegex && !prefixRegex.test(url)) {
121
+ done();
122
+ return;
123
+ }
124
+ await handler(req, _reply, done);
125
+ };
126
+ Object.defineProperty(middlewareHandler, "name", { value: name });
127
+ return middlewareHandler;
128
+ };
129
+ const createRPCMiddleware = (initialOptions = {}) => {
130
+ const options = Object.assign({}, defaultMiddlewareOptions, { rpcPreffix: defaultRPCOptions.rpcPreffix }, initialOptions);
131
+ const rpcPreffix = options.rpcPreffix;
132
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
133
+ const prefixReplace = `/${rpcPreffix}/`;
134
+ return createMiddleware({
135
+ ...options,
136
+ handler: async (req, reply, _done) => {
137
+ const url = new URL(req.url, "http://localhost").pathname;
138
+ if (prefixRegex && !prefixRegex.test(url)) return;
139
+ const functionName = url.replace(prefixReplace, "");
140
+ const serverFunction = serverFunctionsMap.get(functionName);
141
+ if (!serverFunction) {
142
+ reply.status(404).send({ error: "Function not found" });
143
+ return;
144
+ }
145
+ try {
146
+ const body = await readBody(req);
147
+ const args = Array.isArray(body.data) ? body.data : [body.data];
148
+ const { data: dataResult, cancel } = serverFunction.handler(...args);
149
+ const onClose = () => cancel("client disconnected");
150
+ req.raw.on("close", onClose);
151
+ const data = await dataResult;
152
+ req.raw.off("close", onClose);
153
+ if (!reply.raw.headersSent) reply.status(200).send({ data });
154
+ } catch (err) {
155
+ console.error(String(err));
156
+ reply.status(500).send({ error: "Internal Server Error" });
157
+ }
158
+ }
159
+ });
160
+ };
161
+ //#endregion
162
+ export { attachRPC, attachVite, createMiddleware, createRPCMiddleware, readBody };
163
+
164
+ //# sourceMappingURL=fastify.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.mjs","names":["fastifyRpcPlugin"],"sources":["../../src/options.ts","../../src/tools.ts","../../src/fastify/plugin.ts","../../src/fastify/helpers.ts","../../src/fastify/createMiddleware.ts"],"sourcesContent":["import type {\n MiddlewareOptions,\n RpcPluginOptions,\n ServerFunctionOptions,\n} from \"./types.d.ts\";\n\nexport const defaultServerFnOptions = {\n contentType: \"application/json\",\n} 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/fastify/plugin.ts\n// inspired by https://github.com/royalswe/vike-fastify-boilerplate/blob/main/server/index.ts\nimport type { FastifyInstance } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport type { MiddlewareOptions } from \"../types.d.ts\";\nimport { createRPCMiddleware } from \"./createMiddleware.ts\";\n\nexport type { MiddlewareOptions };\n\n// Define the plugin factory\nconst RpcPlugin = (\n fastify: FastifyInstance,\n initialOptions: Partial<MiddlewareOptions<\"fastify\">>,\n done: () => void,\n) => {\n // Register RPC middleware as preHandler hook\n const rpcMiddleware = createRPCMiddleware(initialOptions);\n fastify.addHook(\"preHandler\", async (request, reply) => {\n const next = () =>\n new Promise((resolve) => {\n rpcMiddleware(request, reply, resolve);\n });\n await next();\n });\n\n done();\n};\n\n// Export the plugin wrapped with fastify-plugin\nconst rpcPlugin = fp(RpcPlugin, {\n name: \"uni-rpc-fastify-plugin\",\n});\n\nexport { rpcPlugin as default };\n","// @thednp/rpc/src/fastify/helpers.ts\nimport type { FastifyRequest } from \"fastify\";\nimport type { ViteDevServer } from \"vite\";\nimport type { FastifyInstance } from \"fastify\";\nimport { Buffer } from \"node:buffer\";\nimport fastifyRpcPlugin from \"./plugin.ts\";\nimport type { BodyResult, JsonValue } from \"../types.d.ts\";\n\nexport async function attachRPC(app: FastifyInstance) {\n const { loadRPCConfig } = await import(\"@thednp/rpc\");\n const { adapter: _adapter, ...options } = await loadRPCConfig();\n await app.register(fastifyRpcPlugin, options);\n}\n\nexport function attachVite(app: FastifyInstance, vite: ViteDevServer) {\n app.addHook(\"onRequest\", async (request, reply) => {\n const next = () =>\n new Promise((resolve) => {\n vite.middlewares(request.raw, reply.raw, resolve);\n });\n await next();\n });\n}\n\nexport const readBody = (\n req: FastifyRequest,\n): Promise<BodyResult> => {\n return new Promise((resolve, reject) => {\n const contentType = req.headers[\"content-type\"]?.toLowerCase() || \"\";\n const reqBody = req.body as JsonValue;\n\n if (reqBody !== undefined) {\n const isJSON = contentType.includes(\"json\");\n resolve({\n contentType: isJSON ? \"application/json\" : \"text/plain\",\n data: isJSON ? reqBody : String(reqBody),\n } as BodyResult);\n return;\n }\n\n const toggleListeners = (add?: boolean) => {\n const method = add ? \"on\" : \"off\";\n req.raw[method](\"data\", onData);\n req.raw[method](\"end\", onEnd);\n req.raw[method](\"error\", onError);\n };\n\n let body = \"\";\n\n const onData = (chunk: Buffer) => {\n body += chunk.toString();\n };\n\n const onEnd = () => {\n toggleListeners();\n const isJSON = contentType.includes(\"json\");\n try {\n const data = JSON.parse(body);\n resolve({\n contentType: isJSON ? \"application/json\" : \"text/plain\",\n data,\n });\n } catch (_e) {\n resolve({ contentType: \"text/plain\", data: String(body) });\n }\n };\n const onError = (err: Error) => {\n toggleListeners();\n reject(err);\n };\n\n toggleListeners(true);\n });\n};\n","// src/fastify/createMiddleware.ts\nimport type {\n FastifyReply,\n FastifyRequest,\n HookHandlerDoneFunction,\n} from \"fastify\";\nimport type {\n FastifyMiddlewareFn,\n FastifyMiddlewareOptions,\n} from \"./types.d.ts\";\n// import type { JsonArray, JsonValue } from \"@thednp/rpc\";\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\n// Define the middleware function for Fastify\nexport const createMiddleware: FastifyMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n initialOptions,\n ) as FastifyMiddlewareOptions;\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 = async (\n req: FastifyRequest,\n _reply: FastifyReply,\n done: HookHandlerDoneFunction,\n ) => {\n const reqUrl = new URL(req.url, \"http://localhost\");\n const url = reqUrl.pathname;\n\n // When serving from production server, scan for server files\n if (serverFunctionsMap.size === 0) {\n await scanForServerFiles();\n }\n\n // No need to continue when no handler provided\n if (!handler) {\n done();\n return;\n }\n\n // Path matching\n if (pathMatcher && !pathMatcher.test(url)) {\n done();\n return;\n }\n\n // rpcPreffix matching (boundary-safe via escaped regex)\n if (prefixRegex && !prefixRegex.test(url)) {\n done();\n return;\n }\n\n // Execute handler\n await handler(req, _reply, done);\n };\n\n Object.defineProperty(middlewareHandler, \"name\", {\n value: name,\n });\n\n return middlewareHandler;\n};\n\nexport const createRPCMiddleware: FastifyMiddlewareFn = (\n initialOptions = {},\n) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n { rpcPreffix: defaultRPCOptions.rpcPreffix },\n initialOptions,\n ) as FastifyMiddlewareOptions;\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 (\n req: FastifyRequest,\n reply: FastifyReply,\n _done: HookHandlerDoneFunction,\n ) => {\n const reqUrl = new URL(req.url, \"http://localhost\");\n const url = reqUrl.pathname;\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(url)) {\n return;\n }\n\n const functionName = url.replace(prefixReplace, \"\");\n const serverFunction = serverFunctionsMap.get(functionName);\n\n if (!serverFunction) {\n reply.status(404).send({\n error: \"Function not found\",\n });\n return;\n }\n\n try {\n const body = await readBody(req);\n const args = Array.isArray(body.data) ? body.data : [body.data];\n const { data: dataResult, cancel } = serverFunction.handler(...args);\n const onClose = () => cancel(\"client disconnected\");\n\n req.raw.on(\"close\", onClose);\n const data = await dataResult;\n req.raw.off(\"close\", onClose);\n\n // istanbul ignore else\n if (!reply.raw.headersSent) reply.status(200).send({ data });\n } catch (err) {\n console.error(String(err));\n reply.status(500).send({ error: \"Internal Server Error\" });\n }\n },\n });\n};\n"],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ import { FastifyInstance, FastifyReply, FastifyRequest, HookHandlerDoneFunction } from "fastify";
2
+ import { Connect } from "vite";
3
+ import "@thednp/rpc";
4
+ import { IncomingMessage, ServerResponse } from "node:http";
5
+ import { NextFunction, Request, Response as Response$1 } from "express";
6
+ import { MiddlewareHandler } from "hono";
7
+ import "@hono/node-server";
8
+ import { Context, Next } from "koa";
9
+ //#region src/express/types.d.ts
10
+ interface ExpressMiddlewareHooks {
11
+ handler: (req: IncomingMessage | Request, res: ServerResponse | Response$1, next: Connect.NextFunction | NextFunction) => Promise<void>;
12
+ }
13
+ //#endregion
14
+ //#region src/hono/types.d.ts
15
+ interface HonoMiddlewareHooks {
16
+ handler: MiddlewareHandler;
17
+ }
18
+ //#endregion
19
+ //#region src/fastify/types.d.ts
20
+ interface FastifyMiddlewareHooks {
21
+ handler: (req: FastifyRequest, res: FastifyReply, done: HookHandlerDoneFunction) => Promise<void>;
22
+ }
23
+ //#endregion
24
+ //#region src/koa/types.d.ts
25
+ interface KoaMiddlewareHooks {
26
+ handler: (ctx: Context, next: Next) => Promise<void>;
27
+ }
28
+ //#endregion
29
+ //#region src/types.d.ts
30
+ interface FrameworkHooks {
31
+ express: ExpressMiddlewareHooks;
32
+ hono: HonoMiddlewareHooks;
33
+ fastify: FastifyMiddlewareHooks;
34
+ koa: KoaMiddlewareHooks;
35
+ }
36
+ /**
37
+ * ### @thednp/rpc
38
+ * The plugin configuration allows for granular control of your
39
+ * application RPC calls. The default settings are optimized for development
40
+ * environments while providing a secure foundation for production use.
41
+ */
42
+ interface RpcPluginOptions {
43
+ // RPC Middleware Options
44
+ /**
45
+ * RPC prefix without leading slash (e.g. "__rpc")
46
+ * Leading slash will be added automatically by the middleware.
47
+ * This prefix defines the base path for all RPC endpoints.
48
+ * @default "__rpc"
49
+ * @example
50
+ * // Results in endpoints like: /api/rpc/myFunction
51
+ * rpcPreffix: "api/rpc"
52
+ */
53
+ rpcPreffix: "__rpc" | string;
54
+ /**
55
+ * Option to set an adapter for the middleware connection. The default is _express_,
56
+ * which is the most popular and battle tested server app. The _express_ adapter is
57
+ * also compatible with the vite's Connect development server.
58
+ * @default express
59
+ */
60
+ adapter: "express" | "hono" | "fastify" | "koa";
61
+ }
62
+ interface MiddlewareOptions<A extends RpcPluginOptions["adapter"] = "express"> {
63
+ /**
64
+ * Name for the middleware (used for identification in Express stack)
65
+ */
66
+ name?: string;
67
+ /**
68
+ * Path pattern to match for middleware execution.
69
+ * Accepts string or RegExp to filter requests based on URL path.
70
+ *
71
+ * @example
72
+ * // String path
73
+ * path: "/api/v1"
74
+ *
75
+ * // RegExp pattern
76
+ * path: /^\/api\/v[0-9]+/
77
+ */
78
+ path?: string | RegExp;
79
+ /**
80
+ * RPC prefix without leading slash (e.g. "__rpc")
81
+ * Leading slash will be added automatically by the middleware.
82
+ * This prefix defines the base path for all RPC endpoints.
83
+ * @default string
84
+ * @example
85
+ * // Results in endpoints like: /api/rpc/myFunction
86
+ * rpcPreffix: "api/rpc"
87
+ */
88
+ rpcPreffix?: string | false;
89
+ /**
90
+ * Async handler for request processing.
91
+ * Core middleware function that processes incoming requests.
92
+ *
93
+ * @param req - The incoming request object
94
+ * @param res - The server response object
95
+ * @param next - Function to pass control to the next middleware
96
+ *
97
+ * @example
98
+ * handler: async (req, res, next) => {
99
+ * // Process request
100
+ * const data = await processRequest(req);
101
+ *
102
+ * // Send response
103
+ * sendResponse(res, { data }, 200);
104
+ * }
105
+ */
106
+ handler?: FrameworkHooks[A]["handler"];
107
+ }
108
+ //#endregion
109
+ //#region src/fastify/plugin.d.ts
110
+ declare const RpcPlugin: (fastify: FastifyInstance, initialOptions: Partial<MiddlewareOptions<"fastify">>, done: () => void) => void;
111
+ declare const rpcPlugin: typeof RpcPlugin;
112
+ //#endregion
113
+ export { type MiddlewareOptions, rpcPlugin as default };
114
+ //# sourceMappingURL=plugin.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.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/fastify/plugin.ts"],"mappings":";;;;;;;;;UAaiB;EACf,UACE,KAAK,kBAAkB,SACvB,KAAK,iBAAiB,YACtB,MAAM,QAAQ,eAAe,iBAC1B;;;;UCbU;EACf,SAAS;;;;UCSM;EACf,UACE,KAAK,gBACL,KAAK,cACL,MAAM,4BACH;;;;UCNU;EACf,UAAU,KAAK,SAAS,MAAM,SAAS;;;;UCAxB;EACf,SAAS;EACT,MAAM;EACN,SAAS;EACT,KAAK;;;;;;;;UA8FU;;;;;;;;;;;EAWf;;;;;;;EAQA;;UAGe,kBACf,UAAU;;;;EAKV;;;;;;;;;;;;EAaA,gBAAgB;;;;;;;;;;EAWhB;;;;;;;;;;;;;;;;;;EAmBA,UAAU,eAAe;;;;cC9KrB,YAAS,SACJ,iBAAe,gBACR,QAAQ,+BAA6B;cAiBjD,kBAAS"}
@@ -0,0 +1,151 @@
1
+ import fp from "fastify-plugin";
2
+ import { scanForServerFiles, serverFunctionsMap } from "@thednp/rpc/server";
3
+ //#region src/options.ts
4
+ const defaultRPCOptions = {
5
+ rpcPreffix: "__rpc",
6
+ adapter: "express"
7
+ };
8
+ const defaultMiddlewareOptions = {
9
+ rpcPreffix: void 0,
10
+ path: void 0
11
+ };
12
+ //#endregion
13
+ //#region src/tools.ts
14
+ function escapeRegExp(s) {
15
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16
+ }
17
+ //#endregion
18
+ //#region src/fastify/helpers.ts
19
+ const readBody = (req) => {
20
+ return new Promise((resolve, reject) => {
21
+ const contentType = req.headers["content-type"]?.toLowerCase() || "";
22
+ const reqBody = req.body;
23
+ if (reqBody !== void 0) {
24
+ const isJSON = contentType.includes("json");
25
+ resolve({
26
+ contentType: isJSON ? "application/json" : "text/plain",
27
+ data: isJSON ? reqBody : String(reqBody)
28
+ });
29
+ return;
30
+ }
31
+ const toggleListeners = (add) => {
32
+ const method = add ? "on" : "off";
33
+ req.raw[method]("data", onData);
34
+ req.raw[method]("end", onEnd);
35
+ req.raw[method]("error", onError);
36
+ };
37
+ let body = "";
38
+ const onData = (chunk) => {
39
+ body += chunk.toString();
40
+ };
41
+ const onEnd = () => {
42
+ toggleListeners();
43
+ const isJSON = contentType.includes("json");
44
+ try {
45
+ const data = JSON.parse(body);
46
+ resolve({
47
+ contentType: isJSON ? "application/json" : "text/plain",
48
+ data
49
+ });
50
+ } catch (_e) {
51
+ resolve({
52
+ contentType: "text/plain",
53
+ data: String(body)
54
+ });
55
+ }
56
+ };
57
+ const onError = (err) => {
58
+ toggleListeners();
59
+ reject(err);
60
+ };
61
+ toggleListeners(true);
62
+ });
63
+ };
64
+ //#endregion
65
+ //#region src/fastify/createMiddleware.ts
66
+ let middlewareCount = 0;
67
+ const middlewareStack = /* @__PURE__ */ new Set();
68
+ const createMiddleware = (initialOptions = {}) => {
69
+ const options = Object.assign({}, defaultMiddlewareOptions, initialOptions);
70
+ const middlewareName = options.name;
71
+ const rpcPreffix = options.rpcPreffix;
72
+ const path = options.path;
73
+ const handler = options.handler;
74
+ let name = middlewareName;
75
+ if (!name) {
76
+ name = "viteRPCMiddleware-" + middlewareCount;
77
+ middlewareCount += 1;
78
+ }
79
+ if (middlewareStack.has(name)) throw new Error(`The middleware name "${name}" is already used.`);
80
+ middlewareStack.add(name);
81
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
82
+ const pathMatcher = path ? typeof path === "string" ? new RegExp(path) : path : null;
83
+ const middlewareHandler = async (req, _reply, done) => {
84
+ const url = new URL(req.url, "http://localhost").pathname;
85
+ if (serverFunctionsMap.size === 0) await scanForServerFiles();
86
+ if (!handler) {
87
+ done();
88
+ return;
89
+ }
90
+ if (pathMatcher && !pathMatcher.test(url)) {
91
+ done();
92
+ return;
93
+ }
94
+ if (prefixRegex && !prefixRegex.test(url)) {
95
+ done();
96
+ return;
97
+ }
98
+ await handler(req, _reply, done);
99
+ };
100
+ Object.defineProperty(middlewareHandler, "name", { value: name });
101
+ return middlewareHandler;
102
+ };
103
+ const createRPCMiddleware = (initialOptions = {}) => {
104
+ const options = Object.assign({}, defaultMiddlewareOptions, { rpcPreffix: defaultRPCOptions.rpcPreffix }, initialOptions);
105
+ const rpcPreffix = options.rpcPreffix;
106
+ const prefixRegex = rpcPreffix ? new RegExp(`^/${escapeRegExp(rpcPreffix)}/`) : null;
107
+ const prefixReplace = `/${rpcPreffix}/`;
108
+ return createMiddleware({
109
+ ...options,
110
+ handler: async (req, reply, _done) => {
111
+ const url = new URL(req.url, "http://localhost").pathname;
112
+ if (prefixRegex && !prefixRegex.test(url)) return;
113
+ const functionName = url.replace(prefixReplace, "");
114
+ const serverFunction = serverFunctionsMap.get(functionName);
115
+ if (!serverFunction) {
116
+ reply.status(404).send({ error: "Function not found" });
117
+ return;
118
+ }
119
+ try {
120
+ const body = await readBody(req);
121
+ const args = Array.isArray(body.data) ? body.data : [body.data];
122
+ const { data: dataResult, cancel } = serverFunction.handler(...args);
123
+ const onClose = () => cancel("client disconnected");
124
+ req.raw.on("close", onClose);
125
+ const data = await dataResult;
126
+ req.raw.off("close", onClose);
127
+ if (!reply.raw.headersSent) reply.status(200).send({ data });
128
+ } catch (err) {
129
+ console.error(String(err));
130
+ reply.status(500).send({ error: "Internal Server Error" });
131
+ }
132
+ }
133
+ });
134
+ };
135
+ //#endregion
136
+ //#region src/fastify/plugin.ts
137
+ const RpcPlugin = (fastify, initialOptions, done) => {
138
+ const rpcMiddleware = createRPCMiddleware(initialOptions);
139
+ fastify.addHook("preHandler", async (request, reply) => {
140
+ const next = () => new Promise((resolve) => {
141
+ rpcMiddleware(request, reply, resolve);
142
+ });
143
+ await next();
144
+ });
145
+ done();
146
+ };
147
+ const rpcPlugin = fp(RpcPlugin, { name: "uni-rpc-fastify-plugin" });
148
+ //#endregion
149
+ export { rpcPlugin as default };
150
+
151
+ //# sourceMappingURL=plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../../../../src/options.ts","../../../../src/tools.ts","../../../../src/fastify/helpers.ts","../../../../src/fastify/createMiddleware.ts","../../../../src/fastify/plugin.ts"],"sourcesContent":["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","// @thednp/rpc/src/fastify/helpers.ts\nimport type { FastifyRequest } from \"fastify\";\nimport type { ViteDevServer } from \"vite\";\nimport type { FastifyInstance } from \"fastify\";\nimport { Buffer } from \"node:buffer\";\nimport fastifyRpcPlugin from \"./plugin.ts\";\nimport type { BodyResult, JsonValue } from \"../types.d.ts\";\n\nexport async function attachRPC(app: FastifyInstance) {\n const { loadRPCConfig } = await import(\"@thednp/rpc\");\n const { adapter: _adapter, ...options } = await loadRPCConfig();\n await app.register(fastifyRpcPlugin, options);\n}\n\nexport function attachVite(app: FastifyInstance, vite: ViteDevServer) {\n app.addHook(\"onRequest\", async (request, reply) => {\n const next = () =>\n new Promise((resolve) => {\n vite.middlewares(request.raw, reply.raw, resolve);\n });\n await next();\n });\n}\n\nexport const readBody = (\n req: FastifyRequest,\n): Promise<BodyResult> => {\n return new Promise((resolve, reject) => {\n const contentType = req.headers[\"content-type\"]?.toLowerCase() || \"\";\n const reqBody = req.body as JsonValue;\n\n if (reqBody !== undefined) {\n const isJSON = contentType.includes(\"json\");\n resolve({\n contentType: isJSON ? \"application/json\" : \"text/plain\",\n data: isJSON ? reqBody : String(reqBody),\n } as BodyResult);\n return;\n }\n\n const toggleListeners = (add?: boolean) => {\n const method = add ? \"on\" : \"off\";\n req.raw[method](\"data\", onData);\n req.raw[method](\"end\", onEnd);\n req.raw[method](\"error\", onError);\n };\n\n let body = \"\";\n\n const onData = (chunk: Buffer) => {\n body += chunk.toString();\n };\n\n const onEnd = () => {\n toggleListeners();\n const isJSON = contentType.includes(\"json\");\n try {\n const data = JSON.parse(body);\n resolve({\n contentType: isJSON ? \"application/json\" : \"text/plain\",\n data,\n });\n } catch (_e) {\n resolve({ contentType: \"text/plain\", data: String(body) });\n }\n };\n const onError = (err: Error) => {\n toggleListeners();\n reject(err);\n };\n\n toggleListeners(true);\n });\n};\n","// src/fastify/createMiddleware.ts\nimport type {\n FastifyReply,\n FastifyRequest,\n HookHandlerDoneFunction,\n} from \"fastify\";\nimport type {\n FastifyMiddlewareFn,\n FastifyMiddlewareOptions,\n} from \"./types.d.ts\";\n// import type { JsonArray, JsonValue } from \"@thednp/rpc\";\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\n// Define the middleware function for Fastify\nexport const createMiddleware: FastifyMiddlewareFn = (initialOptions = {}) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n initialOptions,\n ) as FastifyMiddlewareOptions;\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 = async (\n req: FastifyRequest,\n _reply: FastifyReply,\n done: HookHandlerDoneFunction,\n ) => {\n const reqUrl = new URL(req.url, \"http://localhost\");\n const url = reqUrl.pathname;\n\n // When serving from production server, scan for server files\n if (serverFunctionsMap.size === 0) {\n await scanForServerFiles();\n }\n\n // No need to continue when no handler provided\n if (!handler) {\n done();\n return;\n }\n\n // Path matching\n if (pathMatcher && !pathMatcher.test(url)) {\n done();\n return;\n }\n\n // rpcPreffix matching (boundary-safe via escaped regex)\n if (prefixRegex && !prefixRegex.test(url)) {\n done();\n return;\n }\n\n // Execute handler\n await handler(req, _reply, done);\n };\n\n Object.defineProperty(middlewareHandler, \"name\", {\n value: name,\n });\n\n return middlewareHandler;\n};\n\nexport const createRPCMiddleware: FastifyMiddlewareFn = (\n initialOptions = {},\n) => {\n const options = Object.assign(\n {},\n defaultMiddlewareOptions,\n { rpcPreffix: defaultRPCOptions.rpcPreffix },\n initialOptions,\n ) as FastifyMiddlewareOptions;\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 (\n req: FastifyRequest,\n reply: FastifyReply,\n _done: HookHandlerDoneFunction,\n ) => {\n const reqUrl = new URL(req.url, \"http://localhost\");\n const url = reqUrl.pathname;\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(url)) {\n return;\n }\n\n const functionName = url.replace(prefixReplace, \"\");\n const serverFunction = serverFunctionsMap.get(functionName);\n\n if (!serverFunction) {\n reply.status(404).send({\n error: \"Function not found\",\n });\n return;\n }\n\n try {\n const body = await readBody(req);\n const args = Array.isArray(body.data) ? body.data : [body.data];\n const { data: dataResult, cancel } = serverFunction.handler(...args);\n const onClose = () => cancel(\"client disconnected\");\n\n req.raw.on(\"close\", onClose);\n const data = await dataResult;\n req.raw.off(\"close\", onClose);\n\n // istanbul ignore else\n if (!reply.raw.headersSent) reply.status(200).send({ data });\n } catch (err) {\n console.error(String(err));\n reply.status(500).send({ error: \"Internal Server Error\" });\n }\n },\n });\n};\n","// src/fastify/plugin.ts\n// inspired by https://github.com/royalswe/vike-fastify-boilerplate/blob/main/server/index.ts\nimport type { FastifyInstance } from \"fastify\";\nimport fp from \"fastify-plugin\";\nimport type { MiddlewareOptions } from \"../types.d.ts\";\nimport { createRPCMiddleware } from \"./createMiddleware.ts\";\n\nexport type { MiddlewareOptions };\n\n// Define the plugin factory\nconst RpcPlugin = (\n fastify: FastifyInstance,\n initialOptions: Partial<MiddlewareOptions<\"fastify\">>,\n done: () => void,\n) => {\n // Register RPC middleware as preHandler hook\n const rpcMiddleware = createRPCMiddleware(initialOptions);\n fastify.addHook(\"preHandler\", async (request, reply) => {\n const next = () =>\n new Promise((resolve) => {\n rpcMiddleware(request, reply, resolve);\n });\n await next();\n });\n\n done();\n};\n\n// Export the plugin wrapped with fastify-plugin\nconst rpcPlugin = fp(RpcPlugin, {\n name: \"uni-rpc-fastify-plugin\",\n});\n\nexport { rpcPlugin as default };\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ //#region src/helpers.d.ts
2
+ declare const handleResponse: (response: Response) => Promise<any>;
3
+ declare const innerModule: (body: BodyInit, headers: HeadersInit, preffix: string, name: string) => {
4
+ data: Promise<any>;
5
+ cancel: (reason: string) => void;
6
+ };
7
+ //#endregion
8
+ export { handleResponse, innerModule };
9
+ //# sourceMappingURL=helpers.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.mts","names":[],"sources":["../../src/helpers.ts"],"mappings":";cACa,iBAAc,UAAoB,aAAQ;cAY1C,cAAW,MAChB,UAAQ,SACL,aAAW,iBACL;;EAIS,SAAA"}
@@ -0,0 +1,36 @@
1
+ //#region src/helpers.ts
2
+ const handleResponse = async (response) => {
3
+ if (!response.ok) {
4
+ if (response.status === 499 || response.status === 408) return console.warn("Request was cancelled");
5
+ throw new Error("Fetch error: " + response.statusText);
6
+ }
7
+ const result = await response.json();
8
+ if (result.error) throw new Error(result.error);
9
+ return result.data;
10
+ };
11
+ const innerModule = (body, headers, preffix, name) => {
12
+ const controller = new AbortController();
13
+ const cancel = (reason) => controller.abort(reason);
14
+ const fetcher = async () => {
15
+ try {
16
+ const response = await fetch(`/${preffix}/${name}`, {
17
+ method: "POST",
18
+ headers,
19
+ credentials: "include",
20
+ body,
21
+ signal: controller.signal
22
+ });
23
+ return await handleResponse(response);
24
+ } catch (err) {
25
+ throw err;
26
+ }
27
+ };
28
+ return {
29
+ data: fetcher(),
30
+ cancel
31
+ };
32
+ };
33
+ //#endregion
34
+ export { handleResponse, innerModule };
35
+
36
+ //# sourceMappingURL=helpers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.mjs","names":[],"sources":["../../src/helpers.ts"],"sourcesContent":["// Client-side RPC sub-modules\nexport const handleResponse = async (response: Response) => {\n if (!response.ok) {\n if (response.status === 499 || response.status === 408) {\n return console.warn(\"Request was cancelled\");\n }\n throw new Error(\"Fetch error: \" + response.statusText);\n }\n const result = await response.json();\n if (result.error) throw new Error(result.error);\n return result.data;\n};\n\nexport const innerModule = (\n body: BodyInit,\n headers: HeadersInit,\n preffix: string,\n name: string,\n) => {\n const controller = new AbortController();\n const cancel = (reason: string) => controller.abort(reason);\n\n const fetcher = async () => {\n try {\n const response = await fetch(`/${preffix}/${name}`, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body,\n signal: controller.signal,\n });\n return await handleResponse(response);\n } catch (err) {\n throw err;\n }\n };\n\n return {\n data: fetcher(),\n cancel,\n };\n};\n"],"mappings":";AACA,MAAa,iBAAiB,OAAO,aAAuB;CAC1D,IAAI,CAAC,SAAS,IAAI;EAChB,IAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KACjD,OAAO,QAAQ,KAAK,uBAAuB;EAE7C,MAAM,IAAI,MAAM,kBAAkB,SAAS,UAAU;CACvD;CACA,MAAM,SAAS,MAAM,SAAS,KAAK;CACnC,IAAI,OAAO,OAAO,MAAM,IAAI,MAAM,OAAO,KAAK;CAC9C,OAAO,OAAO;AAChB;AAEA,MAAa,eACX,MACA,SACA,SACA,SACG;CACH,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,WAAmB,WAAW,MAAM,MAAM;CAE1D,MAAM,UAAU,YAAY;EAC1B,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,IAAI,QAAQ,GAAG,QAAQ;IAClD,QAAQ;IACR;IACA,aAAa;IACb;IACA,QAAQ,WAAW;GACrB,CAAC;GACD,OAAO,MAAM,eAAe,QAAQ;EACtC,SAAS,KAAK;GACZ,MAAM;EACR;CACF;CAEA,OAAO;EACL,MAAM,QAAQ;EACd;CACF;AACF"}
@@ -0,0 +1,49 @@
1
+ import { Context, Hono, MiddlewareHandler } from "hono";
2
+ import { MiddlewareOptions, RpcPluginOptions } from "@thednp/rpc";
3
+ import { HttpBindings } from "@hono/node-server";
4
+ import { ViteDevServer } from "vite";
5
+ import "express";
6
+ import "fastify";
7
+ import "koa";
8
+ //#region src/hono/types.d.ts
9
+ type HonoMiddlewareOptions = MiddlewareOptions<"hono">;
10
+ interface HonoMiddlewareHooks {
11
+ handler: MiddlewareHandler;
12
+ }
13
+ type HonoMiddlewareFn = <A extends RpcPluginOptions["adapter"] = "hono">(initialOptions?: Partial<MiddlewareOptions<A>>) => HonoMiddlewareHooks["handler"];
14
+ //#endregion
15
+ //#region src/hono/createMiddleware.d.ts
16
+ declare const createMiddleware: HonoMiddlewareFn;
17
+ declare const createRPCMiddleware: HonoMiddlewareFn;
18
+ //#endregion
19
+ //#region src/types.d.ts
20
+ type BodyResult = {
21
+ contentType: "application/json";
22
+ data: JsonValue;
23
+ } | {
24
+ contentType: "text/plain";
25
+ data: string;
26
+ };
27
+ // primitives and their compositions
28
+ type JsonPrimitive = string | number | boolean | null | undefined;
29
+ type JsonObject = {
30
+ [key: string]: JsonValue | JsonArray;
31
+ };
32
+ type JsonArray = JsonValue[];
33
+ type JsonValue = JsonPrimitive | JsonArray | JsonObject;
34
+ //#endregion
35
+ //#region src/hono/helpers.d.ts
36
+ declare function attachRPC(app: Hono): Promise<void>;
37
+ declare function attachVite(app: Hono, vite: ViteDevServer): void;
38
+ /**
39
+ * Creates a hono compatible middleware for a given vite development server.
40
+ * @see https://github.com/honojs/hono/issues/3162#issuecomment-2331118049
41
+ * @param vite the vite development server
42
+ */
43
+ declare const viteMiddleware: (vite: ViteDevServer) => import("hono").MiddlewareHandler<{
44
+ Bindings: HttpBindings;
45
+ }, string, {}, Response>;
46
+ declare const readBody: (c: Context) => Promise<BodyResult>;
47
+ //#endregion
48
+ export { type HonoMiddlewareFn, type HonoMiddlewareHooks, type HonoMiddlewareOptions, attachRPC, attachVite, createMiddleware, createRPCMiddleware, readBody, viteMiddleware };
49
+ //# sourceMappingURL=hono.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.mts","names":[],"sources":["../../src/hono/types.d.ts","../../src/hono/createMiddleware.ts","../../src/types.d.ts","../../src/hono/helpers.ts"],"mappings":";;;;;;;;KAGY,wBAAwB;UAEnB;EACf,SAAS;;KAGC,oBAAoB,UAAU,sCACxC,iBAAiB,QAAQ,kBAAkB,QACxC;;;cCEQ,kBAAkB;cAmElB,qBAAqB;;;KC3CtB;EACN;EAAiC,MAAM;;EACvC;EAA2B;;;KAOrB;KACA;GAAgB,cAAc,YAAY;;KAC1C,YAAY;KACZ,YAAY,gBAAgB,YAAY;;;iBCvC9B,UAAU,KAAK,OAAI;iBAOzB,WAAW,KAAK,MAAM,MAAM;;;;;;cAS/B,iBAAc,MAAU,iCAAa;EACZ,UAAA;eAuCrC;cAEY,WAAQ,GAChB,YACF,QAAQ"}