Skip to content

Fix ENSDb SDK builder for "concrete" ENSIndexer Schema#1832

Merged
tk-o merged 9 commits intomainfrom
fix/ensdb-builder-for-concrete-ensindexer-schema
Mar 26, 2026
Merged

Fix ENSDb SDK builder for "concrete" ENSIndexer Schema#1832
tk-o merged 9 commits intomainfrom
fix/ensdb-builder-for-concrete-ensindexer-schema

Conversation

@tk-o
Copy link
Copy Markdown
Contributor

@tk-o tk-o commented Mar 26, 2026

Lite PR

Tip: Review docs on the ENSNode PR process

Summary

  • Currently, we have to modify the "abstract" ENSIndexer Schema object each time we build a new "concrete" ENSIndexer Schema.

Why

  • The "concrete" ENSIndexer Schema objects are incomplete, as shown by the integration testing suite.

Testing

  • Ran integration testing suite successfully.
  • Ran updated unit testing suite successfully.

Notes for Reviewer (Optional)


Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

tk-o added 4 commits March 26, 2026 07:54
…tests

Previously, we were using `public` value, which masked the issues with the "concrete" ENSIndexer Schema structure.
Our way for "cloning" the `abstractEnsIndexerSchema` object was incorrect, and the produced "concrete" schema object were incomplete. This was made obvious by integration testing suite errors, e.g. https://github.com/namehash/ensnode/actions/runs/23557404679/job/68587898403\#step:5:6533
The `ensNodeMetadata` schema has been fully migrated to ENSNode Schema
@tk-o tk-o requested a review from a team as a code owner March 26, 2026 07:23
Copilot AI review requested due to automatic review settings March 26, 2026 07:23
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 26, 2026

🦋 Changeset detected

Latest commit: a9a08f4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
@ensnode/ensdb-sdk Major
ensapi Major
ensindexer Major
@ensnode/integration-test-env Patch
ensadmin Major
ensrainbow Major
fallback-ensapi Major
@ensnode/datasources Major
@ensnode/ensrainbow-sdk Major
@ensnode/ensnode-react Major
@ensnode/ensnode-sdk Major
@ensnode/ponder-sdk Major
@ensnode/ponder-subgraph Major
@ensnode/shared-configs Major
@docs/ensnode Major
@docs/ensrainbow Major
@docs/mintlify Major
@namehash/ens-referrals Major
@namehash/namehash-ui Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Mar 26, 2026 9:44am
ensnode.io Ready Ready Preview, Comment Mar 26, 2026 9:44am
ensrainbow.io Ready Ready Preview, Comment Mar 26, 2026 9:44am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Removed the ensNodeMetadata table and its re-export; switched schema-building to mutate Drizzle objects in-place (no cloning); simplified tests to expect single application of a concrete schema; updated integration orchestrator polling with resilient snapshot handling, deadline-based timeout, deterministic client shutdown, and set ENSINDEXER_SCHEMA_NAME to ensindexer_0.

Changes

Cohort / File(s) Summary
Schema Removal
packages/ensdb-sdk/src/ensindexer-abstract/ensnode-metadata.schema.ts, packages/ensdb-sdk/src/ensindexer-abstract/index.ts
Deleted the ensNodeMetadata table definition and removed its barrel-file re-export.
Schema Builder (Drizzle)
packages/ensdb-sdk/src/lib/drizzle.ts
Removed table/enum cloning helpers; buildConcreteEnsIndexerSchema now mutates existing Drizzle Table and enum objects by assigning schema properties and guards against reapplication with a different name.
Tests
packages/ensdb-sdk/src/lib/drizzle.test.ts
Replaced previous immutability/dual-schema tests with a single test asserting an error when buildIndividualEnsDbSchemas is called twice with different names.
Integration Orchestrator
packages/integration-test-env/src/orchestrator.ts
Refactored pollIndexingStatus to use EnsDbReader instance, tolerate transient snapshot errors within loop, enforce deadline timeout, always attempt client shutdown, and set ENSINDEXER_SCHEMA_NAME to ensindexer_0.
Metadata / Release
.changeset/chatty-monkeys-run.md
Added a changeset (minor bump) documenting the hotfix that switches schema building from cloning to mutation.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Orchestrator
participant EnsDbReader
participant Indexer
participant Database
Orchestrator->>EnsDbReader: instantiate reader
loop until deadline
Orchestrator->>EnsDbReader: request latest snapshot/status
alt snapshot successful
EnsDbReader->>Indexer: provide snapshot/status
Indexer->>Database: query indexing state
Database-->>Indexer: return indexing snapshot
Indexer-->>Orchestrator: ready = true
break
else snapshot error
EnsDbReader-->>Orchestrator: transient error (tolerate), continue loop
end
end
Orchestrator->>EnsDbReader: shutdown (ensDb.$client.end())
EnsDbReader-->>Orchestrator: closed

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nudged the tables, soft and sly,
Where clones once lived, mutations lie.
A timeout set, a reader curled,
ensindexer_0 bounds the world,
I hop away with schema pie.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main change: fixing the ENSDb SDK builder for creating concrete ENSIndexer Schema objects without modifying the abstract schema.
Description check ✅ Passed The PR description follows the required template structure with all key sections completed: Summary, Why, Testing, Notes for Reviewer, and Pre-Review Checklist. All required information is present and substantive.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ensdb-builder-for-concrete-ensindexer-schema

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tk-o tk-o changed the title Fix/ensdb builder for concrete ensindexer schema Fix ENSDb SDK builder for "concrete" ENSIndexer Schema Mar 26, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR fixes the integration test suite by correcting two root causes: the wrong schema name ("public""ensindexer_0") in the orchestrator, and an incomplete concrete schema build that was missing entries because the previous cloning approach was broken in practice.

The simplification in buildConcreteEnsIndexerSchema trades correctness (proper per-instance isolation) for immediate pragmatism — instead of cloning tables/enums for each schema name, it now directly mutates the shared abstractEnsIndexerSchema module singleton. This is explicitly acknowledged with a skipped test, a TODO comment, and a tracking issue (#1830).

Key changes:

  • drizzle.ts: Replaces the cloneTableWithSchema / enum-clone approach with direct in-place mutation of the shared abstract schema objects, returning the same reference cast as ConcreteEnsIndexerSchema.
  • drizzle.test.ts: Skips the multi-schema isolation test; prototype-preservation tests are now vacuously true since concrete === abstract after the refactor.
  • ensnode-metadata.schema.ts (deleted): Removes the ensNodeMetadata table from the abstract ENSIndexer schema (the metadata table lives correctly in the separate ensnode schema); the TODO on this deletion is now fulfilled.
  • orchestrator.ts: Corrects the integration test schema name from "public" to "ensindexer_0", matching the DATABASE_SCHEMA env var passed to the ENSIndexer service.

Confidence Score: 4/5

Safe to merge for the current single-ENSIndexer-instance deployment; the mutation bug is acknowledged, tracked, and does not affect production behavior today.

Integration and unit tests pass. The core defect (mutation of the abstract schema singleton) is a known, intentional trade-off documented with a TODO, a skipped test, and issue #1830 — not a surprise regression. The only real risk is that issue #1830 is resolved slowly and a future caller inadvertently builds two concurrent schemas, hitting the silent corruption path. The prototype-preservation tests are now vacuously true and no longer provide safety net coverage, but that is a test-quality concern rather than a blocking bug.

packages/ensdb-sdk/src/lib/drizzle.ts — the mutation approach requires issue #1830 to be resolved before multi-instance support can be added safely.

Important Files Changed

Filename Overview
packages/ensdb-sdk/src/lib/drizzle.ts Replaces table/enum cloning with direct in-place mutation of the shared abstract schema module; works for a single concurrent schema but silently corrupts all previously-built schemas if called more than once with different names.
packages/ensdb-sdk/src/lib/drizzle.test.ts Skips the multi-schema isolation test (correctly acknowledging the mutation limitation) but prototype-preservation tests are now vacuously true since concrete === abstract after the refactor.
packages/ensdb-sdk/src/ensindexer-abstract/ensnode-metadata.schema.ts Deleted as planned (TODO comment was present in the index); no remaining internal imports use this file — the metadata table lives in the separate ensnode schema.
packages/ensdb-sdk/src/ensindexer-abstract/index.ts Removes the now-deleted ensnode-metadata.schema export; clean and correct.
packages/integration-test-env/src/orchestrator.ts Fixes the integration test schema name from "public" to "ensindexer_0", aligning with the actual ENSIndexer schema naming convention and the DATABASE_SCHEMA env var passed to the service.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant buildConcreteEnsIndexerSchema
    participant abstractEnsIndexerSchema as abstractEnsIndexerSchema<br/>(ES module singleton)
    participant DrizzleClient

    Note over abstractEnsIndexerSchema: Tables/enums with no schema name set

    Caller->>buildConcreteEnsIndexerSchema: buildIndividualEnsDbSchemas("ensindexer_0")
    buildConcreteEnsIndexerSchema->>abstractEnsIndexerSchema: mutate table[Table.Symbol.Schema] = "ensindexer_0"
    buildConcreteEnsIndexerSchema->>abstractEnsIndexerSchema: mutate enum.schema = "ensindexer_0"
    buildConcreteEnsIndexerSchema-->>Caller: return abstractEnsIndexerSchema (same reference, aliased)

    Caller->>DrizzleClient: buildEnsDbDrizzleClient(connStr, concreteSchema)
    DrizzleClient-->>Caller: Drizzle client targeting "ensindexer_0" ✅

    Note over Caller,abstractEnsIndexerSchema: ⚠️ If called again with a different name (issue #1830):
    Caller->>buildConcreteEnsIndexerSchema: buildIndividualEnsDbSchemas("ensindexer_1")
    buildConcreteEnsIndexerSchema->>abstractEnsIndexerSchema: overwrite table[Table.Symbol.Schema] = "ensindexer_1"
    Note over DrizzleClient: First client's tables now silently point to "ensindexer_1" ❌
Loading

Comments Outside Diff (1)

  1. packages/ensdb-sdk/src/lib/drizzle.test.ts, line 186-211 (link)

    P2 Prototype-preservation tests are now vacuously true

    Since buildConcreteEnsIndexerSchema now returns abstractEnsIndexerSchema as ConcreteEnsIndexerSchema (the same module reference, mutated in place), concreteEnsIndexerSchema.v1Domain === abstractEnsIndexerSchema.v1Domain is always true. As a result:

    • Object.getPrototypeOf(concreteTable) is literally Object.getPrototypeOf(abstractTable) — the same call on the same object.
    • Symbol-keyed property comparisons like concreteTable[IsDrizzleTable] === abstractTable[IsDrizzleTable] compare a property to itself.
    • isTable(concreteEnsIndexerSchema.v1Domain) is guaranteed to return the same value as isTable(abstractEnsIndexerSchema.v1Domain).

    These tests will continue to pass for any implementation, including a completely broken one that returns a random empty object. They no longer provide any safety net against regressions in the cloning logic. Once issue Avoid mutating the "abstract" ENSIndexer Schema definition. #1830 is resolved and proper cloning is restored, these tests should be revisited to assert that prototype and symbol properties on the clone are equal to (not identical with) those on the original.

Reviews (1): Last reviewed commit: "Temporarily mark relevant test cases as ..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adjusts ENSDb schema wiring so the “concrete” ENSIndexer Drizzle schema is derived in a way that aligns with integration expectations, and removes an obsolete ENSIndexer-abstract metadata schema export.

Changes:

  • Update integration test orchestrator to use a non-public ENSIndexer schema name (ensindexer_0).
  • Change buildConcreteEnsIndexerSchema implementation to set schema names by mutating the imported abstract schema objects (and skip the test that asserts non-mutation).
  • Remove ensnode-metadata.schema from ensindexer-abstract exports and delete the file.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/integration-test-env/src/orchestrator.ts Uses ensindexer_0 as the schema name for the integration environment.
packages/ensdb-sdk/src/lib/drizzle.ts Alters concrete schema building logic to mutate shared table/enum objects.
packages/ensdb-sdk/src/lib/drizzle.test.ts Skips the test that previously ensured the abstract schema was not mutated.
packages/ensdb-sdk/src/ensindexer-abstract/index.ts Removes export of the ENSNode metadata schema from the ENSIndexer abstract bundle.
packages/ensdb-sdk/src/ensindexer-abstract/ensnode-metadata.schema.ts Deletes the legacy metadata schema definition.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ensdb-sdk/src/lib/drizzle.ts`:
- Around line 39-59: The code currently aliases abstractEnsIndexerSchema to
concreteEnsIndexerSchema and mutates the shared module objects; instead, create
a fresh concrete schema object per call by cloning each entry from
abstractEnsIndexerSchema (copying table and enum objects rather than referencing
them) and then set the schema name on the clones (use Table.Symbol.Schema for
tables and .schema for enums) before returning the new ConcreteEnsIndexerSchema;
update the loop that uses isTable and isPgEnum to operate on the cloned objects
so callers can safely request multiple different ensIndexerSchemaName values
without mutating the module singleton.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6db3da44-b5a7-43f5-a899-b7af6abd3158

📥 Commits

Reviewing files that changed from the base of the PR and between 756cce0 and e9d496a.

📒 Files selected for processing (5)
  • packages/ensdb-sdk/src/ensindexer-abstract/ensnode-metadata.schema.ts
  • packages/ensdb-sdk/src/ensindexer-abstract/index.ts
  • packages/ensdb-sdk/src/lib/drizzle.test.ts
  • packages/ensdb-sdk/src/lib/drizzle.ts
  • packages/integration-test-env/src/orchestrator.ts
💤 Files with no reviewable changes (2)
  • packages/ensdb-sdk/src/ensindexer-abstract/index.ts
  • packages/ensdb-sdk/src/ensindexer-abstract/ensnode-metadata.schema.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/integration-test-env/src/orchestrator.ts`:
- Around line 200-226: The polling loop swallows all errors from
ensDbClient.getIndexingStatusSnapshot(), losing the real failure and allowing
the finally block to overwrite it when closing the client; modify the logic in
the try/catch around getIndexingStatusSnapshot() to capture and store the last
non-retry error (e.g., in a variable `lastPollingError`) instead of always
ignoring it, continue retrying only for transient conditions, and after the
timeout throw the original `lastPollingError` (or include it as the cause)
rather than a generic timeout; in the finally block, ensure you log any errors
from `ensDbClient.ensDb.$client.end()` instead of rethrowing or replacing the
primary error, and remove the now-unused `util.inspect` import.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dab08587-5dd3-4e5e-8c1e-9d4e13f87a26

📥 Commits

Reviewing files that changed from the base of the PR and between e9d496a and 876f71d.

📒 Files selected for processing (1)
  • packages/integration-test-env/src/orchestrator.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/chatty-monkeys-run.md:
- Line 5: Update the changeset note to mention that the fix for
buildConcreteEnsIndexerSchema uses an interim mutation approach rather than
cloning; explicitly state this is a known limitation and reference the planned
refactor tracked by issue `#1830` and the in-code TODO so readers know mutation is
temporary and will be addressed later.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ba7fa6b9-d578-4f02-8127-a88f33f8301c

📥 Commits

Reviewing files that changed from the base of the PR and between 876f71d and 713a230.

📒 Files selected for processing (1)
  • .changeset/chatty-monkeys-run.md

tk-o added 2 commits March 26, 2026 10:09
Ensure ENSDb connection is closed before testing suite completes
…placing the incomplete cloning approach with working mutation approach.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
packages/integration-test-env/src/orchestrator.ts (1)

198-225: ⚠️ Potential issue | 🟠 Major

Preserve polling root cause and prevent cleanup from masking failures.

Line 214 currently suppresses all DB polling errors, and Line 224 can replace the primary failure if end() throws. This makes timeout/debugging noisy and can hide real regressions.

Suggested fix
   const start = Date.now();
   log("Polling indexing status...");
+  let lastPollError: unknown;

   try {
     while (Date.now() - start < timeoutMs) {
       checkAborted();
       try {
         const snapshot = await ensDbClient.getIndexingStatusSnapshot();
+        lastPollError = undefined;
         if (snapshot !== undefined) {
           const omnichainStatus = snapshot.omnichainSnapshot.omnichainStatus;
           log(`Omnichain status: ${omnichainStatus}`);
           if (
             omnichainStatus === OmnichainIndexingStatusIds.Following ||
             omnichainStatus === OmnichainIndexingStatusIds.Completed
           ) {
             log("Indexing reached target status");
             return;
           }
         }
-      } catch {
+      } catch (error) {
         // indexer may not be ready yet
+        lastPollError = error;
       }
       await new Promise((r) => setTimeout(r, 3000));
     }
-    throw new Error(`Indexing did not complete within ${timeoutMs / 1000}s`);
+    throw new Error(
+      `Indexing did not complete within ${timeoutMs / 1000}s`,
+      { cause: lastPollError },
+    );
   } finally {
-    console.log("Closing ENSDb client...");
-    // `@ts-expect-error` - DrizzleClient.$client is not typed to have an `end` method,
-    // but in practice it does (e.g. pg's Client does).
-    await ensDbClient.ensDb.$client.end();
-    console.log("ENSDb client closed");
+    log("Closing ENSDb client...");
+    try {
+      // `@ts-expect-error` - DrizzleClient.$client is not typed to have an `end` method,
+      // but in practice it does (e.g. pg's Client does).
+      await ensDbClient.ensDb.$client.end();
+      log("ENSDb client closed");
+    } catch (error) {
+      logError(
+        `Failed to close ENSDb client: ${
+          error instanceof Error ? error.message : String(error)
+        }`,
+      );
+    }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/integration-test-env/src/orchestrator.ts` around lines 198 - 225,
The polling code currently swallows all getIndexingStatusSnapshot() errors and
the finally cleanup can mask the primary failure if
ensDbClient.ensDb.$client.end() throws; fix by capturing the first (or last)
polling error into a variable (e.g. lastPollingError) inside the inner catch
around ensDbClient.getIndexingStatusSnapshot(), continue polling as before, and
when timing out throw an Error that includes lastPollingError as the cause or
appended message; also wrap the cleanup call await
ensDbClient.ensDb.$client.end() in its own try/catch and if it throws, log that
cleanup error but rethrow the original timeout/polling error (or the end error
only if there was no prior primary error) so cleanup cannot replace the root
cause.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/integration-test-env/src/orchestrator.ts`:
- Around line 198-225: The polling code currently swallows all
getIndexingStatusSnapshot() errors and the finally cleanup can mask the primary
failure if ensDbClient.ensDb.$client.end() throws; fix by capturing the first
(or last) polling error into a variable (e.g. lastPollingError) inside the inner
catch around ensDbClient.getIndexingStatusSnapshot(), continue polling as
before, and when timing out throw an Error that includes lastPollingError as the
cause or appended message; also wrap the cleanup call await
ensDbClient.ensDb.$client.end() in its own try/catch and if it throws, log that
cleanup error but rethrow the original timeout/polling error (or the end error
only if there was no prior primary error) so cleanup cannot replace the root
cause.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3126e046-216c-436b-b1ec-3ff306cd895d

📥 Commits

Reviewing files that changed from the base of the PR and between 713a230 and b45dea9.

📒 Files selected for processing (2)
  • .changeset/chatty-monkeys-run.md
  • packages/integration-test-env/src/orchestrator.ts

Copy link
Copy Markdown
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Thanks for this update! Shared a few suggestions. Please feel welcome to merge when ready 👍

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +222 to +225
// @ts-expect-error - DrizzleClient.$client is not typed to have an `end` method,
// but in practice it does (e.g. pg's Client does).
await ensDbClient.ensDb.$client.end();
console.log("ENSDb client closed");
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The cleanup logic reaches into Drizzle internals (ensDbClient.ensDb.$client.end()) and suppresses typing with @ts-expect-error. This is brittle (a Drizzle type update that adds end will break the build because @ts-expect-error expects an error, and a driver change could make $client.end absent/different). Prefer a small explicit shutdown helper (e.g. a close() method on EnsDbReader) or a runtime/typed guard that checks for an end() function before calling it, without @ts-expect-error.

Suggested change
// @ts-expect-error - DrizzleClient.$client is not typed to have an `end` method,
// but in practice it does (e.g. pg's Client does).
await ensDbClient.ensDb.$client.end();
console.log("ENSDb client closed");
const anyClient = ensDbClient as any;
try {
const rawEnsDb = anyClient?.ensDb;
const rawUnderlyingClient = rawEnsDb?.$client;
const endFn = rawUnderlyingClient?.end;
if (typeof endFn === "function") {
await endFn.call(rawUnderlyingClient);
console.log("ENSDb client closed");
} else {
console.log("ENSDb client has no 'end' method; skipping close");
}
} catch (closeErr) {
console.log("Failed to close ENSDb client cleanly:", closeErr);
}

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +60
if (
appliedNameForConcreteEnsIndexerSchema !== undefined &&
appliedNameForConcreteEnsIndexerSchema !== ensIndexerSchemaName
) {
throw new Error(
`buildConcreteEnsIndexerSchema was already called with schema "${appliedNameForConcreteEnsIndexerSchema}". ` +
`Calling it again with "${ensIndexerSchemaName}" would corrupt the previously built schema.`,
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

This adds module-level state (appliedNameForConcreteEnsIndexerSchema) so schema-building becomes a process-wide singleton: after the first call, building for a different ensIndexerSchemaName throws. Since callers reach this via exported buildIndividualEnsDbSchemas/EnsDbReader, this constraint should be documented on the public API (and/or in the changeset) to avoid surprising runtime failures in multi-tenant or multi-db scenarios.

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ensdb-sdk/src/lib/drizzle.test.ts (1)

137-167: 🧹 Nitpick | 🔵 Trivial

Test descriptions reference "cloned tables" but implementation no longer clones.

This describe block tests "prototype and Symbol preservation" on "cloned tables," but the current implementation mutates the abstract schema in place—no cloning occurs. These tests now pass trivially because concreteTable and abstractTable are the same object reference, not because properties are preserved through cloning.

Consider either:

  1. Renaming the describe block and tests to reflect mutation-based behavior (e.g., "concrete tables — mutation does not break Drizzle compatibility")
  2. Removing these tests since they no longer validate the intended behavior
✏️ Suggested rename
-describe("concrete tables — prototype and Symbol preservation", () => {
+describe("concrete tables — Drizzle compatibility after mutation", () => {
   const IsDrizzleTable = Symbol.for("drizzle:IsDrizzleTable");
   const Columns = Symbol.for("drizzle:Columns");
   const TableName = Symbol.for("drizzle:Name");

-  it("preserves the Table prototype on cloned tables", () => {
+  it("maintains the Table prototype after schema name mutation", () => {
     // ...
   });

-  it("preserves Symbol-keyed properties (IsDrizzleTable, Columns, TableName) on cloned tables", () => {
+  it("maintains Symbol-keyed properties (IsDrizzleTable, Columns, TableName) after mutation", () => {
     // ...
   });

-  it("isTable() returns true for cloned concrete tables", () => {
+  it("isTable() returns true for concrete tables after mutation", () => {
     // ...
   });
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ensdb-sdk/src/lib/drizzle.test.ts` around lines 137 - 167, The tests
in the describe block assume tables are cloned but the implementation mutates
the abstract schema in place, so concreteEnsIndexerSchema.v1Domain and
abstractEnsIndexerSchema.v1Domain are the same object and tests pass trivially;
update the tests to reflect mutation-based behavior by renaming the suite and
specs (e.g., "concrete tables — mutation does not break Drizzle compatibility")
and adjust assertions to verify that mutation preserves Drizzle compatibility
(use buildIndividualEnsDbSchemas to produce schemas and assert
isTable(concreteEnsIndexerSchema.v1Domain) and that Object.getPrototypeOf and
the Symbol.for keys (Symbol.for("drizzle:IsDrizzleTable"),
Symbol.for("drizzle:Columns"), Symbol.for("drizzle:Name")) exist on the mutated
object), or alternatively remove these cloning-specific tests if you want to
assert cloning behavior elsewhere; make changes around concreteEnsIndexerSchema,
abstractEnsIndexerSchema, buildIndividualEnsDbSchemas, and isTable accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/ensdb-sdk/src/lib/drizzle.test.ts`:
- Around line 137-167: The tests in the describe block assume tables are cloned
but the implementation mutates the abstract schema in place, so
concreteEnsIndexerSchema.v1Domain and abstractEnsIndexerSchema.v1Domain are the
same object and tests pass trivially; update the tests to reflect mutation-based
behavior by renaming the suite and specs (e.g., "concrete tables — mutation does
not break Drizzle compatibility") and adjust assertions to verify that mutation
preserves Drizzle compatibility (use buildIndividualEnsDbSchemas to produce
schemas and assert isTable(concreteEnsIndexerSchema.v1Domain) and that
Object.getPrototypeOf and the Symbol.for keys
(Symbol.for("drizzle:IsDrizzleTable"), Symbol.for("drizzle:Columns"),
Symbol.for("drizzle:Name")) exist on the mutated object), or alternatively
remove these cloning-specific tests if you want to assert cloning behavior
elsewhere; make changes around concreteEnsIndexerSchema,
abstractEnsIndexerSchema, buildIndividualEnsDbSchemas, and isTable accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 68a43991-b2da-4151-92e0-f9cca9ecc130

📥 Commits

Reviewing files that changed from the base of the PR and between b45dea9 and a9a08f4.

📒 Files selected for processing (3)
  • .changeset/chatty-monkeys-run.md
  • packages/ensdb-sdk/src/lib/drizzle.test.ts
  • packages/ensdb-sdk/src/lib/drizzle.ts

Comment on lines +221 to +225
console.log("Closing ENSDb client...");
// @ts-expect-error - DrizzleClient.$client is not typed to have an `end` method,
// but in practice it does (e.g. pg's Client does).
await ensDbClient.ensDb.$client.end();
console.log("ENSDb client closed");
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
console.log("Closing ENSDb client...");
// @ts-expect-error - DrizzleClient.$client is not typed to have an `end` method,
// but in practice it does (e.g. pg's Client does).
await ensDbClient.ensDb.$client.end();
console.log("ENSDb client closed");
log("Closing ENSDb client...");
// @ts-expect-error - DrizzleClient.$client is not typed to have an `end` method,
// but in practice it does (e.g. pg's Client does).
await ensDbClient.ensDb.$client.end();
log("ENSDb client closed");

Finally block uses console.log() instead of the standard log() function, causing inconsistent logging formatting

Fix on Vercel

}
}
} catch {
// indexer may not be ready yet
Copy link
Copy Markdown
Contributor

@vercel vercel bot Mar 26, 2026

Choose a reason for hiding this comment

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

Bare catch block and finally block error masking in the indexing status polling loop

Fix on Vercel

@tk-o tk-o merged commit 9f52a66 into main Mar 26, 2026
21 of 22 checks passed
@tk-o tk-o deleted the fix/ensdb-builder-for-concrete-ensindexer-schema branch March 26, 2026 10:24
@github-actions github-actions bot mentioned this pull request Mar 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants