rpc-express-toolkit 2.2.0 → 2.3.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 +30 -1
- package/examples/ssl-development-example.js +12 -21
- package/package.json +7 -3
- package/src/clients/rpc-client.js +20 -31
- package/src/clients/rpc-client.min.js +1 -1
- package/src/clients/rpc-client.min.mjs +1 -1
- package/src/clients/rpc-client.mjs +4 -22
- package/src/index.d.ts +1 -3
package/README.md
CHANGED
|
@@ -91,6 +91,7 @@ app.listen(3000, () => {
|
|
|
91
91
|
});
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
|
|
94
95
|
### Client Usage
|
|
95
96
|
|
|
96
97
|
```javascript
|
|
@@ -108,6 +109,22 @@ const client = new RpcClient('/api');
|
|
|
108
109
|
const user = await client.call('getUser', { userId: 123 });
|
|
109
110
|
```
|
|
110
111
|
|
|
112
|
+
|
|
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:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
119
|
+
```
|
|
120
|
+
|
|
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.
|
|
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.
|
|
127
|
+
|
|
111
128
|
## Advanced Usage
|
|
112
129
|
|
|
113
130
|
### Configuration Options
|
|
@@ -321,14 +338,26 @@ new RpcClient(baseUrl, options)
|
|
|
321
338
|
|
|
322
339
|
#### Methods
|
|
323
340
|
|
|
341
|
+
|
|
324
342
|
##### `call(method, params, options)`
|
|
325
343
|
|
|
326
344
|
Make a single RPC call.
|
|
327
345
|
|
|
328
346
|
```javascript
|
|
329
|
-
|
|
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 });
|
|
330
356
|
```
|
|
331
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
|
+
|
|
332
361
|
##### `batch(requests)`
|
|
333
362
|
|
|
334
363
|
Make batch RPC calls.
|
|
@@ -19,29 +19,20 @@ async function demonstrateSSLOptions() {
|
|
|
19
19
|
console.log(' ✓ Created client with strict SSL validation (default)\n');
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
// Example 2: Development client per self-signed certificates (bypassa TUTTA la validazione SSL)
|
|
23
24
|
console.log('2. Development Client (bypasses SSL validation):');
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}, {
|
|
28
|
-
rejectUnauthorized: false // Solo per sviluppo!
|
|
29
|
-
});
|
|
30
|
-
console.log(' ✓ Created client that bypasses SSL certificate validation');
|
|
31
|
-
console.log(' ⚠️ WARNING: Only use this for development with self-signed certificates!\n');
|
|
25
|
+
console.log(' ⚠️ Per bypassare la validazione SSL in Node.js, imposta PRIMA di avviare il client:');
|
|
26
|
+
console.log(' process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";');
|
|
27
|
+
console.log(' ✓ Tutte le richieste accetteranno certificati self-signed (solo per sviluppo!)\n');
|
|
32
28
|
|
|
33
|
-
// Example 3:
|
|
34
|
-
console.log('3.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}, {
|
|
41
|
-
ca // Passa il certificato della tua CA self-signed
|
|
42
|
-
});
|
|
43
|
-
console.log(' ✓ Created client that accepts only your self-signed CA');
|
|
44
|
-
console.log(' ⚠️ WARNING: This is safer than disabling validation, but use valid certificates in production!\n');
|
|
29
|
+
// Example 3: Best practice
|
|
30
|
+
console.log('3. Best Practices:');
|
|
31
|
+
console.log(' ✓ Usa sempre la validazione SSL in produzione');
|
|
32
|
+
console.log(' ✓ Disabilita la validazione solo per sviluppo con self-signed');
|
|
33
|
+
console.log(' ✓ Usa variabili d’ambiente per controllare il comportamento SSL');
|
|
34
|
+
console.log(' ✓ Non committare mai process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" in produzione');
|
|
35
|
+
console.log(' ✓ Considera l’uso di certificati di sviluppo validi invece di disabilitare la validazione\n');
|
|
45
36
|
|
|
46
37
|
// Example 4: Demonstrate how to choose configuration based on environment
|
|
47
38
|
console.log('4. Environment-based Configuration:');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rpc-express-toolkit",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Enterprise-ready JSON-RPC 2.0 toolkit for Express.js with simplified APIs, structured logging, middleware system, schema validation,
|
|
3
|
+
"version": "2.3.1",
|
|
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. Semplificato: gestione SSL avanzata rimossa, vedi README.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -39,7 +39,11 @@
|
|
|
39
39
|
"validation",
|
|
40
40
|
"bigint",
|
|
41
41
|
"enterprise",
|
|
42
|
-
"nodejs"
|
|
42
|
+
"nodejs",
|
|
43
|
+
"ssl",
|
|
44
|
+
"self-signed",
|
|
45
|
+
"secure",
|
|
46
|
+
"fetch"
|
|
43
47
|
],
|
|
44
48
|
"author": "Nicola Carpanese <nicola.carpanese@gmail.com>",
|
|
45
49
|
"license": "MIT",
|
|
@@ -52,29 +52,10 @@ class RpcClient {
|
|
|
52
52
|
|
|
53
53
|
// Store fetch options for Node.js environments
|
|
54
54
|
this.#fetchOptions = {};
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// If options.agent is provided, use it directly
|
|
60
|
-
if (options.agent) {
|
|
61
|
-
this.#fetchOptions.agent = options.agent;
|
|
62
|
-
} else if (options.ca) {
|
|
63
|
-
// If options.ca is provided, create an agent with the custom CA
|
|
64
|
-
this.#fetchOptions.agent = new https.Agent({
|
|
65
|
-
ca: options.ca,
|
|
66
|
-
rejectUnauthorized: options.rejectUnauthorized !== false // true by default
|
|
67
|
-
});
|
|
68
|
-
} else if (options.rejectUnauthorized === false) {
|
|
69
|
-
// Legacy: allow disabling SSL validation for dev
|
|
70
|
-
this.#fetchOptions.agent = new https.Agent({
|
|
71
|
-
rejectUnauthorized: false
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
} catch (e) {
|
|
75
|
-
// Ignore if https module is not available
|
|
76
|
-
}
|
|
77
|
-
}
|
|
55
|
+
// SSL validation: la gestione avanzata (agent/ca) è rimossa per semplicità e compatibilità.
|
|
56
|
+
// Per bypassare i certificati self-signed in sviluppo, imposta:
|
|
57
|
+
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
58
|
+
// Vedi README per dettagli e best practice.
|
|
78
59
|
}
|
|
79
60
|
|
|
80
61
|
/**
|
|
@@ -96,13 +77,16 @@ class RpcClient {
|
|
|
96
77
|
async call(method, params = {}, id = undefined, overrideHeaders = {}) {
|
|
97
78
|
// Auto-generate ID if not provided (null means notification)
|
|
98
79
|
const requestId = id === undefined ? this.#generateId() : id;
|
|
99
|
-
|
|
80
|
+
|
|
81
|
+
// Costruisci il payload secondo lo standard: params omesso se undefined/null
|
|
100
82
|
const requestBody = {
|
|
101
83
|
jsonrpc: "2.0",
|
|
102
84
|
method,
|
|
103
|
-
params,
|
|
104
85
|
id: requestId,
|
|
105
86
|
};
|
|
87
|
+
if (params !== undefined && params !== null) {
|
|
88
|
+
requestBody.params = params;
|
|
89
|
+
}
|
|
106
90
|
|
|
107
91
|
try {
|
|
108
92
|
const response = await fetchFn(this.#endpoint, {
|
|
@@ -151,12 +135,17 @@ class RpcClient {
|
|
|
151
135
|
* @returns {Promise<Array<any>>} Array of results in the same order as requests.
|
|
152
136
|
*/
|
|
153
137
|
async batch(requests, overrideHeaders = {}) {
|
|
154
|
-
const batchRequests = requests.map(req =>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
+
});
|
|
160
149
|
|
|
161
150
|
try {
|
|
162
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={}){
|
|
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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
let t;if("undefined"==typeof BigInt||BigInt.prototype.toJSON||(BigInt.prototype.toJSON=function(){return this.toString()}),"undefined"!=typeof fetch)t=fetch;else try{const{default:e}=await import("node-fetch");t=e}catch(e){if("undefined"==typeof globalThis||!globalThis.fetch)throw new Error("fetch is not available. Please install node-fetch or use Node.js 18+");t=globalThis.fetch}export class RpcClient{#t;#e;#r;#
|
|
1
|
+
let t;if("undefined"==typeof BigInt||BigInt.prototype.toJSON||(BigInt.prototype.toJSON=function(){return this.toString()}),"undefined"!=typeof fetch)t=fetch;else try{const{default:e}=await import("node-fetch");t=e}catch(e){if("undefined"==typeof globalThis||!globalThis.fetch)throw new Error("fetch is not available. Please install node-fetch or use Node.js 18+");t=globalThis.fetch}export class RpcClient{#t;#e;#r;#s;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.#s={}}async call(e,r={},s=null,i={}){null===s&&(s=++this.#r);const n={jsonrpc:"2.0",method:e,params:r,id:s};try{const e=await t(this.#t,{method:"POST",headers:{...this.#e,...i},body:JSON.stringify(n),...this.#s});if(!e.ok)throw new Error(`HTTP Error: ${e.status} ${e.statusText}`);const r=await e.json();if(r.error)throw r.error;return this.deserializeBigIntsAndDates(r.result)}catch(t){throw console.error("RPC call failed:",t),t}}async notify(e,r={},s={}){const i={jsonrpc:"2.0",method:e,params:r};try{const e=await t(this.#t,{method:"POST",headers:{...this.#e,...s},body:JSON.stringify(i),...this.#s});if(!e.ok)throw new Error(`HTTP Error: ${e.status} ${e.statusText}`)}catch(t){throw console.error("RPC notification failed:",t),t}}async batch(e,r={}){const s=e.map(t=>{const e={jsonrpc:"2.0",method:t.method,params:t.params||{}};return t.notification||(e.id=void 0!==t.id?t.id:++this.#r),e});try{const e=await t(this.#t,{method:"POST",headers:{...this.#e,...r},body:JSON.stringify(s),...this.#s});if(!e.ok)throw new Error(`HTTP Error: ${e.status} ${e.statusText}`);const i=await e.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("RPC batch 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,s]of Object.entries(t))e[r]=this.serializeBigIntsAndDates(s);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}}
|
|
@@ -53,28 +53,10 @@ export class RpcClient {
|
|
|
53
53
|
|
|
54
54
|
// Store fetch options for Node.js environments
|
|
55
55
|
this.#fetchOptions = {};
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (typeof require !== 'undefined') {
|
|
61
|
-
try {
|
|
62
|
-
const https = require('https');
|
|
63
|
-
this.#fetchOptions.agent = new https.Agent({
|
|
64
|
-
rejectUnauthorized: false
|
|
65
|
-
});
|
|
66
|
-
} catch (e) {
|
|
67
|
-
// For ES modules, we'll try dynamic import
|
|
68
|
-
import('https').then(httpsModule => {
|
|
69
|
-
this.#fetchOptions.agent = new httpsModule.Agent({
|
|
70
|
-
rejectUnauthorized: false
|
|
71
|
-
});
|
|
72
|
-
}).catch(() => {
|
|
73
|
-
// Ignore if https module is not available
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
56
|
+
// SSL validation: la gestione avanzata (agent/ca) è rimossa per semplicità e compatibilità.
|
|
57
|
+
// Per bypassare i certificati self-signed in sviluppo, imposta:
|
|
58
|
+
// process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
59
|
+
// Vedi README per dettagli e best practice.
|
|
78
60
|
}
|
|
79
61
|
|
|
80
62
|
/**
|
package/src/index.d.ts
CHANGED
|
@@ -99,9 +99,7 @@ declare class RpcClient {
|
|
|
99
99
|
endpoint: string,
|
|
100
100
|
defaultHeaders?: Record<string, string>,
|
|
101
101
|
options?: {
|
|
102
|
-
|
|
103
|
-
ca?: string | Buffer;
|
|
104
|
-
agent?: any;
|
|
102
|
+
// Opzioni SSL avanzate rimosse: usa process.env.NODE_TLS_REJECT_UNAUTHORIZED per sviluppo
|
|
105
103
|
}
|
|
106
104
|
);
|
|
107
105
|
|