Skip to content

Use project's installed envio binary in e2e tests - #1157

Merged
DZakh merged 7 commits into
mainfrom
claude/fix-e2e-dependency-tests-t8XxP
Apr 24, 2026
Merged

Use project's installed envio binary in e2e tests#1157
DZakh merged 7 commits into
mainfrom
claude/fix-e2e-dependency-tests-t8XxP

Conversation

@DZakh

@DZakh DZakh commented Apr 24, 2026

Copy link
Copy Markdown
Member

Summary

Modified the isolated dependency e2e tests to invoke envio via the project's installed binary instead of the e2e-tests' envio command. This ensures that bin.mjs and user handlers load the same envio module instance.

Key Changes

  • Added projectEnvioCommand and projectEnvioArgs variables to reference the project's installed envio binary (node_modules/envio/bin.mjs)
  • Updated envio dev command invocation to use the project's binary instead of config.envioCommand
  • Updated envio stop command invocation to use the project's binary instead of config.envioCommand
  • Added detailed comment explaining why this approach is necessary

Implementation Details

The previous approach of using config.envioCommand (the e2e-tests envio) would create two separate copies of the envio module in the same process. This caused handlers to appear unregistered because HandlerRegister.setHandler would write to one module instance's dictionary while applyRegistrations would read from the other. By using the project's installed binary, both the handlers and the indexer load from the same envio module instance, ensuring proper handler registration.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT

Summary by CodeRabbit

  • Chores

    • Removed the PR build-and-verify CI workflow.
  • Refactor

    • Centralized module registration state into a single, process-wide registry to simplify internal state handling and reduce duplication.

After #1116 moved the CLI into in-process bin.mjs, running envio via the
e2e-tests workspace's bin.mjs while the handler imports `generated` →
`envio` resolved to the project's freshly installed envio produced two
distinct envio module instances in one process. HandlerRegister state
lives per-instance, so `indexer.onEvent` wrote to the project's dict
while `applyRegistrations` read the e2e-tests dict, causing every event
to surface as "no event handler" and chain 1 to fail with "Nothing to
fetch".

Invoke envio through `<projectDir>/node_modules/envio/bin.mjs` so bin.mjs
and the handlers resolve to the same physical envio, matching the real
`npx envio dev` flow.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT
@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

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

Replaces module-local handler registration state with a shared process-wide registry on globalThis.__envioRegistry (shared eventRegistrations, activeRegistration, preRegistered) with a version compatibility check; deletes the PR build-and-verify GitHub Actions workflow.

Changes

Cohort / File(s) Summary
CI/CD Workflow
\.github/workflows/build_and_verify_pr.yml
Removed the pull-request build-and-verify GitHub Actions workflow (previously ran Rust/NAPI builds, uploaded artifacts, ran clippy/tests, and executed template/scenario/e2e jobs with Postgres/Hasura provisioning).
Handler Registration
packages/envio/src/HandlerRegister.res
Replaced per-module registration state with a shared globalThis.__envioRegistry. Moved eventRegistrations, activeRegistration (now a shared ref<option<...>>), and preRegistered into the registry; added a version check that errors on incompatible registry shapes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped from locals to one global den,
Registry stitched so handlers meet again.
Workflows folded, quiet as a stream—
I munched my carrot and dreamed of clean beam.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Use project's installed envio binary in e2e tests' is directly related to the main objectives of the changeset, which focuses on using the project's installed envio binary instead of the e2e-tests' envio command to fix dependency issues in end-to-end tests.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-e2e-dependency-tests-t8XxP

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.

The NAPI migration (#1116) is merged to main, so the stopgap
`build_and_verify_pr.yml` (which ran on `pull_request` so PR-modified
build steps were exercised) is no longer needed. `build_and_verify.yml`
already covers PRs via `pull_request_target`.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT

@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 (2)
packages/e2e-tests/src/dependency-tests/install.test.ts (2)

203-214: Minor: guard envio stop against a pre-assignment beforeAll failure.

If beforeAll throws before line 170 (e.g., pm install rejects, or the base-project copy fails), projectEnvioCommand/projectEnvioArgs/projectDir are undefined when afterAll runs. The .catch(() => {}) swallows the resulting rejection, but it’s cleaner — and less confusing in logs — to skip the stop call entirely when it was never started.

🛡️ Proposed guard
-      // docker compose down -v (removes containers + volumes for clean next run)
-      await runCommand(projectEnvioCommand, [...projectEnvioArgs, "stop"], {
-        cwd: projectDir,
-        timeout: 30_000,
-      }).catch(() => {});
+      // docker compose down -v (removes containers + volumes for clean next run)
+      if (projectEnvioCommand && projectDir) {
+        await runCommand(projectEnvioCommand, [...projectEnvioArgs, "stop"], {
+          cwd: projectDir,
+          timeout: 30_000,
+        }).catch(() => {});
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/e2e-tests/src/dependency-tests/install.test.ts` around lines 203 -
214, The afterAll cleanup calls runCommand(projectEnvioCommand,
[...projectEnvioArgs, "stop"], { cwd: projectDir }) even when beforeAll may have
failed and those variables are undefined; update the afterAll block (the
existing afterAll function and its use of projectEnvioCommand, projectEnvioArgs,
projectDir) to skip the envio stop call when the env vars weren't initialized —
either check that projectEnvioCommand, projectEnvioArgs, and projectDir are all
truthy before calling runCommand, or use a boolean flag set in beforeAll (e.g.,
envioStarted) and only run the stop when that flag is true; keep the existing
catch for safety.

170-171: Consider resolving bin.mjs via package.json’s bin field rather than hardcoding.

config.ts::resolveEnvio deliberately reads pkg.bin from envio/package.json (see packages/e2e-tests/src/config.ts:36-74) so the entry point isn’t tied to a specific filename. Here the path is hardcoded to node_modules/envio/bin.mjs, which will silently break if the package ever renames or relocates its bin. Not critical today (the file does exist), but mirroring resolveEnvio — scoped to projectDir’s node_modules — keeps the two code paths consistent.

♻️ Sketch
-      projectEnvioCommand = "node";
-      projectEnvioArgs = [path.join(projectDir, "node_modules", "envio", "bin.mjs")];
+      const projectRequire = createRequire(path.join(projectDir, "package.json"));
+      const pkgJsonPath = projectRequire.resolve("envio/package.json");
+      const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
+      const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.envio;
+      projectEnvioCommand = "node";
+      projectEnvioArgs = [path.resolve(path.dirname(pkgJsonPath), binRel)];

(Requires import { createRequire } from "node:module";.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/e2e-tests/src/dependency-tests/install.test.ts` around lines 170 -
171, The test hardcodes the envio CLI path in projectEnvioArgs to
"node_modules/envio/bin.mjs", which can break if the package's bin changes;
instead resolve the bin via the package.json like resolveEnvio in
packages/e2e-tests/src/config.ts: use Node's module resolution (e.g.,
createRequire) scoped to projectDir to require("envio/package.json") and read
pkg.bin to compute the actual entry point, then set projectEnvioArgs to that
resolved path so the test mirrors resolveEnvio and avoids a brittle hardcoded
filename.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/e2e-tests/src/dependency-tests/install.test.ts`:
- Around line 203-214: The afterAll cleanup calls
runCommand(projectEnvioCommand, [...projectEnvioArgs, "stop"], { cwd: projectDir
}) even when beforeAll may have failed and those variables are undefined; update
the afterAll block (the existing afterAll function and its use of
projectEnvioCommand, projectEnvioArgs, projectDir) to skip the envio stop call
when the env vars weren't initialized — either check that projectEnvioCommand,
projectEnvioArgs, and projectDir are all truthy before calling runCommand, or
use a boolean flag set in beforeAll (e.g., envioStarted) and only run the stop
when that flag is true; keep the existing catch for safety.
- Around line 170-171: The test hardcodes the envio CLI path in projectEnvioArgs
to "node_modules/envio/bin.mjs", which can break if the package's bin changes;
instead resolve the bin via the package.json like resolveEnvio in
packages/e2e-tests/src/config.ts: use Node's module resolution (e.g.,
createRequire) scoped to projectDir to require("envio/package.json") and read
pkg.bin to compute the actual entry point, then set projectEnvioArgs to that
resolved path so the test mirrors resolveEnvio and avoids a brittle hardcoded
filename.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7971eb5-513f-4784-87b8-664396792799

📥 Commits

Reviewing files that changed from the base of the PR and between a7ca442 and 5337498.

📒 Files selected for processing (2)
  • .github/workflows/build_and_verify_pr.yml
  • packages/e2e-tests/src/dependency-tests/install.test.ts
💤 Files with no reviewable changes (1)
  • .github/workflows/build_and_verify_pr.yml

When the CLI's bin.mjs resolves envio from one path (e.g. a globally
installed or test-resolved copy) and the user's handlers resolve
`import "envio"` from a different path (e.g. their project's
node_modules), Node treats them as two separate ESM module instances.
HandlerRegister's module-local state then lives per-instance: the
handler's `indexer.onEvent` writes to one dict while
`HandlerLoader.applyRegistrations` reads the other, so every event ends
up "without an event handler" and chains with no other registrations
fail with "Nothing to fetch".

Stash `eventRegistrations`, `activeRegistration`, and `preRegistered` on
`globalThis` so all envio instances in a single process share one
registry. This is the same trick `react` and `@apollo/client` use to
guard against the "two copies of the package" footgun.

Revert the install.test.ts workaround that routed `envio dev` through
the project-local bin.mjs — with the globalThis fix, the original
`config.envioCommand` invocation works.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/envio/src/HandlerRegister.res (1)

13-56: ⚠️ Potential issue | 🟠 Major

Version-skew risk: shared globalThis slots assume a single registry schema.

If two different envio versions end up in the same process (monorepo with divergent pinned versions, a transitive dep that ships its own copy, or an in-flight upgrade), both will hit the same unversioned keys (__envioHandlerRegistrations, __envioActiveRegistration, __envioPreRegistered) and reuse whichever shape landed first. Any evolution of eventRegistration, activeRegistration, or Internal.onBlockConfig between versions then produces silent cross-version reads/writes — this is exactly the failure mode React/Apollo guard against with internal version checks, and the PR description cites them as precedent but doesn't replicate the guard.

Consider namespacing by package version (or stamping a __envioRegistryVersion and bailing/warning on mismatch):

🛡️ Suggested guard (sketch)
-let eventRegistrations: dict<
-  eventRegistration,
-> = %raw(`globalThis.__envioHandlerRegistrations ??= {}`)
+// Bump when any of the registry record shapes change.
+let registryVersion = "1"
+let eventRegistrations: dict<eventRegistration> = %raw(`
+  (globalThis.__envioRegistry ??= { version: "${registryVersion}" }),
+  (globalThis.__envioRegistry.version !== "${registryVersion}"
+    ? (() => { throw new Error("Incompatible envio HandlerRegister versions loaded in the same process (" + globalThis.__envioRegistry.version + " vs ${registryVersion}). Deduplicate the 'envio' dependency."); })()
+    : (globalThis.__envioRegistry.handlerRegistrations ??= {}))
+`)

(Apply the same pattern to activeRegistration and preRegistered.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/envio/src/HandlerRegister.res` around lines 13 - 56, The globalThis
slots (__envioHandlerRegistrations, __envioActiveRegistration,
__envioPreRegistered) are unversioned and can cause silent cross-version
corruption; update the initialization to namespace or guard by the package
version: compute a versioned key (e.g.,
`__envioRegistry_v${VERSION}_HandlerRegistrations`) or store a
`__envioRegistryVersion` alongside the stored object and detect mismatches, and
if versions differ either create a fresh registry or log/warn and bail; apply
this change for eventRegistrations (symbol: eventRegistrations / getKey),
activeRegistration (symbol: activeRegistration) and preRegistered (symbol:
preRegistered) so reads/writes only use a matching schema and avoid silent
cross-version reads.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/envio/src/HandlerRegister.res`:
- Around line 17-19: The global dict eventRegistrations and
activeRegistration.contents persist across module reloads causing stale handlers
and persistent "indexer finished initializing" errors; modify startRegistration
(or add a reset() called at start) to clear eventRegistrations (reset to an
empty dict) and set activeRegistration.contents = None before beginning a new
registration run so subsequent calls to
registerAllHandlers/finishRegistration/withRegistration/throwIfFinishedRegistration
operate on a fresh state. Ensure the reset happens early in startRegistration
and is also usable from tests if needed.

---

Outside diff comments:
In `@packages/envio/src/HandlerRegister.res`:
- Around line 13-56: The globalThis slots (__envioHandlerRegistrations,
__envioActiveRegistration, __envioPreRegistered) are unversioned and can cause
silent cross-version corruption; update the initialization to namespace or guard
by the package version: compute a versioned key (e.g.,
`__envioRegistry_v${VERSION}_HandlerRegistrations`) or store a
`__envioRegistryVersion` alongside the stored object and detect mismatches, and
if versions differ either create a fresh registry or log/warn and bail; apply
this change for eventRegistrations (symbol: eventRegistrations / getKey),
activeRegistration (symbol: activeRegistration) and preRegistered (symbol:
preRegistered) so reads/writes only use a matching schema and avoid silent
cross-version reads.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 87d89d5f-71af-489f-8dca-c0c128b3dd58

📥 Commits

Reviewing files that changed from the base of the PR and between 5337498 and fff11b1.

📒 Files selected for processing (1)
  • packages/envio/src/HandlerRegister.res

Comment thread packages/envio/src/HandlerRegister.res Outdated
Stashing the registry on unversioned globalThis keys would silently
blend state if two envio majors ended up in one process (monorepo with
divergent pins, a transitive dep shipping its own copy, an in-flight
upgrade). The record shapes evolve between majors, so silent sharing
would corrupt handlers/config reads later with opaque errors.

Collapse the three globalThis slots into a single \`__envioRegistry\`
object tagged with the envio package version. On load, if a registry
already exists we accept it only when versions match; otherwise we
throw with a deduplication hint pointing at the real problem
(\`node_modules/envio\` duplication) rather than a random decode failure.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT

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

♻️ Duplicate comments (1)
packages/envio/src/HandlerRegister.res (1)

102-132: ⚠️ Potential issue | 🟡 Minor

Stale state now survives across registration cycles in the same process.

With eventRegistrations, activeRegistration, and preRegistered anchored on globalThis, a second startRegistration call in the same process (tests spanning multiple indexer runs, watchers, REPL re-imports) inherits every prior entry in eventRegistrations, and finishRegistration never resets activeRegistration.contents so a subsequent withRegistration path can still observe a finished registration. The primary CLI-spawned flow is unaffected, but multi-run-in-process scenarios will see stale handlers and spurious "indexer finished initializing" throws.

♻️ Proposed reset at the start of a new run
 let startRegistration = (~ecosystem, ~multichain) => {
+  // Clear any leftover state from a prior registration cycle in this process.
+  eventRegistrations
+  ->Dict.keysToArray
+  ->Array.forEach(k => eventRegistrations->Utils.Dict.deleteInPlace(k))
   let r = {
     ecosystem,
     multichain,
     registrations: {
       onBlockByChainId: Dict.make(),
     },
     finished: false,
   }
   activeRegistration.contents = Some(r)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/envio/src/HandlerRegister.res` around lines 102 - 132,
startRegistration currently leaves global state from prior runs intact and
finishRegistration never clears activeRegistration, causing stale handlers to
persist; fix by explicitly resetting the global registration state at the start
of startRegistration (clear/recreate eventRegistrations, empty preRegistered
array) before creating the new registration object, and update
finishRegistration to set activeRegistration.contents = None after marking
r.finished (so subsequent withRegistration/startRegistration won't see a
finished registration); refer to startRegistration, finishRegistration,
activeRegistration, eventRegistrations, and preRegistered when applying the
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/envio/src/HandlerRegister.res`:
- Around line 102-132: startRegistration currently leaves global state from
prior runs intact and finishRegistration never clears activeRegistration,
causing stale handlers to persist; fix by explicitly resetting the global
registration state at the start of startRegistration (clear/recreate
eventRegistrations, empty preRegistered array) before creating the new
registration object, and update finishRegistration to set
activeRegistration.contents = None after marking r.finished (so subsequent
withRegistration/startRegistration won't see a finished registration); refer to
startRegistration, finishRegistration, activeRegistration, eventRegistrations,
and preRegistered when applying the changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78077c10-9f96-41ed-81d0-e56cd27f122b

📥 Commits

Reviewing files that changed from the base of the PR and between fff11b1 and 8515336.

📒 Files selected for processing (1)
  • packages/envio/src/HandlerRegister.res

Drop the %raw IIFE that boxed the globalThis lookup + version check in a
JS string, and replace it with a typed ReScript match. `globalThis` is
now bound via `@val external` with an open `{..}` type so bracket access
(\`globalThis["__envioRegistry"]\`) stays syntactically light; the slot's
real shape is encoded as a typed record (\`registryShape\`). The version
check becomes a plain pattern match with a guard.

Behavior unchanged; generated JS is equivalent. Payoff is that the
string-embedded JS no longer shadows types, and future edits (e.g.
adding a field to the registry) are caught by the compiler.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT

@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/HandlerRegister.res (1)

13-63: Typed, version-gated global registry looks correct.

The replacement of the prior raw-JS IIFE with a typed record plus a pattern match with a version guard is a clean win: registryShape makes future schema bumps compiler-checked, and throwing on version mismatch (rather than silently reusing slots) is the safe choice given the duplicate-module scenario this PR targets. Aliases at lines 63/78/87 correctly preserve the existing mutation contract (same dict, same ref, same array).

One optional refinement: the @val external globalThis: {..} open-object type forces bracket access for both the read on line 43 and the write on line 57. You can tighten this a bit and drop the implicit trust in the field shape:

🧹 Optional: narrower external for `__envioRegistry`
-@val external globalThis: {..} = "globalThis"
+@val
+external globalThis: {"__envioRegistry": Nullable.t<registryShape>} = "globalThis"

With this, globalThis["__envioRegistry"] is already Nullable.t<registryShape> without the local annotation on line 43, and the assignment on line 57 is still permitted via the object field.

Not a blocker — purely a readability/type-precision nit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/envio/src/HandlerRegister.res` around lines 13 - 63, Tighten the
external globalThis type so the __envioRegistry slot is typed instead of using
an open object: change the declaration of globalThis (currently `@val` external
globalThis: {..} = "globalThis") to include a "__envioRegistry":
Nullable.t<registryShape> field (e.g. `@val` external globalThis: {.
"__envioRegistry": Nullable.t<registryShape> } = "globalThis"), then remove the
local type annotation on the existing lookup (the `existing:
Nullable.t<registryShape>` local) and read/write the slot via the typed field
(globalThis.__envioRegistry or globalThis["__envioRegistry"]) so the registry
access in the registry initialization (the code that assigns to and reads from
__envioRegistry, and the registryShape usage) is statically typed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/envio/src/HandlerRegister.res`:
- Around line 13-63: Tighten the external globalThis type so the __envioRegistry
slot is typed instead of using an open object: change the declaration of
globalThis (currently `@val` external globalThis: {..} = "globalThis") to include
a "__envioRegistry": Nullable.t<registryShape> field (e.g. `@val` external
globalThis: {. "__envioRegistry": Nullable.t<registryShape> } = "globalThis"),
then remove the local type annotation on the existing lookup (the `existing:
Nullable.t<registryShape>` local) and read/write the slot via the typed field
(globalThis.__envioRegistry or globalThis["__envioRegistry"]) so the registry
access in the registry initialization (the code that assigns to and reads from
__envioRegistry, and the registryShape usage) is statically typed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 167d2f95-50d8-4fee-b979-2da67539757e

📥 Commits

Reviewing files that changed from the base of the PR and between 8515336 and 3933c50.

📒 Files selected for processing (1)
  • packages/envio/src/HandlerRegister.res

Replace \`{..}\` with a \`mutable __envioRegistry\` record so both the
lookup and the assignment are statically typed, and drop the local
\`Nullable.t<registryShape>\` annotation. Typo in the slot name now
surfaces at compile time instead of silently creating a new globalThis
property.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT

@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/HandlerRegister.res (1)

22-64: Global registry setup looks right; one small comment/behavior nit.

The version-gated globalThis.__envioRegistry approach cleanly addresses the dual-instance problem described in the PR: eventRegistrations, activeRegistration, and preRegistered remain reference-shared across any duplicate envio copy of the same version, and incompatible copies fail loudly with an actionable deduplication hint rather than silently splitting state.

One small nit: the rationale comment on lines 27–30 says the shapes "evolve between envio majors," but the guard on line 48 uses strict full-version equality (existing.version === version), so any patch/pre-release mismatch also throws. The stricter behavior is arguably the safer default, but it would be worth reconciling the comment with the implementation (either soften the comment to "between envio versions" or relax the check to compare majors only) so future readers don't assume major-only matching is in effect.

✏️ Optional wording tweak to match the implementation
 // Version-gated: the record shapes below evolve between envio majors, so
 // sharing unversioned slots would corrupt state across incompatible
 // versions. On mismatch we throw with a deduplication hint instead of
 // silently mixing shapes.
+// Version-gated: the record shapes below can evolve between envio versions,
+// so sharing unversioned slots would risk corrupting state across
+// incompatible builds. We compare the full version string and throw with a
+// deduplication hint on any mismatch rather than silently mixing shapes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/envio/src/HandlerRegister.res` around lines 22 - 64, The comment
says "evolve between envio majors" but the code uses strict equality at the
registry init (the equality check existing.version === version inside the
private registry binding that reads globalThis.__envioRegistry using
Utils.EnvioPackage.value.version), so reconcile them: either update the comment
to say "between envio versions" to match the strict comparison, or change the
guard to compare majors only (parse existing.version and
Utils.EnvioPackage.value.version and allow match when major segments are equal)
before creating/throwing during registry initialization; adjust the check around
existing.version === version in the registry setup accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/envio/src/HandlerRegister.res`:
- Around line 22-64: The comment says "evolve between envio majors" but the code
uses strict equality at the registry init (the equality check existing.version
=== version inside the private registry binding that reads
globalThis.__envioRegistry using Utils.EnvioPackage.value.version), so reconcile
them: either update the comment to say "between envio versions" to match the
strict comparison, or change the guard to compare majors only (parse
existing.version and Utils.EnvioPackage.value.version and allow match when major
segments are equal) before creating/throwing during registry initialization;
adjust the check around existing.version === version in the registry setup
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f314e3d9-c2d1-4b72-a688-02aeb7f2fd1f

📥 Commits

Reviewing files that changed from the base of the PR and between 3933c50 and 56e3e36.

📒 Files selected for processing (1)
  • packages/envio/src/HandlerRegister.res

Comment said shapes "evolve between envio majors" but the guard is
full-version equality, so a patch/pre-release mismatch also throws.
Keep the stricter check (safer default) and reword the comment so a
future reader doesn't assume major-only matching.

https://claude.ai/code/session_011rsp7McDiNshdkTLCnP6eT
@DZakh
DZakh enabled auto-merge (squash) April 24, 2026 10:40
@DZakh
DZakh merged commit 6b7c2d7 into main Apr 24, 2026
8 checks passed
@DZakh
DZakh deleted the claude/fix-e2e-dependency-tests-t8XxP branch April 24, 2026 10:46
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