Skip to content

Conversation

@notrab
Copy link
Member

@notrab notrab commented Jan 19, 2026

Lite PR

Summary

  1. Added openapi:generate script to fetch and save OpenAPI spec from ENSApi
  2. Updated Mintlify docs to use production URL for live API docs, committed file for PR previews
  3. Added CI job to validate committed openapi.json stays in sync with production

Why

Production API docs were getting out of sync with deployed API, and there was no way to preview API doc changes in PRs.

This setup ensures:

  • Production docs always reflect the live API (via runtime URL)
  • PR previews show upcoming API changes (via committed file)
  • CI catches drift between committed spec and production

Testing

Ran ./.github/scripts/generate_openapi_spec.sh locally:

ensnode git:(openapi-spec-generator) ✗ ./.github/scripts/generate_openapi_spec.sh
Starting ENSApi in OpenAPI generate mode...
Waiting for ENSApi to start (timeout: 30s)...
Waiting for ENSApi to start... (1/30)
.                                        |  WARN  Unsupported engine: wanted: {"node":">=24.13.0"} (current: {"node":"v22.19.0","pnpm":"10.28.0"})

> ensapi@1.5.1 start /Users/notrab/github.com/namehash/ensnode/apps/ensapi
> tsx src/index.ts

Waiting for ENSApi to start... (2/30)
Waiting for ENSApi to start... (3/30)
Waiting for ENSApi to start... (4/30)
[08:27:12.750] INFO: OPENAPI_GENERATE_MODE enabled - using minimal mock config
[08:27:13.322] INFO: ENSApi listening on port 4334
    config: {
      "port": 4334,
      "databaseUrl": "*****",
      "databaseSchemaName": "public",
      "ensIndexerUrl": "http://localhost:42069/",
      "namespace": "mainnet",
      "rpcConfigs": {},
      "ensIndexerPublicConfig": {
        "labelSet": {
          "labelSetId": "ens-default",
          "labelSetVersion": 1
        },
        "indexedChainIds": {},
        "isSubgraphCompatible": false,
        "namespace": "mainnet",
        "plugins": [
          "subgraph"
        ],
        "databaseSchemaName": "public",
        "versionInfo": {
          "nodejs": "v22.19.0",
          "ponder": "0.0.0",
          "ensDb": "1.5.1",
          "ensIndexer": "1.5.1",
          "ensNormalize": "0.0.0",
          "ensRainbow": "1.5.1",
          "ensRainbowSchema": 1
        }
      },
      "ensHolidayAwardsStart": 1764547200,
      "ensHolidayAwardsEnd": 1767225599,
      "inOpenApiGenerateMode": true
    }
[08:27:13.326] ERROR: Error occurred while fetching a new indexing status snapshot. The cached indexing status snapshot (if any) will not be updated.
    scope: "indexing-status.cache"
    err: {
      "type": "TypeError",
      "message": "fetch failed: ",
      "stack":
          TypeError: fetch failed
              at node:internal/deps/undici/undici:13510:13
              at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
              at async ENSNodeClient.indexingStatus (/Users/notrab/github.com/namehash/ensnode/packages/ensnode-sdk/src/client.ts:349:22)
          caused by: AggregateError [ECONNREFUSED]:
              at internalConnectMultiple (node:net:1134:18)
              at afterConnectMultiple (node:net:1715:7)
    }
ENSApi is ready and responding
Generating OpenAPI spec...
.                                        |  WARN  Unsupported engine: wanted: {"node":">=24.13.0"} (current: {"node":"v22.19.0","pnpm":"10.28.0"})

> @docs/mintlify@1.5.1 openapi:generate /Users/notrab/github.com/namehash/ensnode/docs/docs.ensnode.io
> tsx scripts/generate-openapi.ts http://localhost:4334

Fetching OpenAPI spec from: http://localhost:4334/openapi.json
OpenAPI spec written to: /Users/notrab/github.com/namehash/ensnode/docs/docs.ensnode.io/openapi.json
Spec version: 1.5.1
Paths: 11
Formatting with Biome...
../..                                    |  WARN  Unsupported engine: wanted: {"node":">=24.13.0"} (current: {"node":"v22.19.0","pnpm":"10.28.0"})
../..                                    |  WARN  Unsupported engine: wanted: {"node":">=24.13.0"} (current: {"node":"v22.19.0","pnpm":"10.28.0"})
Formatted 1 file in 10ms. Fixed 1 file.
Stopping ENSApi...
[08:27:23.201] ERROR: Error occurred while fetching a new indexing status snapshot. The cached indexing status snapshot (if any) will not be updated.
    scope: "indexing-status.cache"
    err: {
      "type": "TypeError",
      "message": "fetch failed: ",
      "stack":
          TypeError: fetch failed
              at node:internal/deps/undici/undici:13510:13
              at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
              at async ENSNodeClient.indexingStatus (/Users/notrab/github.com/namehash/ensnode/packages/ensnode-sdk/src/client.ts:349:22)
          caused by: AggregateError [ECONNREFUSED]:
              at internalConnectMultiple (node:net:1134:18)
              at afterConnectMultiple (node:net:1715:7)
    }
  • Verified Biome formatting works on generated output
  • CI workflow validated (after push)

Notes for Reviewer (Optional)

  • Requires curl step inside the switch environment to trigger production build using the Mintlify API.
  • The hidden "Preview" page in docs is for internal use during PR reviews
  • CI check will fail on PRs where API changes haven't been reflected in the committed `openapi.json

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

Copilot AI review requested due to automatic review settings January 19, 2026 13:32
@vercel
Copy link
Contributor

vercel bot commented Jan 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Feb 9, 2026 8:35am
ensnode.io Ready Ready Preview, Comment Feb 9, 2026 8:35am
ensrainbow.io Ready Ready Preview, Comment Feb 9, 2026 8:35am

@changeset-bot
Copy link

changeset-bot bot commented Jan 19, 2026

🦋 Changeset detected

Latest commit: 5d563ea

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 18 packages
Name Type
@docs/mintlify Patch
ensapi Patch
ensindexer Patch
ensadmin Patch
ensrainbow Patch
fallback-ensapi Patch
@ensnode/datasources Patch
@ensnode/ensrainbow-sdk Patch
@ensnode/ponder-metadata Patch
@ensnode/ensnode-schema Patch
@ensnode/ensnode-react Patch
@ensnode/ponder-subgraph Patch
@ensnode/ensnode-sdk Patch
@ensnode/shared-configs Patch
@docs/ensnode Patch
@docs/ensrainbow Patch
@namehash/ens-referrals Patch
@namehash/namehash-ui Patch

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces automated OpenAPI specification generation and documentation for ENSApi. It adds CI infrastructure for generating specs via mock config mode, updates documentation to serve the generated spec, integrates Mintlify rebuild automation, and removes deprecated ENSAnalytics v1 routes.

Changes

Cohort / File(s) Summary
CI Workflow Infrastructure
.github/workflows/test_ci.yml, .github/workflows/deploy_switch_ensnode_environment.yml
Added openapi-sync-check job to validate OpenAPI spec freshness in CI; extended deploy workflow with Mintlify docs rebuild orchestration and success/failure Slack notifications.
Documentation Configuration & Pages
docs/docs.ensnode.io/docs.json, docs/docs.ensnode.io/ensapi/preview.mdx, docs/docs.ensnode.io/README.md
Updated API Reference URL to remote endpoint, added API Preview page with front matter, and expanded README with detailed sections on API generation, deployments, CI validation, and environment switching.
OpenAPI Generation & Tooling
docs/docs.ensnode.io/scripts/generate-openapi.ts, .github/scripts/generate_openapi_spec.sh, docs/docs.ensnode.io/package.json
Added TypeScript CLI script to fetch and validate OpenAPI specs with timeout/error handling; introduced Bash orchestration script for server startup, spec generation, and validation; added npm script and TypeScript dev dependencies.
ENSApi Configuration & Environment
apps/ensapi/src/config/config.schema.ts, apps/ensapi/src/config/environment.ts, apps/ensapi/src/config/openapi-mock-config.ts, apps/ensapi/.env.local.example
Introduced OPENAPI_GENERATE_MODE environment variable and boolean config flag; added buildOpenApiMockConfig function to provide mock dependencies for spec generation without external services; documented generate mode behavior and defaults.
ENSApi Core & Middleware
apps/ensapi/src/index.ts, apps/ensapi/src/middleware/openapi-generate-mode.middleware.ts
Removed v1 ENSAnalytics route and referrerLeaderboardCacheV1; introduced middleware to restrict request handling to /openapi.json endpoint only when in generate mode, returning 503 for other paths.
Test Infrastructure
apps/ensapi/src/config/config.schema.test.ts, apps/ensapi/src/config/openapi-mock-config.test.ts
Added tests for OPENAPI_GENERATE_MODE behavior including port parsing, namespace consistency, and warning logging; validated mock config port defaults and overrides.
Release & Configuration
.changeset/bright-waves-flow.md, .changeset/gentle-clouds-dance.md
Added two changeset entries documenting patch release for API documentation generation and OPENAPI_GENERATE_MODE feature introduction.

Sequence Diagram

sequenceDiagram
    participant CI as GitHub Actions CI
    participant Bash as generate_openapi_spec.sh
    participant Server as ENSApi Server<br/>(OPENAPI_GENERATE_MODE=true)
    participant TS as TypeScript Generation Script
    participant Docs as Documentation System
    
    CI->>Bash: Trigger spec generation
    Bash->>Server: Start ENSApi in generate mode
    activate Server
    Bash->>Bash: Poll /openapi.json until ready
    Bash->>Server: GET /openapi.json
    Server-->>Bash: OpenAPI spec (middleware allows)
    deactivate Server
    
    Bash->>TS: Execute generation command
    TS->>TS: Fetch & validate spec
    TS->>TS: Format with Biome
    TS-->>Bash: Write openapi.json
    
    alt --check mode
        Bash->>Bash: Compare with committed spec
        Bash-->>CI: Pass/Fail validation
    end
    
    Bash->>Docs: Spec available for documentation
    Docs->>Docs: Render API reference from spec
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 OpenAPI specs now generate with care,
Mock configs mock without a dare,
Documentation blooms from generate mode's light,
Middleware guards what's not in sight,
CI validates the spec's in sync—hop, we're alright! 📚✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main changes: adding OpenAPI spec sync with CI validation and PR preview capabilities.
Description check ✅ Passed The PR description follows the template with all required sections completed: Summary (3 bullets), Why (explaining the problem and benefits), Testing (with script output), Notes for Reviewer, and Pre-Review Checklist (both items checked).
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch openapi-spec-generator

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds OpenAPI specification generation and documentation capabilities to the ENSNode project. It introduces a script to fetch and save the OpenAPI spec from a running ENSApi instance, integrates it with Mintlify documentation, and adds CI checks to ensure the spec stays in sync with production.

Changes:

  • Added a 5,106-line OpenAPI specification file documenting all ENSApi endpoints
  • Created a generation script to fetch and update the spec from a running instance
  • Configured Mintlify docs to reference both production and local OpenAPI specs
  • Added CI workflow to validate spec synchronization on the main branch

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
docs/docs.ensnode.io/openapi.json Complete OpenAPI 3.1.0 specification documenting all ENSApi endpoints including resolution, meta, explore, and ENSAwards APIs
apps/ensapi/scripts/generate-openapi.ts TypeScript script to fetch OpenAPI spec from running instance and save to docs directory
apps/ensapi/package.json Added npm script openapi:generate to run the generation script
docs/docs.ensnode.io/docs.json Updated configuration to reference production API spec and added hidden preview section for local spec
.github/workflows/test_ci.yml Added CI job to verify OpenAPI spec stays in sync with production on main branch

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @.github/workflows/test_ci.yml:
- Around line 51-55: The openapi-sync-check job currently only runs when
github.ref == 'refs/heads/main', which lets PRs merge with out-of-sync specs;
update the condition on the openapi-sync-check job (the if: line) to also run
for pull requests (e.g., include github.event_name == 'pull_request' or
github.head_ref checks) and mark it non-blocking for PRs by using
continue-on-error: true (or alternatively remove the if entirely and rely on
branch protection or add contributor documentation). Target the job named
openapi-sync-check and the existing if: github.ref == 'refs/heads/main'
condition when making the change.

In `@apps/ensapi/scripts/generate-openapi.ts`:
- Around line 23-24: ensapiUrl may end with a trailing slash causing openapiUrl
to become "//openapi.json"; normalize ensapiUrl before composing openapiUrl (the
variables to change are ensapiUrl and openapiUrl in generate-openapi.ts) by
trimming any trailing '/' from ensapiUrl (or using a URL-safe join) so
openapiUrl is built as `${normalizedEnsapiUrl}/openapi.json` even when
ENSAPI_URL or process.argv[2] includes a trailing slash; ensure
DEFAULT_ENSAPI_URL remains fallback and normalization runs after selecting the
value.
- Line 12: Update the header comment string that reads "Writes openapi.json to
the docs directory for Mintilify" and correct the product name typo to
"Mintlify" so the comment reads "Writes openapi.json to the docs directory for
Mintlify"; locate this exact comment text in generate-openapi.ts and make the
single-word change.
- Around line 28-33: Add a timeout to the fetch in generate-openapi.ts by
creating an AbortSignal via AbortSignal.timeout(ms) and passing it as the signal
option to the fetch(openapiUrl) call; handle the abort case by catching the
thrown error (check for AbortError or error.name === 'AbortError') and log a
clear timeout message before exiting, and ensure the existing non-ok response
handling remains unchanged.

In `@docs/docs.ensnode.io/docs.json`:
- Around line 29-34: The docs.json "Preview" group references a non-existent
page "ensapi/preview"; fix by either adding a new page file named preview.mdx
under the docs/docs.ensnode.io/ensapi/ folder (so the path matches
"ensapi/preview") or remove the entire Preview group entry (the object with
"group": "Preview", "pages": ["ensapi/preview"], "openapi": "./openapi.json",
"hidden": true) from docs.json; update whichever you choose and ensure the
"pages" array references only existing MDX/MD files.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/workflows/test_ci.yml:
- Around line 58-70: The CI step runs the openapi:generate npm script which
currently relies on a hardcoded production default; make the behavior explicit
by passing the production URL via the ENSAPI_URL env in the workflow step or by
updating the openapi:generate script to accept an ENSAPI_URL argument and
default it to https://api.alpha.ensnode.io; update the step that invokes
openapi:generate to export ENSAPI_URL or call the script with the URL so the
action is explicit and maintainable.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@vercel vercel bot temporarily deployed to Preview – ensrainbow.io January 20, 2026 16:01 Inactive
Co-authored-by: lightwalker.eth <126201998+lightwalker-eth@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 17 changed files in this pull request and generated 1 comment.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 20 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +69 to 73
// use ENSAnalytics API at /ensanalytics
app.route("/ensanalytics", ensanalyticsApi);

// use ENSAnalytics API v1 at /v1/ensanalytics
app.route("/v1/ensanalytics", ensanalyticsApiV1);

// use Am I Realtime API at /amirealtime
app.route("/amirealtime", amIRealtimeApi);
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes the /v1/ensanalytics route (and the corresponding V1 cache shutdown) even though the repo still contains ensanalytics-api-v1.ts and V1 middleware/cache code. If this isn’t an intentional breaking API change, please restore the route (and graceful shutdown for the V1 cache) or remove the unused V1 implementation/tests in a dedicated PR and document the deprecation.

Copilot uses AI. Check for mistakes.

# Start ENSApi in background
cd "$REPO_ROOT"
OPENAPI_GENERATE_MODE=true pnpm --filter ensapi start &
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ENSAPI_PORT is documented as configurable, but the script never passes it through to the ENSApi process (via PORT). In OPENAPI_GENERATE_MODE, ENSApi will start on its default port, so setting ENSAPI_PORT will cause the readiness curl and generator to target the wrong port. Pass PORT=$ENSAPI_PORT (or set ENSAPI_PORT to also export PORT) when starting ENSApi.

Suggested change
OPENAPI_GENERATE_MODE=true pnpm --filter ensapi start &
PORT="$ENSAPI_PORT" OPENAPI_GENERATE_MODE=true pnpm --filter ensapi start &

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +18

const OUTPUT_PATH = resolve(import.meta.dirname, "../openapi.json");
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import.meta.dirname is not a standard Node.js import.meta property, so this path resolution is likely to throw at runtime under Node/tsx. Use import.meta.url + fileURLToPath/new URL() (or equivalent) to build an absolute path to ../openapi.json instead.

Suggested change
const OUTPUT_PATH = resolve(import.meta.dirname, "../openapi.json");
import { fileURLToPath } from "node:url";
const OUTPUT_PATH = fileURLToPath(new URL("../openapi.json", import.meta.url));

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

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)

130-141: 🧹 Nitpick | 🔵 Trivial

Normal path doesn't explicitly set inOpenApiGenerateMode.

This is fine since the schema has .default(false), but for readability and explicitness, consider adding inOpenApiGenerateMode: false to the normal config parse input. This makes it immediately clear to readers that the normal path never enables generate mode, without needing to trace back to the schema definition.

🤖 Fix all issues with AI agents
In @.github/scripts/generate_openapi_spec.sh:
- Around line 23-32: The for-loop iterating over "$@" uses shift (inside the
block handling --check) which is misleading because the for-loop snapshot isn't
affected; remove the shift or convert the loop to a proper while/shift pattern.
Specifically, either delete the shift line inside the case that sets
CHECK_MODE=true (leaving the for arg in "$@" intact and no positional
consumption), or replace the "for arg in \"$@\"; do ... done" with a "while [ $#
-gt 0 ]; do arg=\"$1\"; case $arg in ... esac; shift; done" pattern so the shift
actually consumes arguments; update references to CHECK_MODE and the --check
case accordingly.
- Around line 1-15: The script uses "set -e" but doesn't enable pipefail, so
failures in piped commands can be masked; update the generate_openapi_spec.sh
startup options by adding "set -o pipefail" (alongside the existing "set -e")
near the top of the script so that piped command failures cause the script to
fail fast and Surface errors during OpenAPI generation.

In `@apps/ensapi/src/config/config.schema.test.ts`:
- Around line 74-83: Update the test that calls buildConfigFromEnvironment with
OPENAPI_GENERATE_MODE: "false" to also assert the warning message content so we
guard the warning's origin; replace or augment
expect(logger.warn).toHaveBeenCalled() with an assertion like
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining("OPENAPI_GENERATE_MODE")
or the specific substring your code logs) or use
toHaveBeenCalledWith(expect.stringMatching(/invalid.*OPENAPI_GENERATE_MODE/i))
to verify the message includes the expected text from the warning emitted by
buildConfigFromEnvironment.

In `@apps/ensapi/src/config/openapi-mock-config.ts`:
- Line 23: The current assignment port: port || ENSApi_DEFAULT_PORT mixes types
(port is string | undefined, ENSApi_DEFAULT_PORT is number); update the
expression in openapi-mock-config.ts to use the nullish coalescing operator and
an explicit string conversion so the resulting type is consistently string —
e.g., use port ?? String(ENSApi_DEFAULT_PORT) (or, if you prefer number output,
use port ? Number(port) ?? ENSApi_DEFAULT_PORT); change the expression
referencing ENSApi_DEFAULT_PORT and port accordingly so PortSchema receives a
consistent type.

In `@apps/ensapi/src/index.ts`:
- Around line 69-70: The v1 ENSAnalytics endpoints are no longer mounted so
callers to /v1/ensanalytics/* will 404; fix by restoring a v1 mount or updating
consumers: either add a new route that mounts the existing ensanalyticsApi (or a
v1-compat wrapper) at app.route("/v1/ensanalytics", ensanalyticsApi) so requests
to /v1/ensanalytics/referrers and /v1/ensanalytics/referrers/:referrer work, or
update external callers (e.g., packages/ens-referrals/src/v1/client.ts) to use
/ensanalytics instead and remove any v1-specific files; locate usage around the
existing app.route("/ensanalytics", ensanalyticsApi) to implement the chosen
solution.

In `@docs/docs.ensnode.io/README.md`:
- Around line 46-52: Update the "Generating the Spec" section so it's explicit
which working directory to use: add a short note (e.g., "Run from the repository
root:") immediately above the code block referencing ./
.github/scripts/generate_openapi_spec.sh, or change the invocation to an
absolute/root-relative form that clearly runs from repo root; ensure the script
name generate_openapi_spec.sh is unchanged and the code block shows the correct
command to run from the repository root.
- Line 5: Split the long introductory sentence "Learn more about
[ENSNode](https://ensnode.io) from [the "Starlight" ENSNode
docs](https://ensnode.io/docs/). Everything from these "Starlight" docs is
planned to be transitioned into these Mintlify docs soon." into two shorter
sentences or insert a line break after the first clause so it reads as a brief
invitation (e.g., "Learn more about ENSNode from the 'Starlight' ENSNode docs."
followed by the transitional sentence about migrating content into Mintlify
docs); update the README.md entry containing that exact sentence accordingly for
improved raw Markdown readability.

Comment on lines +1 to +15
#!/bin/bash

# Generate OpenAPI spec from ENSApi
# This script can be used both locally and in CI
#
# Usage:
# ./generate_openapi_spec.sh # Generate spec (default)
# ./generate_openapi_spec.sh --check # Generate and verify against committed version
#
# Environment variables:
# ENSAPI_PORT - Port for ENSApi (default: 4334)
# STARTUP_TIMEOUT - Seconds to wait for server startup (default: 30)
# SKIP_VALIDATION - Set to "true" to skip Mintlify validation (default: false)

set -e
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add set -o pipefail for safer pipe error handling.

With set -e but no pipefail, failures in the left-hand side of pipes (e.g., if curl fails but output is piped through sed/tail later in other scripts) can be silently swallowed. Since this script uses set -e, adding pipefail ensures consistent fail-fast behavior.

Proposed fix
-set -e
+set -euo pipefail
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#!/bin/bash
# Generate OpenAPI spec from ENSApi
# This script can be used both locally and in CI
#
# Usage:
# ./generate_openapi_spec.sh # Generate spec (default)
# ./generate_openapi_spec.sh --check # Generate and verify against committed version
#
# Environment variables:
# ENSAPI_PORT - Port for ENSApi (default: 4334)
# STARTUP_TIMEOUT - Seconds to wait for server startup (default: 30)
# SKIP_VALIDATION - Set to "true" to skip Mintlify validation (default: false)
set -e
#!/bin/bash
# Generate OpenAPI spec from ENSApi
# This script can be used both locally and in CI
#
# Usage:
# ./generate_openapi_spec.sh # Generate spec (default)
# ./generate_openapi_spec.sh --check # Generate and verify against committed version
#
# Environment variables:
# ENSAPI_PORT - Port for ENSApi (default: 4334)
# STARTUP_TIMEOUT - Seconds to wait for server startup (default: 30)
# SKIP_VALIDATION - Set to "true" to skip Mintlify validation (default: false)
set -euo pipefail
🤖 Prompt for AI Agents
In @.github/scripts/generate_openapi_spec.sh around lines 1 - 15, The script
uses "set -e" but doesn't enable pipefail, so failures in piped commands can be
masked; update the generate_openapi_spec.sh startup options by adding "set -o
pipefail" (alongside the existing "set -e") near the top of the script so that
piped command failures cause the script to fail fast and Surface errors during
OpenAPI generation.

Comment on lines +23 to +32
# Parse arguments
CHECK_MODE=false
for arg in "$@"; do
case $arg in
--check)
CHECK_MODE=true
shift
;;
esac
done
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

shift inside a for arg in "$@" loop is misleading.

The shift on line 29 modifies positional parameters but doesn't affect the for loop's iteration (which snapshots "$@" at the start). It's harmless but misleading—readers may think it's skipping the next argument. Consider removing it or switching to a while loop if you need to consume arguments.

Proposed cleanup
 # Parse arguments
 CHECK_MODE=false
 for arg in "$@"; do
   case $arg in
     --check)
       CHECK_MODE=true
-      shift
       ;;
   esac
 done
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Parse arguments
CHECK_MODE=false
for arg in "$@"; do
case $arg in
--check)
CHECK_MODE=true
shift
;;
esac
done
# Parse arguments
CHECK_MODE=false
for arg in "$@"; do
case $arg in
--check)
CHECK_MODE=true
;;
esac
done
🤖 Prompt for AI Agents
In @.github/scripts/generate_openapi_spec.sh around lines 23 - 32, The for-loop
iterating over "$@" uses shift (inside the block handling --check) which is
misleading because the for-loop snapshot isn't affected; remove the shift or
convert the loop to a proper while/shift pattern. Specifically, either delete
the shift line inside the case that sets CHECK_MODE=true (leaving the for arg in
"$@" intact and no positional consumption), or replace the "for arg in \"$@\";
do ... done" with a "while [ $# -gt 0 ]; do arg=\"$1\"; case $arg in ... esac;
shift; done" pattern so the shift actually consumes arguments; update references
to CHECK_MODE and the --check case accordingly.

Comment on lines +74 to +83
it("logs warning when OPENAPI_GENERATE_MODE has invalid value", async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(serializeENSIndexerPublicConfig(ENSINDEXER_PUBLIC_CONFIG)),
});

await buildConfigFromEnvironment({ ...BASE_ENV, OPENAPI_GENERATE_MODE: "false" });

expect(logger.warn).toHaveBeenCalled();
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider asserting the warning message content.

The test confirms logger.warn was called but doesn't verify the message. Asserting on the message string (or at least a substring) would guard against regressions where the warning is triggered by a different code path.

Suggested improvement
-    expect(logger.warn).toHaveBeenCalled();
+    expect(logger.warn).toHaveBeenCalledWith(
+      expect.stringContaining("OPENAPI_GENERATE_MODE is set to 'false'"),
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("logs warning when OPENAPI_GENERATE_MODE has invalid value", async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(serializeENSIndexerPublicConfig(ENSINDEXER_PUBLIC_CONFIG)),
});
await buildConfigFromEnvironment({ ...BASE_ENV, OPENAPI_GENERATE_MODE: "false" });
expect(logger.warn).toHaveBeenCalled();
});
it("logs warning when OPENAPI_GENERATE_MODE has invalid value", async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve(serializeENSIndexerPublicConfig(ENSINDEXER_PUBLIC_CONFIG)),
});
await buildConfigFromEnvironment({ ...BASE_ENV, OPENAPI_GENERATE_MODE: "false" });
expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining("OPENAPI_GENERATE_MODE is set to 'false'"),
);
});
🤖 Prompt for AI Agents
In `@apps/ensapi/src/config/config.schema.test.ts` around lines 74 - 83, Update
the test that calls buildConfigFromEnvironment with OPENAPI_GENERATE_MODE:
"false" to also assert the warning message content so we guard the warning's
origin; replace or augment expect(logger.warn).toHaveBeenCalled() with an
assertion like
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining("OPENAPI_GENERATE_MODE")
or the specific substring your code logs) or use
toHaveBeenCalledWith(expect.stringMatching(/invalid.*OPENAPI_GENERATE_MODE/i))
to verify the message includes the expected text from the warning emitted by
buildConfigFromEnvironment.

*/
export function buildOpenApiMockConfig(port?: string): EnsApiConfigInput {
return {
port: port || ENSApi_DEFAULT_PORT,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Minor: port || ENSApi_DEFAULT_PORT mixes types.

The port parameter is string | undefined, while ENSApi_DEFAULT_PORT is a number (4334). The || fallback produces string | number. This works because PortSchema coerces both, but it's a subtle type mismatch. Consider using port ?? String(ENSApi_DEFAULT_PORT) or port ?? ENSApi_DEFAULT_PORT with a note, for explicit intent. Not a bug since the schema handles it.

🤖 Prompt for AI Agents
In `@apps/ensapi/src/config/openapi-mock-config.ts` at line 23, The current
assignment port: port || ENSApi_DEFAULT_PORT mixes types (port is string |
undefined, ENSApi_DEFAULT_PORT is number); update the expression in
openapi-mock-config.ts to use the nullish coalescing operator and an explicit
string conversion so the resulting type is consistently string — e.g., use port
?? String(ENSApi_DEFAULT_PORT) (or, if you prefer number output, use port ?
Number(port) ?? ENSApi_DEFAULT_PORT); change the expression referencing
ENSApi_DEFAULT_PORT and port accordingly so PortSchema receives a consistent
type.

Comment on lines +69 to 70
// use ENSAnalytics API at /ensanalytics
app.route("/ensanalytics", ensanalyticsApi);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check for remaining references to v1 ensanalytics or referrerLeaderboardCacheV1
rg -n "v1.*ensanalytics|ensanalytics.*v1|referrerLeaderboardCacheV1" --type ts

Repository: namehash/ensnode

Length of output: 1843


🏁 Script executed:

# Get the full context of the index.ts file around lines 69-70
cat -n apps/ensapi/src/index.ts | head -80 | tail -20

Repository: namehash/ensnode

Length of output: 632


🏁 Script executed:

# Check git history to see what was removed from this route
git log --oneline -20 -- apps/ensapi/src/index.ts

Repository: namehash/ensnode

Length of output: 104


🏁 Script executed:

# Check if there are any references in documentation or comments about v1 endpoints
rg -n "v1.*ensanalytics|ensanalytics.*v1" --type md --type json --type yaml

Repository: namehash/ensnode

Length of output: 42


🏁 Script executed:

# Check what ensanalyticsApi is and where it's defined
rg -n "ensanalyticsApi" --type ts -A 3

Repository: namehash/ensnode

Length of output: 576


🏁 Script executed:

# Look at the ensanalytics-api-v1.ts handler to see its routes
cat -n apps/ensapi/src/handlers/ensanalytics-api-v1.ts | head -50

Repository: namehash/ensnode

Length of output: 2109


🏁 Script executed:

# Check if ensanalyticsApi imports or includes the v1 handler
fd "ensanalytics-api" --type f --extension ts | head -10

Repository: namehash/ensnode

Length of output: 255


🏁 Script executed:

# Check the imports in index.ts to see where ensanalyticsApi comes from
head -50 apps/ensapi/src/index.ts | grep -n "import\|ensanalytics"

Repository: namehash/ensnode

Length of output: 1194


🏁 Script executed:

# Check the ensanalytics-api.ts handler to see if it includes v1 routes
cat -n apps/ensapi/src/handlers/ensanalytics-api.ts

Repository: namehash/ensnode

Length of output: 7531


🏁 Script executed:

# Check if ensanalytics-api-v1.ts is imported anywhere else
rg -n "ensanalytics-api-v1|from.*ensanalytics-api-v1" --type ts

Repository: namehash/ensnode

Length of output: 560


🏁 Script executed:

# Check the git diff to see what changed in index.ts
git diff HEAD~10 -- apps/ensapi/src/index.ts 2>/dev/null || echo "Could not get git diff, checking current content"

Repository: namehash/ensnode

Length of output: 108


Address breaking change: v1 ENSAnalytics route is no longer served.

The v1 route has been removed from the main app (not mounted in index.ts), but external consumers like packages/ens-referrals/src/v1/client.ts still depend on it. Requests to /v1/ensanalytics/referrers and /v1/ensanalytics/referrers/:referrer will now fail.

Either restore the v1 route in index.ts, update external consumers to use the non-v1 endpoint at /ensanalytics, or remove v1-related files and dependencies entirely.

🤖 Prompt for AI Agents
In `@apps/ensapi/src/index.ts` around lines 69 - 70, The v1 ENSAnalytics endpoints
are no longer mounted so callers to /v1/ensanalytics/* will 404; fix by
restoring a v1 mount or updating consumers: either add a new route that mounts
the existing ensanalyticsApi (or a v1-compat wrapper) at
app.route("/v1/ensanalytics", ensanalyticsApi) so requests to
/v1/ensanalytics/referrers and /v1/ensanalytics/referrers/:referrer work, or
update external callers (e.g., packages/ens-referrals/src/v1/client.ts) to use
/ensanalytics instead and remove any v1-specific files; locate usage around the
existing app.route("/ensanalytics", ensanalyticsApi) to implement the chosen
solution.


[docs.ensnode.io](https://docs.ensnode.io) runs on [Mintlify](https://mintlify.com).

Learn more about [ENSNode](https://ensnode.io) from [the "Starlight" ENSNode docs](https://ensnode.io/docs/). Everything from these "Starlight" docs is planned to be transitioned into these Mintlify docs soon.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider splitting the long introductory sentence for readability.

This line is quite long. A line break or splitting into two sentences would improve readability in raw Markdown.

🤖 Prompt for AI Agents
In `@docs/docs.ensnode.io/README.md` at line 5, Split the long introductory
sentence "Learn more about [ENSNode](https://ensnode.io) from [the "Starlight"
ENSNode docs](https://ensnode.io/docs/). Everything from these "Starlight" docs
is planned to be transitioned into these Mintlify docs soon." into two shorter
sentences or insert a line break after the first clause so it reads as a brief
invitation (e.g., "Learn more about ENSNode from the 'Starlight' ENSNode docs."
followed by the transitional sentence about migrating content into Mintlify
docs); update the README.md entry containing that exact sentence accordingly for
improved raw Markdown readability.

Comment on lines +46 to +52
### Generating the Spec

When you modify API routes or schemas in ENSApi, regenerate the OpenAPI spec:

```bash
./.github/scripts/generate_openapi_spec.sh
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify the working directory for the generation script.

The "Generating the Spec" section follows the "Local Development" section which instructs cd docs/docs.ensnode.io. The script path ./.github/scripts/generate_openapi_spec.sh is relative to the repository root, not the docs directory. A reader following the README top-to-bottom may already be in the docs/docs.ensnode.io directory.

Consider adding a note like "Run from the repository root:" above the code block to prevent confusion.

🤖 Prompt for AI Agents
In `@docs/docs.ensnode.io/README.md` around lines 46 - 52, Update the "Generating
the Spec" section so it's explicit which working directory to use: add a short
note (e.g., "Run from the repository root:") immediately above the code block
referencing ./ .github/scripts/generate_openapi_spec.sh, or change the
invocation to an absolute/root-relative form that clearly runs from repo root;
ensure the script name generate_openapi_spec.sh is unchanged and the code block
shows the correct command to run from the repository root.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants