prool 0.0.8 → 0.0.9

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/_lib/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../utils.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,IAAI,GAAG,kBAAkB,CAAA,CAAC,kCAAkC;IAClE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,OAAO,IAAI,GAAG,EAAE,CAAC,CAAA;IAC/C,MAAM,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC,QAAQ;SACzC,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAA;IAEhC,MAAM,EAAE,GACN,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QACrD,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QAClB,CAAC,CAAC,SAAS,CAAA;IACf,MAAM,IAAI,GAAG,IACX,OAAO,EAAE,KAAK,QAAQ;QACpB,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QACpB,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CACtC,EAAE,CAAA;IAEF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAA;AACrB,CAAC;AAED,MAAM,cAAc;AAClB,yEAAyE;AACzE,6EAA6E,CAAA;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;AAC5C,CAAC;AAYD;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,UAA4B;IACjD,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACzD,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,EAAE,CAAA;QAElC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAC7C,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE;gBAC1D,IAAI,QAAQ,KAAK,SAAS;oBAAE,OAAO,EAAE,CAAA;gBACrC,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;gBAC5B,MAAM,KAAK,GAAG,GAAG,MAAM,KAAK,QAAQ,EAAE,CAAA;gBACtC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACtB,CAAC,CAAC,CAAA;QAEJ,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAA;QAE5B,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,EAAE,CAAA;QAC9B,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,CAAA;QAEjC,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAA;QACpC,IAAI,WAAW,KAAK,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,CAAA;QAErC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;IAC5B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,KAAK,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAA;AAC3E,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../utils.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,6 @@ test('exports', () => {
6
6
  [
7
7
  "defineInstance",
8
8
  "definePool",
9
- "execa",
10
9
  "createServer",
11
10
  ]
12
11
  `)
package/exports/index.ts CHANGED
@@ -14,14 +14,6 @@ export {
14
14
  definePool,
15
15
  } from '../pool.js'
16
16
 
17
- export {
18
- type ExecaParameters,
19
- type ExecaProcess,
20
- type ExecaReturnType,
21
- type ExecaStartOptions,
22
- execa,
23
- } from '../processes/execa.js'
24
-
25
17
  export {
26
18
  type CreateServerParameters,
27
19
  type CreateServerReturnType,
@@ -4,6 +4,7 @@ import * as exports from './instances.js'
4
4
  test('exports', () => {
5
5
  expect(Object.keys(exports)).toMatchInlineSnapshot(`
6
6
  [
7
+ "alto",
7
8
  "anvil",
8
9
  "stackup",
9
10
  ]
@@ -1,2 +1,3 @@
1
+ export { type AltoParameters, alto } from '../instances/alto.js'
1
2
  export { type AnvilParameters, anvil } from '../instances/anvil.js'
2
3
  export { type StackupParameters, stackup } from '../instances/stackup.js'
@@ -0,0 +1,124 @@
1
+ import getPort from 'get-port'
2
+ import { afterEach, beforeAll, expect, test } from 'vitest'
3
+
4
+ import { altoOptions } from '../../test/utils.js'
5
+ import type { Instance } from '../instance.js'
6
+ import { type AltoParameters, alto } from './alto.js'
7
+ import { anvil } from './anvil.js'
8
+
9
+ const instances: Instance[] = []
10
+
11
+ const port = await getPort()
12
+
13
+ const defineInstance = (parameters: Partial<AltoParameters> = {}) => {
14
+ const instance = alto({
15
+ ...altoOptions({ port }),
16
+ ...parameters,
17
+ })
18
+ instances.push(instance)
19
+ return instance
20
+ }
21
+
22
+ beforeAll(() => anvil({ port }).start())
23
+
24
+ afterEach(async () => {
25
+ for (const instance of instances) await instance.stop().catch(() => {})
26
+ })
27
+
28
+ test('default', async () => {
29
+ const messages: string[] = []
30
+ const stdouts: string[] = []
31
+
32
+ const instance = defineInstance()
33
+
34
+ instance.on('message', (m) => messages.push(m))
35
+ instance.on('stdout', (m) => stdouts.push(m))
36
+
37
+ expect(instance.messages.get()).toMatchInlineSnapshot('[]')
38
+
39
+ await instance.start()
40
+ expect(instance.status).toEqual('started')
41
+
42
+ expect(messages.join('')).toBeDefined()
43
+ expect(stdouts.join('')).toBeDefined()
44
+ expect(instance.messages.get().join('')).toBeDefined()
45
+
46
+ await instance.stop()
47
+ expect(instance.status).toEqual('stopped')
48
+
49
+ expect(messages.join('')).toBeDefined()
50
+ expect(stdouts.join('')).toBeDefined()
51
+ expect(instance.messages.get()).toMatchInlineSnapshot('[]')
52
+ })
53
+
54
+ test('behavior: instance errored (duplicate ports)', async () => {
55
+ const instance_1 = defineInstance({ port: 8545 })
56
+ const instance_2 = defineInstance({ port: 8545 })
57
+
58
+ await instance_1.start()
59
+ await expect(() => instance_2.start()).rejects.toThrowError('EADDRINUSE')
60
+ })
61
+
62
+ test('behavior: start and stop multiple times', async () => {
63
+ const instance = defineInstance()
64
+
65
+ await instance.start()
66
+ await instance.stop()
67
+ await instance.start()
68
+ await instance.stop()
69
+ await instance.start()
70
+ await instance.stop()
71
+ await instance.start()
72
+ await instance.stop()
73
+ })
74
+
75
+ test('behavior: can subscribe to stdout', async () => {
76
+ const messages: string[] = []
77
+ const instance = defineInstance()
78
+ instance.on('stdout', (message) => messages.push(message))
79
+
80
+ await instance.start()
81
+ expect(messages.length).toBeGreaterThanOrEqual(1)
82
+ })
83
+
84
+ test('behavior: can subscribe to stderr', async () => {
85
+ const messages: string[] = []
86
+
87
+ const instance_1 = defineInstance({ port: 8545 })
88
+ const instance_2 = defineInstance({ port: 8545 })
89
+
90
+ await instance_1.start()
91
+ instance_2.on('stderr', (message) => messages.push(message))
92
+ await expect(instance_2.start()).rejects.toThrow('EADDRINUSE')
93
+ })
94
+
95
+ test('behavior: exit', async () => {
96
+ const instance = defineInstance()
97
+
98
+ let exitCode: number | null | undefined = undefined
99
+ instance.on('exit', (code) => {
100
+ exitCode = code
101
+ })
102
+
103
+ await instance.start()
104
+ expect(instance.status).toEqual('started')
105
+
106
+ instance._internal.process.kill()
107
+
108
+ await new Promise<void>((res) => setTimeout(res, 500))
109
+ expect(instance.status).toEqual('stopped')
110
+ expect(typeof exitCode !== 'undefined').toBeTruthy()
111
+ })
112
+
113
+ test('behavior: exit when status is starting', async () => {
114
+ const instance = defineInstance()
115
+
116
+ const promise = instance.start()
117
+ expect(instance.status).toEqual('starting')
118
+
119
+ instance._internal.process.kill()
120
+
121
+ await expect(promise).rejects.toThrowErrorMatchingInlineSnapshot(
122
+ `[Error: Failed to start process "alto": exited]`,
123
+ )
124
+ })
@@ -0,0 +1,289 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import { defineInstance } from '../instance.js'
4
+ import { execa } from '../processes/execa.js'
5
+ import { toArgs } from '../utils.js'
6
+
7
+ export type AltoParameters = {
8
+ /**
9
+ * API version (used for internal Pimlico versioning compatibility).
10
+ */
11
+ apiVersion?: readonly string[] | undefined
12
+ /**
13
+ * Override the sender native token balance during estimation
14
+ */
15
+ balanceOverride?: boolean | undefined
16
+ /**
17
+ * Binary path of the `alto` executable.
18
+ */
19
+ binary?: string | undefined
20
+ /**
21
+ * Address of the `BundleBulker` contract.
22
+ */
23
+ bundleBulkerAddress?: `0x${string}` | undefined
24
+ /**
25
+ * Set if the bundler bundle user operations automatically or only when calling `debug_bundler_sendBundleNow`.
26
+ * @default "auto"
27
+ */
28
+ bundleMode?: 'auto' | 'manual' | undefined
29
+ /**
30
+ * Indicates weather the chain is a OP stack chain, arbitrum chain, or default EVM chain.
31
+ */
32
+ chainType?: 'default' | 'op-stack' | 'arbitrum' | undefined
33
+ /**
34
+ * Path to JSON config file.
35
+ */
36
+ config?: string | undefined
37
+ /**
38
+ * Skip user operation validation, use with caution.
39
+ */
40
+ dangerousSkipUserOperationValidation?: boolean | undefined
41
+ /**
42
+ * Default API version.
43
+ */
44
+ defaultApiVersion?: string | undefined
45
+ /**
46
+ * Enable debug endpoints.
47
+ * @default false
48
+ */
49
+ enableDebugEndpoints?: boolean | undefined
50
+ /**
51
+ * Include user ops with the same sender in the single bundle.
52
+ * @default true
53
+ */
54
+ enforceUniqueSendersPerBundle?: boolean | undefined
55
+ /**
56
+ * EntryPoint contract addresses.
57
+ */
58
+ entrypoints: readonly `0x${string}`[]
59
+ /**
60
+ * Address of the EntryPoint simulations contract.
61
+ */
62
+ entrypointSimulationContract?: `0x${string}` | undefined
63
+ /**
64
+ * Private keys of the executor accounts.
65
+ */
66
+ executorPrivateKeys?: readonly `0x${string}`[]
67
+ /**
68
+ * Interval to refill the signer balance (seconds).
69
+ * @default 1200
70
+ */
71
+ executorRefillInterval?: number | undefined
72
+ /**
73
+ * Should the node make expiration checks.
74
+ * @default true
75
+ */
76
+ expirationCheck?: boolean | undefined
77
+ /**
78
+ * Amount to multiply the gas prices fetched from the node.
79
+ * @default "100"
80
+ */
81
+ gasPriceBump?: string | undefined
82
+ /**
83
+ * Maximum that the gas prices fetched using pimlico_getUserOperationGasPrice will be accepted for (seconds).
84
+ * @default 10
85
+ */
86
+ gasPriceExpiry?: number | undefined
87
+ /**
88
+ * The minimum percentage of incoming user operation gas prices compared to the gas price used by the bundler to submit bundles.
89
+ * @default 101
90
+ */
91
+ gasPriceFloorPercent?: number | undefined
92
+ /**
93
+ * Amount to multiply the gas prices fetched using `pimlico_getUserOperationGasPrice`.
94
+ * @default "105,110,115"
95
+ */
96
+ gasPriceMultipliers?: readonly string[] | undefined
97
+ /**
98
+ * Use a fixed value for gas limits during bundle transaction gas limit estimations
99
+ */
100
+ fixedGasLimitForEstimation?: string | undefined
101
+ /**
102
+ * Flush stuck transactions with old nonces during bundler startup.
103
+ */
104
+ flushStuckTransactionsDuringStartup?: boolean | undefined
105
+ /**
106
+ * Log in JSON format.
107
+ */
108
+ json?: boolean | undefined
109
+ /**
110
+ * Send legacy transactions instead of an EIP-1559 transactions.
111
+ * @default false
112
+ */
113
+ legacyTransactions?: boolean | undefined
114
+ /**
115
+ * Calculate the bundle transaction gas limits locally instead of using the RPC gas limit estimation.
116
+ */
117
+ localGasLimitCalculation?: boolean | undefined
118
+ /**
119
+ * Default log level.
120
+ */
121
+ logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | undefined
122
+ /**
123
+ * Max block range for `eth_getLogs` calls.
124
+ */
125
+ maxBlockRange?: number | undefined
126
+ /**
127
+ * Maximum number of operations allowed in the mempool before a bundle is submitted.
128
+ * @default 10
129
+ */
130
+ maxBundleSize?: number | undefined
131
+ /**
132
+ * Maximum time to wait for a bundle to be submitted (ms).
133
+ * @default 1000
134
+ */
135
+ maxBundleWait?: number | undefined
136
+ /**
137
+ * Maximum amount of gas per bundle.
138
+ * @default "5000000"
139
+ */
140
+ maxGasPerBundle?: string | undefined
141
+ /**
142
+ * Maximum number of executor accounts to use from the list of executor private keys.
143
+ */
144
+ maxExecutors?: number | undefined
145
+ /**
146
+ * Maximum amount of parallel user ops to keep in the meempool (same sender, different nonce keys).
147
+ * @default 10
148
+ */
149
+ mempoolMaxParallelOps?: number | undefined
150
+ /**
151
+ * Maximum amount of sequential user ops to keep in the mempool (same sender and nonce key, different nonce values).
152
+ * @default 0
153
+ */
154
+ mempoolMaxQueuedOps?: number | undefined
155
+ /**
156
+ * Minimum stake required for a relay (in 10e18).
157
+ * @default 1
158
+ */
159
+ minEntityStake?: number | undefined
160
+ /**
161
+ * Minimum unstake delay (seconds).
162
+ * @default 1
163
+ */
164
+ minEntityUnstakeDelay?: number | undefined
165
+ /**
166
+ * Minimum balance required for each executor account (below which the utility account will refill).
167
+ */
168
+ minExecutorBalance?: string | undefined
169
+ /**
170
+ * Name of the network (used for metrics).
171
+ * @default "localhost"
172
+ */
173
+ networkName?: string | undefined
174
+ /**
175
+ * Amount to multiply the paymaster gas limits fetched from simulations.
176
+ */
177
+ paymasterGasLimitMultiplier?: string | undefined
178
+ /**
179
+ * Address of the `PerOpInflator` contract.
180
+ */
181
+ perOpInflatorAddress?: `0x${string}` | undefined
182
+ /**
183
+ * Polling interval for querying for new blocks (ms).
184
+ * @default 1000
185
+ */
186
+ pollingInterval?: number | undefined
187
+ /**
188
+ * Port to listen on.
189
+ * @default 3000
190
+ */
191
+ port?: number | undefined
192
+ /**
193
+ * RPC url to connect to.
194
+ */
195
+ rpcUrl: string
196
+ /**
197
+ * Enable safe mode (enforcing all ERC-4337 rules).
198
+ * @default true
199
+ */
200
+ safeMode?: boolean | undefined
201
+ /**
202
+ * RPC url to send transactions to (e.g. flashbots relay).
203
+ */
204
+ sendTransactionRpcUrl?: string | undefined
205
+ /**
206
+ * Timeout for incoming requests (in ms).
207
+ */
208
+ timeout?: number | undefined
209
+ /**
210
+ * Private key of the utility account.
211
+ */
212
+ utilityPrivateKey?: string | undefined
213
+ /**
214
+ * Maximum payload size for websocket messages in bytes (default to 1MB).
215
+ */
216
+ websocketMaxPayloadSize?: number | undefined
217
+ /**
218
+ * Enable websocket server.
219
+ */
220
+ websocket?: boolean | undefined
221
+ }
222
+
223
+ /**
224
+ * Defines an Alto instance.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * const instance = alto({
229
+ * entrypoints: ['0x0000000071727De22E5E9d8BAf0edAc6f37da032'],
230
+ * rpcUrl: `http://localhost:8545`,
231
+ * executorPrivateKeys: ['0x...'],
232
+ * })
233
+ * await instance.start()
234
+ * // ...
235
+ * await instance.stop()
236
+ * ```
237
+ */
238
+ export const alto = defineInstance((parameters?: AltoParameters) => {
239
+ const { ...args } = (parameters || {}) as AltoParameters
240
+
241
+ const name = 'alto'
242
+ const process = execa({ name })
243
+
244
+ return {
245
+ _internal: {
246
+ args,
247
+ get process() {
248
+ return process._internal.process
249
+ },
250
+ },
251
+ host: 'localhost',
252
+ name,
253
+ port: args.port ?? 3000,
254
+ async start({ port = args.port ?? 3000 }, options) {
255
+ const binary = (() => {
256
+ if (args.binary) return [args.binary]
257
+ const libPath = require.resolve('@pimlico/alto')
258
+
259
+ // Remove the `type` field from the package.json file.
260
+ // `@pimlico/alto`'s CLI is packaged as a CJS module.
261
+ // TODO: Fix upstream in `@pimlico/alto`.
262
+ const { type: _, ...packageJson } = JSON.parse(
263
+ readFileSync(resolve(libPath, '../../package.json'), 'utf-8'),
264
+ )
265
+ writeFileSync(
266
+ resolve(libPath, '../../package.json'),
267
+ JSON.stringify(packageJson),
268
+ )
269
+
270
+ return ['node', resolve(libPath, '../cli/alto.js')]
271
+ })()
272
+
273
+ await process.start(($) => $`${binary} ${toArgs({ port, ...args })}`, {
274
+ ...options,
275
+ // Resolve when the process is listening via a "Server listening at" message.
276
+ resolver({ process, reject, resolve }) {
277
+ process.stdout.on('data', (data) => {
278
+ const message = data.toString()
279
+ if (message.includes('Server listening at')) resolve()
280
+ })
281
+ process.stderr.on('data', reject)
282
+ },
283
+ })
284
+ },
285
+ async stop() {
286
+ await process.stop()
287
+ },
288
+ }
289
+ })
@@ -124,6 +124,6 @@ test('behavior: exit when status is starting', async () => {
124
124
  instance._internal.process.kill()
125
125
 
126
126
  await expect(promise).rejects.toThrowErrorMatchingInlineSnapshot(
127
- '[Error: Failed to start process "anvil": exited.]',
127
+ `[Error: Failed to start process "anvil": exited]`,
128
128
  )
129
129
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prool",
3
3
  "description": "HTTP testing instances for Ethereum",
4
- "version": "0.0.8",
4
+ "version": "0.0.9",
5
5
  "type": "module",
6
6
  "module": "_lib/exports/index.mjs",
7
7
  "types": "_lib/exports/index.d.ts",
@@ -30,6 +30,14 @@
30
30
  "get-port": "^7.1.0",
31
31
  "http-proxy": "^1.18.1"
32
32
  },
33
+ "peerDependencies": {
34
+ "@pimlico/alto": "*"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "@pimlico/alto": {
38
+ "optional": true
39
+ }
40
+ },
33
41
  "license": "MIT",
34
42
  "homepage": "https://wevm.dev",
35
43
  "repository": "wevm/prool",
package/pool.test.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import getPort from 'get-port'
2
2
  import { afterEach, beforeAll, describe, expect, test } from 'vitest'
3
3
 
4
- import { stackupOptions } from '../test/utils.js'
4
+ import { altoOptions, stackupOptions } from '../test/utils.js'
5
+ import { alto } from './instances/alto.js'
5
6
  import { anvil } from './instances/anvil.js'
6
7
  import { stackup } from './instances/stackup.js'
7
8
  import { definePool } from './pool.js'
@@ -21,6 +22,9 @@ afterEach(async () => {
21
22
 
22
23
  describe.each([
23
24
  { instance: anvil() },
25
+ {
26
+ instance: alto(altoOptions({ port })),
27
+ },
24
28
  {
25
29
  instance: stackup(stackupOptions({ port })),
26
30
  },
@@ -30,6 +30,7 @@ export type ExecaReturnType = ExecaProcess
30
30
  export function execa(parameters: ExecaParameters): ExecaReturnType {
31
31
  const { name } = parameters
32
32
 
33
+ const errorMessages: string[] = []
33
34
  let process: Process_internal
34
35
 
35
36
  async function stop() {
@@ -76,6 +77,10 @@ export function execa(parameters: ExecaParameters): ExecaReturnType {
76
77
  })
77
78
  process.stderr.on('data', async (data) => {
78
79
  const message = stripColors(data.toString())
80
+
81
+ errorMessages.push(message)
82
+ if (errorMessages.length > 20) errorMessages.shift()
83
+
79
84
  emitter.emit('message', message)
80
85
  emitter.emit('stderr', message)
81
86
  })
@@ -86,7 +91,15 @@ export function execa(parameters: ExecaParameters): ExecaReturnType {
86
91
  if (!code) {
87
92
  process.removeAllListeners()
88
93
  if (status === 'starting')
89
- reject(new Error(`Failed to start process "${name}": exited.`))
94
+ reject(
95
+ new Error(
96
+ `Failed to start process "${name}": ${
97
+ errorMessages.length > 0
98
+ ? `\n\n${errorMessages.join('\n')}`
99
+ : 'exited'
100
+ }`,
101
+ ),
102
+ )
90
103
  }
91
104
  })
92
105
 
package/server.test.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import getPort from 'get-port'
2
2
  import { beforeAll, describe, expect, test } from 'vitest'
3
3
  import { type MessageEvent, WebSocket } from 'ws'
4
- import { stackupOptions } from '../test/utils.js'
4
+ import { altoOptions, stackupOptions } from '../test/utils.js'
5
+ import { alto } from './instances/alto.js'
5
6
  import { anvil } from './instances/anvil.js'
6
7
  import { stackup } from './instances/stackup.js'
7
8
  import { createServer } from './server.js'
@@ -12,6 +13,9 @@ beforeAll(() => anvil({ port }).start())
12
13
 
13
14
  describe.each([
14
15
  { instance: anvil() },
16
+ {
17
+ instance: alto(altoOptions({ port })),
18
+ },
15
19
  {
16
20
  instance: stackup(stackupOptions({ port })),
17
21
  },