apibara 2.1.0-beta.4 → 2.1.0-beta.40

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 (119) hide show
  1. package/dist/chunks/add.mjs +16 -8
  2. package/dist/chunks/add.mjs.map +1 -0
  3. package/dist/chunks/build.mjs +4 -2
  4. package/dist/chunks/build.mjs.map +1 -0
  5. package/dist/chunks/dev.mjs +55 -21
  6. package/dist/chunks/dev.mjs.map +1 -0
  7. package/dist/chunks/init.mjs +11 -7
  8. package/dist/chunks/init.mjs.map +1 -0
  9. package/dist/chunks/prepare.mjs +4 -2
  10. package/dist/chunks/prepare.mjs.map +1 -0
  11. package/dist/chunks/start.mjs +16 -4
  12. package/dist/chunks/start.mjs.map +1 -0
  13. package/dist/chunks/write-project-info.mjs +51 -0
  14. package/dist/chunks/write-project-info.mjs.map +1 -0
  15. package/dist/cli/index.mjs +3 -1
  16. package/dist/cli/index.mjs.map +1 -0
  17. package/dist/common/index.d.mts +33 -0
  18. package/dist/common/index.d.ts +33 -0
  19. package/dist/common/index.mjs +91 -0
  20. package/dist/common/index.mjs.map +1 -0
  21. package/dist/config/index.mjs +1 -0
  22. package/dist/config/index.mjs.map +1 -0
  23. package/dist/core/index.mjs +134 -69
  24. package/dist/core/index.mjs.map +1 -0
  25. package/dist/create/index.d.mts +2 -1
  26. package/dist/create/index.d.ts +2 -1
  27. package/dist/create/index.mjs +168 -139
  28. package/dist/create/index.mjs.map +1 -0
  29. package/dist/hooks/index.mjs +6 -1
  30. package/dist/hooks/index.mjs.map +1 -0
  31. package/dist/indexer/index.d.ts +1 -0
  32. package/dist/indexer/index.mjs +1 -0
  33. package/dist/indexer/plugins.d.ts +1 -0
  34. package/dist/indexer/plugins.mjs +1 -0
  35. package/dist/indexer/testing.d.ts +1 -0
  36. package/dist/indexer/testing.mjs +1 -0
  37. package/dist/indexer/vcr.d.ts +1 -0
  38. package/dist/indexer/vcr.mjs +1 -0
  39. package/dist/rolldown/index.d.mts +7 -0
  40. package/dist/rolldown/index.d.ts +7 -0
  41. package/dist/rolldown/index.mjs +140 -0
  42. package/dist/rolldown/index.mjs.map +1 -0
  43. package/dist/runtime/dev.mjs +19 -9
  44. package/dist/runtime/internal/app.d.ts +14 -1
  45. package/dist/runtime/internal/app.mjs +26 -21
  46. package/dist/runtime/project-info.d.ts +3 -0
  47. package/dist/runtime/project-info.mjs +67 -0
  48. package/dist/runtime/start.mjs +59 -7
  49. package/dist/shared/apibara.730bb1e4.mjs +18 -0
  50. package/dist/shared/apibara.730bb1e4.mjs.map +1 -0
  51. package/dist/types/index.d.mts +24 -20
  52. package/dist/types/index.d.ts +24 -20
  53. package/dist/types/index.mjs +1 -0
  54. package/dist/types/index.mjs.map +1 -0
  55. package/package.json +33 -16
  56. package/src/cli/commands/add.ts +16 -7
  57. package/src/cli/commands/build.ts +5 -2
  58. package/src/cli/commands/dev.ts +64 -20
  59. package/src/cli/commands/init.ts +12 -7
  60. package/src/cli/commands/prepare.ts +4 -3
  61. package/src/cli/commands/start.ts +18 -3
  62. package/src/cli/commands/write-project-info.ts +56 -0
  63. package/src/cli/index.ts +2 -0
  64. package/src/common/cli.ts +40 -0
  65. package/src/common/constants.ts +6 -0
  66. package/src/common/helper.ts +86 -0
  67. package/src/common/index.ts +3 -0
  68. package/src/core/apibara.ts +7 -2
  69. package/src/core/build/build.ts +13 -5
  70. package/src/core/build/dev.ts +46 -23
  71. package/src/core/build/error.ts +9 -14
  72. package/src/core/build/prepare.ts +5 -3
  73. package/src/core/build/prod.ts +25 -16
  74. package/src/core/build/types.ts +11 -1
  75. package/src/core/config/defaults.ts +3 -0
  76. package/src/core/config/loader.ts +15 -7
  77. package/src/core/config/resolvers/runtime.resolver.ts +44 -0
  78. package/src/core/config/update.ts +6 -2
  79. package/src/core/scan.ts +1 -1
  80. package/src/create/add.ts +14 -20
  81. package/src/create/constants.ts +5 -7
  82. package/src/create/init.ts +8 -5
  83. package/src/create/templates.ts +144 -116
  84. package/src/create/types.ts +3 -0
  85. package/src/create/utils.ts +20 -7
  86. package/src/hooks/useRuntimeConfig.ts +4 -1
  87. package/src/indexer/index.ts +1 -0
  88. package/src/indexer/plugins.ts +1 -0
  89. package/src/indexer/testing.ts +1 -0
  90. package/src/indexer/vcr.ts +1 -0
  91. package/src/rolldown/config.ts +85 -0
  92. package/src/rolldown/index.ts +2 -0
  93. package/src/{rollup → rolldown}/plugins/indexers.ts +3 -3
  94. package/src/rolldown/plugins/instrumentation.ts +68 -0
  95. package/src/rolldown/plugins/static-config.ts +21 -0
  96. package/src/runtime/dev.ts +21 -8
  97. package/src/runtime/internal/app.ts +42 -29
  98. package/src/runtime/project-info.ts +90 -0
  99. package/src/runtime/start.ts +68 -6
  100. package/src/types/config.ts +27 -13
  101. package/src/types/hooks.ts +8 -5
  102. package/src/types/index.ts +1 -1
  103. package/src/types/rolldown.ts +5 -0
  104. package/src/types/virtual/indexers.d.ts +4 -1
  105. package/src/types/virtual/instrumentation.d.ts +4 -0
  106. package/src/types/virtual/static-config.d.ts +4 -0
  107. package/dist/rollup/index.d.mts +0 -6
  108. package/dist/rollup/index.d.ts +0 -6
  109. package/dist/rollup/index.mjs +0 -150
  110. package/dist/shared/apibara.1b515d04.mjs +0 -8
  111. package/src/cli/common.ts +0 -8
  112. package/src/core/config/resolvers/preset.resolver.ts +0 -9
  113. package/src/core/config/resolvers/runtime-config.resolver.ts +0 -6
  114. package/src/rollup/config.ts +0 -87
  115. package/src/rollup/index.ts +0 -2
  116. package/src/rollup/plugins/config.ts +0 -12
  117. package/src/rollup/plugins/esm-shim.ts +0 -69
  118. package/src/types/rollup.ts +0 -8
  119. package/src/types/virtual/config.d.ts +0 -3
@@ -0,0 +1,44 @@
1
+ import {
2
+ ENV_INTERNAL_APIBARA_PRESET,
3
+ ENV_INTERNAL_APIBARA_PRESETS,
4
+ ENV_INTERNAL_APIBARA_PROCESSED_RUNTIME,
5
+ ENV_INTERNAL_APIBARA_RUNTIME,
6
+ USER_ENV_APIBARA_RUNTIME_CONFIG,
7
+ getProcessedRuntimeConfig,
8
+ } from "apibara/common";
9
+ import type { ApibaraOptions } from "apibara/types";
10
+
11
+ export function runtimeConfigResolver(options: Partial<ApibaraOptions>) {
12
+ try {
13
+ const { runtimeConfig, preset, presets } = options;
14
+
15
+ if (runtimeConfig) {
16
+ process.env[ENV_INTERNAL_APIBARA_RUNTIME] = JSON.stringify(runtimeConfig);
17
+ }
18
+ if (preset) {
19
+ process.env[ENV_INTERNAL_APIBARA_PRESET] = preset;
20
+ }
21
+
22
+ if (presets) {
23
+ process.env[ENV_INTERNAL_APIBARA_PRESETS] = JSON.stringify(presets);
24
+ }
25
+
26
+ const userEnvRuntimeConfig = JSON.parse(
27
+ process.env[USER_ENV_APIBARA_RUNTIME_CONFIG] ?? "{}",
28
+ );
29
+
30
+ // This is final processed runtime config that will be used by the indexer and useRuntimeConfig hook
31
+ process.env[ENV_INTERNAL_APIBARA_PROCESSED_RUNTIME] = JSON.stringify(
32
+ getProcessedRuntimeConfig({
33
+ preset,
34
+ presets,
35
+ runtimeConfig,
36
+ userEnvRuntimeConfig,
37
+ }),
38
+ );
39
+ } catch (error) {
40
+ throw new Error("Failed to process & set runtime environment variables", {
41
+ cause: error,
42
+ });
43
+ }
44
+ }
@@ -1,9 +1,13 @@
1
1
  import type { Apibara, ApibaraDynamicConfig } from "apibara/types";
2
+ import { runtimeConfigResolver } from "./resolvers/runtime.resolver";
2
3
 
3
4
  export async function updateApibaraConfig(
4
5
  apibara: Apibara,
5
- _config: ApibaraDynamicConfig,
6
+ newConfig: ApibaraDynamicConfig,
6
7
  ) {
7
- await apibara.hooks.callHook("rollup:reload");
8
+ // applies new config values to env again during hot reload
9
+ runtimeConfigResolver(newConfig);
8
10
  apibara.logger.success("Apibara config hot reloaded!");
11
+ // we simply stop indexers and restart them with updated runtime values
12
+ await apibara.hooks.callHook("dev:reload");
9
13
  }
package/src/core/scan.ts CHANGED
@@ -2,7 +2,7 @@ import type { Apibara } from "apibara/types";
2
2
  import fse from "fs-extra";
3
3
  import { basename, join } from "pathe";
4
4
 
5
- const INDEXER_EXTENSIONS = [".indexer.ts", ".indexer.js"];
5
+ const INDEXER_EXTENSIONS = [".indexer.ts", ".indexer.js", ".indexer.mjs"];
6
6
 
7
7
  export async function scanIndexers(apibara: Apibara) {
8
8
  apibara.logger.debug("Scanning indexers");
package/src/create/add.ts CHANGED
@@ -37,6 +37,7 @@ type Options = {
37
37
  argNetwork?: string;
38
38
  argStorage?: string;
39
39
  argDnaUrl?: string;
40
+ argRootDir?: string;
40
41
  };
41
42
 
42
43
  export async function addIndexer({
@@ -45,8 +46,10 @@ export async function addIndexer({
45
46
  argNetwork,
46
47
  argStorage,
47
48
  argDnaUrl,
49
+ argRootDir,
48
50
  }: Options) {
49
- const configExists = hasApibaraConfig(process.cwd());
51
+ const cwd = path.join(process.cwd(), argRootDir ?? ".");
52
+ const configExists = hasApibaraConfig(cwd);
50
53
 
51
54
  if (!configExists) {
52
55
  consola.error("No apibara.config found in the current directory.");
@@ -74,7 +77,7 @@ export async function addIndexer({
74
77
  }
75
78
  }
76
79
 
77
- const language = getApibaraConfigLanguage(process.cwd());
80
+ const { language, extension } = getApibaraConfigLanguage(cwd);
78
81
 
79
82
  validateIndexerId(argIndexerId, true);
80
83
  validateChain(argChain, true);
@@ -92,14 +95,10 @@ export async function addIndexer({
92
95
  validate: (id) =>
93
96
  validateIndexerId(id)
94
97
  ? checkFileExists(
95
- path.join(
96
- process.cwd(),
97
- "indexers",
98
- `${id}.indexer.${language === "typescript" ? "ts" : "js"}`,
99
- ),
98
+ path.join(cwd, "indexers", `${id}.indexer.${extension}`),
100
99
  ).then(({ exists }) =>
101
100
  exists
102
- ? `Indexer ${cyan(`${id}.indexer.${language === "typescript" ? "ts" : "js"}`)} already exists`
101
+ ? `Indexer ${cyan(`${id}.indexer.${extension}`)} already exists`
103
102
  : true,
104
103
  )
105
104
  : "Invalid indexer ID, it cannot be empty and must be in kebab-case format",
@@ -197,7 +196,7 @@ export async function addIndexer({
197
196
  const pkgManager = getPackageManager();
198
197
 
199
198
  const options: IndexerOptions = {
200
- cwd: process.cwd(),
199
+ cwd: cwd,
201
200
  indexerFileId,
202
201
  indexerId: convertKebabToCamelCase(indexerFileId),
203
202
  chain: (argChain as Chain) ?? prompt_chain?.name!,
@@ -206,13 +205,12 @@ export async function addIndexer({
206
205
  dnaUrl: argDnaUrl ?? prompt_dnaUrl,
207
206
  language,
208
207
  packageManager: pkgManager.name,
208
+ extension,
209
209
  };
210
210
 
211
211
  await updateApibaraConfigFile(options);
212
212
 
213
- consola.success(
214
- `Updated ${cyan("apibara.config." + (language === "typescript" ? "ts" : "js"))}`,
215
- );
213
+ consola.success(`Updated ${cyan(`apibara.config.${extension}`)}`);
216
214
 
217
215
  await updatePackageJson(options);
218
216
 
@@ -220,19 +218,15 @@ export async function addIndexer({
220
218
 
221
219
  await createIndexerFile(options);
222
220
 
223
- consola.success(
224
- `Created ${cyan(`${indexerFileId}.indexer.${language === "typescript" ? "ts" : "js"}`)}`,
225
- );
221
+ consola.success(`Created ${cyan(`${indexerFileId}.indexer.${extension}`)}`);
226
222
 
227
223
  await createStorageRelatedFiles(options);
228
224
 
229
225
  console.log();
230
226
 
227
+ const baseCommand = `${options.packageManager} install`;
228
+ const tsCommand = `${baseCommand} && ${options.packageManager} run prepare`;
231
229
  consola.info(
232
- `Before running the indexer, run ${cyan(`${options.packageManager} run install`)}${
233
- language === "typescript"
234
- ? " & " + cyan(`${options.packageManager} run prepare`)
235
- : ""
236
- }`,
230
+ `Before running the indexer, run ${cyan(language === "typescript" ? tsCommand : baseCommand)}`,
237
231
  );
238
232
  }
@@ -67,7 +67,6 @@ export const storages: StorageDataType[] = [
67
67
  export const packageVersions = {
68
68
  // Required Dependencies
69
69
  apibara: "next",
70
- "@apibara/indexer": "next",
71
70
  "@apibara/protocol": "next",
72
71
  // Chain Dependencies
73
72
  "@apibara/evm": "next",
@@ -79,20 +78,19 @@ export const packageVersions = {
79
78
  "@apibara/plugin-sqlite": "next",
80
79
  // Postgres Dependencies
81
80
  "@electric-sql/pglite": "^0.2.17",
82
- "drizzle-orm": "^0.37.0",
81
+ "drizzle-orm": "^0.40.1",
83
82
  pg: "^8.13.1",
84
83
  "@types/pg": "^8.11.10",
85
84
  "drizzle-kit": "^0.29.0",
86
85
  // Typescript Dependencies
87
86
  typescript: "^5.6.2",
88
- "@rollup/plugin-typescript": "^11.1.6",
89
87
  "@types/node": "^20.5.2",
90
88
  };
91
89
 
92
90
  export const dnaUrls = {
93
- ethereum: "https://ethereum.preview.apibara.org",
94
- ethereumSepolia: "https://ethereum-sepolia.preview.apibara.org",
91
+ ethereum: "https://mainnet.ethereum.a5a.ch",
92
+ ethereumSepolia: "https://sepolia.ethereum.a5a.ch",
95
93
  beaconchain: "https://beaconchain.preview.apibara.org",
96
- starknet: "https://starknet.preview.apibara.org",
97
- starknetSepolia: "https://starknet-sepolia.preview.apibara.org",
94
+ starknet: "https://mainnet.starknet.a5a.ch",
95
+ starknetSepolia: "https://sepolia.starknet.a5a.ch",
98
96
  };
@@ -5,6 +5,7 @@ import prompts from "prompts";
5
5
  import { addIndexer } from "./add";
6
6
  import { cyan, green } from "./colors";
7
7
  import {
8
+ createGitIgnoreFile,
8
9
  generateApibaraConfig,
9
10
  generatePackageJson,
10
11
  generateTsConfig,
@@ -129,7 +130,7 @@ export async function initializeProject({
129
130
  JSON.stringify(packageJson, null, 2) + "\n",
130
131
  );
131
132
  await formatFile(packageJsonPath);
132
- consola.success("Created ", cyan("package.json"));
133
+ consola.success("Created", cyan("package.json"));
133
134
 
134
135
  // Generate tsconfig.json if TypeScript
135
136
  if (isTs) {
@@ -137,7 +138,7 @@ export async function initializeProject({
137
138
  const tsConfig = generateTsConfig();
138
139
  fs.writeFileSync(tsConfigPath, JSON.stringify(tsConfig, null, 2) + "\n");
139
140
  await formatFile(tsConfigPath);
140
- consola.success("Created ", cyan("tsconfig.json"));
141
+ consola.success("Created", cyan("tsconfig.json"));
141
142
  }
142
143
 
143
144
  const apibaraConfigPath = path.join(root, `apibara.config.${configExt}`);
@@ -145,7 +146,7 @@ export async function initializeProject({
145
146
  const apibaraConfig = generateApibaraConfig(isTs);
146
147
  fs.writeFileSync(apibaraConfigPath, apibaraConfig);
147
148
  await formatFile(apibaraConfigPath);
148
- consola.success("Created ", cyan(`apibara.config.${configExt}`));
149
+ consola.success("Created", cyan(`apibara.config.${configExt}`));
149
150
 
150
151
  // Create "indexers" directory if not exists
151
152
  const indexersDir = path.join(root, "indexers");
@@ -154,6 +155,8 @@ export async function initializeProject({
154
155
  consola.success(`Created ${cyan("indexers")} directory`);
155
156
  }
156
157
 
158
+ await createGitIgnoreFile(root);
159
+
157
160
  console.log("\n");
158
161
 
159
162
  consola.ready(green("Project initialized successfully"));
@@ -163,12 +166,12 @@ export async function initializeProject({
163
166
  if (!argNoCreateIndexer) {
164
167
  consola.info("Let's create an indexer\n");
165
168
 
166
- await addIndexer({});
169
+ await addIndexer({ argRootDir: argTargetDir });
167
170
  } else {
168
171
  const pkgManager = getPackageManager();
169
172
  consola.info(
170
173
  "Run ",
171
- green(`${pkgManager.name} run install`),
174
+ green(`${pkgManager.name} install`),
172
175
  " to install all dependencies",
173
176
  );
174
177
  }
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { consola } from "consola";
4
+ import prompts from "prompts";
4
5
  import { type ObjectLiteralExpression, Project, SyntaxKind } from "ts-morph";
5
6
  import { cyan, green, magenta, yellow } from "./colors";
6
7
  import { packageVersions } from "./constants";
@@ -14,21 +15,18 @@ export function generatePackageJson(isTypeScript: boolean) {
14
15
  private: true,
15
16
  type: "module",
16
17
  scripts: {
17
- prepare: "apibara prepare",
18
+ ...(isTypeScript && { prepare: "apibara prepare" }),
18
19
  dev: "apibara dev",
19
20
  start: "apibara start",
20
21
  build: "apibara build",
21
22
  ...(isTypeScript && { typecheck: "tsc --noEmit" }),
22
23
  },
23
24
  dependencies: {
24
- "@apibara/indexer": packageVersions["@apibara/indexer"],
25
25
  "@apibara/protocol": packageVersions["@apibara/protocol"],
26
26
  apibara: packageVersions.apibara,
27
27
  },
28
28
  devDependencies: {
29
29
  ...(isTypeScript && {
30
- "@rollup/plugin-typescript":
31
- packageVersions["@rollup/plugin-typescript"],
32
30
  "@types/node": packageVersions["@types/node"],
33
31
  typescript: packageVersions.typescript,
34
32
  }),
@@ -58,17 +56,10 @@ export function generateTsConfig() {
58
56
  }
59
57
 
60
58
  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";
59
+ return `import { defineConfig } from "apibara/config";
62
60
 
63
61
  export default defineConfig({
64
- runtimeConfig: {},${
65
- isTypeScript
66
- ? `
67
- rollupConfig: {
68
- plugins: [typescript()${isTypeScript ? " as Plugin" : ""}],
69
- },`
70
- : ""
71
- }
62
+ runtimeConfig: {},
72
63
  });\n`;
73
64
  }
74
65
 
@@ -78,9 +69,10 @@ export function generateIndexer({
78
69
  chain,
79
70
  language,
80
71
  }: IndexerOptions) {
81
- return `import { defineIndexer } from "@apibara/indexer";
82
- import { useLogger } from "@apibara/indexer/plugins";
72
+ return `import { defineIndexer } from "apibara/indexer";
73
+ import { useLogger } from "apibara/plugins";
83
74
  ${storage === "postgres" ? `import { drizzleStorage } from "@apibara/plugin-drizzle";` : ""}
75
+ ${storage === "postgres" ? `import { drizzle } from "@apibara/plugin-drizzle";` : ""}
84
76
  ${
85
77
  chain === "ethereum"
86
78
  ? `import { EvmStream } from "@apibara/evm";`
@@ -91,19 +83,16 @@ ${
91
83
  : ""
92
84
  }
93
85
  ${language === "typescript" ? `import type { ApibaraRuntimeConfig } from "apibara/types";` : ""}
94
- ${
95
- storage === "postgres"
96
- ? `import { getDrizzlePgDatabase } from "../lib/db";`
97
- : ""
98
- }
86
+ ${storage === "postgres" ? `import * as schema from "../lib/schema";` : ""}
99
87
 
100
88
 
101
89
  export default function (runtimeConfig${language === "typescript" ? ": ApibaraRuntimeConfig" : ""}) {
102
- const indexerId = "${indexerId}";
103
- const { startingBlock, streamUrl${storage === "postgres" ? ", postgresConnectionString" : ""} } = runtimeConfig[indexerId];
90
+ const { startingBlock, streamUrl } = runtimeConfig["${indexerId}"];
104
91
  ${
105
92
  storage === "postgres"
106
- ? "const { db } = getDrizzlePgDatabase(postgresConnectionString);"
93
+ ? `const db = drizzle({
94
+ schema,
95
+ });`
107
96
  : ""
108
97
  }
109
98
 
@@ -120,9 +109,9 @@ export default function (runtimeConfig${language === "typescript" ? ": ApibaraRu
120
109
  finality: "accepted",
121
110
  startingBlock: BigInt(startingBlock),
122
111
  filter: {
123
- header: "always",
112
+ ${chain === "ethereum" ? "logs: []," : chain === "starknet" ? "events: []," : ""}
124
113
  },
125
- plugins: [${storage === "postgres" ? "drizzleStorage({ db, persistState: true })" : ""}],
114
+ plugins: [${storage === "postgres" ? "drizzleStorage({ db, migrate: { migrationsFolder: './drizzle' } })" : ""}],
126
115
  async transform({ endCursor, finality }) {
127
116
  const logger = useLogger();
128
117
 
@@ -136,14 +125,12 @@ export default function (runtimeConfig${language === "typescript" ? ": ApibaraRu
136
125
  ${
137
126
  storage === "postgres"
138
127
  ? `// 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
- // }`
128
+ // const { db: database } = useDrizzleStorage();
129
+
130
+ // await database.insert(schema.cursorTable).values({
131
+ // endCursor: Number(endCursor?.orderKey),
132
+ // uniqueKey: \`\${endCursor?.uniqueKey}\`,
133
+ // });`
147
134
  : ""
148
135
  }
149
136
  },
@@ -156,7 +143,7 @@ export async function createIndexerFile(options: IndexerOptions) {
156
143
  const indexerFilePath = path.join(
157
144
  options.cwd,
158
145
  "indexers",
159
- `${options.indexerFileId}.indexer.${options.language === "typescript" ? "ts" : "js"}`,
146
+ `${options.indexerFileId}.indexer.${options.extension}`,
160
147
  );
161
148
 
162
149
  const { exists, overwrite } = await checkFileExists(indexerFilePath, {
@@ -227,20 +214,14 @@ export async function updateApibaraConfigFile({
227
214
  language,
228
215
  network,
229
216
  dnaUrl,
217
+ extension,
230
218
  }: IndexerOptions) {
231
- const pathToConfig = path.join(
232
- cwd,
233
- `apibara.config.${language === "typescript" ? "ts" : "js"}`,
234
- );
219
+ const pathToConfig = path.join(cwd, `apibara.config.${extension}`);
235
220
 
236
221
  const runtimeConfigString = `{
237
222
  startingBlock: 0,
238
- streamUrl: "${dnaUrl ?? getDnaUrl(chain, network)}"${
239
- storage === "postgres"
240
- ? `,
241
- postgresConnectionString: process.env["POSTGRES_CONNECTION_STRING"] ?? "memory://${indexerId}"`
242
- : ""
243
- }}`;
223
+ streamUrl: "${dnaUrl ?? getDnaUrl(chain, network)}"
224
+ }`;
244
225
 
245
226
  const project = new Project();
246
227
  const sourceFile = project.addSourceFileAtPath(pathToConfig);
@@ -284,12 +265,16 @@ export async function updateApibaraConfigFile({
284
265
  }
285
266
 
286
267
  export async function createDrizzleStorageFiles(options: IndexerOptions) {
287
- const { cwd, language, storage } = options;
268
+ const {
269
+ cwd,
270
+ language,
271
+ storage,
272
+ indexerId,
273
+ extension: fileExtension,
274
+ } = options;
288
275
 
289
276
  if (storage !== "postgres") return;
290
277
 
291
- const fileExtension = language === "typescript" ? "ts" : "js";
292
-
293
278
  /**
294
279
  *
295
280
  *
@@ -312,11 +297,11 @@ export async function createDrizzleStorageFiles(options: IndexerOptions) {
312
297
  const drizzleConfigContent = `${language === "typescript" ? 'import type { Config } from "drizzle-kit";' : ""}
313
298
 
314
299
  export default {
315
- schema: "./lib/schema.ts",
300
+ schema: "./lib/schema.${fileExtension}",
316
301
  out: "./drizzle",
317
302
  dialect: "postgresql",
318
303
  dbCredentials: {
319
- url: process.env["POSTGRES_CONNECTION_STRING"] ?? "",
304
+ url: process.env["POSTGRES_CONNECTION_STRING"] ?? "memory://${indexerId}",
320
305
  },
321
306
  }${language === "typescript" ? " satisfies Config" : ""};`;
322
307
 
@@ -347,14 +332,14 @@ export default {
347
332
  });
348
333
 
349
334
  if (!schemaExists || schemaOverwrite) {
350
- const schemaContent = `// --- Add your pg table schemas here ----
335
+ const schemaContent = `// --- Add your pg table schemas here ----
351
336
 
352
337
  // import { bigint, pgTable, text, uuid } from "drizzle-orm/pg-core";
353
338
 
354
- // export const exampleTable = pgTable("example_table", {
339
+ // export const cursorTable = pgTable("cursor_table", {
355
340
  // id: uuid("id").primaryKey().defaultRandom(),
356
- // number: bigint("number", { mode: "number" }),
357
- // hash: text("hash"),
341
+ // endCursor: bigint("end_cursor", { mode: "number" }),
342
+ // uniqueKey: text("unique_key"),
358
343
  // });
359
344
 
360
345
  export {};
@@ -366,64 +351,7 @@ export {};
366
351
 
367
352
  await formatFile(schemaPath);
368
353
 
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}`)}`);
354
+ consola.success(`Created ${cyan(`lib/${schemaFileName}`)}`);
427
355
  }
428
356
 
429
357
  console.log("\n");
@@ -440,22 +368,22 @@ export function getDrizzlePgDatabase(connectionString${language === "typescript"
440
368
 
441
369
  ${yellow(`
442
370
  ┌──────────────────────────────────────────┐
443
- │ lib/schema.ts
371
+ │ lib/schema
444
372
  └──────────────────────────────────────────┘
445
373
 
446
374
  import { bigint, pgTable, text, uuid } from "drizzle-orm/pg-core";
447
375
 
448
- export const exampleTable = pgTable("example_table", {
376
+ export const cursorTable = pgTable("cursor_table", {
449
377
  id: uuid("id").primaryKey().defaultRandom(),
450
- number: bigint("number", { mode: "number" }),
451
- hash: text("hash"),
378
+ endCursor: bigint("end_cursor", { mode: "number" }),
379
+ uniqueKey: text("unique_key"),
452
380
  });`)}`);
453
381
 
454
382
  console.log("\n");
455
383
  }
456
384
 
457
385
  consola.info(
458
- `Run ${green(`${options.packageManager} run drizzle:generate`)} & ${green(`${options.packageManager} run drizzle:migrate`)} to generate and apply migrations.`,
386
+ `Run ${green(`${options.packageManager}${options.packageManager === "npm" ? " run" : ""} drizzle:generate`)} & ${green(`${options.packageManager}${options.packageManager === "npm" ? " run" : ""} drizzle:migrate`)} to generate and apply migrations.`,
459
387
  );
460
388
  }
461
389
 
@@ -466,3 +394,103 @@ export async function createStorageRelatedFiles(options: IndexerOptions) {
466
394
  await createDrizzleStorageFiles(options);
467
395
  }
468
396
  }
397
+
398
+ const gitIgnoreItems: {
399
+ isRecommended: boolean;
400
+ description?: string;
401
+ value: string;
402
+ }[] = [
403
+ {
404
+ isRecommended: false,
405
+ value: "node_modules",
406
+ },
407
+ {
408
+ isRecommended: false,
409
+ value: "dist",
410
+ },
411
+ {
412
+ isRecommended: true,
413
+ description: "build and dev files of apibara",
414
+ value: ".apibara",
415
+ },
416
+ {
417
+ isRecommended: false,
418
+ value: ".env",
419
+ },
420
+ {
421
+ isRecommended: false,
422
+ description: "for mac users",
423
+ value: ".DS_Store",
424
+ },
425
+ ];
426
+
427
+ export async function createGitIgnoreFile(cwd: string) {
428
+ const gitIgnorePath = path.join(cwd, ".gitignore");
429
+
430
+ if (fs.existsSync(gitIgnorePath)) {
431
+ const result = await prompts([
432
+ {
433
+ type: "select",
434
+ name: "overwrite",
435
+ message: `${cyan(".gitignore")} already exists. Please choose how to proceed:`,
436
+ initial: 0,
437
+ choices: [
438
+ {
439
+ title: "Choose items to append in your .gitignore",
440
+ value: "append",
441
+ },
442
+ {
443
+ title: "Keep original",
444
+ value: "ignore",
445
+ },
446
+ {
447
+ title: "Overwrite",
448
+ value: "overwrite",
449
+ },
450
+ ],
451
+ },
452
+ {
453
+ type: (overwrite: "append" | "ignore" | "overwrite") =>
454
+ overwrite === "append" ? "multiselect" : null,
455
+ name: "ignoreItems",
456
+ message: "Choose items to append in your .gitignore",
457
+ choices: gitIgnoreItems.map((item) => ({
458
+ title: `${yellow(item.value)}${
459
+ item.description ? ` - ${item.description}` : ""
460
+ }${item.isRecommended ? ` ${green("(recommended)")}` : ""}`,
461
+ value: item.value,
462
+ })),
463
+ },
464
+ ]);
465
+
466
+ const { overwrite, ignoreItems } = result as {
467
+ overwrite: "append" | "ignore" | "overwrite";
468
+ ignoreItems: string[];
469
+ };
470
+
471
+ if (overwrite === "append" && ignoreItems.length > 0) {
472
+ const gitIgnoreContent = fs.readFileSync(gitIgnorePath, "utf8");
473
+ fs.writeFileSync(
474
+ gitIgnorePath,
475
+ `${gitIgnoreContent}\n${result.ignoreItems.join("\n")}`,
476
+ );
477
+ consola.success(`Updated ${cyan(".gitignore")}`);
478
+ return;
479
+ }
480
+
481
+ if (overwrite === "overwrite") {
482
+ fs.writeFileSync(
483
+ gitIgnorePath,
484
+ gitIgnoreItems.map((item) => item.value).join("\n"),
485
+ );
486
+ consola.success(`Updated ${cyan(".gitignore")}`);
487
+ return;
488
+ }
489
+ }
490
+
491
+ fs.writeFileSync(
492
+ gitIgnorePath,
493
+ gitIgnoreItems.map((item) => item.value).join("\n"),
494
+ );
495
+ consola.success(`Created ${cyan(".gitignore")}`);
496
+ }
@@ -16,6 +16,8 @@ export type Network =
16
16
 
17
17
  export type Storage = "postgres" | "none";
18
18
 
19
+ export type FileExtension = "js" | "ts" | "mjs";
20
+
19
21
  export type IndexerOptions = {
20
22
  cwd: string;
21
23
  indexerFileId: string;
@@ -26,6 +28,7 @@ export type IndexerOptions = {
26
28
  dnaUrl?: string;
27
29
  packageManager: string;
28
30
  language: Language;
31
+ extension: FileExtension;
29
32
  };
30
33
 
31
34
  export type PkgInfo = {