rpc-express-toolkit 4.2.0 → 4.2.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 +47 -0
- package/examples/introspection/client.js +68 -0
- package/examples/introspection/server.js +89 -0
- package/package.json +1 -1
- package/src/batch.js +6 -2
- package/src/index.js +1035 -842
- package/src/validation.js +222 -221
package/README.md
CHANGED
|
@@ -105,10 +105,57 @@ const { createSafeClient } = require('rpc-express-toolkit-safe');
|
|
|
105
105
|
const client = createSafeClient('http://localhost:3000/api');
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
## Introspection Methods
|
|
109
|
+
|
|
110
|
+
Enable introspection to expose metadata about registered methods via reserved `__rpc.*` methods:
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
const rpc = new RpcEndpoint(app, context, {
|
|
114
|
+
enableIntrospection: true // Enable __rpc.* methods
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Register methods with public schemas
|
|
118
|
+
rpc.addMethod('add', async (req, ctx, params) => {
|
|
119
|
+
return params.a + params.b;
|
|
120
|
+
}, {
|
|
121
|
+
schema: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: {
|
|
124
|
+
a: { type: 'number' },
|
|
125
|
+
b: { type: 'number' }
|
|
126
|
+
},
|
|
127
|
+
required: ['a', 'b']
|
|
128
|
+
},
|
|
129
|
+
exposeSchema: true, // Make schema publicly queryable
|
|
130
|
+
description: 'Add two numbers'
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Available introspection methods:
|
|
134
|
+
// __rpc.listMethods() → ["add", "multiply", ...]
|
|
135
|
+
// __rpc.describe({method: "add"}) → {name, schema, description}
|
|
136
|
+
// __rpc.describeAll() → [{name, schema, description}, ...]
|
|
137
|
+
// __rpc.version() → {toolkit, version, expressVersion, nodeVersion}
|
|
138
|
+
// __rpc.capabilities() → {safeMode, batch, introspection, ...}
|
|
139
|
+
|
|
140
|
+
// Client usage
|
|
141
|
+
const methods = await client.call('__rpc.listMethods');
|
|
142
|
+
const addInfo = await client.call('__rpc.describe', { method: 'add' });
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Note:** The introspection prefix is configurable via `introspectionPrefix` option (default: `__rpc`). User methods starting with this prefix are rejected to prevent conflicts.
|
|
146
|
+
|
|
108
147
|
## Full Details
|
|
109
148
|
|
|
110
149
|
For advanced configuration, middleware, structured logging, safe serialization, error handling, and more, see `README_ADVANCED.md`.
|
|
111
150
|
|
|
151
|
+
## 🔗 Related Projects
|
|
152
|
+
|
|
153
|
+
- [rpc-php-toolkit](https://github.com/n-car/rpc-php-toolkit) - PHP implementation
|
|
154
|
+
- [rpc-dotnet-toolkit](https://github.com/n-car/rpc-dotnet-toolkit) - .NET implementation
|
|
155
|
+
- [rpc-arduino-toolkit](https://github.com/n-car/rpc-arduino-toolkit) - Arduino/ESP32 implementation
|
|
156
|
+
- [rpc-java-toolkit](https://github.com/n-car/rpc-java-toolkit) - Java & Android implementation
|
|
157
|
+
- [node-red-contrib-rpc-toolkit](https://github.com/n-car/node-red-contrib-rpc-toolkit) - Node-RED visual programming
|
|
158
|
+
|
|
112
159
|
## Contributing
|
|
113
160
|
|
|
114
161
|
```bash
|
|
@@ -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
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rpc-express-toolkit",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.1",
|
|
4
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
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
package/src/batch.js
CHANGED
|
@@ -140,8 +140,8 @@ class BatchHandler {
|
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
const
|
|
144
|
-
if (!
|
|
143
|
+
const methodConfig = this.endpoint.methods[method];
|
|
144
|
+
if (!methodConfig) {
|
|
145
145
|
return {
|
|
146
146
|
jsonrpc: '2.0',
|
|
147
147
|
id: id || null,
|
|
@@ -153,6 +153,10 @@ class BatchHandler {
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// Extract handler (support both function and config object)
|
|
157
|
+
const handler =
|
|
158
|
+
typeof methodConfig === 'function' ? methodConfig : methodConfig.handler;
|
|
159
|
+
|
|
156
160
|
// If no id is provided, this is a notification - don't return response
|
|
157
161
|
const isNotification = id === undefined || id === null;
|
|
158
162
|
|