create-eclesia-indexer 1.1.0 → 2.16.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/bin/create-eclesia-indexer.js +2 -1
- package/dist/index.cjs +57 -20
- package/dist/{index.js → index.mjs} +48 -15
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -17
- package/templates/basic/.dockerignore +7 -0
- package/templates/basic/.env.template +6 -1
- package/templates/basic/Dockerfile.npm +15 -21
- package/templates/basic/Dockerfile.pnpm +16 -25
- package/templates/basic/Dockerfile.yarn +14 -21
- package/templates/basic/LICENSE.md +223 -0
- package/templates/basic/README.md.template +11 -10
- package/templates/basic/_gitignore +4 -0
- package/templates/basic/docker-compose.yml.template +12 -8
- package/templates/basic/package.json.template +10 -6
- package/templates/basic/src/index.ts.template +6 -2
- package/templates/basic/tsconfig.json.template +2 -2
- package/templates/basic/tsdown.config.ts +4 -2
- package/dist/index.js.map +0 -1
- /package/dist/{index.d.ts → index.d.mts} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -7,12 +7,16 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
9
|
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
+
key = keys[i];
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
14
|
+
__defProp(to, key, {
|
|
15
|
+
get: ((k) => from[k]).bind(null, key),
|
|
16
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
16
20
|
}
|
|
17
21
|
return to;
|
|
18
22
|
};
|
|
@@ -25,18 +29,31 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
29
|
let node_fs = require("node:fs");
|
|
26
30
|
let node_path = require("node:path");
|
|
27
31
|
node_path = __toESM(node_path);
|
|
28
|
-
let node_url = require("node:url");
|
|
29
32
|
let picocolors = require("picocolors");
|
|
30
33
|
picocolors = __toESM(picocolors);
|
|
31
34
|
let enquirer = require("enquirer");
|
|
32
35
|
enquirer = __toESM(enquirer);
|
|
33
36
|
let fs_extra = require("fs-extra");
|
|
34
37
|
fs_extra = __toESM(fs_extra);
|
|
38
|
+
let node_crypto = require("node:crypto");
|
|
39
|
+
let node_url = require("node:url");
|
|
35
40
|
|
|
36
41
|
//#region src/create-indexer.ts
|
|
37
42
|
const { prompt } = enquirer.default;
|
|
38
43
|
const { ensureDirSync, copySync, writeFileSync, readFileSync } = fs_extra.default;
|
|
39
44
|
const __dirname$1 = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
45
|
+
/** npm package name rules, restricted to what also works as a directory and a bin name */
|
|
46
|
+
const PROJECT_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,212}[a-z0-9])?$/;
|
|
47
|
+
const CHAIN_PREFIX_PATTERN = /^[a-z][a-z0-9]*$/;
|
|
48
|
+
const CHAIN_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9 ._-]*$/;
|
|
49
|
+
function validateProjectName(value) {
|
|
50
|
+
if (!PROJECT_NAME_PATTERN.test(value)) return "Use lowercase letters, digits, dots, hyphens or underscores (an npm package name without a scope); no slashes or spaces.";
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/** Random secret safe for .env files and connection strings (URL-safe base64, no padding) */
|
|
54
|
+
function generateSecret(bytes) {
|
|
55
|
+
return (0, node_crypto.randomBytes)(bytes).toString("base64url");
|
|
56
|
+
}
|
|
40
57
|
const availableModules = [
|
|
41
58
|
{
|
|
42
59
|
name: "Auth",
|
|
@@ -64,6 +81,10 @@ const minimalAvailableModules = [{
|
|
|
64
81
|
hint: "Token transfers and balances"
|
|
65
82
|
}];
|
|
66
83
|
async function createIndexer(initialProjectName) {
|
|
84
|
+
if (initialProjectName !== void 0) {
|
|
85
|
+
const valid = validateProjectName(initialProjectName);
|
|
86
|
+
if (valid !== true) throw new Error("Invalid project name '" + initialProjectName + "': " + valid);
|
|
87
|
+
}
|
|
67
88
|
const config = await gatherProjectInfo(initialProjectName);
|
|
68
89
|
const targetDir = (0, node_path.resolve)(process.cwd(), config.projectName);
|
|
69
90
|
console.log(picocolors.default.blue("📁 Creating project directory..."));
|
|
@@ -83,7 +104,8 @@ async function createIndexer(initialProjectName) {
|
|
|
83
104
|
console.log(picocolors.default.cyan(` cd ${config.projectName}`));
|
|
84
105
|
console.log(picocolors.default.cyan(` ${config.packageManager} local-dev:start # To run a self-contained local development environment with Postgres`));
|
|
85
106
|
console.log();
|
|
86
|
-
console.log("
|
|
107
|
+
console.log("Credentials for the local environment (Postgres password, Hasura admin secret) were generated into .env. To use an external Postgres instead, change PG_CONNECTION_STRING there.");
|
|
108
|
+
if (config.modules.includes("Bank") && !config.processGenesis) console.log(picocolors.default.yellow("Bank module without genesis processing: balances are tracked as changes since the start height, not absolute amounts."));
|
|
87
109
|
console.log();
|
|
88
110
|
console.log(picocolors.default.cyan(` ${config.packageManager} start # To run the indexer`));
|
|
89
111
|
console.log();
|
|
@@ -97,25 +119,29 @@ async function gatherProjectInfo(initialProjectName) {
|
|
|
97
119
|
name: "projectName",
|
|
98
120
|
message: "Project name:",
|
|
99
121
|
initial: initialProjectName || "my-indexer",
|
|
100
|
-
skip: !!initialProjectName
|
|
122
|
+
skip: !!initialProjectName,
|
|
123
|
+
validate: validateProjectName
|
|
101
124
|
},
|
|
102
125
|
{
|
|
103
126
|
type: "input",
|
|
104
127
|
name: "chainName",
|
|
105
128
|
message: "Chain name:",
|
|
106
|
-
initial: "cosmos-hub"
|
|
129
|
+
initial: "cosmos-hub",
|
|
130
|
+
validate: (value) => CHAIN_NAME_PATTERN.test(value) || "Use letters, digits, spaces, dots, hyphens or underscores."
|
|
107
131
|
},
|
|
108
132
|
{
|
|
109
133
|
type: "input",
|
|
110
134
|
name: "chainPrefix",
|
|
111
135
|
message: "Chain address prefix:",
|
|
112
|
-
initial: "cosmos"
|
|
136
|
+
initial: "cosmos",
|
|
137
|
+
validate: (value) => CHAIN_PREFIX_PATTERN.test(value) || "A bech32 prefix is lowercase letters and digits, starting with a letter."
|
|
113
138
|
},
|
|
114
139
|
{
|
|
115
140
|
type: "input",
|
|
116
141
|
name: "description",
|
|
117
142
|
message: "Description:",
|
|
118
|
-
initial: "A custom Cosmos SDK chain indexer"
|
|
143
|
+
initial: "A custom Cosmos SDK chain indexer",
|
|
144
|
+
validate: (value) => !/[\r\n]/.test(value) || "Keep the description on one line."
|
|
119
145
|
},
|
|
120
146
|
{
|
|
121
147
|
type: "input",
|
|
@@ -235,9 +261,11 @@ async function copyTemplateFiles(config, targetDir) {
|
|
|
235
261
|
if (src.includes(".template")) return false;
|
|
236
262
|
if (config.packageManager !== "pnpm" && src.includes("pnpm-workspace")) return false;
|
|
237
263
|
if (src.includes("Dockerfile")) return false;
|
|
264
|
+
if (src.endsWith("_gitignore")) return false;
|
|
238
265
|
return true;
|
|
239
266
|
} });
|
|
240
267
|
copySync((0, node_path.resolve)(templatesDir, "Dockerfile." + config.packageManager), targetDir + "/Dockerfile");
|
|
268
|
+
copySync((0, node_path.resolve)(templatesDir, "_gitignore"), targetDir + "/.gitignore");
|
|
241
269
|
[
|
|
242
270
|
"package.json.template",
|
|
243
271
|
"src/index.ts.template",
|
|
@@ -255,16 +283,22 @@ async function copyTemplateFiles(config, targetDir) {
|
|
|
255
283
|
}
|
|
256
284
|
async function processTemplates(config, targetDir) {
|
|
257
285
|
let polling = false;
|
|
286
|
+
config.enableHealthcheck = false;
|
|
287
|
+
config.healthCheckPort = 8888;
|
|
258
288
|
config.enablePrometheus = false;
|
|
259
289
|
config.prometheusPort = 9090;
|
|
260
290
|
const url = new URL(config.rpcEndpoint);
|
|
261
291
|
if (url.protocol === "http:" || url.protocol === "https:") polling = true;
|
|
292
|
+
const postgresPassword = generateSecret(18);
|
|
293
|
+
const hasuraAdminSecret = generateSecret(24);
|
|
262
294
|
const templateVars = {
|
|
263
295
|
PROJECT_NAME: config.projectName,
|
|
264
296
|
CHAIN_NAME: config.chainName,
|
|
265
297
|
DESCRIPTION: config.description,
|
|
266
298
|
RPC_ENDPOINT: config.rpcEndpoint,
|
|
267
|
-
PG_CONNECTION_STRING: "postgres://postgres:
|
|
299
|
+
PG_CONNECTION_STRING: "postgres://postgres:" + postgresPassword + "@localhost:5432/indexer",
|
|
300
|
+
POSTGRES_PASSWORD: postgresPassword,
|
|
301
|
+
HASURA_ADMIN_SECRET: hasuraAdminSecret,
|
|
268
302
|
LOG_LEVEL: config.logLevel,
|
|
269
303
|
QUEUE_SIZE: config.queueSize.toString(),
|
|
270
304
|
USE_POLLING: polling ? "true" : "false",
|
|
@@ -273,6 +307,8 @@ async function processTemplates(config, targetDir) {
|
|
|
273
307
|
MINIMAL: config.minimal ? "true" : "false",
|
|
274
308
|
START_HEIGHT: config.startHeight.toString(),
|
|
275
309
|
CHAIN_PREFIX: config.chainPrefix,
|
|
310
|
+
ENABLE_HEALTHCHECK: config.enableHealthcheck ? "true" : "false",
|
|
311
|
+
HEALTH_CHECK_PORT: config.healthCheckPort.toString(),
|
|
276
312
|
ENABLE_PROMETHEUS: config.enablePrometheus ? "true" : "false",
|
|
277
313
|
PROMETHEUS_PORT: config.prometheusPort.toString(),
|
|
278
314
|
MODULES_IMPORT: generateModulesImport(config),
|
|
@@ -292,9 +328,11 @@ async function processTemplates(config, targetDir) {
|
|
|
292
328
|
const filePath = (0, node_path.resolve)(targetDir, file);
|
|
293
329
|
try {
|
|
294
330
|
let content = readFileSync(filePath, "utf-8");
|
|
331
|
+
if (file === "docker-compose.yml" && !config.processGenesis) content = content.replace(/\n {4}volumes:\n {6}- \{\{GENESIS_PATH\}\}:[^\n]*\n/, "\n");
|
|
295
332
|
Object.entries(templateVars).forEach(([key, value]) => {
|
|
296
333
|
const regex = new RegExp(`{{${key}}}`, "g");
|
|
297
|
-
|
|
334
|
+
const replacement = file === "package.json" ? JSON.stringify(value).slice(1, -1) : value;
|
|
335
|
+
content = content.replace(regex, () => replacement);
|
|
298
336
|
});
|
|
299
337
|
writeFileSync(filePath, content);
|
|
300
338
|
} catch (_error) {}
|
|
@@ -304,7 +342,7 @@ function generateModulesImport(config) {
|
|
|
304
342
|
const imports = [];
|
|
305
343
|
imports.push(" Blocks");
|
|
306
344
|
if (config.modules.includes("Auth")) imports.push(" AuthModule");
|
|
307
|
-
if (config.modules.includes("Bank")
|
|
345
|
+
if (config.modules.includes("Bank")) imports.push(" BankModule");
|
|
308
346
|
if (config.modules.includes("Staking") && !config.minimal) imports.push(" StakingModule");
|
|
309
347
|
return `import {\n${imports.join(",")}\n} from "@eclesia/core-modules-pg";\n`;
|
|
310
348
|
}
|
|
@@ -313,7 +351,7 @@ function generateModulesInstantiation(config) {
|
|
|
313
351
|
if (config.minimal) instantiations.push("const blocksModule = new Blocks.MinimalBlocksModule(registry);");
|
|
314
352
|
else instantiations.push("const blocksModule = new Blocks.FullBlocksModule(registry);");
|
|
315
353
|
if (config.modules.includes("Auth")) instantiations.push("const authModule = new AuthModule(registry);");
|
|
316
|
-
if (config.modules.includes("Bank")
|
|
354
|
+
if (config.modules.includes("Bank")) instantiations.push("const bankModule = new BankModule(registry);");
|
|
317
355
|
if (config.modules.includes("Staking") && !config.minimal) instantiations.push("const stakingModule = new StakingModule(registry);");
|
|
318
356
|
return instantiations.join("\n");
|
|
319
357
|
}
|
|
@@ -321,9 +359,9 @@ function generateModulesArray(config) {
|
|
|
321
359
|
const moduleNames = [];
|
|
322
360
|
moduleNames.push("blocksModule");
|
|
323
361
|
if (config.modules.includes("Auth")) moduleNames.push("authModule");
|
|
324
|
-
if (config.modules.includes("Bank")
|
|
362
|
+
if (config.modules.includes("Bank")) moduleNames.push("bankModule");
|
|
325
363
|
if (config.modules.includes("Staking") && !config.minimal) moduleNames.push("stakingModule");
|
|
326
|
-
return `[${moduleNames.
|
|
364
|
+
return `[${moduleNames.join(", ")}]`;
|
|
327
365
|
}
|
|
328
366
|
async function installDependencies(config, targetDir) {
|
|
329
367
|
const { spawn } = await import("node:child_process");
|
|
@@ -344,7 +382,7 @@ async function buildProject(config, targetDir) {
|
|
|
344
382
|
cwd: targetDir,
|
|
345
383
|
stdio: "inherit"
|
|
346
384
|
}).on("close", (code) => {
|
|
347
|
-
if (code !== 0) reject(/* @__PURE__ */ new Error(`
|
|
385
|
+
if (code !== 0) reject(/* @__PURE__ */ new Error(`Build failed with code ${code}`));
|
|
348
386
|
else resolve$2();
|
|
349
387
|
});
|
|
350
388
|
});
|
|
@@ -352,7 +390,6 @@ async function buildProject(config, targetDir) {
|
|
|
352
390
|
|
|
353
391
|
//#endregion
|
|
354
392
|
//#region src/index.ts
|
|
355
|
-
(0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
356
393
|
async function main() {
|
|
357
394
|
console.log();
|
|
358
395
|
console.log(picocolors.default.cyan("🚀 Welcome to create-eclesia-indexer!"));
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import path, { dirname, resolve } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
4
|
import colors from "picocolors";
|
|
6
5
|
import enquirer from "enquirer";
|
|
7
6
|
import fse from "fs-extra";
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
8
9
|
|
|
9
10
|
//#region src/create-indexer.ts
|
|
10
11
|
const { prompt } = enquirer;
|
|
11
12
|
const { ensureDirSync, copySync, writeFileSync, readFileSync } = fse;
|
|
12
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
/** npm package name rules, restricted to what also works as a directory and a bin name */
|
|
15
|
+
const PROJECT_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,212}[a-z0-9])?$/;
|
|
16
|
+
const CHAIN_PREFIX_PATTERN = /^[a-z][a-z0-9]*$/;
|
|
17
|
+
const CHAIN_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9 ._-]*$/;
|
|
18
|
+
function validateProjectName(value) {
|
|
19
|
+
if (!PROJECT_NAME_PATTERN.test(value)) return "Use lowercase letters, digits, dots, hyphens or underscores (an npm package name without a scope); no slashes or spaces.";
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
/** Random secret safe for .env files and connection strings (URL-safe base64, no padding) */
|
|
23
|
+
function generateSecret(bytes) {
|
|
24
|
+
return randomBytes(bytes).toString("base64url");
|
|
25
|
+
}
|
|
13
26
|
const availableModules = [
|
|
14
27
|
{
|
|
15
28
|
name: "Auth",
|
|
@@ -37,6 +50,10 @@ const minimalAvailableModules = [{
|
|
|
37
50
|
hint: "Token transfers and balances"
|
|
38
51
|
}];
|
|
39
52
|
async function createIndexer(initialProjectName) {
|
|
53
|
+
if (initialProjectName !== void 0) {
|
|
54
|
+
const valid = validateProjectName(initialProjectName);
|
|
55
|
+
if (valid !== true) throw new Error("Invalid project name '" + initialProjectName + "': " + valid);
|
|
56
|
+
}
|
|
40
57
|
const config = await gatherProjectInfo(initialProjectName);
|
|
41
58
|
const targetDir = resolve(process.cwd(), config.projectName);
|
|
42
59
|
console.log(colors.blue("📁 Creating project directory..."));
|
|
@@ -56,7 +73,8 @@ async function createIndexer(initialProjectName) {
|
|
|
56
73
|
console.log(colors.cyan(` cd ${config.projectName}`));
|
|
57
74
|
console.log(colors.cyan(` ${config.packageManager} local-dev:start # To run a self-contained local development environment with Postgres`));
|
|
58
75
|
console.log();
|
|
59
|
-
console.log("
|
|
76
|
+
console.log("Credentials for the local environment (Postgres password, Hasura admin secret) were generated into .env. To use an external Postgres instead, change PG_CONNECTION_STRING there.");
|
|
77
|
+
if (config.modules.includes("Bank") && !config.processGenesis) console.log(colors.yellow("Bank module without genesis processing: balances are tracked as changes since the start height, not absolute amounts."));
|
|
60
78
|
console.log();
|
|
61
79
|
console.log(colors.cyan(` ${config.packageManager} start # To run the indexer`));
|
|
62
80
|
console.log();
|
|
@@ -70,25 +88,29 @@ async function gatherProjectInfo(initialProjectName) {
|
|
|
70
88
|
name: "projectName",
|
|
71
89
|
message: "Project name:",
|
|
72
90
|
initial: initialProjectName || "my-indexer",
|
|
73
|
-
skip: !!initialProjectName
|
|
91
|
+
skip: !!initialProjectName,
|
|
92
|
+
validate: validateProjectName
|
|
74
93
|
},
|
|
75
94
|
{
|
|
76
95
|
type: "input",
|
|
77
96
|
name: "chainName",
|
|
78
97
|
message: "Chain name:",
|
|
79
|
-
initial: "cosmos-hub"
|
|
98
|
+
initial: "cosmos-hub",
|
|
99
|
+
validate: (value) => CHAIN_NAME_PATTERN.test(value) || "Use letters, digits, spaces, dots, hyphens or underscores."
|
|
80
100
|
},
|
|
81
101
|
{
|
|
82
102
|
type: "input",
|
|
83
103
|
name: "chainPrefix",
|
|
84
104
|
message: "Chain address prefix:",
|
|
85
|
-
initial: "cosmos"
|
|
105
|
+
initial: "cosmos",
|
|
106
|
+
validate: (value) => CHAIN_PREFIX_PATTERN.test(value) || "A bech32 prefix is lowercase letters and digits, starting with a letter."
|
|
86
107
|
},
|
|
87
108
|
{
|
|
88
109
|
type: "input",
|
|
89
110
|
name: "description",
|
|
90
111
|
message: "Description:",
|
|
91
|
-
initial: "A custom Cosmos SDK chain indexer"
|
|
112
|
+
initial: "A custom Cosmos SDK chain indexer",
|
|
113
|
+
validate: (value) => !/[\r\n]/.test(value) || "Keep the description on one line."
|
|
92
114
|
},
|
|
93
115
|
{
|
|
94
116
|
type: "input",
|
|
@@ -208,9 +230,11 @@ async function copyTemplateFiles(config, targetDir) {
|
|
|
208
230
|
if (src.includes(".template")) return false;
|
|
209
231
|
if (config.packageManager !== "pnpm" && src.includes("pnpm-workspace")) return false;
|
|
210
232
|
if (src.includes("Dockerfile")) return false;
|
|
233
|
+
if (src.endsWith("_gitignore")) return false;
|
|
211
234
|
return true;
|
|
212
235
|
} });
|
|
213
236
|
copySync(resolve(templatesDir, "Dockerfile." + config.packageManager), targetDir + "/Dockerfile");
|
|
237
|
+
copySync(resolve(templatesDir, "_gitignore"), targetDir + "/.gitignore");
|
|
214
238
|
[
|
|
215
239
|
"package.json.template",
|
|
216
240
|
"src/index.ts.template",
|
|
@@ -228,16 +252,22 @@ async function copyTemplateFiles(config, targetDir) {
|
|
|
228
252
|
}
|
|
229
253
|
async function processTemplates(config, targetDir) {
|
|
230
254
|
let polling = false;
|
|
255
|
+
config.enableHealthcheck = false;
|
|
256
|
+
config.healthCheckPort = 8888;
|
|
231
257
|
config.enablePrometheus = false;
|
|
232
258
|
config.prometheusPort = 9090;
|
|
233
259
|
const url = new URL(config.rpcEndpoint);
|
|
234
260
|
if (url.protocol === "http:" || url.protocol === "https:") polling = true;
|
|
261
|
+
const postgresPassword = generateSecret(18);
|
|
262
|
+
const hasuraAdminSecret = generateSecret(24);
|
|
235
263
|
const templateVars = {
|
|
236
264
|
PROJECT_NAME: config.projectName,
|
|
237
265
|
CHAIN_NAME: config.chainName,
|
|
238
266
|
DESCRIPTION: config.description,
|
|
239
267
|
RPC_ENDPOINT: config.rpcEndpoint,
|
|
240
|
-
PG_CONNECTION_STRING: "postgres://postgres:
|
|
268
|
+
PG_CONNECTION_STRING: "postgres://postgres:" + postgresPassword + "@localhost:5432/indexer",
|
|
269
|
+
POSTGRES_PASSWORD: postgresPassword,
|
|
270
|
+
HASURA_ADMIN_SECRET: hasuraAdminSecret,
|
|
241
271
|
LOG_LEVEL: config.logLevel,
|
|
242
272
|
QUEUE_SIZE: config.queueSize.toString(),
|
|
243
273
|
USE_POLLING: polling ? "true" : "false",
|
|
@@ -246,6 +276,8 @@ async function processTemplates(config, targetDir) {
|
|
|
246
276
|
MINIMAL: config.minimal ? "true" : "false",
|
|
247
277
|
START_HEIGHT: config.startHeight.toString(),
|
|
248
278
|
CHAIN_PREFIX: config.chainPrefix,
|
|
279
|
+
ENABLE_HEALTHCHECK: config.enableHealthcheck ? "true" : "false",
|
|
280
|
+
HEALTH_CHECK_PORT: config.healthCheckPort.toString(),
|
|
249
281
|
ENABLE_PROMETHEUS: config.enablePrometheus ? "true" : "false",
|
|
250
282
|
PROMETHEUS_PORT: config.prometheusPort.toString(),
|
|
251
283
|
MODULES_IMPORT: generateModulesImport(config),
|
|
@@ -265,9 +297,11 @@ async function processTemplates(config, targetDir) {
|
|
|
265
297
|
const filePath = resolve(targetDir, file);
|
|
266
298
|
try {
|
|
267
299
|
let content = readFileSync(filePath, "utf-8");
|
|
300
|
+
if (file === "docker-compose.yml" && !config.processGenesis) content = content.replace(/\n {4}volumes:\n {6}- \{\{GENESIS_PATH\}\}:[^\n]*\n/, "\n");
|
|
268
301
|
Object.entries(templateVars).forEach(([key, value]) => {
|
|
269
302
|
const regex = new RegExp(`{{${key}}}`, "g");
|
|
270
|
-
|
|
303
|
+
const replacement = file === "package.json" ? JSON.stringify(value).slice(1, -1) : value;
|
|
304
|
+
content = content.replace(regex, () => replacement);
|
|
271
305
|
});
|
|
272
306
|
writeFileSync(filePath, content);
|
|
273
307
|
} catch (_error) {}
|
|
@@ -277,7 +311,7 @@ function generateModulesImport(config) {
|
|
|
277
311
|
const imports = [];
|
|
278
312
|
imports.push(" Blocks");
|
|
279
313
|
if (config.modules.includes("Auth")) imports.push(" AuthModule");
|
|
280
|
-
if (config.modules.includes("Bank")
|
|
314
|
+
if (config.modules.includes("Bank")) imports.push(" BankModule");
|
|
281
315
|
if (config.modules.includes("Staking") && !config.minimal) imports.push(" StakingModule");
|
|
282
316
|
return `import {\n${imports.join(",")}\n} from "@eclesia/core-modules-pg";\n`;
|
|
283
317
|
}
|
|
@@ -286,7 +320,7 @@ function generateModulesInstantiation(config) {
|
|
|
286
320
|
if (config.minimal) instantiations.push("const blocksModule = new Blocks.MinimalBlocksModule(registry);");
|
|
287
321
|
else instantiations.push("const blocksModule = new Blocks.FullBlocksModule(registry);");
|
|
288
322
|
if (config.modules.includes("Auth")) instantiations.push("const authModule = new AuthModule(registry);");
|
|
289
|
-
if (config.modules.includes("Bank")
|
|
323
|
+
if (config.modules.includes("Bank")) instantiations.push("const bankModule = new BankModule(registry);");
|
|
290
324
|
if (config.modules.includes("Staking") && !config.minimal) instantiations.push("const stakingModule = new StakingModule(registry);");
|
|
291
325
|
return instantiations.join("\n");
|
|
292
326
|
}
|
|
@@ -294,9 +328,9 @@ function generateModulesArray(config) {
|
|
|
294
328
|
const moduleNames = [];
|
|
295
329
|
moduleNames.push("blocksModule");
|
|
296
330
|
if (config.modules.includes("Auth")) moduleNames.push("authModule");
|
|
297
|
-
if (config.modules.includes("Bank")
|
|
331
|
+
if (config.modules.includes("Bank")) moduleNames.push("bankModule");
|
|
298
332
|
if (config.modules.includes("Staking") && !config.minimal) moduleNames.push("stakingModule");
|
|
299
|
-
return `[${moduleNames.
|
|
333
|
+
return `[${moduleNames.join(", ")}]`;
|
|
300
334
|
}
|
|
301
335
|
async function installDependencies(config, targetDir) {
|
|
302
336
|
const { spawn } = await import("node:child_process");
|
|
@@ -317,7 +351,7 @@ async function buildProject(config, targetDir) {
|
|
|
317
351
|
cwd: targetDir,
|
|
318
352
|
stdio: "inherit"
|
|
319
353
|
}).on("close", (code) => {
|
|
320
|
-
if (code !== 0) reject(/* @__PURE__ */ new Error(`
|
|
354
|
+
if (code !== 0) reject(/* @__PURE__ */ new Error(`Build failed with code ${code}`));
|
|
321
355
|
else resolve$1();
|
|
322
356
|
});
|
|
323
357
|
});
|
|
@@ -325,7 +359,6 @@ async function buildProject(config, targetDir) {
|
|
|
325
359
|
|
|
326
360
|
//#endregion
|
|
327
361
|
//#region src/index.ts
|
|
328
|
-
dirname(fileURLToPath(import.meta.url));
|
|
329
362
|
async function main() {
|
|
330
363
|
console.log();
|
|
331
364
|
console.log(colors.cyan("🚀 Welcome to create-eclesia-indexer!"));
|
|
@@ -353,4 +386,4 @@ main().catch((error) => {
|
|
|
353
386
|
|
|
354
387
|
//#endregion
|
|
355
388
|
export { };
|
|
356
|
-
//# sourceMappingURL=index.
|
|
389
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["imports: string[]","instantiations: string[]","moduleNames: string[]"],"sources":["../src/create-indexer.ts","../src/index.ts"],"sourcesContent":["import enquirer from \"enquirer\";\nconst {\n prompt,\n} = enquirer;\nimport fse from \"fs-extra\";\nconst {\n ensureDirSync, copySync, writeFileSync, readFileSync,\n} = fse;\nimport {\n randomBytes,\n} from \"node:crypto\";\nimport path, {\n resolve,\n} from \"node:path\";\nimport {\n dirname,\n} from \"node:path\";\nimport {\n fileURLToPath,\n} from \"node:url\";\n\nimport colors from \"picocolors\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\ninterface ProjectConfig {\n projectName: string\n chainName: string\n description: string\n rpcEndpoint: string\n chainPrefix: string\n minimal: boolean\n startHeight: number\n queueSize: number\n logLevel: string\n processGenesis: boolean\n genesisPath: string | null\n enableHealthcheck: boolean\n healthCheckPort: number\n enablePrometheus: boolean\n prometheusPort: number\n modules: string[]\n packageManager: \"npm\" | \"yarn\" | \"pnpm\"\n}\n\n/** npm package name rules, restricted to what also works as a directory and a bin name */\nconst PROJECT_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,212}[a-z0-9])?$/;\nconst CHAIN_PREFIX_PATTERN = /^[a-z][a-z0-9]*$/;\nconst CHAIN_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9 ._-]*$/;\n\nexport function validateProjectName(value: string): true | string {\n if (!PROJECT_NAME_PATTERN.test(value)) {\n return \"Use lowercase letters, digits, dots, hyphens or underscores (an npm package name without a scope); no slashes or spaces.\";\n }\n return true;\n}\n\n/** Random secret safe for .env files and connection strings (URL-safe base64, no padding) */\nfunction generateSecret(bytes: number): string {\n return randomBytes(bytes).toString(\"base64url\");\n}\n\nconst availableModules = [\n {\n name: \"Auth\",\n value: \"auth\",\n hint: \"Account and authentication data\",\n },\n {\n name: \"Bank\",\n value: \"bank\",\n hint: \"Token transfers and balances\",\n },\n {\n name: \"Staking\",\n value: \"staking\",\n hint: \"Validator and delegation data\",\n },\n];\n\nconst minimalAvailableModules = [\n {\n name: \"Auth\",\n value: \"auth\",\n hint: \"Account and authentication data\",\n },\n {\n name: \"Bank\",\n value: \"bank\",\n hint: \"Token transfers and balances\",\n },\n];\n\nexport async function createIndexer(initialProjectName?: string): Promise<void> {\n if (initialProjectName !== undefined) {\n const valid = validateProjectName(initialProjectName);\n if (valid !== true) {\n throw new Error(\"Invalid project name '\" + initialProjectName + \"': \" + valid);\n }\n }\n const config = await gatherProjectInfo(initialProjectName);\n const targetDir = resolve(process.cwd(), config.projectName);\n\n console.log(colors.blue(\"📁 Creating project directory...\"));\n ensureDirSync(targetDir);\n\n console.log(colors.blue(\"📋 Copying files...\"));\n await copyTemplateFiles(config, targetDir);\n\n console.log(colors.blue(\"🔧 Processing template variables...\"));\n await processTemplates(config, targetDir);\n\n console.log(colors.blue(\"📦 Installing dependencies...\"));\n await installDependencies(config, targetDir);\n\n console.log(colors.blue(\"📦 Building...\"));\n await buildProject(config, targetDir);\n\n console.log();\n console.log(colors.green(\"🎉 Your indexer is ready!\"));\n console.log();\n console.log(\"Next steps:\");\n console.log(colors.cyan(` cd ${config.projectName}`));\n console.log(colors.cyan(` ${config.packageManager} local-dev:start # To run a self-contained local development environment with Postgres`));\n console.log();\n console.log(\"Credentials for the local environment (Postgres password, Hasura admin secret) were generated into .env. To use an external Postgres instead, change PG_CONNECTION_STRING there.\");\n if (config.modules.includes(\"Bank\") && !config.processGenesis) {\n console.log(colors.yellow(\"Bank module without genesis processing: balances are tracked as changes since the start height, not absolute amounts.\"));\n }\n console.log();\n console.log(colors.cyan(` ${config.packageManager} start # To run the indexer`));\n console.log();\n console.log(\"Happy indexing!\");\n console.log();\n}\n\nasync function gatherProjectInfo(initialProjectName?: string): Promise<ProjectConfig> {\n const questions1 = [\n {\n type: \"input\",\n name: \"projectName\",\n message: \"Project name:\",\n initial: initialProjectName || \"my-indexer\",\n skip: !!initialProjectName,\n validate: validateProjectName,\n },\n {\n type: \"input\",\n name: \"chainName\",\n message: \"Chain name:\",\n initial: \"cosmos-hub\",\n validate: (value: string) => CHAIN_NAME_PATTERN.test(value) || \"Use letters, digits, spaces, dots, hyphens or underscores.\",\n },\n {\n type: \"input\",\n name: \"chainPrefix\",\n message: \"Chain address prefix:\",\n initial: \"cosmos\",\n validate: (value: string) => CHAIN_PREFIX_PATTERN.test(value) || \"A bech32 prefix is lowercase letters and digits, starting with a letter.\",\n },\n {\n type: \"input\",\n name: \"description\",\n message: \"Description:\",\n initial: \"A custom Cosmos SDK chain indexer\",\n validate: (value: string) => !/[\\r\\n]/.test(value) || \"Keep the description on one line.\",\n },\n {\n type: \"input\",\n name: \"rpcEndpoint\",\n message: \"RPC endpoint:\",\n initial: \"https://rpc.cosmos.network\",\n validate: (value: string) => {\n try {\n const url = new URL(value);\n if (url.protocol === \"http:\" || url.protocol === \"https:\" || url.protocol === \"ws:\" || url.protocol === \"wss:\") {\n return true;\n }\n return \"Please enter a valid HTTP/HTTPS/WS/WSS URL.\";\n }\n catch (err) {\n return \"Please enter a valid URL: \" + err;\n }\n },\n },\n {\n type: \"toggle\",\n name: \"minimal\",\n message: \"Minimal block indexing? (Only stores heights)\",\n enabled: \"Yes\",\n disabled: \"No\",\n initial: false,\n },\n {\n type: \"number\",\n name: \"queueSize\",\n message: \"Number of blocks to keep prefetched (queue size):\",\n initial: 200,\n },\n {\n type: \"number\",\n name: \"startHeight\",\n message: \"Height to start indexing from (>1 not compatible with standard modules):\",\n initial: 1,\n },\n ];\n const answers1 = await prompt(questions1) as ProjectConfig;\n if (answers1.startHeight == 1) {\n const genesisQuestion = await prompt([\n {\n type: \"toggle\",\n name: \"processGenesis\",\n message: \"Process genesis file?\",\n enabled: \"Yes\",\n disabled: \"No\",\n },\n ]) as ProjectConfig;\n answers1.processGenesis = genesisQuestion.processGenesis;\n }\n else {\n answers1.processGenesis = false;\n }\n if (answers1.processGenesis) {\n const genesisPathQuestion = await prompt([\n {\n type: \"input\",\n name: \"genesisPath\",\n message: \"Path to genesis file:\",\n initial: \"./genesis.json\",\n validate: (value: string) => {\n try {\n fse.accessSync(value, fse.constants.R_OK);\n return true;\n }\n catch (err) {\n return \"File not found or not readable. Please enter a valid path: \" + err;\n }\n },\n },\n ]) as ProjectConfig;\n answers1.genesisPath = genesisPathQuestion.genesisPath ? path.resolve(genesisPathQuestion.genesisPath) : null;\n }\n else {\n answers1.genesisPath = null;\n }\n const questions2 = [\n {\n type: \"multiselect\",\n name: \"modules\",\n message: \"Select modules to include:\",\n choices: answers1.startHeight == 1 && !answers1.minimal && answers1.processGenesis ? availableModules : minimalAvailableModules,\n initial: answers1.startHeight == 1 && !answers1.minimal && answers1.processGenesis ? [0, 1, 2] : [0, 1],\n },\n {\n type: \"select\",\n name: \"packageManager\",\n message: \"Package manager:\",\n choices: [\n {\n name: \"pnpm\",\n hint: \"recommended\",\n },\n {\n name: \"npm\",\n },\n {\n name: \"yarn\",\n },\n ],\n initial: 0,\n },\n {\n type: \"select\",\n name: \"logLevel\",\n message: \"Log level:\",\n choices: [\"error\", \"warn\", \"info\", \"verbose\", \"debug\", \"silly\"],\n initial: 4,\n },\n ];\n const answers2 = await prompt(questions2) as ProjectConfig;\n return {\n ...answers1,\n ...answers2,\n };\n}\n\nasync function copyTemplateFiles(config: ProjectConfig, targetDir: string): Promise<void> {\n const templatesDir = resolve(__dirname, \"..\", \"templates\", \"basic\");\n if (config.genesisPath) {\n console.log(colors.blue(\"📋 Copying genesis file...\"));\n copySync(config.genesisPath, targetDir + \"/genesis.json\");\n }\n\n console.log(colors.blue(\"📋 Copying template files...\"));\n copySync(templatesDir, targetDir, {\n filter: (src) => {\n if (src.includes(\".template\")) {\n return false;\n }\n if (config.packageManager !== \"pnpm\" && src.includes(\"pnpm-workspace\")) {\n return false;\n }\n if (src.includes(\"Dockerfile\")) {\n return false;\n }\n if (src.endsWith(\"_gitignore\")) {\n return false;\n }\n return true;\n },\n });\n copySync(resolve(templatesDir, \"Dockerfile.\" + config.packageManager), targetDir + \"/Dockerfile\");\n // Stored without the leading dot so npm does not rewrite it when the CLI is published\n copySync(resolve(templatesDir, \"_gitignore\"), targetDir + \"/.gitignore\");\n // Copy template files\n const templateFiles = [\"package.json.template\", \"src/index.ts.template\", \"tsconfig.json.template\", \"docker-compose.yml.template\", \"README.md.template\", \".env.template\"];\n\n templateFiles.forEach((templateFile) => {\n const srcPath = resolve(templatesDir, templateFile);\n const destPath = resolve(targetDir, templateFile.replace(\".template\", \"\"));\n\n try {\n copySync(srcPath, destPath);\n }\n catch (_error) {\n // Template file might not exist, that's okay\n }\n });\n}\n\nasync function processTemplates(config: ProjectConfig, targetDir: string): Promise<void> {\n let polling = false;\n config.enableHealthcheck = false;\n config.healthCheckPort = 8888;\n config.enablePrometheus = false;\n config.prometheusPort = 9090;\n const url = new URL(config.rpcEndpoint);\n if (url.protocol === \"http:\" || url.protocol === \"https:\") {\n polling = true;\n }\n // Generated once per project and written only to .env, which is git-ignored\n const postgresPassword = generateSecret(18);\n const hasuraAdminSecret = generateSecret(24);\n const templateVars = {\n PROJECT_NAME: config.projectName,\n CHAIN_NAME: config.chainName,\n DESCRIPTION: config.description,\n RPC_ENDPOINT: config.rpcEndpoint,\n PG_CONNECTION_STRING: \"postgres://postgres:\" + postgresPassword + \"@localhost:5432/indexer\",\n POSTGRES_PASSWORD: postgresPassword,\n HASURA_ADMIN_SECRET: hasuraAdminSecret,\n LOG_LEVEL: config.logLevel,\n QUEUE_SIZE: config.queueSize.toString(),\n USE_POLLING: polling ? \"true\" : \"false\",\n PROCESS_GENESIS: config.processGenesis + \"\",\n GENESIS_PATH: path.resolve(targetDir, \"genesis.json\"),\n MINIMAL: config.minimal ? \"true\" : \"false\",\n START_HEIGHT: config.startHeight.toString(),\n CHAIN_PREFIX: config.chainPrefix,\n ENABLE_HEALTHCHECK: config.enableHealthcheck ? \"true\" : \"false\",\n HEALTH_CHECK_PORT: config.healthCheckPort.toString(),\n ENABLE_PROMETHEUS: config.enablePrometheus ? \"true\" : \"false\",\n PROMETHEUS_PORT: config.prometheusPort.toString(),\n MODULES_IMPORT: generateModulesImport(config),\n PACKAGE_MANAGER: config.packageManager,\n MODULES_INSTANTIATION: generateModulesInstantiation(config),\n MODULES_ARRAY: generateModulesArray(config),\n };\n\n const filesToProcess = [\"package.json\", \"src/index.ts\", \"tsconfig.json\", \"docker-compose.yml\", \"README.md\", \"pnpm-workspace.yaml\", \".env\"];\n\n filesToProcess.forEach((file) => {\n const filePath = resolve(targetDir, file);\n\n try {\n let content = readFileSync(filePath, \"utf-8\");\n\n // Without genesis processing there is no genesis.json to mount; binding a missing host\n // path would make Docker create an empty directory under that name\n if (file === \"docker-compose.yml\" && !config.processGenesis) {\n content = content.replace(/\\n {4}volumes:\\n {6}- \\{\\{GENESIS_PATH\\}\\}:[^\\n]*\\n/, \"\\n\");\n }\n\n Object.entries(templateVars).forEach(([key, value]) => {\n const regex = new RegExp(`{{${key}}}`, \"g\");\n // Placeholders in package.json sit inside JSON strings; escape for that context. A\n // replacer function keeps \"$&\"-style patterns in user input literal.\n const replacement = file === \"package.json\" ? JSON.stringify(value).slice(1, -1) : value;\n content = content.replace(regex, () => replacement);\n });\n\n writeFileSync(filePath, content);\n }\n catch (_error) {\n // File might not exist, that's okay\n }\n });\n}\n\nfunction generateModulesImport(config: ProjectConfig): string {\n const imports: string[] = [];\n\n imports.push(\" Blocks\");\n if (config.modules.includes(\"Auth\")) {\n imports.push(\" AuthModule\");\n }\n if (config.modules.includes(\"Bank\")) {\n imports.push(\" BankModule\");\n }\n if (config.modules.includes(\"Staking\") && !config.minimal) {\n imports.push(\" StakingModule\");\n }\n\n return `import {\\n${imports.join(\",\")}\\n} from \"@eclesia/core-modules-pg\";\\n`;\n}\n\nfunction generateModulesInstantiation(config: ProjectConfig): string {\n const instantiations: string[] = [];\n\n if (config.minimal) {\n instantiations.push(\"const blocksModule = new Blocks.MinimalBlocksModule(registry);\");\n }\n else {\n instantiations.push(\"const blocksModule = new Blocks.FullBlocksModule(registry);\");\n }\n if (config.modules.includes(\"Auth\")) {\n instantiations.push(\"const authModule = new AuthModule(registry);\");\n }\n if (config.modules.includes(\"Bank\")) {\n instantiations.push(\"const bankModule = new BankModule(registry);\");\n }\n if (config.modules.includes(\"Staking\") && !config.minimal) {\n instantiations.push(\"const stakingModule = new StakingModule(registry);\");\n }\n\n return instantiations.join(\"\\n\");\n}\n\nfunction generateModulesArray(config: ProjectConfig): string {\n const moduleNames: string[] = [];\n\n moduleNames.push(\"blocksModule\");\n\n if (config.modules.includes(\"Auth\")) {\n moduleNames.push(\"authModule\");\n }\n if (config.modules.includes(\"Bank\")) {\n moduleNames.push(\"bankModule\");\n }\n if (config.modules.includes(\"Staking\") && !config.minimal) {\n moduleNames.push(\"stakingModule\");\n }\n return `[${moduleNames.join(\", \")}]`;\n}\n\nasync function installDependencies(config: ProjectConfig, targetDir: string): Promise<void> {\n const {\n spawn,\n } = await import(\"node:child_process\");\n\n return new Promise((resolve, reject) => {\n const child = spawn(config.packageManager, [\"install\"], {\n cwd: targetDir,\n stdio: \"inherit\",\n });\n\n child.on(\"close\", (code) => {\n if (code !== 0) {\n reject(new Error(`Package installation failed with code ${code}`));\n }\n else {\n resolve();\n }\n });\n });\n}\n\nasync function buildProject(config: ProjectConfig, targetDir: string): Promise<void> {\n const {\n spawn,\n } = await import(\"node:child_process\");\n\n return new Promise((resolve, reject) => {\n const child = spawn(config.packageManager, [\"run\", \"build\"], {\n cwd: targetDir,\n stdio: \"inherit\",\n });\n\n child.on(\"close\", (code) => {\n if (code !== 0) {\n reject(new Error(`Build failed with code ${code}`));\n }\n else {\n resolve();\n }\n });\n });\n}\n","import {\n existsSync,\n} from \"node:fs\";\nimport {\n resolve,\n} from \"node:path\";\n\nimport colors from \"picocolors\";\n\nimport {\n createIndexer,\n} from \"./create-indexer.js\";\n\nasync function main() {\n console.log();\n console.log(colors.cyan(\"🚀 Welcome to create-eclesia-indexer!\"));\n console.log(colors.gray(\"Scaffolding a new Cosmos SDK chain indexer...\"));\n console.log();\n\n try {\n const projectName = process.argv[2];\n\n if (projectName && existsSync(resolve(process.cwd(), projectName))) {\n console.log(colors.red(`❌ Directory '${projectName}' already exists.`));\n process.exit(1);\n }\n\n await createIndexer(projectName);\n\n console.log();\n console.log(colors.green(\"✅ Indexer created successfully!\"));\n console.log();\n }\n catch (error) {\n console.error(colors.red(\"❌ Error creating indexer:\"), error);\n process.exit(1);\n }\n}\n\nmain().catch((error) => {\n console.error(error);\n process.exit(1);\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-eclesia-indexer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.16.0",
|
|
4
4
|
"description": "CLI tool to scaffold Cosmos SDK chain indexers using eclesia-indexer-core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"create-eclesia-indexer": "./bin/create-eclesia-indexer.js"
|
|
8
8
|
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"build": "tsc --noEmit && tsdown",
|
|
11
|
-
"lint": "eslint src/**/*.ts",
|
|
12
|
-
"lint:fix": "eslint src/**/*.ts --fix",
|
|
13
|
-
"typecheck": "tsc --noEmit",
|
|
14
|
-
"prepublishOnly": "npm run build"
|
|
15
|
-
},
|
|
16
9
|
"files": [
|
|
17
10
|
"dist/",
|
|
18
11
|
"templates/",
|
|
@@ -27,27 +20,58 @@
|
|
|
27
20
|
"template"
|
|
28
21
|
],
|
|
29
22
|
"author": "",
|
|
30
|
-
"license": "
|
|
23
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/allinbits/eclesia-indexer-core.git",
|
|
27
|
+
"directory": "packages/create-eclesia-indexer"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/allinbits/eclesia-indexer-core#readme",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/allinbits/eclesia-indexer-core/issues"
|
|
32
|
+
},
|
|
31
33
|
"engines": {
|
|
32
34
|
"node": ">=18"
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
35
37
|
"enquirer": "^2.4.1",
|
|
36
|
-
"fs-extra": "^11.
|
|
38
|
+
"fs-extra": "^11.4.0",
|
|
37
39
|
"picocolors": "^1.1.1"
|
|
38
40
|
},
|
|
39
41
|
"publishConfig": {
|
|
40
42
|
"access": "public"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^9.39.5",
|
|
46
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
43
47
|
"@types/fs-extra": "^11.0.4",
|
|
44
|
-
"@types/node": "^
|
|
45
|
-
"
|
|
46
|
-
"@stylistic/eslint-plugin": "^5.3.1",
|
|
47
|
-
"eslint": "^9.35.0",
|
|
48
|
+
"@types/node": "^24.13.3",
|
|
49
|
+
"eslint": "^9.39.5",
|
|
48
50
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
49
|
-
"tsdown": "^0.
|
|
50
|
-
"typescript": "^5.9.
|
|
51
|
-
"typescript-eslint": "^8.
|
|
51
|
+
"tsdown": "^0.16.8",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"typescript-eslint": "^8.70.0"
|
|
54
|
+
},
|
|
55
|
+
"exports": {
|
|
56
|
+
".": {
|
|
57
|
+
"import": {
|
|
58
|
+
"types": "./dist/index.d.mts",
|
|
59
|
+
"default": "./dist/index.mjs"
|
|
60
|
+
},
|
|
61
|
+
"require": {
|
|
62
|
+
"types": "./dist/index.d.cts",
|
|
63
|
+
"default": "./dist/index.cjs"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"./package.json": "./package.json"
|
|
67
|
+
},
|
|
68
|
+
"main": "dist/index.cjs",
|
|
69
|
+
"module": "dist/index.mjs",
|
|
70
|
+
"types": "dist/index.d.mts",
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "tsc --noEmit && tsdown",
|
|
73
|
+
"lint": "eslint src/**/*.ts",
|
|
74
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
75
|
+
"typecheck": "tsc --noEmit"
|
|
52
76
|
}
|
|
53
77
|
}
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
# Chain RPC endpoint
|
|
4
4
|
RPC_ENDPOINT={{RPC_ENDPOINT}}
|
|
5
5
|
|
|
6
|
-
# Database connection
|
|
6
|
+
# Database connection (host-side; docker-compose overrides it with the container hostname)
|
|
7
7
|
PG_CONNECTION_STRING={{PG_CONNECTION_STRING}}
|
|
8
|
+
# Generated at scaffold time. Used by docker-compose for the Postgres container and Hasura.
|
|
9
|
+
POSTGRES_PASSWORD={{POSTGRES_PASSWORD}}
|
|
10
|
+
HASURA_GRAPHQL_ADMIN_SECRET={{HASURA_ADMIN_SECRET}}
|
|
8
11
|
|
|
9
12
|
# Indexer settings
|
|
10
13
|
LOG_LEVEL={{LOG_LEVEL}}
|
|
11
14
|
QUEUE_SIZE={{QUEUE_SIZE}}
|
|
12
15
|
PROCESS_GENESIS={{PROCESS_GENESIS}}
|
|
16
|
+
# Path to the genesis file, used only when PROCESS_GENESIS=true
|
|
17
|
+
GENESIS_PATH=./genesis.json
|
|
13
18
|
MINIMAL={{MINIMAL}}
|
|
14
19
|
CHAIN_PREFIX={{CHAIN_PREFIX}}
|
|
15
20
|
USE_POLLING={{USE_POLLING}}
|
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
# syntax=docker
|
|
2
|
-
|
|
3
|
-
FROM node:20-alpine3.21 AS builder
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
4
2
|
|
|
3
|
+
# Build stage: install every dependency, compile, then drop dev dependencies
|
|
4
|
+
FROM node:22-alpine AS build
|
|
5
5
|
WORKDIR /usr/src/app
|
|
6
|
-
|
|
7
|
-
COPY package*.json ./
|
|
8
|
-
|
|
6
|
+
COPY package.json package-lock.json ./
|
|
9
7
|
RUN npm ci
|
|
8
|
+
COPY tsconfig.json tsdown.config.ts ./
|
|
9
|
+
COPY src ./src
|
|
10
|
+
RUN npm run build && npm prune --omit=dev
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
FROM node:20-alpine3.21
|
|
15
|
-
|
|
12
|
+
# Runtime stage: only the compiled output and production dependencies, running as the unprivileged node user
|
|
13
|
+
FROM node:22-alpine
|
|
14
|
+
ENV NODE_ENV=production
|
|
16
15
|
WORKDIR /usr/src/app
|
|
17
|
-
|
|
18
|
-
COPY
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
COPY --from=builder /usr/src/app .
|
|
23
|
-
|
|
24
|
-
RUN npm run build
|
|
25
|
-
|
|
26
|
-
ENTRYPOINT ["node", "dist/index.js" ]
|
|
16
|
+
COPY --from=build --chown=node:node /usr/src/app/package.json ./package.json
|
|
17
|
+
COPY --from=build --chown=node:node /usr/src/app/node_modules ./node_modules
|
|
18
|
+
COPY --from=build --chown=node:node /usr/src/app/dist ./dist
|
|
19
|
+
USER node
|
|
20
|
+
ENTRYPOINT ["node", "dist/index.js"]
|