prool 0.0.4 → 0.0.5
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/CHANGELOG.md +8 -0
- package/_lib/exports/index.d.ts +1 -1
- package/_lib/exports/processes.d.ts +2 -0
- package/_lib/exports/processes.d.ts.map +1 -0
- package/_lib/exports/processes.js +2 -0
- package/_lib/exports/processes.js.map +1 -0
- package/_lib/instance.d.ts +4 -4
- package/_lib/instance.d.ts.map +1 -1
- package/_lib/instance.js +3 -2
- package/_lib/instance.js.map +1 -1
- package/_lib/instances/anvil.d.ts +1 -5
- package/_lib/instances/anvil.d.ts.map +1 -1
- package/_lib/instances/anvil.js +18 -44
- package/_lib/instances/anvil.js.map +1 -1
- package/_lib/instances/stackup.d.ts +86 -0
- package/_lib/instances/stackup.d.ts.map +1 -0
- package/_lib/instances/stackup.js +89 -0
- package/_lib/instances/stackup.js.map +1 -0
- package/_lib/pool.d.ts +6 -6
- package/_lib/pool.d.ts.map +1 -1
- package/_lib/pool.js +5 -2
- package/_lib/pool.js.map +1 -1
- package/_lib/processes/execa.d.ts +27 -0
- package/_lib/processes/execa.d.ts.map +1 -0
- package/_lib/processes/execa.js +63 -0
- package/_lib/processes/execa.js.map +1 -0
- package/_lib/server.d.ts +3 -3
- package/_lib/server.d.ts.map +1 -1
- package/_lib/utils.d.ts +4 -3
- package/_lib/utils.d.ts.map +1 -1
- package/_lib/utils.js +2 -2
- package/_lib/utils.js.map +1 -1
- package/exports/index.ts +2 -2
- package/exports/processes.test.ts +10 -0
- package/exports/processes.ts +8 -0
- package/instance.test.ts +2 -2
- package/instance.ts +15 -10
- package/instances/anvil.test.ts +5 -3
- package/instances/anvil.ts +21 -49
- package/instances/stackup.test.ts +106 -0
- package/instances/stackup.ts +162 -0
- package/package.json +6 -2
- package/pool.test.ts +195 -171
- package/pool.ts +18 -9
- package/processes/execa.test.ts +143 -0
- package/processes/execa.ts +100 -0
- package/server.test.ts +91 -97
- package/server.ts +4 -4
- package/tsconfig.build.tsbuildinfo +1 -1
- package/utils.ts +11 -9
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { execa as exec } from 'execa';
|
|
2
|
+
import { stripColors } from '../utils.js';
|
|
3
|
+
export function execa(parameters) {
|
|
4
|
+
const { name } = parameters;
|
|
5
|
+
let process;
|
|
6
|
+
async function stop() {
|
|
7
|
+
const killed = process.kill();
|
|
8
|
+
if (!killed)
|
|
9
|
+
return;
|
|
10
|
+
return new Promise((resolve) => process.on('close', resolve));
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
_internal: {
|
|
14
|
+
get process() {
|
|
15
|
+
return process;
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
name,
|
|
19
|
+
start(command, { emitter, resolver, status }) {
|
|
20
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
21
|
+
process = command(exec({
|
|
22
|
+
cleanup: true,
|
|
23
|
+
reject: false,
|
|
24
|
+
}));
|
|
25
|
+
resolver({
|
|
26
|
+
process,
|
|
27
|
+
async reject(data) {
|
|
28
|
+
await stop();
|
|
29
|
+
reject(new Error(`Failed to start process "${name}": ${data.toString()}`));
|
|
30
|
+
},
|
|
31
|
+
resolve() {
|
|
32
|
+
emitter.emit('listening');
|
|
33
|
+
return resolve();
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
process.stdout.on('data', (data) => {
|
|
37
|
+
const message = stripColors(data.toString());
|
|
38
|
+
emitter.emit('message', message);
|
|
39
|
+
emitter.emit('stdout', message);
|
|
40
|
+
});
|
|
41
|
+
process.stderr.on('data', async (data) => {
|
|
42
|
+
const message = stripColors(data.toString());
|
|
43
|
+
emitter.emit('message', message);
|
|
44
|
+
emitter.emit('stderr', message);
|
|
45
|
+
});
|
|
46
|
+
process.on('close', () => process.removeAllListeners());
|
|
47
|
+
process.on('exit', (code, signal) => {
|
|
48
|
+
emitter.emit('exit', code, signal);
|
|
49
|
+
if (!code) {
|
|
50
|
+
process.removeAllListeners();
|
|
51
|
+
if (status === 'starting')
|
|
52
|
+
reject(new Error(`Failed to start process "${name}": exited.`));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return promise;
|
|
56
|
+
},
|
|
57
|
+
async stop() {
|
|
58
|
+
process.removeAllListeners();
|
|
59
|
+
await stop();
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=execa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execa.js","sourceRoot":"","sources":["../../processes/execa.ts"],"names":[],"mappings":"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"}
|
package/_lib/server.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { type IncomingMessage, type Server, type ServerResponse } from 'node:http';
|
|
4
4
|
import type { AddressInfo } from 'node:net';
|
|
5
5
|
import { type DefinePoolParameters } from './pool.js';
|
|
6
|
-
export type
|
|
6
|
+
export type CreateServerParameters = DefinePoolParameters<number> & ({
|
|
7
7
|
/** Host to run the server on. */
|
|
8
8
|
host?: string | undefined;
|
|
9
9
|
/** Port to run the server on. */
|
|
@@ -12,10 +12,10 @@ export type createServerParameters = DefinePoolParameters & ({
|
|
|
12
12
|
host?: undefined;
|
|
13
13
|
port?: undefined;
|
|
14
14
|
});
|
|
15
|
-
export type
|
|
15
|
+
export type CreateServerReturnType = Omit<Server<typeof IncomingMessage, typeof ServerResponse>, 'address'> & {
|
|
16
16
|
address(): AddressInfo | null;
|
|
17
17
|
start(): Promise<() => Promise<void>>;
|
|
18
18
|
stop(): Promise<void>;
|
|
19
19
|
};
|
|
20
|
-
export declare function createServer(parameters:
|
|
20
|
+
export declare function createServer(parameters: CreateServerParameters): CreateServerReturnType;
|
|
21
21
|
//# sourceMappingURL=server.d.ts.map
|
package/_lib/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../server.ts"],"names":[],"mappings":";;AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,cAAc,EAEpB,MAAM,WAAW,CAAA;AAClB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAG3C,OAAO,EAAE,KAAK,oBAAoB,EAAc,MAAM,WAAW,CAAA;AAKjE,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../server.ts"],"names":[],"mappings":";;AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,cAAc,EAEpB,MAAM,WAAW,CAAA;AAClB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAG3C,OAAO,EAAE,KAAK,oBAAoB,EAAc,MAAM,WAAW,CAAA;AAKjE,MAAM,MAAM,sBAAsB,GAAG,oBAAoB,CAAC,MAAM,CAAC,GAC/D,CACI;IACE,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAA;CACb,GACD;IACE,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB,CACJ,CAAA;AAEH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,MAAM,CAAC,OAAO,eAAe,EAAE,OAAO,cAAc,CAAC,EACrD,SAAS,CACV,GAAG;IACF,OAAO,IAAI,WAAW,GAAG,IAAI,CAAA;IAC7B,KAAK,IAAI,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACrC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB,CAAA;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,sBAAsB,GACjC,sBAAsB,CAmGxB"}
|
package/_lib/utils.d.ts
CHANGED
|
@@ -16,15 +16,16 @@ export declare function extractPath(request: string): ExtractPathReturnType;
|
|
|
16
16
|
* @returns The stripped string.
|
|
17
17
|
*/
|
|
18
18
|
export declare function stripColors(message: string): string;
|
|
19
|
+
export type ToArgsParameters = {
|
|
20
|
+
[key: string]: Record<string, string> | string | boolean | number | bigint | undefined;
|
|
21
|
+
};
|
|
19
22
|
/**
|
|
20
23
|
* Converts an object of options to an array of command line arguments.
|
|
21
24
|
*
|
|
22
25
|
* @param options The options object.
|
|
23
26
|
* @returns The command line arguments.
|
|
24
27
|
*/
|
|
25
|
-
export declare function toArgs(
|
|
26
|
-
[key: string]: Record<string, string> | string | boolean | number | bigint | undefined;
|
|
27
|
-
}): string[];
|
|
28
|
+
export declare function toArgs(parameters: ToArgsParameters): string[];
|
|
28
29
|
/**
|
|
29
30
|
* Converts a camelCase string to a flag case string.
|
|
30
31
|
*
|
package/_lib/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAkBlE;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,UAE1C;AAED
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IACtB,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAkBlE;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,UAE1C;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,CAAC,GAAG,EAAE,MAAM,GACR,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,CAAA;CACd,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,UAAU,EAAE,gBAAgB,YAsBlD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,UAErC"}
|
package/_lib/utils.js
CHANGED
|
@@ -36,8 +36,8 @@ export function stripColors(message) {
|
|
|
36
36
|
* @param options The options object.
|
|
37
37
|
* @returns The command line arguments.
|
|
38
38
|
*/
|
|
39
|
-
export function toArgs(
|
|
40
|
-
return Object.entries(
|
|
39
|
+
export function toArgs(parameters) {
|
|
40
|
+
return Object.entries(parameters).flatMap(([key, value]) => {
|
|
41
41
|
if (value === undefined)
|
|
42
42
|
return [];
|
|
43
43
|
if (typeof value === 'object' && value !== null)
|
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;
|
|
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"}
|
package/exports/index.ts
CHANGED
package/instance.test.ts
CHANGED
|
@@ -212,7 +212,7 @@ test('behavior: events', async () => {
|
|
|
212
212
|
name: 'foo',
|
|
213
213
|
host: 'localhost',
|
|
214
214
|
port: 3000,
|
|
215
|
-
async start({ emitter }) {
|
|
215
|
+
async start(_, { emitter }) {
|
|
216
216
|
emitter.emit('message', count.toString())
|
|
217
217
|
emitter.emit('listening')
|
|
218
218
|
if (count > 0) emitter.emit('stderr', 'stderr')
|
|
@@ -268,7 +268,7 @@ test('behavior: messages', async () => {
|
|
|
268
268
|
name: 'foo',
|
|
269
269
|
host: 'localhost',
|
|
270
270
|
port: 3000,
|
|
271
|
-
async start({ emitter }) {
|
|
271
|
+
async start(_, { emitter }) {
|
|
272
272
|
for (let i = 0; i < 50; i++) emitter.emit('message', i.toString())
|
|
273
273
|
},
|
|
274
274
|
async stop() {},
|
package/instance.ts
CHANGED
|
@@ -8,11 +8,11 @@ type EventTypes = {
|
|
|
8
8
|
stdout: [message: string]
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
type InstanceStartOptions_internal = {
|
|
11
|
+
export type InstanceStartOptions_internal = {
|
|
12
12
|
emitter: EventEmitter<EventTypes>
|
|
13
13
|
status: Instance['status']
|
|
14
14
|
}
|
|
15
|
-
type InstanceStopOptions_internal = {
|
|
15
|
+
export type InstanceStopOptions_internal = {
|
|
16
16
|
emitter: EventEmitter<EventTypes>
|
|
17
17
|
status: Instance['status']
|
|
18
18
|
}
|
|
@@ -30,9 +30,10 @@ export type DefineInstanceFn<
|
|
|
30
30
|
> = (parameters: parameters) => Pick<Instance, 'host' | 'name' | 'port'> & {
|
|
31
31
|
_internal?: _internal | undefined
|
|
32
32
|
start(
|
|
33
|
-
options: InstanceStartOptions
|
|
33
|
+
options: InstanceStartOptions,
|
|
34
|
+
options_internal: InstanceStartOptions_internal,
|
|
34
35
|
): Promise<void>
|
|
35
|
-
stop(
|
|
36
|
+
stop(options_internal: InstanceStopOptions_internal): Promise<void>
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export type Instance<
|
|
@@ -217,16 +218,20 @@ export function defineInstance<
|
|
|
217
218
|
emitter.on('exit', onExit)
|
|
218
219
|
|
|
219
220
|
status = 'starting'
|
|
220
|
-
start(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
221
|
+
start(
|
|
222
|
+
{
|
|
223
|
+
port,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
emitter,
|
|
227
|
+
status: this.status,
|
|
228
|
+
},
|
|
229
|
+
)
|
|
225
230
|
.then(() => {
|
|
226
231
|
status = 'started'
|
|
227
232
|
|
|
228
233
|
stopResolver = Promise.withResolvers<void>()
|
|
229
|
-
startResolver.resolve(this.stop)
|
|
234
|
+
startResolver.resolve(this.stop.bind(this))
|
|
230
235
|
})
|
|
231
236
|
.catch((error) => {
|
|
232
237
|
status = 'idle'
|
package/instances/anvil.test.ts
CHANGED
|
@@ -47,7 +47,7 @@ test('behavior: instance errored (duplicate ports)', async () => {
|
|
|
47
47
|
|
|
48
48
|
await instance_1.start()
|
|
49
49
|
await expect(() => instance_2.start()).rejects.toThrowError(
|
|
50
|
-
'Failed to start anvil',
|
|
50
|
+
'Failed to start process "anvil"',
|
|
51
51
|
)
|
|
52
52
|
})
|
|
53
53
|
|
|
@@ -81,7 +81,9 @@ test('behavior: can subscribe to stderr', async () => {
|
|
|
81
81
|
|
|
82
82
|
await instance_1.start()
|
|
83
83
|
instance_2.on('stderr', (message) => messages.push(message))
|
|
84
|
-
await expect(instance_2.start()).rejects.toThrow(
|
|
84
|
+
await expect(instance_2.start()).rejects.toThrow(
|
|
85
|
+
'Failed to start process "anvil"',
|
|
86
|
+
)
|
|
85
87
|
})
|
|
86
88
|
|
|
87
89
|
test('behavior: starts anvil with custom options', async () => {
|
|
@@ -122,6 +124,6 @@ test('behavior: exit when status is starting', async () => {
|
|
|
122
124
|
instance._internal.process.kill()
|
|
123
125
|
|
|
124
126
|
await expect(promise).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
125
|
-
'[Error: Failed to start anvil: exited.]',
|
|
127
|
+
'[Error: Failed to start process "anvil": exited.]',
|
|
126
128
|
)
|
|
127
129
|
})
|
package/instances/anvil.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { type ResultPromise, execa } from 'execa'
|
|
2
1
|
import { defineInstance } from '../instance.js'
|
|
3
|
-
import {
|
|
2
|
+
import { execa } from '../processes/execa.js'
|
|
4
3
|
import { toArgs } from '../utils.js'
|
|
5
4
|
|
|
6
5
|
export type AnvilParameters = {
|
|
@@ -259,64 +258,37 @@ export type AnvilParameters = {
|
|
|
259
258
|
export const anvil = defineInstance((parameters?: AnvilParameters) => {
|
|
260
259
|
const { binary = 'anvil', ...args } = parameters || {}
|
|
261
260
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
async function stop() {
|
|
265
|
-
const killed = process.kill()
|
|
266
|
-
if (!killed) throw new Error('Failed to stop anvil')
|
|
267
|
-
return new Promise((resolve) => process.on('close', resolve))
|
|
268
|
-
}
|
|
261
|
+
const name = 'anvil'
|
|
262
|
+
const process = execa({ name })
|
|
269
263
|
|
|
270
264
|
return {
|
|
271
265
|
_internal: {
|
|
272
266
|
args,
|
|
273
267
|
get process() {
|
|
274
|
-
return process
|
|
268
|
+
return process._internal.process
|
|
275
269
|
},
|
|
276
270
|
},
|
|
277
271
|
host: args.host ?? 'localhost',
|
|
278
|
-
name
|
|
272
|
+
name,
|
|
279
273
|
port: args.port ?? 8545,
|
|
280
|
-
async start({
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
})
|
|
297
|
-
process.stderr.on('data', async (data) => {
|
|
298
|
-
const message = stripColors(data.toString())
|
|
299
|
-
emitter.emit('message', message)
|
|
300
|
-
emitter.emit('stderr', message)
|
|
301
|
-
await stop()
|
|
302
|
-
reject(new Error(`Failed to start anvil: ${data.toString()}`))
|
|
303
|
-
})
|
|
304
|
-
process.on('close', () => process.removeAllListeners())
|
|
305
|
-
process.on('exit', (code, signal) => {
|
|
306
|
-
emitter.emit('exit', code, signal)
|
|
307
|
-
|
|
308
|
-
if (!code) {
|
|
309
|
-
process.removeAllListeners()
|
|
310
|
-
if (status === 'starting')
|
|
311
|
-
reject(new Error('Failed to start anvil: exited.'))
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
return promise
|
|
274
|
+
async start({ port = args.port }, options) {
|
|
275
|
+
return await process.start(
|
|
276
|
+
($) => $`${binary} ${toArgs({ ...args, port })}`,
|
|
277
|
+
{
|
|
278
|
+
...options,
|
|
279
|
+
// Resolve when the process is listening via a "Listening on" message.
|
|
280
|
+
resolver({ process, reject, resolve }) {
|
|
281
|
+
process.stdout.on('data', (data) => {
|
|
282
|
+
const message = data.toString()
|
|
283
|
+
if (message.includes('Listening on')) resolve()
|
|
284
|
+
})
|
|
285
|
+
process.stderr.on('data', reject)
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
)
|
|
316
289
|
},
|
|
317
290
|
async stop() {
|
|
318
|
-
process.
|
|
319
|
-
await stop()
|
|
291
|
+
await process.stop()
|
|
320
292
|
},
|
|
321
293
|
}
|
|
322
294
|
})
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import getPort from 'get-port'
|
|
2
|
+
import { afterEach, beforeAll, expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { stackupOptions } from '../../test/utils.js'
|
|
5
|
+
import type { Instance } from '../instance.js'
|
|
6
|
+
import { anvil } from './anvil.js'
|
|
7
|
+
import { type StackupParameters, stackup } from './stackup.js'
|
|
8
|
+
|
|
9
|
+
const instances: Instance[] = []
|
|
10
|
+
|
|
11
|
+
const port = await getPort()
|
|
12
|
+
|
|
13
|
+
const defineInstance = (parameters?: Partial<StackupParameters>) => {
|
|
14
|
+
const instance = stackup({
|
|
15
|
+
...stackupOptions({ 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({
|
|
56
|
+
port: 1337,
|
|
57
|
+
})
|
|
58
|
+
const instance_2 = defineInstance({
|
|
59
|
+
port: 1337,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
await instance_1.start()
|
|
63
|
+
await expect(() => instance_2.start()).rejects.toThrowError()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('behavior: start and stop multiple times', async () => {
|
|
67
|
+
const instance = defineInstance()
|
|
68
|
+
|
|
69
|
+
await instance.start()
|
|
70
|
+
await instance.stop()
|
|
71
|
+
await instance.start()
|
|
72
|
+
await instance.stop()
|
|
73
|
+
await instance.start()
|
|
74
|
+
await instance.stop()
|
|
75
|
+
await instance.start()
|
|
76
|
+
await instance.stop()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('behavior: can subscribe to stderr', async () => {
|
|
80
|
+
const messages: string[] = []
|
|
81
|
+
|
|
82
|
+
const instance_1 = defineInstance({ port: 1337 })
|
|
83
|
+
const instance_2 = defineInstance({ port: 1337 })
|
|
84
|
+
|
|
85
|
+
await instance_1.start()
|
|
86
|
+
instance_2.on('stderr', (message) => messages.push(message))
|
|
87
|
+
await expect(instance_2.start()).rejects.toThrowError()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('behavior: exit', async () => {
|
|
91
|
+
const instance = defineInstance()
|
|
92
|
+
|
|
93
|
+
let exitCode: number | null | undefined = undefined
|
|
94
|
+
instance.on('exit', (code) => {
|
|
95
|
+
exitCode = code
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
await instance.start()
|
|
99
|
+
expect(instance.status).toEqual('started')
|
|
100
|
+
|
|
101
|
+
instance._internal.process.kill()
|
|
102
|
+
|
|
103
|
+
await new Promise<void>((res) => setTimeout(res, 100))
|
|
104
|
+
expect(instance.status).toEqual('stopped')
|
|
105
|
+
expect(typeof exitCode !== 'undefined').toBeTruthy()
|
|
106
|
+
})
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { defineInstance } from '../instance.js'
|
|
2
|
+
import { execa } from '../processes/execa.js'
|
|
3
|
+
|
|
4
|
+
export type StackupParameters = {
|
|
5
|
+
/**
|
|
6
|
+
* The name of the native tracer to use during validation. This will be "bundlerCollectorTracer" if using builds from ERC-4337 execution client repo.
|
|
7
|
+
* Defaults to address of `privateKey`.
|
|
8
|
+
*/
|
|
9
|
+
beneficiary?: string | undefined
|
|
10
|
+
/**
|
|
11
|
+
* Directory to store the embedded database.
|
|
12
|
+
* @default /tmp/stackup_bundler
|
|
13
|
+
*/
|
|
14
|
+
dataDirectory?: string | undefined
|
|
15
|
+
/**
|
|
16
|
+
* RPC url to the execution client.
|
|
17
|
+
*/
|
|
18
|
+
ethClientUrl: string
|
|
19
|
+
/**
|
|
20
|
+
* A boolean value for bundlers on an Arbitrum stack network to properly account for the L1 callData cost.
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
isArbStackNetwork?: boolean | undefined
|
|
24
|
+
/**
|
|
25
|
+
* A boolean value for bundlers on an OP stack network to properly account for the L1 callData cost.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
isOpStackNetwork?: boolean | undefined
|
|
29
|
+
/**
|
|
30
|
+
* A boolean value for bundlers on a network that supports RIP-7212 precompile for secp256r1 signature verification.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
isRip7212Supported?: boolean | undefined
|
|
34
|
+
/**
|
|
35
|
+
* The maximum gas limit that can be submitted per UserOperation batch.
|
|
36
|
+
* @default 18_000_000
|
|
37
|
+
*/
|
|
38
|
+
maxBatchGasLimit?: number | undefined
|
|
39
|
+
/**
|
|
40
|
+
* The maximum duration that a userOp can stay in the mempool before getting dropped.
|
|
41
|
+
* @default 180
|
|
42
|
+
*/
|
|
43
|
+
maxOpTtlSeconds?: number | undefined
|
|
44
|
+
/**
|
|
45
|
+
* The maximum verificationGasLimit on a received UserOperation.
|
|
46
|
+
* @default 6_000_000
|
|
47
|
+
*/
|
|
48
|
+
maxVerificationGas?: number | undefined
|
|
49
|
+
/**
|
|
50
|
+
* The maximum block range when looking up a User Operation with eth_getUserOperationReceipt or eth_getUserOperationByHash.
|
|
51
|
+
*
|
|
52
|
+
* Higher limits allow for fetching older User Operations but will result in higher request latency due to additional compute on the underlying node.
|
|
53
|
+
*
|
|
54
|
+
* @default 2_000
|
|
55
|
+
*/
|
|
56
|
+
opLookupLimit?: number | undefined
|
|
57
|
+
/**
|
|
58
|
+
* Port to start the instance on.
|
|
59
|
+
*/
|
|
60
|
+
port?: number | undefined
|
|
61
|
+
/**
|
|
62
|
+
* The private key for the EOA used to relay User Operation bundles to the EntryPoint.
|
|
63
|
+
*/
|
|
64
|
+
privateKey: string
|
|
65
|
+
/**
|
|
66
|
+
* EntryPoint addresses to support. The first address is the preferred EntryPoint.
|
|
67
|
+
*/
|
|
68
|
+
supportedEntryPoints?: string[]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Defines an Anvil instance.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const instance = stackup({
|
|
77
|
+
* ethClientUrl: 'http://localhost:8545',
|
|
78
|
+
* port: 4337,
|
|
79
|
+
* privateKey: '0x...'
|
|
80
|
+
* })
|
|
81
|
+
* await instance.start()
|
|
82
|
+
* // ...
|
|
83
|
+
* await instance.stop()
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export const stackup = defineInstance((parameters?: StackupParameters) => {
|
|
87
|
+
const args = (parameters || {}) as StackupParameters
|
|
88
|
+
|
|
89
|
+
const host = 'localhost'
|
|
90
|
+
const name = 'stackup'
|
|
91
|
+
const process = execa({ name })
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
_internal: {
|
|
95
|
+
args,
|
|
96
|
+
get process() {
|
|
97
|
+
return process._internal.process
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
host,
|
|
101
|
+
name,
|
|
102
|
+
port: args.port ?? 4337,
|
|
103
|
+
async start({ port = args.port }, options) {
|
|
104
|
+
const args_ = [
|
|
105
|
+
'--net',
|
|
106
|
+
'host',
|
|
107
|
+
'--add-host',
|
|
108
|
+
'host.docker.internal:host-gateway',
|
|
109
|
+
'--add-host',
|
|
110
|
+
'localhost:host-gateway',
|
|
111
|
+
'-p',
|
|
112
|
+
`${port}:${port}`,
|
|
113
|
+
'-e',
|
|
114
|
+
`ERC4337_BUNDLER_PORT=${port}`,
|
|
115
|
+
...Object.entries(args).flatMap(([key, value]) => {
|
|
116
|
+
if (key === 'port') return []
|
|
117
|
+
if (value === undefined) return []
|
|
118
|
+
|
|
119
|
+
if (key === 'ethClientUrl')
|
|
120
|
+
value = (value as string).replaceAll(
|
|
121
|
+
/127\.0\.0\.1|0\.0\.0\.0/g,
|
|
122
|
+
'host.docker.internal',
|
|
123
|
+
)
|
|
124
|
+
if (key === 'privateKey') value = (value as string).replace('0x', '')
|
|
125
|
+
|
|
126
|
+
return [
|
|
127
|
+
'-e',
|
|
128
|
+
`ERC4337_BUNDLER_${key
|
|
129
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
130
|
+
.toUpperCase()}=${value}`,
|
|
131
|
+
]
|
|
132
|
+
}),
|
|
133
|
+
'stackupwallet/stackup-bundler:latest',
|
|
134
|
+
'/app/stackup-bundler',
|
|
135
|
+
'start',
|
|
136
|
+
'--mode',
|
|
137
|
+
'private',
|
|
138
|
+
]
|
|
139
|
+
return await process.start(($) => $`docker run ${args_}`, {
|
|
140
|
+
...options,
|
|
141
|
+
resolver({ process, resolve, reject }) {
|
|
142
|
+
process.stderr.on('data', async (data) => {
|
|
143
|
+
const message = data.toString()
|
|
144
|
+
// For some reason, `stackup-bundler` logs to stderr. So we have to try
|
|
145
|
+
// and dissect what is an error, and what is not. 😅
|
|
146
|
+
if (message.includes('Set nextTxnTs to'))
|
|
147
|
+
setTimeout(() => resolve(), 100)
|
|
148
|
+
else if (
|
|
149
|
+
message
|
|
150
|
+
.toLowerCase()
|
|
151
|
+
.match(/panic|error|connection refused|address already in use/)
|
|
152
|
+
)
|
|
153
|
+
reject(data)
|
|
154
|
+
})
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
},
|
|
158
|
+
async stop() {
|
|
159
|
+
await process.stop()
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prool",
|
|
3
|
-
"description": "",
|
|
4
|
-
"version": "0.0.
|
|
3
|
+
"description": "HTTP testing instances for Ethereum",
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "_lib/exports/index.mjs",
|
|
7
7
|
"types": "_lib/exports/index.d.ts",
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
"types": "./_lib/exports/index.d.ts",
|
|
16
16
|
"default": "./_lib/exports/index.js"
|
|
17
17
|
},
|
|
18
|
+
"./processes": {
|
|
19
|
+
"types": "./_lib/exports/processes.d.ts",
|
|
20
|
+
"default": "./_lib/exports/processes.js"
|
|
21
|
+
},
|
|
18
22
|
"./instances": {
|
|
19
23
|
"types": "./_lib/exports/instances.d.ts",
|
|
20
24
|
"default": "./_lib/exports/instances.js"
|