rpc-express-toolkit 2.3.0 → 2.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/README.md +29 -14
- package/package.json +2 -2
- package/src/clients/rpc-client.js +20 -12
- package/src/clients/rpc-client.min.js +1 -1
package/README.md
CHANGED
|
@@ -109,20 +109,21 @@ const client = new RpcClient('/api');
|
|
|
109
109
|
const user = await client.call('getUser', { userId: 123 });
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
#### SSL e certificati self-signed in sviluppo (Node.js)
|
|
113
112
|
|
|
114
|
-
|
|
113
|
+
#### ⚠️ SSL and self-signed certificates in development (Node.js)
|
|
114
|
+
|
|
115
|
+
If you need to connect to a server with a self-signed certificate during development, set the following environment variable **before** starting your Node.js process:
|
|
115
116
|
|
|
116
117
|
```js
|
|
117
118
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
118
119
|
```
|
|
119
120
|
|
|
120
|
-
**
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
121
|
+
**Warning:**
|
|
122
|
+
- This disables SSL certificate validation for all HTTPS requests in the Node.js process.
|
|
123
|
+
- Use this only in development environments!
|
|
124
|
+
- Never commit or enable this setting in production.
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
Advanced options like `agent` and `ca` have been removed for simplicity and compatibility with modern Node.js. This is the only universal and reliable way to bypass SSL validation for development with self-signed certificates.
|
|
126
127
|
|
|
127
128
|
## Advanced Usage
|
|
128
129
|
|
|
@@ -337,14 +338,26 @@ new RpcClient(baseUrl, options)
|
|
|
337
338
|
|
|
338
339
|
#### Methods
|
|
339
340
|
|
|
341
|
+
|
|
340
342
|
##### `call(method, params, options)`
|
|
341
343
|
|
|
342
344
|
Make a single RPC call.
|
|
343
345
|
|
|
344
346
|
```javascript
|
|
345
|
-
|
|
347
|
+
// If the method does not require parameters, you can omit the params argument:
|
|
348
|
+
const result = await client.call('ping');
|
|
349
|
+
|
|
350
|
+
// If you pass params as undefined or null, the field will be omitted from the JSON-RPC payload (per spec):
|
|
351
|
+
await client.call('ping', undefined);
|
|
352
|
+
await client.call('ping', null);
|
|
353
|
+
|
|
354
|
+
// If you pass an object or array, it will be sent as params:
|
|
355
|
+
await client.call('add', { a: 1, b: 2 });
|
|
346
356
|
```
|
|
347
357
|
|
|
358
|
+
> **Note:**
|
|
359
|
+
> The `params` field is omitted from the JSON-RPC request if you pass `undefined` or `null`, as required by the JSON-RPC 2.0 specification. If you pass an object or array, it will be included as `params`.
|
|
360
|
+
|
|
348
361
|
##### `batch(requests)`
|
|
349
362
|
|
|
350
363
|
Make batch RPC calls.
|
|
@@ -548,24 +561,26 @@ const clientWithHeaders = new RpcClient('https://api.example.com/rpc', {
|
|
|
548
561
|
'User-Agent': 'MyApp/1.0'
|
|
549
562
|
});
|
|
550
563
|
|
|
551
|
-
//
|
|
564
|
+
// Development client with self-signed certificate (accepts any certificate, DO NOT use in production)
|
|
552
565
|
const devClient = new RpcClient('https://localhost:3000/api', {
|
|
553
566
|
'Authorization': 'Bearer dev-token'
|
|
554
567
|
}, {
|
|
555
|
-
rejectUnauthorized: false //
|
|
568
|
+
rejectUnauthorized: false // Development only!
|
|
556
569
|
});
|
|
557
570
|
|
|
558
|
-
//
|
|
571
|
+
// Development client with custom self-signed CA (safer: accepts only your CA)
|
|
559
572
|
const ca = fs.readFileSync('path/to/your/selfsigned-ca.pem');
|
|
560
573
|
const devClientWithCA = new RpcClient('https://localhost:3000/api', {
|
|
561
574
|
'Authorization': 'Bearer dev-token'
|
|
562
575
|
}, {
|
|
563
|
-
ca //
|
|
564
|
-
//
|
|
576
|
+
ca // Pass your self-signed CA certificate
|
|
577
|
+
// or: agent: new require('https').Agent({ ca, rejectUnauthorized: true })
|
|
565
578
|
});
|
|
579
|
+
|
|
580
|
+
**⚠️ Security Warning**: Use `rejectUnauthorized: false` for development only. For better security, always prefer the `ca` solution to accept only your self-signed CA. In production always use valid certificates signed by a trusted CA.
|
|
566
581
|
```
|
|
567
582
|
|
|
568
|
-
**⚠️ Security Warning**:
|
|
583
|
+
**⚠️ Security Warning**: Use `rejectUnauthorized: false` for development only. For better security, always prefer the `ca` solution to accept only your self-signed CA. In production always use valid certificates signed by a trusted CA.
|
|
569
584
|
|
|
570
585
|
### Built-in Middlewares
|
|
571
586
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rpc-express-toolkit",
|
|
3
|
-
"version": "2.3.
|
|
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.
|
|
3
|
+
"version": "2.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. Advanced SSL options removed, see README.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -52,10 +52,10 @@ class RpcClient {
|
|
|
52
52
|
|
|
53
53
|
// Store fetch options for Node.js environments
|
|
54
54
|
this.#fetchOptions = {};
|
|
55
|
-
// SSL validation:
|
|
56
|
-
//
|
|
55
|
+
// SSL validation: advanced options (agent/ca) have been removed for simplicity and compatibility.
|
|
56
|
+
// To bypass self-signed certificates in development, set:
|
|
57
57
|
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
58
|
-
//
|
|
58
|
+
// See README for details and best practices.
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
@@ -74,16 +74,19 @@ class RpcClient {
|
|
|
74
74
|
* @param {Object} [overrideHeaders={}] Optional headers to override defaults for this request.
|
|
75
75
|
* @returns {Promise<any>} The result of the RPC call.
|
|
76
76
|
*/
|
|
77
|
-
async call(method, params
|
|
77
|
+
async call(method, params, id = undefined, overrideHeaders = {}) {
|
|
78
78
|
// Auto-generate ID if not provided (null means notification)
|
|
79
79
|
const requestId = id === undefined ? this.#generateId() : id;
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
// Build the payload according to the spec: params omitted if undefined/null
|
|
81
82
|
const requestBody = {
|
|
82
83
|
jsonrpc: "2.0",
|
|
83
84
|
method,
|
|
84
|
-
params,
|
|
85
85
|
id: requestId,
|
|
86
86
|
};
|
|
87
|
+
if (params !== undefined && params !== null) {
|
|
88
|
+
requestBody.params = params;
|
|
89
|
+
}
|
|
87
90
|
|
|
88
91
|
try {
|
|
89
92
|
const response = await fetchFn(this.#endpoint, {
|
|
@@ -132,12 +135,17 @@ class RpcClient {
|
|
|
132
135
|
* @returns {Promise<Array<any>>} Array of results in the same order as requests.
|
|
133
136
|
*/
|
|
134
137
|
async batch(requests, overrideHeaders = {}) {
|
|
135
|
-
const batchRequests = requests.map(req =>
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
const batchRequests = requests.map(req => {
|
|
139
|
+
const obj = {
|
|
140
|
+
jsonrpc: "2.0",
|
|
141
|
+
method: req.method,
|
|
142
|
+
id: req.id !== undefined ? req.id : this.#generateId()
|
|
143
|
+
};
|
|
144
|
+
if (req.params !== undefined && req.params !== null) {
|
|
145
|
+
obj.params = req.params;
|
|
146
|
+
}
|
|
147
|
+
return obj;
|
|
148
|
+
});
|
|
141
149
|
|
|
142
150
|
try {
|
|
143
151
|
const response = await fetchFn(this.#endpoint, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
let fetchFn;if("undefined"==typeof BigInt||BigInt.prototype.toJSON||(BigInt.prototype.toJSON=function(){return this.toString()}),"undefined"!=typeof fetch)fetchFn=fetch;else try{fetchFn=require("node-fetch")}catch(t){if("undefined"==typeof globalThis||!globalThis.fetch)throw new Error("fetch is not available. Please install node-fetch or use Node.js 18+");fetchFn=globalThis.fetch}class RpcClient{#t;#e;#r;#i;constructor(t,e={},r={}){this.#t=t,this.#e={"Content-Type":"application/json",...e},this.#r=1e3*Date.now()+Math.floor(1e3*Math.random()),this.#i={}}#s(){return++this.#r}async call(t,e
|
|
1
|
+
let fetchFn;if("undefined"==typeof BigInt||BigInt.prototype.toJSON||(BigInt.prototype.toJSON=function(){return this.toString()}),"undefined"!=typeof fetch)fetchFn=fetch;else try{fetchFn=require("node-fetch")}catch(t){if("undefined"==typeof globalThis||!globalThis.fetch)throw new Error("fetch is not available. Please install node-fetch or use Node.js 18+");fetchFn=globalThis.fetch}class RpcClient{#t;#e;#r;#i;constructor(t,e={},r={}){this.#t=t,this.#e={"Content-Type":"application/json",...e},this.#r=1e3*Date.now()+Math.floor(1e3*Math.random()),this.#i={}}#s(){return++this.#r}async call(t,e,r=void 0,i={}){const s={jsonrpc:"2.0",method:t,id:void 0===r?this.#s():r};null!=e&&(s.params=e);try{const t=await fetchFn(this.#t,{method:"POST",headers:{...this.#e,...i},body:JSON.stringify(s),...this.#i});if(!t.ok)throw new Error(`HTTP Error: ${t.status} ${t.statusText}`);const e=await t.json();if(e.error)throw e.error;return this.deserializeBigIntsAndDates(e.result)}catch(t){throw console.error("RPC call failed:",t),t}}async notify(t,e={},r={}){await this.call(t,e,null,r)}async batch(t,e={}){const r=t.map(t=>{const e={jsonrpc:"2.0",method:t.method,id:void 0!==t.id?t.id:this.#s()};return void 0!==t.params&&null!==t.params&&(e.params=t.params),e});try{const t=await fetchFn(this.#t,{method:"POST",headers:{...this.#e,...e},body:JSON.stringify(r),...this.#i});if(!t.ok)throw new Error(`HTTP Error: ${t.status} ${t.statusText}`);const i=await t.json();if(Array.isArray(i))return i.map(t=>{if(t.error)throw t.error;return this.deserializeBigIntsAndDates(t.result)});if(i.error)throw i.error;return[this.deserializeBigIntsAndDates(i.result)]}catch(t){throw console.error("Batch RPC call failed:",t),t}}serializeBigIntsAndDates(t){if("bigint"==typeof t)return t.toString();if(Array.isArray(t))return t.map(t=>this.serializeBigIntsAndDates(t));if(t instanceof Date)return t.toISOString();if(t&&"object"==typeof t){const e={};for(const[r,i]of Object.entries(t))e[r]=this.serializeBigIntsAndDates(i);return e}return t}deserializeBigIntsAndDates(t){const e=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/;if("string"==typeof t){if(/^-?\d+n?$/.test(t))return BigInt(t.replace(/n$/,""));if(e.test(t)){const e=new Date(t);if(!isNaN(e.getTime()))return e}}return Array.isArray(t)?t.map(t=>this.deserializeBigIntsAndDates(t)):t&&"object"==typeof t?Object.fromEntries(Object.entries(t).map(([t,e])=>[t,this.deserializeBigIntsAndDates(e)])):t}}module.exports=RpcClient,module.exports.RpcClient=RpcClient;
|