@acala-network/chopsticks 0.9.9 → 0.9.11-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/dist/cjs/context.js +2 -1
- package/dist/cjs/plugins/dry-run/index.d.ts +3 -0
- package/dist/cjs/schema/index.d.ts +3 -0
- package/dist/cjs/schema/index.js +3 -0
- package/dist/cjs/server.d.ts +1 -1
- package/dist/cjs/server.js +115 -42
- package/dist/esm/context.js +2 -1
- package/dist/esm/plugins/dry-run/index.d.ts +3 -0
- package/dist/esm/schema/index.d.ts +3 -0
- package/dist/esm/schema/index.js +3 -0
- package/dist/esm/server.d.ts +1 -1
- package/dist/esm/server.js +110 -42
- package/package.json +8 -8
package/dist/cjs/context.js
CHANGED
|
@@ -104,7 +104,8 @@ const setupContext = async (argv, overrideParent = false)=>{
|
|
|
104
104
|
runtimeLogLevel: argv['runtime-log-level'],
|
|
105
105
|
registeredTypes: argv['registered-types'],
|
|
106
106
|
offchainWorker: argv['offchain-worker'],
|
|
107
|
-
maxMemoryBlockCount: argv['max-memory-block-count']
|
|
107
|
+
maxMemoryBlockCount: argv['max-memory-block-count'],
|
|
108
|
+
processQueuedMessages: argv['process-queued-messages']
|
|
108
109
|
});
|
|
109
110
|
// load block from db
|
|
110
111
|
if (chain.db) {
|
|
@@ -82,6 +82,7 @@ export declare const dryRunSchema: z.ZodObject<{
|
|
|
82
82
|
'runtime-log-level': z.ZodOptional<z.ZodNumber>;
|
|
83
83
|
'offchain-worker': z.ZodOptional<z.ZodBoolean>;
|
|
84
84
|
resume: z.ZodOptional<z.ZodUnion<[z.ZodIntersection<z.ZodString, z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>>, z.ZodNumber, z.ZodBoolean]>>;
|
|
85
|
+
'process-queued-messages': z.ZodOptional<z.ZodBoolean>;
|
|
85
86
|
}, "strip", z.ZodTypeAny, {
|
|
86
87
|
port: number;
|
|
87
88
|
'build-block-mode': import("@acala-network/chopsticks-core").BuildBlockMode;
|
|
@@ -119,6 +120,7 @@ export declare const dryRunSchema: z.ZodObject<{
|
|
|
119
120
|
'runtime-log-level'?: number | undefined;
|
|
120
121
|
'offchain-worker'?: boolean | undefined;
|
|
121
122
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
123
|
+
'process-queued-messages'?: boolean | undefined;
|
|
122
124
|
}, {
|
|
123
125
|
extrinsic?: string | undefined;
|
|
124
126
|
address?: string | undefined;
|
|
@@ -156,6 +158,7 @@ export declare const dryRunSchema: z.ZodObject<{
|
|
|
156
158
|
'runtime-log-level'?: number | undefined;
|
|
157
159
|
'offchain-worker'?: boolean | undefined;
|
|
158
160
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
161
|
+
'process-queued-messages'?: boolean | undefined;
|
|
159
162
|
}>;
|
|
160
163
|
export type DryRunSchemaType = z.infer<typeof dryRunSchema>;
|
|
161
164
|
export * from './cli.js';
|
|
@@ -78,6 +78,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
78
78
|
'runtime-log-level': z.ZodOptional<z.ZodNumber>;
|
|
79
79
|
'offchain-worker': z.ZodOptional<z.ZodBoolean>;
|
|
80
80
|
resume: z.ZodOptional<z.ZodUnion<[z.ZodIntersection<z.ZodString, z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>>, z.ZodNumber, z.ZodBoolean]>>;
|
|
81
|
+
'process-queued-messages': z.ZodOptional<z.ZodBoolean>;
|
|
81
82
|
}, "strip", ZodTypeAny, {
|
|
82
83
|
port: number;
|
|
83
84
|
'build-block-mode': BuildBlockMode;
|
|
@@ -108,6 +109,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
108
109
|
'runtime-log-level'?: number | undefined;
|
|
109
110
|
'offchain-worker'?: boolean | undefined;
|
|
110
111
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
112
|
+
'process-queued-messages'?: boolean | undefined;
|
|
111
113
|
}, {
|
|
112
114
|
port?: number | undefined;
|
|
113
115
|
endpoint?: string | string[] | undefined;
|
|
@@ -138,6 +140,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
138
140
|
'runtime-log-level'?: number | undefined;
|
|
139
141
|
'offchain-worker'?: boolean | undefined;
|
|
140
142
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
143
|
+
'process-queued-messages'?: boolean | undefined;
|
|
141
144
|
}>;
|
|
142
145
|
export type Config = z.infer<typeof configSchema>;
|
|
143
146
|
export declare const getYargsOptions: (zodShape: ZodRawShape) => {
|
package/dist/cjs/schema/index.js
CHANGED
|
@@ -91,6 +91,9 @@ const configSchema = _zod.z.object({
|
|
|
91
91
|
_zod.z.boolean()
|
|
92
92
|
], {
|
|
93
93
|
description: 'Resume from the specified block hash or block number in db. If true, it will resume from the latest block in db. Note this will override the block option'
|
|
94
|
+
}).optional(),
|
|
95
|
+
'process-queued-messages': _zod.z.boolean({
|
|
96
|
+
description: 'Produce extra block when queued messages are detected. Default to true. Set to false to disable it.'
|
|
94
97
|
}).optional()
|
|
95
98
|
});
|
|
96
99
|
const getZodType = (option)=>{
|
package/dist/cjs/server.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type Handler = (data: {
|
|
|
3
3
|
method: string;
|
|
4
4
|
params: string[];
|
|
5
5
|
}, subscriptionManager: SubscriptionManager) => Promise<any>;
|
|
6
|
-
export declare const createServer: (handler: Handler, port
|
|
6
|
+
export declare const createServer: (handler: Handler, port: number) => Promise<{
|
|
7
7
|
port: number;
|
|
8
8
|
close: () => Promise<void>;
|
|
9
9
|
}>;
|
package/dist/cjs/server.js
CHANGED
|
@@ -11,8 +11,17 @@ Object.defineProperty(exports, "createServer", {
|
|
|
11
11
|
const _ws = require("ws");
|
|
12
12
|
const _chopstickscore = require("@acala-network/chopsticks-core");
|
|
13
13
|
const _zod = require("zod");
|
|
14
|
+
const _nodehttp = /*#__PURE__*/ _interop_require_default(require("node:http"));
|
|
14
15
|
const _logger = require("./logger.js");
|
|
15
|
-
|
|
16
|
+
function _interop_require_default(obj) {
|
|
17
|
+
return obj && obj.__esModule ? obj : {
|
|
18
|
+
default: obj
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const httpLogger = _logger.defaultLogger.child({
|
|
22
|
+
name: 'http'
|
|
23
|
+
});
|
|
24
|
+
const wsLogger = _logger.defaultLogger.child({
|
|
16
25
|
name: 'ws'
|
|
17
26
|
});
|
|
18
27
|
const singleRequest = _zod.z.object({
|
|
@@ -33,34 +42,103 @@ const parseRequest = (request)=>{
|
|
|
33
42
|
return undefined;
|
|
34
43
|
}
|
|
35
44
|
};
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
wss.on('listening', ()=>{
|
|
43
|
-
resolve([
|
|
44
|
-
wss,
|
|
45
|
-
wss.address().port
|
|
46
|
-
]);
|
|
47
|
-
});
|
|
48
|
-
wss.on('error', (_)=>{
|
|
49
|
-
resolve([]);
|
|
45
|
+
const readBody = (request)=>new Promise((resolve)=>{
|
|
46
|
+
const bodyParts = [];
|
|
47
|
+
request.on('data', (chunk)=>{
|
|
48
|
+
bodyParts.push(chunk);
|
|
49
|
+
}).on('end', ()=>{
|
|
50
|
+
resolve(Buffer.concat(bodyParts).toString());
|
|
50
51
|
});
|
|
51
52
|
});
|
|
52
|
-
|
|
53
|
+
const respond = (res, data)=>{
|
|
54
|
+
res.writeHead(200, {
|
|
55
|
+
'Access-Control-Allow-Origin': '*',
|
|
56
|
+
'Access-Control-Allow-Methods': 'POST',
|
|
57
|
+
'Access-Control-Allow-Headers': '*',
|
|
58
|
+
'Content-Type': 'application/json'
|
|
59
|
+
});
|
|
60
|
+
if (data) {
|
|
61
|
+
res.write(data);
|
|
62
|
+
}
|
|
63
|
+
res.end();
|
|
53
64
|
};
|
|
54
65
|
const createServer = async (handler, port)=>{
|
|
55
66
|
let wss;
|
|
56
67
|
let listenPort;
|
|
68
|
+
const emptySubscriptionManager = {
|
|
69
|
+
subscribe: ()=>{
|
|
70
|
+
throw new Error('Subscription is not supported');
|
|
71
|
+
},
|
|
72
|
+
unsubscribe: ()=>{
|
|
73
|
+
throw new Error('Subscription is not supported');
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const server = _nodehttp.default.createServer(async (req, res)=>{
|
|
77
|
+
if (req.method === 'OPTIONS') {
|
|
78
|
+
return respond(res);
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
if (req.method !== 'POST') {
|
|
82
|
+
throw new Error('Only POST method is supported');
|
|
83
|
+
}
|
|
84
|
+
const body = await readBody(req);
|
|
85
|
+
const parsed = await requestSchema.safeParseAsync(parseRequest(body));
|
|
86
|
+
if (!parsed.success) {
|
|
87
|
+
httpLogger.error('Invalid request: %s', body);
|
|
88
|
+
throw new Error('Invalid request: ' + body);
|
|
89
|
+
}
|
|
90
|
+
httpLogger.trace({
|
|
91
|
+
req: parsed.data
|
|
92
|
+
}, 'Received request');
|
|
93
|
+
let response;
|
|
94
|
+
if (Array.isArray(parsed.data)) {
|
|
95
|
+
response = await Promise.all(parsed.data.map((req)=>{
|
|
96
|
+
const result = handler(req, emptySubscriptionManager);
|
|
97
|
+
return {
|
|
98
|
+
id: req.id,
|
|
99
|
+
jsonrpc: '2.0',
|
|
100
|
+
result
|
|
101
|
+
};
|
|
102
|
+
}));
|
|
103
|
+
} else {
|
|
104
|
+
const result = await handler(parsed.data, emptySubscriptionManager);
|
|
105
|
+
response = {
|
|
106
|
+
id: parsed.data.id,
|
|
107
|
+
jsonrpc: '2.0',
|
|
108
|
+
result
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
respond(res, JSON.stringify(response));
|
|
112
|
+
} catch (err) {
|
|
113
|
+
respond(res, JSON.stringify({
|
|
114
|
+
jsonrpc: '2.0',
|
|
115
|
+
id: 1,
|
|
116
|
+
error: {
|
|
117
|
+
message: err.message
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
57
122
|
for(let i = 0; i < 10; i++){
|
|
58
|
-
const preferPort =
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
123
|
+
const preferPort = port ? port + i : undefined;
|
|
124
|
+
wsLogger.debug('Try starting on port %d', preferPort);
|
|
125
|
+
const success = await new Promise((resolve)=>{
|
|
126
|
+
server.once('error', (e)=>{
|
|
127
|
+
if (e.code === 'EADDRINUSE') {
|
|
128
|
+
server.close();
|
|
129
|
+
resolve(false);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
server.listen(preferPort, ()=>{
|
|
133
|
+
wss = new _ws.WebSocketServer({
|
|
134
|
+
server,
|
|
135
|
+
maxPayload: 1024 * 1024 * 100
|
|
136
|
+
});
|
|
137
|
+
listenPort = server.address().port;
|
|
138
|
+
resolve(true);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
if (success) {
|
|
64
142
|
break;
|
|
65
143
|
}
|
|
66
144
|
}
|
|
@@ -68,7 +146,7 @@ const createServer = async (handler, port)=>{
|
|
|
68
146
|
throw new Error(`Failed to create WebsocketServer at port ${port}`);
|
|
69
147
|
}
|
|
70
148
|
wss.on('connection', (ws)=>{
|
|
71
|
-
|
|
149
|
+
wsLogger.debug('New connection');
|
|
72
150
|
const send = (data)=>{
|
|
73
151
|
if (ws.readyState === _ws.WebSocket.OPEN) {
|
|
74
152
|
ws.send(JSON.stringify(data));
|
|
@@ -80,7 +158,7 @@ const createServer = async (handler, port)=>{
|
|
|
80
158
|
subscriptions[subid] = onCancel;
|
|
81
159
|
return (data)=>{
|
|
82
160
|
if (subscriptions[subid]) {
|
|
83
|
-
|
|
161
|
+
wsLogger.trace({
|
|
84
162
|
method,
|
|
85
163
|
subid,
|
|
86
164
|
data: (0, _logger.truncate)(data)
|
|
@@ -104,13 +182,13 @@ const createServer = async (handler, port)=>{
|
|
|
104
182
|
}
|
|
105
183
|
};
|
|
106
184
|
const processRequest = async (req)=>{
|
|
107
|
-
|
|
185
|
+
wsLogger.trace({
|
|
108
186
|
id: req.id,
|
|
109
187
|
method: req.method
|
|
110
188
|
}, 'Received message');
|
|
111
189
|
try {
|
|
112
190
|
const resp = await handler(req, subscriptionManager);
|
|
113
|
-
|
|
191
|
+
wsLogger.trace({
|
|
114
192
|
id: req.id,
|
|
115
193
|
method: req.method,
|
|
116
194
|
result: (0, _logger.truncate)(resp)
|
|
@@ -121,7 +199,7 @@ const createServer = async (handler, port)=>{
|
|
|
121
199
|
result: resp ?? null
|
|
122
200
|
};
|
|
123
201
|
} catch (e) {
|
|
124
|
-
|
|
202
|
+
wsLogger.info('Error handling request: %o', e.stack);
|
|
125
203
|
return {
|
|
126
204
|
id: req.id,
|
|
127
205
|
jsonrpc: '2.0',
|
|
@@ -133,14 +211,14 @@ const createServer = async (handler, port)=>{
|
|
|
133
211
|
}
|
|
134
212
|
};
|
|
135
213
|
ws.on('close', ()=>{
|
|
136
|
-
|
|
214
|
+
wsLogger.debug('Connection closed');
|
|
137
215
|
for (const [subid, onCancel] of Object.entries(subscriptions)){
|
|
138
216
|
onCancel(subid);
|
|
139
217
|
}
|
|
140
218
|
ws.removeAllListeners();
|
|
141
219
|
});
|
|
142
220
|
ws.on('error', ()=>{
|
|
143
|
-
|
|
221
|
+
wsLogger.debug('Connection error');
|
|
144
222
|
for (const [subid, onCancel] of Object.entries(subscriptions)){
|
|
145
223
|
onCancel(subid);
|
|
146
224
|
}
|
|
@@ -149,7 +227,7 @@ const createServer = async (handler, port)=>{
|
|
|
149
227
|
ws.on('message', async (message)=>{
|
|
150
228
|
const parsed = await requestSchema.safeParseAsync(parseRequest(message.toString()));
|
|
151
229
|
if (!parsed.success) {
|
|
152
|
-
|
|
230
|
+
wsLogger.error('Invalid request: %s', message);
|
|
153
231
|
send({
|
|
154
232
|
id: null,
|
|
155
233
|
jsonrpc: '2.0',
|
|
@@ -162,13 +240,13 @@ const createServer = async (handler, port)=>{
|
|
|
162
240
|
}
|
|
163
241
|
const { data: req } = parsed;
|
|
164
242
|
if (Array.isArray(req)) {
|
|
165
|
-
|
|
243
|
+
wsLogger.trace({
|
|
166
244
|
req
|
|
167
245
|
}, 'Received batch request');
|
|
168
246
|
const resp = await Promise.all(req.map(processRequest));
|
|
169
247
|
send(resp);
|
|
170
248
|
} else {
|
|
171
|
-
|
|
249
|
+
wsLogger.trace({
|
|
172
250
|
req
|
|
173
251
|
}, 'Received single request');
|
|
174
252
|
const resp = await processRequest(req);
|
|
@@ -178,15 +256,10 @@ const createServer = async (handler, port)=>{
|
|
|
178
256
|
});
|
|
179
257
|
return {
|
|
180
258
|
port: listenPort,
|
|
181
|
-
close:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
} else {
|
|
187
|
-
resolve();
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
})
|
|
259
|
+
close: async ()=>{
|
|
260
|
+
server.close();
|
|
261
|
+
server.closeAllConnections();
|
|
262
|
+
server.unref();
|
|
263
|
+
}
|
|
191
264
|
};
|
|
192
265
|
};
|
package/dist/esm/context.js
CHANGED
|
@@ -40,7 +40,8 @@ export const setupContext = async (argv, overrideParent = false)=>{
|
|
|
40
40
|
runtimeLogLevel: argv['runtime-log-level'],
|
|
41
41
|
registeredTypes: argv['registered-types'],
|
|
42
42
|
offchainWorker: argv['offchain-worker'],
|
|
43
|
-
maxMemoryBlockCount: argv['max-memory-block-count']
|
|
43
|
+
maxMemoryBlockCount: argv['max-memory-block-count'],
|
|
44
|
+
processQueuedMessages: argv['process-queued-messages']
|
|
44
45
|
});
|
|
45
46
|
// load block from db
|
|
46
47
|
if (chain.db) {
|
|
@@ -82,6 +82,7 @@ export declare const dryRunSchema: z.ZodObject<{
|
|
|
82
82
|
'runtime-log-level': z.ZodOptional<z.ZodNumber>;
|
|
83
83
|
'offchain-worker': z.ZodOptional<z.ZodBoolean>;
|
|
84
84
|
resume: z.ZodOptional<z.ZodUnion<[z.ZodIntersection<z.ZodString, z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>>, z.ZodNumber, z.ZodBoolean]>>;
|
|
85
|
+
'process-queued-messages': z.ZodOptional<z.ZodBoolean>;
|
|
85
86
|
}, "strip", z.ZodTypeAny, {
|
|
86
87
|
port: number;
|
|
87
88
|
'build-block-mode': import("@acala-network/chopsticks-core").BuildBlockMode;
|
|
@@ -119,6 +120,7 @@ export declare const dryRunSchema: z.ZodObject<{
|
|
|
119
120
|
'runtime-log-level'?: number | undefined;
|
|
120
121
|
'offchain-worker'?: boolean | undefined;
|
|
121
122
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
123
|
+
'process-queued-messages'?: boolean | undefined;
|
|
122
124
|
}, {
|
|
123
125
|
extrinsic?: string | undefined;
|
|
124
126
|
address?: string | undefined;
|
|
@@ -156,6 +158,7 @@ export declare const dryRunSchema: z.ZodObject<{
|
|
|
156
158
|
'runtime-log-level'?: number | undefined;
|
|
157
159
|
'offchain-worker'?: boolean | undefined;
|
|
158
160
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
161
|
+
'process-queued-messages'?: boolean | undefined;
|
|
159
162
|
}>;
|
|
160
163
|
export type DryRunSchemaType = z.infer<typeof dryRunSchema>;
|
|
161
164
|
export * from './cli.js';
|
|
@@ -78,6 +78,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
78
78
|
'runtime-log-level': z.ZodOptional<z.ZodNumber>;
|
|
79
79
|
'offchain-worker': z.ZodOptional<z.ZodBoolean>;
|
|
80
80
|
resume: z.ZodOptional<z.ZodUnion<[z.ZodIntersection<z.ZodString, z.ZodType<`0x${string}`, z.ZodTypeDef, `0x${string}`>>, z.ZodNumber, z.ZodBoolean]>>;
|
|
81
|
+
'process-queued-messages': z.ZodOptional<z.ZodBoolean>;
|
|
81
82
|
}, "strip", ZodTypeAny, {
|
|
82
83
|
port: number;
|
|
83
84
|
'build-block-mode': BuildBlockMode;
|
|
@@ -108,6 +109,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
108
109
|
'runtime-log-level'?: number | undefined;
|
|
109
110
|
'offchain-worker'?: boolean | undefined;
|
|
110
111
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
112
|
+
'process-queued-messages'?: boolean | undefined;
|
|
111
113
|
}, {
|
|
112
114
|
port?: number | undefined;
|
|
113
115
|
endpoint?: string | string[] | undefined;
|
|
@@ -138,6 +140,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
138
140
|
'runtime-log-level'?: number | undefined;
|
|
139
141
|
'offchain-worker'?: boolean | undefined;
|
|
140
142
|
resume?: number | boolean | `0x${string}` | undefined;
|
|
143
|
+
'process-queued-messages'?: boolean | undefined;
|
|
141
144
|
}>;
|
|
142
145
|
export type Config = z.infer<typeof configSchema>;
|
|
143
146
|
export declare const getYargsOptions: (zodShape: ZodRawShape) => {
|
package/dist/esm/schema/index.js
CHANGED
|
@@ -59,6 +59,9 @@ export const configSchema = z.object({
|
|
|
59
59
|
z.boolean()
|
|
60
60
|
], {
|
|
61
61
|
description: 'Resume from the specified block hash or block number in db. If true, it will resume from the latest block in db. Note this will override the block option'
|
|
62
|
+
}).optional(),
|
|
63
|
+
'process-queued-messages': z.boolean({
|
|
64
|
+
description: 'Produce extra block when queued messages are detected. Default to true. Set to false to disable it.'
|
|
62
65
|
}).optional()
|
|
63
66
|
});
|
|
64
67
|
const getZodType = (option)=>{
|
package/dist/esm/server.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type Handler = (data: {
|
|
|
3
3
|
method: string;
|
|
4
4
|
params: string[];
|
|
5
5
|
}, subscriptionManager: SubscriptionManager) => Promise<any>;
|
|
6
|
-
export declare const createServer: (handler: Handler, port
|
|
6
|
+
export declare const createServer: (handler: Handler, port: number) => Promise<{
|
|
7
7
|
port: number;
|
|
8
8
|
close: () => Promise<void>;
|
|
9
9
|
}>;
|
package/dist/esm/server.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
2
2
|
import { ResponseError } from '@acala-network/chopsticks-core';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
+
import http from 'node:http';
|
|
4
5
|
import { defaultLogger, truncate } from './logger.js';
|
|
5
|
-
const
|
|
6
|
+
const httpLogger = defaultLogger.child({
|
|
7
|
+
name: 'http'
|
|
8
|
+
});
|
|
9
|
+
const wsLogger = defaultLogger.child({
|
|
6
10
|
name: 'ws'
|
|
7
11
|
});
|
|
8
12
|
const singleRequest = z.object({
|
|
@@ -23,34 +27,103 @@ const parseRequest = (request)=>{
|
|
|
23
27
|
return undefined;
|
|
24
28
|
}
|
|
25
29
|
};
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
wss.on('listening', ()=>{
|
|
33
|
-
resolve([
|
|
34
|
-
wss,
|
|
35
|
-
wss.address().port
|
|
36
|
-
]);
|
|
37
|
-
});
|
|
38
|
-
wss.on('error', (_)=>{
|
|
39
|
-
resolve([]);
|
|
30
|
+
const readBody = (request)=>new Promise((resolve)=>{
|
|
31
|
+
const bodyParts = [];
|
|
32
|
+
request.on('data', (chunk)=>{
|
|
33
|
+
bodyParts.push(chunk);
|
|
34
|
+
}).on('end', ()=>{
|
|
35
|
+
resolve(Buffer.concat(bodyParts).toString());
|
|
40
36
|
});
|
|
41
37
|
});
|
|
42
|
-
|
|
38
|
+
const respond = (res, data)=>{
|
|
39
|
+
res.writeHead(200, {
|
|
40
|
+
'Access-Control-Allow-Origin': '*',
|
|
41
|
+
'Access-Control-Allow-Methods': 'POST',
|
|
42
|
+
'Access-Control-Allow-Headers': '*',
|
|
43
|
+
'Content-Type': 'application/json'
|
|
44
|
+
});
|
|
45
|
+
if (data) {
|
|
46
|
+
res.write(data);
|
|
47
|
+
}
|
|
48
|
+
res.end();
|
|
43
49
|
};
|
|
44
50
|
export const createServer = async (handler, port)=>{
|
|
45
51
|
let wss;
|
|
46
52
|
let listenPort;
|
|
53
|
+
const emptySubscriptionManager = {
|
|
54
|
+
subscribe: ()=>{
|
|
55
|
+
throw new Error('Subscription is not supported');
|
|
56
|
+
},
|
|
57
|
+
unsubscribe: ()=>{
|
|
58
|
+
throw new Error('Subscription is not supported');
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const server = http.createServer(async (req, res)=>{
|
|
62
|
+
if (req.method === 'OPTIONS') {
|
|
63
|
+
return respond(res);
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
if (req.method !== 'POST') {
|
|
67
|
+
throw new Error('Only POST method is supported');
|
|
68
|
+
}
|
|
69
|
+
const body = await readBody(req);
|
|
70
|
+
const parsed = await requestSchema.safeParseAsync(parseRequest(body));
|
|
71
|
+
if (!parsed.success) {
|
|
72
|
+
httpLogger.error('Invalid request: %s', body);
|
|
73
|
+
throw new Error('Invalid request: ' + body);
|
|
74
|
+
}
|
|
75
|
+
httpLogger.trace({
|
|
76
|
+
req: parsed.data
|
|
77
|
+
}, 'Received request');
|
|
78
|
+
let response;
|
|
79
|
+
if (Array.isArray(parsed.data)) {
|
|
80
|
+
response = await Promise.all(parsed.data.map((req)=>{
|
|
81
|
+
const result = handler(req, emptySubscriptionManager);
|
|
82
|
+
return {
|
|
83
|
+
id: req.id,
|
|
84
|
+
jsonrpc: '2.0',
|
|
85
|
+
result
|
|
86
|
+
};
|
|
87
|
+
}));
|
|
88
|
+
} else {
|
|
89
|
+
const result = await handler(parsed.data, emptySubscriptionManager);
|
|
90
|
+
response = {
|
|
91
|
+
id: parsed.data.id,
|
|
92
|
+
jsonrpc: '2.0',
|
|
93
|
+
result
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
respond(res, JSON.stringify(response));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
respond(res, JSON.stringify({
|
|
99
|
+
jsonrpc: '2.0',
|
|
100
|
+
id: 1,
|
|
101
|
+
error: {
|
|
102
|
+
message: err.message
|
|
103
|
+
}
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
47
107
|
for(let i = 0; i < 10; i++){
|
|
48
|
-
const preferPort =
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
108
|
+
const preferPort = port ? port + i : undefined;
|
|
109
|
+
wsLogger.debug('Try starting on port %d', preferPort);
|
|
110
|
+
const success = await new Promise((resolve)=>{
|
|
111
|
+
server.once('error', (e)=>{
|
|
112
|
+
if (e.code === 'EADDRINUSE') {
|
|
113
|
+
server.close();
|
|
114
|
+
resolve(false);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
server.listen(preferPort, ()=>{
|
|
118
|
+
wss = new WebSocketServer({
|
|
119
|
+
server,
|
|
120
|
+
maxPayload: 1024 * 1024 * 100
|
|
121
|
+
});
|
|
122
|
+
listenPort = server.address().port;
|
|
123
|
+
resolve(true);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
if (success) {
|
|
54
127
|
break;
|
|
55
128
|
}
|
|
56
129
|
}
|
|
@@ -58,7 +131,7 @@ export const createServer = async (handler, port)=>{
|
|
|
58
131
|
throw new Error(`Failed to create WebsocketServer at port ${port}`);
|
|
59
132
|
}
|
|
60
133
|
wss.on('connection', (ws)=>{
|
|
61
|
-
|
|
134
|
+
wsLogger.debug('New connection');
|
|
62
135
|
const send = (data)=>{
|
|
63
136
|
if (ws.readyState === WebSocket.OPEN) {
|
|
64
137
|
ws.send(JSON.stringify(data));
|
|
@@ -70,7 +143,7 @@ export const createServer = async (handler, port)=>{
|
|
|
70
143
|
subscriptions[subid] = onCancel;
|
|
71
144
|
return (data)=>{
|
|
72
145
|
if (subscriptions[subid]) {
|
|
73
|
-
|
|
146
|
+
wsLogger.trace({
|
|
74
147
|
method,
|
|
75
148
|
subid,
|
|
76
149
|
data: truncate(data)
|
|
@@ -94,13 +167,13 @@ export const createServer = async (handler, port)=>{
|
|
|
94
167
|
}
|
|
95
168
|
};
|
|
96
169
|
const processRequest = async (req)=>{
|
|
97
|
-
|
|
170
|
+
wsLogger.trace({
|
|
98
171
|
id: req.id,
|
|
99
172
|
method: req.method
|
|
100
173
|
}, 'Received message');
|
|
101
174
|
try {
|
|
102
175
|
const resp = await handler(req, subscriptionManager);
|
|
103
|
-
|
|
176
|
+
wsLogger.trace({
|
|
104
177
|
id: req.id,
|
|
105
178
|
method: req.method,
|
|
106
179
|
result: truncate(resp)
|
|
@@ -111,7 +184,7 @@ export const createServer = async (handler, port)=>{
|
|
|
111
184
|
result: resp ?? null
|
|
112
185
|
};
|
|
113
186
|
} catch (e) {
|
|
114
|
-
|
|
187
|
+
wsLogger.info('Error handling request: %o', e.stack);
|
|
115
188
|
return {
|
|
116
189
|
id: req.id,
|
|
117
190
|
jsonrpc: '2.0',
|
|
@@ -123,14 +196,14 @@ export const createServer = async (handler, port)=>{
|
|
|
123
196
|
}
|
|
124
197
|
};
|
|
125
198
|
ws.on('close', ()=>{
|
|
126
|
-
|
|
199
|
+
wsLogger.debug('Connection closed');
|
|
127
200
|
for (const [subid, onCancel] of Object.entries(subscriptions)){
|
|
128
201
|
onCancel(subid);
|
|
129
202
|
}
|
|
130
203
|
ws.removeAllListeners();
|
|
131
204
|
});
|
|
132
205
|
ws.on('error', ()=>{
|
|
133
|
-
|
|
206
|
+
wsLogger.debug('Connection error');
|
|
134
207
|
for (const [subid, onCancel] of Object.entries(subscriptions)){
|
|
135
208
|
onCancel(subid);
|
|
136
209
|
}
|
|
@@ -139,7 +212,7 @@ export const createServer = async (handler, port)=>{
|
|
|
139
212
|
ws.on('message', async (message)=>{
|
|
140
213
|
const parsed = await requestSchema.safeParseAsync(parseRequest(message.toString()));
|
|
141
214
|
if (!parsed.success) {
|
|
142
|
-
|
|
215
|
+
wsLogger.error('Invalid request: %s', message);
|
|
143
216
|
send({
|
|
144
217
|
id: null,
|
|
145
218
|
jsonrpc: '2.0',
|
|
@@ -152,13 +225,13 @@ export const createServer = async (handler, port)=>{
|
|
|
152
225
|
}
|
|
153
226
|
const { data: req } = parsed;
|
|
154
227
|
if (Array.isArray(req)) {
|
|
155
|
-
|
|
228
|
+
wsLogger.trace({
|
|
156
229
|
req
|
|
157
230
|
}, 'Received batch request');
|
|
158
231
|
const resp = await Promise.all(req.map(processRequest));
|
|
159
232
|
send(resp);
|
|
160
233
|
} else {
|
|
161
|
-
|
|
234
|
+
wsLogger.trace({
|
|
162
235
|
req
|
|
163
236
|
}, 'Received single request');
|
|
164
237
|
const resp = await processRequest(req);
|
|
@@ -168,15 +241,10 @@ export const createServer = async (handler, port)=>{
|
|
|
168
241
|
});
|
|
169
242
|
return {
|
|
170
243
|
port: listenPort,
|
|
171
|
-
close:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
} else {
|
|
177
|
-
resolve();
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
})
|
|
244
|
+
close: async ()=>{
|
|
245
|
+
server.close();
|
|
246
|
+
server.closeAllConnections();
|
|
247
|
+
server.unref();
|
|
248
|
+
}
|
|
181
249
|
};
|
|
182
250
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acala-network/chopsticks",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.11-1",
|
|
4
4
|
"author": "Acala Developers <hello@acala.network>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": "./chopsticks.cjs",
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
"docs:prep": "typedoc"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@acala-network/chopsticks-core": "0.9.
|
|
17
|
-
"@acala-network/chopsticks-db": "0.9.
|
|
16
|
+
"@acala-network/chopsticks-core": "0.9.11-1",
|
|
17
|
+
"@acala-network/chopsticks-db": "0.9.11-1",
|
|
18
18
|
"@pnpm/npm-conf": "^2.2.2",
|
|
19
19
|
"@polkadot/api-augment": "^10.11.2",
|
|
20
20
|
"@polkadot/types": "^10.11.2",
|
|
21
21
|
"@polkadot/util": "^12.6.2",
|
|
22
22
|
"@polkadot/util-crypto": "^12.6.2",
|
|
23
|
-
"axios": "^1.6.
|
|
24
|
-
"dotenv": "^16.4.
|
|
23
|
+
"axios": "^1.6.8",
|
|
24
|
+
"dotenv": "^16.4.5",
|
|
25
25
|
"global-agent": "^3.0.0",
|
|
26
26
|
"js-yaml": "^4.1.0",
|
|
27
27
|
"jsondiffpatch": "^0.5.0",
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@swc/cli": "0.1.65",
|
|
35
|
-
"@swc/core": "^1.
|
|
35
|
+
"@swc/core": "^1.4.8",
|
|
36
36
|
"@types/global-agent": "^2.1.3",
|
|
37
37
|
"@types/js-yaml": "^4.0.9",
|
|
38
|
-
"@types/lodash": "^4.
|
|
38
|
+
"@types/lodash": "^4.17.0",
|
|
39
39
|
"@types/node": "^20.11.5",
|
|
40
40
|
"@types/ws": "^8.5.10",
|
|
41
41
|
"@types/yargs": "^17.0.32",
|
|
42
42
|
"typescript": "^5.3.3",
|
|
43
|
-
"vitest": "^1.
|
|
43
|
+
"vitest": "^1.4.0"
|
|
44
44
|
},
|
|
45
45
|
"files": [
|
|
46
46
|
"dist/esm/**",
|