You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
compilePackages clean-compile — Perry's HIR + codegen must handle every JS pattern the package uses. Known categorical blockers:
arguments magic object (Effect's dual() uses it 148×; documented in docs/src/language/limitations.md)
Reflect.defineMetadata / getMetadata round-trip — NestJS reads 0 instead of the registered route path
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:
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.
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.
Lookbehind regex — many packages use (?<=foo)bar; without it they fall back to V8 or fail.
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-fnsfmt=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).
Snapshot at v0.5.1008
Compat sweep run without
--enable-js-runtime, with the #499 / #947 gate active (default):bundled-dotenvbundled-ioredisfmt=yyyy-01-dd)perry-ext-dayjs::js_datefns_formatshadowed the proper perry-stdlib LDML walker. Same family as last week's #993 / #995 / #1000 work; was clean earlier in the session.perry-jsruntimepulled in)debug'smssubmodule still routes to V8 fallbackperry-jsruntimepulled in)perry-jsruntimepulled in)Effect.succeed(42) === undefinedoncompilePackagespath still open. Categorical:argumentsmagic object used bydual()148×perry-jsruntimepulled in)app.close+ non-blocking listen — flips to PASS once mergedperry-jsruntimepulled in)compilePackages: ["hono"]to clean-compile; today routes through V8 (PR #987 / #1015 / #1016)perry-jsruntimepulled in)perry-jsruntimepulled in)Reflect.defineMetadata/getMetadataround-trip across the Perry/V8 boundaryperry-jsruntimepulled in)compilePackages: ["ramda"]clean-compile, or per-function native bindings via the lodash precedentperry-jsruntimepulled in)perry-jsruntimepulled in)node_modules/zod/v3/errors.js+ZodError.jsroute to V8; pure-JS but uses Symbol.iterator + extends Error in patterns that don't AOT cleanlyScore: 4/16 link V8-free today.
What "compile without V8" requires per package
For every package that needs V8 today, ONE of:
compilePackagesclean-compile — Perry's HIR + codegen must handle every JS pattern the package uses. Known categorical blockers:argumentsmagic object (Effect'sdual()uses it 148×; documented indocs/src/language/limitations.md)Reflect.defineMetadata/getMetadataround-trip — NestJS reads0instead of the registered route pathregexcrate doesn't support; test-parity: audit known_failures.json — every entry needs an issue # and date #797 covers known_failures provenanceconsole.dir/console.group*formatting (smaller scope)path[runtimeVar]()) — already gated by security: refuse dynamic stdlib dispatch (obj[runtimeVar]()) #503; Effect requiresPERRY_ALLOW_DYNAMIC_STDLIB=1opt-inperry-ext-<package>native binding — cookie-cutter Rust crate routed viacrates/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).perry-stdlibbundled 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:
argumentsmagic object — unblocks Effect'scompilePackagespath (Tracking: Effect framework end-to-end compat (post-#309 / #310) #321) and any idiomatic Node library that usesargumentsfor data-first/data-last API duality. Plumbing-only in HIR/codegen; runtime support already exists injs_callee_argumentspaths.Reflect.defineMetadata/getMetadatacross-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 incrates/perry-jsruntime/src/interop.rswires Reflect onto V8's side but Perry-side compiledReflectnamespace doesn't expose those methods. Note: even fixing this leaves nestjs needing V8 for the decorator runtime — closing this gap is necessary for nestjs'scompilePackagespath eventually, not for going V8-free today.(?<=foo)bar; without it they fall back to V8 or fail.compilePackagesruntime stability — Effect'sEffect.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).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:
TypeError: Cannot convert X to a BigIntat i64 boundaries — tracking issue #1049 instances 2+3) — closedconst [a, b] = await Promise.all([fn1(), fn2()])— destructured values are dropped (undefined) when fn returns property access through async; sequential awaits work #1013count=1Out of scope for this tracker
fastifyPASSES with--enable-js-runtimeafter #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-fnsfmt=yyyy-01-ddrow is a regression separate from the V8 question — perry-ext-dayjs'sjs_datefns_formatshadowed 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-runtimeflag. Realistic time horizon is multi-month given the categorical blockers (especiallyargumentsand full decorator metadata).