prool 0.0.3 → 0.0.4

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/pool.ts CHANGED
@@ -13,6 +13,7 @@ export type Pool<key = number> = Pick<
13
13
  }
14
14
  destroy(key: key): Promise<void>
15
15
  destroyAll(): Promise<void>
16
+ restart(key: key): Promise<void>
16
17
  start(key: key, options?: { port?: number }): Promise<Instance_>
17
18
  stop(key: key): Promise<void>
18
19
  stopAll(): Promise<void>
@@ -56,6 +57,7 @@ export function definePool<key = number>(
56
57
  const promises = {
57
58
  destroy: new Map<key, Promise<void>>(),
58
59
  destroyAll: undefined as Promise<void> | undefined,
60
+ restart: new Map<key, Promise<void>>(),
59
61
  start: new Map<key, Promise<Instance_>>(),
60
62
  stop: new Map<key, Promise<void>>(),
61
63
  stopAll: undefined as Promise<void> | undefined,
@@ -71,16 +73,14 @@ export function definePool<key = number>(
71
73
 
72
74
  const resolver = Promise.withResolvers<void>()
73
75
 
74
- try {
75
- promises.destroy.set(key, resolver.promise)
76
+ promises.destroy.set(key, resolver.promise)
76
77
 
77
- await this.stop(key)
78
- instances.delete(key)
79
-
80
- resolver.resolve()
81
- } catch (error) {
82
- resolver.reject(error)
83
- }
78
+ this.stop(key)
79
+ .then(() => {
80
+ instances.delete(key)
81
+ resolver.resolve()
82
+ })
83
+ .catch(resolver.reject)
84
84
 
85
85
  return resolver.promise
86
86
  },
@@ -89,17 +89,35 @@ export function definePool<key = number>(
89
89
 
90
90
  const resolver = Promise.withResolvers<void>()
91
91
 
92
- try {
93
- promises.destroyAll = resolver.promise
92
+ promises.destroyAll = resolver.promise
94
93
 
95
- await Promise.all([...instances.keys()].map((key) => this.destroy(key)))
94
+ Promise.all([...instances.keys()].map((key) => this.destroy(key)))
95
+ .then(() => {
96
+ promises.destroyAll = undefined
97
+ resolver.resolve()
98
+ })
99
+ .catch(resolver.reject)
96
100
 
97
- promises.destroyAll = undefined
101
+ return resolver.promise
102
+ },
103
+ async restart(key) {
104
+ const restartPromise = promises.restart.get(key)
105
+ if (restartPromise) return restartPromise
98
106
 
99
- resolver.resolve()
100
- } catch (error) {
101
- resolver.reject(error)
102
- }
107
+ const resolver = Promise.withResolvers<void>()
108
+
109
+ const instance_ = instances.get(key)
110
+ if (!instance_) return
111
+
112
+ promises.restart.set(key, resolver.promise)
113
+
114
+ instance_
115
+ .restart()
116
+ .then(resolver.resolve)
117
+ .catch(resolver.reject)
118
+ .finally(() => promises.restart.delete(key))
119
+
120
+ return resolver.promise
103
121
  },
104
122
  async start(key, options = {}) {
105
123
  const startPromise = promises.start.get(key)
@@ -107,24 +125,21 @@ export function definePool<key = number>(
107
125
 
108
126
  const resolver = Promise.withResolvers<Instance_>()
109
127
 
110
- try {
111
- promises.start.set(key, resolver.promise)
128
+ if (limit && instances.size >= limit)
129
+ throw new Error(`Instance limit of ${limit} reached.`)
112
130
 
113
- if (limit && instances.size >= limit)
114
- throw new Error(`Instance limit of ${limit} reached.`)
131
+ promises.start.set(key, resolver.promise)
115
132
 
116
- const { port = await getPort() } = options
117
-
118
- const instance_ = instances.get(key) || instance.create({ port })
119
- await instance_.start()
120
-
121
- instances.set(key, instance_)
122
- resolver.resolve(instance_)
123
- } catch (error) {
124
- resolver.reject(error)
125
- } finally {
126
- promises.start.delete(key)
127
- }
133
+ const { port = await getPort() } = options
134
+ const instance_ = instances.get(key) || instance.create({ port })
135
+ instance_
136
+ .start()
137
+ .then(() => {
138
+ instances.set(key, instance_)
139
+ resolver.resolve(instance_)
140
+ })
141
+ .catch(resolver.reject)
142
+ .finally(() => promises.start.delete(key))
128
143
 
129
144
  return resolver.promise
130
145
  },
@@ -132,42 +147,35 @@ export function definePool<key = number>(
132
147
  const stopPromise = promises.stop.get(key)
133
148
  if (stopPromise) return stopPromise
134
149
 
135
- const resolver = Promise.withResolvers<void>()
150
+ const instance_ = instances.get(key)
151
+ if (!instance_) return
136
152
 
137
- try {
138
- promises.stop.set(key, resolver.promise)
139
-
140
- const instance_ = instances.get(key)
141
- if (!instance_) {
142
- resolver.resolve()
143
- return
144
- }
153
+ const resolver = Promise.withResolvers<void>()
145
154
 
146
- await instance_.stop()
155
+ promises.stop.set(key, resolver.promise)
156
+ instance_
157
+ .stop()
158
+ .then(resolver.resolve)
159
+ .catch(resolver.reject)
160
+ .finally(() => promises.stop.delete(key))
147
161
 
148
- resolver.resolve()
149
- } catch (error) {
150
- resolver.reject(error)
151
- } finally {
152
- promises.stop.delete(key)
153
- }
162
+ return resolver.promise
154
163
  },
155
164
  async stopAll() {
156
165
  if (promises.stopAll) return promises.stopAll
157
166
 
158
167
  const resolver = Promise.withResolvers<void>()
159
168
 
160
- try {
161
- promises.stopAll = resolver.promise
162
-
163
- await Promise.all([...instances.keys()].map((key) => this.stop(key)))
169
+ promises.stopAll = resolver.promise
164
170
 
165
- promises.stopAll = undefined
171
+ Promise.all([...instances.keys()].map((key) => this.stop(key)))
172
+ .then(() => {
173
+ promises.stopAll = undefined
174
+ resolver.resolve()
175
+ })
176
+ .catch(resolver.reject)
166
177
 
167
- resolver.resolve()
168
- } catch (error) {
169
- resolver.reject(error)
170
- }
178
+ return resolver.promise
171
179
  },
172
180
 
173
181
  get size() {
package/server.test.ts CHANGED
@@ -7,49 +7,49 @@ describe.each([{ instance: anvil() }])(
7
7
  'instance: $instance.name',
8
8
  ({ instance }) => {
9
9
  test('default', async () => {
10
- const pool = createServer({
10
+ const server = createServer({
11
11
  instance,
12
12
  })
13
- expect(pool).toBeDefined()
13
+ expect(server).toBeDefined()
14
14
 
15
- await pool.start()
16
- expect(pool.address()).toBeDefined()
15
+ await server.start()
16
+ expect(server.address()).toBeDefined()
17
17
 
18
18
  // Stop via instance method.
19
- await pool.stop()
20
- expect(pool.address()).toBeNull()
19
+ await server.stop()
20
+ expect(server.address()).toBeNull()
21
21
 
22
- const stop = await pool.start()
23
- expect(pool.address()).toBeDefined()
22
+ const stop = await server.start()
23
+ expect(server.address()).toBeDefined()
24
24
 
25
25
  // Stop via return value.
26
26
  await stop()
27
- expect(pool.address()).toBeNull()
27
+ expect(server.address()).toBeNull()
28
28
  })
29
29
 
30
30
  test('args: port', async () => {
31
- const pool = createServer({
31
+ const server = createServer({
32
32
  instance,
33
33
  port: 3000,
34
34
  })
35
- expect(pool).toBeDefined()
35
+ expect(server).toBeDefined()
36
36
 
37
- const stop = await pool.start()
38
- expect(pool.address()?.port).toBe(3000)
37
+ const stop = await server.start()
38
+ expect(server.address()?.port).toBe(3000)
39
39
  await stop()
40
40
  })
41
41
 
42
42
  test('args: host', async () => {
43
- const pool = createServer({
43
+ const server = createServer({
44
44
  instance,
45
45
  host: 'localhost',
46
46
  port: 3000,
47
47
  })
48
- expect(pool).toBeDefined()
48
+ expect(server).toBeDefined()
49
49
 
50
- const stop = await pool.start()
51
- expect(pool.address()?.address).toBe('::1')
52
- expect(pool.address()?.port).toBe(3000)
50
+ const stop = await server.start()
51
+ expect(server.address()?.address).toBe('::1')
52
+ expect(server.address()?.port).toBe(3000)
53
53
  await stop()
54
54
  })
55
55
 
@@ -95,6 +95,19 @@ describe.each([{ instance: anvil() }])(
95
95
  await stop()
96
96
  })
97
97
 
98
+ test('request: /restart', async () => {
99
+ const server = createServer({
100
+ instance,
101
+ })
102
+
103
+ const stop = await server.start()
104
+ const { port } = server.address()!
105
+ const response = await fetch(`http://localhost:${port}/1/restart`)
106
+ expect(response.status).toBe(200)
107
+
108
+ await stop()
109
+ })
110
+
98
111
  test('ws', async () => {
99
112
  const server = createServer({
100
113
  instance,
@@ -224,6 +237,76 @@ describe("instance: 'anvil'", () => {
224
237
  await stop()
225
238
  })
226
239
 
240
+ test('request: /restart', async () => {
241
+ const server = createServer({
242
+ instance: anvil(),
243
+ })
244
+
245
+ const stop = await server.start()
246
+ const { port } = server.address()!
247
+
248
+ // Mine block number
249
+ await fetch(`http://localhost:${port}/1`, {
250
+ body: JSON.stringify({
251
+ method: 'anvil_mine',
252
+ params: ['0x69', '0x0'],
253
+ id: 0,
254
+ jsonrpc: '2.0',
255
+ }),
256
+ headers: {
257
+ 'Content-Type': 'application/json',
258
+ },
259
+ method: 'POST',
260
+ })
261
+
262
+ // Check block numbers
263
+ expect(
264
+ await fetch(`http://localhost:${port}/1`, {
265
+ body: JSON.stringify({
266
+ method: 'eth_blockNumber',
267
+ id: 0,
268
+ jsonrpc: '2.0',
269
+ }),
270
+ headers: {
271
+ 'Content-Type': 'application/json',
272
+ },
273
+ method: 'POST',
274
+ }).then((x) => x.json()),
275
+ ).toMatchInlineSnapshot(`
276
+ {
277
+ "id": 0,
278
+ "jsonrpc": "2.0",
279
+ "result": "0x69",
280
+ }
281
+ `)
282
+
283
+ // Restart
284
+ await fetch(`http://localhost:${port}/1/restart`)
285
+
286
+ // Check block numbers
287
+ expect(
288
+ await fetch(`http://localhost:${port}/1`, {
289
+ body: JSON.stringify({
290
+ method: 'eth_blockNumber',
291
+ id: 0,
292
+ jsonrpc: '2.0',
293
+ }),
294
+ headers: {
295
+ 'Content-Type': 'application/json',
296
+ },
297
+ method: 'POST',
298
+ }).then((x) => x.json()),
299
+ ).toMatchInlineSnapshot(`
300
+ {
301
+ "id": 0,
302
+ "jsonrpc": "2.0",
303
+ "result": "0x0",
304
+ }
305
+ `)
306
+
307
+ await stop()
308
+ })
309
+
227
310
  test('request: /messages', async () => {
228
311
  const server = createServer({
229
312
  instance: anvil(),
package/server.ts CHANGED
@@ -56,44 +56,46 @@ export function createServer(
56
56
 
57
57
  const { id, path } = extractPath(url)
58
58
 
59
- if (typeof id === 'number' && path === '/') {
60
- const { host, port } = pool.get(id) || (await pool.start(id))
61
- return proxy.web(request, response, {
62
- target: `http://${host}:${port}`,
63
- })
64
- }
65
- if (typeof id === 'number' && path === '/start') {
66
- const { host, port } = await pool.start(id)
67
- response
68
- .writeHead(200, { 'Content-Type': 'application/json' })
69
- .end(JSON.stringify({ host, port }))
70
- return
71
- }
72
- if (typeof id === 'number' && path === '/stop') {
73
- await pool.stop(id)
74
- response.writeHead(200, { 'Content-Type': 'application/json' }).end()
75
- return
76
- }
77
- if (typeof id === 'number' && path === '/messages') {
78
- const messages = pool.get(id)?.messages.get() || []
79
- response
80
- .writeHead(200, { 'Content-Type': 'application/json' })
81
- .end(JSON.stringify(messages))
82
- return
59
+ if (typeof id === 'number') {
60
+ if (path === '/') {
61
+ const { host, port } = pool.get(id) || (await pool.start(id))
62
+ return proxy.web(request, response, {
63
+ target: `http://${host}:${port}`,
64
+ })
65
+ }
66
+ if (path === '/start') {
67
+ const { host, port } = await pool.start(id)
68
+ return done(response, 200, { host, port })
69
+ }
70
+ if (path === '/stop') {
71
+ await pool.stop(id)
72
+ return done(response, 200)
73
+ }
74
+ if (path === '/restart') {
75
+ await pool.restart(id)
76
+ return done(response, 200)
77
+ }
78
+ if (path === '/messages') {
79
+ const messages = pool.get(id)?.messages.get() || []
80
+ return done(response, 200, messages)
81
+ }
83
82
  }
84
83
 
85
- if (path === '/healthcheck') {
86
- response.writeHead(200, { 'Content-Type': 'application/json' }).end()
87
- return
88
- }
84
+ if (path === '/healthcheck') return done(response, 200)
89
85
 
90
- response.writeHead(404, { 'Content-Type': 'application/json' }).end()
91
- return
86
+ return done(response, 404)
92
87
  } catch (error) {
93
- response
94
- .writeHead(400, { 'Content-Type': 'application/json' })
95
- .end(JSON.stringify({ message: (error as Error).message }))
96
- return
88
+ return done(response, 400, { message: (error as Error).message })
89
+ }
90
+ })
91
+
92
+ proxy.on('proxyReq', (proxyReq, req) => {
93
+ ;(req as any)._proxyReq = proxyReq
94
+ })
95
+
96
+ proxy.on('error', (err, req) => {
97
+ if (req.socket.destroyed && (err as any).code === 'ECONNRESET') {
98
+ ;(req as any)._proxyReq.abort()
97
99
  }
98
100
  })
99
101
 
@@ -135,3 +137,9 @@ export function createServer(
135
137
  },
136
138
  })
137
139
  }
140
+
141
+ function done(res: ServerResponse, statusCode: number, json?: unknown) {
142
+ return res
143
+ .writeHead(statusCode, { 'Content-Type': 'application/json' })
144
+ .end(json ? JSON.stringify(json) : undefined)
145
+ }