3va 1.0.0
[1.0.0] — 2026-06-04
Added
- CDP Inspector (
--inspect) — WebSocket Chrome DevTools Protocol server.debugger;statements are rewritten to__3va_debugger__()at source-load time. Pause is implemented viatokio::task::block_in_place+Condvarso the Tokio runtime remains responsive. Connect withchrome://inspector any DAP-compatible IDE. - NAPI native module loading (
--allow-ffi) — ~30 NAPI v8 functions exposed asunsafe extern "C"..nodeaddons loaded via the standardrequire('./addon.node')path. Requires--allow-ffipermission. - WebAssembly (WASM) — WASI-preview1-compatible runtime via
wasmtime. Supports.wasmand.watfiles. Full permission integration (filesystem preopens, env vars scoped to granted capabilities). - Post-quantum TLS (
__pqTlsConnect) — hybrid classical TLS + ML-KEM-768 key exchange. Returns{ connId, pqSharedSecret }. Non-blocking: runs insidespawn_blocking. Requires--allow-net. - Post-quantum crypto JS API —
require('crypto').pq.kem.{generateKeypair,encapsulate,decapsulate}(ML-KEM-768) andrequire('crypto').pq.dsa.{generateKeypair,sign,verify}(ML-DSA-65). - Fuzz targets in CI — 3 fuzz targets built on nightly; 30 s smoke run in GitHub Actions.
- Doc-tests — public API surfaces of
vvva_core,vvva_permissions,vvva_crypto, andvvva_jsnow have doc-tests. SECURITY.md— explicit acceptance rationale for RUSTSEC-2023-0071 (Marvin Attack); documents "before 1.0" review requirement.- Process manager subcommands —
start,stop,restart,status,logs,deletefor running scripts as managed background daemons.
Fixed
__pqTlsConnectwas synchronous on the JS event loop (blocked all timers and I/O during TLS handshake). Now runs insidespawn_blockingand is registered asAsync.SemverRangesilently rejected"latest","1.x",">=1.0.0 <2.0.0"forms — added dist-tag, x-range, and compound-range support.- Dependency resolver produced non-deterministic lockfiles (HashMap iteration order). Resolution stack is now sorted alphabetically.
- Version conflict in the resolver was silent — now emits a structured
tracing::warn!. Content-Lengthheader forwarded to JS did not reflect the 100 MiB internal cap.
Changed
rquickjs-core 0.6.2vendored atvendor/rquickjs-core/with a one-line fix for thenever type fallbackfuture-incompatibility lint (Rust Edition 2024).- All crate versions bumped to
1.0.0.
Added
-
Expo / React Native package support (
crates/js/src/builtins/modules.rs,crates/js/src/transpiler.rs):
Real Expo npm packages (expo-modules-core,expo-constants,expo-asset,expo-font,expo-file-system) load and execute without errors. 45/45 tests pass intest-projects/expo-test/.ESM→CJS converter fixes:
- Circular dependency guard —
module.exportspre-cached before eval; circular requires get the partial exports object instead of re-executing the module (matches Node.js behavior, eliminates stack overflows). export default Xchained assignment — was setting.defaulton the OLDmodule.exportsobject due to JS LHS-ref evaluation order. Now uses two statements:module.exports = X+ deferredmodule.exports.default = module.exports.- Destructuring exports —
export const { a, b } = Xandexport const [a, b] = Xnow correctly emit individualmodule.exports.a = aentries. - Uninitialised var export —
export var X;is now deferred so TypeScript enum IIFEs can fill the value beforemodule.exports.Xis set. - Empty export marker —
export {}(OXC emits this to tag a file as ESM) is now a no-op; previously surfaced as "unsupported keyword: export". - Dynamic
import()— inlineimport(specifier)expressions are rewritten to__importAsync(specifier)which wraps synchronousrequire()in a resolved Promise. - Deferred exports — all deferred assignments wrapped in
try{}catch{}to tolerate read-only properties defined byObject.defineProperty.
Platform-aware extension resolution:
resolve_file_pathprobes.web.js,.web.tsx,.web.ts,.web.mjsbefore the generic.js,.tsx,.tsvariants. Expo.web.*files are the correct choice in a server/CLI context (they avoid native bridge imports).- Index file probing follows the same order (
index.web.tsbeforeindex.ts).
TypeScript transpiler:
SemanticBuilder::with_enum_eval(true)added — prevents OXC panic when transforming TypeScriptconst enumdeclarations.
New React Native / Expo polyfills:
react-nativepre-cached in__requireCachewithPlatform,NativeModules,TurboModuleRegistry,PixelRatio,Dimensions,StyleSheet,Animated, and all major component stubs.@react-native/assets-registrypre-cached withregisterAsset/getAssetByID.NativeModulesproxy changed to returnundefinedfor unregistered module names (previously returned a truthy function-proxy, causingJSON.parse(function(){})errors inexpo-constants).expo-modules-corepolyfill extended withNativeModule,SharedObject,SharedRef(extendable base classes),registerWebModule,Platform,uuid.requireOptionalNativeModulereturnsnull— correct for a web/server environment where optional native modules are absent.process.env.EXPO_OS = 'web'— Expo packages branch on this to select server-safe code paths.
- Circular dependency guard —
-
CPU sampling profiler (
--prof) (crates/js/src/profiler.rs):3va run app.ts --prof— collects samples every--prof-intervalms (default 10) viasetInterval+new Error().stack; writes V8-compatible.cpuprofileJSON.--flamegraph=<path>— also emits an Inferno-style SVG flamegraph using theinfernocrate.3va prof <file>subcommand — post-hoc analysis: prints top-N hot functions by self% or re-generates a flamegraph from an existing.cpuprofile.console.profile(label)/console.profileEnd(label)— JS-side region markers, active when--profis passed.JsEngine::new_with_profiler(perms, interval_ms)/JsEngine::take_profiler()— public Rust API.- 7 unit tests: stack parser, location parser, folded-stacks aggregation,
.cpuprofileJSON validity,analyze_cpuprofile, JS bootstrap interval embedding.
-
Buffercomo subclase real deUint8Array(builtins/buffer.rs):
Reescrito usando el patrón prototype swap: el constructor devuelve unUint8Arrayreal conBuffer.prototypeen su cadena. Esto garantiza:buf instanceof Uint8Array→truebuf[0]→ valor de byte correcto (proxy nativo de TypedArray)[...buf]spread,buf.set(),Array.from(buf)— todos funcionan como nativosDataView,Float32Arrayy otras vistas sobrebuf.bufferfuncionan sin conversión- Compatibles con
ws,msgpackr,protobufjsy cualquier librería que accede a bytes directamente
Todos los métodos (readUInt32BE,writeFloatLE,BigUInt64,slice,subarray, etc.) actualizados para operar sobrethisdirectamente.
-
crypto.createSign/createVerify— RSA PKCS1v15 y ECDSA reales (builtins/crypto.rs):
Implementación nativa vía crates Rust. Soporta:- RSA PKCS#1 v1.5: algoritmos
RSA-SHA256,RSA-SHA384,RSA-SHA512,SHA256,SHA1 - ECDSA P-256:
SHA256con clave P-256 - ECDSA P-384:
SHA384con clave P-384 - Salida en formato DER (compatible con
jsonwebtoken,passport-jwt,jose) - Acepta DER y P1363 (raw r‖s) en verificación
const sig = crypto.createSign('RSA-SHA256').update(data).sign(privateKey); crypto.createVerify('RSA-SHA256').update(data).verify(publicKey, sig); // → true
- RSA PKCS#1 v1.5: algoritmos
-
crypto.createPrivateKey/createPublicKey/createSecretKey(builtins/crypto.rs):
Importa claves PEM existentes en objetosKeyObjectcompatibles con Node.js..type→'private','public', o'secret'.asymmetricKeyType→'rsa'o'ec'.export()→ PEM string o Uint8Array (conformat: 'der')
Desbloquea:jsonwebtokencon claves externas,passport-jwt,@panva/jose.
-
crypto.sign/crypto.verify(one-shot, Node.js 15+) (builtins/crypto.rs):const sig = crypto.sign('SHA256', data, privateKey); crypto.verify('SHA256', data, publicKey, sig); // → boolean
-
crypto.createHash('md5')(builtins/crypto.rs):
MD5 ahora soportado vía cratemd-5 0.10(algoritmo RustCrypto). Para fingerprinting de contenido,
ETags, compatibilidad legacy. No recomendado para seguridad. -
crypto.getCiphers()/getHashes()/getCurves()(builtins/crypto.rs):
Nuevas funciones de enumeración que devuelven los algoritmos soportados. -
crypto.generateKeyPair/generateKeyPairSync— RSA y EC nativos (builtins/crypto.rs):
Generación de pares de claves asimétricas vía Rust (rsa 0.9,p256 0.13,p384 0.13).crypto.generateKeyPairSync('rsa', { modulusLength: 2048 })→{ publicKey, privateKey }con.export()que devuelve PEM PKCS#8/SPKI.crypto.generateKeyPair('rsa', opts, callback)— versión async con spawn_blocking.- Curvas EC soportadas:
P-256(prime256v1),P-384(secp384r1). - Claves RSA-PSS: misma implementación que RSA estándar.
Desbloquea: JWT RS256/ES256/ES384 conjsonwebtoken,passport-jwt,node-jose.
-
crypto.webcrypto(builtins/crypto.rs):
Añadidocrypto.webcrypto = { subtle }como alias alcrypto.subtleexistente.
Requerido por Hono, edge runtimes, y cualquier código que accede a WebCrypto víarequire('crypto').webcrypto. -
crypto.scryptSync— implementación real con scrypt (builtins/crypto.rs):
Sustituye la aproximación anterior (PBKDF2 como fallback) por__cryptoScryptSync, que llama
directamente a la implementación nativascrypt::scrypt. Nuevo binding Rust síncrono análogo a__cryptoPbkdf2Sync. -
child_process.execSync/spawnSync(builtins/child_process.rs):
Implementación real que bloquea el hilo llamante víastd::process::Command::output().execSync(cmd, opts)— devuelve stdout como Buffer o string; lanza en exit ≠ 0.spawnSync(cmd, args, opts)— devuelve{ status, stdout, stderr, pid, signal, error }.- Ambos respetan el sistema de capabilities: requieren
--allow-child-process.
Desbloquea: Vite/esbuild postinstall, Prisma query engine bootstrap, CLIs con Node.js.
-
util.parseArgs(builtins/modules.rs):
Implementación completa del API de Node.js 18+ para parseo de argumentos CLI.
Soporta:--flag,--key=value,--key value, flags booleanos, valores múltiples, positionals,--, defaults, y el campotokensopcional. -
reflect-metadatapolyfill (builtins/modules.rs):
Polyfill JS completo de la APIReflect.metadatapara decoradores TypeScript.
Implementa:defineMetadata,getMetadata,getOwnMetadata,hasMetadata,hasOwnMetadata,
deleteMetadata,getMetadataKeys,getOwnMetadataKeys,decorate.
Accesible víarequire('reflect-metadata'). Desbloquea: NestJS, TypeORM, tsyringe, routing-controllers.
Fixed
-
assert.deepStrictEqual— implementación completa (builtins/modules.rs):
La implementación anterior usabaJSON.stringifyque fallaba con:- Valores
undefined(eliminados por JSON) - TypedArrays (
Uint8Array,Int32Array, etc.) - Referencias circulares
- Objetos
Date,RegExp,Map,Set
La nueva implementación hace comparación estructural recursiva con: - Detección de ciclos vía lista de pares visitados
- Soporte para
Date(comparación por timestamp),RegExp(por string),TypedArray,Map,Set - Semántica estricta (
===) vs no-estricta (==) según el método
También añadidos:notDeepStrictEqual,notStrictEqual,ifError,fail.
- Valores
-
Buffer.isBuffer(x)ahora devuelvetrueparaUint8Arraynativo (builtins/buffer.rs):
Anteriormente devolvíafalseparaUint8Arrayno envuelto enBuffer, rompiendo librerías que
hacenif (!Buffer.isBuffer(x)) throw. AhoraBuffer.isBuffer(new Uint8Array(4)) === true. -
util.inspect— manejo de referencias circulares ySymbol.for('nodejs.util.inspect.custom')(builtins/modules.rs):
La implementación anterior hacíaJSON.stringifyque lanzaba en objetos circulares. Ahora:- Detecta ciclos y muestra
[Circular *]. - Llama
obj[Symbol.for('nodejs.util.inspect.custom')]si existe (requerido por pino, winston, etc.). - Formatea funciones como
[Function: name], fechas como ISO, y errors como[ErrorType: message]. - Limita la profundidad (2 niveles por defecto, configurable con
{ depth: n }).
- Detecta ciclos y muestra
-
Framework detection —
3va devahora detecta y delega a 8 frameworks (crates/cli/src/main.rs):
3va devdetecta automáticamente el framework del proyecto y delega en su dev server nativo.- Astro (
astro.config.*→astro dev) - Next.js (
next.config.*→next dev) - Nuxt (
nuxt.config.*→nuxi dev) - SvelteKit (
svelte.config.*+@sveltejs/kit→vite dev) - Remix (
remix.config.*→remix dev) - Gatsby (
gatsby-config.*→gatsby develop) - SolidStart (
app.config.*→vinxi dev) - Qwik (
qwik.config.*→qwik dev)
Los flags--port,--hosty--opense reenvían automáticamente al CLI del framework.
- Astro (
-
Process manager nativo — comandos
start,stop,restart,status,logs,delete(crates/cli/src/proc.rs,crates/cli/src/main.rs):
Nuevo sistema de gestión de procesos en producción similar a PM2:3va start <entry>— inicia un proceso como daemon (nuevo session group víasetsid).3va stop <name>— detiene con SIGTERM → SIGKILL tras 1.5 s.3va restart <name>— reinicia con la misma configuración.3va status [name]— muestra estado de procesos con códigos de color.3va logs <name>— muestra las últimas N líneas del log.3va delete <name>— elimina permanentemente el proceso y sus logs.
Los metadatos se almacenan en~/.3va/processes/<name>.jsony los logs en~/.3va/processes/<name>.log.
-
EventEmitter— API completa (modules.rs):
Nuevos métodos que muchos paquetes npm dan por sentados:prependListener(event, fn)/prependOnceListener(event, fn)— agregan listeners al inicio de la cola (en lugar del final).rawListeners(event)— devuelve los wrappers internos deoncetal como están (a diferencia delisteners()que los desenvuelve).eventNames()— array con todos los eventos que tienen listeners registrados.getMaxListeners()— devuelve el límite configurado consetMaxListeners().EventEmitter.listenerCount(emitter, event)— método estático de compatibilidad Node.js.EventEmitter.defaultMaxListeners— propiedad estática equivalente aEventEmitter.setMaxListeners.listeners()corregido: ahora devuelve la función original (no el wrapper deonce).
-
zlib— Transform streams reales (builtins/zlib.rs):
Las funcionescreateGzip,createGunzip,createDeflate,createInflate,createDeflateRaw,createInflateRawya no devuelven objetos vacíos. Ahora devuelven Transform streams con:write(chunk[, enc, cb])— comprime/descomprime asíncronamente; emitedatacon el resultado.end([chunk][, cb])— espera a que todos loswrite()pendientes completen antes de emitirfinish/end.pipe(dest)/unpipe(dest)— encadenamiento estándar de streams.on/once/off/emit— interfaz EventEmitter completa.pause(),resume(),destroy(),setEncoding().- Propagación correcta al destino (
pipe) en eventosdatayend. - Brotli:
createBrotliCompress/createBrotliDecompress(alias sobre gzip — compresión real pendiente).
-
zlib— métodos síncronos reales (builtins/zlib.rs):
gzipSync,gunzipSync,deflateSync,inflateSync,deflateRawSync,inflateRawSync,
brotliCompressSync,brotliDecompressSync— ya no lanzan "not available". Están respaldados
por las mismas funciones Rust (flate2) pero ejecutadas de forma síncrona (sinspawn_blocking).
Útiles en transformaciones de build-time. -
process— EventEmitter completo (builtins/process.rs):
El objetoprocessahora expone la API EventEmitter completa:
on,once,off,emit,removeListener,removeAllListeners,addListener,
prependListener,prependOnceListener,rawListeners,eventNames,listenerCount.
Los listeners de señales (SIGINT,SIGTERM, etc.) se registran con la misma API. -
process.memoryUsage()— valores reales en Linux (builtins/process.rs):
Lee el RSS real de/proc/self/status. Devuelve{ rss, heapTotal, heapUsed, external, arrayBuffers }.
process.memoryUsage.rss()— atajo directo al RSS. -
process.cpuUsage([prev])— valores reales en Linux (builtins/process.rs):
Lee tiempos de CPU de/proc/self/stat. Devuelve{ user, system }en microsegundos.
Acepta un valor previo para obtener el diferencial. -
process.uptime()— segundos transcurridos desde el inicio del proceso. -
process.title,process.execPath,process.execArgv— propiedades estándar. -
process.abort()— llama aprocess.exit(1). -
process.kill(pid)— llama aprocess.exit(0)sipid === process.pid. -
process.report— objeto stub compatible con--report-*de Node.js. -
process.allowedNodeEnvironmentFlags—Setvacío (compatibilidad). -
process.setUncaughtExceptionCaptureCallback(fn)/hasUncaughtExceptionCaptureCallback(). -
os— valores reales del sistema (builtins/process.rs+modules.rs):os.hostname()— nombre real del host víagethostname(3).os.totalmem()/os.freemem()— memoria real de/proc/meminfoen Linux.os.uptime()— uptime real de/proc/uptimeen Linux.os.platform()/os.arch()— derivados deprocess.platform/process.arch.os.homedir()/os.tmpdir()— respetanprocess.env.HOME/process.env.TMPDIR.os.EOL—'\r\n'en Windows,'\n'en Unix.os.availableParallelism(),os.getPriority(),os.setPriority(),os.machine().os.constants.signals,os.constants.errno,os.constants.prioritycon valores correctos.os.userInfo()— respetaprocess.env.USERyprocess.env.HOME.
-
path— reescritura completa (modules.rs):
Implementación generada pormakePath(sep, isAbsFn)— soporta posix y win32 con la misma lógica:path.relative(from, to)— antes devolvíatosin modificar. Ahora calcula la ruta relativa real con...path.normalize(p)— colapsa.y..correctamente.path.resolve(...parts)— sube hasta encontrar una parte absoluta o usaprocess.cwd().path.posix— submódulo con separador/(mismo objeto en Linux/macOS).path.win32— submódulo con separador\eisAbsolutecon regexC:\.require('path/posix'),require('node:path/posix'),require('path/win32'),require('node:path/win32').path.toNamespacedPath(p)— identidad (no-op en POSIX).path.matchesGlob()— stub (devuelvefalse).
-
fs— operaciones basadas en file descriptor (builtins/fs.rs):
Respaldadas por una tabla de FDs Rust (Arc<Mutex<FdTable>>) constd::fs::Filereales:fs.open(path, flags[, mode], cb)/fs.openSync(path, flags[, mode])→fdentero.
Flags de texto:'r','r+','w','w+','a','a+','wx','wx+', etc.fs.close(fd[, cb])/fs.closeSync(fd).fs.read(fd, buffer, offset, length, position, cb)/fs.readSync(...)→ bytes leídos.fs.write(fd, buffer, offset, length, position, cb)/fs.writeSync(...)→ bytes escritos.fs.fstat(fd[, cb])/fs.fstatSync(fd)→ objeto stat con las mismas propiedades questatSync.fs.fsync(fd[, cb])/fs.fdatasync(fd[, cb])— completado silencioso.fs.ftruncate(fd[, len][, cb])/fs.ftruncateSync.
-
fs.mkdtemp(prefix[, opts][, cb])/fs.mkdtempSync(prefix)— crea un directorio temporal único. -
fs.opendir(path[, opts][, cb])/fs.opendirSync(path)— devuelve un objetoDircon:read([cb])— entrada siguiente comoDirentonullal terminar; también retorna Promise.readSync()— variante síncrona.close([cb])— cierra el directorio.[Symbol.asyncIterator]()— iterable asíncrono compatible confor await...of.[Symbol.iterator]()— iterable síncrono.
-
fs— métodos adicionales (modules.rs):
fs.truncate,fs.lutimes,fs.lutimesSync,fs.lchown,fs.lchownSync,
fs.chown,fs.chownSync,fs.fchown,fs.fchownSync,
fs.fchmod,fs.fchmodSync,fs.link,fs.linkSync,
fs.readlink,fs.readlinkSync. -
stat_meta_to_json— helper compartido (builtins/fs.rs):
Función Rust que serializastd::fs::Metadataa JSON. Usada porstatSync,lstatSyncy el nuevofstatSync.
Added
-
WinterCG
Headersclass —new Headers(init?)whereinitmay be a plain object, a
[[key, value]]array, or anotherHeaders. Case-insensitive; iterable viafor..of,
entries(),keys(),values(),forEach().getSetCookie()returns allset-cookie
values as a separate array. (modules.rs) -
WinterCG
Requestclass —new Request(url | request, init?). Properties:url,method,
headers(Headers),bodyUsed,signal,duplex,mode,credentials,cache,
redirect,referrer,integrity,keepalive. Body methods:text(),json(),
arrayBuffer(),bytes(),blob(),formData(),clone().fetch()now accepts a
Requestobject as its first argument. (modules.rs+fetch.rs) -
WinterCG
Responseclass —new Response(body?, init?). Properties:ok,status,
statusText,headers(Headers),url,redirected,type,bodyUsed. Body methods
same asRequest. Static:Response.json(data, init?),Response.error(),
Response.redirect(url, status?).fetch()now returns aResponseinstance instead of a
plain object. (modules.rs+fetch.rs) -
structuredClone(value)— global deep-clone function (JSON round-trip). Throws
DataCloneErrorfor non-serializable values (functions, circular refs), matching browser
behaviour. (modules.rs) -
navigatorglobal — read-only object withuserAgent('3va/0.1 (QuickJS)'),language,
languages,onLine,hardwareConcurrency,platform,cookieEnabled,doNotTrack.
Required by many edge/worker detection checks. (modules.rs) -
self === globalThis—globalThis.selfis now set toglobalThis, unblocking worker-
compat code that checkstypeof self !== 'undefined'. (modules.rs) -
require('crypto')— real implementation — no longer a placeholder that returns random
garbage. Now wrapsglobalThis.crypto(the Rust-backed SubtleCrypto). Added:
getRandomValues,randomBytes,randomUUID(CSPRNG),createHash(alg)/createHmac(alg, key)
(async.digest(enc)returning aPromise),timingSafeEqual(a, b),pbkdf2(...),
constants. (modules.rs) -
jsr:specifier support —require('jsr:@scope/name')and ESMimport 'jsr:@scope/name'
now resolve by stripping thejsr:prefix and looking up the package innode_modules/as
a regular scoped package. Use3va install @scope/name --allow-net=jsr.ioto install.
(modules.rs) -
http.createServer(handler)— real HTTP/1.1 server —require('http').createServer(handler)
now binds a real TCP port and serves HTTP/1.1 connections. Backed bybuiltins/http_server.rs
(Rust, async Tokio listener). Handler receives Node.js-compatibleIncomingMessage(req.method,
req.url,req.headers,req._body) andServerResponse(res.writeHead(),res.write(),
res.end(),res.setHeader(),res.statusCode). Requires--allow-net=<bind-host>. Handles
multiple sequential requests per server instance.const http = require('http'); http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ path: req.url })); }).listen(3000, '0.0.0.0');
-
ML-KEM-768 (FIPS 203 / Kyber) — post-quantum Key Encapsulation Mechanism in
vvva_crypto.
MlKemKeypair::generate(),encapsulate(&ek),decapsulate(&dk, ct). Key sizes: EK=1184 B,
DK=64 B (seed), CT=1088 B, SS=32 B. Wrong-key decapsulation returns a different shared secret
(implicit rejection per spec). Hex serialization helpers included.
(crates/crypto/src/kem.rs) -
ML-DSA-65 (FIPS 204 / Dilithium) — post-quantum digital signature scheme in
vvva_crypto.
generate_signing_key(),sign(&sk, msg),verify(&vk, msg, &sig). Key sizes: SK=32 B (seed),
VK=1952 B, sig=3309 B. Stateless — safe to reuse the same key for multiple messages.
(crates/crypto/src/dsa.rs) -
crypto.subtle(Web Crypto API) — fullSubtleCryptoobject onglobalThis.crypto.subtle
andrequire('crypto').subtle. Backed bybuiltins/crypto.rs(Rust) + JS for HKDF/PBKDF2.
Supported operations:digest(SHA-1/224/256/384/512),generateKey(AES-GCM-128/256, AES-CBC,
AES-CTR, HMAC),importKey/exportKey(raw+jwk),sign/verify(HMAC),encrypt/decrypt
(AES-GCM),deriveBits/deriveKey(HKDF, PBKDF2).wrapKey/unwrapKeythrowNotSupportedError. -
response.formData()—fetchresponses now parse their body into aFormDataobject.
Supportsapplication/x-www-form-urlencoded(percent-decoding,+→space) and
multipart/form-data(boundary splitting,Content-Dispositionparsing, file parts becomeFile
objects). Any otherContent-Typerejects withTypeError. (builtins/fetch.rs) -
net/tls— real TCP/TLS sockets —require('net')andrequire('tls')now return
Rust-backed implementations.Socketclass wrapsTcpStream(plain) orTlsStream(TLS via
native-tls). API:connect(),write(),end(),destroy(),setEncoding(),setTimeout(),
on('data'|'end'|'error'|'close'),pipe().
Requires--allow-net=<host>. (builtins/tcp.rs,modules.rs) -
net.createServer(handler)— raw TCP server —require('net').createServer(handler)binds
a real TCP port and callshandler(socket)for each incoming connection.Socketexposes
write(data),end(),on('data'|'end'|'error'|'close'). Server exposeslisten(port, host)and
server.listeningflag. Backed by__netListen/__netAcceptAsyncinbuiltins/tcp.rs.
Requires--allow-net=<bind-host>.const net = require('net'); net.createServer((socket) => { socket.write('hello\n'); socket.end(); }).listen(4000, '127.0.0.1');
-
http2client —require('http2').connect(authority)returns anHttp2Session. Sessions
exposerequest(headers)which returns anHttp2Requestthat emitsresponse,data, andend
events. NGHTTP2 constants available ashttp2.constants. Backed by__fetchAsync; does not
implement real HTTP/2 framing. (modules.rs) -
--allow-env=VAR[,VAR,...]scoped environment access —--allow-envnow
accepts an optional comma-delimited list of variable names.--allow-env=(no value) → grantsEnvAccess(all variables, previous behaviour).--allow-env=NODE_ENV→ grantsEnvVar("NODE_ENV")only; all other variables
are hidden fromprocess.env.--allow-env=NODE_ENV,PORT→ grants only the two listed names.- Not providing the flag →
process.envis an empty object{}.
-
Capability::EnvVar(String)— new capability variant for per-variable scoping.
EnvAccess(all) covers anyEnvVar(x)viacaps_match; the reverse does not hold. -
process.envpermission enforcement —process.envis now populated by
filtering the host environment throughPermissionState::check(&Capability::EnvVar(key))
at injection time. Variables not granted are absent from the object regardless of
whether they exist in the host environment. Previously all variables were exposed
unconditionally even without--allow-env.
Fixed
-
Package extraction robustness (
crates/pm/src/fetcher.rs) —PackageFetcher::extract
no longer aborts the entire installation on the first entry error. Changes:- Entries with
..or absolute path components are rejected (path traversal). - Resolved output paths are verified to stay within the destination directory
(prevents canonical-escape attacks). EntryType::Symlink/EntryType::Linkare always skipped (supply-chain risk).- Directory entries are handled with
create_dir_allrather thanunpack. - Per-entry IO errors are logged as
WARNand skipped; extraction continues. - Fixes silent install failures for large packages that include native code
(e.g.react-native,canvas,sharp) — these now extract correctly to
node_modules/instead of being left absent.
- Entries with
-
3va runscript arguments — arguments after--are forwarded to the script viaprocess.argv[2+]. Example:3va run server.ts -- --port 3000 --dev.process.argv[0]= binary path,process.argv[1]= absolute script path,process.argv[2+]= script args. -
3va installmulti-package —installnow accepts multiple packages in one invocation:3va install next react react-dom. Previously only one package was accepted. -
--allow-net=without value — passing--allow-net=(empty value via=) grants network access to all hosts (equivalent to*). Same semantics for--allow-read=(all paths) and--allow-write=(all paths). Multiple flags can be combined:3va run app.js --allow-net= --allow-read= --allow-write=. -
process.cwd()— returns the real working directory. Previouslyundefined. -
process.chdir()— no-op stub (sandboxed runtime does not change working directory). -
process.nextTick(cb, ...args)— schedulescbin a microtask viaPromise.resolve().then(), matching Node.js semantics. Multiple callbacks queued in the same tick are flushed in order. -
process.hrtime.bigint()— returnsBigInt(Date.now()) * 1_000_000n. -
setImmediate/clearImmediate— exposed as globals;setImmediateis backed bysetTimeout(fn, 0). -
process.versionsexpanded — now includesnode: "20.0.0",v8: "11.3.244.8-node.20",uv: "1.44.2",zlib: "1.2.13",openssl: "3.0.0",modules: "115". Packages that inspectprocess.versions.nodeno longer crash. -
process.stdout.fd/process.stderr.fd— set to1and2respectively.isTTYset tofalse. -
global/GLOBALglobals —globalThis.globalandglobalThis.GLOBALare now aliases forglobalThis, unblocking packages that useglobal.xxx(e.g.node-polyfill-crypto). -
require('module')shim — the built-inmodulepackage now exposesModule._resolveFilename(),Module._cache,Module._load(),Module.prototype.require(),Module.createRequire(),Module.createRequireFromPath(),Module.builtinModules,Module.isBuiltin(), andModule.syncBuiltinESMExports(). Required by Next.jsrequire-hook.jsand many other packages. -
fsexpanded — 15 new functions (sync + async +fs.promises):existsSync(path)— now exposed on thefsobject (was onglobalThisonly).statSync(path)/stat(path[, cb])— returns a stat object withisFile(),isDirectory(),isSymbolicLink(),size,mode,mtime,atime,ctime,birthtime,mtimeMs,atimeMs,ctimeMs.lstatSync(path)/lstat(path[, cb])— same asstatbut does not follow symlinks.accessSync(path[, mode])/access(path[, mode][, cb])— checks existence and sandbox read/write permissions.modeflags:fs.constants.F_OK(0),R_OK(4),W_OK(2),X_OK(1).realpathSync(path)/realpath(path[, cb])— callsstd::fs::canonicalize.unlinkSync(path)/unlink(path[, cb])— removes a file.renameSync(from, to)/rename(from, to[, cb])— moves/renames.copyFileSync(src, dest)/copyFile(src, dest[, cb])— copies a file.chmodSync(path, mode)/chmod(path, mode[, cb])— changes Unix permissions.symlinkSync(target, path)/symlink(target, path[, cb])— creates a symlink.appendFileSync(path, data)/appendFile(path, data[, cb])— appends to a file.createReadStream(path[, opts])— returns an EventEmitter that emitsdata/end/error. Reads are lazy (fired viasetTimeout(0)so the event loop can drain first).createWriteStream(path[, opts])— returns an object withwrite(chunk)andend([chunk]). Flushes the entire buffer to disk onend().watch(path[, opts][, cb])— returns an EventEmitter stub (no inotify; sandbox limitation).readdirSync(path, { withFileTypes: true })— returnsDirent-like objects withname,isFile(),isDirectory(),isSymbolicLink().fs.constants—{ F_OK: 0, R_OK: 4, W_OK: 2, X_OK: 1, COPYFILE_EXCL: 1 }.fs.promises.*— all async methods mirrored (readFile, writeFile, readdir, mkdir, rm, stat, lstat, access, realpath, rename, unlink, copyFile, chmod, symlink, appendFile).require('fs')andrequire('node:fs')now return the full expanded object;require('fs/promises')returnsfs.promises.
-
JSX transform — the Oxc transpiler now supports JSX via the Classic runtime (
React.createElement):.jsx/.tsxfiles: always transformed..ts/.mts/.ctsfiles: TypeScript strip only (no JSX)..js/.mjs/ unknown extensions: auto-detection vialooks_like_jsx()heuristic — if the source contains<Tagor</Tag, JSX transform is applied automatically.- JSX fragments use
React.Fragment. transpile_jsx(source)andtranspile_js(source)are now public API invvva_js::transpiler.looks_like_jsx(source) -> boolis public for callers that want to pre-check.
-
Flow type stripping —
transpile_js()includes a two-pass Flow fallback:- Strips
@flow,@format,import type,import typeofpragmas. - If Oxc still fails, falls back to
strip_inline_flow_types()which removes: Typeannotations fromconst/let/vardeclarations and function parameters at character level (no regex). Enables basic Flow-annotated.jsfiles from React Native packages to be loaded viarequire().
- Strips
Changed
--allow-read,--allow-net,--allow-writeinrun,install,update,reinstallnow userequire_equals = trueandvalue_delimiter = ',':- Old:
--allow-net registry.npmjs.org(space-separated, consumed next positional arg as value — broken with--allow-netfollowed by FILE). - New:
--allow-net=registry.npmjs.orgor--allow-net=host1,host2(equals sign required; comma-delimited list; omitting value after=grants wildcard).
- Old:
process.argvconstruction moved frominject_process(captured all raw CLI args) toeval_file/eval_file_with_args:process.argv[0]= path to the3vabinary.process.argv[1]= absolute path to the script being run (set just before execution).process.argv[2+]= script arguments passed after--(set byeval_file_with_args).
3va installpackagefield renamed fromOption<String>toVec<String>(packages). Backward-compatible: omitting all packages still installs from manifest.
Fixed
--allow-net=followed immediately by a positional argument (<FILE>) no longer silently consumed the file path as the network host value.--allow-read=and--allow-write=combining multiple empty flags in one command (--allow-net= --allow-read= --allow-write=) no longer errors with "a value is required".process.argvno longer duplicated script args wheneval_file_with_argswas called (the rawstd::env::args()snapshot included the--args, causing double-appending).fs.statSync().isFile()andfs.statSync().isDirectory()returned the method function body instead of a boolean (the boolean values from JSON were overwritten before being captured in the closure). Fixed by saving raw booleans before creating method functions.
Added (previous session)
-
3va dev— full development server with HMR, SPA fallback, static serving. -
3va audit --secrets— Phase 3 audit for hardcoded secrets in dependencies. -
3va audit --json— machine-readable JSON output. -
3va sandbox— interactive REPL with multi-line support, session commands, TTY detection. -
3va test --watch/--coverage/--update-snapshots. -
3va bundle --split/--minify/--source-map. -
Full ESM support with
EsmResolverandEsmLoader. -
3va updatewith per-package registry tracking. -
3va dev— full development server:- Flags
--port <N>(default 3000),--host <H>(default 127.0.0.1),--open,--public-dir <D>. - HMR via Server-Sent Events at the
/__hmrendpoint. - HMR client script injected automatically before
</body>in all served HTML. - Static file serving with correct MIME types (15 supported types).
- SPA fallback: unknown routes serve
public/index.html. - Automatic rebuild with 300 ms debounce when detecting changes in
.js,.ts,.jsx,.tsxfiles. - Built-in development page when
public/index.htmldoes not exist.
- Flags
-
3va audit --secrets— Phase 3 audit: detection of hardcoded secrets in dependencies (AWS keys, GitHub tokens, PEM private keys, JWT tokens, Stripe keys and other common patterns) viaSecretsScanner. -
3va audit --json— machine-readable output with{ passed, phases: { malware, osv, secrets } }structure; completely suppresses human-readable output. -
audit_packages_silent()invvva_pm— audit variant without console output, used internally in--jsonmode. -
3va sandbox— full interactive REPL:- Multi-line support with balanced bracket detection (parentheses, brackets, braces).
- Session commands:
.help,.exit,.clear,.allow-read <path>,.allow-net <host>,.permissions. - Node.js-style output formatting: objects as JSON, explicit
undefinedfor statements. - TTY detection: in pipes and CI environments (stdin non-TTY), exits immediately without blocking.
-
3va test --watch— automatically re-runs the suite when detecting file changes. -
3va test --coverage— line and branch coverage report upon test completion. -
3va test --update-snapshots/-u— overwrites existing snapshots with current values. -
3va bundle --split— code splitting;--minify— minification;--source-map— source map generation. -
Full ESM:
EsmResolverandEsmLoaderinvvva_js::esm;import/exportsupport with relative paths, re-exports and TypeScript modules. -
Full async/await and Promise chain support via the
execute_pending_jobmicrotask loop. -
Bundler watch mode (
start_watch_mode) with realnotifywatcher (previously was a stub without implementation). -
describeblocks and snapshot support (toMatchSnapshot) in the test runner. -
list_granted()inPermissionState— exposes the list of capabilities granted in the current session. -
3va updatesubcommand with per-package registry tracking. -
registryfield in3va-lock.json(inpackagesanddependencies) to record the origin of each installed package. -
Registry preservation logic in the lockfile upon regeneration: registries of already installed packages are not lost when installing new packages.
-
--allow-netvalidation in3va update: the CLI inspects the lockfile, groups packages by registry and displays the exact command to run if any authorized host is missing. -
Multi-registry support in the same project (e.g.,
axiosfromregistry.npmjs.organd@std/pathfromjsr.io). -
Methods
registry_for(),registries_needed()andset_registry()inLockfile(crates/pm/src/lockfile.rs). -
11 integration tests in
crates/test/tests/runner_integration.rs. -
12 unit tests in
crates/pm/src/auditor.rs. -
28 tests in
crates/js/tests/pipeline.rs(ESM, async/await, TypeScript, permissions). -
Integration suite
scripts/integration_tests.sh: 58 tests in 12 phases (100% passing).
Fixed
is_esm_source()stopped scanning upon finding the first line that was not an import; now scans the entire file with block comment tracking.- Snapshot permission failed when the test file was in
/tmp/(TempDir); nowFileRead/FileWriteis granted to the test file's parent directory. audit --jsonemitted human-readable output before JSON because the malware scanner wrote directly to stdout; resolved viaaudit_packages_silent().run_audit_humanreturned before reaching Phase 3 if Phase 1 (malware) or Phase 2 (OSV) produced an error; now all three phases are resilient to individual failures and always execute independently.
[0.1.0-dev] - 2026-05-19
Active development version. Not yet published as a stable release.
Added
Package Manager (crates/pm)
3va install <package>[@<version>] --allow-net=<registry-host>— secure installation from npm, Yarn or JSR.3va reinstall <package> --allow-net=<registry-host>— forced reinstallation.- Automatic registry derivation from the host in
--allow-net(no separate--registryflag needed). - Support for three integrated registries:
registry.npmjs.org,registry.yarnpkg.com,jsr.io. - Support for scoped packages (
@scope/name, mandatory in JSR). - Package existence verification before installing.
- Version resolution: if not specified, uses
dist-tags.latest; if the requested version does not exist, shows the 5 closest by semver distance. - Version suggestions in
name@versionformat. - Security gate: any attempt to install without
--allow-netshows an explanatory error and suggests the correct command — no silent network calls. - Already installed package detection: prevents accidental reinstallation and suggests
reinstall. package.jsonand3va-lock.jsonupdate after each successful installation.- Signature verification via
SignatureVerifier(SHA-256/SHA-512). - JSR API:
/api/scopes/{scope}/packages/{name}/versionsendpoint. - Semver distance algorithm: score =
major × 1_000_000 + minor × 1_000 + patch.
CLI (crates/cli)
- Subcommands:
run,install,reinstall,update,dev,bundle,test,audit,doctor,sandbox. - Global
--accessibleflag for accessible mode (no colors or animations, EN 301 549 compliant). - Granular permissions in
run:--allow-read,--allow-write,--allow-net,--allow-env,--allow-child-process. - Interactive permission prompt enabled by default in
run.
JavaScript Engine (crates/js)
- QuickJS integration via
rquickjs. - Automatic TypeScript transpilation when executing
.ts. - CommonJS-compatible module system.
- Global APIs:
console,fetch,fs(restricted by permissions), timers.
Bundler (crates/bundler)
3va bundle <input> --output <output>— application bundling.- TypeScript transpilation in the bundle process.
Test Runner (crates/test)
3va test [paths]— test suite execution.- Automatic discovery of
*.test.ts,*.test.js,*.spec.*files.
Security
SignatureVerifier: SHA-256 and SHA-512 hash calculation and verification of files.MalwareScanner: static analysis of dependencies.AuditLogger: sensitive operation logging.- Interactive prompting for runtime permission requests.
- Post-install scripts disabled by default.
Changed
- The
--registryflag was removed from the design. The registry is determined exclusively by the authorized host in--allow-net— consistent with 3va's capability model.
Architecture
- Cargo workspace with crates:
vvva_core,vvva_cli,vvva_permissions,vvva_js,vvva_pm,vvva_bundler,vvva_test. - Rust edition 2024.
- Async runtime: Tokio.
Entry format
Each version follows the structure:
## [X.Y.Z] - YYYY-MM-DD
### Added — new functionality
### Changed — changes in existing functionality
### Deprecated — functionality to be removed in future versions
### Removed — removed functionality
### Fixed — bug fixes
### Security — vulnerability patches (reference CVE if applicable)
Compliant with Keep a Changelog 1.0.0 and SemVer 2.0.0.