Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1f1e3df
Update ENSApi Indexing Status API data model
tk-o Feb 24, 2026
e518a43
Update HTTP handlers for ENSApi
tk-o Feb 24, 2026
ac26750
Update Indexing Status cache for ENSApi
tk-o Feb 24, 2026
55414e5
Rename `ENSNodeProvider*` types
tk-o Feb 24, 2026
2557708
Rename `useENSNodeSDKConfig` to `useEnsApiProviderOptions`
tk-o Feb 24, 2026
da220f3
Remove `useENSNodeConfig` hook
tk-o Feb 24, 2026
098ff6e
Update `useResolvedIdentity` to require `namespace` param
tk-o Feb 24, 2026
f7603b8
Update ENSNode hooks to apply renamed functions and types
tk-o Feb 24, 2026
1d45940
Update `useIndexingStatusWithSwr` hook to also cache `config: EnsApiP…
tk-o Feb 24, 2026
ba81822
Create `useEnsApiConfig` for conveniet use of `EnsApiPublicConfig` value
tk-o Feb 24, 2026
72d7ae7
Replace applications of removed `useENSNodeConfig` hook with `useEnsA…
tk-o Feb 24, 2026
c3a1579
Reduce use of `useENSNodeConfig` (removed) and `useIndexingStatusWit…
tk-o Feb 24, 2026
352be15
Replace applications of removed `SelectedENSNodeProvider` with `Selec…
tk-o Feb 24, 2026
f88cc15
Merge remote-tracking branch 'origin/main' into ensapi-merge-config-w…
tk-o Mar 2, 2026
709443d
Fix references to updated ENSApi response data model
tk-o Mar 2, 2026
b768b3d
Fix references to updated ENSIndexer response data model
tk-o Mar 2, 2026
2a2b40f
Fix references to updated Indexing Status data model in ENSAdmin
tk-o Mar 2, 2026
5edb862
Remove config route definition from ENSApi routes
tk-o Mar 2, 2026
073f642
Apply AI PR feedback
tk-o Mar 2, 2026
078f7ef
Merge remote-tracking branch 'origin/main' into ensapi-merge-config-w…
tk-o Apr 13, 2026
e652446
Update OpenAPI Spec
tk-o Apr 13, 2026
02daa2e
Merge remote-tracking branch 'origin/main' into ensapi-merge-config-w…
tk-o Apr 17, 2026
c532285
Apply AI PR feedback
tk-o Apr 17, 2026
4d0703b
Fix conditional logic in react hooks usage
tk-o Apr 17, 2026
acf4e6b
Apply AI PR feedback
tk-o Apr 17, 2026
fa06ece
Rename `EnsApiContext` to `EnsNodeContext` and `*EnsApiProvider*` to …
tk-o Apr 17, 2026
f4e4d56
docs(changeset): Fixed ENS Namespace option for calling `useResolvedI…
tk-o Apr 17, 2026
15f314c
Rename `config` field to `ensApiPublicConfig`
tk-o Apr 17, 2026
a814284
Remove the `ConfigResponse` data model
tk-o Apr 17, 2026
839bb4c
Fix mocked data
tk-o Apr 17, 2026
26a2ff0
Update OpenAPI Spec
tk-o Apr 17, 2026
cdc1736
docs(changeset): Replaced the `EnsApiConfigResponse` data model by ad…
tk-o Apr 17, 2026
6f1d363
docs(changeset): **Breaking**: Replaced the `config()` method in the …
tk-o Apr 17, 2026
288af77
docs(changeset): **Breaking**: Replaced `useENSNodeSDKConfig` hook wi…
tk-o Apr 17, 2026
1ac516c
docs(changeset): **Breaking**: Removed `useENSNodeConfig` hook.
tk-o Apr 17, 2026
dc147d0
docs(changeset): **Breaking**: Removed Config API endpoint at `GET /a…
tk-o Apr 17, 2026
2ce61f8
docs(changeset): Replaced the `useENSNodeConfig` with `useEnsApiPubli…
tk-o Apr 17, 2026
0e7bd2a
Introduce `EnsNodeStackInfo` data model to ENSNode SDK
tk-o Apr 17, 2026
ccc497e
docs(changeset): Introduced `EnsNodeStackInfo` data model.
tk-o Apr 17, 2026
cc131f4
Extend `EnsDbReader` class with `buildEnsDbPublicConfig()` method
tk-o Apr 17, 2026
4658d53
Rename symbols exported from ENSNode React package
tk-o Apr 17, 2026
2eb9070
Return `stackInfo` field from Indexing Status API endpoint
tk-o Apr 17, 2026
774a105
Replace `useEnsApiPublicConfig` hook with `useEnsNodeStackInfo` hook
tk-o Apr 17, 2026
ac94d42
Rename `ensApiPublicConfig` field integrations with `stackInfo` field
tk-o Apr 17, 2026
8086ad9
Update mocks
tk-o Apr 17, 2026
3ca4b1a
Update changesets
tk-o Apr 17, 2026
94cde85
Merge remote-tracking branch 'origin/main' into ensapi-merge-config-w…
tk-o Apr 17, 2026
3aad5fd
Move `useEnsNodeStackInfo` hook into the hooks dir
tk-o Apr 17, 2026
8911aac
Rename `EnsApiClient*` symbols to `EnsNodeClient*`
tk-o Apr 17, 2026
2e8a476
Apply AI PR feedback
tk-o Apr 17, 2026
7132611
Update `SerializedEnsNodeStackInfo` data model
tk-o Apr 21, 2026
446be2e
Add `versionInfo` field to `EnsDbPublicConfig` data model
tk-o Apr 21, 2026
940b401
Apply JSDoc updates
tk-o Apr 21, 2026
74685bd
Fix OpenAPI Spec output for `highestLabelSetVersion` field
tk-o Apr 21, 2026
d15efea
Merge remote-tracking branch 'origin/main' into ensapi-merge-config-w…
tk-o Apr 21, 2026
ec4d4a5
Cleanup teminology around "ENSNode Client"
tk-o Apr 21, 2026
ac957d5
Add tests for parsing pg version info string
tk-o Apr 21, 2026
87523e1
Rename `useActiveENSNodeConfig` hook to `useActiveEnsNodeStackInfo`
tk-o Apr 21, 2026
1cde26a
Merge remote-tracking branch 'origin/main' into ensapi-merge-config-w…
tk-o Apr 21, 2026
23495d0
Apply AI PR feedback
tk-o Apr 21, 2026
b2f37cf
Apply PR feedback
tk-o Apr 21, 2026
5913d2d
Refine examples for `EnsNodeClient`
tk-o Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions apps/ensadmin/src/app/mock/config-api.mock.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { deserializeENSIndexerPublicConfig } from "@ensnode/ensnode-sdk";
import {
deserializeENSIndexerPublicConfig,
Comment thread
tk-o marked this conversation as resolved.
Outdated
SerializedEnsApiConfigResponse,
SerializedEnsIndexerConfigResponse,
} from "@ensnode/ensnode-sdk";

export const ensIndexerPublicConfig = deserializeENSIndexerPublicConfig({
const serializedEnsIndexerPublicConfig = {
labelSet: {
labelSetId: "subgraph",
labelSetVersion: 0,
Expand All @@ -27,4 +31,17 @@ export const ensIndexerPublicConfig = deserializeENSIndexerPublicConfig({
ensRainbowSchema: 3,
ensNormalize: "1.11.1",
},
});
} satisfies SerializedEnsIndexerConfigResponse;

export const ensIndexerPublicConfig = deserializeENSIndexerPublicConfig(
serializedEnsIndexerPublicConfig,
);

export const serializedEnsApiPublicConfig = {
ensIndexerPublicConfig: serializedEnsIndexerPublicConfig,
theGraphFallback: {
canFallback: true,
url: "https://api.thegraph.com/subgraphs/name/ensdomains/ens",
},
version: "0.35.0",
} satisfies SerializedEnsApiConfigResponse;
15 changes: 10 additions & 5 deletions apps/ensadmin/src/app/mock/indexing-stats/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useEffect, useState } from "react";
import {
CrossChainIndexingStatusSnapshot,
createRealtimeIndexingStatusProjection,
EnsApiIndexingStatusResponseOk,
IndexingStatusResponseCodes,
IndexingStatusResponseOk,
OmnichainIndexingStatusIds,
Expand Down Expand Up @@ -37,7 +38,7 @@ let loadingTimeoutId: number;

async function fetchMockedIndexingStatus(
selectedVariant: Variant,
): Promise<CrossChainIndexingStatusSnapshot> {
): Promise<EnsApiIndexingStatusResponseOk> {
// always try clearing loading timeout when performing a mocked fetch
// this way we get a fresh and very long request to observe the loading state
if (loadingTimeoutId) {
Expand All @@ -53,14 +54,14 @@ async function fetchMockedIndexingStatus(
selectedVariant
] as IndexingStatusResponseOk;

return response.realtimeProjection.snapshot;
return response;
}
case "Error ResponseCode":
throw new Error(
"Received Indexing Status response with responseCode other than 'ok' which will not be cached.",
);
case "Loading":
return new Promise<CrossChainIndexingStatusSnapshot>((_resolve, reject) => {
return new Promise<EnsApiIndexingStatusResponseOk>((_resolve, reject) => {
loadingTimeoutId = +setTimeout(reject, 5 * 60 * 1_000);
});
case "Loading Error":
Expand All @@ -77,10 +78,14 @@ export default function MockIndexingStatusPage() {
const mockedIndexingStatus = useQuery({
queryKey: ["mock", "useIndexingStatus", selectedVariant],
queryFn: () => fetchMockedIndexingStatus(selectedVariant),
select: (cachedSnapshot) => {
select: (response) => {
return {
responseCode: IndexingStatusResponseCodes.Ok,
realtimeProjection: createRealtimeIndexingStatusProjection(cachedSnapshot, now),
realtimeProjection: createRealtimeIndexingStatusProjection(
response.realtimeProjection.snapshot,
now,
),
config: response.config,
} satisfies IndexingStatusResponseOk;
},
retry: false, // allows loading error to be observed immediately
Expand Down
6 changes: 6 additions & 0 deletions apps/ensadmin/src/app/mock/indexing-status-api.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
type SerializedOmnichainIndexingStatusSnapshotUnstarted,
} from "@ensnode/ensnode-sdk";

import { serializedEnsApiPublicConfig } from "@/app/mock/config-api.mock";

export const indexingStatusResponseError: IndexingStatusResponseError = {
responseCode: IndexingStatusResponseCodes.Error,
};
Expand Down Expand Up @@ -85,6 +87,7 @@ export const indexingStatusResponseOkOmnichain: Record<
} satisfies SerializedOmnichainIndexingStatusSnapshotUnstarted,
},
},
config: serializedEnsApiPublicConfig,
}),

[OmnichainIndexingStatusIds.Backfill]: deserializeIndexingStatusResponse({
Expand Down Expand Up @@ -163,6 +166,7 @@ export const indexingStatusResponseOkOmnichain: Record<
} satisfies SerializedOmnichainIndexingStatusSnapshotBackfill,
},
},
config: serializedEnsApiPublicConfig,
}),

[OmnichainIndexingStatusIds.Following]: deserializeIndexingStatusResponse({
Expand Down Expand Up @@ -256,6 +260,7 @@ export const indexingStatusResponseOkOmnichain: Record<
} satisfies SerializedOmnichainIndexingStatusSnapshotFollowing,
},
},
config: serializedEnsApiPublicConfig,
}),

[OmnichainIndexingStatusIds.Completed]: deserializeIndexingStatusResponse({
Expand Down Expand Up @@ -293,5 +298,6 @@ export const indexingStatusResponseOkOmnichain: Record<
} satisfies SerializedOmnichainIndexingStatusSnapshotCompleted,
},
},
config: serializedEnsApiPublicConfig,
}),
};
16 changes: 16 additions & 0 deletions apps/ensadmin/src/components/config/useEnsApiConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useQuery } from "@tanstack/react-query";

import { useEnsApiProviderOptions } from "@ensnode/ensnode-react";

import { useIndexingStatusWithSwr } from "@/components/indexing-status";

export function useEnsApiConfig() {
Comment thread
tk-o marked this conversation as resolved.
Outdated
const ensApiProviderOptions = useEnsApiProviderOptions();
const indexingStatus = useIndexingStatusWithSwr();

return useQuery({
enabled: indexingStatus.isSuccess,
queryKey: ["swr", ensApiProviderOptions.client.url.href, "config"],
queryFn: async () => indexingStatus.data?.config, // enabled flag ensures this is only called when indexingStatus.data is available
Comment thread
tk-o marked this conversation as resolved.
Outdated
Comment thread
vercel[bot] marked this conversation as resolved.
Outdated
});
}
42 changes: 22 additions & 20 deletions apps/ensadmin/src/components/connection/cards/ensnode-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { ChainIcon, getChainName } from "@namehash/namehash-ui";
import { History, Replace } from "lucide-react";
import { Fragment, ReactNode } from "react";

import { useENSNodeConfig } from "@ensnode/ensnode-react";
import { type ENSApiPublicConfig, getENSRootChainId } from "@ensnode/ensnode-sdk";
import { type EnsApiPublicConfig, getENSRootChainId } from "@ensnode/ensnode-sdk";

import { useEnsApiConfig } from "@/components/config/useEnsApiConfig";
import { ErrorInfo, type ErrorInfoProps } from "@/components/error-info";
import { ENSApiIcon } from "@/components/icons/ensnode-apps/ensapi-icon";
import { ENSDbIcon } from "@/components/icons/ensnode-apps/ensdb-icon";
Expand Down Expand Up @@ -96,7 +96,7 @@ function ENSNodeCardLoadingSkeleton() {
* Props for ENSNodeConfigCardDisplay - display component that accepts props for testing/mocking
*/
export interface ENSNodeConfigCardDisplayProps {
ensApiPublicConfig: ENSApiPublicConfig;
ensApiPublicConfig: EnsApiPublicConfig;
}

/**
Expand All @@ -114,7 +114,7 @@ export function ENSNodeConfigCardDisplay({ ensApiPublicConfig }: ENSNodeConfigCa
* Props for ENSNodeConfigInfoView - internal component that accepts props for testing/mocking
*/
export interface ENSNodeConfigInfoViewProps {
ensApiPublicConfig?: ENSApiPublicConfig;
ensApiPublicConfig?: EnsApiPublicConfig;
error?: ErrorInfoProps;
isLoading?: boolean;
}
Expand Down Expand Up @@ -147,28 +147,30 @@ export function ENSNodeConfigInfoView({
* ENSNodeConfigInfo component - fetches and displays ENSNode configuration data
*/
export function ENSNodeConfigInfo() {
const ensNodeConfigQuery = useENSNodeConfig();
const ensApiConfig = useEnsApiConfig();

return (
<ENSNodeConfigInfoView
ensApiPublicConfig={ensNodeConfigQuery.isSuccess ? ensNodeConfigQuery.data : undefined}
error={
ensNodeConfigQuery.isError
? {
title: "ENSNodeConfigInfo Error",
description: ensNodeConfigQuery.error.message,
}
: undefined
}
isLoading={ensNodeConfigQuery.isPending}
/>
);
if (ensApiConfig.isPending) {
return <ENSNodeConfigInfoView isLoading={true} />;
}

if (ensApiConfig.isError) {
return (
<ENSNodeConfigInfoView
error={{
title: "Failed to Load ENSNode Configuration",
Comment thread
tk-o marked this conversation as resolved.
Outdated
description: ensApiConfig.error.message,
}}
/>
);
}
Comment thread
tk-o marked this conversation as resolved.
Outdated

return <ENSNodeConfigInfoView ensApiPublicConfig={ensApiConfig.data} />;
}

function ENSNodeConfigCardContent({
ensApiPublicConfig,
}: {
ensApiPublicConfig: ENSApiPublicConfig;
ensApiPublicConfig: EnsApiPublicConfig;
}) {
const cardItemValueStyles = "text-sm leading-6 font-normal text-black";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@

import type { PropsWithChildren } from "react";

import { useENSNodeConfig } from "@ensnode/ensnode-react";

import { useEnsApiConfig } from "@/components/config/useEnsApiConfig";
import { ErrorInfo } from "@/components/error-info";
import { LoadingSpinner } from "@/components/loading-spinner";

/**
* Allows consumers to use `useActiveConnection` by blocking rendering until it is available.
*/
export function RequireActiveConnection({ children }: PropsWithChildren) {
const { status, error } = useENSNodeConfig();
const ensApiConfig = useEnsApiConfig();
Comment thread
tk-o marked this conversation as resolved.
Outdated

if (status === "pending") return <Loading />;
if (ensApiConfig.status === "pending") return <Loading />;

if (status === "error") {
if (ensApiConfig.status === "error") {
return (
<section className="p-6">
<ErrorInfo title="Unable to parse ENSNode Config" description={error.message} />
<ErrorInfo title="Failed to connect to ENSApi" description={ensApiConfig.error.message} />
</section>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,75 +7,92 @@ import { useCallback, useMemo } from "react";
import {
createIndexingStatusQueryOptions,
QueryParameter,
useENSNodeSDKConfig,
useEnsApiProviderOptions,
type useIndexingStatus,
useSwrQuery,
WithSDKConfigParameter,
WithEnsApiProviderOptions,
} from "@ensnode/ensnode-react";
Comment thread
tk-o marked this conversation as resolved.
import {
CrossChainIndexingStatusSnapshotOmnichain,
createRealtimeIndexingStatusProjection,
Duration,
type IndexingStatusRequest,
IndexingStatusResponseCodes,
IndexingStatusResponseOk,
EnsApiIndexingStatusRequest,
EnsApiIndexingStatusResponseCodes,
EnsApiIndexingStatusResponseOk,
EnsApiPublicConfig,
} from "@ensnode/ensnode-sdk";
Comment thread
tk-o marked this conversation as resolved.

const DEFAULT_REFETCH_INTERVAL = secondsToMilliseconds(10);

const REALTIME_PROJECTION_REFRESH_RATE: Duration = 1;

interface CachableIndexingStatus {
Comment thread
tk-o marked this conversation as resolved.
Outdated
crossChainIndexingStatusSnapshot: CrossChainIndexingStatusSnapshotOmnichain;
config: EnsApiPublicConfig;
}

interface UseIndexingStatusParameters
extends IndexingStatusRequest,
QueryParameter<CrossChainIndexingStatusSnapshotOmnichain> {}
extends EnsApiIndexingStatusRequest,
QueryParameter<CachableIndexingStatus> {}

/**
* A proxy hook for {@link useIndexingStatus} which applies
* stale-while-revalidate cache for successful responses.
*/
export function useIndexingStatusWithSwr(
parameters: WithSDKConfigParameter & UseIndexingStatusParameters = {},
parameters: WithEnsApiProviderOptions & UseIndexingStatusParameters = {},
) {
const { config, query = {} } = parameters;
const _config = useENSNodeSDKConfig(config);
const { options, query = {} } = parameters;
const providerOptions = useEnsApiProviderOptions(options);
const now = useNow({ timeToRefresh: REALTIME_PROJECTION_REFRESH_RATE });

const queryOptions = useMemo(() => createIndexingStatusQueryOptions(_config), [_config]);
const queryOptions = useMemo(
() => createIndexingStatusQueryOptions(providerOptions),
[providerOptions],
);
const queryKey = useMemo(() => ["swr", ...queryOptions.queryKey], [queryOptions.queryKey]);
const queryFn = useCallback(
async () =>
queryOptions.queryFn().then(async (response) => {
// An indexing status response was successfully fetched,
// but the response code contained within the response was not 'ok'.
// Therefore, throw an error to avoid caching this response.
if (response.responseCode !== IndexingStatusResponseCodes.Ok) {
if (response.responseCode !== EnsApiIndexingStatusResponseCodes.Ok) {
throw new Error(
"Received Indexing Status response with responseCode other than 'ok' which will not be cached.",
);
}

// The indexing status snapshot has been fetched and successfully validated for caching.
// The indexing status snapshot, including ENSApi public config,
// has been fetched and successfully validated for caching.
Comment thread
tk-o marked this conversation as resolved.
Outdated
// Therefore, return it so that query cache for `queryOptions.queryKey` will:
// - Replace the currently cached value (if any) with this new value.
// - Return this non-null value.
return response.realtimeProjection.snapshot;
return {
crossChainIndexingStatusSnapshot: response.realtimeProjection.snapshot,
config: response.config,
} satisfies CachableIndexingStatus;
}),
[queryOptions.queryFn],
);

// Call select function to `createRealtimeIndexingStatusProjection` each time
// `now` is updated.
const select = useCallback(
(cachedSnapshot: CrossChainIndexingStatusSnapshotOmnichain): IndexingStatusResponseOk => {
const realtimeProjection = createRealtimeIndexingStatusProjection(cachedSnapshot, now);
(cachedResult: CachableIndexingStatus): EnsApiIndexingStatusResponseOk => {
const realtimeProjection = createRealtimeIndexingStatusProjection(
cachedResult.crossChainIndexingStatusSnapshot,
now,
);

// Maintain the original response shape of `IndexingStatusResponse`
// Maintain the original response shape of `EnsApiIndexingStatusResponseOk`
// for the consumers. Creating a new projection from the cached snapshot
// each time `now` is updated should be implementation detail.
return {
responseCode: IndexingStatusResponseCodes.Ok,
responseCode: EnsApiIndexingStatusResponseCodes.Ok,
realtimeProjection,
} satisfies IndexingStatusResponseOk;
config: cachedResult.config,
} satisfies EnsApiIndexingStatusResponseOk;
},
[now],
);
Expand Down
6 changes: 3 additions & 3 deletions apps/ensadmin/src/components/layout-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AppSidebar } from "@/components/app-sidebar";
import { RequireActiveConnection } from "@/components/connections/require-active-connection";
import { RequireSelectedConnection } from "@/components/connections/require-selected-connection";
import { Header, HeaderActions, HeaderBreadcrumbs, HeaderNav } from "@/components/header";
import { SelectedENSNodeProvider } from "@/components/providers/selected-ensnode-provider";
import { SelectedEnsApiProvider } from "@/components/providers/selected-ensnode-provider";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { Skeleton } from "@/components/ui/skeleton";

Expand Down Expand Up @@ -54,15 +54,15 @@ export function LayoutWrapper({
<AppSidebar />
</Suspense>
<SidebarInset className="min-w-0">
<SelectedENSNodeProvider>
<SelectedEnsApiProvider>
<Header>
<HeaderNav>
<HeaderBreadcrumbs>{breadcrumbs}</HeaderBreadcrumbs>
</HeaderNav>
<HeaderActions>{actions}</HeaderActions>
</Header>
<RequireActiveConnection>{children}</RequireActiveConnection>
</SelectedENSNodeProvider>
</SelectedEnsApiProvider>
</SidebarInset>
</SidebarProvider>
</RequireSelectedConnection>
Expand Down
Loading