rpc-express-toolkit 2.3.2 → 3.0.0
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 +221 -9
- package/examples/example.js +17 -18
- package/examples/server-client-example.js +120 -114
- package/examples/ssl-development-example.js +100 -74
- package/package.json +5 -5
- package/src/batch.js +256 -237
- package/src/clients/rpc-client.js +125 -44
- package/src/clients/rpc-client.min.js +1 -1
- package/src/clients/rpc-client.min.mjs +1 -1
- package/src/clients/rpc-client.mjs +159 -96
- package/src/index.d.ts +220 -58
- package/src/index.js +319 -115
- package/src/logger.js +184 -153
- package/src/middleware.js +229 -224
- package/src/validation.js +220 -221
package/src/middleware.js
CHANGED
|
@@ -1,224 +1,229 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @file Middleware Manager
|
|
3
|
+
* @description Handles middleware execution for JSON-RPC requests including built-in middleware
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Middleware manager for handling RPC middleware
|
|
8
|
+
*/
|
|
9
|
+
class MiddlewareManager {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.middlewares = {
|
|
12
|
+
beforeCall: [],
|
|
13
|
+
afterCall: [],
|
|
14
|
+
onError: [],
|
|
15
|
+
beforeValidation: [],
|
|
16
|
+
afterValidation: [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Add middleware for a specific hook
|
|
22
|
+
* @param {string} hook - The hook name (beforeCall, afterCall, onError, etc.)
|
|
23
|
+
* @param {Function} middleware - The middleware function
|
|
24
|
+
*/
|
|
25
|
+
use(hook, middleware) {
|
|
26
|
+
if (!this.middlewares[hook]) {
|
|
27
|
+
throw new Error(`Unknown middleware hook: ${hook}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (typeof middleware !== 'function') {
|
|
31
|
+
throw new Error('Middleware must be a function');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.middlewares[hook].push(middleware);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Execute middlewares for a specific hook
|
|
39
|
+
* @param {string} hook
|
|
40
|
+
* @param {Object} context
|
|
41
|
+
* @returns {Promise<Object>}
|
|
42
|
+
*/
|
|
43
|
+
async execute(hook, context) {
|
|
44
|
+
const middlewares = this.middlewares[hook] || [];
|
|
45
|
+
let result = context;
|
|
46
|
+
|
|
47
|
+
for (const middleware of middlewares) {
|
|
48
|
+
try {
|
|
49
|
+
const middlewareResult = await middleware(result);
|
|
50
|
+
|
|
51
|
+
// If middleware returns an object, merge it with context
|
|
52
|
+
if (middlewareResult && typeof middlewareResult === 'object') {
|
|
53
|
+
result = { ...result, ...middlewareResult };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If middleware explicitly returns false, stop execution
|
|
57
|
+
if (middlewareResult === false) {
|
|
58
|
+
throw new Error('Middleware execution stopped');
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
// For error hooks, continue even if middleware fails
|
|
62
|
+
if (hook === 'onError') {
|
|
63
|
+
console.error('Error middleware failed:', error);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get all middlewares for a hook
|
|
75
|
+
* @param {string} hook
|
|
76
|
+
* @returns {Function[]}
|
|
77
|
+
*/
|
|
78
|
+
getMiddlewares(hook) {
|
|
79
|
+
return this.middlewares[hook] || [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Remove all middlewares for a hook
|
|
84
|
+
* @param {string} hook
|
|
85
|
+
*/
|
|
86
|
+
clear(hook) {
|
|
87
|
+
if (hook) {
|
|
88
|
+
this.middlewares[hook] = [];
|
|
89
|
+
} else {
|
|
90
|
+
// Clear all hooks
|
|
91
|
+
Object.keys(this.middlewares).forEach((h) => {
|
|
92
|
+
this.middlewares[h] = [];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Remove a specific middleware from a hook
|
|
99
|
+
* @param {string} hook
|
|
100
|
+
* @param {Function} middleware
|
|
101
|
+
*/
|
|
102
|
+
remove(hook, middleware) {
|
|
103
|
+
if (!this.middlewares[hook]) return;
|
|
104
|
+
|
|
105
|
+
const index = this.middlewares[hook].indexOf(middleware);
|
|
106
|
+
if (index > -1) {
|
|
107
|
+
this.middlewares[hook].splice(index, 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Built-in middleware functions
|
|
114
|
+
*/
|
|
115
|
+
const builtInMiddlewares = {
|
|
116
|
+
/**
|
|
117
|
+
* Rate limiting middleware
|
|
118
|
+
* @param {Object} options
|
|
119
|
+
* @returns {Function}
|
|
120
|
+
*/
|
|
121
|
+
rateLimit(options = {}) {
|
|
122
|
+
const windowMs = options.windowMs || 60000; // 1 minute
|
|
123
|
+
const max = options.max || 100; // 100 requests per window
|
|
124
|
+
const message = options.message || 'Too many requests';
|
|
125
|
+
|
|
126
|
+
const requests = new Map();
|
|
127
|
+
|
|
128
|
+
return async (context) => {
|
|
129
|
+
const { req } = context;
|
|
130
|
+
const key = req.ip || req.connection?.remoteAddress || 'unknown';
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
|
|
133
|
+
// Clean old entries
|
|
134
|
+
if (requests.has(key)) {
|
|
135
|
+
const userRequests = requests.get(key);
|
|
136
|
+
const validRequests = userRequests.filter(
|
|
137
|
+
(time) => now - time < windowMs
|
|
138
|
+
);
|
|
139
|
+
requests.set(key, validRequests);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const userRequests = requests.get(key) || [];
|
|
143
|
+
|
|
144
|
+
if (userRequests.length >= max) {
|
|
145
|
+
const error = new Error(message);
|
|
146
|
+
error.code = -32000; // Custom JSON-RPC error code
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
userRequests.push(now);
|
|
151
|
+
requests.set(key, userRequests);
|
|
152
|
+
|
|
153
|
+
return context;
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Authentication middleware
|
|
159
|
+
* @param {Function} authFunction
|
|
160
|
+
* @returns {Function}
|
|
161
|
+
*/
|
|
162
|
+
auth(authFunction) {
|
|
163
|
+
return async (context) => {
|
|
164
|
+
const { req } = context;
|
|
165
|
+
const isAuthenticated = await authFunction(req);
|
|
166
|
+
|
|
167
|
+
if (!isAuthenticated) {
|
|
168
|
+
const error = new Error('Authentication required');
|
|
169
|
+
error.code = -32001;
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return context;
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* CORS middleware
|
|
179
|
+
* @param {Object} options
|
|
180
|
+
* @returns {Function}
|
|
181
|
+
*/
|
|
182
|
+
cors(options = {}) {
|
|
183
|
+
const origin = options.origin || '*';
|
|
184
|
+
const methods = options.methods || ['POST'];
|
|
185
|
+
const headers = options.headers || ['Content-Type'];
|
|
186
|
+
|
|
187
|
+
return async (context) => {
|
|
188
|
+
const { res } = context;
|
|
189
|
+
|
|
190
|
+
res.header('Access-Control-Allow-Origin', origin);
|
|
191
|
+
res.header('Access-Control-Allow-Methods', methods.join(', '));
|
|
192
|
+
res.header('Access-Control-Allow-Headers', headers.join(', '));
|
|
193
|
+
|
|
194
|
+
return context;
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Request timing middleware
|
|
200
|
+
* @returns {Function}
|
|
201
|
+
*/
|
|
202
|
+
timing() {
|
|
203
|
+
return async (context) => {
|
|
204
|
+
context.startTime = Date.now();
|
|
205
|
+
return context;
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Method whitelist middleware
|
|
211
|
+
* @param {string[]} allowedMethods
|
|
212
|
+
* @returns {Function}
|
|
213
|
+
*/
|
|
214
|
+
methodWhitelist(allowedMethods) {
|
|
215
|
+
return async (context) => {
|
|
216
|
+
const { method } = context;
|
|
217
|
+
|
|
218
|
+
if (!allowedMethods.includes(method)) {
|
|
219
|
+
const error = new Error(`Method '${method}' is not allowed`);
|
|
220
|
+
error.code = -32601;
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return context;
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
module.exports = { MiddlewareManager, builtInMiddlewares };
|