Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2e5c6a6
Move `ensdb` module contents from ENSNode SDK to ENSDb SDK
tk-o Mar 18, 2026
d3329fd
Add `EnsNodeDbMigrations` interface to ENSDb SDK
tk-o Mar 18, 2026
a844af0
Introduce ENSDb Reader
tk-o Mar 19, 2026
316fe67
Introduce ENSDb Writer
tk-o Mar 19, 2026
90ff103
Setup exports fro ENSDb client module
tk-o Mar 19, 2026
72e1f9b
Replace ENSDb Client implementation in ENSIndexer
tk-o Mar 19, 2026
2df0ab2
Make ENSIndexer to execute ENSNode Schema migrations in ENSDb
tk-o Mar 19, 2026
8357285
Update tests for ENSDb Writer Worker
tk-o Mar 19, 2026
b66c443
docs(changeset): Moved `ensdb` module from ENSNode SDK into ENSDb SDK.
tk-o Mar 19, 2026
187a3b5
docs(changeset): Introduced two client implementations for ENSDb: `En…
tk-o Mar 19, 2026
bcc1784
docs(changeset): Replaced a bespoke `EnsDbClient` implementation with…
tk-o Mar 19, 2026
6c82714
docs(changeset): Added running database migrations for ENSDb as a res…
tk-o Mar 19, 2026
8edabdd
Code cleanup
tk-o Mar 19, 2026
24f2d1b
Introduce testing suite to ENSDb SDK
tk-o Mar 19, 2026
45eea13
Merge remote-tracking branch 'origin/main' into feat/integrate-ensdb-…
tk-o Mar 19, 2026
4b20c50
Fix typo
tk-o Mar 19, 2026
4e216f4
Apply AI PR feedback
tk-o Mar 19, 2026
3b3882b
Merge remote-tracking branch 'origin/main' into feat/integrate-ensdb-…
tk-o Mar 19, 2026
83cf6cf
Apply suggestions from code review
tk-o Mar 20, 2026
f03192e
Apply PR feedback
tk-o Mar 20, 2026
f0c7f1f
Improve layers of responsibility in ENSDb Drizzle Client file
tk-o Mar 20, 2026
54301fc
Improve layers of responsibility in ENSDb SDK clients
tk-o Mar 20, 2026
a3dbb6b
Update testing suite for ENSDb SDK
tk-o Mar 20, 2026
9eeb935
Update docs in `ponder.schema.ts` file
tk-o Mar 20, 2026
0534ef3
Update packages/ensdb-sdk/src/client/ensdb-reader.test.ts
tk-o Mar 20, 2026
132eca8
Rename `ensindexer` file to `ensindexer-abstract` in ENSDb SDK
tk-o Mar 20, 2026
e527292
Improve the method for cloning drizzle table objects
tk-o Mar 20, 2026
121efd5
Allow using individual ENSDb Schema defintions for Drizzle querires
tk-o Mar 20, 2026
6145e50
Update test files
tk-o Mar 20, 2026
d5f7c3f
Handle database migrations execution failure
tk-o Mar 20, 2026
c5397bc
Rename `ensDbWriter` consts to `ensDbClient`
tk-o Mar 21, 2026
5ac0060
Make usage of ENSIndexer Schema explicitly "concrete"
tk-o Mar 21, 2026
593cbb8
Make `drizzle` module abstractions from ENSDb SDK not to be shared pu…
tk-o Mar 21, 2026
ba48daa
Make `EnsDbReader` constructor to build Drizzle client
tk-o Mar 21, 2026
0adcd14
Update test files
tk-o Mar 21, 2026
219b7bd
fix npm audit issues
tk-o Mar 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
12 changes: 4 additions & 8 deletions apps/ensindexer/ponder/ponder.schema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
/**
* Export database schema definition for ENSIndexer
* Note: Ponder uses `globalThis.PONDER_NAMESPACE_BUILD.schema` value to
* dynamically build the "concrete" ENSIndexer Schema definition
* from the "abstract" ENSIndexer Schema definition for Ponder app to use.
*
* @see https://github.com/ponder-sh/ponder/blob/c8f6935fb65176c01b40cae9056be704c0e5318e/packages/core/src/build/index.ts#L380-L424
* @see https://github.com/ponder-sh/ponder/blob/6fcc15d4234e43862cb6e21c05f3c57f4c2f7464/packages/core/src/drizzle/onchain.ts#L280-L281
* We re-export (just) the "abstract" ENSIndexer Schema from ENSDb for Ponder to manage.
* Ponder will internally build a "concrete" ENSIndexer Schema using
* the "abstract" ENSIndexer Schema and the ENSIndexer Schema name.
**/
export * from "@ensnode/ensdb-sdk/ensindexer";
export * from "@ensnode/ensdb-sdk/ensindexer-abstract";
17 changes: 10 additions & 7 deletions apps/ensindexer/ponder/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ import { startEnsDbWriterWorker } from "@/lib/ensdb-writer-worker/singleton";

import ensNodeApi from "./handlers/ensnode-api";

// The entry point for the ENSDb Writer Worker. It must be placed inside
// the `api` directory of the Ponder app to avoid the following build issue:
// Error: Invalid dependency graph. Config, schema, and indexing function files
// cannot import objects from the API function file "src/api/index.ts".
// Before starting the ENSDb Writer Worker, we need to ensure that
// the ENSNode Schema in ENSDb is up to date by running any pending migrations.
migrateEnsNodeSchema().then(() => {
// The entry point for the ENSDb Writer Worker. It must be placed inside
// the `api` directory of the Ponder app to avoid the following build issue:
// Error: Invalid dependency graph. Config, schema, and indexing function files
// cannot import objects from the API function file "src/api/index.ts".
startEnsDbWriterWorker();
});
migrateEnsNodeSchema()
.then(startEnsDbWriterWorker)
.catch((error) => {
console.error("Failed to migrate ENSNode Schema — ", error);
process.exit(1);
});

const app = new Hono();

Comment thread
tk-o marked this conversation as resolved.
Expand Down
17 changes: 14 additions & 3 deletions apps/ensindexer/src/lib/ensdb/singleton.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import config from "@/config";

import { buildEnsDbDrizzleClient, buildEnsDbSchema, EnsDbWriter } from "@ensnode/ensdb-sdk";
import {
buildEnsDbDrizzleClient,
buildEnsDbSchema,
buildIndividualEnsDbSchemas,
EnsDbWriter,
} from "@ensnode/ensdb-sdk";

const { databaseUrl: ensDbConnectionString, databaseSchemaName: ensIndexerSchemaName } = config;

const { ensIndexerSchema, ensNodeSchema } = buildIndividualEnsDbSchemas(ensIndexerSchemaName);
/**
* Build a ENSDb Schema for Drizzle client using the ENSIndexer Schema name from config.
*/
const ensDbSchema = buildEnsDbSchema(ensIndexerSchemaName);
const ensDbSchema = buildEnsDbSchema(ensIndexerSchema);
const ensDbDrizzleClient = buildEnsDbDrizzleClient(ensDbConnectionString, ensDbSchema);

/**
* Singleton instance of ENSDbWriter for the ENSIndexer application.
*/
export const ensDbWriter = new EnsDbWriter(ensDbDrizzleClient, ensDbSchema, ensIndexerSchemaName);
export const ensDbWriter = new EnsDbWriter(
Comment thread
tk-o marked this conversation as resolved.
Outdated
ensDbDrizzleClient,
ensIndexerSchema,
ensIndexerSchemaName,
ensNodeSchema,
);
12 changes: 6 additions & 6 deletions packages/ensdb-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
],
"exports": {
".": "./src/index.ts",
"./ensindexer": "./src/ensindexer/index.ts",
"./ensindexer-abstract": "./src/ensindexer-abstract/index.ts",
"./ensnode": "./src/ensnode/index.ts"
},
"files": [
Expand All @@ -33,10 +33,10 @@
"default": "./dist/index.js"
}
},
"./ensindexer": {
"./ensindexer-abstract": {
"import": {
"types": "./dist/ensindexer/index.d.ts",
"default": "./dist/ensindexer/index.js"
"types": "./dist/ensindexer-abstract/index.d.ts",
"default": "./dist/ensindexer-abstract/index.js"
}
},
"./ensnode": {
Expand All @@ -59,13 +59,13 @@
"drizzle-kit:generate": "drizzle-kit generate"
},
"peerDependencies": {
"@ensnode/ensnode-sdk": "workspace:",
"@ensnode/ensnode-sdk": "workspace:*",
"drizzle-orm": "catalog:",
"ponder": "catalog:",
"viem": "catalog:"
Comment thread
tk-o marked this conversation as resolved.
},
"devDependencies": {
"@ensnode/ensnode-sdk": "workspace:",
"@ensnode/ensnode-sdk": "workspace:*",
"@ensnode/shared-configs": "workspace:*",
"drizzle-kit": "0.31.10",
"drizzle-orm": "catalog:",
Expand Down
14 changes: 11 additions & 3 deletions packages/ensdb-sdk/src/client/ensdb-reader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,28 @@ import {
serializeEnsIndexerPublicConfig,
} from "@ensnode/ensnode-sdk";

import * as ensIndexerSchema from "../ensindexer-abstract";
Comment thread
tk-o marked this conversation as resolved.
Outdated
import * as ensNodeSchema from "../ensnode";
import * as ensDbClientMock from "./ensdb-client.mock";
import { EnsDbReader } from "./ensdb-reader";

const ensIndexerSchemaMock = ensIndexerSchema as any;
Comment thread
tk-o marked this conversation as resolved.
Outdated
const ensNodeSchemaMock = ensNodeSchema as any;

describe("EnsDbReader", () => {
const selectResult = { current: [] as Array<{ value: unknown }> };
const whereMock = vi.fn(async () => selectResult.current);
const fromMock = vi.fn(() => ({ where: whereMock }));
const selectMock = vi.fn(() => ({ from: fromMock }));
const drizzleClientMock = { select: selectMock } as any;
const schemaMock = ensNodeSchema as any;

const createEnsDbReader = () =>
new EnsDbReader(drizzleClientMock, schemaMock, ensDbClientMock.ensIndexerSchemaName);
new EnsDbReader(
drizzleClientMock,
ensIndexerSchemaMock,
ensDbClientMock.ensIndexerSchemaName,
ensNodeSchemaMock,
);

beforeEach(() => {
selectResult.current = [];
Expand All @@ -32,7 +40,7 @@ describe("EnsDbReader", () => {
await expect(createEnsDbReader().getEnsDbVersion()).resolves.toBeUndefined();

expect(selectMock).toHaveBeenCalledTimes(1);
expect(fromMock).toHaveBeenCalledWith(schemaMock.metadata);
expect(fromMock).toHaveBeenCalledWith(ensNodeSchemaMock.metadata);
});

it("returns value when one record exists", async () => {
Expand Down
39 changes: 27 additions & 12 deletions packages/ensdb-sdk/src/client/ensdb-reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
type EnsIndexerPublicConfig,
} from "@ensnode/ensnode-sdk";

import type { AbstractEnsIndexerSchema, EnsDbDrizzleClient, EnsDbSchema } from "../lib/drizzle";
import type { AbstractEnsIndexerSchema, EnsDbDrizzleClient, EnsNodeSchema } from "../lib/drizzle";
import { EnsNodeMetadataKeys } from "./ensnode-metadata";
import type {
SerializedEnsNodeMetadata,
Expand Down Expand Up @@ -38,12 +38,12 @@ export class EnsDbReader<
protected drizzleClient: EnsDbDrizzleClient<EnsIndexerSchemaType>;

/**
* ENSDb Schema definition for ENSDb.
* "Concrete" ENSIndexer Schema definition for ENSDb.
*
* This is the "concrete" ENSDb Schema in which tables reference
* This is the "concrete" ENSIndexer Schema in which tables reference
* the ENSIndexer Schema name from {@link ensIndexerSchemaName}.
*/
protected ensDbSchema: EnsDbSchema<EnsIndexerSchemaType>;
protected _ensIndexerSchema: EnsIndexerSchemaType;
Comment thread
tk-o marked this conversation as resolved.
Outdated

/**
* The name of the ENSIndexer schema to read from in ENSDb.
Expand All @@ -53,19 +53,24 @@ export class EnsDbReader<
*/
protected ensIndexerSchemaName: string;

protected _ensNodeSchema: EnsNodeSchema;

/**
* @param ensDbDrizzleClient Drizzle client for ENSDb, typed with the "concrete" ENSIndexer Schema type.
* @param ensDbSchema ENSDb Schema definition for ENSDb used by the Drizzle client.
* @param ensIndexerSchemaName The name of the ENSIndexer schema to read from in ENSDb, used to identify which ENSNode metadata records to read.
* @param ensNodeSchema The ENSNode Schema definition for ENSDb used by the Drizzle client.
*/
constructor(
ensDbDrizzleClient: EnsDbDrizzleClient<EnsIndexerSchemaType>,
ensDbSchema: EnsDbSchema<EnsIndexerSchemaType>,
ensIndexerSchema: EnsIndexerSchemaType,
ensIndexerSchemaName: string,
ensNodeSchema: EnsNodeSchema,
) {
this.drizzleClient = ensDbDrizzleClient;
this.ensDbSchema = ensDbSchema;
this._ensIndexerSchema = ensIndexerSchema;
this.ensIndexerSchemaName = ensIndexerSchemaName;
this._ensNodeSchema = ensNodeSchema;
}
Comment thread
tk-o marked this conversation as resolved.
Comment thread
tk-o marked this conversation as resolved.

/**
Expand All @@ -78,13 +83,23 @@ export class EnsDbReader<
}

/**
* Getter for the ENSDb Schema definition used in the Drizzle client
* Getter for the "concrete" ENSIndexer Schema definition used in the Drizzle client
* for ENSDb instance.
*
* Useful while working on complex queries for ENSDb.
*/
get ensIndexerSchema(): EnsIndexerSchemaType {
return this._ensIndexerSchema;
}

/**
* Getter for the ENSNode Schema definition used in the Drizzle client
* for ENSDb instance.
*
* Useful while working on complex queries for ENSDb.
*/
get schema(): EnsDbSchema<EnsIndexerSchemaType> {
return this.ensDbSchema;
get ensNodeSchema(): EnsNodeSchema {
return this._ensNodeSchema;
}

/**
Expand Down Expand Up @@ -149,11 +164,11 @@ export class EnsDbReader<
): Promise<EnsNodeMetadataType["value"] | undefined> {
const result = await this.drizzleClient
.select()
.from(this.ensDbSchema.metadata)
.from(this.ensNodeSchema.metadata)
.where(
and(
eq(this.ensDbSchema.metadata.ensIndexerSchemaName, this.ensIndexerSchemaName),
eq(this.ensDbSchema.metadata.key, metadata.key),
eq(this.ensNodeSchema.metadata.ensIndexerSchemaName, this.ensIndexerSchemaName),
eq(this.ensNodeSchema.metadata.key, metadata.key),
),
);

Expand Down
15 changes: 11 additions & 4 deletions packages/ensdb-sdk/src/client/ensdb-writer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
serializeEnsIndexerPublicConfig,
} from "@ensnode/ensnode-sdk";

import * as ensIndexerSchema from "../ensindexer-abstract";
import * as ensNodeSchema from "../ensnode";
import * as ensDbClientMock from "./ensdb-client.mock";
import { EnsDbWriter } from "./ensdb-writer";
Expand All @@ -19,10 +20,16 @@ describe("EnsDbWriter", () => {
const valuesMock = vi.fn(() => ({ onConflictDoUpdate: onConflictDoUpdateMock }));
const insertMock = vi.fn(() => ({ values: valuesMock }));
const drizzleClientMock = { insert: insertMock } as any;
const schemaMock = ensNodeSchema as any;
const ensIndexerSchemaMock = ensIndexerSchema as any;
const ensNodeSchemaMock = ensNodeSchema as any;

const createEnsDbWriter = () =>
new EnsDbWriter(drizzleClientMock, schemaMock, ensDbClientMock.ensIndexerSchemaName);
new EnsDbWriter(
drizzleClientMock,
ensIndexerSchemaMock,
ensDbClientMock.ensIndexerSchemaName,
ensNodeSchemaMock,
);

beforeEach(() => {
onConflictDoUpdateMock.mockClear();
Expand All @@ -35,14 +42,14 @@ describe("EnsDbWriter", () => {
it("writes the database version metadata", async () => {
await createEnsDbWriter().upsertEnsDbVersion("0.2.0");

expect(insertMock).toHaveBeenCalledWith(schemaMock.metadata);
expect(insertMock).toHaveBeenCalledWith(ensNodeSchemaMock.metadata);
expect(valuesMock).toHaveBeenCalledWith({
ensIndexerSchemaName: ensDbClientMock.ensIndexerSchemaName,
key: EnsNodeMetadataKeys.EnsDbVersion,
value: "0.2.0",
});
expect(onConflictDoUpdateMock).toHaveBeenCalledWith({
target: [schemaMock.metadata.ensIndexerSchemaName, schemaMock.metadata.key],
target: [ensNodeSchemaMock.metadata.ensIndexerSchemaName, ensNodeSchemaMock.metadata.key],
set: { value: "0.2.0" },
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/ensdb-sdk/src/client/ensdb-writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ export class EnsDbWriter extends EnsDbReader {
*/
private async upsertEnsNodeMetadata(metadata: SerializedEnsNodeMetadata): Promise<void> {
await this.drizzleClient
.insert(this.ensDbSchema.metadata)
.insert(this.ensNodeSchema.metadata)
.values({
ensIndexerSchemaName: this.ensIndexerSchemaName,
key: metadata.key,
value: metadata.value,
})
.onConflictDoUpdate({
target: [this.ensDbSchema.metadata.ensIndexerSchemaName, this.ensDbSchema.metadata.key],
target: [this.ensNodeSchema.metadata.ensIndexerSchemaName, this.ensNodeSchema.metadata.key],
set: { value: metadata.value },
});
}
Comment thread
tk-o marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/**
* Merge the various sub-schemas into ENSIndexer Schema.
* Merge the various sub-schemas into an "abstract" ENSIndexer Schema.
* This "abstract" ENSIndexer Schema is used to build the "concrete" ENSIndexer Schema
* for ENSDb, which is then used to build the ENSDb Schema for a Drizzle client for ENSDb.
*/

// TODO: remove `ensnode-metadata.schema` export when database migrations
Expand Down
2 changes: 1 addition & 1 deletion packages/ensdb-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./client";
export * from "./ensindexer";
export * from "./ensindexer-abstract";
export * from "./lib/drizzle";
Loading
Loading