Skip to content

tracking: V8-free compat — close the perry-jsruntime gap for all sweep packages #1069

@proggeramlug

Description

@proggeramlug

Snapshot at v0.5.1008

Compat sweep run without --enable-js-runtime, with the #499 / #947 gate active (default):

Package V8-free status Notes
dotenv ✅ PASS perry-stdlib bundled-dotenv
ioredis ✅ PASS perry-stdlib bundled-ioredis
jsonwebtoken ✅ PASS perry-ext-jsonwebtoken
lodash ✅ PASS perry-stdlib lodash manifest
date-fns ❌ diff (fmt=yyyy-01-dd) LDML symbol-shadow regression — perry-ext-dayjs::js_datefns_format shadowed the proper perry-stdlib LDML walker. Same family as last week's #993 / #995 / #1000 work; was clean earlier in the session.
debug ❌ compile (perry-jsruntime pulled in) well-known flip exists but debug's ms submodule still routes to V8 fallback
drizzle ❌ compile (perry-jsruntime pulled in) needs PR #1065 (sqlite Database/Statement V8 proxies) to land + a native binding for drizzle-orm itself if going fully V8-free
effect ❌ compile (perry-jsruntime pulled in) #321 tracker: 7/7 listed sub-issues CLOSED, runtime blocker Effect.succeed(42) === undefined on compilePackages path still open. Categorical: arguments magic object used by dual() 148×
express ❌ compile (perry-jsruntime pulled in) runs through V8 fallback (perry-ext-http-server underneath but middleware chain is in V8)
fastify ❌ runtime (rc=124 timeout) PR #1066 lands app.close + non-blocking listen — flips to PASS once merged
hono ❌ compile (perry-jsruntime pulled in) needs compilePackages: ["hono"] to clean-compile; today routes through V8 (PR #987 / #1015 / #1016)
jose ❌ compile (perry-jsruntime pulled in) works through V8 (PR #1004 / #1010) — would need a native binding to go V8-free
nestjs ❌ compile (perry-jsruntime pulled in) PR #1067 lands CJS-cycle/require-extractor/process.exit fixes; full route response still needs Reflect.defineMetadata / getMetadata round-trip across the Perry/V8 boundary
ramda ❌ compile (perry-jsruntime pulled in) works through V8 — needs compilePackages: ["ramda"] clean-compile, or per-function native bindings via the lodash precedent
vitest ❌ compile (perry-jsruntime pulled in) test-runner — pulls in many transitive deps
zod ❌ compile (perry-jsruntime pulled in) importer node_modules/zod/v3/errors.js + ZodError.js route to V8; pure-JS but uses Symbol.iterator + extends Error in patterns that don't AOT cleanly

Score: 4/16 link V8-free today.

What "compile without V8" requires per package

For every package that needs V8 today, ONE of:

  1. compilePackages clean-compile — Perry's HIR + codegen must handle every JS pattern the package uses. Known categorical blockers:

  2. perry-ext-<package> native binding — cookie-cutter Rust crate routed via crates/perry/well_known_bindings.toml. Precedents: perry-ext-bcrypt, perry-ext-argon2, perry-ext-pg, perry-ext-jsonwebtoken, perry-ext-fastify, perry-ext-better-sqlite3 (PR fix(perry-jsruntime): v8 proxies for sqlite Database/Statement (#1022) #1065).

  3. perry-stdlib bundled native impl — for packages used by enough programs to justify in-tree (lodash manifest, dayjs/date-fns LDML, fastify, http server, ioredis, dotenv).

Highest-leverage unlocks

Ranked by how many sweep rows flip per fix:

  1. arguments magic object — unblocks Effect's compilePackages path (Tracking: Effect framework end-to-end compat (post-#309 / #310) #321) and any idiomatic Node library that uses arguments for data-first/data-last API duality. Plumbing-only in HIR/codegen; runtime support already exists in js_callee_arguments paths.
  2. Reflect.defineMetadata / getMetadata cross-boundary — flagged by PR fix(jsruntime): #1021 — nestjs sweep silent exit (CJS cycles + process.exit) #1067 as the remaining gap for nestjs full PASS. The bridge in crates/perry-jsruntime/src/interop.rs wires Reflect onto V8's side but Perry-side compiled Reflect namespace doesn't expose those methods. Note: even fixing this leaves nestjs needing V8 for the decorator runtime — closing this gap is necessary for nestjs's compilePackages path eventually, not for going V8-free today.
  3. Lookbehind regex — many packages use (?<=foo)bar; without it they fall back to V8 or fail.
  4. compilePackages runtime stability — Effect's Effect.succeed(42) returning undefined through the native compile path is the canonical example. Tracked under Tracking: Effect framework end-to-end compat (post-#309 / #310) #321 (one in-flight agent working on it).
  5. More perry-ext-* cookie cutters — hono, ramda, zod, debug, jose. Each is mechanically straightforward but a per-package surface project.

Status of recently-fixed compile paths

PRs landed today that move sweep packages closer to V8-free:

Out of scope for this tracker

fastify PASSES with --enable-js-runtime after #1066 lands. The runtime-failure row in the V8-free sweep above is the same root cause as the V8-enabled row — once #1066 is merged both flip together. Listed for completeness.

The date-fns fmt=yyyy-01-dd row is a regression separate from the V8 question — perry-ext-dayjs's js_datefns_format shadowed perry-stdlib's LDML walker. Fixed last week via #993 / #995 / #1000 but reappeared in the current build (LDML uppercase token replace, not LDML run-aware walk). Will file as separate issue.

Definition of done

V8-free sweep reads 16/16 PASS without the --enable-js-runtime flag. Realistic time horizon is multi-month given the categorical blockers (especially arguments and full decorator metadata).

Metadata

Metadata

Assignees

No one assigned

    Labels

    parityNode.js compatibility / parity gaps

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions