@ensnode/ensdb-sdk 1.15.1 → 1.16.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 NameHash
3
+ Copyright (c) 2026 NameHash Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,4 +1,4 @@
1
- export { b as account, c as account_relations, d as domain, e as domainEvent, f as domainResolverRelation, g as domainResolverRelation_relations, h as domainType, i as event, j as internal_registrarActionMetadata, k as internal_registrarActionMetadataType, l as label, m as label_relations, n as latestRegistrationIndex, o as latestRegistrationIndex_relations, p as latestRenewalIndex, q as latestRenewalIndex_relations, r as migratedNodeByNode, s as migratedNodeByParent, t as nameSales, u as nameTokens, v as permissions, w as permissionsEvent, x as permissionsResource, y as permissionsUser, z as permissionsUserEvent, A as registrarActionRelations, B as registrarActionType, C as registrarActions, D as registration, E as registrationLifecycleRelations, F as registrationLifecycles, G as registrationType, H as registration_relations, I as registry, J as registryType, K as relations_domain, L as relations_permissions, M as relations_permissionsResource, N as relations_permissionsUser, O as relations_registry, P as renewal, Q as renewal_relations, R as resolver, S as resolverAddressRecord, T as resolverAddressRecordRelations, U as resolverEvent, V as resolverRecords, W as resolverRecords_relations, X as resolverTextRecord, Y as resolverTextRecordRelations, Z as resolver_relations, _ as reverseNameRecord, $ as subgraph_abiChanged, a0 as subgraph_abiChangedRelations, a1 as subgraph_account, a2 as subgraph_accountRelations, a3 as subgraph_addrChanged, a4 as subgraph_addrChangedRelations, a5 as subgraph_authorisationChanged, a6 as subgraph_authorisationChangedRelations, a7 as subgraph_contenthashChanged, a8 as subgraph_contenthashChangedRelations, a9 as subgraph_domain, aa as subgraph_domainRelations, ab as subgraph_expiryExtended, ac as subgraph_expiryExtendedRelations, ad as subgraph_fusesSet, ae as subgraph_fusesSetRelations, af as subgraph_interfaceChanged, ag as subgraph_interfaceChangedRelations, ah as subgraph_multicoinAddrChanged, ai as subgraph_multicoinAddrChangedRelations, aj as subgraph_nameChanged, ak as subgraph_nameChangedRelations, al as subgraph_nameRegistered, am as subgraph_nameRegisteredRelations, an as subgraph_nameRenewed, ao as subgraph_nameRenewedRelations, ap as subgraph_nameTransferred, aq as subgraph_nameTransferredRelations, ar as subgraph_nameUnwrapped, as as subgraph_nameUnwrappedRelations, at as subgraph_nameWrapped, au as subgraph_nameWrappedRelations, av as subgraph_newOwner, aw as subgraph_newOwnerRelations, ax as subgraph_newResolver, ay as subgraph_newResolverRelations, az as subgraph_newTTL, aA as subgraph_newTTLRelations, aB as subgraph_pubkeyChanged, aC as subgraph_pubkeyChangedRelations, aD as subgraph_registration, aE as subgraph_registrationRelations, aF as subgraph_resolver, aG as subgraph_resolverRelations, aH as subgraph_textChanged, aI as subgraph_textChangedRelations, aJ as subgraph_transfer, aK as subgraph_transferRelations, aL as subgraph_versionChanged, aM as subgraph_versionChangedRelations, aN as subgraph_wrappedDomain, aO as subgraph_wrappedDomainRelations, aP as subgraph_wrappedTransfer, aQ as subgraph_wrappedTransferRelations, aR as subregistries, aS as subregistryRelations } from '../index-1D8_3_7g.js';
1
+ export { C as CANONICAL_NAME_PREFIX_LENGTH, R as REGISTRATION_SORT_SENTINEL, b as account, c as account_relations, d as domain, e as domainEvent, f as domainResolverRelation, g as domainResolverRelation_relations, h as domainType, i as event, j as internal_registrarActionMetadata, k as internal_registrarActionMetadataType, l as label, m as label_relations, n as latestRegistrationIndex, o as latestRegistrationIndex_relations, p as latestRenewalIndex, q as latestRenewalIndex_relations, r as migratedNodeByNode, s as migratedNodeByParent, t as nameSales, u as nameTokens, v as permissions, w as permissionsEvent, x as permissionsResource, y as permissionsUser, z as permissionsUserEvent, A as registrarActionRelations, B as registrarActionType, D as registrarActions, E as registration, F as registrationLifecycleRelations, G as registrationLifecycles, H as registrationType, I as registration_relations, J as registry, K as registryType, L as relations_domain, M as relations_permissions, N as relations_permissionsResource, O as relations_permissionsUser, P as relations_registry, Q as renewal, S as renewal_relations, T as resolver, U as resolverAddressRecord, V as resolverAddressRecordRelations, W as resolverEvent, X as resolverRecords, Y as resolverRecords_relations, Z as resolverTextRecord, _ as resolverTextRecordRelations, $ as resolver_relations, a0 as reverseNameRecord, a1 as subgraph_abiChanged, a2 as subgraph_abiChangedRelations, a3 as subgraph_account, a4 as subgraph_accountRelations, a5 as subgraph_addrChanged, a6 as subgraph_addrChangedRelations, a7 as subgraph_authorisationChanged, a8 as subgraph_authorisationChangedRelations, a9 as subgraph_contenthashChanged, aa as subgraph_contenthashChangedRelations, ab as subgraph_domain, ac as subgraph_domainRelations, ad as subgraph_expiryExtended, ae as subgraph_expiryExtendedRelations, af as subgraph_fusesSet, ag as subgraph_fusesSetRelations, ah as subgraph_interfaceChanged, ai as subgraph_interfaceChangedRelations, aj as subgraph_multicoinAddrChanged, ak as subgraph_multicoinAddrChangedRelations, al as subgraph_nameChanged, am as subgraph_nameChangedRelations, an as subgraph_nameRegistered, ao as subgraph_nameRegisteredRelations, ap as subgraph_nameRenewed, aq as subgraph_nameRenewedRelations, ar as subgraph_nameTransferred, as as subgraph_nameTransferredRelations, at as subgraph_nameUnwrapped, au as subgraph_nameUnwrappedRelations, av as subgraph_nameWrapped, aw as subgraph_nameWrappedRelations, ax as subgraph_newOwner, ay as subgraph_newOwnerRelations, az as subgraph_newResolver, aA as subgraph_newResolverRelations, aB as subgraph_newTTL, aC as subgraph_newTTLRelations, aD as subgraph_pubkeyChanged, aE as subgraph_pubkeyChangedRelations, aF as subgraph_registration, aG as subgraph_registrationRelations, aH as subgraph_resolver, aI as subgraph_resolverRelations, aJ as subgraph_textChanged, aK as subgraph_textChangedRelations, aL as subgraph_transfer, aM as subgraph_transferRelations, aN as subgraph_versionChanged, aO as subgraph_versionChangedRelations, aP as subgraph_wrappedDomain, aQ as subgraph_wrappedDomainRelations, aR as subgraph_wrappedTransfer, aS as subgraph_wrappedTransferRelations, aT as subregistries, aU as subregistryRelations, aV as truncateCanonicalNamePrefix } from '../index-D36RM4zS.js';
2
2
  import 'ponder';
3
3
  import 'drizzle-orm';
4
4
  import 'drizzle-orm/pg-core';
@@ -22,6 +22,8 @@ var reverseNameRecord = onchainTable2(
22
22
  (t) => ({
23
23
  // keyed by (address, coinType)
24
24
  address: t.hex().notNull().$type(),
25
+ // @TODO(cointype-bigint): store as `t.int8({ mode: "number" }).$type<CoinType>()` (like chainId
26
+ // elsewhere) so reads/writes use CoinType directly without bigint round-trips. See #2293.
25
27
  coinType: t.bigint().notNull(),
26
28
  /**
27
29
  * Represents the ENSIP-19 Reverse Name Record for a given (address, coinType).
@@ -62,7 +64,13 @@ var resolver = onchainTable2(
62
64
  // keyed by (chainId, address)
63
65
  id: t.text().primaryKey().$type(),
64
66
  chainId: t.int8({ mode: "number" }).notNull().$type(),
65
- address: t.hex().notNull().$type()
67
+ address: t.hex().notNull().$type(),
68
+ /**
69
+ * Whether this Resolver implements ENSIP-10 wildcard resolution (`IExtendedResolver`,
70
+ * interfaceId `0x9061b923`), determined via a single `supportsInterface` RPC the first
71
+ * time the Resolver is observed (see `upsertResolver`).
72
+ */
73
+ isExtended: t.boolean().notNull().default(false)
66
74
  }),
67
75
  (t) => ({
68
76
  byId: uniqueIndex().on(t.chainId, t.address)
@@ -131,17 +139,22 @@ var resolverAddressRecord = onchainTable2(
131
139
  node: t.hex().notNull().$type(),
132
140
  // NOTE: all well-known CoinTypes fit into javascript number but NOT postgres .integer, must be
133
141
  // stored as BigInt
142
+ // @TODO(cointype-bigint): use `t.int8({ mode: "number" }).$type<CoinType>()` like `chainId` above
143
+ // to drop bigint round-trips at every read/write boundary. See #2293.
134
144
  coinType: t.bigint().notNull(),
135
145
  /**
136
- * Represents the value of the Addresss Record specified by ((chainId, resolver, node), coinType).
146
+ * Represents the value of the Address Record specified by ((chainId, resolver, node), coinType).
137
147
  *
138
148
  * The value of this field is interpreted by `interpretAddressRecordValue` — see its implementation
139
149
  * for additional context and specific guarantees.
140
150
  */
141
- value: t.text().notNull()
151
+ value: t.hex().notNull()
142
152
  }),
143
153
  (t) => ({
144
- pk: primaryKey2({ columns: [t.chainId, t.address, t.node, t.coinType] })
154
+ pk: primaryKey2({ columns: [t.chainId, t.address, t.node, t.coinType] }),
155
+ // supports the reverse lookup powering `Account.nameReferences`:
156
+ // `WHERE value = <address> [AND coin_type = <coinType>]`
157
+ byValueAndCoinType: index().on(t.value, t.coinType)
145
158
  })
146
159
  );
147
160
  var resolverAddressRecordRelations = relations(resolverAddressRecord, ({ one }) => ({
@@ -1569,7 +1582,19 @@ var relations_registry = relations4(registry, ({ one, many }) => ({
1569
1582
  references: [permissions.chainId, permissions.address]
1570
1583
  })
1571
1584
  }));
1585
+ var REGISTRATION_SORT_SENTINEL = 2n ** 256n - 1n;
1572
1586
  var domainType = onchainEnum2("DomainType", ["ENSv1Domain", "ENSv2Domain"]);
1587
+ var CANONICAL_NAME_PREFIX_LENGTH = 64;
1588
+ function truncateCanonicalNamePrefix(name) {
1589
+ if (name === null) return null;
1590
+ let prefix = "";
1591
+ let count = 0;
1592
+ for (const codePoint of name) {
1593
+ prefix += codePoint;
1594
+ if (++count >= CANONICAL_NAME_PREFIX_LENGTH) break;
1595
+ }
1596
+ return prefix;
1597
+ }
1573
1598
  var domain = onchainTable6(
1574
1599
  "domains",
1575
1600
  (t) => ({
@@ -1601,6 +1626,18 @@ var domain = onchainTable6(
1601
1626
  * @example "vitalik.eth"
1602
1627
  */
1603
1628
  canonicalName: t.text().$type(),
1629
+ /**
1630
+ * Materialized prefix of `canonicalName` (first {@link CANONICAL_NAME_PREFIX_LENGTH} code
1631
+ * points), NULL iff `canonical = false`. Maintained by `canonicality-db-helpers.ts`.
1632
+ *
1633
+ * Powers left-anchored / substring search (`__canonical_name_prefix LIKE 'vit%'`) and NAME
1634
+ * ordering without `canonical_name`'s full-length btree size hazard. The `__` prefix marks it
1635
+ * an internal implementation detail (mirrors `Registry.__hasChildren`); query `canonical_name`
1636
+ * for exact matches and display.
1637
+ *
1638
+ * @example "vitalik.eth"
1639
+ */
1640
+ __canonicalNamePrefix: t.text("__canonical_name_prefix").$type(),
1604
1641
  /**
1605
1642
  * Materialized Canonical LabelHashPath, NULL iff `canonical = false`.
1606
1643
  * Maintained by `canonicality-db-helpers.ts`.
@@ -1629,7 +1666,29 @@ var domain = onchainTable6(
1629
1666
  *
1630
1667
  * @dev the computed Node (via `namehash`) of this Domain's Canonical Name.
1631
1668
  */
1632
- canonicalNode: t.hex().$type()
1669
+ canonicalNode: t.hex().$type(),
1670
+ /**
1671
+ * Materialized `start` of this Domain's latest Registration, or {@link REGISTRATION_SORT_SENTINEL}
1672
+ * when the Domain has no Registration. Mirror of the latest `registration.start` (see
1673
+ * `latestRegistrationIndex`), maintained inline by `registration-db-helpers.ts`.
1674
+ *
1675
+ * @dev Exists purely so REGISTRATION_TIMESTAMP-ordered find-domains queries can use the
1676
+ * `(registry_id, __latest_registration_start, id)` composite index instead of joining through
1677
+ * `latest_registration_indexes` → `registrations` and sorting. Held NOT NULL (absent → sentinel)
1678
+ * so the keyset stays a plain tuple compare with no NULL-placement special casing; see
1679
+ * find-domains-resolver-helpers.ts. Double-underscore prefix marks it as an internal
1680
+ * materialized mirror, not part of the on-chain protocol surface; the canonical (possibly null)
1681
+ * value lives on the Registration entity.
1682
+ */
1683
+ __latestRegistrationStart: t.bigint("__latest_registration_start").notNull().default(REGISTRATION_SORT_SENTINEL),
1684
+ /**
1685
+ * Materialized `expiry` of this Domain's latest Registration, or {@link REGISTRATION_SORT_SENTINEL}
1686
+ * when the Domain has no Registration or its latest Registration never expires (effectively +∞).
1687
+ * Mirror of the latest `registration.expiry`, maintained inline by `registration-db-helpers.ts`.
1688
+ *
1689
+ * @dev See `__latestRegistrationStart`. Backs REGISTRATION_EXPIRY-ordered queries.
1690
+ */
1691
+ __latestRegistrationExpiry: t.bigint("__latest_registration_expiry").notNull().default(REGISTRATION_SORT_SENTINEL)
1633
1692
  // NOTE: Domain-Resolver Relations tracked via Protocol Acceleration plugin
1634
1693
  }),
1635
1694
  (t) => ({
@@ -1641,29 +1700,40 @@ var domain = onchainTable6(
1641
1700
  // get-domain-by-interpreted-name.ts). The leading `registry_id` column also serves
1642
1701
  // `WHERE registry_id = X` lookups via prefix scan.
1643
1702
  byRegistryAndLabelHash: index5().on(t.registryId, t.labelHash),
1644
- // composite for `WHERE registry_id = X ORDER BY canonical_name LIMIT N` (Domain.subdomains
1645
- // and other find-domains queries when ordering by NAME). Uses `left(canonical_name, 256)`
1646
- // to bound the index tuple under btree's per-tuple max (~2712 bytes): 256 chars × max 4-byte
1647
- // UTF-8 = 1024 bytes, leaving ample room for the registry_id and id columns. Names beyond
1648
- // 256 chars (currently <0.0001% of mainnet) collide on the truncated prefix and tie-break by
1649
- // id; this is acceptable since such names are invariably spam. Callers MUST sort by the same
1650
- // expression for the planner to use this index for ordered scan.
1651
- byRegistryAndCanonicalNameLeft: index5().on(
1652
- t.registryId,
1653
- sql2`left(${t.canonicalName}, 256)`,
1654
- t.id
1655
- ),
1703
+ // composite for `WHERE registry_id = X ORDER BY __canonical_name_prefix LIMIT N` (Domain.subdomains
1704
+ // and other find-domains queries when ordering by NAME). The length-capped prefix keeps the
1705
+ // index tuple under btree's per-tuple max (~2712 bytes); 64 code points × max 4-byte UTF-8 =
1706
+ // 256 bytes, leaving ample room for the registry_id and id columns.
1707
+ byRegistryAndCanonicalNamePrefix: index5().on(t.registryId, t.__canonicalNamePrefix, t.id),
1656
1708
  // hash index avoids the btree 8191-byte row-size hazard for spam names
1657
1709
  byCanonicalNameExact: index5().using("hash", t.canonicalName),
1658
- // GIN trigram index for substring / similarity queries (inline `gin_trgm_ops` via `sql`
1659
- // because passing it through `.op()` gets dropped by Ponder)
1660
- byCanonicalNameFuzzy: index5().using("gin", sql2`${t.canonicalName} gin_trgm_ops`),
1710
+ // GIN trigram on the length-capped prefix for left-anchored (`LIKE 'vit%'`) and substring
1711
+ // search (inline `gin_trgm_ops` via `sql` because passing it through `.op()` gets dropped by
1712
+ // Ponder)
1713
+ byCanonicalNamePrefixFuzzy: index5().using("gin", sql2`${t.__canonicalNamePrefix} gin_trgm_ops`),
1661
1714
  // GIN containment for `cascadeLabelHeal`'s `canonical_label_hash_path @> ARRAY[lh]` lookup
1662
1715
  byCanonicalLabelHashPath: index5().using("gin", t.canonicalLabelHashPath),
1663
1716
  // hash index for resolver-record → canonical-domain joins
1664
1717
  byCanonicalNode: index5().using("hash", t.canonicalNode),
1665
1718
  // btree for ORDER BY canonical_depth (typeahead and DEPTH-ordered browse)
1666
- byCanonicalDepth: index5().on(t.canonicalDepth)
1719
+ byCanonicalDepth: index5().on(t.canonicalDepth),
1720
+ // Composites for `WHERE registry_id = X ORDER BY <latest registration value> LIMIT N`
1721
+ // (REGISTRATION_TIMESTAMP / REGISTRATION_EXPIRY ordering in find-domains queries). The latest
1722
+ // registration's start/expiry is mirrored onto the Domain row (see `__latestRegistration*`) so
1723
+ // the order is an index-ordered scan, not a join through `latest_registration_indexes` →
1724
+ // `registrations` followed by a sort. The columns are NOT NULL (absent → `REGISTRATION_SORT_SENTINEL`),
1725
+ // so a single plain composite per column serves both ASC and DESC (forward / backward scan) with
1726
+ // a plain keyset tuple — see find-domains-resolver-helpers.ts.
1727
+ byRegistryAndLatestRegistrationStart: index5().on(
1728
+ t.registryId,
1729
+ t.__latestRegistrationStart,
1730
+ t.id
1731
+ ),
1732
+ byRegistryAndLatestRegistrationExpiry: index5().on(
1733
+ t.registryId,
1734
+ t.__latestRegistrationExpiry,
1735
+ t.id
1736
+ )
1667
1737
  })
1668
1738
  );
1669
1739
  var relations_domain = relations4(domain, ({ one, many }) => ({
@@ -1715,7 +1785,7 @@ var registration = onchainTable6(
1715
1785
  start: t.bigint().notNull(),
1716
1786
  // may have an expiry
1717
1787
  expiry: t.bigint(),
1718
- // maybe have a grace period (BaseRegistrar)
1788
+ // may have a grace period (BaseRegistrar)
1719
1789
  gracePeriod: t.bigint(),
1720
1790
  // registrar AccountId
1721
1791
  registrarChainId: t.int8({ mode: "number" }).notNull().$type(),
@@ -1923,6 +1993,8 @@ var label_relations = relations4(label, ({ many }) => ({
1923
1993
  domains: many(domain)
1924
1994
  }));
1925
1995
  export {
1996
+ CANONICAL_NAME_PREFIX_LENGTH,
1997
+ REGISTRATION_SORT_SENTINEL,
1926
1998
  account,
1927
1999
  account_relations,
1928
2000
  domain,
@@ -2030,6 +2102,7 @@ export {
2030
2102
  subgraph_wrappedTransfer,
2031
2103
  subgraph_wrappedTransferRelations,
2032
2104
  subregistries,
2033
- subregistryRelations
2105
+ subregistryRelations,
2106
+ truncateCanonicalNamePrefix
2034
2107
  };
2035
2108
  //# sourceMappingURL=index.js.map