Skip to content

feat(ensnode-sdk): Indexing Status validation#1629

Draft
tk-o wants to merge 14 commits intomainfrom
feat/ensnode-indexing-status-validation
Draft

feat(ensnode-sdk): Indexing Status validation#1629
tk-o wants to merge 14 commits intomainfrom
feat/ensnode-indexing-status-validation

Conversation

@tk-o
Copy link
Contributor

@tk-o tk-o commented Feb 13, 2026

Lite PR

Tip: Review docs on the ENSNode PR process

Summary

  • The update splits the ENSIndexer module of ENSNode SDK into granular layers of responsibility.
    • Deserialization layer uses validation layer.
    • Validation layer can be used without deserialization layer, which is a huge unlock for client side use cases.
  • Consumers using ENSNode SDK will now be able to:
    a. Deserialize a serialized representation of business-layer data model, including validation.
    b. Validate an unvalidated object with shape matching business-layer data model.

Why

  • Before this change, deserialization into ENSIndexer data model required building serialized representation of business-layer data model. Such approach prevented validation of unvalidated objects that already had a shape matching business-layer data model. All validation did required mixing serialized data model with buinsess-layer data model.
  • As per Merge /api/config into /api/indexing-status #1405, Indexing Status API will soon be different for ENSApi and ENSIndexer. Changes in this PR enable re-using ENSIndexer data model in the future ENSApi data model for Indexing Status.

Testing

  • Ran lint, typecheck, and test.
  • Tested /api/indexing-status on local ENSIndexer instance.
  • Tested /api/indexing-status on local ENSApi instance connected to ENSIndexer Alpha in the Green env.

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)

Copilot AI review requested due to automatic review settings February 13, 2026 11:39
@changeset-bot
Copy link

changeset-bot bot commented Feb 13, 2026

🦋 Changeset detected

Latest commit: 298f87f

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

This PR includes changesets to release 19 packages
Name Type
@ensnode/ensnode-sdk Major
ensadmin Major
ensapi Major
ensindexer Major
ensrainbow Major
fallback-ensapi Major
@namehash/ens-referrals Major
@ensnode/ensnode-react Major
@ensnode/ensrainbow-sdk Major
@namehash/namehash-ui Major
@ensnode/datasources Major
@ensnode/ponder-metadata Major
@ensnode/ensnode-schema Major
@ensnode/ponder-sdk Major
@ensnode/ponder-subgraph Major
@ensnode/shared-configs Major
@docs/ensnode Major
@docs/ensrainbow Major
@docs/mintlify 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
Contributor

vercel bot commented Feb 13, 2026

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

Project Deployment Actions Updated (UTC)
admin.ensnode.io Error Error Feb 13, 2026 1:38pm
ensnode.io Ready Ready Preview, Comment Feb 13, 2026 1:38pm
ensrainbow.io Ready Ready Preview, Comment Feb 13, 2026 1:38pm

@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 2026

📝 Walkthrough

Walkthrough

This PR bumps the ensnode-sdk package version and introduces exported validation functions for the Indexing Status data model. It adds serialized Zod schemas and refactors deserialization pipelines to use an intermediate Unvalidated type pattern, enabling consumer-side validation use cases.

Changes

Cohort / File(s) Summary
Version & Manifest
.changeset/ten-cups-grab.md
Bumps package version to minor.
Serialized Zod Schemas
packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts, packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/{cross-chain,omnichain,realtime}*.ts
Adds new schema factories for serialized indexing status types (e.g., makeSerializedCrossChainIndexingStatusSnapshotSchema, makeSerializedOmnichainIndexingStatusSnapshotSchema, makeSerializedRealtimeIndexingStatusProjectionSchema). Converts omnichain chain storage to Map-based representation and updates discriminated unions.
Deserialization Pipelines
packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/{chain,cross-chain,omnichain,realtime}*.ts
Refactors deserialization to use two-stage schema validation: first applies serialized schema producing Unvalidated intermediate type, then transforms and pipes through final schema. Updates function signatures to accept Unvalidated<T> parameters. Adds builder functions like buildUnvalidatedCrossChainIndexingStatusSnapshot.
New Validation Functions
packages/ensnode-sdk/src/ensindexer/indexing-status/validate/{cross-chain,omnichain,realtime}*.ts
Introduces public validation functions (validateCrossChainIndexingStatusSnapshot, validateOmnichainIndexingStatusSnapshot, validateRealtimeIndexingStatusProjection) that parse unvalidated data via Zod schemas and throw descriptive errors on failure.

Sequence Diagram

sequenceDiagram
    actor Consumer
    participant SerializedData as Serialized Data
    participant Schema1 as Serialized Schema
    participant Builder as Unvalidated Builder
    participant Schema2 as Final Validation Schema
    participant ValidatedData as Validated Data

    Consumer->>SerializedData: Input serialized JSON/object
    SerializedData->>Schema1: Apply serialized schema<br/>(e.g., makeSerializedCrossChainIndexingStatusSnapshotSchema)
    Schema1->>Builder: Transform via builder function<br/>(e.g., buildUnvalidatedCrossChainIndexingStatusSnapshot)
    Builder->>Schema2: Pipe through final schema<br/>(e.g., makeCrossChainIndexingStatusSnapshotSchema)
    Schema2->>ValidatedData: Parse and return validated result
    ValidatedData-->>Consumer: Validated, typed data<br/>or throw Error on failure
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

ensnode-sdk

Poem

🐰 With whiskers twitching, schemas align,
Two-stage validation, a design so fine,
From Serialized hops to Unvalidated dreams,
Then Final schemas make data pristine!
Consumer-side power now in paws' might, 🌟

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The PR description comprehensively covers all required sections from the template: Summary, Why, Testing, Notes for Reviewer, and Pre-Review Checklist with substantive details.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main
Title check ✅ Passed The title 'feat(ensnode-sdk): Indexing Status validation' clearly and concisely describes the main objective of the PR—adding validation capabilities for Indexing Status data models in the ensnode-sdk package.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ensnode-indexing-status-validation

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.

Copy link

@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: 2

🤖 Fix all issues with AI agents
In
`@packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts`:
- Around line 108-116: The schema factory
makeSerializedCrossChainIndexingStatusSnapshotSchema uses
z.enum(CrossChainIndexingStrategyIds) but CrossChainIndexingStrategyIds is a
const object (not a string tuple), so replace z.enum(...) with
z.nativeEnum(CrossChainIndexingStrategyIds) to correctly validate that value;
update the import/usage in makeSerializedCrossChainIndexingStatusSnapshotSchema
so the strategy property uses z.nativeEnum(CrossChainIndexingStrategyIds) while
leaving the other fields (slowestChainIndexingCursor, snapshotTime,
omnichainSnapshot) unchanged.

In
`@packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/omnichain-indexing-status-snapshot.ts`:
- Around line 284-301: The Backfill schema created by
makeOmnichainIndexingStatusSnapshotBackfillSchema only includes the Queued and
Backfill branch in the discriminatedUnion for "chainStatus", causing valid
Backfill snapshots with Completed chains to fail; update the discriminatedUnion
call inside makeOmnichainIndexingStatusSnapshotBackfillSchema to include the
Completed variant (the same schema used for serialized variants, e.g.,
makeChainIndexingStatusSnapshotCompletedSchema) so the union matches the type
ChainIndexingStatusSnapshotForOmnichainIndexingStatusSnapshotBackfill and the
runtime check
checkChainIndexingStatusSnapshotsForOmnichainStatusSnapshotBackfill.

Copy link
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 extends the ENSNode SDK’s ENSIndexer indexing-status module refactor by adding a standalone validation layer and updating deserialization to validate via “serialized → unvalidated → validated” pipelines. This enables consumers to validate already-shaped indexing-status objects without going through full serialization.

Changes:

  • Added validate* helpers for indexing-status models (CrossChain/Omnichain/Realtime) that validate Unvalidated<T> via Zod schemas.
  • Introduced “serialized” Zod schemas and updated deserializers to transform serialized payloads into unvalidated business models before piping into validation schemas.
  • Adjusted duration parsing and updated ENSApi’s /amirealtime query parsing to explicitly coerce numbers.

Reviewed changes

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

Show a summary per file
File Description
packages/ensnode-sdk/src/shared/zod-schemas.ts Changes makeDurationSchema parsing behavior (coercion removed).
packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts Switches to .object, adds serialized schema maker.
packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/omnichain-indexing-status-snapshot.ts Reworks omnichain snapshot schemas (Map-based) and adds serialized variants.
packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts Removes debug logging, adds serialized schema maker.
packages/ensnode-sdk/src/ensindexer/indexing-status/validate/realtime-indexing-status-projection.ts New validation helper for realtime projection.
packages/ensnode-sdk/src/ensindexer/indexing-status/validate/omnichain-indexing-status-snapshot.ts New validation helper for omnichain snapshot.
packages/ensnode-sdk/src/ensindexer/indexing-status/validate/cross-chain-indexing-status-snapshot.ts New validation helper for cross-chain snapshot.
packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-projection.ts Adds unvalidated builder + pipes serialized schema into validated schema.
packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/omnichain-indexing-status-snapshot.ts Adds unvalidated builder (record → Map) + serialized→validated pipeline.
packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts Adds unvalidated builder + serialized→validated pipeline.
packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts Adds serialized response schemas and imports serialized realtime schema.
packages/ensnode-sdk/src/api/indexing-status/deserialize.ts Deserializes via serialized schema → unvalidated builder → validated schema.
apps/ensapi/src/handlers/amirealtime-api.ts Updates query param parsing to explicitly coerce to number before duration validation.
.changeset/ten-cups-grab.md Adds changeset for new validate* functions.
Comments suppressed due to low confidence (1)

packages/ensnode-sdk/src/shared/zod-schemas.ts:94

  • makeDurationSchema no longer coerces input to a number. This breaks callers that pass durations as strings/unknown (e.g. deserializeDuration(record.totalIncrementalDuration) in ENSApi DB code, where totalIncrementalDuration is a string). Consider restoring z.coerce.number() here (or adding a separate makeCoercedDurationSchema) so both deserialization and validation paths continue to accept serialized numeric strings.
  z
    .number({
      error: `${valueLabel} must be a number.`,
    })
    .pipe(makeNonNegativeIntegerSchema(valueLabel));

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

Copy link
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

Three validation functions (validateOmnichainIndexingStatusSnapshot, validateCrossChainIndexingStatusSnapshot, validateRealtimeIndexingStatusProjection) were not exported from the public API index.ts file

Fix on Vercel

Copy link

@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: 3

🤖 Fix all issues with AI agents
In @.changeset/ten-cups-grab.md:
- Line 5: The changeset note uses the awkward phrase "consumers side"; update
the wording to "consumer side" (or "consumers' side" if you prefer possessive)
so the sentence reads: "These functions enable new use cases on the consumer
side." Locate the sentence mentioning "consumers side" in the .changeset note
and replace it accordingly.

In `@packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts`:
- Around line 13-16: Change the runtime import of
SerializedIndexingStatusResponse and SerializedIndexingStatusResponseOk to a
type-only import since they are only used for JSDoc links; specifically replace
the current import that brings those symbols into runtime with a type import
referencing SerializedIndexingStatusResponse and
SerializedIndexingStatusResponseOk from serialized-response so they are removed
from the emitted bundle while keeping the JSDoc `@link` references intact.

In
`@packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-projection.ts`:
- Around line 35-37: The pipeline currently uses
.transform(buildUnvalidatedRealtimeIndexingStatusProjection) on the schema
produced by makeSerializedRealtimeIndexingStatusProjectionSchema(valueLabel) but
transform runs after parsing; replace that call with
.preprocess(buildUnvalidatedRealtimeIndexingStatusProjection) so preprocessing
occurs before validation and the resulting value matches the expected input for
makeRealtimeIndexingStatusProjectionSchema(valueLabel); update the chain to use
.preprocess(...) .pipe(makeRealtimeIndexingStatusProjectionSchema(valueLabel))
and keep existing coercion logic in makeDurationSchema unchanged.

tk-o added 10 commits February 13, 2026 14:31
Define business-layer-oriented schema for each `OmnichainIndexingStatusSnapshot` variant, including more precise definition for `chains` schema. Also, using similar approach, create zod-schemas for `SerializedOmnichainIndexingStatusSnapshot`.
Compose serialization-layer schema and business-layer schema to enable precise deserialization protocol.
Validation-layer uses just business-level schema and does not rely on serialization-layer schema at all.
Create independent schemas for each layer, business and serialization
Compose serialization-layer schema and business-layer schema to enable precise deserialization protocol.
Validation-layer uses just business-level schema and does not rely on serialization-layer schema at all.
Create independent schemas for each layer, business and serialization
Compose serialization-layer schema and business-layer schema to enable precise deserialization protocol.
Validation-layer uses just business-level schema and does not rely on serialization-layer schema at all.
Create independent schemas for each layer, business and serialization
Copilot AI review requested due to automatic review settings February 13, 2026 13:32
@tk-o tk-o force-pushed the feat/ensnode-indexing-status-validation branch from 35b5a36 to ea86060 Compare February 13, 2026 13:32
tk-o added 4 commits February 13, 2026 14:36
… data model. These functions enable new use cases on consumers side.
Assume input type for maybe serialized data is `Unvalidated<Serialized*>` to highlight the target type for the input and allow partial input to fail deserialization attempt.
Remove generic definitions, that were replaced by granular definitions for each `OmnichainIndexingStatusSnapshot` variant.
Copy link
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 14 out of 14 changed files in this pull request and generated 5 comments.


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

*
* @throws Error if the provided object is not a valid {@link OmnichainIndexingStatusSnapshot}.
*/
export function validateOmnichainIndexingStatusSnapshot(
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The new validate functions (validateOmnichainIndexingStatusSnapshot, validateCrossChainIndexingStatusSnapshot, and validateRealtimeIndexingStatusProjection) are not being exported from the indexing-status module's index.ts file. The PR description states that "Consumers using ENSNode SDK will now be able to... b. Validate an unvalidated object with shape matching business-layer data model", but without exporting these functions, they are not accessible to consumers.

The index.ts file at packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts only exports from validate/chain-indexing-status-snapshot but is missing exports for the three new validate modules. You should add these exports:

  • export * from "./validate/omnichain-indexing-status-snapshot";
  • export * from "./validate/cross-chain-indexing-status-snapshot";
  • export * from "./validate/realtime-indexing-status-projection";

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +29
import {
SerializedOmnichainIndexingStatusSnapshot,
SerializedOmnichainIndexingStatusSnapshotBackfill,
SerializedOmnichainIndexingStatusSnapshotCompleted,
SerializedOmnichainIndexingStatusSnapshotFollowing,
SerializedOmnichainIndexingStatusSnapshotUnstarted,
} from "../serialize/omnichain-indexing-status-snapshot";
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The Serialized* types (SerializedOmnichainIndexingStatusSnapshot, SerializedOmnichainIndexingStatusSnapshotBackfill, SerializedOmnichainIndexingStatusSnapshotCompleted, SerializedOmnichainIndexingStatusSnapshotFollowing, SerializedOmnichainIndexingStatusSnapshotUnstarted) are only used in JSDoc comments and should be imported with the 'type' keyword to indicate they are type-only imports. Change line 23 to use 'import type {' instead of 'import {'.

Copilot uses AI. Check for mistakes.
export const makeSerializedIndexingStatusResponseOkSchema = (
valueLabel: string = "Serialized Indexing Status Response OK",
) =>
z.strictObject({
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The new serialized schema makers (makeSerializedIndexingStatusResponseOkSchema and makeSerializedIndexingStatusResponseSchema) use z.strictObject, which is inconsistent with the pattern used in the ensindexer indexing status schemas that were changed from z.strictObject to z.object. For consistency with the ensindexer schemas, consider using z.object instead of z.strictObject here.

Suggested change
z.strictObject({
z.object({

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +16
import {
SerializedIndexingStatusResponse,
SerializedIndexingStatusResponseOk,
} from "./serialized-response";
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Unused imports SerializedIndexingStatusResponse, SerializedIndexingStatusResponseOk.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +30
SerializedOmnichainIndexingStatusSnapshot,
SerializedOmnichainIndexingStatusSnapshotBackfill,
SerializedOmnichainIndexingStatusSnapshotCompleted,
SerializedOmnichainIndexingStatusSnapshotFollowing,
SerializedOmnichainIndexingStatusSnapshotUnstarted,
} from "../serialize/omnichain-indexing-status-snapshot";
import {
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Unused imports SerializedOmnichainIndexingStatusSnapshot, SerializedOmnichainIndexingStatusSnapshotBackfill, SerializedOmnichainIndexingStatusSnapshotCompleted, SerializedOmnichainIndexingStatusSnapshotFollowing, SerializedOmnichainIndexingStatusSnapshotUnstarted.

Suggested change
SerializedOmnichainIndexingStatusSnapshot,
SerializedOmnichainIndexingStatusSnapshotBackfill,
SerializedOmnichainIndexingStatusSnapshotCompleted,
SerializedOmnichainIndexingStatusSnapshotFollowing,
SerializedOmnichainIndexingStatusSnapshotUnstarted,
} from "../serialize/omnichain-indexing-status-snapshot";
import {

Copilot uses AI. Check for mistakes.
@tk-o tk-o changed the title Feat/ensnode indexing status validation feat(ensnode-sdk): Indexing Status validation Feb 13, 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.

1 participant