Skip to content

Commit

Permalink
feat: add cli package (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
fracek authored Aug 7, 2024
2 parents aafd709 + 16541b9 commit 8eb401b
Show file tree
Hide file tree
Showing 53 changed files with 2,074 additions and 225 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ jobs:
run: pnpm lint
- name: Run format
run: pnpm format
- name: Run typecheck
run: pnpm typecheck
- name: Run build
run: pnpm build
- name: Run typecheck
run: pnpm typecheck
- name: Run test
run: pnpm test:ci
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules
dist
.turbo
bun.lockb
.vscode
.vscode
.apibara
3 changes: 3 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"recommended": true,
"correctness": {
"noUnusedImports": "error"
},
"style": {
"noNonNullAssertion": "off"
}
}
},
Expand Down
7 changes: 7 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `@apibara/cli`

TODO

## Installation

TODO
31 changes: 31 additions & 0 deletions packages/cli/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { fileURLToPath } from "node:url";
import { resolve } from "pathe";
import { defineBuildConfig } from "unbuild";

const modules = ["cli", "config", "core", "rollup", "types", "hooks "];

// @ts-ignore The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', or 'nodenext'.
const srcDir = fileURLToPath(new URL("src", import.meta.url));

export default defineBuildConfig({
entries: [
{ input: "./src/cli/index.ts" },
{ input: "./src/config/index.ts" },
{ input: "./src/core/index.ts" },
{ input: "./src/rollup/index.ts" },
{ input: "./src/types/index.ts" },
{ input: "./src/hooks/index.ts" },
],
clean: true,
outDir: "./dist",
declaration: true,
alias: {
...Object.fromEntries(
modules.map((module) => [
`apibara/${module}`,
resolve(srcDir, `${module}/index.ts`),
]),
),
},
externals: [...modules.map((module) => `apibara/${module}`)],
});
85 changes: 85 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"name": "apibara",
"version": "2.0.0",
"type": "module",
"source": "./src/core/index.ts",
"main": "./dist/core/index.mjs",
"exports": {
".": {
"types": "./dist/core/index.d.ts",
"import": "./dist/core/index.mjs"
},
"./cli": {
"types": "./dist/cli/index.d.ts",
"import": "./dist/cli/index.mjs"
},
"./config": {
"types": "./dist/config/index.d.ts",
"import": "./dist/config/index.mjs"
},
"./core": {
"types": "./dist/core/index.d.ts",
"import": "./dist/core/index.mjs"
},
"./rollup": {
"types": "./dist/rollup/index.d.ts",
"import": "./dist/rollup/index.mjs"
},
"./types": {
"types": "./dist/types/index.d.ts",
"import": "./dist/types/index.mjs"
},
"./hooks": {
"types": "./dist/hooks/index.d.ts",
"import": "./dist/hooks/index.mjs"
}
},
"bin": {
"apibara": "./dist/cli/index.mjs"
},
"files": ["dist", "src", "README.md"],
"scripts": {
"build": "unbuild",
"lint": "biome check .",
"typecheck": "tsc --noEmit",
"lint:fix": "pnpm lint --write",
"format": "biome format . --write",
"playground": "JITI_ESM_RESOLVE=1 NODE_OPTIONS=\"--enable-source-maps\" jiti ./src/cli/index.ts",
"playground:prepare": "pnpm playground prepare --dir playground",
"playground:dev": "pnpm playground dev --dir playground",
"playground:build": "pnpm playground build --dir playground",
"playground:start": "jiti ./playground/.apibara/build/main.mjs"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/node": "^20.14.0",
"jiti": "^1.21.0",
"unbuild": "^2.0.0",
"viem": "^2.12.4",
"vitest": "^1.6.0"
},
"dependencies": {
"@apibara/evm": "workspace:*",
"@apibara/indexer": "workspace:*",
"@apibara/protocol": "workspace:*",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"c12": "^1.11.1",
"chokidar": "^3.6.0",
"citty": "^0.1.6",
"consola": "^3.2.3",
"defu": "^6.1.4",
"esbuild": "^0.23.0",
"fs-extra": "^11.2.0",
"hookable": "^5.5.3",
"klona": "^2.0.6",
"pathe": "^1.1.2",
"perfect-debounce": "^1.0.0",
"pkg-types": "^1.1.3",
"rollup": "^4.18.1",
"tslib": "^2.6.3",
"untyped": "^1.4.2"
}
}
19 changes: 19 additions & 0 deletions packages/cli/playground/apibara.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defaultSink } from "@apibara/indexer";
import { defineConfig } from "apibara/config";

export default defineConfig({
runtimeConfig: {
test: 123,
check: "something",
},
presets: {
dev: {
runtimeConfig: {
test: 999,
},
},
},
sink: {
default: () => defaultSink(),
},
});
30 changes: 30 additions & 0 deletions packages/cli/playground/indexers/simple.indexer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { EvmStream } from "@apibara/evm";
import { defineIndexer } from "@apibara/indexer";
import { encodeEventTopics, parseAbi } from "viem";

const abi = parseAbi([
"event Transfer(address indexed from, address indexed to, uint256 value)",
]);

export default defineIndexer(EvmStream)({
streamUrl: "https://sepolia.ethereum.a5a.ch",
finality: "accepted",
startingCursor: {
orderKey: 5_000_000n,
},
filter: {
logs: [
{
strict: true,
topics: encodeEventTopics({
abi,
eventName: "Transfer",
args: { from: null, to: null },
}) as `0x${string}`[],
},
],
},
async transform({ block: { header } }) {
return [header];
},
});
35 changes: 35 additions & 0 deletions packages/cli/playground/indexers/with-config.indexer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { EvmStream } from "@apibara/evm";
import { defineIndexer } from "@apibara/indexer";
import type { ApibaraRuntimeConfig } from "apibara/types";
import consola from "consola";
import { encodeEventTopics, parseAbi } from "viem";

const abi = parseAbi([
"event Transfer(address indexed from, address indexed to, uint256 value)",
]);

export default function indexer(runtimeConfig: ApibaraRuntimeConfig) {
consola.log("Runtime Config", runtimeConfig);
return defineIndexer(EvmStream)({
streamUrl: "https://sepolia.ethereum.a5a.ch",
finality: "accepted",
startingCursor: {
orderKey: 5_000_000n,
},
filter: {
logs: [
{
strict: true,
topics: encodeEventTopics({
abi,
eventName: "Transfer",
args: { from: null, to: null },
}) as `0x${string}`[],
},
],
},
async transform({ block: { header } }) {
return [header];
},
});
}
11 changes: 11 additions & 0 deletions packages/cli/playground/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": ["../../../tsconfig.json", "./.apibara/types/tsconfig.json"],
"compilerOptions": {
"outDir": "dist",
"declarationDir": "dist",
"noEmit": false,
"rootDir": ".",
"types": ["node"]
},
"include": ["indexers/", "apibara.config.ts"]
}
26 changes: 26 additions & 0 deletions packages/cli/src/cli/commands/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { build, createApibara, prepare, writeTypes } from "apibara/core";
import { defineCommand } from "citty";
import consola from "consola";
import { resolve } from "pathe";
import { commonArgs } from "../common";

export default defineCommand({
meta: {
name: "build",
description: "Build indexer",
},
args: {
...commonArgs,
},
async run({ args }) {
consola.start("Building");
const rootDir = resolve((args.dir || args._dir || ".") as string);
const apibara = await createApibara({
rootDir,
});
await prepare(apibara);
await writeTypes(apibara);
await build(apibara);
await apibara.close();
},
});
110 changes: 110 additions & 0 deletions packages/cli/src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { type ChildProcess, spawn } from "node:child_process";
import { build, createApibara, prepare, writeTypes } from "apibara/core";
import type { Apibara } from "apibara/types";
import { defineCommand } from "citty";
import consola from "consola";
import { resolve } from "pathe";
import { commonArgs } from "../common";

// Hot module reloading key regex
// for only runtimeConfig.* keys
const hmrKeyRe = /^runtimeConfig\./;

let childProcess: ChildProcess | undefined;

export default defineCommand({
meta: {
name: "dev",
description: "Start the development server",
},
args: {
...commonArgs,
indexers: {
type: "string",
description: "Comma-separated list of indexers to run",
},
preset: {
type: "string",
description: "Preset to use",
},
sink: {
type: "string",
description: "Sink to use",
},
},
async run({ args }) {
consola.start("Starting dev server");
const rootDir = resolve((args.dir || args._dir || ".") as string);
let apibara: Apibara;

const reload = async () => {
if (apibara) {
consola.info("Restarting dev server");
if ("unwatch" in apibara.options._c12) {
await apibara.options._c12.unwatch();
}
await apibara.close();
}
apibara = await createApibara(
{
rootDir,
},
{
watch: true,
c12: {
async onUpdate({ getDiff, newConfig }) {
const diff = getDiff();

if (diff.length === 0) {
return; // No changes
}

consola.info(
`Nitro config updated:
${diff.map((entry) => ` ${entry.toString()}`).join("\n")}`,
);
await (diff.every((e) => hmrKeyRe.test(e.key))
? apibara.updateConfig(newConfig.config || {}) // Hot reload
: reload()); // Full reload
},
},
},
true,
);
apibara.hooks.hookOnce("restart", reload);

await prepare(apibara);
await writeTypes(apibara);
await build(apibara);

apibara.hooks.hook("dev:reload", () => {
if (childProcess) {
consola.start("Restarting indexers");
childProcess.kill();
} else {
consola.success("Dev server started");
consola.success("Starting indexers");
}

const childArgs = [
resolve(apibara.options.outputDir || "./.apibara/build", "main.mjs"),
...(args.indexers ? ["--indexers", args.indexers] : []),
...(args.preset ? ["--preset", args.preset] : []),
...(args.sink ? ["--sink", args.sink] : []),
];

childProcess = spawn("jiti", childArgs, {
stdio: "inherit",
});

childProcess.on("close", (code) => {
if (code !== null) {
consola.log(`Indexers process exited with code ${code}`);
}
});
});
};

await reload();
},
});
Loading

0 comments on commit 8eb401b

Please sign in to comment.