diff --git a/.changeset/ensindexer-ponder-fork.md b/.changeset/ensindexer-ponder-fork.md new file mode 100644 index 000000000..e6d90dd91 --- /dev/null +++ b/.changeset/ensindexer-ponder-fork.md @@ -0,0 +1,5 @@ +--- +"ensindexer": patch +--- + +ENSIndexer now depends on NameHash's published Ponder fork (`@ensnode/ponder`, aliased to `ponder` in the pnpm catalog) instead of upstream `ponder`. diff --git a/package.json b/package.json index c4f2e3837..e23d70311 100644 --- a/package.json +++ b/package.json @@ -67,12 +67,12 @@ "tar@<7.5.16": "^7.5.16", "ajv@<8.18.0": "^8.18.0", "minimatch@<10.2.3": "^10.2.3", - "ponder>@hono/node-server@<1.19.13": "catalog:", + "@ensnode/ponder>@hono/node-server@<1.19.13": "catalog:", "yauzl@<3.2.1": "^3.2.1", "fast-xml-parser@>=5.0.0 <5.7.0": "^5.7.0", "kysely@>=0.26.0 <0.28.17": "^0.28.17", "yaml@>=2.0.0 <2.8.3": "^2.8.3", - "ponder>vite": "^6.4.3", + "@ensnode/ponder>vite": "^6.4.3", "vite-node>vite": "^6.4.3", "vite@>=7.0.0 <=7.3.4": "^7.3.5", "dompurify@<3.4.11": "^3.4.11", @@ -106,7 +106,6 @@ "@opentelemetry/api": "patches/@opentelemetry__api.patch", "@opentelemetry/otlp-exporter-base": "patches/@opentelemetry__otlp-exporter-base.patch", "js-yaml@4.2.0": "patches/js-yaml@4.2.0.patch", - "ponder@0.16.6": "patches/ponder@0.16.6.patch", "starlight-llms-txt@0.10.0": "patches/starlight-llms-txt@0.10.0.patch" } } diff --git a/packages/integration-test-env/src/lifecycle.ts b/packages/integration-test-env/src/lifecycle.ts index 47d941b5b..5021c0ca1 100644 --- a/packages/integration-test-env/src/lifecycle.ts +++ b/packages/integration-test-env/src/lifecycle.ts @@ -41,6 +41,9 @@ const ENSINDEXER_PORT = 42069; const ENSAPI_PORT = 4334; const ENSDB_PORT = 5433; +// docker-compose.orchestrator.yml overrides container_name to these. +const EFP_DEVNET_CONTAINER = "efp-devnet-orchestrator"; + // Shared config const ENSRAINBOW_URL = `http://localhost:${ENSRAINBOW_PORT}`; const ENSINDEXER_SCHEMA_NAME = "ensindexer_integration_test"; @@ -170,6 +173,73 @@ async function waitForHealth(url: string, timeoutMs: number, serviceName: string throw new Error(`${serviceName} did not become healthy within ${timeoutMs / 1000}s`); } +function containerHealth(container: string): string { + const result = execaSync( + "docker", + ["inspect", "--format", "{{.State.Health.Status}}", container], + { + reject: false, + }, + ); + // `docker inspect` exits non-zero only when the container is missing (or docker itself errors) — + // legitimate `starting`/`unhealthy` states exit 0. Fail loudly instead of returning "" and + // silently looping, which would otherwise mask a container-name mismatch (e.g. if + // docker-compose.orchestrator.yml's container_name diverges from these constants). + if (result.exitCode !== 0) { + throw new Error( + `docker inspect failed for container "${container}" (exit ${result.exitCode}): ${ + result.stderr?.trim() || "unknown error" + }. Does this name match docker-compose's container_name?`, + ); + } + return result.stdout?.trim() ?? "unknown"; +} + +/** + * Wait for a docker container to report `healthy`, restarting it if it stalls. + * + * Some images (notably `efp-devnet`, an amd64 image that runs under QEMU emulation on arm64/Apple + * Silicon) intermittently deadlock on their very first start: the process idles at ~0% CPU and + * never brings up its healthcheck endpoint, so the container sits in `starting`/`unhealthy` + * forever. A `docker restart` reliably clears the hang. We poll for `healthy` within a per-attempt + * budget and restart-and-retry rather than passively waiting out the full compose startup timeout. + */ +async function waitForContainerHealthyWithRestart( + container: string, + serviceName: string, + { attempts, perAttemptMs }: { attempts: number; perAttemptMs: number }, +): Promise { + for (let attempt = 1; attempt <= attempts; attempt++) { + const start = Date.now(); + while (Date.now() - start < perAttemptMs) { + checkAborted(); + const status = containerHealth(container); + if (status === "healthy") { + log(`${serviceName} is healthy`); + return; + } + await new Promise((r) => setTimeout(r, 1000)); + } + + if (attempt < attempts) { + log( + `${serviceName} did not become healthy within ${perAttemptMs / 1000}s (attempt ${attempt}/${attempts}); restarting container...`, + ); + const restart = execaSync("docker", ["restart", container], { reject: false }); + if (restart.exitCode !== 0) { + throw new Error( + `Failed to restart container "${container}" (exit ${restart.exitCode}): ${ + restart.stderr?.trim() || "unknown error" + }. Does this name match docker-compose's container_name?`, + ); + } + } + } + throw new Error( + `${serviceName} did not become healthy after ${attempts} attempts of ${perAttemptMs / 1000}s each`, + ); +} + function spawnService( command: string, args: string[], @@ -292,13 +362,24 @@ export async function bringUp(options: { only?: Set } = {}): Promise {}, 1 << 30); await new Promise(() => {}); } diff --git a/patches/ponder@0.16.6.patch b/patches/ponder@0.16.6.patch deleted file mode 100644 index 373bc98be..000000000 --- a/patches/ponder@0.16.6.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/dist/esm/utils/pg.js b/dist/esm/utils/pg.js -index 0000000..0000000 100644 ---- a/dist/esm/utils/pg.js -+++ b/dist/esm/utils/pg.js -@@ -62,9 +62,24 @@ export function createPool(config, logger) { - } - } - } -+ const rawStatementTimeout = process.env.PONDER_STATEMENT_TIMEOUT; -+ const statementTimeout = rawStatementTimeout === undefined || rawStatementTimeout.trim() === "" -+ ? 2 * 60 * 1000 -+ : Number(rawStatementTimeout); -+ if ( -+ !Number.isFinite(statementTimeout) || -+ !Number.isInteger(statementTimeout) || -+ statementTimeout < 0 -+ ) { -+ throw new Error(`Invalid PONDER_STATEMENT_TIMEOUT: "${rawStatementTimeout}". It must be a non-negative integer (milliseconds).`); -+ } -+ logger.debug({ -+ msg: "Postgres pool config", -+ statement_timeout: statementTimeout, -+ }); - const pool = new pg.Pool({ - // https://stackoverflow.com/questions/59155572/how-to-set-query-timeout-in-relation-to-statement-timeout -- statement_timeout: 2 * 60 * 1000, -+ statement_timeout: statementTimeout, - // @ts-expect-error: The custom Client is an undocumented option. - Client: Client, - ...config, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9fcdc3bf2..884d2b52b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,8 +64,8 @@ catalogs: specifier: 10.1.0 version: 10.1.0 ponder: - specifier: 0.16.6 - version: 0.16.6 + specifier: npm:@ensnode/ponder@0.16.7 + version: 0.16.7 postcss: specifier: 8.5.12 version: 8.5.12 @@ -121,12 +121,12 @@ overrides: tar@<7.5.16: ^7.5.16 ajv@<8.18.0: ^8.18.0 minimatch@<10.2.3: ^10.2.3 - ponder>@hono/node-server@<1.19.13: ^1.19.13 + '@ensnode/ponder>@hono/node-server@<1.19.13': ^1.19.13 yauzl@<3.2.1: ^3.2.1 fast-xml-parser@>=5.0.0 <5.7.0: ^5.7.0 kysely@>=0.26.0 <0.28.17: ^0.28.17 yaml@>=2.0.0 <2.8.3: ^2.8.3 - ponder>vite: ^6.4.3 + '@ensnode/ponder>vite': ^6.4.3 vite-node>vite: ^6.4.3 vite@>=7.0.0 <=7.3.4: ^7.3.5 dompurify@<3.4.11: ^3.4.11 @@ -149,9 +149,6 @@ patchedDependencies: js-yaml@4.2.0: hash: ac52f51689f99f5b6ae9e93670d300024c90ce32bf55ab2753541edc2775c69a path: patches/js-yaml@4.2.0.patch - ponder@0.16.6: - hash: 07ce9ff0276cbbcf8d5b7a93d4109d50575d5b305099e06eb2e0c19953f1eab9 - path: patches/ponder@0.16.6.patch starlight-llms-txt@0.10.0: hash: b0be40873b81b978ff243b51b244d99ddad85f5b0ff444cc9890cb6a1566277a path: patches/starlight-llms-txt@0.10.0.patch @@ -572,7 +569,7 @@ importers: version: 7.1.1 ponder: specifier: 'catalog:' - version: 0.16.6(patch_hash=07ce9ff0276cbbcf8d5b7a93d4109d50575d5b305099e06eb2e0c19953f1eab9)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6) + version: '@ensnode/ponder@0.16.7(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6)' viem: specifier: 'catalog:' version: 2.50.3(typescript@5.9.3)(zod@4.3.6) @@ -1068,7 +1065,7 @@ importers: version: 0.41.0(@electric-sql/pglite@0.2.13)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/pg@8.16.0)(kysely@0.28.17)(pg@8.16.3) ponder: specifier: 'catalog:' - version: 0.16.6(patch_hash=07ce9ff0276cbbcf8d5b7a93d4109d50575d5b305099e06eb2e0c19953f1eab9)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6) + version: '@ensnode/ponder@0.16.7(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6)' viem: specifier: 'catalog:' version: 2.50.3(typescript@5.9.3)(zod@4.3.6) @@ -1115,7 +1112,7 @@ importers: version: 0.41.0(@electric-sql/pglite@0.2.13)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/pg@8.16.0)(kysely@0.28.17)(pg@8.16.3) ponder: specifier: 'catalog:' - version: 0.16.6(patch_hash=07ce9ff0276cbbcf8d5b7a93d4109d50575d5b305099e06eb2e0c19953f1eab9)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6) + version: '@ensnode/ponder@0.16.7(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6)' tsup: specifier: 'catalog:' version: 8.5.0(jiti@2.6.1)(postcss@8.5.12)(tsx@4.22.3)(typescript@5.9.3)(yaml@2.8.3) @@ -2225,6 +2222,18 @@ packages: peerDependencies: viem: ^2.50.3 + '@ensnode/ponder@0.16.7': + resolution: {integrity: sha512-SkFx6k4MlsgsvdfE1n1TGQimyNQ1mqPk/b2dr9/FnB0dawofnFdOw07VACRArkEcsxtwVC0eAfcMjYDmq9QMLw==} + engines: {node: '>=18.14'} + hasBin: true + peerDependencies: + hono: '>=4.5' + typescript: '>=5.0.4' + viem: '>=2' + peerDependenciesMeta: + typescript: + optional: true + '@envelop/core@5.3.2': resolution: {integrity: sha512-06Mu7fmyKzk09P2i2kHpGfItqLLgCq7uO5/nX4fc/iHMplWPNuAx4iYR+WXUQoFHDnP6EUbceQNQ5iyeMz9f3g==} engines: {node: '>=18.0.0'} @@ -8364,18 +8373,6 @@ packages: graphql: ^16.10.0 hono: ^4.6.19 - ponder@0.16.6: - resolution: {integrity: sha512-y4JusgnLZ9uACTr9pj0DjfPSqlsaSLJ7Gxpe/ByfwR5fHuwoPOoolix+Y+9EPqs0cr1KxrG35aVBCR0kT+LJew==} - engines: {node: '>=18.14'} - hasBin: true - peerDependencies: - hono: '>=4.5' - typescript: '>=5.0.4' - viem: '>=2' - peerDependenciesMeta: - typescript: - optional: true - postcss-cli@11.0.1: resolution: {integrity: sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==} engines: {node: '>=18'} @@ -11447,6 +11444,91 @@ snapshots: - gql.tada - graphql + '@ensnode/ponder@0.16.7(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6)': + dependencies: + '@babel/code-frame': 7.29.7 + '@commander-js/extra-typings': 12.1.0(commander@12.1.0) + '@electric-sql/pglite': 0.2.13 + '@escape.tech/graphql-armor-max-aliases': 2.6.2 + '@escape.tech/graphql-armor-max-depth': 2.4.2 + '@escape.tech/graphql-armor-max-tokens': 2.5.1 + '@hono/node-server': 1.19.14(hono@4.12.25) + '@ponder/utils': 0.2.18(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)) + abitype: 0.10.3(typescript@5.9.3)(zod@4.3.6) + ansi-escapes: 7.1.1 + commander: 12.1.0 + conf: 12.0.0 + dataloader: 2.2.3 + detect-package-manager: 3.0.2 + dotenv: 16.6.1 + drizzle-orm: 0.41.0(@electric-sql/pglite@0.2.13)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/pg@8.16.0)(kysely@0.28.17)(pg@8.16.3) + glob: 10.5.0 + graphql: 16.8.2 + graphql-yoga: 5.17.1(graphql@16.8.2) + hono: 4.12.25 + http-terminator: 3.2.0 + kysely: 0.28.17 + pg: 8.16.3 + pg-connection-string: 2.9.1 + pg-copy-streams: 6.0.6 + pg-query-emscripten: 5.1.0 + picocolors: 1.1.1 + pino: 8.21.0 + prom-client: 15.1.3 + semver: 7.8.0 + stacktrace-parser: 0.1.11 + superjson: 2.2.6 + terminal-size: 4.0.0 + viem: 2.50.3(typescript@5.9.3)(zod@4.3.6) + vite: 6.4.3(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(yaml@2.8.3) + vite-node: 1.0.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(yaml@2.8.3) + vite-tsconfig-paths: 4.3.1(typescript@5.9.3)(vite@6.4.3(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(yaml@2.8.3)) + ws: 8.21.0 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@aws-sdk/client-rds-data' + - '@cloudflare/workers-types' + - '@libsql/client' + - '@libsql/client-wasm' + - '@neondatabase/serverless' + - '@op-engineering/op-sqlite' + - '@opentelemetry/api' + - '@planetscale/database' + - '@prisma/client' + - '@tidbcloud/serverless' + - '@types/better-sqlite3' + - '@types/node' + - '@types/pg' + - '@types/sql.js' + - '@vercel/postgres' + - '@xata.io/client' + - better-sqlite3 + - bufferutil + - bun-types + - expo-sqlite + - gel + - jiti + - knex + - less + - lightningcss + - mysql2 + - pg-native + - postgres + - prisma + - sass + - sass-embedded + - sql.js + - sqlite3 + - stylus + - sugarss + - supports-color + - terser + - tsx + - utf-8-validate + - yaml + - zod + '@envelop/core@5.3.2': dependencies: '@envelop/instrumentation': 1.0.0 @@ -18564,91 +18646,6 @@ snapshots: graphql: 16.11.0 hono: 4.12.25 - ponder@0.16.6(patch_hash=07ce9ff0276cbbcf8d5b7a93d4109d50575d5b305099e06eb2e0c19953f1eab9)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.25)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)(zod@4.3.6))(yaml@2.8.3)(zod@4.3.6): - dependencies: - '@babel/code-frame': 7.29.7 - '@commander-js/extra-typings': 12.1.0(commander@12.1.0) - '@electric-sql/pglite': 0.2.13 - '@escape.tech/graphql-armor-max-aliases': 2.6.2 - '@escape.tech/graphql-armor-max-depth': 2.4.2 - '@escape.tech/graphql-armor-max-tokens': 2.5.1 - '@hono/node-server': 1.19.14(hono@4.12.25) - '@ponder/utils': 0.2.18(typescript@5.9.3)(viem@2.50.3(typescript@5.9.3)) - abitype: 0.10.3(typescript@5.9.3)(zod@4.3.6) - ansi-escapes: 7.1.1 - commander: 12.1.0 - conf: 12.0.0 - dataloader: 2.2.3 - detect-package-manager: 3.0.2 - dotenv: 16.6.1 - drizzle-orm: 0.41.0(@electric-sql/pglite@0.2.13)(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/pg@8.16.0)(kysely@0.28.17)(pg@8.16.3) - glob: 10.5.0 - graphql: 16.8.2 - graphql-yoga: 5.17.1(graphql@16.8.2) - hono: 4.12.25 - http-terminator: 3.2.0 - kysely: 0.28.17 - pg: 8.16.3 - pg-connection-string: 2.9.1 - pg-copy-streams: 6.0.6 - pg-query-emscripten: 5.1.0 - picocolors: 1.1.1 - pino: 8.21.0 - prom-client: 15.1.3 - semver: 7.8.0 - stacktrace-parser: 0.1.11 - superjson: 2.2.6 - terminal-size: 4.0.0 - viem: 2.50.3(typescript@5.9.3)(zod@4.3.6) - vite: 6.4.3(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(yaml@2.8.3) - vite-node: 1.0.2(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(yaml@2.8.3) - vite-tsconfig-paths: 4.3.1(typescript@5.9.3)(vite@6.4.3(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.22.3)(yaml@2.8.3)) - ws: 8.21.0 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@aws-sdk/client-rds-data' - - '@cloudflare/workers-types' - - '@libsql/client' - - '@libsql/client-wasm' - - '@neondatabase/serverless' - - '@op-engineering/op-sqlite' - - '@opentelemetry/api' - - '@planetscale/database' - - '@prisma/client' - - '@tidbcloud/serverless' - - '@types/better-sqlite3' - - '@types/node' - - '@types/pg' - - '@types/sql.js' - - '@vercel/postgres' - - '@xata.io/client' - - better-sqlite3 - - bufferutil - - bun-types - - expo-sqlite - - gel - - jiti - - knex - - less - - lightningcss - - mysql2 - - pg-native - - postgres - - prisma - - sass - - sass-embedded - - sql.js - - sqlite3 - - stylus - - sugarss - - supports-color - - terser - - tsx - - utf-8-validate - - yaml - - zod - postcss-cli@11.0.1(jiti@2.6.1)(postcss@8.5.12)(tsx@4.22.3): dependencies: chokidar: 3.6.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 49eb833dd..1a53e4a57 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -25,7 +25,7 @@ catalog: p-retry: 7.1.1 pg-connection-string: ^2.9.1 pino: 10.1.0 - ponder: 0.16.6 + ponder: "npm:@ensnode/ponder@0.16.7" postcss: 8.5.12 prettier: 3.6.2 prettier-plugin-astro: 0.14.1