Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions apps/ensindexer/src/lib/file-checksum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// src/lib/checksum.ts

Comment on lines +1 to +2
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File header comment is misleading (// src/lib/checksum.ts) and doesn’t match the actual filename (file-checksum.ts). Please update or remove it to avoid confusion when navigating/searching.

Suggested change
// src/lib/checksum.ts

Copilot uses AI. Check for mistakes.
import { createHash } from "node:crypto";
import { resolve } from "node:path";

import ts from "typescript";

import { logger } from "@/lib/logger";
Comment on lines +6 to +8
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fileChecksum imports typescript at runtime. In this package, typescript is currently a devDependency, so environments that install only production deps will crash when this code runs. Either move typescript to dependencies or change the implementation to avoid requiring the TypeScript compiler at runtime.

Copilot uses AI. Check for mistakes.

/**
* Get a checksum representing a given entry point file.
*
* @param entryPoint The entry point file for which to compute the checksum.
* This is a path relative to the ENSIndexer app directory (`apps/ensindexer`).
* @param tsConfigPath The path to the TypeScript configuration file.
* @returns The computed checksum as a string.
*
* @example
* ```ts
* // Compute checksums for the `apps/ensindexer/ponder/ponder.config.ts` file
* const ponderConfigChecksum = fileChecksum("ponder/ponder.config.ts");
* // Compute checksums for the `apps/ensindexer/ponder/ponder.schema.ts` file
* const ponderSchemaChecksum = fileChecksum("ponder/ponder.schema.ts");
* // Compute checksums for the `apps/ensindexer/ponder/src/register-handlers.ts` file
* const ponderLogicChecksum = fileChecksum("ponder/src/register-handlers.ts");
* ```
*/
export function fileChecksum(entryPoint: string, tsConfigPath: string = "tsconfig.json"): string {
const root = process.cwd();
const resolvedEntry = resolve(root, entryPoint);

logger.info({
msg: "Computing entrypoint checksum",
entryPoint,
resolvedEntry,
});

// Read tsconfig
const tsConfigFile = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
if (tsConfigFile.error) {
throw new Error(`Failed to read tsconfig: ${tsConfigPath}`);
}

const parsedTsConfig = ts.parseJsonConfigFileContent(
tsConfigFile.config,
ts.sys,
root,
undefined,
tsConfigPath,
);

const tsProgram = ts.createProgram([resolvedEntry], parsedTsConfig.options);
Comment on lines +28 to +52
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New helper has non-trivial behavior (nested import resolution + hashing) but no tests. Adding a file-checksum.test.ts with small fixture files would help lock in: (1) checksum changes when an imported file changes (even if entrypoint is only a re-export), and (2) determinism across runs.

Copilot uses AI. Check for mistakes.

const allTsFiles = tsProgram
.getSourceFiles()
.filter(
(sourceFile) =>
!sourceFile.isDeclarationFile &&
!sourceFile.fileName.includes("node_modules") &&
!sourceFile.fileName.startsWith("\0"),
)
.map((sourceFile) => sourceFile.fileName);
const tsSourceFiles = new Set<string>(allTsFiles);

if (tsSourceFiles.size === 0) {
throw new Error(`No source files found for entry point: ${entryPoint}`);
} else {
logger.info({
msg: "Files included in logic checksum",
entryPoint,
filesCount: tsSourceFiles.size,
Comment on lines +63 to +71
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checksum depends on the iteration order of tsProgram.getSourceFiles(). That ordering is not part of a stable public contract, so the same project can produce different checksums across TS versions/OS, causing unnecessary Build ID churn. Sort the final file list (e.g. by normalized file path) before hashing to make the checksum deterministic.

Suggested change
const tsSourceFiles = new Set<string>(allTsFiles);
if (tsSourceFiles.size === 0) {
throw new Error(`No source files found for entry point: ${entryPoint}`);
} else {
logger.info({
msg: "Files included in logic checksum",
entryPoint,
filesCount: tsSourceFiles.size,
const tsSourceFiles = Array.from(
new Map(allTsFiles.map((file) => [file.replace(/\\/g, "/"), file])).entries(),
)
.sort(([normalizedFileA], [normalizedFileB]) => normalizedFileA.localeCompare(normalizedFileB))
.map(([, file]) => file);
if (tsSourceFiles.length === 0) {
throw new Error(`No source files found for entry point: ${entryPoint}`);
} else {
logger.info({
msg: "Files included in logic checksum",
entryPoint,
filesCount: tsSourceFiles.length,

Copilot uses AI. Check for mistakes.
});
}

const hash = createHash("sha256");

for (const file of tsSourceFiles) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (const file of tsSourceFiles) {
// Sort files to ensure deterministic hash output across different runs and machines
const sortedFiles = Array.from(tsSourceFiles).sort();
for (const file of sortedFiles) {

File checksum for Build ID is non-deterministic due to unsorted iteration over TypeScript source files returned by compiler

Fix on Vercel

const content = ts.sys.readFile(file);
if (content) hash.update(content);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ts.sys.readFile(file) returns undefined (permissions/race/missing file), the current code silently skips that file, producing a checksum that no longer represents the full dependency set. It’s safer to throw (or at least log+fail) when any expected source file can’t be read.

Suggested change
if (content) hash.update(content);
if (content === undefined) {
throw new Error(`Failed to read source file for checksum: ${file}`);
}
hash.update(content);

Copilot uses AI. Check for mistakes.
}

const checksum = hash.digest("hex").slice(0, 16);

logger.info({
msg: "Logic checksum",
entryPoint,
checksum,
});

return checksum;
}
13 changes: 13 additions & 0 deletions apps/ensindexer/src/lib/indexing-engines/ponder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
} from "ponder:registry";

import { waitForEnsRainbowToBeReady } from "@/lib/ensrainbow/singleton";
import { fileChecksum } from "@/ponder/logical-checksum";

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > error propagation > re-throws errors from async handlers

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > error propagation > re-throws errors from sync handlers

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > handler types > supports sync handlers

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > handler types > supports async handlers

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > event forwarding > supports multiple independent event registrations

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > event forwarding > passes the original event to the handler unchanged

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > context transformation > preserves all other Ponder context properties

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > context transformation > adds ensDb as an alias to the Ponder db

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > handler registration > returns the subscription object from ponder.on

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }

Check failure on line 19 in apps/ensindexer/src/lib/indexing-engines/ponder.ts

View workflow job for this annotation

GitHub Actions / Unit Tests

src/lib/indexing-engines/ponder.test.ts > addOnchainEventListener > handler registration > registers the event name and handler with ponder.on

Error: Cannot find package '@/ponder/logical-checksum' imported from '/home/runner/_work/ensnode/ensnode/apps/ensindexer/src/lib/indexing-engines/ponder.ts' ❯ src/lib/indexing-engines/ponder.ts:19:1 ❯ getPonderModule src/lib/indexing-engines/ponder.test.ts:34:12 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Serialized Error: { code: 'ERR_MODULE_NOT_FOUND' }
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import path looks incorrect: @/ponder/logical-checksum doesn’t exist under apps/ensindexer/src (tsconfig @/* maps to ./src/*). This will fail typecheck/build. Import fileChecksum from the module you added (e.g. @/lib/file-checksum) or add the missing src/ponder/logical-checksum.ts file.

Suggested change
import { fileChecksum } from "@/ponder/logical-checksum";
import { fileChecksum } from "@/lib/file-checksum";

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { fileChecksum } from "@/ponder/logical-checksum";
import { fileChecksum } from "@/lib/file-checksum";

Import path is incorrect: trying to import from @/ponder/logical-checksum but the file is actually at @/lib/file-checksum

Fix on Vercel


/**
* Context passed to event handlers registered with
Expand Down Expand Up @@ -173,6 +174,18 @@
* ```
*/
async function initializeIndexingActivation(): Promise<void> {
const ponderConfigChecksum = fileChecksum("ponder/ponder.config.ts");
const ponderSchemaChecksum = fileChecksum("ponder/ponder.schema.ts");
const ponderLogicChecksum = fileChecksum("ponder/src/register-handlers.ts");

console.log(`Logical checksum`, {
ponderConfig: ponderConfigChecksum,
ponderSchema: ponderSchemaChecksum,
ponderLogic: ponderLogicChecksum,
});
Comment on lines +181 to +185
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This console.log will run on every startup (once per process) and will bypass the app’s structured logger. Please switch to logger (and/or gate behind a debug flag) to avoid noisy stdout in production.

Suggested change
console.log(`Logical checksum`, {
ponderConfig: ponderConfigChecksum,
ponderSchema: ponderSchemaChecksum,
ponderLogic: ponderLogicChecksum,
});
if (process.env.DEBUG) {
console.log(`Logical checksum`, {
ponderConfig: ponderConfigChecksum,
ponderSchema: ponderSchemaChecksum,
ponderLogic: ponderLogicChecksum,
});
}

Copilot uses AI. Check for mistakes.

// TODO: calculate the ENSIndexer Build ID from the checksums above

await waitForEnsRainbowToBeReady();
}

Expand Down
Loading