rpc-express-toolkit 4.3.5 → 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,21 +6,108 @@
6
6
  [![npm version](https://img.shields.io/npm/v/rpc-express-toolkit.svg)](https://www.npmjs.com/package/rpc-express-toolkit)
7
7
  [![npm downloads](https://img.shields.io/npm/dm/rpc-express-toolkit.svg)](https://www.npmjs.com/package/rpc-express-toolkit)
8
8
  [![node](https://img.shields.io/node/v/rpc-express-toolkit.svg)](https://www.npmjs.com/package/rpc-express-toolkit)
9
+ [![Status](https://img.shields.io/badge/status-stable-green.svg)](https://github.com/n-car/rpc-express-toolkit/releases)
9
10
 
10
11
  JSON-RPC 2.0 toolkit for Express.js with simplified APIs, middleware, schema validation, batch support, and optional Safe Mode type preservation.
11
12
 
13
+ ## Which Package Should I Use?
14
+
15
+ - Use `rpc-express-toolkit` if you are building a JSON-RPC endpoint on Express.
16
+ - Use [`rpc-node-toolkit`](https://github.com/n-car/rpc-node-toolkit) if you want framework-agnostic Node.js or plain `node:http`.
17
+ - Use [`rpc-toolkit-js-client`](https://github.com/n-car/rpc-toolkit-js-client) if you only need a browser or Node.js client.
18
+ - Use [`rpc-toolkit`](https://github.com/n-car/rpc-toolkit) as the ecosystem hub and compatibility reference.
19
+
12
20
  ## Quick Start
13
21
 
14
22
  ### Installation
15
23
 
16
24
  ```bash
17
- npm install rpc-express-toolkit
25
+ npm install express rpc-express-toolkit
18
26
  # or
19
- yarn add rpc-express-toolkit
27
+ yarn add express rpc-express-toolkit
20
28
  ```
21
29
 
22
30
  Requirements:
31
+
23
32
  - Node.js 18+ (uses `globalThis.fetch`)
33
+ - Express 4.21+ or Express 5.x
34
+
35
+ ### Compatibility
36
+
37
+ `rpc-express-toolkit` is tested with Node.js 18, 20, 22, and 24 across Express 4 and Express 5. See [Compatibility](docs/COMPATIBILITY.md) for the current runtime matrix.
38
+
39
+ ### TypeScript
40
+
41
+ The package supports TypeScript ESM/NodeNext and CommonJS consumers. Both forms are tested with `strict: true`, `skipLibCheck: false`, and `esModuleInterop: false` against the tarball produced by `npm pack`.
42
+
43
+ Install the TypeScript and Express declarations used by these examples:
44
+
45
+ ```bash
46
+ npm install --save-dev typescript @types/express @types/node
47
+ ```
48
+
49
+ ESM/NodeNext (`package.json` contains `"type": "module"`):
50
+
51
+ ```typescript
52
+ import express from 'express';
53
+ import RpcEndpoint, {
54
+ RpcEndpoint as NamedRpcEndpoint,
55
+ RpcClient,
56
+ type RpcEndpointOptions,
57
+ } from 'rpc-express-toolkit';
58
+ import {
59
+ RpcSafeClient,
60
+ RpcSafeEndpoint,
61
+ } from 'rpc-express-toolkit/safe';
62
+
63
+ const app = express();
64
+ app.use(express.json());
65
+
66
+ const options: RpcEndpointOptions = { safeEnabled: false };
67
+ const rpc = new RpcEndpoint(app, {}, options);
68
+ const namedRpc = new NamedRpcEndpoint(app, {}, '/named');
69
+ const client = new RpcClient('http://localhost:3000/api');
70
+ const safeRpc = new RpcSafeEndpoint(app, {}, '/safe');
71
+ const safeClient = new RpcSafeClient('http://localhost:3000/safe');
72
+ ```
73
+
74
+ Use these compiler options for the ESM example:
75
+
76
+ ```json
77
+ {
78
+ "compilerOptions": {
79
+ "module": "NodeNext",
80
+ "moduleResolution": "NodeNext",
81
+ "strict": true,
82
+ "skipLibCheck": false,
83
+ "esModuleInterop": false,
84
+ "ignoreDeprecations": "6.0"
85
+ }
86
+ }
87
+ ```
88
+
89
+ `ignoreDeprecations` only acknowledges TypeScript 6's deprecation notice for
90
+ the explicitly tested `esModuleInterop: false` setting.
91
+
92
+ CommonJS TypeScript (`.cts` with NodeNext or Node16 resolution):
93
+
94
+ ```typescript
95
+ import express = require('express');
96
+ import RpcEndpoint = require('rpc-express-toolkit');
97
+ import Safe = require('rpc-express-toolkit/safe');
98
+
99
+ const app = express();
100
+ app.use(express.json());
101
+
102
+ const options: RpcEndpoint.RpcEndpointOptions = { safeEnabled: false };
103
+ const rpc = new RpcEndpoint(app, {}, options);
104
+ const namedRpc = new RpcEndpoint.RpcEndpoint(app, {}, '/named');
105
+ const client = new RpcEndpoint.RpcClient('http://localhost:3000/api');
106
+ const safeRpc = new Safe.RpcSafeEndpoint(app, {}, '/safe');
107
+ const safeClient = new Safe.RpcSafeClient('http://localhost:3000/safe');
108
+ ```
109
+
110
+ The root import remains the `RpcEndpoint` CommonJS default while also exposing its named API. The `/safe` subpath exposes the safe classes and the root utilities it re-exports at runtime.
24
111
 
25
112
  ### Server
26
113
 
@@ -142,7 +229,7 @@ Calls to methods outside the whitelist are rejected. This is a basic method rest
142
229
  [![npm (proxy)](https://img.shields.io/npm/v/rpc-express-toolkit-safe.svg)](https://www.npmjs.com/package/rpc-express-toolkit-safe)
143
230
  [![safe preset](https://img.shields.io/badge/safe%20preset-available-blue)](#safe-import-opt-in)
144
231
 
145
- When you control both client and server and want safer type round-trips, import the safe preset. It enables safe serialization by default (and strict mode on the server):
232
+ When you control both client and server and want safer type round-trips, import the built-in safe preset from `rpc-express-toolkit/safe`. It enables safe serialization by default and strict mode on the server:
146
233
 
147
234
  ```js
148
235
  // Server (safe preset)
@@ -161,9 +248,9 @@ const client = new RpcSafeClient('http://localhost:3000/api');
161
248
 
162
249
  This keeps JSON-RPC 2.0 compliance as default for the main entrypoint, while offering a convenient safe-mode import for projects that prefer explicit type disambiguation.
163
250
 
164
- Alternative: npm proxy package
251
+ #### Alternative Compatibility Package
165
252
 
166
- If you prefer a dedicated package name, you can install the thin proxy `rpc-express-toolkit-safe` which simply re-exports the safe preset:
253
+ `rpc-express-toolkit-safe` is a thin compatibility package that re-exports the same safe preset. Use it only when a separate package name is useful for dependency policy or migration; new projects can import `rpc-express-toolkit/safe` directly.
167
254
 
168
255
  ```bash
169
256
  npm install rpc-express-toolkit-safe
@@ -227,7 +314,7 @@ In `__rpc.capabilities`, `auth` is a boolean indicating whether authentication m
227
314
 
228
315
  For advanced configuration, middleware, structured logging, safe serialization, error handling, and more, see `README_ADVANCED.md`.
229
316
 
230
- ## 🔗 Related Projects
317
+ ## Related Projects
231
318
 
232
319
  - [rpc-php-toolkit](https://github.com/n-car/rpc-php-toolkit) - PHP implementation
233
320
  - [rpc-dotnet-toolkit](https://github.com/n-car/rpc-dotnet-toolkit) - .NET implementation
@@ -0,0 +1,64 @@
1
+ # Compatibility
2
+
3
+ `rpc-express-toolkit` is tested as an Express integration library, not as an application framework replacement.
4
+
5
+ ## Runtime Matrix
6
+
7
+ | Runtime | Status |
8
+ | --- | --- |
9
+ | Node.js 18.x | CI tested |
10
+ | Node.js 20.x | CI tested |
11
+ | Node.js 22.x | CI tested |
12
+ | Node.js 24.x | CI tested |
13
+ | Express 4.x | CI tested |
14
+ | Express 5.x | CI tested |
15
+
16
+ The GitHub Actions matrix runs the test suite across Node.js 18, 20, 22, and 24 with both Express 4 and Express 5.
17
+
18
+ ## Module And TypeScript Compatibility
19
+
20
+ The published package remains CommonJS at runtime and supports both CommonJS and Node.js ESM consumers. Its TypeScript declarations model the CommonJS default export and the named properties exposed by the root and `/safe` entrypoints.
21
+
22
+ | Consumer | Module setup | Verified imports | Validation |
23
+ | --- | --- | --- | --- |
24
+ | TypeScript ESM | `"type": "module"` with `module` and `moduleResolution` set to `NodeNext` | Root default and named imports, public root types, and named `/safe` imports | TypeScript 6, `strict: true`, `skipLibCheck: false`, `esModuleInterop: false` |
25
+ | TypeScript CommonJS | `.cts` with NodeNext resolution | `import = require()` for the root and `/safe`, including namespace properties | TypeScript 6, `strict: true`, `skipLibCheck: false`, `esModuleInterop: false` |
26
+ | Node.js ESM | `.mjs` | Root default and named imports plus named `/safe` imports | Runtime smoke test |
27
+ | Node.js CommonJS | `.cjs` | `require()` for the root and `/safe`, including root default identity | Runtime smoke test |
28
+
29
+ The TypeScript ESM consumer covers `RpcEndpoint` as both the default and a named import, `RpcClient`, `RpcEndpointOptions`, `RpcSafeEndpoint`, and `RpcSafeClient`. The CommonJS consumer covers the constructable root export, `RpcEndpoint.RpcClient`, `Safe.RpcSafeEndpoint`, and `Safe.RpcSafeClient`.
30
+
31
+ TypeScript applications using Express should install `@types/express` and
32
+ `@types/node`; the package test installs these declarations in its isolated
33
+ consumer.
34
+
35
+ The TypeScript 6 fixtures set `ignoreDeprecations: "6.0"` solely to
36
+ acknowledge the compiler's deprecation notice for the deliberately explicit
37
+ `esModuleInterop: false` test setting.
38
+
39
+ ### Packaged Consumer Validation
40
+
41
+ `npm run package-test` builds a tarball with `npm pack`, installs that tarball into isolated consumer fixtures, and runs the full matrix above. This verifies the published `exports` map, included files, declaration resolution, module extensions, and runtime format instead of importing the repository sources directly.
42
+
43
+ Package validation runs in CI and as part of `prepublishOnly`, so declaration or packaging regressions block publication.
44
+
45
+ ## Express Dependency
46
+
47
+ Express is declared as a peer dependency:
48
+
49
+ ```json
50
+ "express": "^4.21.2 || ^5.0.0"
51
+ ```
52
+
53
+ Applications should install Express directly alongside `rpc-express-toolkit`.
54
+
55
+ ## Compatibility Coverage
56
+
57
+ The Express compatibility tests cover:
58
+
59
+ - standard JSON-RPC endpoint calls;
60
+ - batch requests;
61
+ - Safe Mode endpoints;
62
+ - introspection reporting of the active Express runtime.
63
+
64
+ Broader RPC behavior is covered by the main test suite and is expected to run identically on Express 4 and Express 5. Module and declaration compatibility is covered separately by the packaged consumer validation.
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "rpc-express-toolkit",
3
- "version": "4.3.5",
3
+ "version": "4.4.1",
4
4
  "description": "JSON-RPC 2.0 toolkit for Express.js with simplified APIs, structured logging, middleware, schema validation, batch support, and optional Safe Mode.",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
7
+ "type": "commonjs",
7
8
  "exports": {
8
9
  ".": {
9
10
  "types": "./src/index.d.ts",
@@ -19,6 +20,7 @@
19
20
  "files": [
20
21
  "src/",
21
22
  "examples/",
23
+ "docs/COMPATIBILITY.md",
22
24
  "README.md",
23
25
  "LICENSE"
24
26
  ],
@@ -30,7 +32,9 @@
30
32
  "lint:fix": "eslint src/ examples/ --fix",
31
33
  "clean": "node -e \"require(\u0027fs\u0027).existsSync(\u0027dist\u0027) \u0026\u0026 require(\u0027fs\u0027).rmSync(\u0027dist\u0027, {recursive: true})\"",
32
34
  "build": "node -e \"require.resolve(\u0027rpc-toolkit-js-client\u0027)\"",
33
- "prepublishOnly": "npm run build",
35
+ "typecheck": "node test/package/runner.js --types-only",
36
+ "package-test": "node test/package/runner.js",
37
+ "prepublishOnly": "npm run build \u0026\u0026 npm run package-test",
34
38
  "precommit": "npm run lint \u0026\u0026 npm test"
35
39
  },
36
40
  "repository": {
@@ -66,7 +70,7 @@
66
70
  "rate-limit",
67
71
  "authentication"
68
72
  ],
69
- "author": "Nicola Carpanese \u003cnicola.carpanese@gmail.com\u003e",
73
+ "author": "Nicola Carpanese (https://github.com/n-car)",
70
74
  "license": "MIT",
71
75
  "bugs": {
72
76
  "url": "https://github.com/n-car/rpc-express-toolkit/issues"
@@ -76,22 +80,30 @@
76
80
  "node": "\u003e=18.0.0"
77
81
  },
78
82
  "devDependencies": {
83
+ "@arethetypeswrong/cli": "^0.18.5",
84
+ "@types/express": "^5.0.6",
85
+ "@types/node": "^18.19.0",
79
86
  "eslint": "^8.57.1",
80
87
  "eslint-config-airbnb-base": "^15.0.0",
81
88
  "eslint-config-prettier": "^9.1.2",
82
89
  "eslint-plugin-import": "^2.31.0",
83
90
  "eslint-plugin-prettier": "^5.5.3",
91
+ "express": "^4.21.2 || ^5.0.0",
84
92
  "husky": "^8.0.0",
85
93
  "jest": "^29.7.0",
86
94
  "lint-staged": "^13.0.0",
87
95
  "prettier": "^3.6.2",
96
+ "publint": "^0.3.22",
88
97
  "supertest": "^6.3.3",
89
- "terser": "^5.43.1"
98
+ "terser": "^5.43.1",
99
+ "typescript": "~6.0.3"
90
100
  },
101
+ "peerDependencies": {
102
+ "express": "^4.21.2 || ^5.0.0"
103
+ },
91
104
  "dependencies": {
92
105
  "ajv": "^8.12.0",
93
106
  "ajv-formats": "^2.1.1",
94
- "express": "^4.21.2",
95
107
  "nested-error-stacks": "^2.1.1",
96
108
  "rpc-toolkit-js-client": "^1.1.2"
97
109
  },
@@ -101,7 +113,6 @@
101
113
  "contributors": [
102
114
  {
103
115
  "name": "Nicola Carpanese",
104
- "email": "nicola.carpanese@gmail.com",
105
116
  "url": "https://github.com/n-car"
106
117
  }
107
118
  ],
package/src/index.d.ts CHANGED
@@ -1,290 +1,287 @@
1
1
  // Type definitions for rpc-express-toolkit
2
2
 
3
- import type { Router, Request, Response } from 'express';
3
+ import type { Request, Response, Router } from 'express';
4
4
  import {
5
5
  RpcClient as SharedRpcClient,
6
6
  RpcError as SharedRpcError,
7
7
  RpcHttpError as SharedRpcHttpError,
8
- RpcSafeClient as SharedRpcSafeClient
8
+ RpcSafeClient as SharedRpcSafeClient,
9
9
  } from 'rpc-toolkit-js-client';
10
10
  import type {
11
11
  RpcBatchRequest as SharedRpcBatchRequest,
12
- RpcClientOptions as SharedRpcClientOptions
12
+ RpcClientOptions as SharedRpcClientOptions,
13
13
  } from 'rpc-toolkit-js-client';
14
14
 
15
15
  /**
16
- * JSON-RPC handler function type
16
+ * The main RpcEndpoint class for JSON-RPC 2.0 endpoints.
17
17
  */
18
- type JSONRPCHandler<C> = (
19
- req: Request,
20
- context: C,
21
- params: any
22
- ) => any | Promise<any>;
18
+ declare class RpcEndpoint<C = any> {
19
+ constructor(router: Router, context: C, endpoint?: string);
20
+ constructor(
21
+ router: Router,
22
+ context: C,
23
+ options?: RpcEndpoint.RpcEndpointOptions
24
+ );
25
+ constructor(
26
+ router: Router,
27
+ context: C,
28
+ endpoint?: string,
29
+ options?: RpcEndpoint.RpcEndpointOptions
30
+ );
31
+
32
+ /** The endpoint path, for example "/api". */
33
+ get endpoint(): string;
23
34
 
24
- /**
25
- * Method configuration object
26
- */
27
- interface MethodConfig<C> {
28
- handler: JSONRPCHandler<C>;
29
- schema?: object;
30
- }
35
+ /** All registered methods. */
36
+ get methods(): Record<
37
+ string,
38
+ RpcEndpoint.JSONRPCHandler<C> | RpcEndpoint.MethodConfig<C>
39
+ >;
31
40
 
32
- /**
33
- * JSON-RPC error object
34
- */
35
- interface JSONRPCError {
36
- code: number;
37
- message: string;
38
- data?: any;
39
- }
41
+ get logger(): RpcEndpoint.Logger;
42
+ get middleware(): RpcEndpoint.MiddlewareManager;
43
+ get validator(): RpcEndpoint.SchemaValidator;
44
+ get options(): RpcEndpoint.RpcEndpointOptions;
40
45
 
41
- /**
42
- * JSON-RPC response payload
43
- */
44
- interface JSONRPCResponsePayload {
45
- id?: string | number | null;
46
- result?: any;
47
- error?: JSONRPCError;
48
- }
46
+ addMethod(name: string, handler: RpcEndpoint.JSONRPCHandler<C>): void;
47
+ addMethod(
48
+ name: string,
49
+ handler: RpcEndpoint.JSONRPCHandler<C>,
50
+ schemaOrOptions: object
51
+ ): void;
52
+ addMethod(name: string, config: RpcEndpoint.MethodConfig<C>): void;
49
53
 
50
- /**
51
- * Logging configuration
52
- */
53
- interface LoggingConfig {
54
- level?: 'trace' | 'debug' | 'info' | 'warn' | 'error';
55
- format?: 'json' | 'simple';
56
- transports?: Array<'console' | 'file'>;
57
- file?: {
58
- filename?: string;
59
- maxsize?: number;
60
- maxFiles?: number;
61
- };
62
- }
54
+ use(hook: RpcEndpoint.MiddlewareHook, middleware: Function): void;
63
55
 
64
- /**
65
- * CORS configuration
66
- */
67
- interface CorsConfig {
68
- origin?: string | string[] | boolean;
69
- credentials?: boolean;
70
- methods?: string[];
71
- allowedHeaders?: string[];
72
- }
56
+ removeMethod(name: string): void;
57
+ getMethod(
58
+ name: string
59
+ ): RpcEndpoint.JSONRPCHandler<C> | RpcEndpoint.MethodConfig<C> | undefined;
60
+ listMethods(): string[];
61
+ getMetrics(): object;
73
62
 
74
- /**
75
- * Rate limiting configuration
76
- */
77
- interface RateLimitConfig {
78
- windowMs?: number;
79
- max?: number;
80
- message?: string;
81
- standardHeaders?: boolean;
82
- legacyHeaders?: boolean;
83
- }
63
+ static serveScripts(router: Router, url?: string): void;
84
64
 
85
- /**
86
- * Authentication verifier. Return a truthy value to allow the call.
87
- */
88
- type AuthConfig = (req: Request) => boolean | Promise<boolean>;
65
+ reply(
66
+ res: Response,
67
+ responsePayload: RpcEndpoint.JSONRPCResponsePayload
68
+ ): void;
89
69
 
90
- /**
91
- * Schema validation configuration
92
- */
93
- interface ValidationConfig {
94
- strict?: boolean;
95
- coerceTypes?: boolean;
96
- removeAdditional?: boolean;
70
+ serializeBigIntsAndDates(value: any): any;
71
+ deserializeBigIntsAndDates(
72
+ value: any,
73
+ options?: RpcEndpoint.DeserializationOptions
74
+ ): any;
97
75
  }
98
76
 
99
77
  /**
100
- * RPC endpoint configuration options
78
+ * Public properties and types attached to the CommonJS export.
101
79
  */
102
- interface RpcEndpointOptions {
103
- prefix?: string;
104
- maxBodySize?: string;
105
- timeout?: number;
106
- cors?: boolean | CorsConfig;
107
- auth?: AuthConfig;
108
- validation?: ValidationConfig;
109
- safeEnabled?: boolean;
110
- crossConfigurationEnabled?: boolean;
111
- enableSchema?: boolean;
112
- rateLimit?: RateLimitConfig;
113
- logging?: LoggingConfig;
114
- warnOnUnsafe?: boolean;
115
- strictMode?: boolean;
116
- maxSerializationDepth?: number;
117
- maxDeserializationDepth?: number;
118
- autoJsonMiddleware?: boolean;
119
- jsonOptions?: {
120
- limit?: string;
121
- strict?: boolean;
122
- type?: string | string[] | ((req: any) => boolean);
123
- verify?: (req: any, res: any, buf: Buffer, encoding: string) => void;
80
+ declare namespace RpcEndpoint {
81
+ // module.exports.RpcEndpoint === module.exports
82
+ export { RpcEndpoint };
83
+
84
+ // Classes re-exported from rpc-toolkit-js-client.
85
+ export {
86
+ SharedRpcClient as RpcClient,
87
+ SharedRpcError as RpcError,
88
+ SharedRpcHttpError as RpcHttpError,
89
+ SharedRpcSafeClient as RpcSafeClient,
124
90
  };
125
- // Additional options supported by implementation
126
- methodWhitelist?: string[];
127
- timing?: boolean;
128
- metrics?: boolean;
129
- healthCheck?: boolean;
130
- }
131
-
132
- /**
133
- * RPC client configuration options
134
- */
135
- type RpcClientOptions = SharedRpcClientOptions;
136
- type RpcBatchRequest = SharedRpcBatchRequest;
137
- type RpcClient = SharedRpcClient;
138
- type RpcError = SharedRpcError;
139
- type RpcHttpError = SharedRpcHttpError;
140
- type RpcSafeClient = SharedRpcSafeClient;
141
-
142
- declare const RpcClient: typeof SharedRpcClient;
143
- declare const RpcError: typeof SharedRpcError;
144
- declare const RpcHttpError: typeof SharedRpcHttpError;
145
- declare const RpcSafeClient: typeof SharedRpcSafeClient;
146
-
147
- /**
148
- * Deserialization options for safe prefixes
149
- */
150
- interface DeserializationOptions {
151
- safeEnabled?: boolean;
152
- }
153
-
154
- /**
155
- * The main RpcEndpoint class for JSON-RPC 2.0 endpoints.
156
- */
157
- declare class RpcEndpoint<C = any> {
158
- /**
159
- * Constructor.
160
- */
161
- constructor(router: Router, context: C, endpoint?: string);
162
- constructor(router: Router, context: C, options?: RpcEndpointOptions);
163
- constructor(router: Router, context: C, endpoint?: string, options?: RpcEndpointOptions);
164
-
165
- /**
166
- * Returns the endpoint path, e.g. "/api".
167
- */
168
- get endpoint(): string;
169
91
 
170
- /**
171
- * Returns the map of all registered methods.
172
- */
173
- get methods(): { [methodName: string]: JSONRPCHandler<C> | MethodConfig<C> };
174
-
175
- /**
176
- * Get logger instance
177
- */
178
- get logger(): any;
179
-
180
- /**
181
- * Get middleware manager
182
- */
183
- get middleware(): any;
184
-
185
- /**
186
- * Get validator instance
187
- */
188
- get validator(): any;
189
-
190
- /**
191
- * Add a JSON-RPC method with optional schema validation.
192
- */
193
- addMethod(name: string, handler: JSONRPCHandler<C>): void;
194
- addMethod(name: string, handler: JSONRPCHandler<C>, schema: object): void;
195
- addMethod(name: string, config: MethodConfig<C>): void;
196
-
197
- /**
198
- * Add middleware for specific hooks
199
- */
200
- use(hook: 'beforeCall' | 'afterCall' | 'onError' | 'beforeValidation' | 'afterValidation', middleware: Function): void;
92
+ /** JSON-RPC handler function. */
93
+ export type JSONRPCHandler<C> = (
94
+ req: Request,
95
+ context: C,
96
+ params: any
97
+ ) => any | Promise<any>;
98
+
99
+ /** Method configuration object. */
100
+ export interface MethodConfig<C> {
101
+ handler: JSONRPCHandler<C>;
102
+ schema?: object;
103
+ exposeSchema?: boolean;
104
+ description?: string;
105
+ }
106
+
107
+ /** JSON-RPC error object. */
108
+ export interface JSONRPCError {
109
+ code: number;
110
+ message: string;
111
+ data?: any;
112
+ }
113
+
114
+ /** JSON-RPC response payload. */
115
+ export interface JSONRPCResponsePayload {
116
+ id?: string | number | null;
117
+ result?: any;
118
+ error?: JSONRPCError;
119
+ }
120
+
121
+ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
122
+
123
+ /** Logging configuration. */
124
+ export interface LoggingConfig {
125
+ level?: LogLevel;
126
+ enabled?: boolean;
127
+ prefix?: string;
128
+ logger?: Partial<
129
+ Record<LogLevel, (entry: Record<string, unknown>) => void>
130
+ >;
131
+ format?: 'json' | 'simple';
132
+ transports?: Array<'console' | 'file'>;
133
+ file?: {
134
+ filename?: string;
135
+ maxsize?: number;
136
+ maxFiles?: number;
137
+ };
138
+ }
139
+
140
+ /** CORS configuration. */
141
+ export interface CorsConfig {
142
+ origin?: string | string[] | boolean;
143
+ credentials?: boolean;
144
+ methods?: string[];
145
+ allowedHeaders?: string[];
146
+ headers?: string[];
147
+ }
148
+
149
+ /** Rate limiting configuration. */
150
+ export interface RateLimitConfig {
151
+ windowMs?: number;
152
+ max?: number;
153
+ message?: string;
154
+ standardHeaders?: boolean;
155
+ legacyHeaders?: boolean;
156
+ }
157
+
158
+ /** Authentication verifier. Return a truthy value to allow the call. */
159
+ export type AuthConfig = (req: Request) => boolean | Promise<boolean>;
160
+
161
+ /** Schema validation configuration. */
162
+ export interface ValidationConfig {
163
+ strict?: boolean;
164
+ coerceTypes?: boolean;
165
+ removeAdditional?: boolean;
166
+ useDefaults?: boolean;
167
+ ajvOptions?: Record<string, unknown>;
168
+ }
169
+
170
+ /** RPC endpoint configuration options. */
171
+ export interface RpcEndpointOptions {
172
+ endpoint?: string;
173
+ prefix?: string;
174
+ maxBodySize?: string;
175
+ timeout?: number;
176
+ cors?: boolean | CorsConfig;
177
+ auth?: AuthConfig;
178
+ validation?: ValidationConfig;
179
+ safeEnabled?: boolean;
180
+ crossConfigurationEnabled?: boolean;
181
+ enableSchema?: boolean;
182
+ rateLimit?: RateLimitConfig;
183
+ logging?: LoggingConfig;
184
+ warnOnUnsafe?: boolean;
185
+ strictMode?: boolean;
186
+ maxSerializationDepth?: number;
187
+ maxDeserializationDepth?: number;
188
+ autoJsonMiddleware?: boolean;
189
+ jsonOptions?: {
190
+ limit?: string;
191
+ strict?: boolean;
192
+ type?: string | string[] | ((req: any) => boolean);
193
+ verify?: (req: any, res: any, buf: Buffer, encoding: string) => void;
194
+ };
195
+ methodWhitelist?: string[];
196
+ timing?: boolean;
197
+ metrics?: boolean;
198
+ healthCheck?: boolean;
199
+ enableIntrospection?: boolean;
200
+ introspectionPrefix?: string;
201
+ }
202
+
203
+ /** RPC client configuration options. */
204
+ export type RpcClientOptions = SharedRpcClientOptions;
205
+ export type RpcBatchRequest = SharedRpcBatchRequest;
206
+
207
+ /** Deserialization options for safe prefixes. */
208
+ export interface DeserializationOptions {
209
+ safeEnabled?: boolean;
210
+ }
211
+
212
+ export type MiddlewareHook =
213
+ | 'beforeCall'
214
+ | 'afterCall'
215
+ | 'onError'
216
+ | 'beforeValidation'
217
+ | 'afterValidation';
218
+
219
+ export interface MiddlewareContext<C = any> {
220
+ req?: Request;
221
+ res?: Response | null;
222
+ context?: C;
223
+ method?: string;
224
+ params?: any;
225
+ result?: any;
226
+ error?: any;
227
+ batchIndex?: number;
228
+ isNotification?: boolean;
229
+ startTime?: number;
230
+ [key: string]: any;
231
+ }
232
+
233
+ export type Middleware = (context: MiddlewareContext) => any | Promise<any>;
234
+
235
+ export interface RpcMetrics {
236
+ methods: number;
237
+ middleware: {
238
+ beforeCall: number;
239
+ afterCall: number;
240
+ onError: number;
241
+ };
242
+ uptime: number;
243
+ memory: Record<string, number>;
244
+ timestamp: string;
245
+ }
201
246
 
202
247
  /**
203
- * Remove a method
248
+ * Utility helpers were historically exposed as `any`. Keep their instance
249
+ * contracts permissive in this patch while still publishing value/type names.
204
250
  */
205
- removeMethod(name: string): void;
251
+ export type Logger = any;
252
+ export const Logger: {
253
+ new (...args: any[]): Logger;
254
+ };
206
255
 
207
- /**
208
- * Get method configuration
209
- */
210
- getMethod(name: string): JSONRPCHandler<C> | MethodConfig<C> | undefined;
256
+ export type MiddlewareManager = any;
257
+ export const MiddlewareManager: {
258
+ new (...args: any[]): MiddlewareManager;
259
+ };
211
260
 
212
- /**
213
- * List all registered methods
214
- */
215
- listMethods(): string[];
261
+ export const builtInMiddlewares: any;
216
262
 
217
- /**
218
- * Get metrics data
219
- */
220
- getMetrics(): object;
263
+ export interface SchemaValidationResult<T = any> {
264
+ valid: boolean;
265
+ errors: any[] | null;
266
+ data: T;
267
+ }
221
268
 
222
- /**
223
- * Serve client scripts (for calling JSON-RPC from browser).
224
- */
225
- static serveScripts(router: Router, url?: string): void;
269
+ export type SchemaValidator = any;
270
+ export const SchemaValidator: {
271
+ new (...args: any[]): SchemaValidator;
272
+ };
226
273
 
227
- /**
228
- * Send a JSON-RPC 2.0 response to the client.
229
- */
230
- reply(res: Response, responsePayload: JSONRPCResponsePayload): void;
274
+ export const commonSchemas: any;
231
275
 
232
- /**
233
- * Recursively serialize BigInt and Date to JSON-safe string formats.
234
- */
235
- serializeBigIntsAndDates(value: any): any;
276
+ export type SchemaBuilder = any;
277
+ export const SchemaBuilder: {
278
+ new (...args: any[]): SchemaBuilder;
279
+ };
236
280
 
237
- /**
238
- * Recursively deserialize strings representing BigInt and Date.
239
- */
240
- deserializeBigIntsAndDates(value: any, options?: DeserializationOptions): any;
281
+ export type BatchHandler = any;
282
+ export const BatchHandler: {
283
+ new (...args: any[]): BatchHandler;
284
+ };
241
285
  }
242
286
 
243
- /**
244
- * Export types for external use
245
- */
246
- export {
247
- JSONRPCHandler,
248
- MethodConfig,
249
- JSONRPCError,
250
- JSONRPCResponsePayload,
251
- LoggingConfig,
252
- CorsConfig,
253
- RateLimitConfig,
254
- AuthConfig,
255
- ValidationConfig,
256
- RpcEndpointOptions,
257
- RpcClientOptions,
258
- RpcBatchRequest,
259
- DeserializationOptions,
260
- RpcEndpoint,
261
- RpcClient,
262
- RpcError,
263
- RpcHttpError,
264
- RpcSafeClient
265
- };
266
-
267
- /**
268
- * Main export - RpcEndpoint class with static properties
269
- */
270
- declare const Main: {
271
- new <C>(router: Router, context: C, endpoint?: string): RpcEndpoint<C>;
272
- new <C>(router: Router, context: C, options?: RpcEndpointOptions): RpcEndpoint<C>;
273
- new <C>(router: Router, context: C, endpoint?: string, options?: RpcEndpointOptions): RpcEndpoint<C>;
274
- prototype: RpcEndpoint<any>;
275
- serveScripts(router: Router, url?: string): void;
276
- RpcEndpoint: typeof RpcEndpoint;
277
- RpcClient: typeof RpcClient;
278
- RpcError: typeof RpcError;
279
- RpcHttpError: typeof RpcHttpError;
280
- RpcSafeClient: typeof RpcSafeClient;
281
- Logger: any;
282
- MiddlewareManager: any;
283
- builtInMiddlewares: any;
284
- SchemaValidator: any;
285
- commonSchemas: any;
286
- SchemaBuilder: any;
287
- BatchHandler: any;
288
- };
289
-
290
- export = Main;
287
+ export = RpcEndpoint;
package/src/index.js CHANGED
@@ -288,21 +288,23 @@ class RpcEndpoint {
288
288
  this.addMethod(
289
289
  `${this.#introspectionPrefix}.describeAll`,
290
290
  async (_req, _context, _params) => {
291
- const publicMethods = [];
291
+ const publicMethods = Object.entries(this.#methods)
292
+ .filter(([name]) => !name.startsWith(this.#introspectionPrefix))
293
+ .map(([name, methodConfig]) => {
294
+ const config =
295
+ typeof methodConfig === 'function' ? {} : methodConfig;
292
296
 
293
- for (const [name, methodConfig] of Object.entries(this.#methods)) {
294
- // Skip introspection methods
295
- if (name.startsWith(this.#introspectionPrefix)) continue;
297
+ if (!config.exposeSchema) {
298
+ return null;
299
+ }
296
300
 
297
- const config = typeof methodConfig === 'function' ? {} : methodConfig;
298
- if (config.exposeSchema) {
299
- publicMethods.push({
301
+ return {
300
302
  name,
301
303
  schema: config.schema || null,
302
304
  description: config.description || '',
303
- });
304
- }
305
- }
305
+ };
306
+ })
307
+ .filter(Boolean);
306
308
 
307
309
  return publicMethods;
308
310
  },
@@ -1156,3 +1158,6 @@ module.exports.SchemaValidator = SchemaValidator;
1156
1158
  module.exports.commonSchemas = commonSchemas;
1157
1159
  module.exports.SchemaBuilder = SchemaBuilder;
1158
1160
  module.exports.BatchHandler = BatchHandler;
1161
+ // Keep the existing static method discoverable as a named export to Node's
1162
+ // CommonJS lexer without changing the RpcEndpoint object or its descriptor.
1163
+ module.exports.serveScripts = RpcEndpoint.serveScripts;
package/src/safe.d.ts CHANGED
@@ -1,63 +1,91 @@
1
1
  import type { Router } from 'express';
2
- import type { RpcEndpoint, RpcClient, RpcEndpointOptions, RpcClientOptions } from './index';
2
+ import Main = require('./index');
3
3
 
4
4
  /**
5
- * RPC Client with safe serialization enabled by default.
6
- * Extends RpcClient with safeEnabled: true preset.
5
+ * Public properties attached to the CommonJS safe preset export.
7
6
  */
8
- export class RpcSafeClient extends RpcClient {
9
- constructor(
10
- endpoint: string,
11
- defaultHeaders?: Record<string, string>,
12
- options?: RpcClientOptions
13
- );
14
- }
7
+ declare namespace Safe {
8
+ // Root values re-exported by src/safe.js.
9
+ export import RpcEndpoint = Main;
10
+ export import RpcClient = Main.RpcClient;
11
+ export import RpcError = Main.RpcError;
12
+ export import RpcHttpError = Main.RpcHttpError;
13
+ export import Logger = Main.Logger;
14
+ export import MiddlewareManager = Main.MiddlewareManager;
15
+ export import builtInMiddlewares = Main.builtInMiddlewares;
16
+ export import SchemaValidator = Main.SchemaValidator;
17
+ export import commonSchemas = Main.commonSchemas;
18
+ export import SchemaBuilder = Main.SchemaBuilder;
19
+ export import BatchHandler = Main.BatchHandler;
15
20
 
16
- /**
17
- * RPC Endpoint with safe serialization enabled by default.
18
- * Extends RpcEndpoint with safeEnabled: true and strictMode: true presets.
19
- */
20
- export class RpcSafeEndpoint<C = any> extends RpcEndpoint<C> {
21
- constructor(
21
+ // Root public types previously re-exported by `export *`.
22
+ export import JSONRPCHandler = Main.JSONRPCHandler;
23
+ export import MethodConfig = Main.MethodConfig;
24
+ export import JSONRPCError = Main.JSONRPCError;
25
+ export import JSONRPCResponsePayload = Main.JSONRPCResponsePayload;
26
+ export import LogLevel = Main.LogLevel;
27
+ export import LoggingConfig = Main.LoggingConfig;
28
+ export import CorsConfig = Main.CorsConfig;
29
+ export import RateLimitConfig = Main.RateLimitConfig;
30
+ export import AuthConfig = Main.AuthConfig;
31
+ export import ValidationConfig = Main.ValidationConfig;
32
+ export import RpcEndpointOptions = Main.RpcEndpointOptions;
33
+ export import RpcClientOptions = Main.RpcClientOptions;
34
+ export import RpcBatchRequest = Main.RpcBatchRequest;
35
+ export import DeserializationOptions = Main.DeserializationOptions;
36
+ export import MiddlewareHook = Main.MiddlewareHook;
37
+ export import MiddlewareContext = Main.MiddlewareContext;
38
+ export import Middleware = Main.Middleware;
39
+ export import RpcMetrics = Main.RpcMetrics;
40
+ export import SchemaValidationResult = Main.SchemaValidationResult;
41
+
42
+ /**
43
+ * RPC client with safe serialization enabled by default.
44
+ */
45
+ export class RpcSafeClient extends Main.RpcClient {
46
+ constructor(
47
+ endpoint: string,
48
+ defaultHeaders?: Record<string, string>,
49
+ options?: Main.RpcClientOptions
50
+ );
51
+ }
52
+
53
+ /**
54
+ * RPC endpoint with safe serialization and strict mode enabled by default.
55
+ */
56
+ export class RpcSafeEndpoint<C = any> extends Main<C> {
57
+ constructor(
58
+ router: Router,
59
+ context: C,
60
+ endpoint?: string,
61
+ options?: Main.RpcEndpointOptions
62
+ );
63
+ constructor(router: Router, context: C, options?: Main.RpcEndpointOptions);
64
+ }
65
+
66
+ /**
67
+ * @deprecated Use `new RpcSafeEndpoint(...)` instead.
68
+ */
69
+ export function createSafeEndpoint<C = any>(
22
70
  router: Router,
23
71
  context: C,
24
72
  endpoint?: string,
25
- options?: RpcEndpointOptions
26
- );
27
-
28
- constructor(
73
+ options?: Main.RpcEndpointOptions
74
+ ): RpcSafeEndpoint<C>;
75
+ export function createSafeEndpoint<C = any>(
29
76
  router: Router,
30
77
  context: C,
31
- options?: RpcEndpointOptions
32
- );
33
- }
78
+ options?: Main.RpcEndpointOptions
79
+ ): RpcSafeEndpoint<C>;
34
80
 
35
- /**
36
- * @deprecated Use `new RpcSafeEndpoint(...)` instead.
37
- * Factory: create a RpcEndpoint with safe defaults (safeEnabled=true, strictMode=true).
38
- */
39
- export function createSafeEndpoint<C = any>(
40
- router: Router,
41
- context: C,
42
- endpoint?: string,
43
- options?: RpcEndpointOptions
44
- ): RpcEndpoint<C>;
45
-
46
- export function createSafeEndpoint<C = any>(
47
- router: Router,
48
- context: C,
49
- options?: RpcEndpointOptions
50
- ): RpcEndpoint<C>;
51
-
52
- /**
53
- * @deprecated Use `new RpcSafeClient(...)` instead.
54
- * Factory: create a RpcClient with safe defaults (safeEnabled=true).
55
- */
56
- export function createSafeClient(
57
- endpoint: string,
58
- defaultHeaders?: Record<string, string>,
59
- options?: RpcClientOptions
60
- ): RpcClient;
61
-
62
- export * from './index';
81
+ /**
82
+ * @deprecated Use `new RpcSafeClient(...)` instead.
83
+ */
84
+ export function createSafeClient(
85
+ endpoint: string,
86
+ defaultHeaders?: Record<string, string>,
87
+ options?: Main.RpcClientOptions
88
+ ): RpcSafeClient;
89
+ }
63
90
 
91
+ export = Safe;
package/src/safe.js CHANGED
@@ -110,3 +110,17 @@ module.exports = {
110
110
  createSafeEndpoint,
111
111
  createSafeClient,
112
112
  };
113
+
114
+ // Make spread-based root re-exports visible to Node's CommonJS named-export
115
+ // detection. These assignments preserve the existing object values and order.
116
+ module.exports.RpcEndpoint = Main.RpcEndpoint;
117
+ module.exports.RpcClient = Main.RpcClient;
118
+ module.exports.RpcError = Main.RpcError;
119
+ module.exports.RpcHttpError = Main.RpcHttpError;
120
+ module.exports.Logger = Main.Logger;
121
+ module.exports.MiddlewareManager = Main.MiddlewareManager;
122
+ module.exports.builtInMiddlewares = Main.builtInMiddlewares;
123
+ module.exports.SchemaValidator = Main.SchemaValidator;
124
+ module.exports.commonSchemas = Main.commonSchemas;
125
+ module.exports.SchemaBuilder = Main.SchemaBuilder;
126
+ module.exports.BatchHandler = Main.BatchHandler;