Skip to content

Add handler type-checking to mock indexer fixtures - #1470

Merged
DZakh merged 9 commits into
mainfrom
claude/mock-indexer-handlers-plan-shi26i
Jul 23, 2026
Merged

Add handler type-checking to mock indexer fixtures#1470
DZakh merged 9 commits into
mainfrom
claude/mock-indexer-handlers-plan-shi26i

Conversation

@DZakh

@DZakh DZakh commented Jul 22, 2026

Copy link
Copy Markdown
Member

What

Lets an internal test-indexer fixture type-check TypeScript handler source (import { indexer } from "envio"; indexer.onEvent(...)) against the indexer surface generated from its own inline config.yaml + schema.graphql. This is the type-test half of the tooling; running the handlers is intentionally out of scope for now.

InternalTestIndexer.fromUserApi(
  ~schema,
  ~handlers=`
    import { indexer } from "envio";
    import { expectType, type TypeEqual } from "ts-expect";
    indexer.onEvent({ contract: "Token", event: "Transfer" }, async ({ event, context }) => {
      expectType<TypeEqual<typeof event.params.value, bigint>>(true);
      context.Account.set({ id: event.params.to, balance: event.params.value });
    });
  `,
  ~configYaml,
)

A handler type error throws with the compiler diagnostics.

How

  • napi from_user_api — parses an inline config the way a user's project would (no filesystem/env access) and, when with_indexer_types is set, also returns the generated .envio/types.d.ts (the same TypeScript production codegen writes, via ProjectTemplate::from_config) from the same single parse. Returns { config, indexer_types }. Surfaced in ReScript as Core.fromUserApi.
  • TypeChecker.ts — type-checks the handlers in an isolated, in-process ts.Program using the same compiler options as the init-template tsconfig.json, pinned to the same TypeScript version the templates pin. The generated declare module "envio" augmentation is injected as a virtual file, so each fixture binds indexer to its own config for that program only — independent fixtures never collide on the global augmentation. The envio + lib .d.ts graph is parsed once and its SourceFiles (plus the prior program structure) are reused across calls, so only the two per-fixture virtual files re-parse.
  • InternalTestIndexer.fromUserApi — the fixture builder; gains an optional ~handlers (throws on any handler type error). configYaml is a labeled argument.

Tests

  • MockIndexerHandlers_test — a clean handler set type-checks; a nonexistent event name throws the exact diagnostic Type '"Nonexistent"' is not assignable to type '"Transfer"'.
  • UserApiValidation_test (config parsing/validation) and ColumnNameFormat_test route through InternalTestIndexer.fromUserApi; cross-package test_codegen (YamlConfigIndexer_test, ClickHouse_test) too.
  • Rust envio_types_dts snapshots (evm/fuel/svm) unchanged.

Summary by CodeRabbit

  • New Features

    • Added an API for generating TypeScript indexer type definitions from YAML configuration.
    • Exposed generated type definitions through the core SDK for tooling and validation workflows.
  • Bug Fixes

    • Improved handler type validation, including clearer errors for invalid event references and mismatched event parameter types.
  • Tests

    • Expanded coverage for YAML configuration parsing, generated types, handler validation, and database-related configuration scenarios.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QM68d5PWeGDSiVGh5xDiM4

claude added 2 commits July 22, 2026 14:11
Lets a mock indexer config type-check TypeScript handler source against the
`indexer` surface generated from its own config + schema. Each check runs in an
isolated in-process TS program, so independent configs never collide on the
global `envio` module augmentation.

- napi: `generate_indexer_types` returns the generated `.envio/types.d.ts` for
  an inline config (reuses `ProjectTemplate::from_config`), mirroring
  `parse_config_yaml`'s inputs.
- Core: `generateIndexerTypes` binding.
- envio-tests: `TypeChecker.ts` type-checks handlers via the TS compiler API
  with the init-template tsconfig options; `MockIndexerConfig.parseYaml` gains
  `~handlers`, throwing on any type error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QM68d5PWeGDSiVGh5xDiM4
The helper now builds a mock indexer fixture (config + optional handler
type-check), so the `Config` suffix and `parseYaml` name no longer fit. Updates
all call sites in envio-tests and test_codegen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QM68d5PWeGDSiVGh5xDiM4
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds YAML-based generation of .envio/types.d.ts, exposes it through the ReScript and N-API layers, type-checks mock handlers against generated definitions, and routes configuration-focused tests through InternalTestIndexer.fromUserApi.

Changes

YAML indexer types and fixture validation

Layer / File(s) Summary
Expose YAML-based indexer type generation
packages/envio/src/Core.res, packages/cli/src/napi.rs, packages/cli/src/hbs_templating/codegen_templates.rs
Adds the generateIndexerTypes API, centralizes YAML option parsing, constructs ProjectTemplate, and returns generated indexer declarations.
Add fixture handler type checking
packages/envio-tests/test/helpers/InternalTestIndexer.res, packages/envio-tests/test/helpers/TypeChecker.ts, packages/envio-tests/test/MockIndexerHandlers_test.res, packages/envio-tests/package.json
Generates indexer types, checks virtual handler sources with TypeScript, adds TypeScript tooling, and tests valid and invalid handler types.
Route tests through InternalTestIndexer
packages/envio-tests/test/FromUserApi_test.res, packages/envio-tests/test/lib_tests/ColumnNameFormat_test.res, scenarios/test_codegen/test/YamlConfigIndexer_test.res, scenarios/test_codegen/test/lib_tests/ClickHouse_test.res, scenarios/test_codegen/test/helpers/MockIndexer.res
Updates configuration fixtures and related documentation to use InternalTestIndexer.fromUserApi with explicit schema, YAML, environment, and virtual-file inputs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Test
  participant InternalTestIndexer
  participant Core
  participant NAPI
  participant ProjectTemplate
  participant TypeChecker
  Test->>InternalTestIndexer: fromUserApi(configYaml, handlers)
  InternalTestIndexer->>Core: parseConfigYaml(configYaml)
  InternalTestIndexer->>Core: generateIndexerTypes(configYaml)
  Core->>NAPI: generateIndexerTypes(yaml, options)
  NAPI->>ProjectTemplate: build generated template
  ProjectTemplate-->>NAPI: indexer_types_dts()
  NAPI-->>Core: types.d.ts content
  InternalTestIndexer->>TypeChecker: checkHandlerTypes(typesDts, handlers)
  TypeChecker-->>InternalTestIndexer: diagnostics
  InternalTestIndexer-->>Test: config or handler type errors
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding handler type-checking to mock indexer fixtures.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/envio/src/Core.res (1)

224-225: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove comments that restate the API or caller flow.

  • packages/envio/src/Core.res#L224-L225: remove the generated-types API description.
  • packages/envio-tests/test/helpers/MockIndexerFixture.res#L1-L2: retain only a non-obvious compiler-API constraint, if needed.
  • packages/envio-tests/test/helpers/MockIndexerFixture.res#L11-L13: remove the fromYaml behavior narration.
  • scenarios/test_codegen/test/helpers/MockIndexer.res#L410-L410: remove the fixture-provenance pointer.

As per coding guidelines, comments must not restate what code does or point to where values are defined.

🤖 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 `@packages/envio/src/Core.res` around lines 224 - 225, Remove the
API/caller-flow comment in packages/envio/src/Core.res at lines 224-225; in
packages/envio-tests/test/helpers/MockIndexerFixture.res at lines 1-2, retain
only any genuinely non-obvious compiler-API constraint; remove the fromYaml
behavior narration at lines 11-13; and remove the fixture-provenance pointer in
scenarios/test_codegen/test/helpers/MockIndexer.res at line 410. Keep code
unchanged and retain comments only when they document non-obvious constraints.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@packages/envio/src/Core.res`:
- Around line 224-225: Remove the API/caller-flow comment in
packages/envio/src/Core.res at lines 224-225; in
packages/envio-tests/test/helpers/MockIndexerFixture.res at lines 1-2, retain
only any genuinely non-obvious compiler-API constraint; remove the fromYaml
behavior narration at lines 11-13; and remove the fixture-provenance pointer in
scenarios/test_codegen/test/helpers/MockIndexer.res at line 410. Keep code
unchanged and retain comments only when they document non-obvious constraints.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 96ede47b-d5db-4f20-93c0-b616b35624c4

📥 Commits

Reviewing files that changed from the base of the PR and between 2ef5bd1 and 7799f7c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • packages/cli/src/hbs_templating/codegen_templates.rs
  • packages/cli/src/napi.rs
  • packages/envio-tests/package.json
  • packages/envio-tests/test/ConfigYaml_test.res
  • packages/envio-tests/test/MockIndexerHandlers_test.res
  • packages/envio-tests/test/helpers/MockIndexerConfig.res
  • packages/envio-tests/test/helpers/MockIndexerFixture.res
  • packages/envio-tests/test/helpers/TypeChecker.ts
  • packages/envio-tests/test/lib_tests/ColumnNameFormat_test.res
  • packages/envio/src/Core.res
  • scenarios/test_codegen/test/YamlConfigIndexer_test.res
  • scenarios/test_codegen/test/helpers/MockIndexer.res
💤 Files with no reviewable changes (1)
  • packages/envio-tests/test/helpers/MockIndexerConfig.res

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7799f7cf7a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

[handlersPath, handlers],
]);

const host = ts.createCompilerHost(compilerOptions, true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve type roots from the fixture package

createCompilerHost leaves getCurrentDirectory() as process.cwd(), so the types: ["node"] lookup is based on whichever package launched the test rather than helpersDir/envio-tests. When MockIndexerFixture.fromYaml(~handlers=...) is called from a package that does not also expose @types/node, the fixture reports a global TS2688 even though envio-tests declares that dependency; importing checkHandlerTypes from the repo root reproduces this. Set the compiler host current directory to helpersDir (or parse a tsconfig rooted there) before creating the program.

Useful? React with 👍 / 👎.

…gYaml

- MockIndexerFixture -> InternalTestIndexer, fromYaml -> fromUserApi (it builds
  a fixture through the user-facing public boundary).
- Make the config YAML a labeled ~configYaml argument at all call sites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QM68d5PWeGDSiVGh5xDiM4
claude added 2 commits July 23, 2026 09:54
`createCompilerHost`'s current directory defaults to `process.cwd()`, so
`types: ["node"]` resolution depended on the launching package rather than
envio-tests — a caller without `@types/node` reachable from its cwd got a
spurious global TS2688. Point the host at helpersDir so type-root lookup is
stable across packages.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QM68d5PWeGDSiVGh5xDiM4
…andlers-plan-shi26i

# Conflicts:
#	packages/envio-tests/test/ConfigYaml_test.res
Matches the module it exercises (InternalTestIndexer.fromUserApi).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QM68d5PWeGDSiVGh5xDiM4

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
scenarios/test_codegen/test/helpers/MockIndexer.res (1)

410-410: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the parser-origin comment.

Line 410 only identifies where callers obtain customConfig; line 411 already documents its non-obvious default behavior.

Proposed cleanup
-    // A config parsed through the user-facing pipeline (InternalTestIndexer.fromUserApi).
     // Defaults to the generated project config.

As per coding guidelines, “Don't write a comment that restates what the code already says … which callers use a value, history of a refactor, or pointers to where something is ‘now defined’.”

🤖 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 `@scenarios/test_codegen/test/helpers/MockIndexer.res` at line 410, Remove the
parser-origin comment immediately above the customConfig setup; retain the
existing comment documenting its non-obvious default behavior.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@scenarios/test_codegen/test/helpers/MockIndexer.res`:
- Line 410: Remove the parser-origin comment immediately above the customConfig
setup; retain the existing comment documenting its non-obvious default behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d5ac382a-70cd-45fa-a0ae-3e3c5f702f0e

📥 Commits

Reviewing files that changed from the base of the PR and between 7799f7c and 4b1e692.

📒 Files selected for processing (10)
  • packages/cli/src/hbs_templating/codegen_templates.rs
  • packages/envio-tests/test/FromUserApi_test.res
  • packages/envio-tests/test/MockIndexerHandlers_test.res
  • packages/envio-tests/test/helpers/InternalTestIndexer.res
  • packages/envio-tests/test/helpers/TypeChecker.ts
  • packages/envio-tests/test/lib_tests/ColumnNameFormat_test.res
  • packages/envio/src/Core.res
  • scenarios/test_codegen/test/YamlConfigIndexer_test.res
  • scenarios/test_codegen/test/helpers/MockIndexer.res
  • scenarios/test_codegen/test/lib_tests/ClickHouse_test.res
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/envio-tests/test/MockIndexerHandlers_test.res
  • packages/envio-tests/test/helpers/TypeChecker.ts
  • packages/envio/src/Core.res

The comment only named which callers supply the value; the line below already
documents its non-obvious default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QM68d5PWeGDSiVGh5xDiM4

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 789d2b09ea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

~handlers=`
import { indexer } from "envio";
import type { Address } from "envio";
import { expectType, type TypeEqual } from "ts-expect";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use a resolvable type assertion helper

When this positive handler fixture is type-checked, this import is resolved by the new checkHandlerTypes program; the locked ts-expect@1.3.0 package points its typings at dist/index.d.ts, but the installed package contents do not include dist, so the fixture reports TS2307: Cannot find module 'ts-expect' before it ever validates the generated indexer types. Avoid this dependency in the inline handler or switch to an assertion package whose declarations are actually published.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This looks like a false positive. ts-expect@1.3.0 does ship its declarations — the installed package contains node_modules/ts-expect/dist/index.d.ts (which is exactly what "typings" points to), so the import resolves. The positive fixture confirms it: it type-checks to zero diagnostics, and a TS2307 on this import would surface as a handler-file diagnostic and make fromUserApi throw, failing the test. No change needed.


Generated by Claude Code

Collapse the `parse_config_yaml` + `generate_indexer_types` napi exports into a
single `from_user_api` that parses the inline config once and, when asked,
returns the generated `.envio/types.d.ts` alongside the public config JSON —
removing the double-parse and the risk of the runtime config drifting from the
types. Drop the unused `isRescript` option from the test helper and napi
surface.

Narrow the handler type-check to diagnostics inferred in the handler source
(plus global config errors); the generated `.d.ts` is trusted, not re-checked.
Assert the TypeChecker's TypeScript version matches the init-template pin so the
two can't silently diverge, and cache the unchanged declaration graph + reuse
the prior program across fixtures so each check no longer re-parses `envio` and
`@types/node`.

Assert the exact diagnostic in the negative handler test, and rename the
config-validation test to UserApiValidation_test.


Claude-Session: https://claude.ai/code/session_018jWRbYWgh5DQ74GF6NDdu2

Co-authored-by: Claude <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 434101769d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

};

const typesPath = path.join(helpersDir, "__mock_indexer_types.d.ts");
const handlersPath = path.join(helpersDir, "__mock_indexer_handlers.ts");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve handler imports from the caller package

When ~handlers is used from another test package, every import in the virtual handler is resolved as if the file lived under packages/envio-tests/test/helpers. That makes valid project handlers fail with TS2307 for dependencies that are declared by the caller but not by envio-tests itself (for example scenarios/test_codegen declares viem and its real handlers import it, but this helper would search from envio-tests instead of that scenario). Root the virtual handler in the caller/project directory, or accept a base directory for module resolution, so the fixture matches production handler compilation.

Useful? React with 👍 / 👎.


type fromUserApiResult = {
config: string,
indexerTypes: Null.t<string>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Model omitted indexerTypes as nullable

When withIndexerTypes is false, the native object omits indexerTypes rather than setting it to null, but this binding exposes it as Null.t<string>. Any ReScript caller that converts the field with Null.toOption will treat undefined as Some(undefined) (instead of None) and can then pass an undefined string into the type checker or other string consumers. Use a binding that handles both null and undefined, or make the Rust side always populate the field with null.

Useful? React with 👍 / 👎.

@DZakh
DZakh merged commit 47fae3e into main Jul 23, 2026
8 checks passed
@DZakh
DZakh deleted the claude/mock-indexer-handlers-plan-shi26i branch July 23, 2026 14:44
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