@ariestools/chain-serve-local-s3 0.1.15

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/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # @ariestools/chain-serve-local-s3
2
+
3
+ > **Dev/test only.** Boots an ephemeral local XL1 environment for integration
4
+ > and e2e tests. Not for production.
5
+
6
+ One function call starts:
7
+
8
+ 1. **`@ariestools/aries-chain-serve`** — S3-compatible published-chain layout
9
+ (`/blocks`, `/state`, `/indexes`) via s3rver
10
+ 2. **`@xyo-network/xl1-cli`** — `api` + `producer` + `finalizer` + `mempool` +
11
+ `indexer`, publishing finalized blocks and index into that layout
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ pnpm add -D @ariestools/chain-serve-local-s3
17
+ ```
18
+
19
+ Pulls in `@ariestools/aries-chain-serve`, `@xyo-network/xl1-cli`, and
20
+ `@xyo-network/xl1-sdk` as runtime dependencies.
21
+
22
+ ## Single-call usage
23
+
24
+ ```ts
25
+ import { startLocalS3Chain } from '@ariestools/chain-serve-local-s3'
26
+
27
+ const chain = await startLocalS3Chain()
28
+
29
+ // chain.rpcUrl http://127.0.0.1:<port>/rpc
30
+ // chain.apiPort
31
+ // chain.chainId
32
+ // chain.blockProducerWallet // path 0 of the insecure test mnemonic
33
+ // chain.s3Endpoint s3rver origin
34
+ // chain.s3ReadUrls { finalized, chainState, index }
35
+
36
+ await chain.stop()
37
+ ```
38
+
39
+ ## Vitest setupFile (lifehash-style globals)
40
+
41
+ ```ts
42
+ // test/setup/api-local-s3.ts
43
+ import { afterAll, beforeAll } from 'vitest'
44
+ import { registerLocalS3Chain } from '@ariestools/chain-serve-local-s3'
45
+
46
+ registerLocalS3Chain(
47
+ { beforeAll, afterAll },
48
+ { assignGlobals: true },
49
+ )
50
+ ```
51
+
52
+ With `assignGlobals: true`, specs can read:
53
+
54
+ | Global | Description |
55
+ | -------------------- | ------------------------------------------------ |
56
+ | `apiPort` | XL1 API port |
57
+ | `rpcUrl` | `http://127.0.0.1:<port>/rpc` |
58
+ | `chainId` | Local chain id |
59
+ | `blockProducerWallet`| Genesis / producer path-0 account |
60
+ | `s3ReadUrls` | `{ finalized, chainState, index }` read base URLs|
61
+ | `s3Endpoint` | s3rver base URL |
62
+
63
+ ## Options
64
+
65
+ | Option | Default |
66
+ | ----------------- | -------------------------------------------- |
67
+ | `apiPort` | `8080 + VITEST_WORKER_ID` (or free fallback) |
68
+ | `chainId` | local-dev fixture id |
69
+ | `mnemonic` | insecure `test test … junk` phrase |
70
+ | `healthTimeoutMs` | `120_000` |
71
+ | `assignGlobals` | `false` |
72
+ | `silent` | `true` (s3rver) |
73
+ | `logXl1` | `true` (child logs → stderr) |
74
+
75
+ ## Notes
76
+
77
+ - The local API serves `/rpc` only (no REST datalake). Writers typically run in
78
+ inline mode; body resolution on the read side is up to the test.
79
+ - S3 credentials accepted by the layout: `S3RVER` / `S3RVER` (exported as
80
+ `S3_CREDENTIALS`).
81
+ - Parallel vitest workers get distinct preferred API ports via
82
+ `VITEST_WORKER_ID`; Prometheus and health-check ports are disabled in the
83
+ child config to avoid clashes.
@@ -0,0 +1,10 @@
1
+ import type { LocalS3Chain } from './types.ts';
2
+ /**
3
+ * Keys written to `globalThis` when `assignGlobals: true`.
4
+ * Matches the lifehash / chain-test vitest setupFile convention.
5
+ */
6
+ export declare const LOCAL_S3_CHAIN_GLOBAL_KEYS: readonly ["apiPort", "rpcUrl", "chainId", "blockProducerWallet", "s3ReadUrls", "s3Endpoint"];
7
+ export type LocalS3ChainGlobalKey = typeof LOCAL_S3_CHAIN_GLOBAL_KEYS[number];
8
+ /** Assign chain harness fields onto `globalThis` for vitest e2e specs. */
9
+ export declare function assignLocalS3ChainGlobals(chain: LocalS3Chain): void;
10
+ //# sourceMappingURL=assignGlobals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignGlobals.d.ts","sourceRoot":"","sources":["../../src/assignGlobals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAQ9C;;;GAGG;AACH,eAAO,MAAM,0BAA0B,8FAO7B,CAAA;AAEV,MAAM,MAAM,qBAAqB,GAAG,OAAO,0BAA0B,CAAC,MAAM,CAAC,CAAA;AAE7E,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAOnE"}
@@ -0,0 +1,15 @@
1
+ export interface BuildXl1ConfigOptions {
2
+ apiPort: number;
3
+ baseUrl: string;
4
+ chainId: string;
5
+ genesisRewardAddress: string;
6
+ /** Port 0 disables Prometheus scrape (avoids default 9465 clashes). */
7
+ metricsPort?: number;
8
+ s3Endpoint: string;
9
+ }
10
+ /**
11
+ * Memory authority store + S3 publish targets for the published REST layout.
12
+ * Section key is `xl1` (xl1-cli config file shape).
13
+ */
14
+ export declare function buildXl1Config(options: BuildXl1ConfigOptions): Record<string, unknown>;
15
+ //# sourceMappingURL=buildXl1Config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildXl1Config.d.ts","sourceRoot":"","sources":["../../src/buildXl1Config.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,oBAAoB,EAAE,MAAM,CAAA;IAC5B,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAwEtF"}
@@ -0,0 +1,24 @@
1
+ /** Built-in insecure genesis mnemonic — same phrase xl1-cli uses for the
2
+ * genesis reward wallet when no production mnemonic is configured. */
3
+ export declare const GENESIS_MNEMONIC = "test test test test test test test test test test test junk";
4
+ /** Default local-dev chain id (matches xl1/aries fixtures). */
5
+ export declare const DEFAULT_CHAIN_ID = "1111111111111111111111111111111111111111";
6
+ /** Credentials s3rver accepts for any signed S3 API call. */
7
+ export declare const S3_CREDENTIALS: {
8
+ readonly accessKeyId: "S3RVER";
9
+ readonly accountId: "test";
10
+ readonly secretAccessKey: "S3RVER";
11
+ };
12
+ /**
13
+ * aries-chain-serve bucket names (path-style: /blocks, /state, /indexes),
14
+ * mapped to the connection roles used in the xl1-cli config.
15
+ */
16
+ export declare const CHAIN_BUCKETS: {
17
+ readonly chainState: "state";
18
+ readonly finalized: "blocks";
19
+ readonly index: "indexes";
20
+ };
21
+ export declare const DEFAULT_HEALTH_TIMEOUT_MS = 120000;
22
+ export declare const DEFAULT_HEALTH_POLL_MS = 500;
23
+ export declare const DEFAULT_API_PORT_BASE = 8080;
24
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;uEACuE;AACvE,eAAO,MAAM,gBAAgB,gEAAgE,CAAA;AAE7F,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,6CAA6C,CAAA;AAE1E,6DAA6D;AAC7D,eAAO,MAAM,cAAc;;;;CAIjB,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;CAIhB,CAAA;AAEV,eAAO,MAAM,yBAAyB,SAAU,CAAA;AAChD,eAAO,MAAM,sBAAsB,MAAM,CAAA;AACzC,eAAO,MAAM,qBAAqB,OAAO,CAAA"}
@@ -0,0 +1,12 @@
1
+ export type { LocalS3ChainGlobalKey } from './assignGlobals.ts';
2
+ export { assignLocalS3ChainGlobals, LOCAL_S3_CHAIN_GLOBAL_KEYS } from './assignGlobals.ts';
3
+ export type { BuildXl1ConfigOptions } from './buildXl1Config.ts';
4
+ export { buildXl1Config } from './buildXl1Config.ts';
5
+ export { CHAIN_BUCKETS, DEFAULT_API_PORT_BASE, DEFAULT_CHAIN_ID, DEFAULT_HEALTH_POLL_MS, DEFAULT_HEALTH_TIMEOUT_MS, GENESIS_MNEMONIC, S3_CREDENTIALS, } from './constants.ts';
6
+ export { findAvailablePort } from './ports.ts';
7
+ export type { RegisterLocalS3ChainOptions, TestLifecycleHooks } from './registerLocalS3Chain.ts';
8
+ export { registerLocalS3Chain } from './registerLocalS3Chain.ts';
9
+ export { resolveXl1Bin } from './resolveXl1Bin.ts';
10
+ export type { LocalS3Chain, S3ReadUrls, StartLocalS3ChainOptions, } from './startLocalS3Chain.ts';
11
+ export { startLocalS3Chain } from './startLocalS3Chain.ts';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAA;AAC1F,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,gBAAgB,EAChB,cAAc,GACf,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC9C,YAAY,EAAE,2BAA2B,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EACV,YAAY,EAAE,UAAU,EAAE,wBAAwB,GACnD,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA"}
@@ -0,0 +1,439 @@
1
+ // src/assignGlobals.ts
2
+ function assignGlobal(key, value) {
3
+ Object.defineProperty(globalThis, key, {
4
+ configurable: true,
5
+ enumerable: true,
6
+ value,
7
+ writable: true
8
+ });
9
+ }
10
+ var LOCAL_S3_CHAIN_GLOBAL_KEYS = [
11
+ "apiPort",
12
+ "rpcUrl",
13
+ "chainId",
14
+ "blockProducerWallet",
15
+ "s3ReadUrls",
16
+ "s3Endpoint"
17
+ ];
18
+ function assignLocalS3ChainGlobals(chain) {
19
+ assignGlobal("apiPort", chain.apiPort);
20
+ assignGlobal("rpcUrl", chain.rpcUrl);
21
+ assignGlobal("chainId", chain.chainId);
22
+ assignGlobal("blockProducerWallet", chain.blockProducerWallet);
23
+ assignGlobal("s3ReadUrls", chain.s3ReadUrls);
24
+ assignGlobal("s3Endpoint", chain.s3Endpoint);
25
+ }
26
+
27
+ // src/constants.ts
28
+ var GENESIS_MNEMONIC = "test test test test test test test test test test test junk";
29
+ var DEFAULT_CHAIN_ID = "1111111111111111111111111111111111111111";
30
+ var S3_CREDENTIALS = {
31
+ accessKeyId: "S3RVER",
32
+ accountId: "test",
33
+ secretAccessKey: "S3RVER"
34
+ };
35
+ var CHAIN_BUCKETS = {
36
+ chainState: "state",
37
+ finalized: "blocks",
38
+ index: "indexes"
39
+ };
40
+ var DEFAULT_HEALTH_TIMEOUT_MS = 12e4;
41
+ var DEFAULT_HEALTH_POLL_MS = 500;
42
+ var DEFAULT_API_PORT_BASE = 8080;
43
+
44
+ // src/buildXl1Config.ts
45
+ function buildS3Connection(endpoint, bucket, readUrl) {
46
+ return {
47
+ type: "s3",
48
+ accessKeyId: S3_CREDENTIALS.accessKeyId,
49
+ accountId: S3_CREDENTIALS.accountId,
50
+ secretAccessKey: S3_CREDENTIALS.secretAccessKey,
51
+ bucket,
52
+ endpoint,
53
+ readUrl
54
+ };
55
+ }
56
+ function buildXl1Config(options) {
57
+ const metricsPort = options.metricsPort ?? 0;
58
+ const {
59
+ apiPort,
60
+ baseUrl,
61
+ chainId,
62
+ genesisRewardAddress,
63
+ s3Endpoint
64
+ } = options;
65
+ return {
66
+ xl1: {
67
+ chain: { id: chainId, genesisRewardAddress },
68
+ // Port 0 disables the HTTP system-status publisher (default 9099) so
69
+ // parallel vitest files / leftover nodes cannot collide on it.
70
+ healthCheckPort: 0,
71
+ // Explicit free metrics scrape port (port 0 disables Prometheus entirely
72
+ // in xl1 telemetry). Avoids the default 9465 clash when multiple xl1
73
+ // children start together.
74
+ telemetry: { metrics: { scrape: { port: metricsPort } } },
75
+ connections: {
76
+ "local-store": { type: "memory" },
77
+ "s3-finalized": buildS3Connection(s3Endpoint, CHAIN_BUCKETS.finalized, `${baseUrl}/${CHAIN_BUCKETS.finalized}`),
78
+ "s3-chain-state": buildS3Connection(s3Endpoint, CHAIN_BUCKETS.chainState, `${baseUrl}/${CHAIN_BUCKETS.chainState}`),
79
+ "s3-index": buildS3Connection(s3Endpoint, CHAIN_BUCKETS.index, `${baseUrl}/${CHAIN_BUCKETS.index}`)
80
+ },
81
+ providerBindings: {
82
+ // Authority / write path — in-memory single process.
83
+ BlockRunner: { connection: "local-store" },
84
+ BlockValidationViewer: { connection: "local-store" },
85
+ BlockViewer: { connection: "local-store" },
86
+ DeadLetterQueueRunner: { connection: "local-store" },
87
+ DeadLetterQueueViewer: { connection: "local-store" },
88
+ FinalizationRunner: { connection: "local-store" },
89
+ FinalizationViewer: { connection: "local-store" },
90
+ MempoolRunner: { connection: "local-store" },
91
+ MempoolViewer: { connection: "local-store" },
92
+ TransactionValidationViewer: { connection: "local-store" },
93
+ WindowedBlockViewer: { connection: "local-store" },
94
+ XyoRunner: { connection: "local-store" },
95
+ // Published REST layout writers + chain-state gate for the indexer.
96
+ BlockPublishRunner: { connection: "s3-finalized" },
97
+ ChainStatePublishRunner: { connection: "s3-chain-state" },
98
+ IndexPublishRunner: { connection: "s3-index" },
99
+ ChainStateViewer: { connection: "s3-chain-state" },
100
+ // Dev placeholder — no real EVM staking contract in this harness.
101
+ ChainContractViewer: { provider: "SimpleChainContractViewer" }
102
+ },
103
+ actors: [
104
+ {
105
+ name: "api",
106
+ host: "127.0.0.1",
107
+ port: apiPort
108
+ },
109
+ {
110
+ name: "producer",
111
+ accountPath: "0",
112
+ // Keep production under s3rver publish catch-up capacity.
113
+ blockProductionCheckInterval: 250,
114
+ heartbeatInterval: 1e3
115
+ },
116
+ {
117
+ name: "finalizer",
118
+ accountPath: "5",
119
+ finalizationCheckInterval: 100,
120
+ minCandidates: 1,
121
+ publishOnFinalize: true,
122
+ publishContentEncoding: "none"
123
+ },
124
+ { name: "mempool" },
125
+ {
126
+ name: "indexer",
127
+ accountPath: "6",
128
+ publishSyncInterval: 250,
129
+ publishContentEncoding: "none"
130
+ }
131
+ ]
132
+ }
133
+ };
134
+ }
135
+
136
+ // src/ports.ts
137
+ import net from "node:net";
138
+ function isPortAvailable(port) {
139
+ return new Promise((resolve) => {
140
+ const server = net.createServer();
141
+ server.once("error", () => resolve(false));
142
+ server.listen(port, () => {
143
+ server.close(() => resolve(true));
144
+ });
145
+ });
146
+ }
147
+ async function getFreePort() {
148
+ return await new Promise((resolve, reject) => {
149
+ const server = net.createServer();
150
+ server.once("error", reject);
151
+ server.listen(0, "127.0.0.1", () => {
152
+ const address = server.address();
153
+ if (address === null || typeof address === "string") {
154
+ server.close(() => reject(new Error("getFreePort: no address")));
155
+ return;
156
+ }
157
+ const { port } = address;
158
+ server.close((error) => error ? reject(error) : resolve(port));
159
+ });
160
+ });
161
+ }
162
+ async function findAvailablePort(preferred) {
163
+ if (await isPortAvailable(preferred)) return preferred;
164
+ return await getFreePort();
165
+ }
166
+
167
+ // src/startLocalS3Chain.ts
168
+ import { createMemoryBacking, startChainServer } from "@ariestools/aries-chain-serve";
169
+ import { generateXyoBaseWalletFromPhrase } from "@xyo-network/xl1-sdk/protocol-sdk";
170
+
171
+ // src/startXl1.ts
172
+ import { spawn } from "node:child_process";
173
+ import FS from "node:fs/promises";
174
+ import OS from "node:os";
175
+ import PATH2 from "node:path";
176
+ import { setTimeout as delay } from "node:timers/promises";
177
+
178
+ // src/resolveXl1Bin.ts
179
+ import { createRequire } from "node:module";
180
+ import PATH from "node:path";
181
+ import xl1CliPkg from "@xyo-network/xl1-cli/package.json" with { type: "json" };
182
+ function binFromPkg(pkgPath, pkg) {
183
+ const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.xl1;
184
+ if (!binEntry) throw new Error('@xyo-network/xl1-cli does not declare a "xl1" bin');
185
+ return PATH.resolve(PATH.dirname(pkgPath), binEntry);
186
+ }
187
+ function resolveXl1Bin() {
188
+ if (xl1CliPkg.name !== "@xyo-network/xl1-cli") {
189
+ throw new Error("@ariestools/chain-serve-local-s3: unexpected @xyo-network/xl1-cli package name");
190
+ }
191
+ const localRequire = createRequire(import.meta.url);
192
+ try {
193
+ const pkgPath = localRequire.resolve("@xyo-network/xl1-cli/package.json");
194
+ const pkg = localRequire("@xyo-network/xl1-cli/package.json");
195
+ return binFromPkg(pkgPath, pkg);
196
+ } catch {
197
+ }
198
+ const cwdPkg = PATH.join(process.cwd(), "package.json");
199
+ const cwdRequire = createRequire(cwdPkg);
200
+ try {
201
+ const pkgPath = cwdRequire.resolve("@xyo-network/xl1-cli/package.json");
202
+ const pkg = cwdRequire("@xyo-network/xl1-cli/package.json");
203
+ return binFromPkg(pkgPath, pkg);
204
+ } catch (error) {
205
+ throw new Error(
206
+ "@ariestools/chain-serve-local-s3: @xyo-network/xl1-cli is not installed. It is a runtime dependency of this package \u2014 reinstall node_modules.",
207
+ { cause: error }
208
+ );
209
+ }
210
+ }
211
+
212
+ // src/startXl1.ts
213
+ async function waitForApi(rpcUrl, child, healthTimeoutMs, healthPollMs) {
214
+ const deadline = Date.now() + healthTimeoutMs;
215
+ const healthUrl = rpcUrl.replace(/\/rpc$/, "/");
216
+ while (Date.now() < deadline) {
217
+ if (child.exitCode !== null) {
218
+ throw new Error(
219
+ `@ariestools/chain-serve-local-s3: xl1 exited early (code=${child.exitCode}) before API became reachable on ${rpcUrl}`
220
+ );
221
+ }
222
+ try {
223
+ const response = await fetch(healthUrl, { method: "GET" });
224
+ if (response.status > 0) return;
225
+ } catch {
226
+ }
227
+ await delay(healthPollMs);
228
+ }
229
+ throw new Error(
230
+ `@ariestools/chain-serve-local-s3: xl1 API did not become reachable on ${rpcUrl} within ${healthTimeoutMs}ms`
231
+ );
232
+ }
233
+ async function startXl1(options) {
234
+ const {
235
+ apiPort,
236
+ chainId,
237
+ genesisRewardAddress,
238
+ healthPollMs = DEFAULT_HEALTH_POLL_MS,
239
+ healthTimeoutMs = DEFAULT_HEALTH_TIMEOUT_MS,
240
+ logXl1 = true,
241
+ mnemonic = GENESIS_MNEMONIC,
242
+ s3
243
+ } = options;
244
+ const rpcUrl = `http://127.0.0.1:${apiPort}/rpc`;
245
+ const metricsPort = 0;
246
+ const configDir = await FS.mkdtemp(PATH2.join(OS.tmpdir(), "chain-serve-local-s3-"));
247
+ const configPath = PATH2.join(configDir, "xyo.json");
248
+ await FS.writeFile(
249
+ configPath,
250
+ `${JSON.stringify(buildXl1Config({
251
+ apiPort,
252
+ baseUrl: s3.baseUrl,
253
+ chainId,
254
+ genesisRewardAddress,
255
+ metricsPort,
256
+ s3Endpoint: s3.baseUrl
257
+ }), null, 2)}
258
+ `,
259
+ "utf8"
260
+ );
261
+ const xl1Bin = resolveXl1Bin();
262
+ const child = spawn(
263
+ process.execPath,
264
+ [
265
+ xl1Bin,
266
+ "start",
267
+ "api",
268
+ "producer",
269
+ "finalizer",
270
+ "mempool",
271
+ "indexer",
272
+ "-c",
273
+ configPath,
274
+ "--skip-insecure-confirm"
275
+ ],
276
+ {
277
+ env: {
278
+ ...process.env,
279
+ // Seed the shared root mnemonic so producer path 0 == genesis reward.
280
+ XL1_MNEMONIC: mnemonic
281
+ },
282
+ stdio: ["ignore", "pipe", "pipe"]
283
+ }
284
+ );
285
+ if (logXl1) {
286
+ child.stdout?.on("data", (chunk) => {
287
+ process.stderr.write(`[xl1] ${chunk.toString("utf8")}`);
288
+ });
289
+ child.stderr?.on("data", (chunk) => {
290
+ process.stderr.write(`[xl1!] ${chunk.toString("utf8")}`);
291
+ });
292
+ child.on("exit", (code, signal) => {
293
+ process.stderr.write(`[xl1] exited code=${code} signal=${signal}
294
+ `);
295
+ });
296
+ }
297
+ try {
298
+ await waitForApi(rpcUrl, child, healthTimeoutMs, healthPollMs);
299
+ } catch (error) {
300
+ if (child.exitCode === null) child.kill("SIGTERM");
301
+ try {
302
+ await FS.rm(configDir, { recursive: true, force: true });
303
+ } catch {
304
+ }
305
+ throw error;
306
+ }
307
+ return {
308
+ apiPort,
309
+ chainId,
310
+ child,
311
+ configDir,
312
+ rpcUrl
313
+ };
314
+ }
315
+ async function stopXl1(result) {
316
+ if (!result) return;
317
+ const cleanup = async () => {
318
+ try {
319
+ await FS.rm(result.configDir, { recursive: true, force: true });
320
+ } catch {
321
+ }
322
+ };
323
+ if (result.child.exitCode !== null) {
324
+ await cleanup();
325
+ return;
326
+ }
327
+ const stopped = new Promise((resolve) => {
328
+ result.child.once("exit", () => resolve());
329
+ });
330
+ result.child.kill("SIGTERM");
331
+ await Promise.race([
332
+ stopped,
333
+ delay(1e4).then(() => {
334
+ if (result.child.exitCode === null) result.child.kill("SIGKILL");
335
+ })
336
+ ]);
337
+ await cleanup();
338
+ }
339
+
340
+ // src/startLocalS3Chain.ts
341
+ async function startLocalS3Chain(options = {}) {
342
+ const {
343
+ assignGlobals = false,
344
+ chainId = DEFAULT_CHAIN_ID,
345
+ healthTimeoutMs = DEFAULT_HEALTH_TIMEOUT_MS,
346
+ logXl1 = true,
347
+ mnemonic = GENESIS_MNEMONIC,
348
+ silent = true
349
+ } = options;
350
+ const s3 = await startChainServer({
351
+ backing: createMemoryBacking(),
352
+ port: 0,
353
+ silent
354
+ });
355
+ let xl1;
356
+ try {
357
+ const preferredApi = options.apiPort ?? DEFAULT_API_PORT_BASE + Number(process.env.VITEST_WORKER_ID ?? 0);
358
+ const apiPort = await findAvailablePort(preferredApi);
359
+ const genesisRoot = await generateXyoBaseWalletFromPhrase(mnemonic);
360
+ const genesisWallet = await genesisRoot.derivePath("0");
361
+ xl1 = await startXl1({
362
+ apiPort,
363
+ chainId,
364
+ genesisRewardAddress: genesisWallet.address,
365
+ healthTimeoutMs,
366
+ logXl1,
367
+ mnemonic,
368
+ s3
369
+ });
370
+ const s3ReadUrls = {
371
+ chainState: `${s3.baseUrl}/${CHAIN_BUCKETS.chainState}`,
372
+ finalized: `${s3.baseUrl}/${CHAIN_BUCKETS.finalized}`,
373
+ index: `${s3.baseUrl}/${CHAIN_BUCKETS.index}`
374
+ };
375
+ const chain = {
376
+ apiPort,
377
+ blockProducerWallet: genesisWallet,
378
+ chainId: xl1.chainId,
379
+ rpcUrl: xl1.rpcUrl,
380
+ s3Endpoint: s3.baseUrl,
381
+ s3ReadUrls,
382
+ stop: async () => {
383
+ await stopXl1(xl1);
384
+ xl1 = void 0;
385
+ await s3.close();
386
+ }
387
+ };
388
+ if (assignGlobals) assignLocalS3ChainGlobals(chain);
389
+ if (logXl1) {
390
+ process.stderr.write(
391
+ `chain-serve-local-s3: XL1 on ${chain.rpcUrl}, published layout on ${chain.s3Endpoint}, chain ${chain.chainId}
392
+ `
393
+ );
394
+ }
395
+ return chain;
396
+ } catch (error) {
397
+ await stopXl1(xl1);
398
+ await s3.close();
399
+ throw error;
400
+ }
401
+ }
402
+
403
+ // src/registerLocalS3Chain.ts
404
+ function registerLocalS3Chain(hooks, options = {}) {
405
+ const {
406
+ afterAllTimeoutMs = 6e4,
407
+ beforeAllTimeoutMs,
408
+ healthTimeoutMs = DEFAULT_HEALTH_TIMEOUT_MS,
409
+ ...startOptions
410
+ } = options;
411
+ let chain;
412
+ hooks.beforeAll(async () => {
413
+ chain = await startLocalS3Chain({
414
+ healthTimeoutMs,
415
+ ...startOptions
416
+ });
417
+ }, beforeAllTimeoutMs ?? healthTimeoutMs + 6e4);
418
+ hooks.afterAll(async () => {
419
+ await chain?.stop();
420
+ chain = void 0;
421
+ }, afterAllTimeoutMs);
422
+ }
423
+ export {
424
+ CHAIN_BUCKETS,
425
+ DEFAULT_API_PORT_BASE,
426
+ DEFAULT_CHAIN_ID,
427
+ DEFAULT_HEALTH_POLL_MS,
428
+ DEFAULT_HEALTH_TIMEOUT_MS,
429
+ GENESIS_MNEMONIC,
430
+ LOCAL_S3_CHAIN_GLOBAL_KEYS,
431
+ S3_CREDENTIALS,
432
+ assignLocalS3ChainGlobals,
433
+ buildXl1Config,
434
+ findAvailablePort,
435
+ registerLocalS3Chain,
436
+ resolveXl1Bin,
437
+ startLocalS3Chain
438
+ };
439
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/assignGlobals.ts", "../../src/constants.ts", "../../src/buildXl1Config.ts", "../../src/ports.ts", "../../src/startLocalS3Chain.ts", "../../src/startXl1.ts", "../../src/resolveXl1Bin.ts", "../../src/registerLocalS3Chain.ts"],
4
+ "sourcesContent": ["import type { LocalS3Chain } from './types.ts'\n\nfunction assignGlobal(key: string, value: unknown): void {\n Object.defineProperty(globalThis, key, {\n configurable: true, enumerable: true, value, writable: true,\n })\n}\n\n/**\n * Keys written to `globalThis` when `assignGlobals: true`.\n * Matches the lifehash / chain-test vitest setupFile convention.\n */\nexport const LOCAL_S3_CHAIN_GLOBAL_KEYS = [\n 'apiPort',\n 'rpcUrl',\n 'chainId',\n 'blockProducerWallet',\n 's3ReadUrls',\n 's3Endpoint',\n] as const\n\nexport type LocalS3ChainGlobalKey = typeof LOCAL_S3_CHAIN_GLOBAL_KEYS[number]\n\n/** Assign chain harness fields onto `globalThis` for vitest e2e specs. */\nexport function assignLocalS3ChainGlobals(chain: LocalS3Chain): void {\n assignGlobal('apiPort', chain.apiPort)\n assignGlobal('rpcUrl', chain.rpcUrl)\n assignGlobal('chainId', chain.chainId)\n assignGlobal('blockProducerWallet', chain.blockProducerWallet)\n assignGlobal('s3ReadUrls', chain.s3ReadUrls)\n assignGlobal('s3Endpoint', chain.s3Endpoint)\n}\n", "/** Built-in insecure genesis mnemonic \u2014 same phrase xl1-cli uses for the\n * genesis reward wallet when no production mnemonic is configured. */\nexport const GENESIS_MNEMONIC = 'test test test test test test test test test test test junk'\n\n/** Default local-dev chain id (matches xl1/aries fixtures). */\nexport const DEFAULT_CHAIN_ID = '1111111111111111111111111111111111111111'\n\n/** Credentials s3rver accepts for any signed S3 API call. */\nexport const S3_CREDENTIALS = {\n accessKeyId: 'S3RVER',\n accountId: 'test',\n secretAccessKey: 'S3RVER',\n} as const\n\n/**\n * aries-chain-serve bucket names (path-style: /blocks, /state, /indexes),\n * mapped to the connection roles used in the xl1-cli config.\n */\nexport const CHAIN_BUCKETS = {\n chainState: 'state',\n finalized: 'blocks',\n index: 'indexes',\n} as const\n\nexport const DEFAULT_HEALTH_TIMEOUT_MS = 120_000\nexport const DEFAULT_HEALTH_POLL_MS = 500\nexport const DEFAULT_API_PORT_BASE = 8080\n", "import { CHAIN_BUCKETS, S3_CREDENTIALS } from './constants.ts'\n\nfunction buildS3Connection(endpoint: string, bucket: string, readUrl: string): Record<string, unknown> {\n return {\n type: 's3',\n accessKeyId: S3_CREDENTIALS.accessKeyId,\n accountId: S3_CREDENTIALS.accountId,\n secretAccessKey: S3_CREDENTIALS.secretAccessKey,\n bucket,\n endpoint,\n readUrl,\n }\n}\n\nexport interface BuildXl1ConfigOptions {\n apiPort: number\n baseUrl: string\n chainId: string\n genesisRewardAddress: string\n /** Port 0 disables Prometheus scrape (avoids default 9465 clashes). */\n metricsPort?: number\n s3Endpoint: string\n}\n\n/**\n * Memory authority store + S3 publish targets for the published REST layout.\n * Section key is `xl1` (xl1-cli config file shape).\n */\nexport function buildXl1Config(options: BuildXl1ConfigOptions): Record<string, unknown> {\n const metricsPort = options.metricsPort ?? 0\n const {\n apiPort, baseUrl, chainId, genesisRewardAddress, s3Endpoint,\n } = options\n return {\n xl1: {\n chain: { id: chainId, genesisRewardAddress },\n // Port 0 disables the HTTP system-status publisher (default 9099) so\n // parallel vitest files / leftover nodes cannot collide on it.\n healthCheckPort: 0,\n // Explicit free metrics scrape port (port 0 disables Prometheus entirely\n // in xl1 telemetry). Avoids the default 9465 clash when multiple xl1\n // children start together.\n telemetry: { metrics: { scrape: { port: metricsPort } } },\n connections: {\n 'local-store': { type: 'memory' },\n 's3-finalized': buildS3Connection(s3Endpoint, CHAIN_BUCKETS.finalized, `${baseUrl}/${CHAIN_BUCKETS.finalized}`),\n 's3-chain-state': buildS3Connection(s3Endpoint, CHAIN_BUCKETS.chainState, `${baseUrl}/${CHAIN_BUCKETS.chainState}`),\n 's3-index': buildS3Connection(s3Endpoint, CHAIN_BUCKETS.index, `${baseUrl}/${CHAIN_BUCKETS.index}`),\n },\n providerBindings: {\n // Authority / write path \u2014 in-memory single process.\n BlockRunner: { connection: 'local-store' },\n BlockValidationViewer: { connection: 'local-store' },\n BlockViewer: { connection: 'local-store' },\n DeadLetterQueueRunner: { connection: 'local-store' },\n DeadLetterQueueViewer: { connection: 'local-store' },\n FinalizationRunner: { connection: 'local-store' },\n FinalizationViewer: { connection: 'local-store' },\n MempoolRunner: { connection: 'local-store' },\n MempoolViewer: { connection: 'local-store' },\n TransactionValidationViewer: { connection: 'local-store' },\n WindowedBlockViewer: { connection: 'local-store' },\n XyoRunner: { connection: 'local-store' },\n // Published REST layout writers + chain-state gate for the indexer.\n BlockPublishRunner: { connection: 's3-finalized' },\n ChainStatePublishRunner: { connection: 's3-chain-state' },\n IndexPublishRunner: { connection: 's3-index' },\n ChainStateViewer: { connection: 's3-chain-state' },\n // Dev placeholder \u2014 no real EVM staking contract in this harness.\n ChainContractViewer: { provider: 'SimpleChainContractViewer' },\n },\n actors: [\n {\n name: 'api', host: '127.0.0.1', port: apiPort,\n },\n {\n name: 'producer',\n accountPath: '0',\n // Keep production under s3rver publish catch-up capacity.\n blockProductionCheckInterval: 250,\n heartbeatInterval: 1000,\n },\n {\n name: 'finalizer',\n accountPath: '5',\n finalizationCheckInterval: 100,\n minCandidates: 1,\n publishOnFinalize: true,\n publishContentEncoding: 'none',\n },\n { name: 'mempool' },\n {\n name: 'indexer',\n accountPath: '6',\n publishSyncInterval: 250,\n publishContentEncoding: 'none',\n },\n ],\n },\n }\n}\n", "import net from 'node:net'\n\nfunction isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = net.createServer()\n server.once('error', () => resolve(false))\n server.listen(port, () => {\n server.close(() => resolve(true))\n })\n })\n}\n\nasync function getFreePort(): Promise<number> {\n return await new Promise((resolve, reject) => {\n const server = net.createServer()\n server.once('error', reject)\n server.listen(0, '127.0.0.1', () => {\n const address = server.address()\n if (address === null || typeof address === 'string') {\n server.close(() => reject(new Error('getFreePort: no address')))\n return\n }\n const { port } = address\n server.close(error => (error ? reject(error) : resolve(port)))\n })\n })\n}\n\n/** Prefer `preferred` when free on every interface; otherwise an ephemeral port. */\nexport async function findAvailablePort(preferred: number): Promise<number> {\n if (await isPortAvailable(preferred)) return preferred\n return await getFreePort()\n}\n", "import { createMemoryBacking, startChainServer } from '@ariestools/aries-chain-serve'\nimport { generateXyoBaseWalletFromPhrase } from '@xyo-network/xl1-sdk/protocol-sdk'\n\nimport { assignLocalS3ChainGlobals } from './assignGlobals.ts'\nimport {\n CHAIN_BUCKETS,\n DEFAULT_API_PORT_BASE,\n DEFAULT_CHAIN_ID,\n DEFAULT_HEALTH_TIMEOUT_MS,\n GENESIS_MNEMONIC,\n} from './constants.ts'\nimport { findAvailablePort } from './ports.ts'\nimport type { RunningXl1 } from './startXl1.ts'\nimport { startXl1, stopXl1 } from './startXl1.ts'\nimport type { LocalS3Chain, S3ReadUrls } from './types.ts'\n\nexport type { LocalS3Chain, S3ReadUrls } from './types.ts'\n\nexport interface StartLocalS3ChainOptions {\n /**\n * Preferred API port. Defaults to `8080 + Number(process.env.VITEST_WORKER_ID ?? 0)`.\n * Falls back to an ephemeral port when the preferred port is busy.\n */\n apiPort?: number\n /**\n * When true, also assign `apiPort`, `rpcUrl`, `chainId`, `blockProducerWallet`,\n * `s3ReadUrls`, and `s3Endpoint` on `globalThis` (lifehash-style vitest globals).\n */\n assignGlobals?: boolean\n /** Chain id hex string. Default: {@link DEFAULT_CHAIN_ID}. */\n chainId?: string\n /** API readiness timeout in ms. Default: 120_000. */\n healthTimeoutMs?: number\n /** Pipe xl1 child stdout/stderr to process.stderr. Default: true. */\n logXl1?: boolean\n /**\n * Root mnemonic for xl1 actors and the genesis reward address.\n * Default: insecure test phrase ({@link GENESIS_MNEMONIC}).\n */\n mnemonic?: string\n /** Silence the embedded s3rver logs. Default: true. */\n silent?: boolean\n}\n\n/**\n * Boots a local XL1 test environment in one call:\n *\n * 1. `@ariestools/aries-chain-serve` \u2014 S3-compatible published-chain layout\n * (`/blocks`, `/state`, `/indexes`)\n * 2. `@xyo-network/xl1-cli` \u2014 api + producer + finalizer + mempool + indexer,\n * publishing finalized blocks and index into that layout\n *\n * @example\n * ```ts\n * const chain = await startLocalS3Chain()\n * // chain.rpcUrl, chain.s3ReadUrls, chain.blockProducerWallet, ...\n * await chain.stop()\n * ```\n */\nexport async function startLocalS3Chain(\n options: StartLocalS3ChainOptions = {},\n): Promise<LocalS3Chain> {\n const {\n assignGlobals = false,\n chainId = DEFAULT_CHAIN_ID,\n healthTimeoutMs = DEFAULT_HEALTH_TIMEOUT_MS,\n logXl1 = true,\n mnemonic = GENESIS_MNEMONIC,\n silent = true,\n } = options\n\n const s3 = await startChainServer({\n backing: createMemoryBacking(), port: 0, silent,\n })\n\n let xl1: RunningXl1 | undefined\n try {\n const preferredApi = options.apiPort\n ?? (DEFAULT_API_PORT_BASE + Number(process.env.VITEST_WORKER_ID ?? 0))\n const apiPort = await findAvailablePort(preferredApi)\n\n // Genesis reward wallet = path 0 of the mnemonic (producer path 0).\n const genesisRoot = await generateXyoBaseWalletFromPhrase(mnemonic)\n const genesisWallet = await genesisRoot.derivePath('0')\n\n xl1 = await startXl1({\n apiPort,\n chainId,\n genesisRewardAddress: genesisWallet.address,\n healthTimeoutMs,\n logXl1,\n mnemonic,\n s3,\n })\n\n const s3ReadUrls: S3ReadUrls = {\n chainState: `${s3.baseUrl}/${CHAIN_BUCKETS.chainState}`,\n finalized: `${s3.baseUrl}/${CHAIN_BUCKETS.finalized}`,\n index: `${s3.baseUrl}/${CHAIN_BUCKETS.index}`,\n }\n\n const chain: LocalS3Chain = {\n apiPort,\n blockProducerWallet: genesisWallet,\n chainId: xl1.chainId,\n rpcUrl: xl1.rpcUrl,\n s3Endpoint: s3.baseUrl,\n s3ReadUrls,\n stop: async (): Promise<void> => {\n await stopXl1(xl1)\n xl1 = undefined\n await s3.close()\n },\n }\n\n if (assignGlobals) assignLocalS3ChainGlobals(chain)\n\n if (logXl1) {\n process.stderr.write(\n `chain-serve-local-s3: XL1 on ${chain.rpcUrl}, published layout on ${chain.s3Endpoint}, chain ${chain.chainId}\\n`,\n )\n }\n\n return chain\n } catch (error) {\n await stopXl1(xl1)\n await s3.close()\n throw error\n }\n}\n", "import type { ChildProcess } from 'node:child_process'\nimport { spawn } from 'node:child_process'\nimport FS from 'node:fs/promises'\nimport OS from 'node:os'\nimport PATH from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\n\nimport type { RunningChainServer } from '@ariestools/aries-chain-serve'\n\nimport { buildXl1Config } from './buildXl1Config.ts'\nimport {\n DEFAULT_HEALTH_POLL_MS, DEFAULT_HEALTH_TIMEOUT_MS, GENESIS_MNEMONIC,\n} from './constants.ts'\nimport { resolveXl1Bin } from './resolveXl1Bin.ts'\n\nexport interface RunningXl1 {\n apiPort: number\n chainId: string\n child: ChildProcess\n configDir: string\n rpcUrl: string\n}\n\nexport interface StartXl1Options {\n apiPort: number\n chainId: string\n genesisRewardAddress: string\n healthPollMs?: number\n healthTimeoutMs?: number\n logXl1?: boolean\n mnemonic?: string\n s3: RunningChainServer\n}\n\nasync function waitForApi(\n rpcUrl: string,\n child: ChildProcess,\n healthTimeoutMs: number,\n healthPollMs: number,\n): Promise<void> {\n const deadline = Date.now() + healthTimeoutMs\n const healthUrl = rpcUrl.replace(/\\/rpc$/, '/')\n while (Date.now() < deadline) {\n if (child.exitCode !== null) {\n throw new Error(\n `@ariestools/chain-serve-local-s3: xl1 exited early (code=${child.exitCode}) before API became reachable on ${rpcUrl}`,\n )\n }\n try {\n const response = await fetch(healthUrl, { method: 'GET' })\n if (response.status > 0) return\n } catch {\n // not ready\n }\n await delay(healthPollMs)\n }\n throw new Error(\n `@ariestools/chain-serve-local-s3: xl1 API did not become reachable on ${rpcUrl} within ${healthTimeoutMs}ms`,\n )\n}\n\nexport async function startXl1(options: StartXl1Options): Promise<RunningXl1> {\n const {\n apiPort,\n chainId,\n genesisRewardAddress,\n healthPollMs = DEFAULT_HEALTH_POLL_MS,\n healthTimeoutMs = DEFAULT_HEALTH_TIMEOUT_MS,\n logXl1 = true,\n mnemonic = GENESIS_MNEMONIC,\n s3,\n } = options\n\n // Prefer 127.0.0.1 over localhost \u2014 Node may resolve localhost to ::1 while\n // the API binds IPv4-only (UND_ERR_SOCKET on RPC writes).\n const rpcUrl = `http://127.0.0.1:${apiPort}/rpc`\n // Port 0 = disable Prometheus scrape (see initTelemetryNode). Avoids the\n // default 9465 clash across concurrent vitest setupFiles.\n const metricsPort = 0\n const configDir = await FS.mkdtemp(PATH.join(OS.tmpdir(), 'chain-serve-local-s3-'))\n const configPath = PATH.join(configDir, 'xyo.json')\n await FS.writeFile(\n configPath,\n `${JSON.stringify(buildXl1Config({\n apiPort,\n baseUrl: s3.baseUrl,\n chainId,\n genesisRewardAddress,\n metricsPort,\n s3Endpoint: s3.baseUrl,\n }), null, 2)}\\n`,\n 'utf8',\n )\n\n const xl1Bin = resolveXl1Bin()\n const child = spawn(\n process.execPath,\n [\n xl1Bin,\n 'start',\n 'api',\n 'producer',\n 'finalizer',\n 'mempool',\n 'indexer',\n '-c',\n configPath,\n '--skip-insecure-confirm',\n ],\n {\n env: {\n ...process.env,\n // Seed the shared root mnemonic so producer path 0 == genesis reward.\n XL1_MNEMONIC: mnemonic,\n },\n stdio: ['ignore', 'pipe', 'pipe'],\n },\n )\n\n if (logXl1) {\n child.stdout?.on('data', (chunk: Buffer) => {\n process.stderr.write(`[xl1] ${chunk.toString('utf8')}`)\n })\n child.stderr?.on('data', (chunk: Buffer) => {\n process.stderr.write(`[xl1!] ${chunk.toString('utf8')}`)\n })\n child.on('exit', (code, signal) => {\n process.stderr.write(`[xl1] exited code=${code} signal=${signal}\\n`)\n })\n }\n\n try {\n await waitForApi(rpcUrl, child, healthTimeoutMs, healthPollMs)\n } catch (error) {\n if (child.exitCode === null) child.kill('SIGTERM')\n try {\n await FS.rm(configDir, { recursive: true, force: true })\n } catch {\n // best-effort cleanup after failed start\n }\n throw error\n }\n\n return {\n apiPort, chainId, child, configDir, rpcUrl,\n }\n}\n\nexport async function stopXl1(result: RunningXl1 | undefined): Promise<void> {\n if (!result) return\n const cleanup = async (): Promise<void> => {\n try {\n await FS.rm(result.configDir, { recursive: true, force: true })\n } catch {\n // best-effort\n }\n }\n if (result.child.exitCode !== null) {\n await cleanup()\n return\n }\n const stopped = new Promise<void>((resolve) => {\n result.child.once('exit', () => resolve())\n })\n result.child.kill('SIGTERM')\n await Promise.race([\n stopped,\n delay(10_000).then(() => {\n if (result.child.exitCode === null) result.child.kill('SIGKILL')\n }),\n ])\n await cleanup()\n}\n", "import { createRequire } from 'node:module'\nimport PATH from 'node:path'\n\n// Static import so deplint / package managers treat @xyo-network/xl1-cli as a\n// used runtime dependency. The bin path is still resolved via createRequire\n// below (the package's main entry is not the CLI binary).\nimport xl1CliPkg from '@xyo-network/xl1-cli/package.json' with { type: 'json' }\n\ninterface Xl1Pkg {\n bin?: Record<string, string> | string\n name?: string\n}\n\nfunction binFromPkg(pkgPath: string, pkg: Xl1Pkg): string {\n const binEntry = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.xl1\n if (!binEntry) throw new Error('@xyo-network/xl1-cli does not declare a \"xl1\" bin')\n return PATH.resolve(PATH.dirname(pkgPath), binEntry)\n}\n\n/**\n * Resolve the `xl1` bin from `@xyo-network/xl1-cli`. Tries this package's\n * dependency tree first, then the consumer project's cwd.\n */\nexport function resolveXl1Bin(): string {\n // Touch the static import so tree-shakers keep the package installed.\n if (xl1CliPkg.name !== '@xyo-network/xl1-cli') {\n throw new Error('@ariestools/chain-serve-local-s3: unexpected @xyo-network/xl1-cli package name')\n }\n\n const localRequire = createRequire(import.meta.url)\n try {\n const pkgPath = localRequire.resolve('@xyo-network/xl1-cli/package.json')\n const pkg = localRequire('@xyo-network/xl1-cli/package.json') as Xl1Pkg\n return binFromPkg(pkgPath, pkg)\n } catch {\n // fall through\n }\n const cwdPkg = PATH.join(process.cwd(), 'package.json')\n const cwdRequire = createRequire(cwdPkg)\n try {\n const pkgPath = cwdRequire.resolve('@xyo-network/xl1-cli/package.json')\n const pkg = cwdRequire('@xyo-network/xl1-cli/package.json') as Xl1Pkg\n return binFromPkg(pkgPath, pkg)\n } catch (error) {\n throw new Error(\n '@ariestools/chain-serve-local-s3: @xyo-network/xl1-cli is not installed. '\n + 'It is a runtime dependency of this package \u2014 reinstall node_modules.',\n { cause: error },\n )\n }\n}\n", "import { DEFAULT_HEALTH_TIMEOUT_MS } from './constants.ts'\nimport type { StartLocalS3ChainOptions } from './startLocalS3Chain.ts'\nimport { startLocalS3Chain } from './startLocalS3Chain.ts'\nimport type { LocalS3Chain } from './types.ts'\n\n/** Minimal vitest (or jest) hook surface \u2014 avoids a hard vitest dependency. */\nexport interface TestLifecycleHooks {\n afterAll: (fn: () => Promise<void> | void, timeout?: number) => void\n beforeAll: (fn: () => Promise<void> | void, timeout?: number) => void\n}\n\nexport interface RegisterLocalS3ChainOptions extends StartLocalS3ChainOptions {\n /** afterAll timeout ms. Default: 60_000. */\n afterAllTimeoutMs?: number\n /** beforeAll timeout ms. Default: healthTimeoutMs + 60_000. */\n beforeAllTimeoutMs?: number\n}\n\n/**\n * Wire `startLocalS3Chain` into vitest (or any beforeAll/afterAll host).\n *\n * @example\n * ```ts\n * // vitest setupFile\n * import { afterAll, beforeAll } from 'vitest'\n * import { registerLocalS3Chain } from '@ariestools/chain-serve-local-s3'\n *\n * registerLocalS3Chain({ beforeAll, afterAll }, { assignGlobals: true })\n * ```\n */\nexport function registerLocalS3Chain(\n hooks: TestLifecycleHooks,\n options: RegisterLocalS3ChainOptions = {},\n): void {\n const {\n afterAllTimeoutMs = 60_000,\n beforeAllTimeoutMs,\n healthTimeoutMs = DEFAULT_HEALTH_TIMEOUT_MS,\n ...startOptions\n } = options\n\n let chain: LocalS3Chain | undefined\n\n hooks.beforeAll(async () => {\n chain = await startLocalS3Chain({\n healthTimeoutMs,\n ...startOptions,\n })\n }, beforeAllTimeoutMs ?? healthTimeoutMs + 60_000)\n\n hooks.afterAll(async () => {\n await chain?.stop()\n chain = undefined\n }, afterAllTimeoutMs)\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["PATH", "PATH"]
7
+ }
@@ -0,0 +1,3 @@
1
+ /** Prefer `preferred` when free on every interface; otherwise an ephemeral port. */
2
+ export declare function findAvailablePort(preferred: number): Promise<number>;
3
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/ports.ts"],"names":[],"mappings":"AA4BA,oFAAoF;AACpF,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG1E"}
@@ -0,0 +1,26 @@
1
+ import type { StartLocalS3ChainOptions } from './startLocalS3Chain.ts';
2
+ /** Minimal vitest (or jest) hook surface — avoids a hard vitest dependency. */
3
+ export interface TestLifecycleHooks {
4
+ afterAll: (fn: () => Promise<void> | void, timeout?: number) => void;
5
+ beforeAll: (fn: () => Promise<void> | void, timeout?: number) => void;
6
+ }
7
+ export interface RegisterLocalS3ChainOptions extends StartLocalS3ChainOptions {
8
+ /** afterAll timeout ms. Default: 60_000. */
9
+ afterAllTimeoutMs?: number;
10
+ /** beforeAll timeout ms. Default: healthTimeoutMs + 60_000. */
11
+ beforeAllTimeoutMs?: number;
12
+ }
13
+ /**
14
+ * Wire `startLocalS3Chain` into vitest (or any beforeAll/afterAll host).
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // vitest setupFile
19
+ * import { afterAll, beforeAll } from 'vitest'
20
+ * import { registerLocalS3Chain } from '@ariestools/chain-serve-local-s3'
21
+ *
22
+ * registerLocalS3Chain({ beforeAll, afterAll }, { assignGlobals: true })
23
+ * ```
24
+ */
25
+ export declare function registerLocalS3Chain(hooks: TestLifecycleHooks, options?: RegisterLocalS3ChainOptions): void;
26
+ //# sourceMappingURL=registerLocalS3Chain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registerLocalS3Chain.d.ts","sourceRoot":"","sources":["../../src/registerLocalS3Chain.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAItE,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACpE,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CACtE;AAED,MAAM,WAAW,2BAA4B,SAAQ,wBAAwB;IAC3E,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,kBAAkB,EACzB,OAAO,GAAE,2BAAgC,GACxC,IAAI,CAqBN"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Resolve the `xl1` bin from `@xyo-network/xl1-cli`. Tries this package's
3
+ * dependency tree first, then the consumer project's cwd.
4
+ */
5
+ export declare function resolveXl1Bin(): string;
6
+ //# sourceMappingURL=resolveXl1Bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveXl1Bin.d.ts","sourceRoot":"","sources":["../../src/resolveXl1Bin.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,wBAAgB,aAAa,IAAI,MAAM,CA2BtC"}
@@ -0,0 +1,44 @@
1
+ import type { LocalS3Chain } from './types.ts';
2
+ export type { LocalS3Chain, S3ReadUrls } from './types.ts';
3
+ export interface StartLocalS3ChainOptions {
4
+ /**
5
+ * Preferred API port. Defaults to `8080 + Number(process.env.VITEST_WORKER_ID ?? 0)`.
6
+ * Falls back to an ephemeral port when the preferred port is busy.
7
+ */
8
+ apiPort?: number;
9
+ /**
10
+ * When true, also assign `apiPort`, `rpcUrl`, `chainId`, `blockProducerWallet`,
11
+ * `s3ReadUrls`, and `s3Endpoint` on `globalThis` (lifehash-style vitest globals).
12
+ */
13
+ assignGlobals?: boolean;
14
+ /** Chain id hex string. Default: {@link DEFAULT_CHAIN_ID}. */
15
+ chainId?: string;
16
+ /** API readiness timeout in ms. Default: 120_000. */
17
+ healthTimeoutMs?: number;
18
+ /** Pipe xl1 child stdout/stderr to process.stderr. Default: true. */
19
+ logXl1?: boolean;
20
+ /**
21
+ * Root mnemonic for xl1 actors and the genesis reward address.
22
+ * Default: insecure test phrase ({@link GENESIS_MNEMONIC}).
23
+ */
24
+ mnemonic?: string;
25
+ /** Silence the embedded s3rver logs. Default: true. */
26
+ silent?: boolean;
27
+ }
28
+ /**
29
+ * Boots a local XL1 test environment in one call:
30
+ *
31
+ * 1. `@ariestools/aries-chain-serve` — S3-compatible published-chain layout
32
+ * (`/blocks`, `/state`, `/indexes`)
33
+ * 2. `@xyo-network/xl1-cli` — api + producer + finalizer + mempool + indexer,
34
+ * publishing finalized blocks and index into that layout
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const chain = await startLocalS3Chain()
39
+ * // chain.rpcUrl, chain.s3ReadUrls, chain.blockProducerWallet, ...
40
+ * await chain.stop()
41
+ * ```
42
+ */
43
+ export declare function startLocalS3Chain(options?: StartLocalS3ChainOptions): Promise<LocalS3Chain>;
44
+ //# sourceMappingURL=startLocalS3Chain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startLocalS3Chain.d.ts","sourceRoot":"","sources":["../../src/startLocalS3Chain.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,YAAY,CAAA;AAE1D,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1D,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,qEAAqE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,YAAY,CAAC,CAoEvB"}
@@ -0,0 +1,22 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+ import type { RunningChainServer } from '@ariestools/aries-chain-serve';
3
+ export interface RunningXl1 {
4
+ apiPort: number;
5
+ chainId: string;
6
+ child: ChildProcess;
7
+ configDir: string;
8
+ rpcUrl: string;
9
+ }
10
+ export interface StartXl1Options {
11
+ apiPort: number;
12
+ chainId: string;
13
+ genesisRewardAddress: string;
14
+ healthPollMs?: number;
15
+ healthTimeoutMs?: number;
16
+ logXl1?: boolean;
17
+ mnemonic?: string;
18
+ s3: RunningChainServer;
19
+ }
20
+ export declare function startXl1(options: StartXl1Options): Promise<RunningXl1>;
21
+ export declare function stopXl1(result: RunningXl1 | undefined): Promise<void>;
22
+ //# sourceMappingURL=startXl1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startXl1.d.ts","sourceRoot":"","sources":["../../src/startXl1.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAOtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAQvE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,YAAY,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,oBAAoB,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,EAAE,EAAE,kBAAkB,CAAA;CACvB;AA6BD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAqF5E;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB3E"}
@@ -0,0 +1,22 @@
1
+ import type { AccountInstance } from '@xyo-network/sdk';
2
+ export interface S3ReadUrls {
3
+ chainState: string;
4
+ finalized: string;
5
+ index: string;
6
+ }
7
+ /**
8
+ * Running local XL1 + published-chain S3 layout. Call `stop()` in teardown.
9
+ *
10
+ * DEV/TEST ONLY — accepts any S3 credentials, memory authority store, no auth.
11
+ */
12
+ export interface LocalS3Chain {
13
+ apiPort: number;
14
+ /** Genesis reward / producer path-0 account (same mnemonic as the xl1 child). */
15
+ blockProducerWallet: AccountInstance;
16
+ chainId: string;
17
+ rpcUrl: string;
18
+ s3Endpoint: string;
19
+ s3ReadUrls: S3ReadUrls;
20
+ stop(): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAEvD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,iFAAiF;IACjF,mBAAmB,EAAE,eAAe,CAAA;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@ariestools/chain-serve-local-s3",
3
+ "version": "0.1.15",
4
+ "description": "One-call local XL1 test harness: published-chain S3 layout (s3rver) + xl1-cli (api/producer/finalizer/mempool/indexer)",
5
+ "keywords": [
6
+ "ariestools",
7
+ "xl1",
8
+ "xyo",
9
+ "chain",
10
+ "s3",
11
+ "s3rver",
12
+ "test",
13
+ "e2e",
14
+ "typescript"
15
+ ],
16
+ "homepage": "https://ariestools.ai",
17
+ "license": "LGPL-3.0-only",
18
+ "author": {
19
+ "name": "Arie Trouw",
20
+ "email": "support@ariestools.ai",
21
+ "url": "https://ariestools.ai"
22
+ },
23
+ "sideEffects": false,
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/node/index.d.ts",
28
+ "default": "./dist/node/index.mjs"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "README.md"
35
+ ],
36
+ "dependencies": {
37
+ "@xyo-network/sdk": "~7.2.1",
38
+ "@xyo-network/xl1-cli": "~4.5.2",
39
+ "@xyo-network/xl1-sdk": "~4.6.0",
40
+ "@ariestools/aries-chain-serve": "~0.1.15"
41
+ },
42
+ "devDependencies": {
43
+ "@ariestools/sdk": "~8.1.4",
44
+ "@ariestools/tsconfig": "~8.7.23",
45
+ "@opentelemetry/api": "~1.9.1",
46
+ "@types/node": "~26.1.1",
47
+ "@xyo-network/sdk-protocol": "~7.2.2",
48
+ "async-mutex": "~0.5.0",
49
+ "ethers": "~6.17.0",
50
+ "typescript": "~6.0.3",
51
+ "vite": "~8.1.5",
52
+ "vitest": "~4.1.10",
53
+ "zod": "~4.4.3"
54
+ },
55
+ "engines": {
56
+ "node": ">=18.17.1"
57
+ },
58
+ "engineStrict": true,
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "scripts": {
63
+ "package-compile": "package-compile-only"
64
+ }
65
+ }