Skip to content

fix(api): load route modules on Node without the Uint8Array hex proposal - #3972

Closed
kwakayama wants to merge 1 commit into
mainfrom
fix/issue-3968-node-tohex
Closed

kwakayama wants to merge 1 commit into
mainfrom
fix/issue-3968-node-tohex

Conversation

@kwakayama

@kwakayama kwakayama commented Aug 22, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3968.

The bug

src/routing/api/module-loader/loader.ts hashed route module source with
new Uint8Array(digest).toHex(). That is the Uint8Array base64/hex proposal — native and
unflagged in Deno, and absent from every Node release before 25, where it sits behind V8's
--js-base-64 and cannot be enabled through NODE_OPTIONS.

On the supported Node floor, veryfront dev therefore returned 500 for every API route in
every app
, not just workflow routes:

Failed to load API handler: (intermediate value).toHex is not a function

Reproduction

Minimal app — a package.json and one app/api/hello/route.ts returning Response.json({ ok: true })
— against published veryfront@0.1.1248:

Runtime Uint8Array.prototype.toHex GET /api/hello
Node 22.22.3 absent 500 — toHex is not a function
Node 24.15.0 absent 500
Node 25.9.0 present 200 {"ok":true}
Node 25.9.0 + strip preload removed 500 — identical failure

Same app, same package, one variable.

The fix

Reuse the hand-rolled hex encoder already present in src/utils/hash-utils.ts, which is how the
rest of the codebase hashes. Net −5/+3 lines in loader.ts.

Why this shipped

tests (npm install smoke) already does a clean-room install of the built npm packages and runs a
real dev server on Node 24 — which lacks the method. It stayed green because step 7 only ever
requests /. Page rendering never touches the API module loader, so a total API outage was
invisible to the one job positioned to catch it.

Step 8 closes that: it drops an API route into the packed starter and requires 200 {"ok":true}.

It strips the proposal methods explicitly instead of trusting the runner's Node version. Node 25
ships them unflagged, so a version-dependent check would go quietly vacuous the moment CI moves off
Node 24 — the same way step 7 was vacuous for this bug. The step asserts the strip actually worked
before relying on it, so a preload that silently stopped working can't leave the step passing for
the wrong reason.

Verification against the real build

deno task build:npm then bash scripts/test/npm-install-smoke.sh on Node 24:

steps 1–7 step 8 exit
with the fix pass pass 0
without the fix (.toHex() restored in the built artifact) pass FAIL — API route returned 500, expected 200 (issue #3968) 1

Step 7 passes in both rows. That is the coverage gap, demonstrated rather than asserted.

Also run: deno task fmt:check, deno task lint, deno task typecheck, and
deno task test src/routing/api/module-loader/ src/routing/api/handler.test.ts src/routing/api/route-executor.test.ts
(11 passed, 264 steps, 0 failed).

Deliberately not in this PR

Two more production call sites use the same proposal and are not fixed here:

  • src/security/sandbox/worker-generation.ts:74 — digest.toHex()
  • src/security/sandbox/worker-script.ts:159-160 — captures both toBase64 and toHex off
    NativeUint8Array.prototype at module load

Step 8 does not exercise those paths — I confirmed that from the run, rather than assuming the
class is closed. They are the same latent break on the same Node floor and deserve their own change
with their own reproduction; folding them in here on a guess would ship untested edits to the
sandbox. Worth a follow-up issue.

The .toHex() calls in test fixtures (handler.test.ts, route-executor.test.ts,
worker-script.test.ts) are left alone — they only run under Deno.

Summary by CodeRabbit

  • Bug Fixes

    • Improved API module loading while preserving existing behavior.
    • Enhanced compatibility when certain Uint8Array encoding methods are unavailable.
  • Tests

    • Added smoke-test coverage for API routes in the packaged starter.
    • Verified successful server startup, JSON responses, and HTTP 200 health checks in the simulated environment.

The API module loader hashed route source with `new Uint8Array(digest).toHex()`.
That method is native and unflagged in Deno, and absent from every Node release
before 25 -- it is the Uint8Array base64/hex proposal, still behind V8's
--js-base-64 flag there and not settable via NODE_OPTIONS. So on the supported
Node floor, `veryfront dev` returned 500 for *every* API route in every app:

    Failed to load API handler: (intermediate value).toHex is not a function

Reuse the hand-rolled hex encoder that already exists two files over in
hash-utils.ts, which is what the rest of the codebase hashes with.

The clean-room npm smoke test already ran on Node 24, which lacks the method,
and stayed green because it only ever requested `/`. Step 8 now drops an API
route into the packed starter and requires a 200, which is the path that was
broken.

That step strips the proposal methods explicitly rather than trusting the
runner's Node version: Node 25 ships them unflagged, so a version-dependent
check would go quietly vacuous the moment CI moves off Node 24. It asserts the
strip worked before relying on it.

Verified against the real build:

  with the fix     steps 1-8 pass, exit 0
  without the fix  steps 1-7 pass, step 8 fails with
                   "API route returned 500, expected 200 (issue #3968)"

Step 7 passes in both, which is the coverage gap this closes.

Fixes #3968
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66264dc6-3f00-49c2-8524-955031246825

📥 Commits

Reviewing files that changed from the base of the PR and between ce033bf and 1fc3fc4.

📒 Files selected for processing (2)
  • scripts/test/npm-install-smoke.sh
  • src/routing/api/module-loader/loader.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The module loader now uses shared hashing utilities for prepared module bytes and source strings. The npm smoke test adds API-route coverage under a runtime without Uint8Array base64/hex proposal methods.

Changes

Node API compatibility

Layer / File(s) Summary
Shared module hashing
src/routing/api/module-loader/loader.ts
The loader uses computeHashBytes for prepared module bytes and computeHash for source strings.
Stripped-runtime API validation
scripts/test/npm-install-smoke.sh
The smoke test removes the Uint8Array proposal methods, starts the development server, and verifies a successful JSON response from /api/health. It also checks readiness failures and missing-method errors.

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

Merge Risk: ⚪ Minimal · up to 1fc3f

The PR replaces an unsupported runtime API used to load API routes and adds regression coverage for that path; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: kojiwakayama, ariskemper

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately identifies the API route loading fix for Node runtimes without Uint8Array hex support.
Linked Issues check ✅ Passed The loader fix removes the unsupported Uint8Array proposal dependency, and the npm smoke test covers API route success on stock Node as required by #3968.
Out of Scope Changes check ✅ Passed All changes directly support #3968 by fixing Node route loading and adding npm artifact smoke coverage; no unrelated code changes are shown.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch fix/issue-3968-node-tohex
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-3968-node-tohex

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.

@kojiwakayama

Copy link
Copy Markdown
Contributor

Closing as superseded by #3970, which is already merged.

#3970 applies the existing cross-runtime computeHash implementation at all three API module-loader hash sites and adds a packed npm smoke that starts the Node server and requires a real API route to return HTTP 200 with the expected JSON body. The published RC (veryfront@0.1.1250-rc.13757) has also been verified against the original reporter repository on stock Node 22 and Node 24.

The useful additional concerns from this PR are preserved separately:

The stable 0.1.1250 promotion is proceeding in #3973, so merging this conflicting duplicate would add risk without adding loader coverage.

@kwakayama
kwakayama deleted the fix/issue-3968-node-tohex branch August 22, 2026 17:58
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.

[BUG] Every API route fails to load on stock Node — module loader calls Deno-native Uint8Array.prototype.toHex()

2 participants