@paimaexample/npm-avail-light-client 0.3.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/binary.js ADDED
@@ -0,0 +1,173 @@
1
+ const os = require("os");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const { spawn } = require("child_process");
5
+ const tar = require("tar");
6
+ const extract = require("extract-zip");
7
+
8
+ const downloadLinks = {
9
+ "apple-arm64":
10
+ "https://github.com/availproject/avail-light/releases/download/avail-light-client-v1.13.0-rc10/avail-light-apple-arm64.tar.gz",
11
+ "apple-x86_64":
12
+ "https://github.com/availproject/avail-light/releases/download/avail-light-client-v1.13.0-rc10/avail-light-apple-x86_64.tar.gz",
13
+ "linux-amd64":
14
+ "https://github.com/availproject/avail-light/releases/download/avail-light-client-v1.13.0-rc10/avail-light-linux-amd64.tar.gz",
15
+ "windows-x86_64":
16
+ "https://github.com/availproject/avail-light/releases/download/avail-light-client-v1.13.0-rc10/avail-light-x86_64-pc-windows-msvc.exe.zip",
17
+ };
18
+
19
+ const getBinaryKey = () => {
20
+ const isLinux = os.platform() === "linux";
21
+ const isWindows = os.platform() === "win32";
22
+ const isApple = os.platform() === "darwin";
23
+ const isArm = os.arch() === "arm64";
24
+ const isX86 = os.arch() === "x64";
25
+
26
+ if (isLinux && isArm) {
27
+ throw new Error(
28
+ "Unsupported platform: Linux ARM64 is not supported for native binary.",
29
+ );
30
+ }
31
+
32
+ if (isLinux && isX86) return "linux-amd64";
33
+
34
+ if (isWindows && isX86) return "windows-x86_64";
35
+
36
+ if (isApple && isArm) return "apple-arm64";
37
+
38
+ if (isApple && isX86) return "apple-x86_64";
39
+
40
+ throw new Error("Unsupported platform");
41
+ };
42
+
43
+ const getBinaryLink = () => {
44
+ const key = getBinaryKey();
45
+ const binaryLink = downloadLinks[key];
46
+ if (!binaryLink) throw new Error("Unsupported platform " + key);
47
+ return binaryLink;
48
+ };
49
+
50
+ const getBinaryPath = () => {
51
+ const key = getBinaryKey();
52
+ const isWindows = key.startsWith("windows");
53
+ const exeName = isWindows ? "avail-light-client.exe" : "avail-light-client";
54
+ return path.join(__dirname, "bin", exeName);
55
+ };
56
+
57
+ const downloadBinary = async () => {
58
+ const binaryLink = getBinaryLink();
59
+ const key = getBinaryKey();
60
+ const binDir = path.join(__dirname, "bin");
61
+ if (!fs.existsSync(binDir)) {
62
+ fs.mkdirSync(binDir, { recursive: true });
63
+ }
64
+
65
+ const tempPath = path.join(__dirname, path.basename(binaryLink));
66
+
67
+ console.log(`Downloading binary from ${binaryLink}...`);
68
+ const response = await fetch(binaryLink);
69
+ if (!response.ok) {
70
+ throw new Error(`Failed to download binary: ${response.statusText}`);
71
+ }
72
+ const fileStream = fs.createWriteStream(tempPath);
73
+ await new Promise(async (resolve, reject) => {
74
+ try {
75
+ for await (const chunk of response.body) {
76
+ fileStream.write(chunk);
77
+ }
78
+ fileStream.end();
79
+ fileStream.on("finish", resolve);
80
+ fileStream.on("error", reject);
81
+ } catch (error) {
82
+ reject(error);
83
+ }
84
+ });
85
+
86
+ console.log("Extracting binary...");
87
+ if (binaryLink.endsWith(".zip")) {
88
+ await extract(tempPath, { dir: path.join(__dirname, "bin") });
89
+ const files = fs.readdirSync(path.join(__dirname, "bin"));
90
+ const exeFile = files.find((f) => f.endsWith(".exe"));
91
+ if (exeFile && exeFile !== "avail-light-client.exe") {
92
+ fs.renameSync(
93
+ path.join(__dirname, "bin", exeFile),
94
+ path.join(__dirname, "bin", "avail-light-client.exe"),
95
+ );
96
+ }
97
+ } else if (binaryLink.endsWith(".tar.gz")) {
98
+ console.log("Extracting tar.gz file...");
99
+ console.log(tempPath);
100
+ console.log(binDir);
101
+ await tar.x({
102
+ file: tempPath,
103
+ cwd: binDir,
104
+ }).catch((err) => {
105
+ throw new Error(`Failed to extract tar.gz file: ${err.message}`);
106
+ });
107
+
108
+ // Verify extraction was successful
109
+ const extractedFiles = fs.readdirSync(binDir);
110
+ console.log(extractedFiles);
111
+ if (extractedFiles.length === 0) {
112
+ throw new Error("No files were extracted from the tar.gz archive");
113
+ }
114
+
115
+ const binaryName = os.platform() === "win32"
116
+ ? "avail-light-client.exe"
117
+ : "avail-light-client";
118
+ if (extractedFiles.length > 0 && extractedFiles[0] !== binaryName) {
119
+ fs.renameSync(
120
+ path.join(binDir, extractedFiles[0]),
121
+ path.join(binDir, binaryName),
122
+ );
123
+ }
124
+
125
+ // Ensure the binary exists
126
+ if (!fs.existsSync(path.join(binDir, binaryName))) {
127
+ throw new Error(
128
+ `Extracted files do not contain expected binary: ${binaryName}`,
129
+ );
130
+ }
131
+ }
132
+ console.log("Binary extracted successfully at " + binDir);
133
+ fs.unlinkSync(tempPath);
134
+
135
+ const binaryPath = getBinaryPath();
136
+ if (!fs.existsSync(binaryPath)) {
137
+ throw new Error(`Binary not found at expected path: ${binaryPath}`);
138
+ }
139
+
140
+ if (os.platform() !== "win32") {
141
+ fs.chmodSync(binaryPath, "755");
142
+ }
143
+
144
+ console.log("Binary ready at:", binaryPath);
145
+ return binaryPath;
146
+ };
147
+
148
+ const runBinary = (args) => {
149
+ const binaryPath = getBinaryPath();
150
+ if (!fs.existsSync(binaryPath)) {
151
+ throw new Error(
152
+ `Binary not found at path: ${binaryPath}. Please run downloadBinary first.`,
153
+ );
154
+ }
155
+ console.log("Running binary at:", binaryPath);
156
+ console.log("Args:", args);
157
+ console.log("CWD:", process.cwd());
158
+ const child = spawn(binaryPath, args, { stdio: "inherit" });
159
+
160
+ child.on("exit", (code) => {
161
+ if (code !== 0) {
162
+ console.error(`Binary process exited with code ${code}`);
163
+ }
164
+ });
165
+ };
166
+
167
+ module.exports = {
168
+ getBinaryKey,
169
+ getBinaryLink,
170
+ getBinaryPath,
171
+ downloadBinary,
172
+ runBinary,
173
+ };
package/config.yml ADDED
@@ -0,0 +1,31 @@
1
+ # Default configuration for avail-light-client
2
+ # For a full list of options, see the official Avail documentation.
3
+
4
+ log_level="info"
5
+ http_server_host="127.0.0.1"
6
+ http_server_port=7007
7
+
8
+ # Secret key for libp2p keypair. Can be either set to 'seed' or to 'key'.
9
+ # If set to seed, keypair will be generated from that seed.
10
+ # If 'secret_key' is not set, a random seed will be used.
11
+ # secret_key={ seed: "avail" }
12
+
13
+ # P2P TCP listener port (default: 37000).
14
+ port=37000
15
+
16
+ # WebSocket endpoint of a full node.
17
+ full_node_ws = ["ws://127.0.0.1:9944"]
18
+
19
+ # Application ID. If not set or set to 0, application client is not started.
20
+ app_id=0
21
+
22
+ # Confidence threshold (default: 92.0).
23
+ confidence=92.0
24
+
25
+ # File system path where RocksDB used by the light client stores its data.
26
+ # This path is relative to the location of this config file.
27
+ avail_path="avail_path"
28
+
29
+ # Vector of Light Client bootstrap nodes.
30
+ # This is for a local setup. Replace with public bootstraps for testnet/mainnet.
31
+ bootstraps=["/ip4/127.0.0.1/tcp/39000/p2p/12D3KooWMm1c4pzeLPGkkCJMAgFbsfQ8xmVDusg272icWsaNHWzN"]
package/docker.js ADDED
@@ -0,0 +1,79 @@
1
+ const { exec } = require('child_process');
2
+ const util = require('util');
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ const execPromise = util.promisify(exec);
7
+
8
+ const checkIfDockerExists = async () => {
9
+ try {
10
+ await execPromise('docker --version');
11
+ return true;
12
+ } catch (error) {
13
+ return false;
14
+ }
15
+ };
16
+
17
+ const runDockerContainer = ({ args = [] }) => {
18
+ let finalArgs = [...args];
19
+ let tag = 'availj/avail-light:avail-light-client-v1.13.0-rc11';
20
+ let p2pPort = 37000;
21
+ let httpPort = 7007;
22
+ let volumeMounts = [];
23
+
24
+ const extractArgValue = (argName, arr) => {
25
+ const index = arr.indexOf(argName);
26
+ if (index > -1 && index < arr.length - 1) {
27
+ const value = arr[index + 1];
28
+ arr.splice(index, 2);
29
+ return value;
30
+ }
31
+ return undefined;
32
+ };
33
+
34
+ const customTag = extractArgValue('--docker-tag', finalArgs);
35
+ if (customTag) tag = customTag;
36
+
37
+ const customP2pPort = extractArgValue('--p2p-port', finalArgs);
38
+ if (customP2pPort) p2pPort = customP2pPort;
39
+
40
+ const customHttpPort = extractArgValue('--http-port', finalArgs);
41
+ if (customHttpPort) httpPort = customHttpPort;
42
+
43
+ const configArgIndex = finalArgs.findIndex(arg => arg === '--config');
44
+ if (configArgIndex === -1) {
45
+ console.error('FATAL: --config flag with a path is required for Docker execution.');
46
+ process.exit(1);
47
+ }
48
+
49
+ const hostConfigPath = path.resolve(finalArgs[configArgIndex + 1]);
50
+ const hostWorkDir = path.dirname(hostConfigPath);
51
+ const configFileName = path.basename(hostConfigPath);
52
+ const containerWorkDir = '/data';
53
+ const containerConfigPath = path.join(containerWorkDir, configFileName);
54
+
55
+ volumeMounts.push(`-v "${hostWorkDir}:${containerWorkDir}"`);
56
+ finalArgs[configArgIndex + 1] = containerConfigPath;
57
+
58
+ const portMappings = `-p ${p2pPort}:37000 -p ${httpPort}:7007`;
59
+
60
+ const command = `docker run --rm -it ${volumeMounts.join(' ')} ${portMappings} ${tag} ${finalArgs.join(' ')}`;
61
+
62
+ console.log(`Executing Docker command: ${command}`);
63
+ const child = exec(command);
64
+
65
+ child.stdout.pipe(process.stdout);
66
+ child.stderr.pipe(process.stderr);
67
+
68
+ child.on('exit', (code) => {
69
+ if (code !== 0) {
70
+ console.error(`Docker container exited with code ${code}`);
71
+ }
72
+ });
73
+ };
74
+
75
+ module.exports = {
76
+ checkIfDockerExists,
77
+ runDockerContainer,
78
+ };
79
+
package/identity.toml ADDED
@@ -0,0 +1 @@
1
+ avail_secret_uri = 'discover casual enrich injury install chronic useful foot deny brave alter patrol chaos dice essence stomach ski stairs multiply will relief hobby quarter decline'
package/index.js ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { getBinaryKey, downloadBinary, runBinary, getBinaryPath } = require('./binary');
6
+ const { checkIfDockerExists, runDockerContainer } = require('./docker');
7
+
8
+ const helpMessage = `
9
+ NAME:
10
+ avail-light-client - A wrapper for the Avail light client.
11
+
12
+ DESCRIPTION:
13
+ A user-friendly npm package that manages and runs the Avail light client command-line tool.
14
+ It uses a native binary on supported platforms and seamlessly falls back to a Docker container
15
+ on other operating systems or when explicitly requested.
16
+
17
+ USAGE:
18
+ avail-light-client [WRAPPER_OPTIONS] -- [EXECUTABLE_OPTIONS]
19
+ npx avail-light-client [WRAPPER_OPTIONS] -- [EXECUTABLE_OPTIONS]
20
+
21
+ WRAPPER OPTIONS:
22
+ --help, -h Prints this help message and exits.
23
+ --docker Forces the use of Docker, even on a natively supported OS.
24
+ --docker-tag <TAG> Specifies a custom Docker image tag to use.
25
+ --p2p-port <PORT> Maps a host port to the container's p2p port (37000).
26
+ --http-port <PORT> Maps a host port to the container's http port (7007).
27
+
28
+ EXECUTABLE OPTIONS (passed to the avail-light binary/Docker container):
29
+ --config <PATH> Specifies the location of the configuration file.
30
+ (Default: The 'config.yml' included with the package)
31
+ --network <NETWORK> Select a network (e.g., local, turing).
32
+ --identity <PATH> Specifies the location of the identity file.
33
+ --app-id <ID> The appID for the application client.
34
+ --verbosity <LEVEL> Sets the log level (e.g., info, debug, trace).
35
+
36
+ For a full list of executable flags and options, please refer to the official Avail documentation.
37
+
38
+ CONFIGURATION:
39
+ A 'config.yml' file is included with this package. You can use it as a starting point
40
+ by copying it, modifying it, and then pointing to it with the '--config' option.
41
+ `;
42
+
43
+ const main = async () => {
44
+ let args = process.argv.slice(2);
45
+
46
+ if (args.includes('--help') || args.includes('-h')) {
47
+ console.log(helpMessage);
48
+ process.exit(0);
49
+ }
50
+
51
+ const dockerFlagIndex = args.indexOf('--docker');
52
+ let useDocker = false;
53
+ if (dockerFlagIndex !== -1) {
54
+ useDocker = true;
55
+ }
56
+
57
+ let configPath;
58
+ const configArgIndex = args.findIndex(arg => arg === '--config');
59
+
60
+ if (configArgIndex !== -1 && args[configArgIndex + 1]) {
61
+ configPath = args[configArgIndex + 1];
62
+ } else {
63
+ configPath = path.join(__dirname, 'config.yml');
64
+ }
65
+
66
+ const absoluteConfigPath = path.resolve(configPath);
67
+
68
+ if (!fs.existsSync(absoluteConfigPath)) {
69
+ console.error(`Error: Configuration file not found at '${absoluteConfigPath}'`);
70
+ process.exit(1);
71
+ }
72
+
73
+ if (configArgIndex !== -1) {
74
+ args[configArgIndex + 1] = absoluteConfigPath;
75
+ } else {
76
+ const dockerArgs = useDocker ? args.filter(arg => !arg.startsWith('--')) : [];
77
+ args.push('--config', absoluteConfigPath, ...dockerArgs);
78
+ }
79
+
80
+
81
+ let isNativeSupported = true;
82
+ try {
83
+ getBinaryKey();
84
+ } catch (e) {
85
+ isNativeSupported = false;
86
+ }
87
+
88
+ const willUseDocker = useDocker || !isNativeSupported;
89
+
90
+ if (willUseDocker) {
91
+ console.log('Attempting to run with Docker...');
92
+ const dockerExists = await checkIfDockerExists();
93
+ if (!dockerExists) {
94
+ let errorMessage = 'Docker is not installed or the Docker daemon is not running. Please install Docker and try again.';
95
+ if (!isNativeSupported) {
96
+ errorMessage = `Your OS is not supported for native binaries, and Docker is not available to use as a fallback. Please install Docker.`;
97
+ } else if (useDocker) {
98
+ errorMessage = `You passed the --docker flag, but Docker is not available. Please install Docker or run without the --docker flag.`;
99
+ }
100
+ console.error(errorMessage);
101
+ process.exit(1);
102
+ }
103
+ runDockerContainer({ args });
104
+ } else {
105
+ const binaryPath = getBinaryPath();
106
+ try {
107
+ if (!fs.existsSync(binaryPath)) {
108
+ console.log('Binary not found, downloading...');
109
+ await downloadBinary();
110
+ }
111
+ const finalArgs = args.filter(arg => arg !== '--docker');
112
+ runBinary(finalArgs);
113
+ } catch (error) {
114
+ console.error('Failed to run native binary:', error.message);
115
+ console.log('Attempting to fall back to Docker...');
116
+ const dockerExists = await checkIfDockerExists();
117
+ if (!dockerExists) {
118
+ console.error('Failed to run native binary and Docker is not available to use as a fallback. Please install Docker.');
119
+ process.exit(1);
120
+ }
121
+ runDockerContainer({ args });
122
+ }
123
+ }
124
+ };
125
+
126
+ main().catch(error => {
127
+ console.error(`An unexpected error occurred: ${error.message}`);
128
+ process.exit(1);
129
+ });
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@paimaexample/npm-avail-light-client",
3
+ "version": "0.3.15",
4
+ "description": "A wrapper for the Avail Light Client CLI",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "npm-avail-light-client": "index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "avail",
14
+ "blockchain"
15
+ ],
16
+ "type": "commonjs",
17
+ "author": "Paima Studios",
18
+ "license": "ISC",
19
+ "dependencies": {
20
+ "extract-zip": "^2.0.1",
21
+ "tar": "^6.2.0"
22
+ }
23
+ }