apibara 2.0.0-beta.9 → 2.1.0-beta.3

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.
Files changed (83) hide show
  1. package/dist/chunks/add.mjs +44 -0
  2. package/dist/chunks/build.mjs +3 -3
  3. package/dist/chunks/dev.mjs +22 -18
  4. package/dist/chunks/init.mjs +37 -0
  5. package/dist/chunks/prepare.mjs +0 -2
  6. package/dist/chunks/start.mjs +56 -0
  7. package/dist/cli/index.mjs +5 -1
  8. package/dist/config/index.d.mts +1 -1
  9. package/dist/config/index.d.ts +1 -1
  10. package/dist/core/index.mjs +61 -97
  11. package/dist/create/index.d.mts +17 -0
  12. package/dist/create/index.d.ts +17 -0
  13. package/dist/create/index.mjs +981 -0
  14. package/dist/rollup/index.d.mts +2 -1
  15. package/dist/rollup/index.d.ts +2 -1
  16. package/dist/rollup/index.mjs +130 -167
  17. package/dist/runtime/dev.d.ts +3 -0
  18. package/dist/runtime/dev.mjs +55 -0
  19. package/dist/runtime/index.d.ts +2 -0
  20. package/dist/runtime/index.mjs +2 -0
  21. package/dist/runtime/internal/app.d.ts +2 -0
  22. package/dist/runtime/internal/app.mjs +56 -0
  23. package/dist/runtime/internal/logger.d.ts +14 -0
  24. package/dist/runtime/internal/logger.mjs +45 -0
  25. package/dist/runtime/start.d.ts +3 -0
  26. package/dist/runtime/start.mjs +41 -0
  27. package/dist/types/index.d.mts +22 -19
  28. package/dist/types/index.d.ts +22 -19
  29. package/package.json +35 -13
  30. package/runtime-meta.d.ts +2 -0
  31. package/runtime-meta.mjs +7 -0
  32. package/src/cli/commands/add.ts +44 -0
  33. package/src/cli/commands/build.ts +5 -3
  34. package/src/cli/commands/dev.ts +28 -18
  35. package/src/cli/commands/init.ts +36 -0
  36. package/src/cli/commands/prepare.ts +0 -2
  37. package/src/cli/commands/start.ts +61 -0
  38. package/src/cli/index.ts +3 -0
  39. package/src/config/index.ts +5 -4
  40. package/src/core/apibara.ts +4 -2
  41. package/src/core/build/build.ts +2 -0
  42. package/src/core/build/dev.ts +1 -0
  43. package/src/core/build/error.ts +0 -1
  44. package/src/core/build/prepare.ts +5 -2
  45. package/src/core/build/prod.ts +10 -6
  46. package/src/core/build/types.ts +4 -95
  47. package/src/core/config/defaults.ts +1 -4
  48. package/src/core/config/loader.ts +1 -0
  49. package/src/core/config/resolvers/runtime-config.resolver.ts +1 -1
  50. package/src/core/config/update.ts +2 -3
  51. package/src/core/path.ts +11 -0
  52. package/src/core/scan.ts +40 -0
  53. package/src/create/add.ts +238 -0
  54. package/src/create/colors.ts +15 -0
  55. package/src/create/constants.ts +98 -0
  56. package/src/create/index.ts +2 -0
  57. package/src/create/init.ts +175 -0
  58. package/src/create/templates.ts +468 -0
  59. package/src/create/types.ts +34 -0
  60. package/src/create/utils.ts +422 -0
  61. package/src/rollup/config.ts +67 -189
  62. package/src/rollup/index.ts +1 -0
  63. package/src/rollup/plugins/config.ts +12 -0
  64. package/src/rollup/plugins/esm-shim.ts +69 -0
  65. package/src/rollup/plugins/indexers.ts +17 -0
  66. package/src/runtime/dev.ts +64 -0
  67. package/src/runtime/index.ts +2 -0
  68. package/src/runtime/internal/app.ts +78 -0
  69. package/src/runtime/internal/logger.ts +70 -0
  70. package/src/runtime/start.ts +48 -0
  71. package/src/types/apibara.ts +8 -0
  72. package/src/types/config.ts +28 -27
  73. package/src/types/hooks.ts +1 -0
  74. package/src/types/virtual/config.d.ts +3 -0
  75. package/src/types/virtual/indexers.d.ts +10 -0
  76. package/dist/internal/citty/index.d.mts +0 -1
  77. package/dist/internal/citty/index.d.ts +0 -1
  78. package/dist/internal/citty/index.mjs +0 -1
  79. package/dist/internal/consola/index.d.mts +0 -2
  80. package/dist/internal/consola/index.d.ts +0 -2
  81. package/dist/internal/consola/index.mjs +0 -1
  82. package/src/internal/citty/index.ts +0 -1
  83. package/src/internal/consola/index.ts +0 -1
@@ -0,0 +1,175 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import consola from "consola";
4
+ import prompts from "prompts";
5
+ import { addIndexer } from "./add";
6
+ import { cyan, green } from "./colors";
7
+ import {
8
+ generateApibaraConfig,
9
+ generatePackageJson,
10
+ generateTsConfig,
11
+ } from "./templates";
12
+ import type { Language } from "./types";
13
+ import {
14
+ cancelOperation,
15
+ emptyDir,
16
+ formatFile,
17
+ getLanguageFromAlias,
18
+ getPackageManager,
19
+ isEmpty,
20
+ validateLanguage,
21
+ } from "./utils";
22
+
23
+ type Options = {
24
+ argTargetDir: string;
25
+ argLanguage?: string;
26
+ argNoCreateIndexer?: boolean;
27
+ };
28
+
29
+ export async function initializeProject({
30
+ argTargetDir,
31
+ argLanguage,
32
+ argNoCreateIndexer,
33
+ }: Options) {
34
+ const cwd = process.cwd();
35
+ validateLanguage(argLanguage, true);
36
+
37
+ console.log();
38
+
39
+ const result = await prompts(
40
+ [
41
+ {
42
+ type: () =>
43
+ argTargetDir &&
44
+ (!fs.existsSync(argTargetDir) || isEmpty(argTargetDir))
45
+ ? null
46
+ : "select",
47
+ name: "overwrite",
48
+ message: () =>
49
+ (argTargetDir === "."
50
+ ? "Current directory"
51
+ : `Target directory "${argTargetDir}"`) +
52
+ " is not empty. Please choose how to proceed:",
53
+ initial: 0,
54
+ choices: [
55
+ {
56
+ title: "Cancel operation",
57
+ value: "no",
58
+ },
59
+ {
60
+ title: "Remove existing files and continue",
61
+ value: "yes",
62
+ },
63
+ {
64
+ title: "Ignore files and continue",
65
+ value: "ignore",
66
+ },
67
+ ],
68
+ hint: "\nCurrent Working Directory: " + cwd,
69
+ },
70
+ {
71
+ type: (_, { overwrite }: { overwrite?: string }) => {
72
+ if (overwrite === "no") {
73
+ cancelOperation();
74
+ }
75
+ return null;
76
+ },
77
+ name: "overwriteChecker",
78
+ },
79
+ {
80
+ type: argLanguage ? null : "select",
81
+ name: "prompt_language",
82
+ message: "Select a language:",
83
+ choices: [
84
+ {
85
+ title: "Typescript",
86
+ value: "typescript",
87
+ },
88
+ {
89
+ title: "Javascript",
90
+ value: "javascript",
91
+ },
92
+ ],
93
+ },
94
+ ],
95
+ {
96
+ onCancel: () => {
97
+ cancelOperation();
98
+ },
99
+ },
100
+ );
101
+
102
+ const { overwrite, prompt_language } = result as {
103
+ overwrite: "no" | "yes" | "ignore";
104
+ prompt_language: "typescript" | "javascript";
105
+ };
106
+
107
+ const root = path.join(cwd, argTargetDir);
108
+ if (overwrite === "yes") {
109
+ emptyDir(root);
110
+ } else if (!fs.existsSync(root)) {
111
+ fs.mkdirSync(root, { recursive: true });
112
+ }
113
+
114
+ const lang: Language = argLanguage
115
+ ? getLanguageFromAlias(argLanguage)
116
+ : prompt_language;
117
+
118
+ const isTs = lang === "typescript";
119
+ const configExt = isTs ? "ts" : "js";
120
+
121
+ console.log("\n");
122
+ consola.info(`Initializing project in ${argTargetDir}\n\n`);
123
+
124
+ // Generate package.json
125
+ const packageJsonPath = path.join(root, "package.json");
126
+ const packageJson = generatePackageJson(isTs);
127
+ fs.writeFileSync(
128
+ packageJsonPath,
129
+ JSON.stringify(packageJson, null, 2) + "\n",
130
+ );
131
+ await formatFile(packageJsonPath);
132
+ consola.success("Created ", cyan("package.json"));
133
+
134
+ // Generate tsconfig.json if TypeScript
135
+ if (isTs) {
136
+ const tsConfigPath = path.join(root, "tsconfig.json");
137
+ const tsConfig = generateTsConfig();
138
+ fs.writeFileSync(tsConfigPath, JSON.stringify(tsConfig, null, 2) + "\n");
139
+ await formatFile(tsConfigPath);
140
+ consola.success("Created ", cyan("tsconfig.json"));
141
+ }
142
+
143
+ const apibaraConfigPath = path.join(root, `apibara.config.${configExt}`);
144
+ // Generate apibara.config
145
+ const apibaraConfig = generateApibaraConfig(isTs);
146
+ fs.writeFileSync(apibaraConfigPath, apibaraConfig);
147
+ await formatFile(apibaraConfigPath);
148
+ consola.success("Created ", cyan(`apibara.config.${configExt}`));
149
+
150
+ // Create "indexers" directory if not exists
151
+ const indexersDir = path.join(root, "indexers");
152
+ if (!fs.existsSync(indexersDir)) {
153
+ fs.mkdirSync(indexersDir, { recursive: true });
154
+ consola.success(`Created ${cyan("indexers")} directory`);
155
+ }
156
+
157
+ console.log("\n");
158
+
159
+ consola.ready(green("Project initialized successfully"));
160
+
161
+ console.log();
162
+
163
+ if (!argNoCreateIndexer) {
164
+ consola.info("Let's create an indexer\n");
165
+
166
+ await addIndexer({});
167
+ } else {
168
+ const pkgManager = getPackageManager();
169
+ consola.info(
170
+ "Run ",
171
+ green(`${pkgManager.name} run install`),
172
+ " to install all dependencies",
173
+ );
174
+ }
175
+ }
@@ -0,0 +1,468 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { consola } from "consola";
4
+ import { type ObjectLiteralExpression, Project, SyntaxKind } from "ts-morph";
5
+ import { cyan, green, magenta, yellow } from "./colors";
6
+ import { packageVersions } from "./constants";
7
+ import type { IndexerOptions } from "./types";
8
+ import { checkFileExists, formatFile, getDnaUrl } from "./utils";
9
+
10
+ export function generatePackageJson(isTypeScript: boolean) {
11
+ return {
12
+ name: "apibara-app",
13
+ version: "0.1.0",
14
+ private: true,
15
+ type: "module",
16
+ scripts: {
17
+ prepare: "apibara prepare",
18
+ dev: "apibara dev",
19
+ start: "apibara start",
20
+ build: "apibara build",
21
+ ...(isTypeScript && { typecheck: "tsc --noEmit" }),
22
+ },
23
+ dependencies: {
24
+ "@apibara/indexer": packageVersions["@apibara/indexer"],
25
+ "@apibara/protocol": packageVersions["@apibara/protocol"],
26
+ apibara: packageVersions.apibara,
27
+ },
28
+ devDependencies: {
29
+ ...(isTypeScript && {
30
+ "@rollup/plugin-typescript":
31
+ packageVersions["@rollup/plugin-typescript"],
32
+ "@types/node": packageVersions["@types/node"],
33
+ typescript: packageVersions.typescript,
34
+ }),
35
+ },
36
+ };
37
+ }
38
+
39
+ export function generateTsConfig() {
40
+ return {
41
+ $schema: "https://json.schemastore.org/tsconfig",
42
+ display: "Default",
43
+ compilerOptions: {
44
+ forceConsistentCasingInFileNames: true,
45
+ target: "ES2022",
46
+ lib: ["ESNext"],
47
+ module: "ESNext",
48
+ moduleResolution: "bundler",
49
+ skipLibCheck: true,
50
+ types: ["node"],
51
+ noEmit: true,
52
+ strict: true,
53
+ baseUrl: ".",
54
+ },
55
+ include: [".", "./.apibara/types"],
56
+ exclude: ["node_modules"],
57
+ };
58
+ }
59
+
60
+ export function generateApibaraConfig(isTypeScript: boolean) {
61
+ return `${isTypeScript ? 'import typescript from "@rollup/plugin-typescript";\nimport type { Plugin } from "apibara/rollup";\n' : ""}import { defineConfig } from "apibara/config";
62
+
63
+ export default defineConfig({
64
+ runtimeConfig: {},${
65
+ isTypeScript
66
+ ? `
67
+ rollupConfig: {
68
+ plugins: [typescript()${isTypeScript ? " as Plugin" : ""}],
69
+ },`
70
+ : ""
71
+ }
72
+ });\n`;
73
+ }
74
+
75
+ export function generateIndexer({
76
+ indexerId,
77
+ storage,
78
+ chain,
79
+ language,
80
+ }: IndexerOptions) {
81
+ return `import { defineIndexer } from "@apibara/indexer";
82
+ import { useLogger } from "@apibara/indexer/plugins";
83
+ ${storage === "postgres" ? `import { drizzleStorage } from "@apibara/plugin-drizzle";` : ""}
84
+ ${
85
+ chain === "ethereum"
86
+ ? `import { EvmStream } from "@apibara/evm";`
87
+ : chain === "beaconchain"
88
+ ? `import { BeaconChainStream } from "@apibara/beaconchain";`
89
+ : chain === "starknet"
90
+ ? `import { StarknetStream } from "@apibara/starknet";`
91
+ : ""
92
+ }
93
+ ${language === "typescript" ? `import type { ApibaraRuntimeConfig } from "apibara/types";` : ""}
94
+ ${
95
+ storage === "postgres"
96
+ ? `import { getDrizzlePgDatabase } from "../lib/db";`
97
+ : ""
98
+ }
99
+
100
+
101
+ export default function (runtimeConfig${language === "typescript" ? ": ApibaraRuntimeConfig" : ""}) {
102
+ const indexerId = "${indexerId}";
103
+ const { startingBlock, streamUrl${storage === "postgres" ? ", postgresConnectionString" : ""} } = runtimeConfig[indexerId];
104
+ ${
105
+ storage === "postgres"
106
+ ? "const { db } = getDrizzlePgDatabase(postgresConnectionString);"
107
+ : ""
108
+ }
109
+
110
+ return defineIndexer(${
111
+ chain === "ethereum"
112
+ ? "EvmStream"
113
+ : chain === "beaconchain"
114
+ ? "BeaconChainStream"
115
+ : chain === "starknet"
116
+ ? "StarknetStream"
117
+ : ""
118
+ })({
119
+ streamUrl,
120
+ finality: "accepted",
121
+ startingBlock: BigInt(startingBlock),
122
+ filter: {
123
+ header: "always",
124
+ },
125
+ plugins: [${storage === "postgres" ? "drizzleStorage({ db, persistState: true })" : ""}],
126
+ async transform({ endCursor, finality }) {
127
+ const logger = useLogger();
128
+
129
+ logger.info(
130
+ "Transforming block | orderKey: ",
131
+ endCursor?.orderKey,
132
+ " | finality: ",
133
+ finality
134
+ );
135
+
136
+ ${
137
+ storage === "postgres"
138
+ ? `// Example snippet to insert data into db using drizzle with postgres
139
+ // const { db } = useDrizzleStorage();
140
+ // const { logs } = block;
141
+ // for (const log of logs) {
142
+ // await db.insert(exampleTable).values({
143
+ // number: Number(endCursor?.orderKey),
144
+ // hash: log.transactionHash,
145
+ // });
146
+ // }`
147
+ : ""
148
+ }
149
+ },
150
+ });
151
+ }
152
+ `;
153
+ }
154
+
155
+ export async function createIndexerFile(options: IndexerOptions) {
156
+ const indexerFilePath = path.join(
157
+ options.cwd,
158
+ "indexers",
159
+ `${options.indexerFileId}.indexer.${options.language === "typescript" ? "ts" : "js"}`,
160
+ );
161
+
162
+ const { exists, overwrite } = await checkFileExists(indexerFilePath, {
163
+ askPrompt: true,
164
+ });
165
+
166
+ if (exists && !overwrite) return;
167
+
168
+ const indexerContent = generateIndexer(options);
169
+
170
+ fs.mkdirSync(path.dirname(indexerFilePath), { recursive: true });
171
+ fs.writeFileSync(indexerFilePath, indexerContent);
172
+
173
+ await formatFile(indexerFilePath);
174
+ }
175
+
176
+ export async function updatePackageJson({
177
+ cwd,
178
+ chain,
179
+ storage,
180
+ language,
181
+ }: IndexerOptions) {
182
+ const packageJsonPath = path.join(cwd, "package.json");
183
+
184
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
185
+
186
+ if (chain === "ethereum") {
187
+ packageJson.dependencies["@apibara/evm"] = packageVersions["@apibara/evm"];
188
+ } else if (chain === "beaconchain") {
189
+ packageJson.dependencies["@apibara/beaconchain"] =
190
+ packageVersions["@apibara/beaconchain"];
191
+ } else if (chain === "starknet") {
192
+ packageJson.dependencies["@apibara/starknet"] =
193
+ packageVersions["@apibara/starknet"];
194
+ }
195
+
196
+ if (storage === "postgres") {
197
+ packageJson.scripts["drizzle:generate"] = "drizzle-kit generate";
198
+ packageJson.scripts["drizzle:migrate"] = "drizzle-kit migrate";
199
+
200
+ packageJson.dependencies["@apibara/plugin-drizzle"] =
201
+ packageVersions["@apibara/plugin-drizzle"];
202
+
203
+ packageJson.dependencies["drizzle-orm"] = packageVersions["drizzle-orm"];
204
+
205
+ packageJson.dependencies["@electric-sql/pglite"] =
206
+ packageVersions["@electric-sql/pglite"];
207
+
208
+ packageJson.dependencies["drizzle-kit"] = packageVersions["drizzle-kit"];
209
+
210
+ packageJson.dependencies["pg"] = packageVersions["pg"];
211
+
212
+ if (language === "typescript") {
213
+ packageJson.devDependencies["@types/pg"] = packageVersions["@types/pg"];
214
+ }
215
+ }
216
+
217
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
218
+
219
+ await formatFile(packageJsonPath);
220
+ }
221
+
222
+ export async function updateApibaraConfigFile({
223
+ indexerId,
224
+ cwd,
225
+ chain,
226
+ storage,
227
+ language,
228
+ network,
229
+ dnaUrl,
230
+ }: IndexerOptions) {
231
+ const pathToConfig = path.join(
232
+ cwd,
233
+ `apibara.config.${language === "typescript" ? "ts" : "js"}`,
234
+ );
235
+
236
+ const runtimeConfigString = `{
237
+ startingBlock: 0,
238
+ streamUrl: "${dnaUrl ?? getDnaUrl(chain, network)}"${
239
+ storage === "postgres"
240
+ ? `,
241
+ postgresConnectionString: process.env["POSTGRES_CONNECTION_STRING"] ?? "memory://${indexerId}"`
242
+ : ""
243
+ }}`;
244
+
245
+ const project = new Project();
246
+ const sourceFile = project.addSourceFileAtPath(pathToConfig);
247
+
248
+ // Find the defineConfig call expression
249
+ const defineConfigCall = sourceFile.getFirstDescendantByKind(
250
+ SyntaxKind.CallExpression,
251
+ );
252
+ if (!defineConfigCall) return;
253
+
254
+ const configObjectExpression =
255
+ defineConfigCall.getArguments()[0] as ObjectLiteralExpression;
256
+
257
+ const runtimeConfigObject =
258
+ configObjectExpression.getProperty("runtimeConfig");
259
+
260
+ if (!runtimeConfigObject) {
261
+ configObjectExpression.addPropertyAssignment({
262
+ name: "runtimeConfig",
263
+ initializer: `{
264
+ "${indexerId}": ${runtimeConfigString}
265
+ }`,
266
+ });
267
+ } else {
268
+ const runtimeConfigProp = runtimeConfigObject.asKindOrThrow(
269
+ SyntaxKind.PropertyAssignment,
270
+ );
271
+ const runtimeConfigObj = runtimeConfigProp
272
+ .getInitializerOrThrow()
273
+ .asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
274
+
275
+ runtimeConfigObj.addPropertyAssignment({
276
+ name: `"${indexerId}"`,
277
+ initializer: runtimeConfigString,
278
+ });
279
+ }
280
+ // Save the changes
281
+ sourceFile.saveSync();
282
+
283
+ await formatFile(pathToConfig);
284
+ }
285
+
286
+ export async function createDrizzleStorageFiles(options: IndexerOptions) {
287
+ const { cwd, language, storage } = options;
288
+
289
+ if (storage !== "postgres") return;
290
+
291
+ const fileExtension = language === "typescript" ? "ts" : "js";
292
+
293
+ /**
294
+ *
295
+ *
296
+ * Drizzle Config File
297
+ *
298
+ *
299
+ */
300
+
301
+ const drizzleConfigFileName = `drizzle.config.${fileExtension}`;
302
+
303
+ // create drizzle.config.ts
304
+ const drizzleConfigPath = path.join(cwd, drizzleConfigFileName);
305
+
306
+ const { exists, overwrite } = await checkFileExists(drizzleConfigPath, {
307
+ askPrompt: true,
308
+ allowIgnore: true,
309
+ });
310
+
311
+ if (!exists || overwrite) {
312
+ const drizzleConfigContent = `${language === "typescript" ? 'import type { Config } from "drizzle-kit";' : ""}
313
+
314
+ export default {
315
+ schema: "./lib/schema.ts",
316
+ out: "./drizzle",
317
+ dialect: "postgresql",
318
+ dbCredentials: {
319
+ url: process.env["POSTGRES_CONNECTION_STRING"] ?? "",
320
+ },
321
+ }${language === "typescript" ? " satisfies Config" : ""};`;
322
+
323
+ fs.writeFileSync(drizzleConfigPath, drizzleConfigContent);
324
+
325
+ await formatFile(drizzleConfigPath);
326
+
327
+ consola.success(`Created ${cyan(drizzleConfigFileName)}`);
328
+ }
329
+
330
+ /**
331
+ *
332
+ *
333
+ * Schema File
334
+ *
335
+ *
336
+ */
337
+
338
+ const schemaFileName = `schema.${fileExtension}`;
339
+
340
+ const schemaPath = path.join(cwd, "lib", schemaFileName);
341
+
342
+ const { exists: schemaExists, overwrite: schemaOverwrite } =
343
+ await checkFileExists(schemaPath, {
344
+ askPrompt: true,
345
+ allowIgnore: true,
346
+ fileName: `lib/${schemaFileName}`,
347
+ });
348
+
349
+ if (!schemaExists || schemaOverwrite) {
350
+ const schemaContent = `// --- Add your pg table schemas here ----
351
+
352
+ // import { bigint, pgTable, text, uuid } from "drizzle-orm/pg-core";
353
+
354
+ // export const exampleTable = pgTable("example_table", {
355
+ // id: uuid("id").primaryKey().defaultRandom(),
356
+ // number: bigint("number", { mode: "number" }),
357
+ // hash: text("hash"),
358
+ // });
359
+
360
+ export {};
361
+ `;
362
+
363
+ // create directory if it doesn't exist
364
+ fs.mkdirSync(path.dirname(schemaPath), { recursive: true });
365
+ fs.writeFileSync(schemaPath, schemaContent);
366
+
367
+ await formatFile(schemaPath);
368
+
369
+ consola.success(`Created ${cyan("lib/schema.ts")}`);
370
+ }
371
+
372
+ /**
373
+ *
374
+ *
375
+ * DB File
376
+ *
377
+ *
378
+ */
379
+ const dbFileName = `db.${fileExtension}`;
380
+
381
+ const dbPath = path.join(cwd, "lib", dbFileName);
382
+
383
+ const { exists: dbExists, overwrite: dbOverwrite } = await checkFileExists(
384
+ dbPath,
385
+ {
386
+ askPrompt: true,
387
+ fileName: `lib/${dbFileName}`,
388
+ allowIgnore: true,
389
+ },
390
+ );
391
+
392
+ if (!dbExists || dbOverwrite) {
393
+ const dbContent = `import * as schema from "./schema";
394
+ import { drizzle as nodePgDrizzle } from "drizzle-orm/node-postgres";
395
+ import { drizzle as pgLiteDrizzle } from "drizzle-orm/pglite";
396
+ import pg from "pg";
397
+
398
+
399
+ export function getDrizzlePgDatabase(connectionString${language === "typescript" ? ": string" : ""}) {
400
+ // Create pglite instance
401
+ if (connectionString.includes("memory")) {
402
+ return {
403
+ db: pgLiteDrizzle({
404
+ schema,
405
+ connection: {
406
+ dataDir: connectionString,
407
+ },
408
+ }),
409
+ };
410
+ }
411
+
412
+ // Create node-postgres instance
413
+ const pool = new pg.Pool({
414
+ connectionString,
415
+ });
416
+
417
+ return { db: nodePgDrizzle(pool, { schema }) };
418
+ }`;
419
+
420
+ // create directory if it doesn't exist
421
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
422
+ fs.writeFileSync(dbPath, dbContent);
423
+
424
+ await formatFile(dbPath);
425
+
426
+ consola.success(`Created ${cyan(`lib/${dbFileName}`)}`);
427
+ }
428
+
429
+ console.log("\n");
430
+
431
+ // If schema file is created, show the example
432
+ if (!schemaExists || schemaOverwrite) {
433
+ consola.info(
434
+ `Make sure to export your pgTables in ${cyan(`lib/${schemaFileName}`)}`,
435
+ );
436
+
437
+ console.log();
438
+
439
+ consola.info(`${magenta("Example:")}
440
+
441
+ ${yellow(`
442
+ ┌──────────────────────────────────────────┐
443
+ │ lib/schema.ts │
444
+ └──────────────────────────────────────────┘
445
+
446
+ import { bigint, pgTable, text, uuid } from "drizzle-orm/pg-core";
447
+
448
+ export const exampleTable = pgTable("example_table", {
449
+ id: uuid("id").primaryKey().defaultRandom(),
450
+ number: bigint("number", { mode: "number" }),
451
+ hash: text("hash"),
452
+ });`)}`);
453
+
454
+ console.log("\n");
455
+ }
456
+
457
+ consola.info(
458
+ `Run ${green(`${options.packageManager} run drizzle:generate`)} & ${green(`${options.packageManager} run drizzle:migrate`)} to generate and apply migrations.`,
459
+ );
460
+ }
461
+
462
+ export async function createStorageRelatedFiles(options: IndexerOptions) {
463
+ const { storage } = options;
464
+
465
+ if (storage === "postgres") {
466
+ await createDrizzleStorageFiles(options);
467
+ }
468
+ }
@@ -0,0 +1,34 @@
1
+ export type ColorFunc = (str: string | number) => string;
2
+
3
+ export type Language = "typescript" | "javascript";
4
+
5
+ export type Chain = "starknet" | "ethereum" | "beaconchain";
6
+
7
+ export type Starknet_Network = "mainnet" | "sepolia";
8
+ export type Ethereum_Network = "mainnet" | "sepolia";
9
+ export type Beaconchain_Network = "mainnet";
10
+
11
+ export type Network =
12
+ | Starknet_Network
13
+ | Ethereum_Network
14
+ | Beaconchain_Network
15
+ | "other";
16
+
17
+ export type Storage = "postgres" | "none";
18
+
19
+ export type IndexerOptions = {
20
+ cwd: string;
21
+ indexerFileId: string;
22
+ indexerId: string;
23
+ chain: Chain;
24
+ network: Network;
25
+ storage: Storage;
26
+ dnaUrl?: string;
27
+ packageManager: string;
28
+ language: Language;
29
+ };
30
+
31
+ export type PkgInfo = {
32
+ name: string;
33
+ version?: string;
34
+ };