From 2604c0d8574995171ecb70c9f6cdeb99c0229561 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 08:15:53 +0200 Subject: [PATCH 01/11] Make ENSNode SDK a regular dependency for ENSDb SDK We do not want the users of ENSDb SDK to have to worry about ENSNode SDK dependency on their end --- packages/ensdb-sdk/package.json | 5 +++-- pnpm-lock.yaml | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/ensdb-sdk/package.json b/packages/ensdb-sdk/package.json index b26ab6a37a..81fdf97b23 100644 --- a/packages/ensdb-sdk/package.json +++ b/packages/ensdb-sdk/package.json @@ -58,14 +58,15 @@ "typecheck": "tsgo --noEmit", "drizzle-kit:generate": "drizzle-kit generate" }, + "dependencies": { + "@ensnode/ensnode-sdk": "workspace:*" + }, "peerDependencies": { - "@ensnode/ensnode-sdk": "workspace:*", "drizzle-orm": "catalog:", "ponder": "catalog:", "viem": "catalog:" }, "devDependencies": { - "@ensnode/ensnode-sdk": "workspace:*", "@ensnode/shared-configs": "workspace:*", "drizzle-kit": "0.31.10", "drizzle-orm": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 175697d55d..24b1b88cf0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -842,10 +842,11 @@ importers: packages/enscli: {} packages/ensdb-sdk: - devDependencies: + dependencies: '@ensnode/ensnode-sdk': specifier: workspace:* version: link:../ensnode-sdk + devDependencies: '@ensnode/shared-configs': specifier: workspace:* version: link:../shared-configs From b133b42298a4c5674208fbcd0986471102a5b2e3 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 10:46:03 +0200 Subject: [PATCH 02/11] Update ENSDb SDK dependencies --- apps/ensapi/package.json | 1 - apps/ensindexer/package.json | 1 - packages/ensdb-sdk/package.json | 4 +- pnpm-lock.yaml | 161 ++++---------------------------- 4 files changed, 20 insertions(+), 147 deletions(-) diff --git a/apps/ensapi/package.json b/apps/ensapi/package.json index 522b318156..280041bc1a 100644 --- a/apps/ensapi/package.json +++ b/apps/ensapi/package.json @@ -58,7 +58,6 @@ "hono": "catalog:", "p-memoize": "^8.0.0", "p-retry": "catalog:", - "pg-connection-string": "catalog:", "pino": "catalog:", "ponder-enrich-gql-docs-middleware": "^0.1.3", "superjson": "^2.2.6", diff --git a/apps/ensindexer/package.json b/apps/ensindexer/package.json index 75d0330fb0..d6da4c51b7 100644 --- a/apps/ensindexer/package.json +++ b/apps/ensindexer/package.json @@ -34,7 +34,6 @@ "deepmerge-ts": "^7.1.5", "dns-packet": "^5.6.1", "drizzle-orm": "catalog:", - "pg-connection-string": "catalog:", "p-retry": "catalog:", "hono": "catalog:", "ponder": "catalog:", diff --git a/packages/ensdb-sdk/package.json b/packages/ensdb-sdk/package.json index 81fdf97b23..3a72d29790 100644 --- a/packages/ensdb-sdk/package.json +++ b/packages/ensdb-sdk/package.json @@ -59,7 +59,9 @@ "drizzle-kit:generate": "drizzle-kit generate" }, "dependencies": { - "@ensnode/ensnode-sdk": "workspace:*" + "@ensnode/ensnode-sdk": "workspace:*", + "pg-connection-string": "catalog:", + "zod": "catalog:" }, "peerDependencies": { "drizzle-orm": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24b1b88cf0..0ccd7328b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -417,9 +417,6 @@ importers: p-retry: specifier: 'catalog:' version: 7.1.1 - pg-connection-string: - specifier: 'catalog:' - version: 2.9.1 pino: specifier: 'catalog:' version: 10.1.0 @@ -511,9 +508,6 @@ importers: p-retry: specifier: 'catalog:' version: 7.1.1 - pg-connection-string: - specifier: 'catalog:' - version: 2.9.1 ponder: specifier: 'catalog:' version: 0.16.3(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.7)(lightningcss@1.30.2)(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@4.3.6))(zod@4.3.6) @@ -846,6 +840,12 @@ importers: '@ensnode/ensnode-sdk': specifier: workspace:* version: link:../ensnode-sdk + pg-connection-string: + specifier: 'catalog:' + version: 2.9.1 + zod: + specifier: 'catalog:' + version: 4.3.6 devDependencies: '@ensnode/shared-configs': specifier: workspace:* @@ -858,7 +858,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.14)(pg@8.16.3) ponder: specifier: 'catalog:' - version: 0.16.3(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.7)(lightningcss@1.30.2)(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@3.25.76))(zod@3.25.76) + version: 0.16.3(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.7)(lightningcss@1.30.2)(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@4.3.6))(zod@4.3.6) tsup: specifier: 'catalog:' version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) @@ -867,7 +867,7 @@ importers: version: 5.9.3 viem: specifier: 'catalog:' - version: 2.38.5(typescript@5.9.3)(zod@3.25.76) + version: 2.38.5(typescript@5.9.3)(zod@4.3.6) vitest: specifier: 'catalog:' version: 4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3) @@ -11829,12 +11829,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@ponder/utils@0.2.17(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@3.25.76))': - dependencies: - viem: 2.38.5(typescript@5.9.3)(zod@3.25.76) - optionalDependencies: - typescript: 5.9.3 - '@ponder/utils@0.2.17(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@4.3.6))': dependencies: viem: 2.38.5(typescript@5.9.3)(zod@4.3.6) @@ -13397,6 +13391,14 @@ snapshots: chai: 6.2.0 tinyrainbow: 3.0.3 + '@vitest/mocker@4.0.5(vite@7.1.12(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3))': + dependencies: + '@vitest/spy': 4.0.5 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.1.12(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3) + '@vitest/mocker@4.0.5(vite@7.1.12(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.0.5 @@ -13548,31 +13550,16 @@ snapshots: '@zeit/schemas@2.36.0': {} - abitype@0.10.3(typescript@5.9.3)(zod@3.25.76): - optionalDependencies: - typescript: 5.9.3 - zod: 3.25.76 - abitype@0.10.3(typescript@5.9.3)(zod@4.3.6): optionalDependencies: typescript: 5.9.3 zod: 4.3.6 - abitype@1.1.0(typescript@5.9.3)(zod@3.25.76): - optionalDependencies: - typescript: 5.9.3 - zod: 3.25.76 - abitype@1.1.0(typescript@5.9.3)(zod@4.3.6): optionalDependencies: typescript: 5.9.3 zod: 4.3.6 - abitype@1.1.1(typescript@5.9.3)(zod@3.25.76): - optionalDependencies: - typescript: 5.9.3 - zod: 3.25.76 - abitype@1.1.1(typescript@5.9.3)(zod@4.3.6): optionalDependencies: typescript: 5.9.3 @@ -16672,21 +16659,6 @@ snapshots: outdent@0.5.0: {} - ox@0.9.6(typescript@5.9.3)(zod@3.25.76): - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3)(zod@3.25.76) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - zod - ox@0.9.6(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -16980,88 +16952,6 @@ snapshots: graphql: 16.11.0 hono: 4.12.7 - ponder@0.16.3(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.7)(lightningcss@1.30.2)(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@3.25.76))(zod@3.25.76): - dependencies: - '@babel/code-frame': 7.29.0 - '@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.11(hono@4.12.7) - '@ponder/utils': 0.2.17(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@3.25.76)) - abitype: 0.10.3(typescript@5.9.3)(zod@3.25.76) - 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.14)(pg@8.16.3) - glob: 10.5.0 - graphql: 16.8.2 - graphql-yoga: 5.17.1(graphql@16.8.2) - hono: 4.12.7 - http-terminator: 3.2.0 - kysely: 0.28.14 - 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.7.3 - stacktrace-parser: 0.1.11 - superjson: 2.2.6 - terminal-size: 4.0.0 - viem: 2.38.5(typescript@5.9.3)(zod@3.25.76) - vite: 5.4.21(@types/node@24.10.9)(lightningcss@1.30.2) - vite-node: 1.0.2(@types/node@24.10.9)(lightningcss@1.30.2) - vite-tsconfig-paths: 4.3.1(typescript@5.9.3)(vite@5.4.21(@types/node@24.10.9)(lightningcss@1.30.2)) - ws: 8.18.3 - 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 - - knex - - less - - lightningcss - - mysql2 - - pg-native - - postgres - - prisma - - sass - - sass-embedded - - sql.js - - sqlite3 - - stylus - - sugarss - - supports-color - - terser - - utf-8-validate - - zod - ponder@0.16.3(@opentelemetry/api@1.9.0(patch_hash=4b2adeefaf7c22f9987d0a125d69cab900719bec7ed7636648bea6947107033a))(@types/node@24.10.9)(@types/pg@8.16.0)(hono@4.12.7)(lightningcss@1.30.2)(typescript@5.9.3)(viem@2.38.5(typescript@5.9.3)(zod@4.3.6))(zod@4.3.6): dependencies: '@babel/code-frame': 7.29.0 @@ -18566,23 +18456,6 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - viem@2.38.5(typescript@5.9.3)(zod@3.25.76): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.3)(zod@3.25.76) - isows: 1.0.7(ws@8.18.3) - ox: 0.9.6(typescript@5.9.3)(zod@3.25.76) - ws: 8.18.3 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - viem@2.38.5(typescript@5.9.3)(zod@4.3.6): dependencies: '@noble/curves': 1.9.1 @@ -18694,7 +18567,7 @@ snapshots: vitest@4.0.5(@types/debug@4.1.12)(@types/node@24.10.9)(jiti@2.6.1)(jsdom@27.0.1(postcss@8.5.6))(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3): dependencies: '@vitest/expect': 4.0.5 - '@vitest/mocker': 4.0.5(vite@7.1.12(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.0.5(vite@7.1.12(@types/node@24.10.9)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)(yaml@2.8.3)) '@vitest/pretty-format': 4.0.5 '@vitest/runner': 4.0.5 '@vitest/snapshot': 4.0.5 From cba260e8bc35471819d1c8b7a2911c9f0ea5f609 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 10:50:53 +0200 Subject: [PATCH 03/11] Extend ENSDb SDK data model Added `EnsDbConfig` type and `validateEnsDbConfig` function --- packages/ensdb-sdk/src/client/ensdb-config.ts | 15 ++++++++ packages/ensdb-sdk/src/client/index.ts | 2 ++ .../src/client/validate/ensdb-config.ts | 23 +++++++++++++ .../src/client/zod-schemas/ensdb-config.ts | 34 +++++++++++++++++++ 4 files changed, 74 insertions(+) create mode 100644 packages/ensdb-sdk/src/client/ensdb-config.ts create mode 100644 packages/ensdb-sdk/src/client/validate/ensdb-config.ts create mode 100644 packages/ensdb-sdk/src/client/zod-schemas/ensdb-config.ts diff --git a/packages/ensdb-sdk/src/client/ensdb-config.ts b/packages/ensdb-sdk/src/client/ensdb-config.ts new file mode 100644 index 0000000000..a5fede78b6 --- /dev/null +++ b/packages/ensdb-sdk/src/client/ensdb-config.ts @@ -0,0 +1,15 @@ +/** + * ENSDb Config + */ +export interface EnsDbConfig { + /** + * PostgreSQL connection string for ENSDb. + * Expected format: postgresql://username:password@host:port/database + */ + ensDbUrl: string; + + /** + * The name of the ENSIndexer Schema in the ENSDb instance. + */ + ensIndexerSchemaName: string; +} diff --git a/packages/ensdb-sdk/src/client/index.ts b/packages/ensdb-sdk/src/client/index.ts index f9b6c416af..4dd2716958 100644 --- a/packages/ensdb-sdk/src/client/index.ts +++ b/packages/ensdb-sdk/src/client/index.ts @@ -1,3 +1,5 @@ +export * from "./ensdb-config"; export * from "./ensdb-reader"; export * from "./ensdb-writer"; export * from "./ensnode-metadata"; +export * from "./validate/ensdb-config"; diff --git a/packages/ensdb-sdk/src/client/validate/ensdb-config.ts b/packages/ensdb-sdk/src/client/validate/ensdb-config.ts new file mode 100644 index 0000000000..41785f92c0 --- /dev/null +++ b/packages/ensdb-sdk/src/client/validate/ensdb-config.ts @@ -0,0 +1,23 @@ +import { prettifyError } from "zod/v4"; + +import type { Unvalidated } from "@ensnode/ensnode-sdk"; + +import type { EnsDbConfig } from "../ensdb-config"; +import { EnsDbConfigSchema } from "../zod-schemas/ensdb-config"; + +/** + * Validate ENSDb config + * + * @param unvalidatedConfig - Unvalidated ENSDb config + * @returns Valid ENSDb config + * @throws Error if validation fails, with details about the validation errors + */ +export function validateEnsDbConfig(unvalidatedConfig: Unvalidated): EnsDbConfig { + const ensDbConfig = EnsDbConfigSchema.safeParse(unvalidatedConfig); + + if (!ensDbConfig.success) { + throw new Error(`Failed to parse ENSDb configuration: \n${prettifyError(ensDbConfig.error)}\n`); + } + + return ensDbConfig.data; +} diff --git a/packages/ensdb-sdk/src/client/zod-schemas/ensdb-config.ts b/packages/ensdb-sdk/src/client/zod-schemas/ensdb-config.ts new file mode 100644 index 0000000000..c88114b7d7 --- /dev/null +++ b/packages/ensdb-sdk/src/client/zod-schemas/ensdb-config.ts @@ -0,0 +1,34 @@ +import { parse as parseConnectionString } from "pg-connection-string"; +import { z } from "zod/v4"; + +export const EnsDbUrlSchema = z.string().refine( + (url) => { + try { + if (!url.startsWith("postgresql://") && !url.startsWith("postgres://")) { + return false; + } + const config = parseConnectionString(url); + return !!(config.host && config.port && config.database); + } catch { + return false; + } + }, + { + error: + "Invalid PostgreSQL connection string for ENSDb. Expected format: postgresql://username:password@host:port/database", + }, +); + +const EnsIndexerSchemaNameSchema = z + .string({ + error: "ENSIndexer Schema Name is required.", + }) + .trim() + .nonempty({ + error: "ENSIndexer Schema Name cannot be an empty string.", + }); + +export const EnsDbConfigSchema = z.object({ + ensDbUrl: EnsDbUrlSchema, + ensIndexerSchemaName: EnsIndexerSchemaNameSchema, +}); From c55893ca209fc7f7017de030ffa02eb231bbc05b Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 10:53:47 +0200 Subject: [PATCH 04/11] docs(changeset): Added `validateEnsDbConfig` function to support validation for the `EnsDbConfig` data model. --- .changeset/fluffy-forks-film.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fluffy-forks-film.md diff --git a/.changeset/fluffy-forks-film.md b/.changeset/fluffy-forks-film.md new file mode 100644 index 0000000000..1fe1969110 --- /dev/null +++ b/.changeset/fluffy-forks-film.md @@ -0,0 +1,5 @@ +--- +"@ensnode/ensdb-sdk": minor +--- + +Added `validateEnsDbConfig` function to support validation for the `EnsDbConfig` data model. From ba3989b69ab517f705f7ee4ab4e1e69049d4ec76 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 10:55:48 +0200 Subject: [PATCH 05/11] Build `EnsDbConfig` for ENSApi Defined `buildEnsDbConfigFromEnvironment` function to source ENSDb config from env vars. --- apps/ensapi/src/config/config.schema.ts | 11 +++- apps/ensapi/src/config/ensdb-config.schema.ts | 60 ------------------- apps/ensapi/src/config/ensdb-config.ts | 32 +++++++++- apps/ensapi/src/lib/ensdb/singleton.ts | 2 +- 4 files changed, 40 insertions(+), 65 deletions(-) delete mode 100644 apps/ensapi/src/config/ensdb-config.schema.ts diff --git a/apps/ensapi/src/config/config.schema.ts b/apps/ensapi/src/config/config.schema.ts index 1fdbe732fb..0e0813d151 100644 --- a/apps/ensapi/src/config/config.schema.ts +++ b/apps/ensapi/src/config/config.schema.ts @@ -14,7 +14,7 @@ import { } from "@ensnode/ensnode-sdk/internal"; import { ENSApi_DEFAULT_PORT } from "@/config/defaults"; -import { EnsDbConfigSchema } from "@/config/ensdb-config.schema"; +import ensDbConfig from "@/config/ensdb-config"; import type { EnsApiEnvironment } from "@/config/environment"; import { invariant_ensIndexerPublicConfigVersionInfo } from "@/config/validations"; import { ensDbClient } from "@/lib/ensdb/singleton"; @@ -48,7 +48,14 @@ const EnsApiConfigSchema = z ensIndexerPublicConfig: makeENSIndexerPublicConfigSchema("ensIndexerPublicConfig"), customReferralProgramEditionConfigSetUrl: CustomReferralProgramEditionConfigSetUrlSchema, }) - .extend(EnsDbConfigSchema.shape) + // include the validated ENSDb config params + .transform(function includeEnsDbConfig(config) { + return { + ...config, + ensDbUrl: ensDbConfig.ensDbUrl, + ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, + }; + }) .check(invariant_rpcConfigsSpecifiedForRootChain) .check(invariant_ensIndexerPublicConfigVersionInfo); diff --git a/apps/ensapi/src/config/ensdb-config.schema.ts b/apps/ensapi/src/config/ensdb-config.schema.ts deleted file mode 100644 index 8dbfdcc854..0000000000 --- a/apps/ensapi/src/config/ensdb-config.schema.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { parse as parseConnectionString } from "pg-connection-string"; -import { prettifyError, z } from "zod/v4"; - -import type { EnsApiEnvironment } from "@/config/environment"; -import logger from "@/lib/logger"; - -export const EnsDbUrlSchema = z.string().refine( - (url) => { - try { - if (!url.startsWith("postgresql://") && !url.startsWith("postgres://")) { - return false; - } - const config = parseConnectionString(url); - return !!(config.host && config.port && config.database); - } catch { - return false; - } - }, - { - error: - "Invalid PostgreSQL connection string for ENSDb. Expected format: postgresql://username:password@host:port/database", - }, -); - -const EnsIndexerSchemaNameSchema = z - .string({ - error: "ENSINDEXER_SCHEMA_NAME is required.", - }) - .trim() - .min(1, { - error: "ENSINDEXER_SCHEMA_NAME is required and cannot be an empty string.", - }); - -export const EnsDbConfigSchema = z.object({ - ensDbUrl: EnsDbUrlSchema, - ensIndexerSchemaName: EnsIndexerSchemaNameSchema, -}); - -export type EnsDbConfig = z.infer; - -/** - * Build ENSDb config from environment variables. - * - * Exits the process if the configuration is invalid, logging the error details. - */ -export function buildEnsDbConfigFromEnvironment(env: EnsApiEnvironment): EnsDbConfig { - const ensDbConfig = EnsDbConfigSchema.safeParse({ - ensDbUrl: env.ENSDB_URL, - ensIndexerSchemaName: env.ENSINDEXER_SCHEMA_NAME, - }); - - if (!ensDbConfig.success) { - logger.error( - `Failed to parse ENSDb configuration from environment: \n${prettifyError(ensDbConfig.error)}\n`, - ); - process.exit(1); - } - - return ensDbConfig.data; -} diff --git a/apps/ensapi/src/config/ensdb-config.ts b/apps/ensapi/src/config/ensdb-config.ts index 12863f8a8a..60b11a0547 100644 --- a/apps/ensapi/src/config/ensdb-config.ts +++ b/apps/ensapi/src/config/ensdb-config.ts @@ -1,3 +1,31 @@ -import { buildEnsDbConfigFromEnvironment } from "./ensdb-config.schema"; +import { type EnsDbConfig, validateEnsDbConfig } from "@ensnode/ensdb-sdk"; +import type { Unvalidated } from "@ensnode/ensnode-sdk"; -export default buildEnsDbConfigFromEnvironment(process.env); +import { lazyProxy } from "@/lib/lazy"; +import logger from "@/lib/logger"; + +/** + * Build ENSDb config from environment variables for ENSApi app. + * + * Exits the process if the configuration is invalid, logging the error details. + */ +export function buildEnsDbConfigFromEnvironment(env: NodeJS.ProcessEnv): EnsDbConfig { + const unvalidatedConfig = { + ensDbUrl: env.ENSDB_URL, + ensIndexerSchemaName: env.ENSINDEXER_SCHEMA_NAME, + } satisfies Unvalidated; + + try { + return validateEnsDbConfig(unvalidatedConfig); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + logger.error(`Failed to validate ENSDb config from environment: ${errorMessage}`); + process.exit(1); + } +} + +// lazyProxy defers construction until first use so that this module can be +// imported without env vars being present (e.g. during OpenAPI generation). +const ensDbConfig = lazyProxy(() => buildEnsDbConfigFromEnvironment(process.env)); + +export default ensDbConfig; diff --git a/apps/ensapi/src/lib/ensdb/singleton.ts b/apps/ensapi/src/lib/ensdb/singleton.ts index 0e392fd53a..e718911e24 100644 --- a/apps/ensapi/src/lib/ensdb/singleton.ts +++ b/apps/ensapi/src/lib/ensdb/singleton.ts @@ -1,6 +1,6 @@ import { EnsDbReader } from "@ensnode/ensdb-sdk"; -import { buildEnsDbConfigFromEnvironment } from "@/config/ensdb-config.schema"; +import { buildEnsDbConfigFromEnvironment } from "@/config/ensdb-config"; import { lazyProxy } from "@/lib/lazy"; // lazyProxy defers construction until first use so that this module can be From 4b9ecabe136927b427d62062d3776f76a9fcf073 Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 10:57:40 +0200 Subject: [PATCH 06/11] Build `EnsDbConfig` for ENSIndexer Defined `buildEnsDbConfigFromEnvironment` function to source ENSDb config from env vars. --- apps/ensindexer/src/config/config.schema.ts | 31 ++++++--------------- apps/ensindexer/src/config/ensdb-config.ts | 24 ++++++++++++++++ 2 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 apps/ensindexer/src/config/ensdb-config.ts diff --git a/apps/ensindexer/src/config/config.schema.ts b/apps/ensindexer/src/config/config.schema.ts index fc3286a9d1..e2f59bfdeb 100644 --- a/apps/ensindexer/src/config/config.schema.ts +++ b/apps/ensindexer/src/config/config.schema.ts @@ -1,11 +1,9 @@ -import { parse as parseConnectionString } from "pg-connection-string"; import { prettifyError, ZodError, z } from "zod/v4"; import { buildBlockNumberRange, PluginName, uniq } from "@ensnode/ensnode-sdk"; import { buildRpcConfigsFromEnv, ENSNamespaceSchema, - EnsIndexerSchemaNameSchema, invariant_isSubgraphCompatibleRequirements, invariant_rpcConfigsSpecifiedForRootChain, makeFullyPinnedLabelSetSchema, @@ -14,6 +12,7 @@ import { } from "@ensnode/ensnode-sdk/internal"; import { DEFAULT_SUBGRAPH_COMPAT } from "@/config/defaults"; +import ensDbConfig from "@/config/ensdb-config"; import type { ENSIndexerEnvironment } from "@/config/environment"; import { applyDefaults, EnvironmentDefaults } from "@/config/environment-defaults"; @@ -27,24 +26,6 @@ import { invariant_validContractConfigs, } from "./validations"; -export const EnsDbUrlSchema = z.string().refine( - (url) => { - try { - if (!url.startsWith("postgresql://") && !url.startsWith("postgres://")) { - return false; - } - const config = parseConnectionString(url); - return !!(config.host && config.port && config.database); - } catch { - return false; - } - }, - { - error: - "Invalid PostgreSQL connection string for ENSDb. Expected format: postgresql://username:password@host:port/database", - }, -); - // parses an env string bool with strict requirement of 'true' or 'false' const makeEnvStringBoolSchema = (envVarKey: string) => z @@ -106,8 +87,6 @@ const IsSubgraphCompatibleSchema = const ENSIndexerConfigSchema = z .object({ - ensDbUrl: EnsDbUrlSchema, - ensIndexerSchemaName: EnsIndexerSchemaNameSchema, rpcConfigs: RpcConfigsSchema, namespace: ENSNamespaceSchema, @@ -117,6 +96,14 @@ const ENSIndexerConfigSchema = z ensRainbowUrl: EnsRainbowUrlSchema, labelSet: LabelSetSchema, }) + // include the validated ENSDb config params + .transform(function includeEnsDbConfig(config) { + return { + ...config, + ensDbUrl: ensDbConfig.ensDbUrl, + ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, + }; + }) /** * Derived configuration * diff --git a/apps/ensindexer/src/config/ensdb-config.ts b/apps/ensindexer/src/config/ensdb-config.ts new file mode 100644 index 0000000000..01f31efb24 --- /dev/null +++ b/apps/ensindexer/src/config/ensdb-config.ts @@ -0,0 +1,24 @@ +import { type EnsDbConfig, validateEnsDbConfig } from "@ensnode/ensdb-sdk"; +import type { Unvalidated } from "@ensnode/ensnode-sdk"; + +/** + * Build ENSDb config from environment variables for ENSIndexer app. + * + * Exits the process if the configuration is invalid, logging the error details. + */ +function buildEnsDbConfigFromEnvironment(env: NodeJS.ProcessEnv): EnsDbConfig { + const unvalidatedConfig = { + ensDbUrl: env.ENSDB_URL, + ensIndexerSchemaName: env.ENSINDEXER_SCHEMA_NAME, + } satisfies Unvalidated; + + try { + return validateEnsDbConfig(unvalidatedConfig); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error(`Failed to validate ENSDb config from environment: ${errorMessage}`); + process.exit(1); + } +} + +export default buildEnsDbConfigFromEnvironment(process.env); From 3e8161ff7c44d4cb9183b2db6cf771365c4da61a Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 10:57:57 +0200 Subject: [PATCH 07/11] Update testing suite --- apps/ensapi/src/config/config.schema.test.ts | 11 +++- apps/ensindexer/src/config/config.test.ts | 33 ++++++++--- .../ensindexer/src/lib/__test__/mockConfig.ts | 56 +++++++++++++++++++ .../src/lib/graphnode-helpers.test.ts | 6 +- apps/ensindexer/src/lib/subgraph/ids.test.ts | 11 +++- 5 files changed, 103 insertions(+), 14 deletions(-) diff --git a/apps/ensapi/src/config/config.schema.test.ts b/apps/ensapi/src/config/config.schema.test.ts index 01fee8abaf..315c34c7fe 100644 --- a/apps/ensapi/src/config/config.schema.test.ts +++ b/apps/ensapi/src/config/config.schema.test.ts @@ -11,6 +11,13 @@ vi.mock("@/lib/ensdb/singleton", () => ({ }, })); +vi.mock("@/config/ensdb-config", () => ({ + default: { + ensDbUrl: "postgresql://user:password@localhost:5432/mydb", + ensIndexerSchemaName: "ensindexer_0", + }, +})); + import { buildConfigFromEnvironment, buildEnsApiPublicConfig } from "@/config/config.schema"; import { ENSApi_DEFAULT_PORT } from "@/config/defaults"; import type { EnsApiEnvironment } from "@/config/environment"; @@ -97,9 +104,7 @@ describe("buildConfigFromEnvironment", () => { mockExit.mockClear(); }); - const TEST_ENV: EnsApiEnvironment = { - ENSDB_URL: BASE_ENV.ENSDB_URL, - }; + const TEST_ENV: EnsApiEnvironment = structuredClone(BASE_ENV); it("logs error and exits when CUSTOM_REFERRAL_PROGRAM_EDITIONS is not a valid URL", async () => { await buildConfigFromEnvironment({ diff --git a/apps/ensindexer/src/config/config.test.ts b/apps/ensindexer/src/config/config.test.ts index 92596a5eb4..3052b4b87b 100644 --- a/apps/ensindexer/src/config/config.test.ts +++ b/apps/ensindexer/src/config/config.test.ts @@ -1,5 +1,28 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +// For config.test.ts, we need a mock that validates env vars without providing defaults, +// because this test file specifically tests validation failures. +vi.mock("@/config/ensdb-config", async () => { + const { validateEnsDbConfig } = + await vi.importActual("@ensnode/ensdb-sdk"); + return { + default: { + get ensDbUrl() { + const url = process.env.ENSDB_URL; + const schema = process.env.ENSINDEXER_SCHEMA_NAME; + validateEnsDbConfig({ ensDbUrl: url, ensIndexerSchemaName: schema }); + return url; + }, + get ensIndexerSchemaName() { + const url = process.env.ENSDB_URL; + const schema = process.env.ENSINDEXER_SCHEMA_NAME; + validateEnsDbConfig({ ensDbUrl: url, ensIndexerSchemaName: schema }); + return schema; + }, + }, + }; +}); + import { type ENSNamespaceId, ensTestEnvChain, @@ -170,21 +193,17 @@ describe("config (with base env)", () => { it("throws an error when ENSINDEXER_SCHEMA_NAME is not set", async () => { vi.stubEnv("ENSINDEXER_SCHEMA_NAME", undefined); - await expect(getConfig()).rejects.toThrow(/ENSINDEXER_SCHEMA_NAME is required/); + await expect(getConfig()).rejects.toThrow(/ENSIndexer Schema Name is required/); }); it("throws an error when ENSINDEXER_SCHEMA_NAME is empty", async () => { vi.stubEnv("ENSINDEXER_SCHEMA_NAME", ""); - await expect(getConfig()).rejects.toThrow( - /ENSINDEXER_SCHEMA_NAME is required and cannot be an empty string/, - ); + await expect(getConfig()).rejects.toThrow(/ENSIndexer Schema Name cannot be an empty string/); }); it("throws an error when ENSINDEXER_SCHEMA_NAME is only whitespace", async () => { vi.stubEnv("ENSINDEXER_SCHEMA_NAME", " "); - await expect(getConfig()).rejects.toThrow( - /ENSINDEXER_SCHEMA_NAME is required and cannot be an empty string/, - ); + await expect(getConfig()).rejects.toThrow(/ENSIndexer Schema Name cannot be an empty string/); }); }); diff --git a/apps/ensindexer/src/lib/__test__/mockConfig.ts b/apps/ensindexer/src/lib/__test__/mockConfig.ts index 87b0a11cd6..1c51471600 100644 --- a/apps/ensindexer/src/lib/__test__/mockConfig.ts +++ b/apps/ensindexer/src/lib/__test__/mockConfig.ts @@ -35,6 +35,62 @@ export function resetMockConfig() { // This ensures it's defined before setupConfigMock or any tests run. resetMockConfig(); +/** + * Sets up mocking for the ensdb-config module. + * Call this function at the very top of the test file before any imports + * that depend on the config (including config.schema). + * + * This mock uses the actual validation logic to provide proper error messages + * but throws instead of calling process.exit(1). + * + * When env vars are undefined (not set), provides default test values. + * When env vars are explicitly set (even to empty/invalid values), uses those values. + * Tests can override values using vi.stubEnv() before accessing config. + * + * @example + * // At the top of the test file (before all other imports) + * import { setupEnsDbConfigMock } from '@/lib/__test__/mockConfig'; + * setupEnsDbConfigMock(); + * + * // Now we can safely import modules that depend on ensdb-config + * import { buildConfigFromEnvironment } from '@/config/config.schema'; + */ +export function setupEnsDbConfigMock() { + vi.mock("@/config/ensdb-config", async () => { + const { validateEnsDbConfig } = + await vi.importActual("@ensnode/ensdb-sdk"); + + // Default test values when env vars are not explicitly set + const defaultEnsDbUrl = "postgresql://postgres:postgres@localhost:5432/postgres"; + const defaultEnsIndexerSchemaName = "ensindexer_0"; + + return { + default: { + get ensDbUrl() { + // Only use default if env var is undefined (not set) + // If explicitly set (even to empty string), use that value for validation + const url = process.env.ENSDB_URL === undefined ? defaultEnsDbUrl : process.env.ENSDB_URL; + const schema = + process.env.ENSINDEXER_SCHEMA_NAME === undefined + ? defaultEnsIndexerSchemaName + : process.env.ENSINDEXER_SCHEMA_NAME; + validateEnsDbConfig({ ensDbUrl: url, ensIndexerSchemaName: schema }); + return url; + }, + get ensIndexerSchemaName() { + const url = process.env.ENSDB_URL === undefined ? defaultEnsDbUrl : process.env.ENSDB_URL; + const schema = + process.env.ENSINDEXER_SCHEMA_NAME === undefined + ? defaultEnsIndexerSchemaName + : process.env.ENSINDEXER_SCHEMA_NAME; + validateEnsDbConfig({ ensDbUrl: url, ensIndexerSchemaName: schema }); + return schema; + }, + }, + }; + }); +} + /** * Sets up mocking for app-config module * Call this function at the top of the test file before any imports diff --git a/apps/ensindexer/src/lib/graphnode-helpers.test.ts b/apps/ensindexer/src/lib/graphnode-helpers.test.ts index aeb4af1e33..e0bf8bafe9 100644 --- a/apps/ensindexer/src/lib/graphnode-helpers.test.ts +++ b/apps/ensindexer/src/lib/graphnode-helpers.test.ts @@ -2,9 +2,11 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { LabelHash } from "@ensnode/ensnode-sdk"; -import { setupConfigMock } from "@/lib/__test__/mockConfig"; +import { setupConfigMock, setupEnsDbConfigMock } from "@/lib/__test__/mockConfig"; -setupConfigMock(); // setup config mock before importing dependent modules +// Setup mocks before any imports that depend on them +setupEnsDbConfigMock(); +setupConfigMock(); // Use real p-retry logic but with 0 timeouts so tests don't incur actual backoff delays. vi.mock("p-retry", async () => { diff --git a/apps/ensindexer/src/lib/subgraph/ids.test.ts b/apps/ensindexer/src/lib/subgraph/ids.test.ts index c1a07e94be..f3765e4f81 100644 --- a/apps/ensindexer/src/lib/subgraph/ids.test.ts +++ b/apps/ensindexer/src/lib/subgraph/ids.test.ts @@ -1,8 +1,15 @@ import { beforeEach, describe, expect, it } from "vitest"; -import { resetMockConfig, setupConfigMock, updateMockConfig } from "@/lib/__test__/mockConfig"; +import { + resetMockConfig, + setupConfigMock, + setupEnsDbConfigMock, + updateMockConfig, +} from "@/lib/__test__/mockConfig"; -setupConfigMock(); // setup config mock before importing dependent modules +// Setup mocks before any imports that depend on them +setupEnsDbConfigMock(); +setupConfigMock(); import { labelhash, namehash, zeroAddress } from "viem"; From 7aa8ce499e064eab47df662a61679e3e46701d0f Mon Sep 17 00:00:00 2001 From: Tomasz Kopacki Date: Fri, 3 Apr 2026 11:19:44 +0200 Subject: [PATCH 08/11] Apply AI PR feedback --- apps/ensindexer/src/config/types.ts | 12 ++++------- packages/ensdb-sdk/src/client/ensdb-config.ts | 8 +++++++- .../src/ensindexer/config/zod-schemas.test.ts | 20 ------------------- .../ensnode-sdk/src/shared/config/types.ts | 12 ++--------- .../src/shared/config/zod-schemas.ts | 9 --------- 5 files changed, 13 insertions(+), 48 deletions(-) diff --git a/apps/ensindexer/src/config/types.ts b/apps/ensindexer/src/config/types.ts index fc70c9b182..b998579305 100644 --- a/apps/ensindexer/src/config/types.ts +++ b/apps/ensindexer/src/config/types.ts @@ -1,11 +1,7 @@ import type { ENSNamespaceId } from "@ensnode/datasources"; +import type { EnsDbConfig } from "@ensnode/ensdb-sdk"; import type { BlockNumberRange, ChainId, PluginName } from "@ensnode/ensnode-sdk"; -import { - type DatabaseUrl, - type EnsIndexerSchemaName, - RpcConfig, - type RpcConfigs, -} from "@ensnode/ensnode-sdk/internal"; +import { RpcConfig, type RpcConfigs } from "@ensnode/ensnode-sdk/internal"; import type { EnsRainbowClientLabelSet } from "@ensnode/ensrainbow-sdk"; /** @@ -81,7 +77,7 @@ export interface EnsIndexerConfig { * Invariants: * - Must be a non-empty string that is a valid Postgres database schema identifier. */ - ensIndexerSchemaName: EnsIndexerSchemaName; + ensIndexerSchemaName: EnsDbConfig["ensIndexerSchemaName"]; /** * A set of {@link PluginName}s indicating which plugins to activate. @@ -119,7 +115,7 @@ export interface EnsIndexerConfig { * Invariants: * - The URL must be a valid PostgreSQL connection string */ - ensDbUrl: DatabaseUrl; + ensDbUrl: EnsDbConfig["ensDbUrl"]; /** * Constrains the global blockrange for indexing, useful for testing purposes. diff --git a/packages/ensdb-sdk/src/client/ensdb-config.ts b/packages/ensdb-sdk/src/client/ensdb-config.ts index a5fede78b6..148837e6f4 100644 --- a/packages/ensdb-sdk/src/client/ensdb-config.ts +++ b/packages/ensdb-sdk/src/client/ensdb-config.ts @@ -4,12 +4,18 @@ export interface EnsDbConfig { /** * PostgreSQL connection string for ENSDb. - * Expected format: postgresql://username:password@host:port/database + * + * Guaranteed to be a valid PostgreSQL connection string with the format: + * `postgresql://username:password@host:port/database` or + * `postgres://username:password@host:port/database` */ ensDbUrl: string; /** * The name of the ENSIndexer Schema in the ENSDb instance. + * + * Guaranteed to be a non-empty string that is + * a valid Postgres database schema identifier */ ensIndexerSchemaName: string; } diff --git a/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts index 97e057544e..3d24cfa9eb 100644 --- a/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/config/zod-schemas.test.ts @@ -6,7 +6,6 @@ import type { SerializedEnsIndexerPublicConfig } from "./serialized-types"; import { type EnsIndexerVersionInfo, PluginName } from "./types"; import { makeEnsIndexerPublicConfigSchema, - makeEnsIndexerSchemaNameSchema, makeEnsIndexerVersionInfoSchema, makeFullyPinnedLabelSetSchema, makeIndexedChainIdsSchema, @@ -20,16 +19,6 @@ describe("ENSIndexer: Config", () => { prettifyError(zodParseError.error!); describe("Parsing", () => { - it("can parse database schema name values", () => { - expect(makeEnsIndexerSchemaNameSchema().parse("public")).toBe("public"); - expect(makeEnsIndexerSchemaNameSchema().parse("the_schema")).toBe("the_schema"); - expect(makeEnsIndexerSchemaNameSchema().parse("theSchema")).toBe("theSchema"); - - expect(formatParseError(makeEnsIndexerSchemaNameSchema().safeParse(1))).toContain( - "ENS Indexer Schema Name must be a string", - ); - }); - it("can parse a list of plugin name values", () => { expect( makePluginsListSchema().parse([ @@ -262,15 +251,6 @@ describe("ENSIndexer: Config", () => { describe("Useful error messages", () => { it("can apply custom value labels", () => { - expect( - formatParseError(makeEnsIndexerSchemaNameSchema("ensIndexerSchemaName").safeParse("")), - ).toContain("ensIndexerSchemaName is required and must be a non-empty string."); - expect( - formatParseError( - makeEnsIndexerSchemaNameSchema("ENSINDEXER_SCHEMA_NAME env var").safeParse(""), - ), - ).toContain("ENSINDEXER_SCHEMA_NAME env var is required and must be a non-empty string."); - expect( formatParseError( makePluginsListSchema("PLUGINS env var").safeParse([ diff --git a/packages/ensnode-sdk/src/shared/config/types.ts b/packages/ensnode-sdk/src/shared/config/types.ts index 0f2ac4770d..e3c295a16a 100644 --- a/packages/ensnode-sdk/src/shared/config/types.ts +++ b/packages/ensnode-sdk/src/shared/config/types.ts @@ -1,11 +1,7 @@ -import type { ChainId, UrlString } from "enssdk"; +import type { ChainId } from "enssdk"; import type { z } from "zod/v4"; -import type { - EnsIndexerSchemaNameSchema, - PortNumberSchema, - TheGraphApiKeySchema, -} from "./zod-schemas"; +import type { PortNumberSchema, TheGraphApiKeySchema } from "./zod-schemas"; /** * RPC configuration for a single chain. @@ -43,10 +39,6 @@ export interface RpcConfig { export type RpcConfigs = Map; -export type DatabaseUrl = UrlString; - -export type EnsIndexerSchemaName = z.infer; - export type TheGraphApiKey = z.infer; export type PortNumber = z.infer; diff --git a/packages/ensnode-sdk/src/shared/config/zod-schemas.ts b/packages/ensnode-sdk/src/shared/config/zod-schemas.ts index 272be05c1a..df193019b2 100644 --- a/packages/ensnode-sdk/src/shared/config/zod-schemas.ts +++ b/packages/ensnode-sdk/src/shared/config/zod-schemas.ts @@ -12,15 +12,6 @@ import { invariant_rpcEndpointConfigIncludesAtMostOneWebSocketsProtocolURL, } from "./validatons"; -export const EnsIndexerSchemaNameSchema = z - .string({ - error: "ENSINDEXER_SCHEMA_NAME is required.", - }) - .trim() - .min(1, { - error: "ENSINDEXER_SCHEMA_NAME is required and cannot be an empty string.", - }); - const RpcConfigSchema = z .string() .transform((val) => val.split(",")) From 7518d898fe02845a8ccdc5383d8cfd3d80ec0735 Mon Sep 17 00:00:00 2001 From: shrugs Date: Fri, 3 Apr 2026 11:48:14 -0500 Subject: [PATCH 09/11] fix: tidy up schema parsing --- apps/ensapi/src/config/config.schema.ts | 18 ++++++++---------- apps/ensindexer/src/config/config.schema.ts | 18 ++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/apps/ensapi/src/config/config.schema.ts b/apps/ensapi/src/config/config.schema.ts index 0e0813d151..318755d093 100644 --- a/apps/ensapi/src/config/config.schema.ts +++ b/apps/ensapi/src/config/config.schema.ts @@ -47,14 +47,10 @@ const EnsApiConfigSchema = z rpcConfigs: RpcConfigsSchema, ensIndexerPublicConfig: makeENSIndexerPublicConfigSchema("ensIndexerPublicConfig"), customReferralProgramEditionConfigSetUrl: CustomReferralProgramEditionConfigSetUrlSchema, - }) - // include the validated ENSDb config params - .transform(function includeEnsDbConfig(config) { - return { - ...config, - ensDbUrl: ensDbConfig.ensDbUrl, - ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, - }; + + // include the ENSDbConfig params in the EnsApiConfigSchema + ensDbUrl: z.string(), + ensIndexerSchemaName: z.string(), }) .check(invariant_rpcConfigsSpecifiedForRootChain) .check(invariant_ensIndexerPublicConfigVersionInfo); @@ -96,13 +92,15 @@ export async function buildConfigFromEnvironment(env: EnsApiEnvironment): Promis return EnsApiConfigSchema.parse({ port: env.PORT, - ensDbUrl: env.ENSDB_URL, theGraphApiKey: env.THEGRAPH_API_KEY, ensIndexerPublicConfig, namespace: ensIndexerPublicConfig.namespace, - ensIndexerSchemaName: ensIndexerPublicConfig.ensIndexerSchemaName, rpcConfigs, customReferralProgramEditionConfigSetUrl: env.CUSTOM_REFERRAL_PROGRAM_EDITIONS, + + // include the validated ENSDb config params in the parsed EnsApiConfig + ensDbUrl: ensDbConfig.ensDbUrl, + ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, }); } catch (error) { if (error instanceof ZodError) { diff --git a/apps/ensindexer/src/config/config.schema.ts b/apps/ensindexer/src/config/config.schema.ts index e2f59bfdeb..2e95934855 100644 --- a/apps/ensindexer/src/config/config.schema.ts +++ b/apps/ensindexer/src/config/config.schema.ts @@ -95,14 +95,10 @@ const ENSIndexerConfigSchema = z globalBlockrange: BlockrangeSchema, ensRainbowUrl: EnsRainbowUrlSchema, labelSet: LabelSetSchema, - }) - // include the validated ENSDb config params - .transform(function includeEnsDbConfig(config) { - return { - ...config, - ensDbUrl: ensDbConfig.ensDbUrl, - ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, - }; + + // include the ENSDbConfig params in the EnsApiConfigSchema + ensDbUrl: z.string(), + ensIndexerSchemaName: z.string(), }) /** * Derived configuration @@ -171,8 +167,6 @@ export function buildConfigFromEnvironment(_env: ENSIndexerEnvironment): EnsInde // parse/validate with ENSIndexerConfigSchema return ENSIndexerConfigSchema.parse({ - ensDbUrl: env.ENSDB_URL, - ensIndexerSchemaName: env.ENSINDEXER_SCHEMA_NAME, namespace: env.NAMESPACE, rpcConfigs, @@ -187,6 +181,10 @@ export function buildConfigFromEnvironment(_env: ENSIndexerEnvironment): EnsInde labelSetId: env.LABEL_SET_ID, labelSetVersion: env.LABEL_SET_VERSION, }, + + // include the validated ENSDb config params in the parsed EnsApiConfig + ensDbUrl: ensDbConfig.ensDbUrl, + ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, }); } catch (error) { if (error instanceof ZodError) { From d10db1fba45d62d1c5de7074c420967d82b6f304 Mon Sep 17 00:00:00 2001 From: shrugs Date: Fri, 3 Apr 2026 11:48:49 -0500 Subject: [PATCH 10/11] nit: 'values' --- apps/ensapi/src/config/config.schema.ts | 2 +- apps/ensindexer/src/config/config.schema.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ensapi/src/config/config.schema.ts b/apps/ensapi/src/config/config.schema.ts index 318755d093..a17df235c1 100644 --- a/apps/ensapi/src/config/config.schema.ts +++ b/apps/ensapi/src/config/config.schema.ts @@ -98,7 +98,7 @@ export async function buildConfigFromEnvironment(env: EnsApiEnvironment): Promis rpcConfigs, customReferralProgramEditionConfigSetUrl: env.CUSTOM_REFERRAL_PROGRAM_EDITIONS, - // include the validated ENSDb config params in the parsed EnsApiConfig + // include the validated ENSDb config values in the parsed EnsApiConfig ensDbUrl: ensDbConfig.ensDbUrl, ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, }); diff --git a/apps/ensindexer/src/config/config.schema.ts b/apps/ensindexer/src/config/config.schema.ts index 2e95934855..977296f12c 100644 --- a/apps/ensindexer/src/config/config.schema.ts +++ b/apps/ensindexer/src/config/config.schema.ts @@ -182,7 +182,7 @@ export function buildConfigFromEnvironment(_env: ENSIndexerEnvironment): EnsInde labelSetVersion: env.LABEL_SET_VERSION, }, - // include the validated ENSDb config params in the parsed EnsApiConfig + // include the validated ENSDb config values in the parsed EnsApiConfig ensDbUrl: ensDbConfig.ensDbUrl, ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, }); From caab8f07e3c86421a626672ed383ad2c655f30f6 Mon Sep 17 00:00:00 2001 From: shrugs Date: Fri, 3 Apr 2026 11:57:31 -0500 Subject: [PATCH 11/11] fix: comments --- apps/ensindexer/src/config/config.schema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ensindexer/src/config/config.schema.ts b/apps/ensindexer/src/config/config.schema.ts index 977296f12c..5e4239cde9 100644 --- a/apps/ensindexer/src/config/config.schema.ts +++ b/apps/ensindexer/src/config/config.schema.ts @@ -96,7 +96,7 @@ const ENSIndexerConfigSchema = z ensRainbowUrl: EnsRainbowUrlSchema, labelSet: LabelSetSchema, - // include the ENSDbConfig params in the EnsApiConfigSchema + // include the ENSDbConfig params in the ENSIndexerConfigSchema ensDbUrl: z.string(), ensIndexerSchemaName: z.string(), }) @@ -182,7 +182,7 @@ export function buildConfigFromEnvironment(_env: ENSIndexerEnvironment): EnsInde labelSetVersion: env.LABEL_SET_VERSION, }, - // include the validated ENSDb config values in the parsed EnsApiConfig + // include the validated ENSDb config values in the parsed EnsIndexerConfig ensDbUrl: ensDbConfig.ensDbUrl, ensIndexerSchemaName: ensDbConfig.ensIndexerSchemaName, });