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

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