@logrix/sdk 0.3.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.
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initCommand = initCommand;
37
+ const commander_1 = require("commander");
38
+ const prompts_1 = require("@inquirer/prompts");
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const templates_1 = require("../templates");
42
+ const NETWORKS = {
43
+ "ethereum": { name: "ethereum", chainId: 1, rpcUrl: "https://eth.llamarpc.com" },
44
+ "arbitrum-one": { name: "arbitrum-one", chainId: 42161, rpcUrl: "https://arb1.arbitrum.io/rpc" },
45
+ "base": { name: "base", chainId: 8453, rpcUrl: "https://mainnet.base.org" },
46
+ "polygon": { name: "polygon", chainId: 137, rpcUrl: "https://polygon-rpc.com" },
47
+ "arbitrum-sepolia": { name: "arbitrum-sepolia", chainId: 421614, rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc" },
48
+ "sepolia": { name: "sepolia", chainId: 11155111, rpcUrl: "https://rpc.sepolia.org" },
49
+ };
50
+ function initCommand() {
51
+ const cmd = new commander_1.Command("init");
52
+ cmd
53
+ .description("Scaffold a new Logrix indexer project")
54
+ .argument("[name]", "Project directory name")
55
+ .option("-y, --yes", "Skip prompts and use defaults (non-interactive)")
56
+ .option("--network <network>", "Network preset name (e.g. arbitrum-one, ethereum, base)")
57
+ .option("--rpc <url>", "Custom RPC endpoint URL")
58
+ .option("--contract-name <name>", "Contract name")
59
+ .option("--address <address>", "Contract target address")
60
+ .option("--start-block <block>", "Starting block number")
61
+ .action(async (nameArg, opts) => {
62
+ console.log("\nLogrix Project Initializer\n");
63
+ const isNonInteractive = opts.yes || !process.stdin.isTTY;
64
+ // Project Name
65
+ let projectName = nameArg || opts.name;
66
+ if (!projectName) {
67
+ if (isNonInteractive) {
68
+ projectName = "my-indexer";
69
+ }
70
+ else {
71
+ projectName = await (0, prompts_1.input)({
72
+ message: "Project name:",
73
+ default: "my-indexer",
74
+ validate: (v) => v.trim().length > 0 || "Name cannot be empty",
75
+ });
76
+ }
77
+ }
78
+ projectName = projectName.trim();
79
+ const targetDir = path.resolve(process.cwd(), projectName);
80
+ if (fs.existsSync(targetDir)) {
81
+ console.error(`Error: directory '${projectName}' already exists.`);
82
+ process.exit(1);
83
+ }
84
+ // Network selection
85
+ let networkKey = opts.network;
86
+ if (!networkKey || !NETWORKS[networkKey]) {
87
+ if (isNonInteractive) {
88
+ networkKey = "arbitrum-one";
89
+ }
90
+ else {
91
+ networkKey = await (0, prompts_1.select)({
92
+ message: "Network:",
93
+ choices: Object.entries(NETWORKS).map(([key, net]) => ({
94
+ name: `${net.name} (chain ${net.chainId})`,
95
+ value: key,
96
+ })),
97
+ default: "arbitrum-one",
98
+ });
99
+ }
100
+ }
101
+ const network = NETWORKS[networkKey] || NETWORKS["arbitrum-one"];
102
+ // RPC URL
103
+ let rpcUrl = opts.rpc;
104
+ if (!rpcUrl) {
105
+ if (isNonInteractive) {
106
+ rpcUrl = network.rpcUrl;
107
+ }
108
+ else {
109
+ rpcUrl = await (0, prompts_1.input)({
110
+ message: "RPC URL:",
111
+ default: network.rpcUrl,
112
+ validate: (v) => v.trim().length > 0 || "RPC URL cannot be empty",
113
+ });
114
+ }
115
+ }
116
+ rpcUrl = rpcUrl.trim();
117
+ // Contract Name
118
+ let contractName = opts.contractName;
119
+ if (!contractName) {
120
+ if (isNonInteractive) {
121
+ contractName = "TokenContract";
122
+ }
123
+ else {
124
+ contractName = await (0, prompts_1.input)({
125
+ message: "Contract name (used for ABI file and YAML):",
126
+ default: "TokenContract",
127
+ validate: (v) => /^[A-Za-z][A-Za-z0-9]*$/.test(v.trim()) ||
128
+ "Must be alphanumeric, starting with a letter",
129
+ });
130
+ }
131
+ }
132
+ contractName = contractName.trim();
133
+ // Contract Address
134
+ let contractAddress = opts.address;
135
+ if (!contractAddress) {
136
+ if (isNonInteractive) {
137
+ contractAddress = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";
138
+ }
139
+ else {
140
+ contractAddress = await (0, prompts_1.input)({
141
+ message: "Contract address:",
142
+ default: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
143
+ validate: (v) => /^0x[0-9a-fA-F]{40}$/.test(v.trim()) || "Must be a valid 0x address",
144
+ });
145
+ }
146
+ }
147
+ contractAddress = contractAddress.trim();
148
+ // Start Block
149
+ let startBlockStr = opts.startBlock;
150
+ if (!startBlockStr) {
151
+ if (isNonInteractive) {
152
+ startBlockStr = "0";
153
+ }
154
+ else {
155
+ startBlockStr = await (0, prompts_1.input)({
156
+ message: "Start block:",
157
+ default: "0",
158
+ validate: (v) => /^\d+$/.test(v.trim()) || "Must be a non-negative integer",
159
+ });
160
+ }
161
+ }
162
+ const answers = {
163
+ projectName,
164
+ networkName: network.name,
165
+ chainId: network.chainId,
166
+ rpcUrl,
167
+ contractName,
168
+ contractAddress,
169
+ startBlock: parseInt(startBlockStr.trim(), 10),
170
+ };
171
+ console.log(`Scaffolding in ./${projectName}/ ...\n`);
172
+ // Directory structure
173
+ fs.mkdirSync(path.join(targetDir, "abis"), { recursive: true });
174
+ fs.mkdirSync(path.join(targetDir, "src"), { recursive: true });
175
+ fs.mkdirSync(path.join(targetDir, "build"), { recursive: true });
176
+ // logrix.yaml
177
+ fs.writeFileSync(path.join(targetDir, "logrix.yaml"), (0, templates_1.logrixYamlTemplate)(answers));
178
+ // schema.graphql
179
+ fs.writeFileSync(path.join(targetDir, "schema.graphql"), (0, templates_1.schemaGraphqlTemplate)());
180
+ // Placeholder ABI
181
+ fs.writeFileSync(path.join(targetDir, "abis", `${answers.contractName}.json`), JSON.stringify(templates_1.ERC20_ABI, null, 2));
182
+ // src/mapping.ts
183
+ fs.writeFileSync(path.join(targetDir, "src", "mapping.ts"), (0, templates_1.mappingTemplate)(answers.contractName));
184
+ // package.json
185
+ fs.writeFileSync(path.join(targetDir, "package.json"), JSON.stringify((0, templates_1.packageJsonTemplate)(projectName), null, 2));
186
+ // tsconfig.json (AS config)
187
+ fs.writeFileSync(path.join(targetDir, "tsconfig.json"), JSON.stringify((0, templates_1.tsconfigTemplate)(), null, 2));
188
+ // .gitignore
189
+ fs.writeFileSync(path.join(targetDir, ".gitignore"), (0, templates_1.gitignoreTemplate)());
190
+ // README.md
191
+ fs.writeFileSync(path.join(targetDir, "README.md"), (0, templates_1.readmeTemplate)(projectName));
192
+ console.log("Done. Next steps:\n");
193
+ console.log(` cd ${projectName}`);
194
+ console.log(" npm install");
195
+ console.log(" npm run codegen");
196
+ console.log(" npm run validate");
197
+ console.log(" npm run build\n");
198
+ });
199
+ return cmd;
200
+ }
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.testCommand = testCommand;
37
+ const commander_1 = require("commander");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const child_process_1 = require("child_process");
41
+ const yaml_1 = require("yaml");
42
+ function testCommand() {
43
+ const cmd = new commander_1.Command("test");
44
+ cmd
45
+ .description("Run local tests for indexer handlers, configuration, and WASM build")
46
+ .argument("[testFile]", "Optional specific test file to run")
47
+ .option("--watch", "Watch mode (rerun on file changes)")
48
+ .action(async (testFile, opts) => {
49
+ const projectDir = process.cwd();
50
+ const configPath = path.join(projectDir, "logrix.yaml");
51
+ console.log("\nLogrix Test Runner");
52
+ console.log("------------------------------------------");
53
+ if (!fs.existsSync(configPath)) {
54
+ console.error("Error: logrix.yaml not found. Run `logrix test` from your project root.");
55
+ process.exit(1);
56
+ }
57
+ let failed = false;
58
+ // 1. Validate configuration
59
+ process.stdout.write(" [1/3] Validating configuration & schema... ");
60
+ try {
61
+ const configContent = fs.readFileSync(configPath, "utf-8");
62
+ const config = (0, yaml_1.parse)(configContent);
63
+ if (!config.dataSources || config.dataSources.length === 0) {
64
+ throw new Error("No dataSources found in logrix.yaml");
65
+ }
66
+ const schemaPath = path.join(projectDir, "schema.graphql");
67
+ if (!fs.existsSync(schemaPath)) {
68
+ throw new Error("schema.graphql missing");
69
+ }
70
+ console.log("PASSED");
71
+ }
72
+ catch (e) {
73
+ console.log("FAILED");
74
+ console.error(` ${e.message}`);
75
+ failed = true;
76
+ }
77
+ // 2. Compile AssemblyScript mapping
78
+ process.stdout.write(" [2/3] Compiling AssemblyScript handlers... ");
79
+ try {
80
+ const ascBin = path.join(projectDir, "node_modules", ".bin", "asc");
81
+ const ascCmd = fs.existsSync(ascBin) ? ascBin : "npx asc";
82
+ (0, child_process_1.execSync)(`${ascCmd} src/mapping.ts -o build/test_mapping.wasm -O3 --runtime stub --noAssert`, { cwd: projectDir, stdio: "pipe" });
83
+ console.log("PASSED");
84
+ }
85
+ catch (e) {
86
+ console.log("FAILED");
87
+ console.error(` ${e.stderr ? e.stderr.toString() : e.message}`);
88
+ failed = true;
89
+ }
90
+ // 3. Run user test suite if tests directory or file exists
91
+ const testsDir = path.join(projectDir, "tests");
92
+ const hasTestsDir = fs.existsSync(testsDir);
93
+ if (testFile || hasTestsDir) {
94
+ process.stdout.write(" [3/3] Running handler unit tests... ");
95
+ try {
96
+ const target = testFile
97
+ ? path.resolve(projectDir, testFile)
98
+ : `${testsDir}/**/*.test.{ts,js}`;
99
+ (0, child_process_1.execSync)(`node --test ${target}`, {
100
+ cwd: projectDir,
101
+ stdio: "inherit",
102
+ });
103
+ console.log("PASSED");
104
+ }
105
+ catch {
106
+ console.log("FAILED");
107
+ failed = true;
108
+ }
109
+ }
110
+ else {
111
+ console.log(" [3/3] Handler unit tests... SKIPPED (no tests/ directory found)");
112
+ console.log(" Create tests in tests/*.test.ts to add custom unit tests.");
113
+ }
114
+ console.log("------------------------------------------");
115
+ if (failed) {
116
+ console.error("Result: TESTS FAILED\n");
117
+ process.exit(1);
118
+ }
119
+ else {
120
+ console.log("Result: ALL CHECKS & TESTS PASSED\n");
121
+ }
122
+ });
123
+ return cmd;
124
+ }
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.validateCommand = validateCommand;
37
+ const commander_1 = require("commander");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const yaml_1 = require("yaml");
41
+ function validateCommand() {
42
+ const cmd = new commander_1.Command("validate");
43
+ cmd
44
+ .description("Validate project configuration, ABIs, schema, and handler files")
45
+ .action(() => {
46
+ const projectDir = process.cwd();
47
+ let hasErrors = false;
48
+ console.log("\nValidating Logrix project in:", projectDir, "\n");
49
+ // 1. Check logrix.yaml
50
+ const configPath = path.join(projectDir, "logrix.yaml");
51
+ if (!fs.existsSync(configPath)) {
52
+ console.error(" [FAIL] logrix.yaml not found.");
53
+ process.exit(1);
54
+ }
55
+ let config;
56
+ try {
57
+ config = (0, yaml_1.parse)(fs.readFileSync(configPath, "utf-8"));
58
+ console.log(" [OK] logrix.yaml syntax valid.");
59
+ }
60
+ catch (e) {
61
+ console.error(` [FAIL] logrix.yaml syntax error: ${e.message}`);
62
+ process.exit(1);
63
+ }
64
+ if (!config.dataSources || !Array.isArray(config.dataSources) || config.dataSources.length === 0) {
65
+ console.error(" [FAIL] logrix.yaml must contain at least one dataSource in 'dataSources'.");
66
+ hasErrors = true;
67
+ }
68
+ else {
69
+ console.log(` [OK] Found ${config.dataSources.length} dataSource(s).`);
70
+ }
71
+ // 2. Check each dataSource
72
+ if (Array.isArray(config.dataSources)) {
73
+ for (let i = 0; i < config.dataSources.length; i++) {
74
+ const ds = config.dataSources[i];
75
+ const dsName = ds.name || `dataSource[${i}]`;
76
+ if (!ds.name) {
77
+ console.error(` [FAIL] ${dsName}: missing 'name' attribute.`);
78
+ hasErrors = true;
79
+ }
80
+ const addr = ds.source?.address;
81
+ if (!addr || !/^0x[0-9a-fA-F]{40}$/.test(addr)) {
82
+ console.error(` [FAIL] ${dsName}: source.address '${addr}' is not a valid 40-character 0x hex address.`);
83
+ hasErrors = true;
84
+ }
85
+ else {
86
+ console.log(` [OK] ${dsName}: contract address valid (${addr}).`);
87
+ }
88
+ // Check ABIs
89
+ const abis = ds.mapping?.abis || [];
90
+ if (!Array.isArray(abis) || abis.length === 0) {
91
+ console.error(` [FAIL] ${dsName}: mapping.abis must list at least one ABI file.`);
92
+ hasErrors = true;
93
+ }
94
+ else {
95
+ for (const abiRef of abis) {
96
+ const abiFile = path.resolve(projectDir, abiRef.file);
97
+ if (!fs.existsSync(abiFile)) {
98
+ console.error(` [FAIL] ${dsName}: ABI file not found at ${abiRef.file}`);
99
+ hasErrors = true;
100
+ }
101
+ else {
102
+ try {
103
+ const parsed = JSON.parse(fs.readFileSync(abiFile, "utf-8"));
104
+ if (!Array.isArray(parsed)) {
105
+ console.error(` [FAIL] ${dsName}: ABI file ${abiRef.file} is not a JSON array.`);
106
+ hasErrors = true;
107
+ }
108
+ else {
109
+ const eventCount = parsed.filter((item) => item.type === "event").length;
110
+ console.log(` [OK] ${dsName}: ABI ${abiRef.name} loaded with ${eventCount} event definition(s).`);
111
+ }
112
+ }
113
+ catch (e) {
114
+ console.error(` [FAIL] ${dsName}: ABI file ${abiRef.file} is invalid JSON: ${e.message}`);
115
+ hasErrors = true;
116
+ }
117
+ }
118
+ }
119
+ }
120
+ // Check handler mapping file
121
+ const mappingFile = path.resolve(projectDir, ds.mapping?.file || "src/mapping.ts");
122
+ const mappingSrc = path.resolve(projectDir, "src/mapping.ts");
123
+ if (!fs.existsSync(mappingSrc)) {
124
+ console.error(` [FAIL] ${dsName}: handler source file not found at src/mapping.ts`);
125
+ hasErrors = true;
126
+ }
127
+ else {
128
+ console.log(` [OK] ${dsName}: handler entry point exists at src/mapping.ts`);
129
+ }
130
+ }
131
+ }
132
+ // 3. Check schema.graphql
133
+ const schemaPath = path.join(projectDir, "schema.graphql");
134
+ if (!fs.existsSync(schemaPath)) {
135
+ console.error(" [FAIL] schema.graphql not found.");
136
+ hasErrors = true;
137
+ }
138
+ else {
139
+ const schemaContent = fs.readFileSync(schemaPath, "utf-8");
140
+ const entityCount = (schemaContent.match(/type\s+\w+\s+@entity/g) || []).length;
141
+ if (entityCount === 0) {
142
+ console.error(" [FAIL] schema.graphql has no @entity types declared.");
143
+ hasErrors = true;
144
+ }
145
+ else {
146
+ console.log(` [OK] schema.graphql valid with ${entityCount} @entity definition(s).`);
147
+ }
148
+ }
149
+ console.log("");
150
+ if (hasErrors) {
151
+ console.error("Validation failed. Please address the errors above before building.\n");
152
+ process.exit(1);
153
+ }
154
+ else {
155
+ console.log("Validation passed successfully! All configuration files, ABIs, and schemas are valid.\n");
156
+ }
157
+ });
158
+ return cmd;
159
+ }
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const init_1 = require("./commands/init");
6
+ const codegen_1 = require("./commands/codegen");
7
+ const build_1 = require("./commands/build");
8
+ const add_1 = require("./commands/add");
9
+ const validate_1 = require("./commands/validate");
10
+ const export_values_1 = require("./commands/export_values");
11
+ const test_1 = require("./commands/test");
12
+ const program = new commander_1.Command();
13
+ program
14
+ .name("logrix")
15
+ .description("Developer CLI for Logrix blockchain indexers")
16
+ .version("0.3.0");
17
+ program.addCommand((0, init_1.initCommand)());
18
+ program.addCommand((0, add_1.addCommand)());
19
+ program.addCommand((0, codegen_1.codegenCommand)());
20
+ program.addCommand((0, validate_1.validateCommand)());
21
+ program.addCommand((0, build_1.buildCommand)());
22
+ program.addCommand((0, export_values_1.exportValuesCommand)());
23
+ program.addCommand((0, test_1.testCommand)());
24
+ program.parse(process.argv);