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 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
- Per bypassare la validazione dei certificati SSL (ad esempio con certificati self-signed in sviluppo), imposta **prima** di avviare il client:
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
- **Attenzione:**
121
- - Questa impostazione disabilita la validazione SSL per tutte le richieste nel processo Node.js.
122
- - Usala solo in ambienti di sviluppo!
123
- - Non committare mai questa impostazione in produzione.
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
- Le opzioni avanzate `agent` e `ca` sono state rimosse per semplicità e compatibilità con Node.js moderno.
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
- const result = await client.call('add', { a: 1, b: 2 });
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
- // Client per sviluppo con certificato self-signed (accetta qualsiasi certificato, NON usare in produzione)
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 // Solo per sviluppo!
568
+ rejectUnauthorized: false // Development only!
556
569
  });
557
570
 
558
- // Client per sviluppo con CA self-signed (modo sicuro: accetta solo la tua CA)
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 // Passa il certificato della tua CA self-signed
564
- // oppure: agent: new require('https').Agent({ ca, rejectUnauthorized: true })
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**: Usa `rejectUnauthorized: false` solo in sviluppo. Per maggiore sicurezza, preferisci sempre la soluzione con `ca` per accettare solo la tua CA self-signed. In produzione usa sempre certificati validi e firmati da una CA attendibile.
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.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. Semplificato: gestione SSL avanzata rimossa, vedi README.",
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: la gestione avanzata (agent/ca) è rimossa per semplicità e compatibilità.
56
- // Per bypassare i certificati self-signed in sviluppo, imposta:
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
- // Vedi README per dettagli e best practice.
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 = {}, id = undefined, overrideHeaders = {}) {
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
- jsonrpc: "2.0",
137
- method: req.method,
138
- params: req.params || {},
139
- id: req.id !== undefined ? req.id : this.#generateId()
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={},r=void 0,i={}){const s={jsonrpc:"2.0",method:t,params:e,id:void 0===r?this.#s():r};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=>({jsonrpc:"2.0",method:t.method,params:t.params||{},id:void 0!==t.id?t.id:this.#s()}));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
+ 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;