@ensnode/ensdb-sdk 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,18 +4,6 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
- // src/client/ensdb-reader.ts
8
- import { and, eq } from "drizzle-orm/sql";
9
- import {
10
- deserializeCrossChainIndexingStatusSnapshot,
11
- deserializeEnsIndexerPublicConfig
12
- } from "@ensnode/ensnode-sdk";
13
-
14
- // src/lib/drizzle.ts
15
- import { drizzle } from "drizzle-orm/node-postgres";
16
- import { isPgEnum } from "drizzle-orm/pg-core";
17
- import { isTable, Table } from "drizzle-orm/table";
18
-
19
7
  // src/ensindexer-abstract/index.ts
20
8
  var ensindexer_abstract_exports = {};
21
9
  __export(ensindexer_abstract_exports, {
@@ -316,15 +304,16 @@ var registrationType = onchainEnum("RegistrationType", [
316
304
  "NameWrapper",
317
305
  "BaseRegistrar",
318
306
  "ThreeDNS",
319
- "ENSv2Registry"
307
+ "ENSv2RegistryRegistration",
308
+ "ENSv2RegistryReservation"
320
309
  ]);
321
310
  var registration = onchainTable(
322
311
  "registrations",
323
312
  (t) => ({
324
- // keyed by (domainId, index)
313
+ // keyed by (domainId, registrationIndex)
325
314
  id: t.text().primaryKey().$type(),
326
315
  domainId: t.text().notNull().$type(),
327
- index: t.integer().notNull(),
316
+ registrationIndex: t.integer().notNull(),
328
317
  // has a type
329
318
  type: registrationType().notNull(),
330
319
  // has a start
@@ -336,9 +325,11 @@ var registration = onchainTable(
336
325
  // registrar AccountId
337
326
  registrarChainId: t.integer().notNull().$type(),
338
327
  registrarAddress: t.hex().notNull().$type(),
339
- // references registrant
328
+ // may reference a registrant
340
329
  registrantId: t.hex().$type(),
341
- // may have a referrer
330
+ // may reference an unregistrant
331
+ unregistrantId: t.hex().$type(),
332
+ // may have referrer data
342
333
  referrer: t.hex().$type(),
343
334
  // may have fuses (NameWrapper, Wrapped BaseRegistrar)
344
335
  fuses: t.integer(),
@@ -353,12 +344,12 @@ var registration = onchainTable(
353
344
  eventId: t.text().notNull()
354
345
  }),
355
346
  (t) => ({
356
- byId: uniqueIndex().on(t.domainId, t.index)
347
+ byId: uniqueIndex().on(t.domainId, t.registrationIndex)
357
348
  })
358
349
  );
359
350
  var latestRegistrationIndex = onchainTable("latest_registration_indexes", (t) => ({
360
351
  domainId: t.text().primaryKey().$type(),
361
- index: t.integer().notNull()
352
+ registrationIndex: t.integer().notNull()
362
353
  }));
363
354
  var registration_relations = relations(registration, ({ one, many }) => ({
364
355
  // belongs to either v1Domain or v2Domain
@@ -376,6 +367,12 @@ var registration_relations = relations(registration, ({ one, many }) => ({
376
367
  references: [account.id],
377
368
  relationName: "registrant"
378
369
  }),
370
+ // has one unregistrant
371
+ unregistrant: one(account, {
372
+ fields: [registration.unregistrantId],
373
+ references: [account.id],
374
+ relationName: "unregistrant"
375
+ }),
379
376
  // has many renewals
380
377
  renewals: many(renewal),
381
378
  // has an event
@@ -391,7 +388,7 @@ var renewal = onchainTable(
391
388
  id: t.text().primaryKey().$type(),
392
389
  domainId: t.text().notNull().$type(),
393
390
  registrationIndex: t.integer().notNull(),
394
- index: t.integer().notNull(),
391
+ renewalIndex: t.integer().notNull(),
395
392
  // all renewals have a duration
396
393
  duration: t.bigint().notNull(),
397
394
  // may have a referrer
@@ -405,14 +402,14 @@ var renewal = onchainTable(
405
402
  eventId: t.text().notNull()
406
403
  }),
407
404
  (t) => ({
408
- byId: uniqueIndex().on(t.domainId, t.registrationIndex, t.index)
405
+ byId: uniqueIndex().on(t.domainId, t.registrationIndex, t.renewalIndex)
409
406
  })
410
407
  );
411
408
  var renewal_relations = relations(renewal, ({ one }) => ({
412
409
  // belongs to registration
413
410
  registration: one(registration, {
414
411
  fields: [renewal.domainId, renewal.registrationIndex],
415
- references: [registration.domainId, registration.index]
412
+ references: [registration.domainId, registration.registrationIndex]
416
413
  }),
417
414
  // has an event
418
415
  event: one(event, {
@@ -425,7 +422,7 @@ var latestRenewalIndex = onchainTable(
425
422
  (t) => ({
426
423
  domainId: t.text().notNull().$type(),
427
424
  registrationIndex: t.integer().notNull(),
428
- index: t.integer().notNull()
425
+ renewalIndex: t.integer().notNull()
429
426
  }),
430
427
  (t) => ({ pk: primaryKey({ columns: [t.domainId, t.registrationIndex] }) })
431
428
  );
@@ -524,13 +521,9 @@ var reverseNameRecord = onchainTable2(
524
521
  /**
525
522
  * Represents the ENSIP-19 Reverse Name Record for a given (address, coinType).
526
523
  *
527
- * The value of this field is guaranteed to be a non-empty-string normalized ENS name (see
528
- * `interpretNameRecordValue` for additional context and specific guarantees). Unnormalized
529
- * names and empty string values are interpreted as a deletion of the associated Reverse Name
530
- * Record entity (represented in the schema as the _absence_ of a relevant Reverse Name Record
531
- * entity).
524
+ * The value of this field is guaranteed to be a non-empty {@link InterpretedName}.
532
525
  */
533
- value: t.text().notNull()
526
+ value: t.text().notNull().$type()
534
527
  }),
535
528
  (t) => ({
536
529
  pk: primaryKey2({ columns: [t.address, t.coinType] })
@@ -583,14 +576,30 @@ var resolverRecords = onchainTable2(
583
576
  /**
584
577
  * Represents the value of the reverse-resolution (ENSIP-3) name() record, used for Reverse Resolution.
585
578
  *
586
- * The emitted record values are interpreted according to `interpretNameRecordValue` unnormalized
587
- * names and empty string values are interpreted as a deletion of the associated record (represented
588
- * here as `null`).
579
+ * If present, the value of this field is guaranteed to be a non-empty {@link InterpretedName}.
580
+ */
581
+ name: t.text().$type(),
582
+ /**
583
+ * ENSIP-7 contenthash raw bytes or null if not set.
584
+ */
585
+ contenthash: t.hex(),
586
+ /**
587
+ * PubkeyResolver (x, y) pair, or null if not set.
589
588
  *
590
- * If set, the value of this field is guaranteed to be a non-empty-string normalized ENS name
591
- * (see `interpretNameRecordValue` for additional context and specific guarantees).
589
+ * Invariant: both null together, or both set together.
592
590
  */
593
- name: t.text()
591
+ pubkeyX: t.hex(),
592
+ pubkeyY: t.hex(),
593
+ /**
594
+ * IDNSZoneResolver zonehash or null if not set.
595
+ */
596
+ dnszonehash: t.hex(),
597
+ /**
598
+ * IVersionableResolver version. Null when no `VersionChanged` event has been seen for this
599
+ * (chainId, address, node) — the resolver may not implement `IVersionableResolver`, or simply
600
+ * may never have been version-bumped. Consumers should treat null as "unknown" rather than 0.
601
+ */
602
+ version: t.bigint().$type()
594
603
  }),
595
604
  (t) => ({
596
605
  byId: uniqueIndex2().on(t.chainId, t.address, t.node)
@@ -668,7 +677,7 @@ var resolverTextRecordRelations = relations2(resolverTextRecord, ({ one }) => ({
668
677
  })
669
678
  }));
670
679
  var migratedNode = onchainTable2("migrated_nodes", (t) => ({
671
- node: t.hex().primaryKey()
680
+ node: t.hex().primaryKey().$type()
672
681
  }));
673
682
 
674
683
  // src/ensindexer-abstract/registrars.schema.ts
@@ -935,7 +944,7 @@ var registrarActions = onchainTable3(
935
944
  * 3) May be the "zero address" to represent that an `encodedReferrer` is
936
945
  * defined but that it is interpreted as no referrer.
937
946
  */
938
- decodedReferrer: t.hex(),
947
+ decodedReferrer: t.hex().$type(),
939
948
  /**
940
949
  * Number of the block that includes the "logical registrar action".
941
950
  *
@@ -1049,7 +1058,7 @@ var registrarActionRelations = relations3(registrarActions, ({ one }) => ({
1049
1058
  }));
1050
1059
 
1051
1060
  // src/ensindexer-abstract/subgraph.schema.ts
1052
- import { index as index3, onchainTable as onchainTable4, relations as relations4 } from "ponder";
1061
+ import { index as index3, onchainTable as onchainTable4, relations as relations4, sql as sql2 } from "ponder";
1053
1062
 
1054
1063
  // src/lib/collate.ts
1055
1064
  function monkeypatchCollate(col, collation) {
@@ -1129,9 +1138,12 @@ var subgraph_domain = onchainTable4(
1129
1138
  expiryDate: t.bigint()
1130
1139
  }),
1131
1140
  (t) => ({
1132
- // Temporarily disable the `byName` index to avoid index creation issues.
1133
- // For more details, see: https://github.com/namehash/ensnode/issues/1819
1134
- // byName: index().on(t.name),
1141
+ // uses a hash index because some name values exceed the btree max row size (8191 bytes)
1142
+ byExactName: index3().using("hash", t.name),
1143
+ // GIN trigram index for partial-match filters (_contains, _starts_with, _ends_with).
1144
+ // (inline `gin_trgm_ops` via `sql` because passing it through `.op()` gets dropped by Ponder,
1145
+ // producing `USING gin (name)` with no opclass)
1146
+ byFuzzyName: index3().using("gin", sql2`${t.name} gin_trgm_ops`),
1135
1147
  byLabelhash: index3().on(t.labelhash),
1136
1148
  byParentId: index3().on(t.parentId),
1137
1149
  byOwnerId: index3().on(t.ownerId),
@@ -1983,6 +1995,17 @@ var metadata = ENSNODE_SCHEMA.table(
1983
1995
  ]
1984
1996
  );
1985
1997
 
1998
+ // src/lib/drizzle.ts
1999
+ import { drizzle } from "drizzle-orm/node-postgres";
2000
+ import { isPgEnum } from "drizzle-orm/pg-core";
2001
+ import { isTable, Table } from "drizzle-orm/table";
2002
+
2003
+ // src/lib/checksum.ts
2004
+ import { createHash } from "crypto";
2005
+ function createChecksum(data) {
2006
+ return createHash("sha256").update(data).digest("hex").slice(0, 10);
2007
+ }
2008
+
1986
2009
  // src/lib/drizzle.ts
1987
2010
  var appliedNameForConcreteEnsIndexerSchema;
1988
2011
  function buildConcreteEnsIndexerSchema(ensIndexerSchemaName) {
@@ -2023,6 +2046,50 @@ function buildEnsDbDrizzleClient(connectionString, concreteEnsIndexerSchema, log
2023
2046
  logger
2024
2047
  });
2025
2048
  }
2049
+ function safeStringifyDrizzleSchema(schema) {
2050
+ const seen = /* @__PURE__ */ new WeakSet();
2051
+ return JSON.stringify(schema, (_key, value) => {
2052
+ if (typeof value === "bigint") return `${value}n`;
2053
+ if (typeof value === "object" && value !== null) {
2054
+ if (seen.has(value)) return "[circular]";
2055
+ seen.add(value);
2056
+ }
2057
+ return value;
2058
+ });
2059
+ }
2060
+ function getDrizzleSchemaChecksum(schema) {
2061
+ const stringifiedSchema = safeStringifyDrizzleSchema(schema);
2062
+ return createChecksum(stringifiedSchema);
2063
+ }
2064
+
2065
+ // src/client/ensdb-config.ts
2066
+ var ENSDB_SCHEMA_CHECKSUM = getDrizzleSchemaChecksum({
2067
+ ...ensindexer_abstract_exports,
2068
+ ...ensnode_exports
2069
+ });
2070
+
2071
+ // src/client/ensdb-reader.ts
2072
+ import { and, eq } from "drizzle-orm/sql";
2073
+ import {
2074
+ deserializeCrossChainIndexingStatusSnapshot,
2075
+ deserializeEnsIndexerPublicConfig
2076
+ } from "@ensnode/ensnode-sdk";
2077
+
2078
+ // src/lib/parse-pg-version-info.ts
2079
+ function parsePgVersionInfo(versionString) {
2080
+ if (typeof versionString !== "string") {
2081
+ throw new Error("PostgreSQL version string must be a string");
2082
+ }
2083
+ const match = versionString.match(/PostgreSQL (\d+\.\d+)/);
2084
+ if (!match) {
2085
+ throw new Error(`Failed to parse PostgreSQL version from version string: '${versionString}'`);
2086
+ }
2087
+ const parsedVersion = match[1];
2088
+ if (typeof parsedVersion !== "string") {
2089
+ throw new Error(`Parsed PostgreSQL version is not a string: '${parsedVersion}'`);
2090
+ }
2091
+ return parsedVersion;
2092
+ }
2026
2093
 
2027
2094
  // src/client/ensnode-metadata.ts
2028
2095
  var EnsNodeMetadataKeys = {
@@ -2131,6 +2198,15 @@ var EnsDbReader = class {
2131
2198
  }
2132
2199
  return deserializeEnsIndexerPublicConfig(record);
2133
2200
  }
2201
+ /**
2202
+ * Build ENSDb Public Config
2203
+ */
2204
+ async buildEnsDbPublicConfig() {
2205
+ const versionInfo = await this.buildEnsDbVersionInfo();
2206
+ return {
2207
+ versionInfo
2208
+ };
2209
+ }
2134
2210
  /**
2135
2211
  * Get Indexing Status Snapshot
2136
2212
  *
@@ -2172,6 +2248,33 @@ var EnsDbReader = class {
2172
2248
  `There must be exactly one ENSNodeMetadata record for ('${this.ensIndexerSchemaName}', '${metadata2.key}') composite key`
2173
2249
  );
2174
2250
  }
2251
+ /**
2252
+ * Get PostgreSQL version for the server hosting the ENSDb instance.
2253
+ *
2254
+ * @throws when the version cannot be retrieved or parsed from the query result.
2255
+ */
2256
+ async getPostgresVersion() {
2257
+ const result = await this.ensDb.execute("SELECT version();");
2258
+ const versionString = result.rows[0]?.version;
2259
+ try {
2260
+ return parsePgVersionInfo(versionString);
2261
+ } catch (error) {
2262
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2263
+ throw new Error(`Failed to get PostgreSQL version for the ENSDb instance: ${errorMessage}`);
2264
+ }
2265
+ }
2266
+ /**
2267
+ * Build ENSDb version info.
2268
+ *
2269
+ * @throws when version info cannot be retrieved or parsed from
2270
+ * the ENSDb instance.
2271
+ */
2272
+ async buildEnsDbVersionInfo() {
2273
+ const postgresVersion = await this.getPostgresVersion();
2274
+ return {
2275
+ postgresql: postgresVersion
2276
+ };
2277
+ }
2175
2278
  };
2176
2279
 
2177
2280
  // src/client/ensdb-writer.ts
@@ -2243,9 +2346,54 @@ var EnsDbWriter = class extends EnsDbReader {
2243
2346
  });
2244
2347
  }
2245
2348
  };
2349
+
2350
+ // src/client/validate/ensdb-config.ts
2351
+ import { prettifyError } from "zod/v4";
2352
+
2353
+ // src/client/zod-schemas/ensdb-config.ts
2354
+ import { parse as parseConnectionString } from "pg-connection-string";
2355
+ import { z } from "zod/v4";
2356
+ var EnsDbUrlSchema = z.string().refine(
2357
+ (url) => {
2358
+ try {
2359
+ if (!url.startsWith("postgresql://") && !url.startsWith("postgres://")) {
2360
+ return false;
2361
+ }
2362
+ const config = parseConnectionString(url);
2363
+ return !!(config.host && config.port && config.database);
2364
+ } catch {
2365
+ return false;
2366
+ }
2367
+ },
2368
+ {
2369
+ error: "Invalid PostgreSQL connection string for ENSDb. Expected format: postgresql://username:password@host:port/database"
2370
+ }
2371
+ );
2372
+ var EnsIndexerSchemaNameSchema = z.string({
2373
+ error: "ENSIndexer Schema Name is required."
2374
+ }).trim().nonempty({
2375
+ error: "ENSIndexer Schema Name cannot be an empty string."
2376
+ });
2377
+ var EnsDbConfigSchema = z.object({
2378
+ ensDbUrl: EnsDbUrlSchema,
2379
+ ensIndexerSchemaName: EnsIndexerSchemaNameSchema
2380
+ });
2381
+
2382
+ // src/client/validate/ensdb-config.ts
2383
+ function validateEnsDbConfig(unvalidatedConfig) {
2384
+ const ensDbConfig = EnsDbConfigSchema.safeParse(unvalidatedConfig);
2385
+ if (!ensDbConfig.success) {
2386
+ throw new Error(`Failed to parse ENSDb configuration:
2387
+ ${prettifyError(ensDbConfig.error)}
2388
+ `);
2389
+ }
2390
+ return ensDbConfig.data;
2391
+ }
2246
2392
  export {
2393
+ ENSDB_SCHEMA_CHECKSUM,
2247
2394
  EnsDbReader,
2248
2395
  EnsDbWriter,
2249
- EnsNodeMetadataKeys
2396
+ EnsNodeMetadataKeys,
2397
+ validateEnsDbConfig
2250
2398
  };
2251
2399
  //# sourceMappingURL=index.js.map