DI container for ENSApi#2159
Conversation
🦋 Changeset detectedLatest commit: e4fc2db The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds a DI container (di) exposing runtime ENSApi/ENSDb config, namespace/root-chain IDs, public client and caches; migrates modules and middleware to read di.context instead of static config; adds subgraph GraphQL middleware; adds SWRCache.peek() with tests. ChangesDependency Injection Refactor
SWRCache Enhancement
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/ensapi/src/config/config.schema.ts (1)
41-46:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winStale JSDoc: function no longer fetches anything.
The comment says "fetching the EnsIndexerPublicConfig" but the function is now synchronous and only parses environment variables. Update the documentation to reflect the simplified behavior.
📝 Suggested fix
/** - * Builds the EnsApiConfig from an EnsApiEnvironment object, fetching the EnsIndexerPublicConfig. + * Builds the EnsApiConfig from an EnsApiEnvironment object by parsing environment variables. * * `@returns` A validated EnsApiConfig object * `@throws` Error with formatted validation messages if environment parsing fails */🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/ensapi/src/config/config.schema.ts` around lines 41 - 46, The JSDoc above the config builder is stale: it claims the function "fetches the EnsIndexerPublicConfig" but the implementation is now synchronous and only parses environment variables into an EnsApiConfig; update the comment to accurately describe the behavior of the function (that it builds/validates an EnsApiConfig from an EnsApiEnvironment synchronously), remove any mention of fetching EnsIndexerPublicConfig, and keep the existing notes about returning a validated EnsApiConfig and throwing an Error with formatted validation messages on failure; reference the EnsApiConfig and EnsApiEnvironment symbols in the revised doc so readers can find the relevant types.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/ensapi/src/di.ts`:
- Around line 141-163: The three getters indexingStatusCache,
referralProgramEditionConfigSetCache, and stackInfoCache use redundant
memoization around module-level singletons; change each getter to simply return
the imported singleton (i.e., return indexingStatusCache; return
referralProgramEditionConfigSetCache; return stackInfoCache) and remove the
conditional assignment logic; also remove or stop referencing the corresponding
properties on the instances object if they become unused so there is no dead
state left behind.
- Around line 226-230: The di.context getter currently calls
buildEnsApiDiContext(process.env) on every access, which rebuilds the DI
container and loses memoization; change this to compute and cache the context
once at module level (or lazily on first access) so the same frozen
EnsApiDiContext instance is returned on subsequent accesses. Concretely, create
a module-scoped variable (e.g., cachedEnsApiDiContext) and initialize it by
calling buildEnsApiDiContext(process.env) (or set it on first di.context
access), then have the di.context getter return
Object.freeze(cachedEnsApiDiContext) instead of calling buildEnsApiDiContext
each time; ensure references to buildEnsApiDiContext, di.context, and
EnsApiDiContext are used to locate and update the code.
In `@apps/ensapi/src/handlers/api/explore/name-tokens-api.ts`:
- Around line 80-83: The code computes indexedSubregistries on every request by
calling getIndexedSubregistries(di.context.ensNamespaceId,
di.context.stackInfo.ensIndexer.plugins as PluginName[]); move this into a
memoized or lazily-initialized location (e.g., module-level lazyProxy or store
it on di/context during app startup) so the result is computed once instead of
per-request, and update call sites to read the cached value; also remove the
unchecked cast of di.context.stackInfo.ensIndexer.plugins to PluginName[]—either
validate/filter plugins to the PluginName set before passing them to
getIndexedSubregistries or change getIndexedSubregistries to accept the actual
plugin type and internally narrow/validate entries to avoid silent type errors.
In `@apps/ensapi/src/lib/resolution/multichain-primary-name-resolution.ts`:
- Around line 17-37: The current getENSIP19SupportedChainIds builds an array
that always includes mainnet.id and then appends datasource-derived chain ids
which may include mainnet again; change getENSIP19SupportedChainIds so the final
returned list is deduplicated (e.g., apply uniq or convert to a Set) across the
entire array before it is used by resolveReverse fan-out to avoid duplicate
reverse lookups; keep references to
maybeGetDatasource(di.context.ensNamespaceId, DatasourceNames.*) and mainnet.id
intact and dedupe the combined result returned by getENSIP19SupportedChainIds.
In `@packages/ensnode-sdk/src/shared/cache/swr-cache.ts`:
- Around line 176-180: The JSDoc for peak() repeats the summary in an
unnecessary `@returns` tag; remove the redundant `@returns` line from the peak()
comment block while keeping the summary and the `@throws` tag intact so the doc
follows the repo rule about not restating the method summary.
---
Outside diff comments:
In `@apps/ensapi/src/config/config.schema.ts`:
- Around line 41-46: The JSDoc above the config builder is stale: it claims the
function "fetches the EnsIndexerPublicConfig" but the implementation is now
synchronous and only parses environment variables into an EnsApiConfig; update
the comment to accurately describe the behavior of the function (that it
builds/validates an EnsApiConfig from an EnsApiEnvironment synchronously),
remove any mention of fetching EnsIndexerPublicConfig, and keep the existing
notes about returning a validated EnsApiConfig and throwing an Error with
formatted validation messages on failure; reference the EnsApiConfig and
EnsApiEnvironment symbols in the revised doc so readers can find the relevant
types.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f6c095f7-651a-434f-96cd-2e8951278c2f
📒 Files selected for processing (28)
apps/ensapi/src/cache/referral-edition-snapshots.cache.tsapps/ensapi/src/cache/referral-program-edition-set.cache.tsapps/ensapi/src/cache/stack-info.cache.tsapps/ensapi/src/config/config.schema.tsapps/ensapi/src/config/index.tsapps/ensapi/src/config/redact.tsapps/ensapi/src/config/validations.tsapps/ensapi/src/di.tsapps/ensapi/src/handlers/api/explore/name-tokens-api.tsapps/ensapi/src/handlers/api/omnigraph/omnigraph-api.tsapps/ensapi/src/handlers/subgraph/subgraph-api.tsapps/ensapi/src/index.tsapps/ensapi/src/lib/name-tokens/find-name-tokens-for-domain.tsapps/ensapi/src/lib/protocol-acceleration/find-resolver.tsapps/ensapi/src/lib/protocol-acceleration/get-records-from-index.tsapps/ensapi/src/lib/resolution/forward-resolution.tsapps/ensapi/src/lib/resolution/multichain-primary-name-resolution.tsapps/ensapi/src/lib/subgraph/indexing-status-to-subgraph-meta.tsapps/ensapi/src/middleware/can-accelerate.middleware.tsapps/ensapi/src/middleware/ensanalytics.middleware.tsapps/ensapi/src/middleware/name-tokens.middleware.tsapps/ensapi/src/middleware/registrar-actions.middleware.tsapps/ensapi/src/middleware/thegraph-fallback.middleware.tsapps/ensapi/src/omnigraph-api/lib/get-domain-by-interpreted-name.tsapps/ensapi/src/omnigraph-api/schema/query.tsapps/ensapi/src/omnigraph-api/schema/resolver.tspackages/ensnode-sdk/src/shared/cache/swr-cache.test.tspackages/ensnode-sdk/src/shared/cache/swr-cache.ts
💤 Files with no reviewable changes (3)
- apps/ensapi/src/config/index.ts
- apps/ensapi/src/config/redact.ts
- apps/ensapi/src/config/validations.ts
There was a problem hiding this comment.
Pull request overview
This PR introduces a new DI container for ENSApi and migrates many modules off the previous global config singleton, while also extending the SDK’s SWR cache to support synchronous reads.
Changes:
- Add
SWRCache.peak()(sync access to cached value) plus tests. - Add
apps/ensapi/src/di.tsand refactor ENSApi codepaths to read config/stack info/caches viadi.context. - Remove the old ENSApi config singleton and related validation/redaction helpers, updating handlers/middleware accordingly.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ensnode-sdk/src/shared/cache/swr-cache.ts | Adds new synchronous cache accessor (peak). |
| packages/ensnode-sdk/src/shared/cache/swr-cache.test.ts | Adds test coverage for the new cache accessor. |
| apps/ensapi/src/omnigraph-api/schema/resolver.ts | Switches namespace sourcing from config to DI. |
| apps/ensapi/src/omnigraph-api/schema/query.ts | Switches namespace sourcing from config to DI. |
| apps/ensapi/src/omnigraph-api/lib/get-domain-by-interpreted-name.ts | Switches namespace sourcing from config to DI. |
| apps/ensapi/src/middleware/thegraph-fallback.middleware.ts | Reads namespace/TheGraph key/subgraph compatibility from DI. |
| apps/ensapi/src/middleware/registrar-actions.middleware.ts | Reads ENSIndexer capability info from DI stackInfo. |
| apps/ensapi/src/middleware/name-tokens.middleware.ts | Reads ENSIndexer capability info from DI stackInfo. |
| apps/ensapi/src/middleware/ensanalytics.middleware.ts | Reads ENSIndexer capability info from DI stackInfo. |
| apps/ensapi/src/middleware/can-accelerate.middleware.ts | Reads namespace/plugins from DI stackInfo. |
| apps/ensapi/src/lib/subgraph/indexing-status-to-subgraph-meta.ts | Uses DI-derived root chain + version info for _meta. |
| apps/ensapi/src/lib/resolution/multichain-primary-name-resolution.ts | Switches namespace sourcing to DI; removes prior lazy memoization. |
| apps/ensapi/src/lib/resolution/forward-resolution.ts | Switches namespace sourcing to DI; changes public client wiring. |
| apps/ensapi/src/lib/protocol-acceleration/get-records-from-index.ts | Switches namespace sourcing to DI. |
| apps/ensapi/src/lib/protocol-acceleration/find-resolver.ts | Switches namespace sourcing to DI (and leaves an error message typo). |
| apps/ensapi/src/lib/name-tokens/find-name-tokens-for-domain.ts | Switches namespace sourcing to DI. |
| apps/ensapi/src/index.ts | Boots server using DI config and warms caches on startup; updates shutdown hooks. |
| apps/ensapi/src/handlers/subgraph/subgraph-api.ts | Uses DI-provided subgraph GraphQL middleware. |
| apps/ensapi/src/handlers/api/omnigraph/omnigraph-api.ts | Reads ENSIndexer capability info from DI stackInfo. |
| apps/ensapi/src/handlers/api/explore/name-tokens-api.ts | Switches namespace/plugins sourcing to DI stackInfo. |
| apps/ensapi/src/di.ts | Adds new ENSApi DI container and wiring for config/db/stackInfo/rpc/subgraph middleware. |
| apps/ensapi/src/config/validations.ts | Removes previous config validation checks. |
| apps/ensapi/src/config/redact.ts | Removes prior config redaction helper. |
| apps/ensapi/src/config/index.ts | Removes the old global config singleton. |
| apps/ensapi/src/config/config.schema.ts | Simplifies EnsApiConfig parsing (no longer fetches indexer public config). |
| apps/ensapi/src/cache/stack-info.cache.ts | Updates stack-info cache to build public config via DI. |
| apps/ensapi/src/cache/referral-program-edition-set.cache.ts | Reads referral editions URL from DI config; exports cache type. |
| apps/ensapi/src/cache/referral-edition-snapshots.cache.ts | Reads ENSIndexer capability info from DI stackInfo. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Enables sync access to the currently cached value.
f9e4226 to
cdd3f19
Compare
cdd3f19 to
a960bc4
Compare
We use `import di from @/di;` now to allow for lazy initialization upon config access.
a960bc4 to
95c0285
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 36 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (3)
apps/ensapi/src/lib/resolution/forward-resolution.ts:165
publicClientis alwaysdi.context.rootChainPublicClient, but_resolveForwardcan recurse into a bridged resolver path (registry: bridged.targetRegistry) wherechainIdchanges to the target registry’s chain. Using a root-chain-only client will send RPC reads to the wrong network for bridged registries (Basenames/Lineanames shadow registries). This should select aPublicClientbased on the currentchainId(e.g., DI exposes agetPublicClient(chainId)backed by per-chain RPC configs).
const publicClient = di.context.rootChainPublicClient;
////////////////////////////
/// 0. Temporary ENSv2 Bailout
////////////////////////////
// TODO: re-enable protocol acceleration for ENSv2
apps/ensapi/src/handlers/subgraph/subgraph-api.ts:27
hasSubgraphApiConfigSupport(di.context.stackInfo.ensIndexer)can throw ifstackInfoCache.peek()is not ready (or errored), which would bypass the intended 503 response logic in this guard. Consider using an awaited stack-info value (e.g.,await di.context.stackInfoCache.read()and handleError) or reusing the existingstackInfoMiddlewarepattern for request-scoped gating.
app.use(async (c, next) => {
const configPrerequisite = hasSubgraphApiConfigSupport(di.context.stackInfo.ensIndexer);
// 503 if Subgraph API is not available due to config prerequisites not met
if (!configPrerequisite.supported) {
return c.text(`Service Unavailable: ${configPrerequisite.reason}`, 503);
}
.changeset/clever-coins-sell.md:6
- Typo in changeset text: "granual" → "granular".
Replaced all eagerly-evaluated reads from `import config from "@/config";` with lazy-evaluated reads from `import di from "@/di";`. This change allows more granual control over internal resources in ENSApi.
Greptile SummaryThis PR replaces the eager
Confidence Score: 5/5Safe to merge; the DI container correctly gates all stackInfo-dependent paths at request time, and the intentional simplification to a single root-chain RPC client is consistent throughout the change. The core refactor is mechanical (swapping eager config reads for lazy DI context reads) and is well-covered by existing tests. The fire-and-forget cache warm-up in init() is safe because routes are gated on cache availability. The only issues found are a destroy→init re-initialization edge case where cached data is not flushed and the proactive-revalidation interval is not re-armed, and a fragile load-order assumption in gql.middleware.ts — neither affects the normal single-lifecycle production path. packages/ensnode-sdk/src/shared/cache/swr-cache.ts (destroy semantics) and apps/ensapi/src/lib/subgraph/gql.middleware.ts (implicit init dependency at module level) Important Files Changed
Sequence DiagramsequenceDiagram
participant idx as index.ts
participant di as EnsApiDiContainer
participant ctx as EnsApiDiContext
participant sc as stackInfoCache
participant ic as indexingStatusCache
participant req as HTTP Request
idx->>di: di.init()
di->>di: loadContext() builds EnsApiDiContext from process.env
di->>sc: void stackInfoCache.read() [fire-and-forget]
di->>ic: void indexingStatusCache.read() [fire-and-forget]
di-->>idx: returns synchronously
idx->>idx: serve(app, port from di.context.ensApiConfig.port)
req->>ctx: di.context.namespace
ctx->>sc: stackInfoCache.peek()
sc-->>ctx: EnsNodeStackInfo or throws if not ready
ctx-->>req: ENSNamespaceId
req->>ctx: di.context.rootChainPublicClient
ctx->>ctx: buildRootChainRpcConfig cached on first access
ctx-->>req: PublicClient for root chain
idx->>di: di.destroy() on SIGTERM or SIGINT
di->>sc: stackInfoCache.destroy()
di->>ic: indexingStatusCache.destroy()
di->>di: set _context to undefined
Reviews (5): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile |
|
@greptile review |
shrugs
left a comment
There was a problem hiding this comment.
some questions but otherwise LGTM! can we get rid of the lazy proxy entirely now?
| "ensapi": minor | ||
| --- | ||
|
|
||
| Replaced all eagerly-evaluated reads from `import config from "@/config";` with lazy-evaluated reads from `import di from "@/di";`. This change allows more granual control over internal resources in ENSApi. |
There was a problem hiding this comment.
imo internal change needs no changeset
| // biome-ignore lint/style/noNonNullAssertion: domain.name guaranteed to exist | ||
| const name = asInterpretedName(record.domains.name!); | ||
| const ownership = getNameTokenOwnership(config.namespace, name, owner); | ||
| const ownership = getNameTokenOwnership(di.context.ensNamespaceId, name, owner); |
There was a problem hiding this comment.
i realize ensNamespaceId is most correct, but what about maintaining namespace? di.context.namespace reads cleaner ¯\_(ツ)_/¯
There was a problem hiding this comment.
Sure, will update that one 👍
| * Initializes the DI container by loading the context and initializing | ||
| * necessary resources. | ||
| */ | ||
| init(): void { |
There was a problem hiding this comment.
I'm certainly not an expert in this pattern, but my intuition was that the init function would have to be async because of the async dependencies, but then after we await init, anything further can run synchronously. and that we'd do something like await di.init() somewhere in the ensapi startup sequence.
There was a problem hiding this comment.
The idea here is that we need the init to lazy-access all cache instances which are configured withproactivelyInitialize: true. The idea here is not to wait for the cache instances to actually have cached result ready, but rather to have the cache instances to be instantiated.
The goal of the di.init() is not to block the HTTP server from becoming open ASAP. The HTTP server needs to be available as fast as possible, and the HTTP route handlers have to be able to handle situations when relevant cache result is not available at the request time.
There was a problem hiding this comment.
Also, we don't really need the async dependencies inside di.init() to actually return any value to the caller scope, so not need to wait for this step really.
| // start ENSNode API OpenTelemetry SDK | ||
| sdk.start(); | ||
| // initialize DI container and its resources | ||
| di.init(); |
There was a problem hiding this comment.
like right here I'm surprised that we don't need to await
There was a problem hiding this comment.
I shared the explanation here. In short, we don't want the HTTP server start to become blocked on caches being populated from I/O.
|
@shrugs about this one:
I'll need two PRs more to make the |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/ensapi/src/di.ts (1)
149-156:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftDon't collapse RPC access to a root-chain-only client.
This container now exposes only
rootChainPublicClient, but forward resolution still hops intobridged.targetRegistryand then runs resolver RPCs there. Once the active resolver lives on Base/Linea/etc,isExtendedResolver()/executeOperations()will still hit the root-chain RPC through this client and query the wrong network. DI still needs a client lookup keyed by the active registry/resolver chain for bridged resolution to remain correct.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/ensapi/src/di.ts` around lines 149 - 156, The DI currently exposes a single rootChainPublicClient (instances.rootChainPublicClient created from context.rootChainRpcConfig) which causes bridged resolution paths (bridged.targetRegistry -> isExtendedResolver / executeOperations) to query the wrong network; change the DI to provide a lookup/registry of PublicClient instances keyed by chain/registry/resolver identity (e.g., map from chainId or targetRegistry id to PublicClient) instead of collapsing to only rootChainPublicClient, populate that map from the configured RPC lists (similar to how rootChainPublicClient is created from context.rootChainRpcConfig), and update callers that resolve bridged.targetRegistry, isExtendedResolver, and executeOperations to fetch the correct client from the new lookup by the active registry/chain key.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/clever-coins-sell.md:
- Line 5: Update the changeset description to fix the typo: replace "granual"
with "granular" in the sentence that reads "This change allows more granual
control over internal resources in ENSApi." Ensure the updated text references
the same symbols/imports (import config, import di, ENSApi) so the meaning
remains unchanged.
In `@apps/ensapi/src/di.ts`:
- Around line 126-146: The namespace/getters currently read context.stackInfo
synchronously which can fail before stackInfoCache.read() warms up; change the
implementation so di.context.namespace, rootChainId, and rootChainRpcConfig do
not block on stackInfoCache: either (A) initialize and store a minimal fallback
namespace synchronously during init() (e.g., set instances.namespace in init
prior to starting stackInfoCache.read()) and have namespace return that stored
value, then compute rootChainId via getENSRootChainId(instances.namespace) and
rootChainRpcConfig via buildRootChainRpcConfig(instances.namespace), or (B) make
these getters throw a clear NotReady/503-style error and expose a readiness
check (e.g., context.isReady or stackReady) that init() flips once
stackInfoCache.read() completes; update di.context.namespace, rootChainId,
rootChainRpcConfig, and init() accordingly and ensure route handlers use the
readiness check if you choose option B.
---
Outside diff comments:
In `@apps/ensapi/src/di.ts`:
- Around line 149-156: The DI currently exposes a single rootChainPublicClient
(instances.rootChainPublicClient created from context.rootChainRpcConfig) which
causes bridged resolution paths (bridged.targetRegistry -> isExtendedResolver /
executeOperations) to query the wrong network; change the DI to provide a
lookup/registry of PublicClient instances keyed by chain/registry/resolver
identity (e.g., map from chainId or targetRegistry id to PublicClient) instead
of collapsing to only rootChainPublicClient, populate that map from the
configured RPC lists (similar to how rootChainPublicClient is created from
context.rootChainRpcConfig), and update callers that resolve
bridged.targetRegistry, isExtendedResolver, and executeOperations to fetch the
correct client from the new lookup by the active registry/chain key.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 71a42f8e-bfd5-4df6-8806-78a2d222b12d
📒 Files selected for processing (17)
.changeset/clever-coins-sell.md.changeset/thirty-bees-divide.mdapps/ensapi/src/config/config.schema.test.tsapps/ensapi/src/config/config.schema.tsapps/ensapi/src/di.tsapps/ensapi/src/handlers/api/explore/name-tokens-api.tsapps/ensapi/src/index.tsapps/ensapi/src/lib/name-tokens/find-name-tokens-for-domain.tsapps/ensapi/src/lib/protocol-acceleration/find-resolver.tsapps/ensapi/src/lib/protocol-acceleration/get-records-from-index.tsapps/ensapi/src/lib/resolution/forward-resolution.tsapps/ensapi/src/lib/resolution/multichain-primary-name-resolution.tsapps/ensapi/src/middleware/can-accelerate.middleware.tsapps/ensapi/src/middleware/thegraph-fallback.middleware.tsapps/ensapi/src/omnigraph-api/lib/get-domain-by-interpreted-name.tsapps/ensapi/src/omnigraph-api/schema/query.tsapps/ensapi/src/omnigraph-api/schema/resolver.ts
Lite PR
Tip: Review docs on the ENSNode PR process
Summary
EnsApiDiContainer(apps/ensapi/src/di.ts) that lazily evaluates config, stack info, caches, and root-chain clients on first access via getters.import config from "@/config";reads across ENSApi handlers, middleware, and libraries with lazyimport di from "@/di";reads fromdi.context(e.g.di.context.ensApiConfig,di.context.ensNamespaceId,di.context.stackInfo).EnsApiConfigto only env-based settings; moves previously eager/async-fetched data (ENSIndexer public config, namespace, RPC configs) out of the config object and into the DI container where they are resolved lazily.apps/ensapi/src/lib/resolution/forward-resolution.ts).Why
lazy/lazyProxycalls is challenging when the proxied objects need to be enumerated down the app stack. We need a way to lazy-evaluated certain values / objects, but ideally, without the use of theProxyprimitive.disafely at the top level and values are computed on first property access, not at import time.di.init()/di.destroy()for caches) inindex.tsand makes dependencies explicit and testable.Testing
ensanalytics-api.test.ts,execute-operations.integration.test.ts,find-domains-resolver-helpers.test.ts).pnpm test --project ensapito verify affected tests pass.pnpm -F ensapi typecheckandpnpm lintto validate TypeScript and formatting.Notes for Reviewer (Optional)
import { ensDb, ensIndexerSchema } from "@/lib/ensdb/singleton";. Those will be moved into the DI container in a follow-up PR so that all dependencies are lazy and context-bound.EnsApiDiContaineris designed to support re-initialization (destroy()theninit()) which is useful for integration tests and hot-reload scenarios.Pre-Review Checklist (Blocking)