rpc-express-toolkit 2.3.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 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rpc-express-toolkit",
3
- "version": "2.3.0",
3
+ "version": "2.3.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. Semplificato: gestione SSL avanzata rimossa, vedi README.",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -77,13 +77,16 @@ class RpcClient {
77
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
+ // Costruisci il payload secondo lo standard: params omesso se 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;