rpc-express-toolkit 4.2.0 → 4.3.2

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/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -42,9 +42,61 @@ app.listen(3000);
42
42
  ### Client
43
43
 
44
44
  ```javascript
45
- const { RpcClient } = require('rpc-express-toolkit');
45
+ const { RpcClient, RpcSafeClient } = require('rpc-express-toolkit');
46
+
46
47
  const client = new RpcClient('http://localhost:3000/api');
47
48
  const sum = await client.call('add', { a: 1, b: 2 });
49
+
50
+ const safeClient = new RpcSafeClient('http://localhost:3000/api');
51
+ await safeClient.notify('add', { a: 0, b: 0 });
52
+ ```
53
+
54
+ `RpcClient` and `RpcSafeClient` are re-exported from the shared `rpc-toolkit-js-client` package, so existing Node.js imports from `rpc-express-toolkit` continue to work.
55
+
56
+ ### Browser Client Assets
57
+
58
+ Serve the shared browser client bundles from the endpoint:
59
+
60
+ ```javascript
61
+ const { RpcEndpoint } = require('rpc-express-toolkit');
62
+
63
+ RpcEndpoint.serveScripts(app);
64
+ ```
65
+
66
+ Default paths:
67
+
68
+ ```text
69
+ /vendor/rpc-client/rpc-client.js
70
+ /vendor/rpc-client/rpc-client.min.js
71
+ /vendor/rpc-client/rpc-client.mjs
72
+ /vendor/rpc-client/rpc-client.min.mjs
73
+ ```
74
+
75
+ Classic browser script:
76
+
77
+ ```html
78
+ <script src="/vendor/rpc-client/rpc-client.min.js"></script>
79
+ <script>
80
+ const client = new RpcToolkitClient.RpcClient('/api');
81
+ const safeClient = new RpcToolkitClient.RpcSafeClient('/api');
82
+
83
+ client.call('add', { a: 1, b: 2 }).then(console.log);
84
+ safeClient.notify('add', { a: 0, b: 0 });
85
+ </script>
86
+ ```
87
+
88
+ Module script:
89
+
90
+ ```html
91
+ <script type="module">
92
+ import { RpcClient, RpcSafeClient } from '/vendor/rpc-client/rpc-client.mjs';
93
+
94
+ const client = new RpcClient('/api');
95
+ const safeClient = new RpcSafeClient('/api');
96
+
97
+ console.log(await client.call('add', { a: 1, b: 2 }));
98
+ await safeClient.notify('add', { a: 0, b: 0 });
99
+ </script>
48
100
  ```
49
101
 
50
102
  ### Safe Type Disambiguation (optional)
@@ -60,6 +112,31 @@ Safe type disambiguation is disabled by default for maximum JSON-RPC 2.0 compati
60
112
  - `client.batch([...])`: batch.
61
113
  - `client.notify(method, params?)`: notification.
62
114
 
115
+ ## Authentication And Method Restrictions
116
+
117
+ Authentication is supported through the built-in `auth` middleware. The `auth` option is a function that receives the Express request. Returning a truthy value allows the RPC call; returning a falsy value rejects it with JSON-RPC error `-32001` and message `Authentication required`. Throwing also rejects the call and returns a JSON-RPC error based on the thrown error.
118
+
119
+ ```js
120
+ const rpc = new RpcEndpoint(app, context, {
121
+ auth: async (req) => {
122
+ const token = req.headers.authorization;
123
+ return token === 'Bearer secret-token';
124
+ },
125
+ });
126
+
127
+ rpc.addMethod('ping', () => 'pong');
128
+ ```
129
+
130
+ For simple method-level restriction, use `methodWhitelist`:
131
+
132
+ ```js
133
+ const rpc = new RpcEndpoint(app, context, {
134
+ methodWhitelist: ['ping', 'status.read'],
135
+ });
136
+ ```
137
+
138
+ Calls to methods outside the whitelist are rejected. This is a basic method restriction mechanism, not a complete role or permission system. Project-specific authorization rules can be implemented with middleware hooks such as `beforeCall`; see `README_ADVANCED.md`.
139
+
63
140
  ### Safe Import (opt-in)
64
141
 
65
142
  [![npm (proxy)](https://img.shields.io/npm/v/rpc-express-toolkit-safe.svg)](https://www.npmjs.com/package/rpc-express-toolkit-safe)
@@ -70,16 +147,16 @@ When you control both client and server and want safer type round-trips, import
70
147
  ```js
71
148
  // Server (safe preset)
72
149
  const express = require('express');
73
- const { createSafeEndpoint } = require('rpc-express-toolkit/safe');
150
+ const { RpcSafeEndpoint } = require('rpc-express-toolkit/safe');
74
151
 
75
152
  const app = express();
76
153
  app.use(express.json());
77
154
 
78
- const rpc = createSafeEndpoint(app, {}, { endpoint: '/api' /* strictMode: true by default */ });
155
+ const rpc = new RpcSafeEndpoint(app, {}, { endpoint: '/api' /* strictMode: true by default */ });
79
156
 
80
157
  // Client (safe preset)
81
- const { createSafeClient } = require('rpc-express-toolkit/safe');
82
- const client = createSafeClient('http://localhost:3000/api');
158
+ const { RpcSafeClient } = require('rpc-express-toolkit/safe');
159
+ const client = new RpcSafeClient('http://localhost:3000/api');
83
160
  ```
84
161
 
85
162
  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.
@@ -95,20 +172,69 @@ npm install rpc-express-toolkit-safe
95
172
  ```js
96
173
  // Server
97
174
  const express = require('express');
98
- const { createSafeEndpoint } = require('rpc-express-toolkit-safe');
175
+ const { RpcSafeEndpoint } = require('rpc-express-toolkit-safe');
99
176
  const app = express();
100
177
  app.use(express.json());
101
- const rpc = createSafeEndpoint(app, {}, { endpoint: '/api' });
178
+ const rpc = new RpcSafeEndpoint(app, {}, { endpoint: '/api' });
102
179
 
103
180
  // Client
104
- const { createSafeClient } = require('rpc-express-toolkit-safe');
105
- const client = createSafeClient('http://localhost:3000/api');
181
+ const { RpcSafeClient } = require('rpc-express-toolkit-safe');
182
+ const client = new RpcSafeClient('http://localhost:3000/api');
183
+ ```
184
+
185
+ ## Introspection Methods
186
+
187
+ Enable introspection to expose metadata about registered methods via reserved `__rpc.*` methods:
188
+
189
+ ```javascript
190
+ const rpc = new RpcEndpoint(app, context, {
191
+ enableIntrospection: true // Enable __rpc.* methods
192
+ });
193
+
194
+ // Register methods with public schemas
195
+ rpc.addMethod('add', async (req, ctx, params) => {
196
+ return params.a + params.b;
197
+ }, {
198
+ schema: {
199
+ type: 'object',
200
+ properties: {
201
+ a: { type: 'number' },
202
+ b: { type: 'number' }
203
+ },
204
+ required: ['a', 'b']
205
+ },
206
+ exposeSchema: true, // Make schema publicly queryable
207
+ description: 'Add two numbers'
208
+ });
209
+
210
+ // Available introspection methods:
211
+ // __rpc.listMethods() → ["add", "multiply", ...]
212
+ // __rpc.describe({method: "add"}) → {name, schema, description}
213
+ // __rpc.describeAll() → [{name, schema, description}, ...]
214
+ // __rpc.version() → {toolkit, version, expressVersion, nodeVersion}
215
+ // __rpc.capabilities() → {safeMode, batch, introspection, ...}
216
+
217
+ // Client usage
218
+ const methods = await client.call('__rpc.listMethods');
219
+ const addInfo = await client.call('__rpc.describe', { method: 'add' });
106
220
  ```
107
221
 
222
+ In `__rpc.capabilities`, `auth` is a boolean indicating whether authentication middleware is configured. It does not expose roles, scopes, or permission rules.
223
+
224
+ **Note:** The introspection prefix is configurable via `introspectionPrefix` option (default: `__rpc`). User methods starting with this prefix are rejected to prevent conflicts.
225
+
108
226
  ## Full Details
109
227
 
110
228
  For advanced configuration, middleware, structured logging, safe serialization, error handling, and more, see `README_ADVANCED.md`.
111
229
 
230
+ ## 🔗 Related Projects
231
+
232
+ - [rpc-php-toolkit](https://github.com/n-car/rpc-php-toolkit) - PHP implementation
233
+ - [rpc-dotnet-toolkit](https://github.com/n-car/rpc-dotnet-toolkit) - .NET implementation
234
+ - [rpc-arduino-toolkit](https://github.com/n-car/rpc-arduino-toolkit) - Arduino/ESP32 implementation
235
+ - [rpc-java-toolkit](https://github.com/n-car/rpc-java-toolkit) - Java & Android implementation
236
+ - [node-red-contrib-rpc-toolkit](https://github.com/n-car/node-red-contrib-rpc-toolkit) - Node-RED visual programming
237
+
112
238
  ## Contributing
113
239
 
114
240
  ```bash
File without changes
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Introspection Example - Client
3
+ * Demonstrates how to use __rpc.* introspection methods
4
+ */
5
+
6
+ const { RpcClient } = require('../..');
7
+
8
+ async function main() {
9
+ const client = new RpcClient('http://localhost:3000/api');
10
+
11
+ console.log('=== RPC Introspection Demo ===\n');
12
+
13
+ // 1. List all methods
14
+ console.log('1. __rpc.listMethods():');
15
+ const methods = await client.call('__rpc.listMethods');
16
+ console.log(' Available methods:', methods);
17
+ console.log('');
18
+
19
+ // 2. Get version info
20
+ console.log('2. __rpc.version():');
21
+ const version = await client.call('__rpc.version');
22
+ console.log(' Toolkit:', version.toolkit);
23
+ console.log(' Version:', version.version);
24
+ console.log(' Express:', version.expressVersion);
25
+ console.log(' Node:', version.nodeVersion);
26
+ console.log('');
27
+
28
+ // 3. Get server capabilities
29
+ console.log('3. __rpc.capabilities():');
30
+ const caps = await client.call('__rpc.capabilities');
31
+ console.log(' Safe Mode:', caps.safeMode);
32
+ console.log(' Batch Support:', caps.batch);
33
+ console.log(' Introspection:', caps.introspection);
34
+ console.log(' Method Count:', caps.methodCount);
35
+ console.log('');
36
+
37
+ // 4. Describe a specific method
38
+ console.log('4. __rpc.describe({ method: "add" }):');
39
+ const addInfo = await client.call('__rpc.describe', { method: 'add' });
40
+ console.log(' Name:', addInfo.name);
41
+ console.log(' Description:', addInfo.description);
42
+ console.log(' Schema:', JSON.stringify(addInfo.schema, null, 2));
43
+ console.log('');
44
+
45
+ // 5. Describe all public methods
46
+ console.log('5. __rpc.describeAll():');
47
+ const allMethods = await client.call('__rpc.describeAll');
48
+ allMethods.forEach((method) => {
49
+ console.log(` - ${method.name}: ${method.description}`);
50
+ });
51
+ console.log('');
52
+
53
+ // 6. Try to describe a private method (should fail)
54
+ console.log('6. Try to describe private method:');
55
+ try {
56
+ await client.call('__rpc.describe', { method: 'internalCalculation' });
57
+ } catch (error) {
58
+ console.log(' Error:', error.message);
59
+ }
60
+ console.log('');
61
+
62
+ // 7. Test a public method
63
+ console.log('7. Call add method:');
64
+ const sum = await client.call('add', { a: 5, b: 3 });
65
+ console.log(' 5 + 3 =', sum);
66
+ }
67
+
68
+ main().catch(console.error);
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Introspection Example - Server
3
+ * Demonstrates how to enable and use __rpc.* introspection methods
4
+ */
5
+
6
+ const express = require('express');
7
+ const { RpcEndpoint } = require('../..');
8
+
9
+ const app = express();
10
+ app.use(express.json());
11
+
12
+ // Context for methods
13
+ const context = {
14
+ database: 'mock-db',
15
+ config: { maxValue: 1000 },
16
+ };
17
+
18
+ // Create endpoint with introspection enabled
19
+ const rpc = new RpcEndpoint(app, context, {
20
+ endpoint: '/api',
21
+ enableIntrospection: true, // Enable __rpc.* methods
22
+ introspectionPrefix: '__rpc', // Optional: customize prefix (default: __rpc)
23
+ });
24
+
25
+ // Public method with exposed schema
26
+ rpc.addMethod('add', async (req, ctx, params) => params.a + params.b, {
27
+ schema: {
28
+ type: 'object',
29
+ properties: {
30
+ a: { type: 'number' },
31
+ b: { type: 'number' },
32
+ },
33
+ required: ['a', 'b'],
34
+ },
35
+ exposeSchema: true,
36
+ description: 'Add two numbers and return the sum',
37
+ });
38
+
39
+ // Public method with exposed schema
40
+ rpc.addMethod('multiply', async (req, ctx, params) => params.a * params.b, {
41
+ schema: {
42
+ type: 'object',
43
+ properties: {
44
+ a: { type: 'number' },
45
+ b: { type: 'number' },
46
+ },
47
+ required: ['a', 'b'],
48
+ },
49
+ exposeSchema: true,
50
+ description: 'Multiply two numbers',
51
+ });
52
+
53
+ // Private method (schema not exposed)
54
+ rpc.addMethod(
55
+ 'internalCalculation',
56
+ async (req, ctx, params) => params.value * 2 + 42,
57
+ {
58
+ schema: {
59
+ type: 'object',
60
+ properties: {
61
+ value: { type: 'number' },
62
+ },
63
+ required: ['value'],
64
+ },
65
+ exposeSchema: false, // Schema is private
66
+ description: 'Internal calculation (not exposed)',
67
+ }
68
+ );
69
+
70
+ // Public method without schema
71
+ rpc.addMethod('echo', async (req, ctx, params) => params, {
72
+ exposeSchema: true,
73
+ description: 'Echo back the parameters',
74
+ });
75
+
76
+ // Start server
77
+ const PORT = process.env.PORT || 3000;
78
+ app.listen(PORT, () => {
79
+ console.log(
80
+ `RPC Server with introspection running on http://localhost:${PORT}/api`
81
+ );
82
+ console.log('\nAvailable introspection methods:');
83
+ console.log('- __rpc.listMethods()');
84
+ console.log('- __rpc.describe({method: "add"})');
85
+ console.log('- __rpc.describeAll()');
86
+ console.log('- __rpc.version()');
87
+ console.log('- __rpc.capabilities()');
88
+ console.log('\nTry: node client.js');
89
+ });
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,126 +1,124 @@
1
- {
2
- "name": "rpc-express-toolkit",
3
- "version": "4.2.0",
4
- "description": "Enterprise-ready JSON-RPC 2.0 toolkit for Express.js with simplified APIs, structured logging, middleware system, schema validation, comprehensive BigInt/Date support. JSON middleware validation and explicit configuration.",
5
- "main": "src/index.js",
6
- "types": "src/index.d.ts",
7
- "exports": {
8
- ".": {
9
- "types": "./src/index.d.ts",
10
- "require": "./src/index.js",
11
- "import": "./src/index.js"
12
- },
13
- "./safe": {
14
- "types": "./src/safe.d.ts",
15
- "require": "./src/safe.js",
16
- "import": "./src/safe.js"
17
- }
18
- },
19
- "files": [
20
- "src/",
21
- "examples/",
22
- "README.md",
23
- "LICENSE"
24
- ],
25
- "scripts": {
26
- "test": "jest",
27
- "test:coverage": "jest --coverage",
28
- "test:watch": "jest --watch",
29
- "lint": "eslint src/ examples/",
30
- "lint:fix": "eslint src/ examples/ --fix",
31
- "clean": "node -e \"require(\u0027fs\u0027).existsSync(\u0027dist\u0027) \u0026\u0026 require(\u0027fs\u0027).rmSync(\u0027dist\u0027, {recursive: true})\"",
32
- "build": "npm run build:minify",
33
- "build:minify": "npm run build:js \u0026\u0026 npm run build:mjs",
34
- "build:js": "terser src/clients/rpc-client.js -o src/clients/rpc-client.min.js --compress --mangle --comments false --ecma 2020",
35
- "build:mjs": "terser src/clients/rpc-client.mjs -o src/clients/rpc-client.min.mjs --compress --mangle --comments false --module --ecma 2020",
36
- "prepublishOnly": "npm run build",
37
- "precommit": "npm run lint \u0026\u0026 npm test"
38
- },
39
- "repository": {
40
- "type": "git",
41
- "url": "git+https://github.com/n-car/rpc-express-toolkit.git"
42
- },
43
- "keywords": [
44
- "json-rpc",
45
- "jsonrpc",
46
- "json-rpc-2.0",
47
- "express",
48
- "toolkit",
49
- "rpc",
50
- "server",
51
- "client",
52
- "middleware",
53
- "validation",
54
- "schema",
55
- "ajv",
56
- "bigint",
57
- "date",
58
- "enterprise",
59
- "nodejs",
60
- "node",
61
- "browser",
62
- "ssl",
63
- "self-signed",
64
- "secure",
65
- "fetch",
66
- "batch",
67
- "typescript",
68
- "logging",
69
- "cors",
70
- "rate-limit",
71
- "authentication"
72
- ],
73
- "author": "Nicola Carpanese \u003cnicola.carpanese@gmail.com\u003e",
74
- "license": "MIT",
75
- "bugs": {
76
- "url": "https://github.com/n-car/rpc-express-toolkit/issues"
77
- },
78
- "homepage": "https://github.com/n-car/rpc-express-toolkit#readme",
79
- "engines": {
80
- "node": "\u003e=18.0.0"
81
- },
82
- "devDependencies": {
83
- "eslint": "^8.57.1",
84
- "eslint-config-airbnb-base": "^15.0.0",
85
- "eslint-config-prettier": "^9.1.2",
86
- "eslint-plugin-import": "^2.31.0",
87
- "eslint-plugin-prettier": "^5.5.3",
88
- "husky": "^8.0.0",
89
- "jest": "^29.7.0",
90
- "lint-staged": "^13.0.0",
91
- "prettier": "^3.6.2",
92
- "supertest": "^6.3.3",
93
- "terser": "^5.43.1"
94
- },
95
- "dependencies": {
96
- "ajv": "^8.12.0",
97
- "ajv-formats": "^2.1.1",
98
- "express": "^4.21.2",
99
- "nested-error-stacks": "^2.1.1"
100
- },
101
- "optionalDependencies": {
102
- "node-fetch": "^2.7.0"
103
- },
104
- "contributors": [
105
- {
106
- "name": "Nicola Carpanese",
107
- "email": "nicola.carpanese@gmail.com",
108
- "url": "https://github.com/n-car"
109
- }
110
- ],
111
- "husky": {
112
- "hooks": {
113
- "pre-commit": "lint-staged"
114
- }
115
- },
116
- "lint-staged": {
117
- "src/**/*.js": [
118
- "eslint --fix",
119
- "prettier --write"
120
- ],
121
- "examples/**/*.js": [
122
- "eslint --fix",
123
- "prettier --write"
124
- ]
125
- }
126
- }
1
+ {
2
+ "name": "rpc-express-toolkit",
3
+ "version": "4.3.2",
4
+ "description": "Enterprise-ready JSON-RPC 2.0 toolkit for Express.js with simplified APIs, structured logging, middleware system, schema validation, comprehensive BigInt/Date support. JSON middleware validation and explicit configuration.",
5
+ "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.d.ts",
10
+ "require": "./src/index.js",
11
+ "import": "./src/index.js"
12
+ },
13
+ "./safe": {
14
+ "types": "./src/safe.d.ts",
15
+ "require": "./src/safe.js",
16
+ "import": "./src/safe.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "src/",
21
+ "examples/",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "test": "jest",
27
+ "test:coverage": "jest --coverage",
28
+ "test:watch": "jest --watch",
29
+ "lint": "eslint src/ examples/",
30
+ "lint:fix": "eslint src/ examples/ --fix",
31
+ "clean": "node -e \"require(\u0027fs\u0027).existsSync(\u0027dist\u0027) \u0026\u0026 require(\u0027fs\u0027).rmSync(\u0027dist\u0027, {recursive: true})\"",
32
+ "build": "node -e \"require.resolve(\u0027rpc-toolkit-js-client\u0027)\"",
33
+ "prepublishOnly": "npm run build",
34
+ "precommit": "npm run lint \u0026\u0026 npm test"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/n-car/rpc-express-toolkit.git"
39
+ },
40
+ "keywords": [
41
+ "json-rpc",
42
+ "jsonrpc",
43
+ "json-rpc-2.0",
44
+ "express",
45
+ "toolkit",
46
+ "rpc",
47
+ "server",
48
+ "client",
49
+ "middleware",
50
+ "validation",
51
+ "schema",
52
+ "ajv",
53
+ "bigint",
54
+ "date",
55
+ "enterprise",
56
+ "nodejs",
57
+ "node",
58
+ "browser",
59
+ "ssl",
60
+ "self-signed",
61
+ "secure",
62
+ "fetch",
63
+ "batch",
64
+ "typescript",
65
+ "logging",
66
+ "cors",
67
+ "rate-limit",
68
+ "authentication"
69
+ ],
70
+ "author": "Nicola Carpanese \u003cnicola.carpanese@gmail.com\u003e",
71
+ "license": "MIT",
72
+ "bugs": {
73
+ "url": "https://github.com/n-car/rpc-express-toolkit/issues"
74
+ },
75
+ "homepage": "https://github.com/n-car/rpc-express-toolkit#readme",
76
+ "engines": {
77
+ "node": "\u003e=18.0.0"
78
+ },
79
+ "devDependencies": {
80
+ "eslint": "^8.57.1",
81
+ "eslint-config-airbnb-base": "^15.0.0",
82
+ "eslint-config-prettier": "^9.1.2",
83
+ "eslint-plugin-import": "^2.31.0",
84
+ "eslint-plugin-prettier": "^5.5.3",
85
+ "husky": "^8.0.0",
86
+ "jest": "^29.7.0",
87
+ "lint-staged": "^13.0.0",
88
+ "prettier": "^3.6.2",
89
+ "supertest": "^6.3.3",
90
+ "terser": "^5.43.1"
91
+ },
92
+ "dependencies": {
93
+ "ajv": "^8.12.0",
94
+ "ajv-formats": "^2.1.1",
95
+ "express": "^4.21.2",
96
+ "nested-error-stacks": "^2.1.1",
97
+ "rpc-toolkit-js-client": "https://github.com/n-car/rpc-toolkit-js-client/archive/refs/tags/v1.1.0.tar.gz"
98
+ },
99
+ "optionalDependencies": {
100
+ "node-fetch": "^2.7.0"
101
+ },
102
+ "contributors": [
103
+ {
104
+ "name": "Nicola Carpanese",
105
+ "email": "nicola.carpanese@gmail.com",
106
+ "url": "https://github.com/n-car"
107
+ }
108
+ ],
109
+ "husky": {
110
+ "hooks": {
111
+ "pre-commit": "lint-staged"
112
+ }
113
+ },
114
+ "lint-staged": {
115
+ "src/**/*.js": [
116
+ "eslint --fix",
117
+ "prettier --write"
118
+ ],
119
+ "examples/**/*.js": [
120
+ "eslint --fix",
121
+ "prettier --write"
122
+ ]
123
+ }
124
+ }