moonwall 1.0.0-dev.0
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/LICENSE +681 -0
- package/README.md +54 -0
- package/config_schema.json +811 -0
- package/dist/api/constants/accounts.d.ts +36 -0
- package/dist/api/constants/accounts.d.ts.map +1 -0
- package/dist/api/constants/accounts.js +67 -0
- package/dist/api/constants/chain.d.ts +134 -0
- package/dist/api/constants/chain.d.ts.map +1 -0
- package/dist/api/constants/chain.js +149 -0
- package/dist/api/constants/index.d.ts +4 -0
- package/dist/api/constants/index.d.ts.map +1 -0
- package/dist/api/constants/index.js +3 -0
- package/dist/api/constants/smartContract.d.ts +29 -0
- package/dist/api/constants/smartContract.d.ts.map +1 -0
- package/dist/api/constants/smartContract.js +118 -0
- package/dist/api/testing/blocks.d.ts +59 -0
- package/dist/api/testing/blocks.d.ts.map +1 -0
- package/dist/api/testing/blocks.js +147 -0
- package/dist/api/testing/contracts.d.ts +5 -0
- package/dist/api/testing/contracts.d.ts.map +1 -0
- package/dist/api/testing/contracts.js +32 -0
- package/dist/api/testing/ethers.d.ts +3 -0
- package/dist/api/testing/ethers.d.ts.map +1 -0
- package/dist/api/testing/ethers.js +38 -0
- package/dist/api/testing/events.d.ts +12 -0
- package/dist/api/testing/events.d.ts.map +1 -0
- package/dist/api/testing/events.js +23 -0
- package/dist/api/testing/extrinsics.d.ts +5 -0
- package/dist/api/testing/extrinsics.d.ts.map +1 -0
- package/dist/api/testing/extrinsics.js +10 -0
- package/dist/api/testing/index.d.ts +9 -0
- package/dist/api/testing/index.d.ts.map +1 -0
- package/dist/api/testing/index.js +8 -0
- package/dist/api/testing/jumping.d.ts +8 -0
- package/dist/api/testing/jumping.d.ts.map +1 -0
- package/dist/api/testing/jumping.js +78 -0
- package/dist/api/testing/providers.d.ts +18 -0
- package/dist/api/testing/providers.d.ts.map +1 -0
- package/dist/api/testing/providers.js +34 -0
- package/dist/api/testing/viem.d.ts +139 -0
- package/dist/api/testing/viem.d.ts.map +1 -0
- package/dist/api/testing/viem.js +247 -0
- package/dist/api/types/config.d.ts +609 -0
- package/dist/api/types/config.d.ts.map +1 -0
- package/dist/api/types/config.js +1 -0
- package/dist/api/types/context.d.ts +125 -0
- package/dist/api/types/context.d.ts.map +1 -0
- package/dist/api/types/context.js +1 -0
- package/dist/api/types/contracts.d.ts +66 -0
- package/dist/api/types/contracts.d.ts.map +1 -0
- package/dist/api/types/contracts.js +1 -0
- package/dist/api/types/eth.d.ts +3 -0
- package/dist/api/types/eth.d.ts.map +1 -0
- package/dist/api/types/eth.js +1 -0
- package/dist/api/types/foundations.d.ts +11 -0
- package/dist/api/types/foundations.d.ts.map +1 -0
- package/dist/api/types/foundations.js +1 -0
- package/dist/api/types/helpers.d.ts +7 -0
- package/dist/api/types/helpers.d.ts.map +1 -0
- package/dist/api/types/helpers.js +1 -0
- package/dist/api/types/index.d.ts +8 -0
- package/dist/api/types/index.d.ts.map +1 -0
- package/dist/api/types/index.js +7 -0
- package/dist/api/types/runner.d.ts +490 -0
- package/dist/api/types/runner.d.ts.map +1 -0
- package/dist/api/types/runner.js +1 -0
- package/dist/cli/cmds/components/LogViewer.d.ts +17 -0
- package/dist/cli/cmds/components/LogViewer.d.ts.map +1 -0
- package/dist/cli/cmds/components/LogViewer.js +171 -0
- package/dist/cli/cmds/entrypoint.d.ts +6 -0
- package/dist/cli/cmds/entrypoint.d.ts.map +1 -0
- package/dist/cli/cmds/entrypoint.js +192 -0
- package/dist/cli/cmds/interactiveCmds/chopsticksIntCmds.d.ts +2 -0
- package/dist/cli/cmds/interactiveCmds/chopsticksIntCmds.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/chopsticksIntCmds.js +117 -0
- package/dist/cli/cmds/interactiveCmds/devIntCmds.d.ts +2 -0
- package/dist/cli/cmds/interactiveCmds/devIntCmds.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/devIntCmds.js +103 -0
- package/dist/cli/cmds/interactiveCmds/index.d.ts +4 -0
- package/dist/cli/cmds/interactiveCmds/index.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/index.js +3 -0
- package/dist/cli/cmds/interactiveCmds/zombieIntCmds.d.ts +2 -0
- package/dist/cli/cmds/interactiveCmds/zombieIntCmds.d.ts.map +1 -0
- package/dist/cli/cmds/interactiveCmds/zombieIntCmds.js +32 -0
- package/dist/cli/cmds/main.d.ts +2 -0
- package/dist/cli/cmds/main.d.ts.map +1 -0
- package/dist/cli/cmds/main.js +336 -0
- package/dist/cli/cmds/runNetwork.d.ts +3 -0
- package/dist/cli/cmds/runNetwork.d.ts.map +1 -0
- package/dist/cli/cmds/runNetwork.js +292 -0
- package/dist/cli/cmds/runTests.d.ts +12 -0
- package/dist/cli/cmds/runTests.d.ts.map +1 -0
- package/dist/cli/cmds/runTests.js +257 -0
- package/dist/cli/internal/cmdFunctions/downloader.d.ts +4 -0
- package/dist/cli/internal/cmdFunctions/downloader.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/downloader.js +49 -0
- package/dist/cli/internal/cmdFunctions/fetchArtifact.d.ts +10 -0
- package/dist/cli/internal/cmdFunctions/fetchArtifact.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/fetchArtifact.js +145 -0
- package/dist/cli/internal/cmdFunctions/index.d.ts +5 -0
- package/dist/cli/internal/cmdFunctions/index.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/index.js +4 -0
- package/dist/cli/internal/cmdFunctions/initialisation.d.ts +20 -0
- package/dist/cli/internal/cmdFunctions/initialisation.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/initialisation.js +150 -0
- package/dist/cli/internal/cmdFunctions/tempLogs.d.ts +3 -0
- package/dist/cli/internal/cmdFunctions/tempLogs.d.ts.map +1 -0
- package/dist/cli/internal/cmdFunctions/tempLogs.js +37 -0
- package/dist/cli/internal/commandParsers.d.ts +59 -0
- package/dist/cli/internal/commandParsers.d.ts.map +1 -0
- package/dist/cli/internal/commandParsers.js +305 -0
- package/dist/cli/internal/deriveTestIds.d.ts +8 -0
- package/dist/cli/internal/deriveTestIds.d.ts.map +1 -0
- package/dist/cli/internal/deriveTestIds.js +123 -0
- package/dist/cli/internal/effect/index.d.ts +6 -0
- package/dist/cli/internal/effect/index.d.ts.map +1 -0
- package/dist/cli/internal/effect/index.js +5 -0
- package/dist/cli/internal/fileCheckers.d.ts +11 -0
- package/dist/cli/internal/fileCheckers.d.ts.map +1 -0
- package/dist/cli/internal/fileCheckers.js +165 -0
- package/dist/cli/internal/foundations/index.d.ts +4 -0
- package/dist/cli/internal/foundations/index.d.ts.map +1 -0
- package/dist/cli/internal/foundations/index.js +4 -0
- package/dist/cli/internal/index.d.ts +12 -0
- package/dist/cli/internal/index.d.ts.map +1 -0
- package/dist/cli/internal/index.js +11 -0
- package/dist/cli/internal/launcherCommon.d.ts +4 -0
- package/dist/cli/internal/launcherCommon.d.ts.map +1 -0
- package/dist/cli/internal/launcherCommon.js +130 -0
- package/dist/cli/internal/localNode.d.ts +16 -0
- package/dist/cli/internal/localNode.d.ts.map +1 -0
- package/dist/cli/internal/localNode.js +362 -0
- package/dist/cli/internal/logging.d.ts +2 -0
- package/dist/cli/internal/logging.d.ts.map +1 -0
- package/dist/cli/internal/logging.js +26 -0
- package/dist/cli/internal/node.d.ts +33 -0
- package/dist/cli/internal/node.d.ts.map +1 -0
- package/dist/cli/internal/node.js +228 -0
- package/dist/cli/internal/processHelpers.d.ts +17 -0
- package/dist/cli/internal/processHelpers.d.ts.map +1 -0
- package/dist/cli/internal/processHelpers.js +56 -0
- package/dist/cli/internal/providerFactories.d.ts +48 -0
- package/dist/cli/internal/providerFactories.d.ts.map +1 -0
- package/dist/cli/internal/providerFactories.js +442 -0
- package/dist/cli/internal/testIdParser.d.ts +34 -0
- package/dist/cli/internal/testIdParser.d.ts.map +1 -0
- package/dist/cli/internal/testIdParser.js +167 -0
- package/dist/cli/lib/binariesHelpers.d.ts +15 -0
- package/dist/cli/lib/binariesHelpers.d.ts.map +1 -0
- package/dist/cli/lib/binariesHelpers.js +99 -0
- package/dist/cli/lib/configReader.d.ts +8 -0
- package/dist/cli/lib/configReader.d.ts.map +1 -0
- package/dist/cli/lib/configReader.js +115 -0
- package/dist/cli/lib/contractFunctions.d.ts +2 -0
- package/dist/cli/lib/contractFunctions.d.ts.map +1 -0
- package/dist/cli/lib/contractFunctions.js +2 -0
- package/dist/cli/lib/globalContext.d.ts +46 -0
- package/dist/cli/lib/globalContext.d.ts.map +1 -0
- package/dist/cli/lib/globalContext.js +710 -0
- package/dist/cli/lib/governanceProcedures.d.ts +27 -0
- package/dist/cli/lib/governanceProcedures.d.ts.map +1 -0
- package/dist/cli/lib/governanceProcedures.js +458 -0
- package/dist/cli/lib/handlers/index.d.ts +5 -0
- package/dist/cli/lib/handlers/index.d.ts.map +1 -0
- package/dist/cli/lib/handlers/index.js +5 -0
- package/dist/cli/lib/repoDefinitions/index.d.ts +6 -0
- package/dist/cli/lib/repoDefinitions/index.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/index.js +21 -0
- package/dist/cli/lib/repoDefinitions/moonbeam.d.ts +4 -0
- package/dist/cli/lib/repoDefinitions/moonbeam.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/moonbeam.js +30 -0
- package/dist/cli/lib/repoDefinitions/polkadot.d.ts +4 -0
- package/dist/cli/lib/repoDefinitions/polkadot.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/polkadot.js +11 -0
- package/dist/cli/lib/repoDefinitions/tanssi.d.ts +4 -0
- package/dist/cli/lib/repoDefinitions/tanssi.d.ts.map +1 -0
- package/dist/cli/lib/repoDefinitions/tanssi.js +14 -0
- package/dist/cli/lib/rpcFunctions.d.ts +2 -0
- package/dist/cli/lib/rpcFunctions.d.ts.map +1 -0
- package/dist/cli/lib/rpcFunctions.js +26 -0
- package/dist/cli/lib/runnerContext.d.ts +32 -0
- package/dist/cli/lib/runnerContext.d.ts.map +1 -0
- package/dist/cli/lib/runnerContext.js +156 -0
- package/dist/cli/lib/shardManager.d.ts +40 -0
- package/dist/cli/lib/shardManager.d.ts.map +1 -0
- package/dist/cli/lib/shardManager.js +80 -0
- package/dist/cli/lib/upgradeProcedures.d.ts +5 -0
- package/dist/cli/lib/upgradeProcedures.d.ts.map +1 -0
- package/dist/cli/lib/upgradeProcedures.js +221 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3 -0
- package/dist/contracts/contractInteraction.d.ts +17 -0
- package/dist/contracts/contractInteraction.d.ts.map +1 -0
- package/dist/contracts/contractInteraction.js +170 -0
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/foundations/chopsticks/handler.d.ts +3 -0
- package/dist/foundations/chopsticks/handler.d.ts.map +1 -0
- package/dist/foundations/chopsticks/handler.js +93 -0
- package/dist/foundations/chopsticks/helpers.d.ts +27 -0
- package/dist/foundations/chopsticks/helpers.d.ts.map +1 -0
- package/dist/foundations/chopsticks/helpers.js +133 -0
- package/dist/foundations/dev/handler.d.ts +3 -0
- package/dist/foundations/dev/handler.d.ts.map +1 -0
- package/dist/foundations/dev/handler.js +136 -0
- package/dist/foundations/dev/helpers.d.ts +27 -0
- package/dist/foundations/dev/helpers.d.ts.map +1 -0
- package/dist/foundations/dev/helpers.js +161 -0
- package/dist/foundations/read-only/handler.d.ts +3 -0
- package/dist/foundations/read-only/handler.d.ts.map +1 -0
- package/dist/foundations/read-only/handler.js +32 -0
- package/dist/foundations/zombie/handler.d.ts +3 -0
- package/dist/foundations/zombie/handler.d.ts.map +1 -0
- package/dist/foundations/zombie/handler.js +92 -0
- package/dist/foundations/zombie/helpers.d.ts +16 -0
- package/dist/foundations/zombie/helpers.d.ts.map +1 -0
- package/dist/foundations/zombie/helpers.js +97 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/internal/common.d.ts +3 -0
- package/dist/internal/common.d.ts.map +1 -0
- package/dist/internal/common.js +41 -0
- package/dist/internal/index.d.ts +4 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +3 -0
- package/dist/internal/logger.d.ts +24 -0
- package/dist/internal/logger.d.ts.map +1 -0
- package/dist/internal/logger.js +66 -0
- package/dist/internal/logging.d.ts +7 -0
- package/dist/internal/logging.d.ts.map +1 -0
- package/dist/internal/logging.js +36 -0
- package/dist/moondebug.d.ts +3 -0
- package/dist/moondebug.d.ts.map +1 -0
- package/dist/moondebug.js +2 -0
- package/dist/services/cache/FileLock.d.ts +11 -0
- package/dist/services/cache/FileLock.d.ts.map +1 -0
- package/dist/services/cache/FileLock.js +68 -0
- package/dist/services/cache/StartupCacheService.d.ts +23 -0
- package/dist/services/cache/StartupCacheService.d.ts.map +1 -0
- package/dist/services/cache/StartupCacheService.js +159 -0
- package/dist/services/cache/index.d.ts +3 -0
- package/dist/services/cache/index.d.ts.map +1 -0
- package/dist/services/cache/index.js +2 -0
- package/dist/services/chopsticks/ChopsticksMultiChain.d.ts +158 -0
- package/dist/services/chopsticks/ChopsticksMultiChain.d.ts.map +1 -0
- package/dist/services/chopsticks/ChopsticksMultiChain.js +282 -0
- package/dist/services/chopsticks/ChopsticksService.d.ts +313 -0
- package/dist/services/chopsticks/ChopsticksService.d.ts.map +1 -0
- package/dist/services/chopsticks/ChopsticksService.js +77 -0
- package/dist/services/chopsticks/chopsticksConfigParser.d.ts +40 -0
- package/dist/services/chopsticks/chopsticksConfigParser.d.ts.map +1 -0
- package/dist/services/chopsticks/chopsticksConfigParser.js +201 -0
- package/dist/services/chopsticks/index.d.ts +5 -0
- package/dist/services/chopsticks/index.d.ts.map +1 -0
- package/dist/services/chopsticks/index.js +4 -0
- package/dist/services/chopsticks/launchChopsticksEffect.d.ts +225 -0
- package/dist/services/chopsticks/launchChopsticksEffect.d.ts.map +1 -0
- package/dist/services/chopsticks/launchChopsticksEffect.js +623 -0
- package/dist/services/config/configAccessors.d.ts +41 -0
- package/dist/services/config/configAccessors.d.ts.map +1 -0
- package/dist/services/config/configAccessors.js +149 -0
- package/dist/services/config/index.d.ts +2 -0
- package/dist/services/config/index.d.ts.map +1 -0
- package/dist/services/config/index.js +1 -0
- package/dist/services/errors.d.ts +72 -0
- package/dist/services/errors.d.ts.map +1 -0
- package/dist/services/errors.js +31 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -0
- package/dist/services/network/NodeReadinessService.d.ts +35 -0
- package/dist/services/network/NodeReadinessService.d.ts.map +1 -0
- package/dist/services/network/NodeReadinessService.js +120 -0
- package/dist/services/network/PortDiscoveryService.d.ts +22 -0
- package/dist/services/network/PortDiscoveryService.d.ts.map +1 -0
- package/dist/services/network/PortDiscoveryService.js +77 -0
- package/dist/services/network/RpcPortDiscoveryService.d.ts +25 -0
- package/dist/services/network/RpcPortDiscoveryService.d.ts.map +1 -0
- package/dist/services/network/RpcPortDiscoveryService.js +136 -0
- package/dist/services/network/index.d.ts +4 -0
- package/dist/services/network/index.d.ts.map +1 -0
- package/dist/services/network/index.js +3 -0
- package/dist/services/process/ProcessManagerService.d.ts +49 -0
- package/dist/services/process/ProcessManagerService.d.ts.map +1 -0
- package/dist/services/process/ProcessManagerService.js +162 -0
- package/dist/services/process/index.d.ts +3 -0
- package/dist/services/process/index.d.ts.map +1 -0
- package/dist/services/process/index.js +2 -0
- package/dist/services/process/launchNodeEffect.d.ts +40 -0
- package/dist/services/process/launchNodeEffect.d.ts.map +1 -0
- package/dist/services/process/launchNodeEffect.js +86 -0
- package/dist/util/functions/index.d.ts +3 -0
- package/dist/util/functions/index.d.ts.map +1 -0
- package/dist/util/functions/index.js +4 -0
- package/dist/util/index.d.ts +4 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +2 -0
- package/package.json +157 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { createLogger } from "../../util/index.js";
|
|
2
|
+
import Docker from "dockerode";
|
|
3
|
+
import { exec, spawn, spawnSync } from "node:child_process";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { setTimeout as timer } from "node:timers/promises";
|
|
7
|
+
import util from "node:util";
|
|
8
|
+
import invariant from "tiny-invariant";
|
|
9
|
+
import WebSocket from "ws";
|
|
10
|
+
import { isEthereumDevConfig, isEthereumZombieConfig } from "../lib/configReader.js";
|
|
11
|
+
import { checkAccess, checkExists } from "./fileCheckers.js";
|
|
12
|
+
const execAsync = util.promisify(exec);
|
|
13
|
+
const logger = createLogger({ name: "localNode" });
|
|
14
|
+
const debug = logger.debug.bind(logger);
|
|
15
|
+
// TODO: Add multi-threading support
|
|
16
|
+
async function launchDockerContainer(imageName, args, name, dockerConfig) {
|
|
17
|
+
const docker = new Docker();
|
|
18
|
+
const port = args.find((a) => a.includes("port"))?.split("=")[1];
|
|
19
|
+
debug(`\x1b[36mStarting Docker container ${imageName} on port ${port}...\x1b[0m`);
|
|
20
|
+
const dirPath = path.join(process.cwd(), "tmp", "node_logs");
|
|
21
|
+
const logLocation = path.join(dirPath, `${name}_docker_${Date.now()}.log`);
|
|
22
|
+
const fsStream = fs.createWriteStream(logLocation);
|
|
23
|
+
process.env.MOON_LOG_LOCATION = logLocation;
|
|
24
|
+
const portBindings = dockerConfig?.exposePorts?.reduce((acc, { hostPort, internalPort }) => {
|
|
25
|
+
acc[`${internalPort}/tcp`] = [{ HostPort: hostPort.toString() }];
|
|
26
|
+
return acc;
|
|
27
|
+
}, {});
|
|
28
|
+
const rpcPort = args.find((a) => a.includes("rpc-port"))?.split("=")[1];
|
|
29
|
+
invariant(rpcPort, "RPC port not found, this is a bug");
|
|
30
|
+
const containerOptions = {
|
|
31
|
+
Image: imageName,
|
|
32
|
+
platform: "linux/amd64",
|
|
33
|
+
Cmd: args,
|
|
34
|
+
name: dockerConfig?.containerName || `moonwall_${name}_${Date.now()}`,
|
|
35
|
+
ExposedPorts: {
|
|
36
|
+
...Object.fromEntries(dockerConfig?.exposePorts?.map(({ internalPort }) => [`${internalPort}/tcp`, {}]) || []),
|
|
37
|
+
[`${rpcPort}/tcp`]: {},
|
|
38
|
+
},
|
|
39
|
+
HostConfig: {
|
|
40
|
+
PortBindings: {
|
|
41
|
+
...portBindings,
|
|
42
|
+
[`${rpcPort}/tcp`]: [{ HostPort: rpcPort }],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
Env: dockerConfig?.runArgs?.filter((arg) => arg.startsWith("env:")).map((arg) => arg.slice(4)),
|
|
46
|
+
};
|
|
47
|
+
try {
|
|
48
|
+
await pullImage(imageName, docker);
|
|
49
|
+
const container = await docker.createContainer(containerOptions);
|
|
50
|
+
await container.start();
|
|
51
|
+
const containerInfo = await container.inspect();
|
|
52
|
+
if (!containerInfo.State.Running) {
|
|
53
|
+
const errorMessage = `Container failed to start: ${containerInfo.State.Error}`;
|
|
54
|
+
console.error(errorMessage);
|
|
55
|
+
fs.appendFileSync(logLocation, `${errorMessage}\n`);
|
|
56
|
+
throw new Error(errorMessage);
|
|
57
|
+
}
|
|
58
|
+
for (let i = 0; i < 300; i++) {
|
|
59
|
+
const isReady = await checkWebSocketJSONRPC(Number.parseInt(rpcPort, 10));
|
|
60
|
+
if (isReady) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
await timer(100);
|
|
64
|
+
}
|
|
65
|
+
return { runningNode: container, fsStream };
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error instanceof Error) {
|
|
69
|
+
console.error(`Docker container launch failed: ${error.message}`);
|
|
70
|
+
fs.appendFileSync(logLocation, `Docker launch error: ${error.message}\n`);
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export async function launchNodeLegacy(options) {
|
|
76
|
+
const { command: cmd, args, name, launchSpec: config } = options;
|
|
77
|
+
if (config?.useDocker) {
|
|
78
|
+
return launchDockerContainer(cmd, args, name, config.dockerConfig);
|
|
79
|
+
}
|
|
80
|
+
if (cmd.includes("moonbeam")) {
|
|
81
|
+
await checkExists(cmd);
|
|
82
|
+
checkAccess(cmd);
|
|
83
|
+
}
|
|
84
|
+
const port = args.find((a) => a.includes("port"))?.split("=")[1];
|
|
85
|
+
debug(`\x1b[36mStarting ${name} node on port ${port}...\x1b[0m`);
|
|
86
|
+
const dirPath = path.join(process.cwd(), "tmp", "node_logs");
|
|
87
|
+
const runningNode = spawn(cmd, args);
|
|
88
|
+
const logLocation = path
|
|
89
|
+
.join(dirPath, `${path.basename(cmd)}_node_${args.find((a) => a.includes("port"))?.split("=")[1]}_${runningNode.pid}.log`)
|
|
90
|
+
.replaceAll("node_node_undefined", "chopsticks");
|
|
91
|
+
process.env.MOON_LOG_LOCATION = logLocation;
|
|
92
|
+
const fsStream = fs.createWriteStream(logLocation);
|
|
93
|
+
runningNode.on("error", (err) => {
|
|
94
|
+
if (err.errno === "ENOENT") {
|
|
95
|
+
console.error(`\x1b[31mMissing Local binary at(${cmd}).\nPlease compile the project\x1b[0m`);
|
|
96
|
+
}
|
|
97
|
+
throw new Error(err.message);
|
|
98
|
+
});
|
|
99
|
+
const logHandler = (chunk) => {
|
|
100
|
+
if (fsStream.writable) {
|
|
101
|
+
fsStream.write(chunk, (err) => {
|
|
102
|
+
if (err)
|
|
103
|
+
console.error(err);
|
|
104
|
+
else
|
|
105
|
+
fsStream.emit("drain");
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
runningNode.stderr?.on("data", logHandler);
|
|
110
|
+
runningNode.stdout?.on("data", logHandler);
|
|
111
|
+
runningNode.once("exit", (code, signal) => {
|
|
112
|
+
const timestamp = new Date().toISOString();
|
|
113
|
+
let message;
|
|
114
|
+
// Check if this termination was initiated by Moonwall
|
|
115
|
+
const moonwallNode = runningNode;
|
|
116
|
+
if (moonwallNode.isMoonwallTerminating) {
|
|
117
|
+
message = `${timestamp} [moonwall] process killed. reason: ${moonwallNode.moonwallTerminationReason || "unknown"}`;
|
|
118
|
+
}
|
|
119
|
+
else if (code !== null) {
|
|
120
|
+
message = `${timestamp} [moonwall] process exited with status code ${code}`;
|
|
121
|
+
}
|
|
122
|
+
else if (signal !== null) {
|
|
123
|
+
message = `${timestamp} [moonwall] process terminated by signal ${signal}`;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
message = `${timestamp} [moonwall] process terminated unexpectedly`;
|
|
127
|
+
}
|
|
128
|
+
// Write the message before closing the stream
|
|
129
|
+
if (fsStream.writable) {
|
|
130
|
+
fsStream.write(`${message}\n`, (err) => {
|
|
131
|
+
if (err)
|
|
132
|
+
console.error(`Failed to write exit message to log: ${err}`);
|
|
133
|
+
fsStream.end();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Fallback: append to file directly if stream is not writable
|
|
138
|
+
try {
|
|
139
|
+
fs.appendFileSync(logLocation, `${message}\n`);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
console.error(`Failed to append exit message to log file: ${err}`);
|
|
143
|
+
}
|
|
144
|
+
fsStream.end();
|
|
145
|
+
}
|
|
146
|
+
runningNode.stderr?.removeListener("data", logHandler);
|
|
147
|
+
runningNode.stdout?.removeListener("data", logHandler);
|
|
148
|
+
});
|
|
149
|
+
if (!runningNode.pid) {
|
|
150
|
+
const errorMessage = "Failed to start child process";
|
|
151
|
+
console.error(errorMessage);
|
|
152
|
+
fs.appendFileSync(logLocation, `${errorMessage}\n`);
|
|
153
|
+
throw new Error(errorMessage);
|
|
154
|
+
}
|
|
155
|
+
// Check if the process exited immediately
|
|
156
|
+
if (runningNode.exitCode !== null) {
|
|
157
|
+
const errorMessage = `Child process exited immediately with code ${runningNode.exitCode}`;
|
|
158
|
+
console.error(errorMessage);
|
|
159
|
+
fs.appendFileSync(logLocation, `${errorMessage}\n`);
|
|
160
|
+
throw new Error(errorMessage);
|
|
161
|
+
}
|
|
162
|
+
const isRunning = await isPidRunning(runningNode.pid);
|
|
163
|
+
if (!isRunning) {
|
|
164
|
+
const errorMessage = `Process with PID ${runningNode.pid} is not running`;
|
|
165
|
+
spawnSync(cmd, args, { stdio: "inherit" });
|
|
166
|
+
throw new Error(errorMessage);
|
|
167
|
+
}
|
|
168
|
+
probe: for (let i = 0;; i++) {
|
|
169
|
+
try {
|
|
170
|
+
const ports = await findPortsByPid(runningNode.pid);
|
|
171
|
+
if (ports) {
|
|
172
|
+
for (const port of ports) {
|
|
173
|
+
try {
|
|
174
|
+
const isReady = await checkWebSocketJSONRPC(port);
|
|
175
|
+
if (isReady) {
|
|
176
|
+
break probe;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch { }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
if (i === 300) {
|
|
185
|
+
throw new Error("Could not find ports for node after 30 seconds");
|
|
186
|
+
}
|
|
187
|
+
await timer(100);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
await timer(100);
|
|
191
|
+
}
|
|
192
|
+
return { runningNode, fsStream };
|
|
193
|
+
}
|
|
194
|
+
function isPidRunning(pid) {
|
|
195
|
+
return new Promise((resolve) => {
|
|
196
|
+
exec(`ps -p ${pid} -o pid=`, (error, stdout, _stderr) => {
|
|
197
|
+
if (error) {
|
|
198
|
+
resolve(false);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
resolve(stdout.trim() !== "");
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function checkWebSocketJSONRPC(port) {
|
|
207
|
+
try {
|
|
208
|
+
// Determine if this is an Ethereum-compatible chain from config
|
|
209
|
+
const isEthereumChain = isEthereumDevConfig() || isEthereumZombieConfig();
|
|
210
|
+
// First check WebSocket availability
|
|
211
|
+
const ws = new WebSocket(`ws://localhost:${port}`);
|
|
212
|
+
const checkWsMethod = async (method) => {
|
|
213
|
+
return new Promise((resolve) => {
|
|
214
|
+
const timeout = setTimeout(() => {
|
|
215
|
+
resolve(false);
|
|
216
|
+
}, 5000);
|
|
217
|
+
ws.send(JSON.stringify({
|
|
218
|
+
jsonrpc: "2.0",
|
|
219
|
+
id: Math.floor(Math.random() * 10000),
|
|
220
|
+
method,
|
|
221
|
+
params: [],
|
|
222
|
+
}));
|
|
223
|
+
const messageHandler = (data) => {
|
|
224
|
+
try {
|
|
225
|
+
const response = JSON.parse(data.toString());
|
|
226
|
+
if (response.jsonrpc === "2.0" && !response.error) {
|
|
227
|
+
clearTimeout(timeout);
|
|
228
|
+
ws.removeListener("message", messageHandler);
|
|
229
|
+
resolve(true);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch (_e) {
|
|
233
|
+
// Ignore parse errors
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
ws.on("message", messageHandler);
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
const wsResult = await new Promise((resolve) => {
|
|
240
|
+
ws.on("open", async () => {
|
|
241
|
+
try {
|
|
242
|
+
// Check system_chain first via WebSocket (works for all chains)
|
|
243
|
+
const systemChainAvailable = await checkWsMethod("system_chain");
|
|
244
|
+
if (!systemChainAvailable) {
|
|
245
|
+
resolve(false);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// For Ethereum-compatible chains, also check eth_chainId via WebSocket
|
|
249
|
+
if (isEthereumChain) {
|
|
250
|
+
const ethChainIdAvailable = await checkWsMethod("eth_chainId");
|
|
251
|
+
if (!ethChainIdAvailable) {
|
|
252
|
+
resolve(false);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// WebSocket checks passed
|
|
257
|
+
resolve(true);
|
|
258
|
+
}
|
|
259
|
+
catch (_e) {
|
|
260
|
+
resolve(false);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
ws.on("error", () => {
|
|
264
|
+
resolve(false);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
ws?.close();
|
|
268
|
+
if (!wsResult) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
// Now also check HTTP service is ready
|
|
272
|
+
const httpUrl = `http://localhost:${port}`;
|
|
273
|
+
const checkHttpMethod = async (method) => {
|
|
274
|
+
try {
|
|
275
|
+
const response = await fetch(httpUrl, {
|
|
276
|
+
method: "POST",
|
|
277
|
+
headers: { "Content-Type": "application/json" },
|
|
278
|
+
body: JSON.stringify({
|
|
279
|
+
jsonrpc: "2.0",
|
|
280
|
+
id: Math.floor(Math.random() * 10000),
|
|
281
|
+
method,
|
|
282
|
+
params: [],
|
|
283
|
+
}),
|
|
284
|
+
});
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
const data = await response.json();
|
|
289
|
+
return !data.error;
|
|
290
|
+
}
|
|
291
|
+
catch (_e) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
try {
|
|
296
|
+
// Always check system_chain via HTTP (works for all chains)
|
|
297
|
+
const systemChainAvailable = await checkHttpMethod("system_chain");
|
|
298
|
+
if (!systemChainAvailable) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
// For Ethereum chains, also verify eth_chainId is available via HTTP
|
|
302
|
+
if (isEthereumChain) {
|
|
303
|
+
const ethChainIdAvailable = await checkHttpMethod("eth_chainId");
|
|
304
|
+
return ethChainIdAvailable;
|
|
305
|
+
}
|
|
306
|
+
// For non-Ethereum chains, system_chain being available is enough
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
catch (_e) {
|
|
310
|
+
// HTTP service not ready yet
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function findPortsByPid(pid, retryCount = 600, retryDelay = 100) {
|
|
319
|
+
for (let i = 0; i < retryCount; i++) {
|
|
320
|
+
try {
|
|
321
|
+
const { stdout } = await execAsync(`lsof -p ${pid} -n -P | grep LISTEN`);
|
|
322
|
+
const ports = [];
|
|
323
|
+
const lines = stdout.split("\n");
|
|
324
|
+
for (const line of lines) {
|
|
325
|
+
// Example outputs:
|
|
326
|
+
// - lsof node 97796 romarq 26u IPv6 0xb6c3e894a2247189 0t0 TCP *:8000 (LISTEN)
|
|
327
|
+
// - lsof node 97242 romarq 26u IPv6 0x330c461cca8d2b63 0t0 TCP [::1]:8000 (LISTEN)
|
|
328
|
+
const regex = /(?:.+):(\d+)/;
|
|
329
|
+
const match = line.match(regex);
|
|
330
|
+
if (match) {
|
|
331
|
+
ports.push(Number(match[1]));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (ports.length) {
|
|
335
|
+
return ports;
|
|
336
|
+
}
|
|
337
|
+
throw new Error("Could not find any ports");
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
if (i === retryCount - 1) {
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
345
|
+
}
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
async function pullImage(imageName, docker) {
|
|
349
|
+
console.log(`Pulling Docker image: ${imageName}`);
|
|
350
|
+
const pullStream = await docker.pull(imageName);
|
|
351
|
+
// Dockerode pull doesn't wait for completion by default ðŸ«
|
|
352
|
+
await new Promise((resolve, reject) => {
|
|
353
|
+
docker.modem.followProgress(pullStream, (err, output) => {
|
|
354
|
+
if (err) {
|
|
355
|
+
reject(err);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
resolve(output);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../../src/cli/internal/logging.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const originalWrite = process.stderr.write.bind(process.stderr);
|
|
2
|
+
const blockList = [
|
|
3
|
+
"has multiple versions, ensure that there is only one installed",
|
|
4
|
+
"Unable to map [u8; 32] to a lookup index",
|
|
5
|
+
"Either remove and explicitly install matching versions or dedupe using your package manager.",
|
|
6
|
+
];
|
|
7
|
+
process.stderr.write = (chunk, encodingOrCallback, callback) => {
|
|
8
|
+
let shouldWrite = true;
|
|
9
|
+
if (typeof chunk === "string") {
|
|
10
|
+
shouldWrite = !blockList.some((phrase) => chunk.includes(phrase));
|
|
11
|
+
}
|
|
12
|
+
if (shouldWrite) {
|
|
13
|
+
if (typeof encodingOrCallback === "function") {
|
|
14
|
+
// Second argument must always be BufferEncoding or undefined.
|
|
15
|
+
// When encodingOrCallback is a function, pass as cb; encoding is undefined.
|
|
16
|
+
return originalWrite.call(process.stderr, chunk, undefined, encodingOrCallback);
|
|
17
|
+
}
|
|
18
|
+
return originalWrite.call(process.stderr, chunk, encodingOrCallback, callback);
|
|
19
|
+
}
|
|
20
|
+
// Suppress output but invoke callback if present
|
|
21
|
+
const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
|
|
22
|
+
if (cb)
|
|
23
|
+
cb(null);
|
|
24
|
+
return true;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import type { DevLaunchSpec } from "../../api/types/index.js";
|
|
4
|
+
import Docker from "dockerode";
|
|
5
|
+
/**
|
|
6
|
+
* Extended ChildProcess interface with Moonwall termination tracking
|
|
7
|
+
*/
|
|
8
|
+
export interface MoonwallProcess extends ChildProcess {
|
|
9
|
+
/**
|
|
10
|
+
* Flag indicating if this process is being terminated by Moonwall
|
|
11
|
+
*/
|
|
12
|
+
isMoonwallTerminating?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Reason for Moonwall-initiated termination
|
|
15
|
+
*/
|
|
16
|
+
moonwallTerminationReason?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Effect-based cleanup function for automatic resource management
|
|
19
|
+
*/
|
|
20
|
+
effectCleanup?: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export declare function launchNode(options: {
|
|
23
|
+
command: string;
|
|
24
|
+
args: string[];
|
|
25
|
+
name: string;
|
|
26
|
+
launchSpec?: DevLaunchSpec;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
runningNode: Docker.Container;
|
|
29
|
+
fsStream: fs.WriteStream;
|
|
30
|
+
} | {
|
|
31
|
+
runningNode: MoonwallProcess;
|
|
32
|
+
}>;
|
|
33
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/cli/internal/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,MAAM,SAAS,CAAC;AAMzB,OAAO,KAAK,EAAE,aAAa,EAAE,iCAAiC;AAC9D,OAAO,MAAM,MAAM,WAAW,CAAC;AAQ/B;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAiFD,wBAAsB,UAAU,CAAC,OAAO,EAAE;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,aAAa,CAAC;CAC5B;;;;;GAwCA"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import WebSocket from "ws";
|
|
4
|
+
import { checkAccess, checkExists } from "./fileCheckers.js";
|
|
5
|
+
import { createLogger } from "../../util/index.js";
|
|
6
|
+
import { setTimeout as timer } from "node:timers/promises";
|
|
7
|
+
import Docker from "dockerode";
|
|
8
|
+
import invariant from "tiny-invariant";
|
|
9
|
+
import { isEthereumDevConfig, isEthereumZombieConfig } from "../lib/configReader.js";
|
|
10
|
+
import { launchNodeEffect } from "./effect/index.js";
|
|
11
|
+
const logger = createLogger({ name: "node" });
|
|
12
|
+
const debug = logger.debug.bind(logger);
|
|
13
|
+
// TODO: Add multi-threading support
|
|
14
|
+
async function launchDockerContainer(imageName, args, name, dockerConfig) {
|
|
15
|
+
const docker = new Docker();
|
|
16
|
+
const port = args.find((a) => a.includes("port"))?.split("=")[1];
|
|
17
|
+
debug(`\x1b[36mStarting Docker container ${imageName} on port ${port}...\x1b[0m`);
|
|
18
|
+
const dirPath = path.join(process.cwd(), "tmp", "node_logs");
|
|
19
|
+
const logLocation = path.join(dirPath, `${name}_docker_${Date.now()}.log`);
|
|
20
|
+
const fsStream = fs.createWriteStream(logLocation);
|
|
21
|
+
process.env.MOON_LOG_LOCATION = logLocation;
|
|
22
|
+
const portBindings = dockerConfig?.exposePorts?.reduce((acc, { hostPort, internalPort }) => {
|
|
23
|
+
acc[`${internalPort}/tcp`] = [{ HostPort: hostPort.toString() }];
|
|
24
|
+
return acc;
|
|
25
|
+
}, {});
|
|
26
|
+
const rpcPort = args.find((a) => a.includes("rpc-port"))?.split("=")[1];
|
|
27
|
+
invariant(rpcPort, "RPC port not found, this is a bug");
|
|
28
|
+
const containerOptions = {
|
|
29
|
+
Image: imageName,
|
|
30
|
+
platform: "linux/amd64",
|
|
31
|
+
Cmd: args,
|
|
32
|
+
name: dockerConfig?.containerName || `moonwall_${name}_${Date.now()}`,
|
|
33
|
+
ExposedPorts: {
|
|
34
|
+
...Object.fromEntries(dockerConfig?.exposePorts?.map(({ internalPort }) => [`${internalPort}/tcp`, {}]) || []),
|
|
35
|
+
[`${rpcPort}/tcp`]: {},
|
|
36
|
+
},
|
|
37
|
+
HostConfig: {
|
|
38
|
+
PortBindings: {
|
|
39
|
+
...portBindings,
|
|
40
|
+
[`${rpcPort}/tcp`]: [{ HostPort: rpcPort }],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
Env: dockerConfig?.runArgs?.filter((arg) => arg.startsWith("env:")).map((arg) => arg.slice(4)),
|
|
44
|
+
};
|
|
45
|
+
try {
|
|
46
|
+
await pullImage(imageName, docker);
|
|
47
|
+
const container = await docker.createContainer(containerOptions);
|
|
48
|
+
await container.start();
|
|
49
|
+
const containerInfo = await container.inspect();
|
|
50
|
+
if (!containerInfo.State.Running) {
|
|
51
|
+
const errorMessage = `Container failed to start: ${containerInfo.State.Error}`;
|
|
52
|
+
console.error(errorMessage);
|
|
53
|
+
fs.appendFileSync(logLocation, `${errorMessage}\n`);
|
|
54
|
+
throw new Error(errorMessage);
|
|
55
|
+
}
|
|
56
|
+
for (let i = 0; i < 300; i++) {
|
|
57
|
+
const isReady = await checkWebSocketJSONRPC(Number.parseInt(rpcPort, 10));
|
|
58
|
+
if (isReady) {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
await timer(100);
|
|
62
|
+
}
|
|
63
|
+
return { runningNode: container, fsStream };
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
console.error(`Docker container launch failed: ${error.message}`);
|
|
68
|
+
fs.appendFileSync(logLocation, `Docker launch error: ${error.message}\n`);
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export async function launchNode(options) {
|
|
74
|
+
const { command: cmd, args, name, launchSpec: config } = options;
|
|
75
|
+
if (config?.useDocker) {
|
|
76
|
+
return launchDockerContainer(cmd, args, name, config.dockerConfig);
|
|
77
|
+
}
|
|
78
|
+
if (cmd.includes("moonbeam")) {
|
|
79
|
+
await checkExists(cmd);
|
|
80
|
+
checkAccess(cmd);
|
|
81
|
+
}
|
|
82
|
+
// Determine if this is an Ethereum chain based on args
|
|
83
|
+
const isEthereumChain = args.some((arg) => arg.includes("--ethapi") || arg.includes("--eth-rpc") || arg.includes("--enable-evm"));
|
|
84
|
+
const { result, cleanup } = await launchNodeEffect({
|
|
85
|
+
command: cmd,
|
|
86
|
+
args,
|
|
87
|
+
name,
|
|
88
|
+
launchSpec: config,
|
|
89
|
+
isEthereumChain,
|
|
90
|
+
});
|
|
91
|
+
logger.debug(`✅ Node '${name}' started with PID ${result.runningNode.pid} on port ${result.port}`);
|
|
92
|
+
process.env.MOON_LOG_LOCATION = result.logPath;
|
|
93
|
+
// CRITICAL: Set MOONWALL_RPC_PORT and WSS_URL so tests can connect
|
|
94
|
+
process.env.MOONWALL_RPC_PORT = result.port.toString();
|
|
95
|
+
process.env.WSS_URL = `ws://127.0.0.1:${result.port}`;
|
|
96
|
+
debug(`Set MOONWALL_RPC_PORT=${result.port}, WSS_URL=${process.env.WSS_URL}`);
|
|
97
|
+
// Store cleanup function for later teardown
|
|
98
|
+
const moonwallNode = result.runningNode;
|
|
99
|
+
moonwallNode.effectCleanup = cleanup;
|
|
100
|
+
return { runningNode: moonwallNode };
|
|
101
|
+
}
|
|
102
|
+
async function checkWebSocketJSONRPC(port) {
|
|
103
|
+
try {
|
|
104
|
+
// Determine if this is an Ethereum-compatible chain from config
|
|
105
|
+
const isEthereumChain = isEthereumDevConfig() || isEthereumZombieConfig();
|
|
106
|
+
// First check WebSocket availability
|
|
107
|
+
const ws = new WebSocket(`ws://localhost:${port}`);
|
|
108
|
+
const checkWsMethod = async (method) => {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const timeout = setTimeout(() => {
|
|
111
|
+
resolve(false);
|
|
112
|
+
}, 5000);
|
|
113
|
+
ws.send(JSON.stringify({
|
|
114
|
+
jsonrpc: "2.0",
|
|
115
|
+
id: Math.floor(Math.random() * 10000),
|
|
116
|
+
method,
|
|
117
|
+
params: [],
|
|
118
|
+
}));
|
|
119
|
+
const messageHandler = (data) => {
|
|
120
|
+
try {
|
|
121
|
+
const response = JSON.parse(data.toString());
|
|
122
|
+
if (response.jsonrpc === "2.0" && !response.error) {
|
|
123
|
+
clearTimeout(timeout);
|
|
124
|
+
ws.removeListener("message", messageHandler);
|
|
125
|
+
resolve(true);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (_error) {
|
|
129
|
+
// Ignore parse errors
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
ws.on("message", messageHandler);
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
const wsResult = await new Promise((resolve) => {
|
|
136
|
+
ws.on("open", async () => {
|
|
137
|
+
try {
|
|
138
|
+
// Check system_chain first via WebSocket (works for all chains)
|
|
139
|
+
const systemChainAvailable = await checkWsMethod("system_chain");
|
|
140
|
+
if (!systemChainAvailable) {
|
|
141
|
+
resolve(false);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// For Ethereum-compatible chains, also check eth_chainId via WebSocket
|
|
145
|
+
if (isEthereumChain) {
|
|
146
|
+
const ethChainIdAvailable = await checkWsMethod("eth_chainId");
|
|
147
|
+
if (!ethChainIdAvailable) {
|
|
148
|
+
resolve(false);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// WebSocket checks passed
|
|
153
|
+
resolve(true);
|
|
154
|
+
}
|
|
155
|
+
catch (_error) {
|
|
156
|
+
resolve(false);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
ws.on("error", () => {
|
|
160
|
+
resolve(false);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
ws?.close();
|
|
164
|
+
if (!wsResult) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
// Now also check HTTP service is ready
|
|
168
|
+
const httpUrl = `http://localhost:${port}`;
|
|
169
|
+
const checkHttpMethod = async (method) => {
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch(httpUrl, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: { "Content-Type": "application/json" },
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
jsonrpc: "2.0",
|
|
176
|
+
id: Math.floor(Math.random() * 10000),
|
|
177
|
+
method,
|
|
178
|
+
params: [],
|
|
179
|
+
}),
|
|
180
|
+
});
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
const data = await response.json();
|
|
185
|
+
return !data.error;
|
|
186
|
+
}
|
|
187
|
+
catch (_error) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
try {
|
|
192
|
+
// Always check system_chain via HTTP (works for all chains)
|
|
193
|
+
const systemChainAvailable = await checkHttpMethod("system_chain");
|
|
194
|
+
if (!systemChainAvailable) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
// For Ethereum chains, also verify eth_chainId is available via HTTP
|
|
198
|
+
if (isEthereumChain) {
|
|
199
|
+
const ethChainIdAvailable = await checkHttpMethod("eth_chainId");
|
|
200
|
+
return ethChainIdAvailable;
|
|
201
|
+
}
|
|
202
|
+
// For non-Ethereum chains, system_chain being available is enough
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
catch (_error) {
|
|
206
|
+
// HTTP service not ready yet
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function pullImage(imageName, docker) {
|
|
215
|
+
console.log(`Pulling Docker image: ${imageName}`);
|
|
216
|
+
const pullStream = await docker.pull(imageName);
|
|
217
|
+
// Dockerode pull doesn't wait for completion by default ðŸ«
|
|
218
|
+
await new Promise((resolve, reject) => {
|
|
219
|
+
docker.modem.followProgress(pullStream, (err, output) => {
|
|
220
|
+
if (err) {
|
|
221
|
+
reject(err);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
resolve(output);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ChildProcessWithoutNullStreams } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a promise with a timeout. If the original promise does not resolve within the specified time, it rejects.
|
|
4
|
+
* @param promise The original promise to wrap.
|
|
5
|
+
* @param ms The timeout duration in milliseconds.
|
|
6
|
+
* @returns A promise that either resolves/rejects with the original promise or rejects with a timeout error.
|
|
7
|
+
*/
|
|
8
|
+
export declare const withTimeout: <T>(promise: Promise<T>, ms: number) => Promise<T>;
|
|
9
|
+
export declare function runTask(cmd: string, { cwd, env }?: {
|
|
10
|
+
cwd: string;
|
|
11
|
+
env?: NodeJS.ProcessEnv;
|
|
12
|
+
}, title?: string): Promise<string>;
|
|
13
|
+
export declare function spawnTask(cmd: string, { cwd, env }?: {
|
|
14
|
+
cwd: string;
|
|
15
|
+
env?: NodeJS.ProcessEnv;
|
|
16
|
+
}, title?: string): Promise<ChildProcessWithoutNullStreams>;
|
|
17
|
+
//# sourceMappingURL=processHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processHelpers.d.ts","sourceRoot":"","sources":["../../../src/cli/internal/processHelpers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAKzE;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAE,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,CAAC,CAOzE,CAAC;AAGF,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,EAAE,GAAG,EAAE,GAAG,EAAE,GAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAEnD,EACD,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAWjB;AAGD,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,EAAE,GAAG,EAAE,GAAG,EAAE,GAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAEnD,EACD,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,8BAA8B,CAAC,CAqBzC"}
|