Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123const util = require('util');
function assert(cond, msg) { if (!cond) throw new Error(msg || 'assertion failed');}
process.env.ANT_ENV_PROXY_T = 7;assert(process.env.ANT_ENV_PROXY_T === '7', 'values are coerced to strings');assert('ANT_ENV_PROXY_T' in process.env && 'HOME' in process.env, 'in sees set and inherited vars');assert(!('ANT_ENV_PROXY_MISSING' in process.env), 'in rejects missing vars');
const keys = Object.keys(process.env);assert(keys.includes('ANT_ENV_PROXY_T') && keys.includes('HOME'), 'keys list set and inherited vars');
const spread = { ...process.env };assert(spread.ANT_ENV_PROXY_T === '7' && typeof spread.HOME === 'string', 'spread copies the environment');assert(Object.assign({}, process.env).ANT_ENV_PROXY_T === '7', 'Object.assign copies the environment');
const envKeys = Object.keys(process.env);const envEntries = Object.entries(process.env);assert(envEntries.map(([k]) => k).join('\0') === envKeys.join('\0'), 'entries follow keys in order');assert(envEntries.every(([k, v]) => process.env[k] === v), 'entries carry the current values');assert(Object.values(process.env).join('\0') === envKeys.map(k => process.env[k]).join('\0'), 'values follow keys');assert(Object.keys({ ...process.env }).join('\0') === envKeys.join('\0'), 'spread keeps the key order');assert(Object.keys(Object.assign({}, process.env)).join('\0') === envKeys.join('\0'), 'Object.assign keeps the key order');const { ANT_ENV_PROXY_T: restT, ...envRest } = process.env;assert(restT === '7' && !('ANT_ENV_PROXY_T' in envRest) && envRest.HOME === process.env.HOME, 'rest excludes named vars');
let seen = 0;for (const k in process.env) if (k === 'HOME' || k === 'ANT_ENV_PROXY_T') seen++;assert(seen === 2, 'for-in visits set and inherited vars');
const desc = Object.getOwnPropertyDescriptor(process.env, 'HOME');assert(desc && desc.enumerable && desc.configurable && desc.writable, 'inherited vars have data descriptors');
assert(!util.types.isProxy(process.env), 'process.env is not reported as a proxy');assert(util.inspect(process.env).includes('ANT_ENV_PROXY_T'), 'inspect shows the environment');
delete process.env.ANT_ENV_PROXY_T;assert(process.env.ANT_ENV_PROXY_T === undefined && !('ANT_ENV_PROXY_T' in process.env), 'delete removes the var');
process.env.ANT_ENV_PROXY_CHILD = 'inherited';const cp = require('child_process');const child = cp.spawnSync('/bin/sh', ['-c', 'printf "%s|%s" "$HOME" "$ANT_ENV_PROXY_CHILD"'], { env: process.env });assert(String(child.stdout) === process.env.HOME + '|inherited', 'spawnSync with env: process.env passes the environment');const execOut = cp.execSync('printf "%s" "$ANT_ENV_PROXY_CHILD"', { env: process.env });assert(String(execOut) === 'inherited', 'execSync with env: process.env passes the environment');
assert(Object.getOwnPropertyNames(process.env).includes('ANT_ENV_PROXY_CHILD'), 'getOwnPropertyNames lists env vars');assert(Reflect.ownKeys(process.env).includes('HOME'), 'Reflect.ownKeys lists env vars');assert(Object.getOwnPropertyDescriptors(process.env).HOME.value === process.env.HOME, 'getOwnPropertyDescriptors sees env vars');assert(structuredClone(process.env).ANT_ENV_PROXY_CHILD === 'inherited', 'structuredClone copies env vars');
let setErr;try { process.env.ANT_ENV_PROXY_BAD = { toString() { throw new Error('bad-tostring'); } }; }catch (e) { setErr = e; }assert(setErr && setErr.message === 'bad-tostring', 'a failing set reports the original error');
function throws(fn) { try { fn(); } catch (e) { return e; } return null;}
// like node and bun, a name the environment cannot hold is accepted and droppedprocess.env['ANT_ENV=BAD'] = 'x';assert(Reflect.set(process.env, '', 'x') === true && !('' in process.env), 'an empty name is accepted and dropped');assert(throws(() => { 'use strict'; process.env['ANT_ENV=BAD'] = 'x'; }) === null, 'a strict write to an invalid name does not throw');
process.env.ANT_ENV_PROXY_EQ = '1';assert(Reflect.set(process.env, 'ANT_ENV_PROXY_EQ=Q', '2') === true, "a name with '=' is accepted");assert(process.env.ANT_ENV_PROXY_EQ === '1', "a name with '=' never reaches the var before it");delete process.env.ANT_ENV_PROXY_EQ;
process.env.ANT_ENV_PROXY_NUL = 'kept';assert(Reflect.set(process.env, 'ANT_ENV_PROXY_NUL\0X', 'clobbered') === true, 'a name with NUL is accepted');assert(process.env['ANT_ENV_PROXY_NUL\0X'] === undefined, 'a name with NUL reads as absent');assert(delete process.env['ANT_ENV_PROXY_NUL\0X'] && process.env.ANT_ENV_PROXY_NUL === 'kept', 'a name with NUL never reaches the var before it');delete process.env.ANT_ENV_PROXY_NUL;
const inheritsEnv = Object.create(process.env);inheritsEnv.ANT_ENV_PROXY_RECV = 'r';assert(Object.hasOwn(inheritsEnv, 'ANT_ENV_PROXY_RECV'), 'a write through the prototype chain lands on the receiver');assert(process.env.ANT_ENV_PROXY_RECV === undefined, 'a write through the prototype chain leaves the environment alone');
const full = { value: 5, writable: true, enumerable: true, configurable: true };Object.defineProperty(process.env, 'ANT_ENV_PROXY_DEF', full);assert(process.env.ANT_ENV_PROXY_DEF === '5', 'a full data descriptor stores the value in the environment');assert(throws(() => Object.defineProperty(process.env, 'ANT_ENV_PROXY_DEF', { value: 'x' })) instanceof TypeError, 'a partial descriptor is rejected');assert(throws(() => Object.defineProperty(process.env, 'ANT_ENV_PROXY_ACC', { get() { return 1; } })) instanceof TypeError, 'accessors cannot be defined on env vars');assert(process.env.ANT_ENV_PROXY_DEF === '5', 'a rejected descriptor changes nothing');
const envSym = Symbol('env');const symbolError = throws(() => { process.env[envSym] = 1; });assert(symbolError instanceof TypeError && symbolError.message === 'Cannot convert a Symbol value to a string', 'a symbol key cannot be set');assert(throws(() => Object.defineProperty(process.env, envSym, full)) instanceof TypeError, 'a symbol key cannot be defined');assert(throws(() => { process.env.ANT_ENV_PROXY_SYMV = Symbol('v'); }) instanceof TypeError, 'a symbol value cannot be stored');assert(process.env[envSym] === undefined && !(envSym in process.env) && delete process.env[envSym], 'a symbol key reads as absent');assert(Object.getOwnPropertyNames(process.env).includes('toObject'), 'own methods of process.env are listed');assert(!Object.keys(process.env).includes('toObject'), 'own methods of process.env are not enumerable');
delete process.env.ANT_ENV_PROXY_CHILD;delete process.env.ANT_ENV_PROXY_DEF;
assert(throws(() => Object.freeze(process.env))?.message === 'Cannot freeze', 'freezing process.env throws');assert(throws(() => Object.seal(process.env))?.message === 'Cannot seal', 'sealing process.env throws');assert(throws(() => Object.preventExtensions(process.env))?.message === 'Cannot prevent extensions', 'preventExtensions on process.env throws');assert(Reflect.preventExtensions(process.env) === false, 'Reflect.preventExtensions on process.env returns false');assert(Object.isExtensible(process.env) && !Object.isFrozen(process.env), 'process.env stays extensible');
const fs = require('fs');const os = require('os');const path = require('path');const dotenvDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ant-dotenv-'));fs.writeFileSync(path.join(dotenvDir, '.env'), 'ANT_DOTENV_BOTH=file\nANT_DOTENV_FILE=file\n');const dotenvChild = cp.spawnSync(process.execPath, ['-e', 'console.log(process.env.ANT_DOTENV_BOTH + "|" + process.env.ANT_DOTENV_FILE)'], { cwd: dotenvDir, env: { ...process.env, ANT_DOTENV_BOTH: 'env' },});fs.rmSync(dotenvDir, { recursive: true, force: true });assert(String(dotenvChild.stdout).trim() === 'env|file', '.env fills gaps but the real environment wins: ' + String(dotenvChild.stdout).trim());
console.log('process.env proxy OK');