Skip to content

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

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

    parityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions