starskiff 0.0.0 → 0.4.2

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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +327 -0
  3. package/dist/Instance.d.ts +89 -0
  4. package/dist/Instance.d.ts.map +1 -0
  5. package/dist/Instance.js +206 -0
  6. package/dist/Instance.js.map +1 -0
  7. package/dist/accounts.d.ts +26 -0
  8. package/dist/accounts.d.ts.map +1 -0
  9. package/dist/accounts.js +38 -0
  10. package/dist/accounts.js.map +1 -0
  11. package/dist/cosmos.d.ts +265 -0
  12. package/dist/cosmos.d.ts.map +1 -0
  13. package/dist/cosmos.js +359 -0
  14. package/dist/cosmos.js.map +1 -0
  15. package/dist/index.d.ts +8 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +7 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/instances/evmd.d.ts +68 -0
  20. package/dist/instances/evmd.d.ts.map +1 -0
  21. package/dist/instances/evmd.js +106 -0
  22. package/dist/instances/evmd.js.map +1 -0
  23. package/dist/instances/gaiad.d.ts +46 -0
  24. package/dist/instances/gaiad.d.ts.map +1 -0
  25. package/dist/instances/gaiad.js +35 -0
  26. package/dist/instances/gaiad.js.map +1 -0
  27. package/dist/instances/hermes.d.ts +52 -0
  28. package/dist/instances/hermes.d.ts.map +1 -0
  29. package/dist/instances/hermes.js +457 -0
  30. package/dist/instances/hermes.js.map +1 -0
  31. package/dist/instances/simd.d.ts +43 -0
  32. package/dist/instances/simd.d.ts.map +1 -0
  33. package/dist/instances/simd.js +21 -0
  34. package/dist/instances/simd.js.map +1 -0
  35. package/dist/instances/wasmd.d.ts +45 -0
  36. package/dist/instances/wasmd.d.ts.map +1 -0
  37. package/dist/instances/wasmd.js +23 -0
  38. package/dist/instances/wasmd.js.map +1 -0
  39. package/dist/instances/xplad.d.ts +55 -0
  40. package/dist/instances/xplad.d.ts.map +1 -0
  41. package/dist/instances/xplad.js +86 -0
  42. package/dist/instances/xplad.js.map +1 -0
  43. package/dist/ports.d.ts +31 -0
  44. package/dist/ports.d.ts.map +1 -0
  45. package/dist/ports.js +45 -0
  46. package/dist/ports.js.map +1 -0
  47. package/dist/process.d.ts +34 -0
  48. package/dist/process.d.ts.map +1 -0
  49. package/dist/process.js +92 -0
  50. package/dist/process.js.map +1 -0
  51. package/dist/utils.d.ts +15 -0
  52. package/dist/utils.d.ts.map +1 -0
  53. package/dist/utils.js +33 -0
  54. package/dist/utils.js.map +1 -0
  55. package/package.json +50 -7
  56. package/src/Instance.ts +288 -0
  57. package/src/accounts.ts +49 -0
  58. package/src/cosmos.ts +601 -0
  59. package/src/index.ts +7 -0
  60. package/src/instances/evmd.ts +126 -0
  61. package/src/instances/gaiad.ts +40 -0
  62. package/src/instances/hermes.ts +580 -0
  63. package/src/instances/simd.ts +26 -0
  64. package/src/instances/wasmd.ts +28 -0
  65. package/src/instances/xplad.ts +107 -0
  66. package/src/ports.ts +63 -0
  67. package/src/process.ts +137 -0
  68. package/src/utils.ts +38 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Wonhee Lee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,327 @@
1
+ # starskiff
2
+
3
+ > [!WARNING]
4
+ > This project is under active development. APIs may change without notice until v1.0.
5
+
6
+ Real Cosmos SDK nodes as ephemeral test instances — spawned as child processes, no Docker, no Kubernetes.
7
+
8
+ starskiff is the lightweight skiff to [Starship](https://github.com/cosmology-tech/starship)'s heavy vessel: where Starship stands up multi-chain environments on Kubernetes, starskiff boots a single real chain node in seconds — for integration tests and CI. Not a mock; the actual Go binary.
9
+
10
+ Inspired by [prool](https://github.com/wevm/prool) (test instances for Ethereum).
11
+
12
+ ## Features
13
+
14
+ - Spawn real Cosmos SDK nodes as child processes
15
+ - No Docker, no Kubernetes — just a Go binary
16
+ - Genesis account injection with mnemonic recovery
17
+ - Full lifecycle management (start/stop/restart)
18
+ - Compatible with [@cosmjs/stargate](https://github.com/cosmos/cosmjs) and [@cosmjs/cosmwasm-stargate](https://github.com/cosmos/cosmjs) for testing
19
+ - Extensible to any Cosmos SDK chain binary via `cosmosBase`
20
+
21
+ ## Instances
22
+
23
+ | Instance | Binary | Modules | Use case |
24
+ | ------------------ | ------- | ----------------------------------------------- | ---------------------------- |
25
+ | `Instance.simd()` | `simd` | bank, staking, gov, mint | Basic Cosmos SDK testing |
26
+ | `Instance.wasmd()` | `wasmd` | bank, staking, gov, mint, **IBC**, **CosmWasm** | Contract deploy/execute, IBC |
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pnpm add -D starskiff
32
+ ```
33
+
34
+ ### Prerequisites
35
+
36
+ Install the binary for the instance you need. Requires [Go](https://go.dev/dl/) >= 1.25.
37
+
38
+ **simd** (Cosmos SDK simapp):
39
+
40
+ ```bash
41
+ git clone --depth 1 https://github.com/cosmos/cosmos-sdk.git /tmp/cosmos-sdk
42
+ cd /tmp/cosmos-sdk/simapp && go build -o ~/go/bin/simd ./simd/
43
+ ```
44
+
45
+ **wasmd** (CosmWasm — includes IBC):
46
+
47
+ ```bash
48
+ git clone --depth 1 https://github.com/CosmWasm/wasmd.git /tmp/wasmd
49
+ cd /tmp/wasmd && go build -o ~/go/bin/wasmd ./cmd/wasmd/
50
+ ```
51
+
52
+ ### Prebuilt binaries (CI)
53
+
54
+ Prebuilt `linux/amd64` binaries are available in [GitHub Releases](https://github.com/2wheeh/starskiff/releases/tag/binaries/latest) for CI environments:
55
+
56
+ ```bash
57
+ # Download and install (e.g. in GitHub Actions)
58
+ gh release download "binaries/latest" --repo 2wheeh/starskiff --pattern "*.gz" --dir /tmp
59
+ gunzip -c /tmp/simd-linux-amd64.gz > /usr/local/bin/simd
60
+ gunzip -c /tmp/wasmd-linux-amd64.gz > /usr/local/bin/wasmd
61
+ chmod +x /usr/local/bin/simd /usr/local/bin/wasmd
62
+ ```
63
+
64
+ > For local development on macOS/Windows, build from source using the instructions above.
65
+
66
+ ## Usage
67
+
68
+ ### Basic (simd)
69
+
70
+ ```ts
71
+ import { Instance } from 'starskiff';
72
+
73
+ const instance = Instance.simd({
74
+ chainId: 'test-1',
75
+ denom: 'stake',
76
+ accounts: [
77
+ {
78
+ mnemonic: 'abandon abandon abandon ...',
79
+ coins: '1000000000stake',
80
+ name: 'alice',
81
+ },
82
+ ],
83
+ });
84
+
85
+ await instance.start();
86
+
87
+ // Connect with cosmjs
88
+ import { StargateClient } from '@cosmjs/stargate';
89
+ const client = await StargateClient.connect(instance.rpcUrl);
90
+ const balance = await client.getBalance(address, 'stake');
91
+
92
+ await instance.stop();
93
+ ```
94
+
95
+ ### Test accounts
96
+
97
+ Skip deriving your own dev mnemonic — `testAccounts` ships a handful of
98
+ well-known, publicly-documented BIP39 mnemonics with addresses pre-derived
99
+ for the `cosmos` bech32 prefix:
100
+
101
+ ```ts
102
+ import { Instance, testAccounts } from 'starskiff';
103
+
104
+ const [alice, bob] = testAccounts;
105
+
106
+ const instance = Instance.simd({
107
+ chainId: 'test-1',
108
+ accounts: [{ mnemonic: alice.mnemonic, coins: '1000000000stake', name: alice.name }],
109
+ });
110
+ ```
111
+
112
+ These mnemonics are public — never fund them with real value. Addresses
113
+ assume the `cosmos` prefix; chains configured with a different `prefix`
114
+ (e.g. `osmo`, `xpla`) need addresses re-derived from the same mnemonic.
115
+
116
+ ### CosmWasm (wasmd)
117
+
118
+ ```ts
119
+ import { Instance } from 'starskiff';
120
+ import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate';
121
+ import { GasPrice } from '@cosmjs/stargate';
122
+
123
+ const instance = Instance.wasmd({
124
+ chainId: 'wasm-test-1',
125
+ accounts: [{ mnemonic: '...', coins: '1000000000stake', name: 'alice' }],
126
+ });
127
+ await instance.start();
128
+
129
+ const client = await SigningCosmWasmClient.connectWithSigner(instance.rpcUrl, wallet, {
130
+ gasPrice: GasPrice.fromString('0stake'),
131
+ });
132
+
133
+ // Upload, instantiate, execute
134
+ const { codeId } = await client.upload(address, wasmBytecode, 'auto');
135
+ const { contractAddress } = await client.instantiate(address, codeId, initMsg, 'label', 'auto');
136
+ const result = await client.execute(address, contractAddress, executeMsg, 'auto');
137
+ ```
138
+
139
+ ### vitest globalSetup (provide/inject)
140
+
141
+ ```ts
142
+ // test/global-setup.ts
143
+ import type { TestProject } from 'vitest/node';
144
+ import { Instance } from 'starskiff';
145
+
146
+ export default async function setup({ provide }: TestProject) {
147
+ const instance = Instance.wasmd({
148
+ chainId: 'test-1',
149
+ accounts: [{ mnemonic: '...', coins: '1000000000stake' }],
150
+ });
151
+ await instance.start();
152
+
153
+ provide('rpcUrl', instance.rpcUrl);
154
+
155
+ return () => instance.stop();
156
+ }
157
+
158
+ declare module 'vitest' {
159
+ export interface ProvidedContext {
160
+ rpcUrl: string;
161
+ }
162
+ }
163
+ ```
164
+
165
+ ```ts
166
+ // vitest.config.ts
167
+ export default defineConfig({
168
+ test: {
169
+ globalSetup: './test/global-setup.ts',
170
+ },
171
+ });
172
+ ```
173
+
174
+ ```ts
175
+ // test/bank.test.ts
176
+ import { inject } from 'vitest';
177
+
178
+ const rpcUrl = inject('rpcUrl');
179
+
180
+ it('queries balance', async () => {
181
+ const client = await StargateClient.connect(rpcUrl);
182
+ const balance = await client.getBalance(address, 'stake');
183
+ // ...
184
+ });
185
+ ```
186
+
187
+ ### Multi-chain
188
+
189
+ Hand-assigning ports gets tedious (and error-prone) once you run more than one
190
+ chain — use `findFreePorts()` instead:
191
+
192
+ ```ts
193
+ import { findFreePorts, Instance } from 'starskiff';
194
+
195
+ const [ports1, ports2] = await Promise.all([findFreePorts(), findFreePorts()]);
196
+
197
+ const chain1 = Instance.wasmd({ chainId: 'wasm-1', ...ports1 });
198
+ const chain2 = Instance.wasmd({ chainId: 'wasm-2', ...ports2 });
199
+
200
+ await Promise.all([chain1.start(), chain2.start()]);
201
+ ```
202
+
203
+ Each `findFreePorts()` call returns a `PortSet` of distinct, currently-free
204
+ ports (`rpcPort`, `grpcPort`, `apiPort`, `p2pPort`, `grpcWebPort`,
205
+ `pprofPort`). Pass `{ evm: true }` to also grab an `evmPort` for EVM-enabled
206
+ chains (e.g. `xplad`, `evmd`). Note the TOCTOU caveat: a port is free at grab
207
+ time, but nothing stops another process from binding it before the instance
208
+ actually starts — rare, but possible under heavy concurrent test runs.
209
+
210
+ ### Custom chain binary
211
+
212
+ ```ts
213
+ import { Instance, cosmosBase } from 'starskiff';
214
+
215
+ // Any Cosmos SDK binary works — same init/genesis/start flow
216
+ const gaiad = Instance.define(params => cosmosBase({ binary: 'gaiad', name: 'gaiad', ...params }));
217
+ ```
218
+
219
+ ## API
220
+
221
+ ### `Instance.simd(parameters?, options?)`
222
+
223
+ Creates a simd (Cosmos SDK simapp) instance.
224
+
225
+ ### `Instance.wasmd(parameters?, options?)`
226
+
227
+ Creates a wasmd (CosmWasm + IBC) instance. Same parameters as simd.
228
+
229
+ ### Parameters
230
+
231
+ Shared by all instances (`CosmosChainParameters`):
232
+
233
+ | Parameter | Type | Default | Description |
234
+ | ------------------ | ----------------- | ----------------- | ------------------ |
235
+ | `binary` | `string` | instance-specific | Path to binary |
236
+ | `chainId` | `string` | `"starskiff-1"` | Chain ID |
237
+ | `denom` | `string` | `"stake"` | Default denom |
238
+ | `accounts` | `CosmosAccount[]` | `[]` | Genesis accounts |
239
+ | `minimumGasPrices` | `string` | `"0{denom}"` | Minimum gas prices |
240
+ | `rpcPort` | `number` | `26657` | CometBFT RPC port |
241
+ | `grpcPort` | `number` | `9090` | gRPC port |
242
+ | `apiPort` | `number` | `1317` | REST API port |
243
+ | `p2pPort` | `number` | `26656` | P2P port |
244
+ | `grpcWebPort` | `number` | `9091` | gRPC-Web port |
245
+ | `pprofPort` | `number` | `6060` | pprof port |
246
+
247
+ ### Instance options (second argument)
248
+
249
+ | Option | Type | Default | Description |
250
+ | --------------- | -------- | ------- | ---------------------------------- |
251
+ | `messageBuffer` | `number` | `20` | Max messages to store in-memory |
252
+ | `timeout` | `number` | `60000` | Start/stop timeout in milliseconds |
253
+
254
+ ```ts
255
+ const instance = Instance.wasmd({ chainId: 'test-1' }, { timeout: 30_000 });
256
+ ```
257
+
258
+ ### Instance methods
259
+
260
+ | Method | Description |
261
+ | --------------------- | ---------------------------------------------------------------------- |
262
+ | `start()` | Start the instance. Returns a stop function. |
263
+ | `stop()` | Stop the instance and cleanup temp directory. |
264
+ | `restart()` | Stop then start. |
265
+ | `on(event, handler)` | Listen to events (`message`, `stdout`, `stderr`, `listening`, `exit`). |
266
+ | `off(event, handler)` | Remove event listener. |
267
+
268
+ ### Instance properties
269
+
270
+ | Property | Type | Description |
271
+ | ---------- | -------- | ----------------------------------------------------------------------- |
272
+ | `status` | `string` | `idle` / `starting` / `started` / `stopping` / `stopped` / `restarting` |
273
+ | `host` | `string` | Host (default: `localhost`) |
274
+ | `port` | `number` | RPC port |
275
+ | `name` | `string` | Instance name |
276
+ | `messages` | `object` | `.get()` returns buffered messages, `.clear()` clears them |
277
+
278
+ ### URL getters
279
+
280
+ Cosmos instances (`simd`, `wasmd`, `xplad`, `evmd`, and anything built on
281
+ `cosmosBase`) also expose ready-to-use endpoint URLs — no more manually
282
+ templating `http://localhost:${instance.port}`:
283
+
284
+ | Property | Type | Description |
285
+ | --------- | -------- | ---------------------------------------------------------|
286
+ | `rpcUrl` | `string` | `http://{host}:{port}` — CometBFT RPC endpoint |
287
+ | `grpcUrl` | `string` | `http://{host}:{grpcPort}` — gRPC endpoint |
288
+ | `apiUrl` | `string` | `http://{host}:{apiPort}` — REST (Cosmos SDK API) endpoint |
289
+ | `evmUrl` | `string` | `http://{host}:{evmPort}` — EVM JSON-RPC endpoint (EVM instances only, e.g. `xplad`, `evmd`) |
290
+
291
+ ```ts
292
+ const client = await StargateClient.connect(instance.rpcUrl);
293
+ ```
294
+
295
+ ## Testing strategies
296
+
297
+ | Strategy | Isolation | Speed | Use case |
298
+ | ------------------------ | --------- | --------- | ------------------------------------------- |
299
+ | **Account isolation** | Practical | Fast | Most tests — each test uses unique accounts |
300
+ | **Suite-level instance** | Full | ~5s setup | Tests that modify chain-wide state |
301
+ | **Shared instance** | None | Fastest | Read-only queries, smoke tests |
302
+
303
+ Recommended: fund multiple accounts in genesis, assign each test its own account(s).
304
+
305
+ ## Why not Starship?
306
+
307
+ | | Starship | starskiff |
308
+ | ------------ | -------------------------- | -------------------- |
309
+ | Infra | Kubernetes + Helm + Docker | None (child process) |
310
+ | Startup | 2-5 min | 3-5 sec |
311
+ | Dependencies | K8s cluster | Go binary |
312
+ | State reset | Helm redeploy (minutes) | kill + restart (~3s) |
313
+ | Best for | Production simulation | Dev/test |
314
+
315
+ ## TODO
316
+
317
+ - [ ] `cosmosEvmBase` — EVM JSON-RPC port support for EVM-enabled chains (e.g. xpla, evmos)
318
+ - [ ] `starskiff.config.ts` — config-based chain + relayer declaration
319
+ - [ ] `starskiff/vitest` — vitest plugin (automatic setup/teardown via `vitestPlugin(config)`)
320
+ - [ ] `starskiff/playwright` — playwright plugin (`playwrightPlugin(config)`)
321
+ - [ ] Automatic port allocation (avoid port conflicts in parallel tests)
322
+ - [x] `findFreePorts()` utility for direct `Instance` users
323
+ - [ ] `starskiff/setup-binaries` GitHub Action for CI binary setup
324
+
325
+ ## License
326
+
327
+ MIT
@@ -0,0 +1,89 @@
1
+ import { type Emitter } from 'mitt';
2
+ import type { EventTypes } from './process.js';
3
+ export { simd } from './instances/simd.js';
4
+ export { wasmd } from './instances/wasmd.js';
5
+ export { gaiad } from './instances/gaiad.js';
6
+ export { xplad } from './instances/xplad.js';
7
+ export { evmd } from './instances/evmd.js';
8
+ export { hermes } from './instances/hermes.js';
9
+ export type InstanceStatus = 'idle' | 'starting' | 'started' | 'stopping' | 'stopped' | 'restarting';
10
+ /** A managed instance with lifecycle control and event emitting. */
11
+ export type Instance = {
12
+ /** Host the instance is running on. */
13
+ host: string;
14
+ /** Name of the instance (e.g. "simd"). */
15
+ name: string;
16
+ /** RPC port the instance is listening on. */
17
+ port: number;
18
+ /** Current lifecycle status. */
19
+ status: InstanceStatus;
20
+ /** In-memory message buffer for debugging. */
21
+ messages: {
22
+ /** Clear all buffered messages. */
23
+ clear(): void;
24
+ /** Get all buffered messages. */
25
+ get(): string[];
26
+ };
27
+ /** Start the instance. Returns a stop function. */
28
+ start(): Promise<() => void>;
29
+ /** Stop the instance and clean up resources. */
30
+ stop(): Promise<void>;
31
+ /** Stop then start the instance. */
32
+ restart(): Promise<void>;
33
+ /** Subscribe to instance events (message, stdout, stderr, listening, exit). */
34
+ on: Emitter<EventTypes>['on'];
35
+ /** Unsubscribe from instance events. */
36
+ off: Emitter<EventTypes>['off'];
37
+ };
38
+ export type InstanceOptions = {
39
+ /** Number of messages to store in-memory. @default 20 */
40
+ messageBuffer?: number;
41
+ /** Timeout (in milliseconds) for starting and stopping. @default 60_000 */
42
+ timeout?: number;
43
+ };
44
+ export type InstanceStartOptions = {
45
+ port?: number | undefined;
46
+ };
47
+ export type InstanceStartContext = {
48
+ emitter: Emitter<EventTypes>;
49
+ setEndpoint?(endpoint: {
50
+ host?: string;
51
+ port?: number;
52
+ }): void;
53
+ status: InstanceStatus;
54
+ };
55
+ export type InstanceStopContext = {
56
+ emitter: Emitter<EventTypes>;
57
+ status: InstanceStatus;
58
+ };
59
+ type DefineFnResult = {
60
+ name: string;
61
+ host: string;
62
+ port: number;
63
+ start(options: InstanceStartOptions, context: InstanceStartContext): Promise<void>;
64
+ stop(context: InstanceStopContext): Promise<void>;
65
+ };
66
+ /**
67
+ * Creates an instance definition.
68
+ *
69
+ * Takes a factory function that returns the instance's name, host, port,
70
+ * and start/stop implementations. Returns a callable that creates
71
+ * managed instances with lifecycle control.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const simd = Instance.define((params?: { chainId?: string }) => ({
76
+ * name: 'simd',
77
+ * host: 'localhost',
78
+ * port: 26657,
79
+ * async start(opts, ctx) { ... },
80
+ * async stop(ctx) { ... },
81
+ * }))
82
+ *
83
+ * const instance = simd({ chainId: 'test-1' })
84
+ * instance.chainId // string — extra field preserved
85
+ * await instance.start()
86
+ * ```
87
+ */
88
+ export declare function define<P = undefined, R extends DefineFnResult = DefineFnResult>(fn: (parameters: P) => R): (...args: P extends undefined ? [options?: InstanceOptions] : [parameters: P, options?: InstanceOptions]) => Omit<R, keyof DefineFnResult> & Instance;
89
+ //# sourceMappingURL=Instance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Instance.d.ts","sourceRoot":"","sources":["../src/Instance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,206 @@
1
+ import mitt, {} from 'mitt';
2
+ export { simd } from './instances/simd.js';
3
+ export { wasmd } from './instances/wasmd.js';
4
+ export { gaiad } from './instances/gaiad.js';
5
+ export { xplad } from './instances/xplad.js';
6
+ export { evmd } from './instances/evmd.js';
7
+ export { hermes } from './instances/hermes.js';
8
+ /**
9
+ * Creates an instance definition.
10
+ *
11
+ * Takes a factory function that returns the instance's name, host, port,
12
+ * and start/stop implementations. Returns a callable that creates
13
+ * managed instances with lifecycle control.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const simd = Instance.define((params?: { chainId?: string }) => ({
18
+ * name: 'simd',
19
+ * host: 'localhost',
20
+ * port: 26657,
21
+ * async start(opts, ctx) { ... },
22
+ * async stop(ctx) { ... },
23
+ * }))
24
+ *
25
+ * const instance = simd({ chainId: 'test-1' })
26
+ * instance.chainId // string — extra field preserved
27
+ * await instance.start()
28
+ * ```
29
+ */
30
+ export function define(fn) {
31
+ return (...[parametersOrOptions, options_]) => {
32
+ const isInstanceOptions = (v) => v != null && typeof v === 'object' && ('messageBuffer' in v || 'timeout' in v);
33
+ // When P = undefined: (options?) → first arg is options
34
+ // When P is defined: (params, options?) → first arg is params, second is options
35
+ const parameters = (options_ !== undefined || !isInstanceOptions(parametersOrOptions)
36
+ ? parametersOrOptions
37
+ : undefined);
38
+ const options = options_ ?? (isInstanceOptions(parametersOrOptions) ? parametersOrOptions : {});
39
+ const raw = fn(parameters);
40
+ const { name, start, stop } = raw;
41
+ let host = raw.host;
42
+ let port = raw.port;
43
+ const { messageBuffer = 20, timeout = 60_000 } = options;
44
+ let startResolver = Promise.withResolvers();
45
+ let stopResolver = Promise.withResolvers();
46
+ let restartResolver = Promise.withResolvers();
47
+ const emitter = mitt();
48
+ let messages = [];
49
+ let status = 'idle';
50
+ let restarting = false;
51
+ const onMessage = (message) => {
52
+ messages.push(message);
53
+ if (messages.length > messageBuffer)
54
+ messages.shift();
55
+ };
56
+ const onListening = () => { status = 'started'; };
57
+ const onExit = () => { status = 'stopped'; };
58
+ const self = {
59
+ get host() { return host; },
60
+ name,
61
+ get port() { return port; },
62
+ get status() {
63
+ if (restarting)
64
+ return 'restarting';
65
+ return status;
66
+ },
67
+ messages: {
68
+ clear() { messages = []; },
69
+ get() { return [...messages]; },
70
+ },
71
+ async start() {
72
+ if (status === 'starting')
73
+ return startResolver.promise;
74
+ if (status !== 'idle' && status !== 'stopped')
75
+ throw new Error(`Instance "${name}" is not in an idle or stopped state. Status: ${status}`);
76
+ let startTimer;
77
+ // Guards the in-flight start(...) call's .then/.catch below: once the
78
+ // timeout fires, status/resolvers have already been reset (and a
79
+ // retry may be in flight), so a late settle must not touch them.
80
+ let timedOut = false;
81
+ if (typeof timeout === 'number') {
82
+ startTimer = setTimeout(() => {
83
+ timedOut = true;
84
+ status = 'idle';
85
+ self.messages.clear();
86
+ emitter.off('message', onMessage);
87
+ emitter.off('listening', onListening);
88
+ emitter.off('exit', onExit);
89
+ startResolver.reject(new Error(`Instance "${name}" failed to start in time.`));
90
+ startResolver = Promise.withResolvers();
91
+ // Best-effort: the child may still be running post-timeout, so
92
+ // ask the underlying stop to tear it down. Never let this throw —
93
+ // the instance must remain retryable regardless of the outcome.
94
+ try {
95
+ void stop({ emitter, status: self.status }).catch(() => { });
96
+ }
97
+ catch {
98
+ // ignore
99
+ }
100
+ }, timeout);
101
+ }
102
+ emitter.on('message', onMessage);
103
+ emitter.on('listening', onListening);
104
+ emitter.on('exit', onExit);
105
+ status = 'starting';
106
+ start({ port }, {
107
+ emitter,
108
+ setEndpoint(endpoint) {
109
+ if (endpoint.host)
110
+ host = endpoint.host;
111
+ if (endpoint.port)
112
+ port = endpoint.port;
113
+ },
114
+ status: self.status,
115
+ })
116
+ .then(() => {
117
+ if (startTimer)
118
+ clearTimeout(startTimer);
119
+ if (timedOut)
120
+ return;
121
+ status = 'started';
122
+ stopResolver = Promise.withResolvers();
123
+ startResolver.resolve(self.stop.bind(self));
124
+ })
125
+ .catch((error) => {
126
+ if (startTimer)
127
+ clearTimeout(startTimer);
128
+ if (timedOut)
129
+ return;
130
+ status = 'idle';
131
+ self.messages.clear();
132
+ emitter.off('message', onMessage);
133
+ emitter.off('listening', onListening);
134
+ emitter.off('exit', onExit);
135
+ startResolver.reject(error);
136
+ startResolver = Promise.withResolvers();
137
+ });
138
+ return startResolver.promise;
139
+ },
140
+ async stop() {
141
+ if (status === 'stopping')
142
+ return stopResolver.promise;
143
+ if (status === 'starting')
144
+ throw new Error(`Instance "${name}" is starting.`);
145
+ let stopTimer;
146
+ if (typeof timeout === 'number') {
147
+ stopTimer = setTimeout(() => {
148
+ stopResolver.reject(new Error(`Instance "${name}" failed to stop in time.`));
149
+ }, timeout);
150
+ }
151
+ status = 'stopping';
152
+ stop({
153
+ emitter,
154
+ status: self.status,
155
+ })
156
+ .then((...args) => {
157
+ if (stopTimer)
158
+ clearTimeout(stopTimer);
159
+ status = 'stopped';
160
+ self.messages.clear();
161
+ emitter.off('message', onMessage);
162
+ emitter.off('listening', onListening);
163
+ emitter.off('exit', onExit);
164
+ startResolver = Promise.withResolvers();
165
+ stopResolver.resolve(...args);
166
+ })
167
+ .catch((error) => {
168
+ if (stopTimer)
169
+ clearTimeout(stopTimer);
170
+ status = 'started';
171
+ stopResolver.reject(error);
172
+ stopResolver = Promise.withResolvers();
173
+ });
174
+ return stopResolver.promise;
175
+ },
176
+ async restart() {
177
+ if (restarting)
178
+ return restartResolver.promise;
179
+ restarting = true;
180
+ self.stop()
181
+ .then(() => self.start())
182
+ .then(() => {
183
+ restarting = false;
184
+ restartResolver.resolve();
185
+ restartResolver = Promise.withResolvers();
186
+ })
187
+ .catch((error) => {
188
+ restarting = false;
189
+ restartResolver.reject(error);
190
+ restartResolver = Promise.withResolvers();
191
+ });
192
+ return restartResolver.promise;
193
+ },
194
+ on: emitter.on.bind(emitter),
195
+ off: emitter.off.bind(emitter),
196
+ };
197
+ const knownKeys = new Set(['name', 'host', 'port', 'start', 'stop']);
198
+ const extra = {};
199
+ for (const key of Object.keys(raw)) {
200
+ if (!knownKeys.has(key))
201
+ extra[key] = raw[key];
202
+ }
203
+ return Object.assign(self, extra);
204
+ };
205
+ }
206
+ //# sourceMappingURL=Instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Instance.js","sourceRoot":"","sources":["../src/Instance.ts"],"names":[],"mappings":"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"}