diff --git a/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts b/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts index 407ae50ce..9475b6cf1 100644 --- a/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts +++ b/packages/ensnode-sdk/src/api/indexing-status/zod-schemas.ts @@ -1,6 +1,6 @@ import { z } from "zod/v4"; -import { makeRealtimeIndexingStatusProjectionSchema } from "../../ensindexer/indexing-status/zod-schemas"; +import { makeRealtimeIndexingStatusProjectionSchema } from "../../ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection"; import { type IndexingStatusResponse, IndexingStatusResponseCodes, diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts similarity index 75% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts index 8165b0732..a438783ae 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/types.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/cross-chain-indexing-status-snapshot.ts @@ -1,8 +1,5 @@ -import type { Duration, UnixTimestamp } from "../../shared/types"; -import type { - ChainIndexingConfigTypeIds, - ChainIndexingStatusIds, -} from "./chain-indexing-status-snapshot"; +import type { BlockRef, ChainId, UnixTimestamp } from "../../shared/types"; +import { ChainIndexingStatusIds } from "./chain-indexing-status-snapshot"; import type { OmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; /** @@ -95,32 +92,26 @@ export interface CrossChainIndexingStatusSnapshotOmnichain { export type CrossChainIndexingStatusSnapshot = CrossChainIndexingStatusSnapshotOmnichain; /** - * A "realtime" indexing status projection based on worst-case assumptions - * from the `snapshot`. + * Gets the latest indexed {@link BlockRef} for the given {@link ChainId}. * - * Invariants: - * - `projectedAt` is always >= `snapshot.snapshotTime`. - * - `worstCaseDistance` is always equal to - * `projectedAt - snapshot.slowestChainIndexingCursor`. + * @returns the latest indexed {@link BlockRef} for the given {@link ChainId}, or null if the chain + * isn't being indexed at all or is queued and therefore hasn't started indexing yet. */ -export type RealtimeIndexingStatusProjection = { - /** - * The timestamp representing "now" as of the time this projection was generated. - */ - projectedAt: UnixTimestamp; +export function getLatestIndexedBlockRef( + indexingStatus: CrossChainIndexingStatusSnapshot, + chainId: ChainId, +): BlockRef | null { + const chainIndexingStatus = indexingStatus.omnichainSnapshot.chains.get(chainId); - /** - * The distance between `projectedAt` and `snapshot.slowestChainIndexingCursor`. - * - * This is "worst-case" because it assumes all of the following: - * - the `snapshot` (which may have `snapshot.snapshotTime < projectedAt`) is still the - * latest snapshot and no indexing progress has been made since `snapshotTime`. - * - each indexed chain has added a new block as of `projectedAt`. - */ - worstCaseDistance: Duration; + if (chainIndexingStatus === undefined) { + // chain isn't being indexed at all + return null; + } - /** - * The {@link CrossChainIndexingStatusSnapshot} that this projection is based on. - */ - snapshot: CrossChainIndexingStatusSnapshot; -}; + if (chainIndexingStatus.chainStatus === ChainIndexingStatusIds.Queued) { + // chain is queued, so no data for the chain has been indexed yet + return null; + } + + return chainIndexingStatus.latestIndexedBlock; +} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts deleted file mode 100644 index ea49239ff..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { prettifyError } from "zod/v4"; - -import type { - SerializedCrossChainIndexingStatusSnapshot, - SerializedRealtimeIndexingStatusProjection, -} from "./serialized-types"; -import type { CrossChainIndexingStatusSnapshot, RealtimeIndexingStatusProjection } from "./types"; -import { - makeCrossChainIndexingStatusSnapshotSchema, - makeRealtimeIndexingStatusProjectionSchema, -} from "./zod-schemas"; - -/** - * Deserialize an {@link CrossChainIndexingStatusSnapshot} object. - */ -export function deserializeCrossChainIndexingStatusSnapshot( - maybeSnapshot: SerializedCrossChainIndexingStatusSnapshot, - valueLabel?: string, -): CrossChainIndexingStatusSnapshot { - const schema = makeCrossChainIndexingStatusSnapshotSchema(valueLabel); - const parsed = schema.safeParse(maybeSnapshot); - - if (parsed.error) { - throw new Error( - `Cannot deserialize into CrossChainIndexingStatusSnapshot:\n${prettifyError(parsed.error)}\n`, - ); - } - - return parsed.data; -} - -/** - * Deserialize into a {@link RealtimeIndexingStatusProjection} object. - */ -export function deserializeRealtimeIndexingStatusProjection( - maybeProjection: SerializedRealtimeIndexingStatusProjection, - valueLabel?: string, -): RealtimeIndexingStatusProjection { - const schema = makeRealtimeIndexingStatusProjectionSchema(valueLabel); - const parsed = schema.safeParse(maybeProjection); - - if (parsed.error) { - throw new Error( - `Cannot deserialize into RealtimeIndexingStatusProjection:\n${prettifyError(parsed.error)}\n`, - ); - } - - return parsed.data; -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts new file mode 100644 index 000000000..c346c88cf --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/cross-chain-indexing-status-snapshot.ts @@ -0,0 +1,24 @@ +import { prettifyError } from "zod/v4"; + +import type { CrossChainIndexingStatusSnapshot } from "../cross-chain-indexing-status-snapshot"; +import type { SerializedCrossChainIndexingStatusSnapshot } from "../serialize/cross-chain-indexing-status-snapshot"; +import { makeCrossChainIndexingStatusSnapshotSchema } from "../zod-schema/cross-chain-indexing-status-snapshot"; + +/** + * Deserialize an {@link CrossChainIndexingStatusSnapshot} object. + */ +export function deserializeCrossChainIndexingStatusSnapshot( + maybeSnapshot: SerializedCrossChainIndexingStatusSnapshot, + valueLabel?: string, +): CrossChainIndexingStatusSnapshot { + const schema = makeCrossChainIndexingStatusSnapshotSchema(valueLabel); + const parsed = schema.safeParse(maybeSnapshot); + + if (parsed.error) { + throw new Error( + `Cannot deserialize into CrossChainIndexingStatusSnapshot:\n${prettifyError(parsed.error)}\n`, + ); + } + + return parsed.data; +} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-projection.ts new file mode 100644 index 000000000..90a79470e --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/deserialize/realtime-indexing-status-projection.ts @@ -0,0 +1,24 @@ +import { prettifyError } from "zod/v4"; + +import type { RealtimeIndexingStatusProjection } from "../realtime-indexing-status-projection"; +import type { SerializedRealtimeIndexingStatusProjection } from "../serialize/realtime-indexing-status-projection"; +import { makeRealtimeIndexingStatusProjectionSchema } from "../zod-schema/realtime-indexing-status-projection"; + +/** + * Deserialize into a {@link RealtimeIndexingStatusProjection} object. + */ +export function deserializeRealtimeIndexingStatusProjection( + maybeProjection: SerializedRealtimeIndexingStatusProjection, + valueLabel?: string, +): RealtimeIndexingStatusProjection { + const schema = makeRealtimeIndexingStatusProjectionSchema(valueLabel); + const parsed = schema.safeParse(maybeProjection); + + if (parsed.error) { + throw new Error( + `Cannot deserialize into RealtimeIndexingStatusProjection:\n${prettifyError(parsed.error)}\n`, + ); + } + + return parsed.data; +} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts deleted file mode 100644 index 911676896..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/helpers.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { BlockRef, ChainId } from "../../shared/types"; -import { ChainIndexingStatusIds } from "./chain-indexing-status-snapshot"; -import type { CrossChainIndexingStatusSnapshot } from "./types"; - -/** - * Gets the latest indexed {@link BlockRef} for the given {@link ChainId}. - * - * @returns the latest indexed {@link BlockRef} for the given {@link ChainId}, or null if the chain - * isn't being indexed at all or is queued and therefore hasn't started indexing yet. - */ -export function getLatestIndexedBlockRef( - indexingStatus: CrossChainIndexingStatusSnapshot, - chainId: ChainId, -): BlockRef | null { - const chainIndexingStatus = indexingStatus.omnichainSnapshot.chains.get(chainId); - - if (chainIndexingStatus === undefined) { - // chain isn't being indexed at all - return null; - } - - if (chainIndexingStatus.chainStatus === ChainIndexingStatusIds.Queued) { - // chain is queued, so no data for the chain has been indexed yet - return null; - } - - return chainIndexingStatus.latestIndexedBlock; -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts index d9ba4f01a..7d642d64a 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/index.ts @@ -1,13 +1,12 @@ export * from "./chain-indexing-status-snapshot"; -export * from "./deserialize"; +export * from "./cross-chain-indexing-status-snapshot"; export * from "./deserialize/chain-indexing-status-snapshot"; +export * from "./deserialize/cross-chain-indexing-status-snapshot"; export * from "./deserialize/omnichain-indexing-status-snapshot"; -export * from "./helpers"; +export * from "./deserialize/realtime-indexing-status-projection"; export * from "./omnichain-indexing-status-snapshot"; -export * from "./projection"; -export * from "./serialize"; +export * from "./realtime-indexing-status-projection"; export * from "./serialize/chain-indexing-status-snapshot"; export * from "./serialize/omnichain-indexing-status-snapshot"; -export * from "./serialized-types"; -export * from "./types"; +export * from "./serialize/realtime-indexing-status-projection"; export * from "./validate/chain-indexing-status-snapshot"; diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts deleted file mode 100644 index 5989d3631..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { UnixTimestamp } from "../../shared/types"; -import type { CrossChainIndexingStatusSnapshot, RealtimeIndexingStatusProjection } from "./types"; - -/** - * Create realtime indexing status projection from - * a {@link CrossChainIndexingStatusSnapshot}. - */ -export function createRealtimeIndexingStatusProjection( - snapshot: CrossChainIndexingStatusSnapshot, - now: UnixTimestamp, -): RealtimeIndexingStatusProjection { - /** - * The timestamp when the realtime indexing status was projected. - * - * Due to possible clock skew between different systems, - * if the "now" timestamp on the system generating this indexing status - * projection is less than the snapshot time, then this value must be set to - * equal to the whichever is higher between the `now` and - * the snapshot time to ensure all invariants are followed. - */ - const projectedAt = Math.max(now, snapshot.snapshotTime); - - return { - projectedAt, - worstCaseDistance: projectedAt - snapshot.slowestChainIndexingCursor, - snapshot, - }; -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.test.ts similarity index 85% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.test.ts index e8b2c33fd..91515fd83 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/projection.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.test.ts @@ -5,10 +5,13 @@ import { ChainIndexingConfigTypeIds, ChainIndexingStatusIds, } from "./chain-indexing-status-snapshot"; -import { deserializeCrossChainIndexingStatusSnapshot } from "./deserialize"; +import { CrossChainIndexingStrategyIds } from "./cross-chain-indexing-status-snapshot"; +import { deserializeCrossChainIndexingStatusSnapshot } from "./deserialize/cross-chain-indexing-status-snapshot"; import { OmnichainIndexingStatusIds } from "./omnichain-indexing-status-snapshot"; -import { createRealtimeIndexingStatusProjection } from "./projection"; -import { CrossChainIndexingStrategyIds, type RealtimeIndexingStatusProjection } from "./types"; +import { + createRealtimeIndexingStatusProjection, + type RealtimeIndexingStatusProjection, +} from "./realtime-indexing-status-projection"; describe("Realtime Indexing Status Projection", () => { it("can be created from existing omnichain snapshot", () => { diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts new file mode 100644 index 000000000..39d2c3389 --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/realtime-indexing-status-projection.ts @@ -0,0 +1,59 @@ +import type { Duration, UnixTimestamp } from "../../shared/types"; +import type { CrossChainIndexingStatusSnapshot } from "./cross-chain-indexing-status-snapshot"; + +/** + * A "realtime" indexing status projection based on worst-case assumptions + * from the `snapshot`. + * + * Invariants: + * - `projectedAt` is always >= `snapshot.snapshotTime`. + * - `worstCaseDistance` is always equal to + * `projectedAt - snapshot.slowestChainIndexingCursor`. + */ +export type RealtimeIndexingStatusProjection = { + /** + * The timestamp representing "now" as of the time this projection was generated. + */ + projectedAt: UnixTimestamp; + + /** + * The distance between `projectedAt` and `snapshot.slowestChainIndexingCursor`. + * + * This is "worst-case" because it assumes all of the following: + * - the `snapshot` (which may have `snapshot.snapshotTime < projectedAt`) is still the + * latest snapshot and no indexing progress has been made since `snapshotTime`. + * - each indexed chain has added a new block as of `projectedAt`. + */ + worstCaseDistance: Duration; + + /** + * The {@link CrossChainIndexingStatusSnapshot} that this projection is based on. + */ + snapshot: CrossChainIndexingStatusSnapshot; +}; + +/** + * Create realtime indexing status projection from + * a {@link CrossChainIndexingStatusSnapshot}. + */ +export function createRealtimeIndexingStatusProjection( + snapshot: CrossChainIndexingStatusSnapshot, + now: UnixTimestamp, +): RealtimeIndexingStatusProjection { + /** + * The timestamp when the realtime indexing status was projected. + * + * Due to possible clock skew between different systems, + * if the "now" timestamp on the system generating this indexing status + * projection is less than the snapshot time, then this value must be set to + * equal to the whichever is higher between the `now` and + * the snapshot time to ensure all invariants are followed. + */ + const projectedAt = Math.max(now, snapshot.snapshotTime); + + return { + projectedAt, + worstCaseDistance: projectedAt - snapshot.slowestChainIndexingCursor, + snapshot, + }; +} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts deleted file mode 100644 index fd7b82448..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { serializeOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; -import type { - SerializedCrossChainIndexingStatusSnapshot, - SerializedRealtimeIndexingStatusProjection, -} from "./serialized-types"; -import type { CrossChainIndexingStatusSnapshot, RealtimeIndexingStatusProjection } from "./types"; - -export function serializeCrossChainIndexingStatusSnapshotOmnichain({ - strategy, - slowestChainIndexingCursor, - snapshotTime, - omnichainSnapshot, -}: CrossChainIndexingStatusSnapshot): SerializedCrossChainIndexingStatusSnapshot { - return { - strategy, - slowestChainIndexingCursor, - snapshotTime, - omnichainSnapshot: serializeOmnichainIndexingStatusSnapshot(omnichainSnapshot), - }; -} - -export function serializeRealtimeIndexingStatusProjection( - indexingProjection: RealtimeIndexingStatusProjection, -): SerializedRealtimeIndexingStatusProjection { - return { - projectedAt: indexingProjection.projectedAt, - worstCaseDistance: indexingProjection.worstCaseDistance, - snapshot: serializeCrossChainIndexingStatusSnapshotOmnichain(indexingProjection.snapshot), - } satisfies SerializedRealtimeIndexingStatusProjection; -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts new file mode 100644 index 000000000..82c1c309f --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/cross-chain-indexing-status-snapshot.ts @@ -0,0 +1,46 @@ +import { + type CrossChainIndexingStatusSnapshot, + type CrossChainIndexingStatusSnapshotOmnichain, + CrossChainIndexingStrategyIds, +} from "../cross-chain-indexing-status-snapshot"; +import { + type SerializedOmnichainIndexingStatusSnapshot, + serializeOmnichainIndexingStatusSnapshot, +} from "./omnichain-indexing-status-snapshot"; + +/** + * Serialized representation of {@link CrossChainIndexingStatusSnapshotOmnichain} + */ +export interface SerializedCrossChainIndexingStatusSnapshotOmnichain + extends Omit { + omnichainSnapshot: SerializedOmnichainIndexingStatusSnapshot; +} + +/** + * Serialized representation of {@link CrossChainIndexingStatusSnapshot} + */ +export type SerializedCrossChainIndexingStatusSnapshot = + SerializedCrossChainIndexingStatusSnapshotOmnichain; + +export function serializeCrossChainIndexingStatusSnapshotOmnichain({ + strategy, + slowestChainIndexingCursor, + snapshotTime, + omnichainSnapshot, +}: CrossChainIndexingStatusSnapshotOmnichain): SerializedCrossChainIndexingStatusSnapshotOmnichain { + return { + strategy, + slowestChainIndexingCursor, + snapshotTime, + omnichainSnapshot: serializeOmnichainIndexingStatusSnapshot(omnichainSnapshot), + }; +} + +export function serializeCrossChainIndexingStatusSnapshot( + snapshot: CrossChainIndexingStatusSnapshot, +): SerializedCrossChainIndexingStatusSnapshot { + switch (snapshot.strategy) { + case CrossChainIndexingStrategyIds.Omnichain: + return serializeCrossChainIndexingStatusSnapshotOmnichain(snapshot); + } +} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts new file mode 100644 index 000000000..0ae32d1d9 --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialize/realtime-indexing-status-projection.ts @@ -0,0 +1,32 @@ +import type { RealtimeIndexingStatusProjection } from "../realtime-indexing-status-projection"; +import { + type SerializedCrossChainIndexingStatusSnapshot, + serializeCrossChainIndexingStatusSnapshotOmnichain, +} from "./cross-chain-indexing-status-snapshot"; +import type { SerializedOmnichainIndexingStatusSnapshot } from "./omnichain-indexing-status-snapshot"; + +/** + * Serialized representation of {@link RealtimeIndexingStatusProjection} + */ +export interface SerializedCurrentIndexingProjectionOmnichain + extends Omit { + snapshot: SerializedOmnichainIndexingStatusSnapshot; +} + +/** + * Serialized representation of {@link RealtimeIndexingStatusProjection} + */ +export interface SerializedRealtimeIndexingStatusProjection + extends Omit { + snapshot: SerializedCrossChainIndexingStatusSnapshot; +} + +export function serializeRealtimeIndexingStatusProjection( + indexingProjection: RealtimeIndexingStatusProjection, +): SerializedRealtimeIndexingStatusProjection { + return { + projectedAt: indexingProjection.projectedAt, + worstCaseDistance: indexingProjection.worstCaseDistance, + snapshot: serializeCrossChainIndexingStatusSnapshotOmnichain(indexingProjection.snapshot), + } satisfies SerializedRealtimeIndexingStatusProjection; +} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts deleted file mode 100644 index e866d06df..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/serialized-types.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { SerializedOmnichainIndexingStatusSnapshot } from "./serialize/omnichain-indexing-status-snapshot"; -import type { - CrossChainIndexingStatusSnapshot, - CrossChainIndexingStatusSnapshotOmnichain, - RealtimeIndexingStatusProjection, -} from "./types"; - -/** - * Serialized representation of {@link CrossChainIndexingStatusSnapshotOmnichain} - */ -export interface SerializedCrossChainIndexingStatusSnapshotOmnichain - extends Omit { - omnichainSnapshot: SerializedOmnichainIndexingStatusSnapshot; -} - -/** - * Serialized representation of {@link CrossChainIndexingStatusSnapshot} - */ -export type SerializedCrossChainIndexingStatusSnapshot = - SerializedCrossChainIndexingStatusSnapshotOmnichain; - -/** - * Serialized representation of {@link RealtimeIndexingStatusProjection} - */ -export interface SerializedCurrentIndexingProjectionOmnichain - extends Omit { - snapshot: SerializedOmnichainIndexingStatusSnapshot; -} - -/** - * Serialized representation of {@link RealtimeIndexingStatusProjection} - */ -export interface SerializedRealtimeIndexingStatusProjection - extends Omit { - snapshot: SerializedCrossChainIndexingStatusSnapshot; -} diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.test.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/chain-indexing-status-snapshot.test.ts similarity index 98% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.test.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/chain-indexing-status-snapshot.test.ts index e58b6dd31..084138ceb 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.test.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/chain-indexing-status-snapshot.test.ts @@ -6,7 +6,7 @@ import { earliestBlockRef, laterBlockRef, latestBlockRef, -} from "./block-refs.mock"; +} from "../block-refs.mock"; import { ChainIndexingConfigTypeIds, ChainIndexingStatusIds, @@ -15,8 +15,8 @@ import { type ChainIndexingStatusSnapshotCompleted, type ChainIndexingStatusSnapshotFollowing, type ChainIndexingStatusSnapshotQueued, -} from "./chain-indexing-status-snapshot"; -import { makeChainIndexingStatusSnapshotSchema } from "./zod-schema/chain-indexing-status-snapshot"; +} from "../chain-indexing-status-snapshot"; +import { makeChainIndexingStatusSnapshotSchema } from "./chain-indexing-status-snapshot"; describe("ENSIndexer: Indexing Status", () => { describe("Zod Schemas", () => { diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts similarity index 59% rename from packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts rename to packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts index 94f86b126..872a42601 100644 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/validations.ts +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/cross-chain-indexing-status-snapshot.ts @@ -1,21 +1,16 @@ +import { z } from "zod/v4"; import type { ParsePayload } from "zod/v4/core"; +import { makeUnixTimestampSchema } from "../../../shared/zod-schemas"; import { ChainIndexingConfigTypeIds, ChainIndexingStatusIds, -} from "./chain-indexing-status-snapshot"; -import type { - CrossChainIndexingStatusSnapshotOmnichain, - RealtimeIndexingStatusProjection, -} from "./types"; - -/** - * Invariants for {@link OmnichainIndexingSnapshot}. - */ - -/** - * Invariants for {@link CrossChainIndexingStatusSnapshotOmnichain}. - */ +} from "../chain-indexing-status-snapshot"; +import { + type CrossChainIndexingStatusSnapshotOmnichain, + CrossChainIndexingStrategyIds, +} from "../cross-chain-indexing-status-snapshot"; +import { makeOmnichainIndexingStatusSnapshotSchema } from "./omnichain-indexing-status-snapshot"; /** * Invariant: for cross-chain indexing status snapshot omnichain, @@ -82,48 +77,27 @@ export function invariant_snapshotTimeIsTheHighestKnownBlockTimestamp( } /** - * Invariants for {@link RealtimeIndexingStatusProjection}. + * Makes Zod schema for {@link CrossChainIndexingStatusSnapshotOmnichain} */ +const makeCrossChainIndexingStatusSnapshotOmnichainSchema = ( + valueLabel: string = "Cross-chain Indexing Status Snapshot Omnichain", +) => + z + .strictObject({ + strategy: z.literal(CrossChainIndexingStrategyIds.Omnichain), + slowestChainIndexingCursor: makeUnixTimestampSchema(valueLabel), + snapshotTime: makeUnixTimestampSchema(valueLabel), + omnichainSnapshot: makeOmnichainIndexingStatusSnapshotSchema(valueLabel), + }) + .check(invariant_slowestChainEqualsToOmnichainSnapshotTime) + .check(invariant_snapshotTimeIsTheHighestKnownBlockTimestamp); /** - * Invariant: For realtime indexing status projection, - * `projectedAt` is after or same as `snapshot.snapshotTime`. + * Makes Zod schema for {@link CrossChainIndexingStatusSnapshot} */ -export function invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime( - ctx: ParsePayload, -) { - const projection = ctx.value; - - const { snapshot, projectedAt } = projection; - - if (snapshot.snapshotTime > projectedAt) { - ctx.issues.push({ - code: "custom", - input: projection, - message: "`projectedAt` must be after or same as `snapshot.snapshotTime`.", - }); - } -} - -/** - * Invariant: For realtime indexing status projection, - * `worstCaseDistance` is the difference between `projectedAt` - * and `omnichainIndexingCursor`. - */ -export function invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect( - ctx: ParsePayload, -) { - const projection = ctx.value; - const { projectedAt, snapshot, worstCaseDistance } = projection; - const { omnichainSnapshot } = snapshot; - const expectedWorstCaseDistance = projectedAt - omnichainSnapshot.omnichainIndexingCursor; - - if (worstCaseDistance !== expectedWorstCaseDistance) { - ctx.issues.push({ - code: "custom", - input: projection, - message: - "`worstCaseDistance` must be the exact difference between `projectedAt` and `snapshot.omnichainIndexingCursor`.", - }); - } -} +export const makeCrossChainIndexingStatusSnapshotSchema = ( + valueLabel: string = "Cross-chain Indexing Status Snapshot", +) => + z.discriminatedUnion("strategy", [ + makeCrossChainIndexingStatusSnapshotOmnichainSchema(valueLabel), + ]); diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts new file mode 100644 index 000000000..0558caeb2 --- /dev/null +++ b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schema/realtime-indexing-status-projection.ts @@ -0,0 +1,71 @@ +/** + * All zod schemas we define must remain internal implementation details. + * We want the freedom to move away from zod in the future without impacting + * any users of the ensnode-sdk package. + * + * The only way to share Zod schemas is to re-export them from + * `./src/internal.ts` file. + */ +import { z } from "zod/v4"; +import type { ParsePayload } from "zod/v4/core"; + +import { makeDurationSchema, makeUnixTimestampSchema } from "../../../shared/zod-schemas"; +import type { RealtimeIndexingStatusProjection } from "../realtime-indexing-status-projection"; +import { makeCrossChainIndexingStatusSnapshotSchema } from "./cross-chain-indexing-status-snapshot"; + +/** + * Invariant: For realtime indexing status projection, + * `projectedAt` is after or same as `snapshot.snapshotTime`. + */ +export function invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime( + ctx: ParsePayload, +) { + const projection = ctx.value; + + const { snapshot, projectedAt } = projection; + + if (snapshot.snapshotTime > projectedAt) { + ctx.issues.push({ + code: "custom", + input: projection, + message: "`projectedAt` must be after or same as `snapshot.snapshotTime`.", + }); + } +} + +/** + * Invariant: For realtime indexing status projection, + * `worstCaseDistance` is the difference between `projectedAt` + * and `snapshot.slowestChainIndexingCursor`. + */ +export function invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect( + ctx: ParsePayload, +) { + const projection = ctx.value; + const { projectedAt, snapshot, worstCaseDistance } = projection; + const expectedWorstCaseDistance = projectedAt - snapshot.slowestChainIndexingCursor; + + if (worstCaseDistance !== expectedWorstCaseDistance) { + ctx.issues.push({ + code: "custom", + input: projection, + message: + "`worstCaseDistance` must be the exact difference between `projectedAt` and `snapshot.slowestChainIndexingCursor`.", + }); + } +} + +/** + * Makes Zod schema for {@link RealtimeIndexingStatusProjection} + */ +export const makeRealtimeIndexingStatusProjectionSchema = ( + valueLabel: string = "Realtime Indexing Status Projection", +) => + z + .strictObject({ + projectedAt: makeUnixTimestampSchema(valueLabel), + worstCaseDistance: makeDurationSchema(valueLabel), + snapshot: makeCrossChainIndexingStatusSnapshotSchema(valueLabel), + }) + .check(invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime) + .check(invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect); diff --git a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts b/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts deleted file mode 100644 index a93463abb..000000000 --- a/packages/ensnode-sdk/src/ensindexer/indexing-status/zod-schemas.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * All zod schemas we define must remain internal implementation details. - * We want the freedom to move away from zod in the future without impacting - * any users of the ensnode-sdk package. - * - * The only way to share Zod schemas is to re-export them from - * `./src/internal.ts` file. - */ -import { z } from "zod/v4"; - -import { makeDurationSchema, makeUnixTimestampSchema } from "../../shared/zod-schemas"; -import { CrossChainIndexingStatusSnapshotOmnichain, CrossChainIndexingStrategyIds } from "./types"; -import { - invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime, - invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect, - invariant_slowestChainEqualsToOmnichainSnapshotTime, - invariant_snapshotTimeIsTheHighestKnownBlockTimestamp, -} from "./validations"; -import { makeOmnichainIndexingStatusSnapshotSchema } from "./zod-schema/omnichain-indexing-status-snapshot"; - -/** - * Makes Zod schema for {@link CrossChainIndexingStatusSnapshotOmnichain} - */ -const makeCrossChainIndexingStatusSnapshotOmnichainSchema = ( - valueLabel: string = "Cross-chain Indexing Status Snapshot Omnichain", -) => - z - .strictObject({ - strategy: z.literal(CrossChainIndexingStrategyIds.Omnichain), - slowestChainIndexingCursor: makeUnixTimestampSchema(valueLabel), - snapshotTime: makeUnixTimestampSchema(valueLabel), - omnichainSnapshot: makeOmnichainIndexingStatusSnapshotSchema(valueLabel), - }) - .check(invariant_slowestChainEqualsToOmnichainSnapshotTime) - .check(invariant_snapshotTimeIsTheHighestKnownBlockTimestamp); - -/** - * Makes Zod schema for {@link CrossChainIndexingStatusSnapshot} - */ -export const makeCrossChainIndexingStatusSnapshotSchema = ( - valueLabel: string = "Cross-chain Indexing Status Snapshot", -) => - z.discriminatedUnion("strategy", [ - makeCrossChainIndexingStatusSnapshotOmnichainSchema(valueLabel), - ]); - -/** - * Makes Zod schema for {@link RealtimeIndexingStatusProjection} - */ -export const makeRealtimeIndexingStatusProjectionSchema = ( - valueLabel: string = "Realtime Indexing Status Projection", -) => - z - .strictObject({ - projectedAt: makeUnixTimestampSchema(valueLabel), - worstCaseDistance: makeDurationSchema(valueLabel), - snapshot: makeCrossChainIndexingStatusSnapshotSchema(valueLabel), - }) - .check(invariant_realtimeIndexingStatusProjectionProjectedAtIsAfterOrEqualToSnapshotTime) - .check(invariant_realtimeIndexingStatusProjectionWorstCaseDistanceIsCorrect); diff --git a/packages/ensnode-sdk/src/internal.ts b/packages/ensnode-sdk/src/internal.ts index 7352f9a05..da2b4a166 100644 --- a/packages/ensnode-sdk/src/internal.ts +++ b/packages/ensnode-sdk/src/internal.ts @@ -20,7 +20,6 @@ export * from "./api/shared/errors/zod-schemas"; export * from "./api/shared/pagination/zod-schemas"; export * from "./ensapi/config/zod-schemas"; export * from "./ensindexer/config/zod-schemas"; -export * from "./ensindexer/indexing-status/zod-schemas"; export * from "./registrars/zod-schemas"; export * from "./rpc"; export * from "./shared/config/build-rpc-urls";