fix: add uuid v7 support in pre-request scripts (#7377)

This commit is contained in:
Chirag Chandrashekhar
2026-04-03 17:20:23 +05:30
committed by GitHub
parent 073b1ef036
commit c273c10f0c
8 changed files with 416 additions and 209 deletions

178
package-lock.json generated
View File

@@ -32002,9 +32002,9 @@
}
},
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
@@ -32885,16 +32885,6 @@
"webpack-cli": "^4.9.1"
}
},
"packages/bruno-app/node_modules/@babel/compat-data": {
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz",
"integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"packages/bruno-app/node_modules/@babel/helper-create-regexp-features-plugin": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz",
@@ -33156,23 +33146,6 @@
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-app/node_modules/@babel/plugin-transform-class-properties": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz",
"integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-app/node_modules/@babel/plugin-transform-class-static-block": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz",
@@ -33458,23 +33431,6 @@
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-app/node_modules/@babel/plugin-transform-modules-commonjs": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz",
"integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-app/node_modules/@babel/plugin-transform-modules-systemjs": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz",
@@ -34530,16 +34486,6 @@
"typescript": "^5.8.3"
}
},
"packages/bruno-common/node_modules/@babel/compat-data": {
"version": "7.26.8",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
"integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"packages/bruno-common/node_modules/@babel/generator": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
@@ -34574,46 +34520,6 @@
"node": ">=6.9.0"
}
},
"packages/bruno-common/node_modules/@babel/helper-create-class-features-plugin": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz",
"integrity": "sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
"@babel/helper-member-expression-to-functions": "^7.25.9",
"@babel/helper-optimise-call-expression": "^7.25.9",
"@babel/helper-replace-supers": "^7.26.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
"@babel/traverse": "^7.27.0",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"packages/bruno-common/node_modules/@babel/helper-replace-supers": {
"version": "7.26.5",
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz",
"integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-member-expression-to-functions": "^7.25.9",
"@babel/helper-optimise-call-expression": "^7.25.9",
"@babel/traverse": "^7.26.5"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"packages/bruno-common/node_modules/@babel/parser": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
@@ -34729,26 +34635,6 @@
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-common/node_modules/@babel/plugin-transform-typescript": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.0.tgz",
"integrity": "sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
"@babel/helper-create-class-features-plugin": "^7.27.0",
"@babel/helper-plugin-utils": "^7.26.5",
"@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
"@babel/plugin-syntax-typescript": "^7.25.9"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-common/node_modules/@babel/preset-env": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz",
@@ -34833,26 +34719,6 @@
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-common/node_modules/@babel/preset-typescript": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.0.tgz",
"integrity": "sha512-vxaPFfJtHhgeOVXRKuHpHPAOgymmy8V8I65T1q53R7GCZlefKeCaTyDs3zOPHTTbmquvNlQYC5klEvWsBAtrBQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.26.5",
"@babel/helper-validator-option": "^7.25.9",
"@babel/plugin-syntax-jsx": "^7.25.9",
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
"@babel/plugin-transform-typescript": "^7.27.0"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"packages/bruno-common/node_modules/@babel/template": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
@@ -35212,7 +35078,7 @@
"simple-git": "3.32.3",
"socks-proxy-agent": "^8.0.2",
"tough-cookie": "^6.0.0",
"uuid": "^9.0.0",
"uuid": "^10.0.0",
"yup": "^0.32.11"
},
"devDependencies": {
@@ -35938,23 +35804,6 @@
"react": "^18.2.0"
}
},
"packages/bruno-graphql-docs/node_modules/rollup": {
"version": "3.29.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true,
"license": "MIT",
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"packages/bruno-graphql-docs/node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -35990,7 +35839,7 @@
"path": "^0.12.7",
"quickjs-emscripten": "^0.29.2",
"tv4": "^1.3.0",
"uuid": "^9.0.0",
"uuid": "^10.0.0",
"xml-formatter": "^3.5.0",
"xml2js": "^0.6.2",
"yaml": "^2.3.4"
@@ -36104,23 +35953,6 @@
"typescript": "^4.8.4"
}
},
"packages/bruno-query/node_modules/rollup": {
"version": "3.29.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true,
"license": "MIT",
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"packages/bruno-requests": {
"name": "@usebruno/requests",
"version": "0.1.0",

View File

@@ -74,7 +74,7 @@
"simple-git": "3.32.3",
"socks-proxy-agent": "^8.0.2",
"tough-cookie": "^6.0.0",
"uuid": "^9.0.0",
"uuid": "^10.0.0",
"yup": "^0.32.11"
},
"optionalDependencies": {

View File

@@ -32,7 +32,7 @@
"path": "^0.12.7",
"quickjs-emscripten": "^0.29.2",
"tv4": "^1.3.0",
"uuid": "^9.0.0",
"uuid": "^10.0.0",
"xml-formatter": "^3.5.0",
"xml2js": "^0.6.2",
"yaml": "^2.3.4"

View File

@@ -5,6 +5,7 @@ const addBrunoResponseShimToContext = require('./shims/bruno-response');
const addTestShimToContext = require('./shims/test');
const addLibraryShimsToContext = require('./shims/lib');
const addLocalModuleLoaderShimToContext = require('./shims/local-module');
const { getRequireCode } = require('./shims/require');
const { newQuickJSWASMModule, memoizePromiseFactory } = require('quickjs-emscripten');
// execute `npm run sandbox:bundle-libraries` if the below file doesn't exist
@@ -104,44 +105,11 @@ const executeQuickJsVmAsync = async ({ script: externalScript, context: external
await addCryptoUtilsShimToContext(vm);
const bundledCode = getBundledCode?.toString() || '';
const moduleLoaderCode = function () {
return `
globalThis.require = (mod) => {
let lib = globalThis.requireObject[mod];
let isModuleAPath = (module) => (module?.startsWith('.') || module?.startsWith?.(bru.cwd()))
if (lib) {
return lib;
}
else if (isModuleAPath(mod)) {
// fetch local module
let localModuleCode = globalThis.__brunoLoadLocalModule(mod);
// compile local module as iife
(function (){
const initModuleExportsCode = "const module = { exports: {} };"
const copyModuleExportsCode = "\\n;globalThis.requireObject[mod] = module.exports;";
const patchedRequire = ${`
"\\n;" +
"let require = (subModule) => isModuleAPath(subModule) ? globalThis.require(path.resolve(bru.cwd(), mod, '..', subModule)) : globalThis.require(subModule)" +
"\\n;"
`}
eval(initModuleExportsCode + patchedRequire + localModuleCode + copyModuleExportsCode);
})();
// resolve module
return globalThis.requireObject[mod];
}
else {
throw new Error("Cannot find module " + mod);
}
}
`;
};
vm.evalCode(
`
(${bundledCode})()
${moduleLoaderCode()}
${getRequireCode()}
`
);

View File

@@ -0,0 +1,166 @@
const { describe, it, expect, beforeAll, beforeEach, afterEach, afterAll } = require('@jest/globals');
const { newQuickJSWASMModule } = require('quickjs-emscripten');
const { validate: uuidValidate, version: uuidVersion } = require('uuid');
const addUuidShimToContext = require('./uuid');
const { addRequireShimToContext } = require('../require');
const { createEvalHelper } = require('../../utils/test-helpers');
describe('uuid shim tests', () => {
let vm, module, evalAndDump;
beforeAll(async () => {
module = await newQuickJSWASMModule();
});
beforeEach(async () => {
vm = module.newContext();
evalAndDump = createEvalHelper(vm);
await addUuidShimToContext(vm);
addRequireShimToContext(vm, { enableLocalModules: false });
});
afterEach(() => {
if (vm) {
try {
vm.dispose();
} catch (err) {
// Ignore disposal errors
}
vm = null;
}
});
afterAll(() => {
if (module) {
try {
module.dispose();
} catch (err) {
// Ignore disposal errors
}
module = null;
}
});
/**
* Validates that a string is a valid UUID of the expected version
*/
function expectUuidVersion(uuid, expectedVersion) {
expect(uuidValidate(uuid)).toBe(true);
expect(uuidVersion(uuid)).toBe(expectedVersion);
}
describe('uuid version functions', () => {
const versionTests = [
{ fn: 'v1', version: 1 },
{ fn: 'v4', version: 4 },
{ fn: 'v6', version: 6, note: 'RFC 9562' },
{ fn: 'v7', version: 7, note: 'RFC 9562' }
];
it.each(versionTests)('$fn should generate valid uuid and be accessible via require', ({ fn, version }) => {
// Test direct access
const directUuid = evalAndDump(`globalThis.uuid.${fn}()`);
expectUuidVersion(directUuid, version);
// Test require with destructuring
const requireUuid = evalAndDump(`const { ${fn} } = require('uuid'); ${fn}()`);
expectUuidVersion(requireUuid, version);
});
it('v7 should be accessible with alias pattern (v7: uuidv7) - issue #7333', () => {
const uuid = evalAndDump(`
const { v7: uuidv7 } = require('uuid');
uuidv7();
`);
expectUuidVersion(uuid, 7);
});
it('v7 should generate time-ordered uuids', () => {
const [uuid1, uuid2] = evalAndDump(`
const { v7 } = require('uuid');
[v7(), v7()];
`);
// v7 UUIDs are lexicographically sortable by design (timestamp in most significant bits)
expect(uuid1 <= uuid2).toBe(true);
});
});
describe('name-based uuid functions (v3, v5)', () => {
const DNS_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
it.each([
{ fn: 'v3', version: 3 },
{ fn: 'v5', version: 5 }
])('$fn should generate valid uuid with namespace', ({ fn, version }) => {
const uuid = evalAndDump(`
const { ${fn} } = require('uuid');
${fn}('example.com', '${DNS_NAMESPACE}');
`);
expectUuidVersion(uuid, version);
});
});
describe('conversion functions', () => {
it('should convert between v1 and v6 using v1ToV6 and v6ToV1', () => {
const [v1Uuid, v6FromV1, v6Uuid, v1FromV6] = evalAndDump(`
const { v1, v6, v1ToV6, v6ToV1 } = require('uuid');
const v1Uuid = v1();
const v6Uuid = v6();
[v1Uuid, v1ToV6(v1Uuid), v6Uuid, v6ToV1(v6Uuid)];
`);
expectUuidVersion(v1Uuid, 1);
expectUuidVersion(v6FromV1, 6);
expectUuidVersion(v6Uuid, 6);
expectUuidVersion(v1FromV6, 1);
});
});
describe('utility functions', () => {
it('should validate uuids correctly', () => {
const [isValid, isInvalid] = evalAndDump(`
const { v4, validate } = require('uuid');
[validate(v4()), validate('not-a-uuid')];
`);
expect(isValid).toBe(true);
expect(isInvalid).toBe(false);
});
it('should detect uuid version correctly', () => {
const [v4Version, v7Version] = evalAndDump(`
const { v4, v7, version } = require('uuid');
[version(v4()), version(v7())];
`);
expect(v4Version).toBe(4);
expect(v7Version).toBe(7);
});
it('should parse and stringify uuid', () => {
const [original, stringified, isObject, length] = evalAndDump(`
const { v4, parse, stringify } = require('uuid');
const original = v4();
const parsed = parse(original);
[original, stringify(parsed), typeof parsed === 'object', Object.keys(parsed).length];
`);
expect(original).toBe(stringified);
expect(isObject).toBe(true);
expect(length).toBe(16);
});
});
describe('issue #7333 regression test', () => {
it('should work with the exact pattern from the bug report', () => {
const [typeOfFn, id1, id2] = evalAndDump(`
const { v7: uuidv7 } = require('uuid');
const id1 = uuidv7();
const id2 = uuidv7();
[typeof uuidv7, id1, id2];
`);
expect(typeOfFn).toBe('function');
expectUuidVersion(id1, 7);
expectUuidVersion(id2, 7);
expect(id1).not.toBe(id2);
});
});
});

View File

@@ -0,0 +1,56 @@
/**
* Returns JavaScript code that sets up the require() function in the QuickJS VM.
* This module loader looks up modules from globalThis.requireObject and optionally
* supports loading local modules if the necessary context (bru.cwd, __brunoLoadLocalModule) is available.
*
* @param {Object} options
* @param {boolean} options.enableLocalModules - Whether to enable local module loading (requires bru context)
* @returns {string} JavaScript code to eval in the VM
*/
function getRequireCode() {
return `
globalThis.require = (mod) => {
let lib = globalThis.requireObject[mod];
let isModuleAPath = (module) => (module?.startsWith('.') || (typeof bru !== 'undefined' && module?.startsWith(bru.cwd())))
if (lib) {
return lib;
}
else if (isModuleAPath(mod)) {
// fetch local module
let localModuleCode = globalThis.__brunoLoadLocalModule(mod);
// compile local module as iife
(function (){
const initModuleExportsCode = "const module = { exports: {} };"
const copyModuleExportsCode = "\\n;globalThis.requireObject[mod] = module.exports;";
const patchedRequire = ${`
"\\n;" +
"let require = (subModule) => isModuleAPath(subModule) ? globalThis.require(path.resolve(bru.cwd(), mod, '..', subModule)) : globalThis.require(subModule)" +
"\\n;"
`}
eval(initModuleExportsCode + patchedRequire + localModuleCode + copyModuleExportsCode);
})();
// resolve module
return globalThis.requireObject[mod];
}
else {
throw new Error("Cannot find module " + mod);
}
}
`;
}
/**
* Adds the require() function to a QuickJS VM context
* @param {Object} vm - QuickJS VM context
* @param {Object} options - Options passed to getRequireCode
*/
function addRequireShimToContext(vm) {
vm.evalCode(getRequireCode());
}
module.exports = {
getRequireCode,
addRequireShimToContext
};

View File

@@ -0,0 +1,154 @@
const { describe, it, expect, beforeAll, beforeEach, afterEach, afterAll } = require('@jest/globals');
const { newQuickJSWASMModule } = require('quickjs-emscripten');
const { addRequireShimToContext, getRequireCode } = require('./require');
const { createEvalHelper } = require('../utils/test-helpers');
describe('require shim tests', () => {
let vm, module, evalAndDump;
beforeAll(async () => {
module = await newQuickJSWASMModule();
});
beforeEach(() => {
vm = module.newContext();
evalAndDump = createEvalHelper(vm);
// Initialize empty requireObject
vm.evalCode('globalThis.requireObject = {}');
});
afterEach(() => {
if (vm) {
try {
vm.dispose();
} catch (err) {
// Ignore disposal errors
}
vm = null;
}
});
afterAll(() => {
if (module) {
try {
module.dispose();
} catch (err) {
// Ignore disposal errors
}
module = null;
}
});
describe('getRequireCode', () => {
it('should return a string', () => {
expect(typeof getRequireCode()).toBe('string');
});
it('should contain require function definition', () => {
const code = getRequireCode();
expect(code).toContain('globalThis.require');
expect(code).toContain('requireObject');
});
});
describe('addRequireShimToContext', () => {
it('should add require function to the VM context', () => {
addRequireShimToContext(vm);
const typeOfRequire = evalAndDump('typeof globalThis.require');
expect(typeOfRequire).toBe('function');
});
it('should return module from requireObject', () => {
addRequireShimToContext(vm);
// Register a mock module
vm.evalCode(`
globalThis.requireObject['test-module'] = { foo: 'bar', answer: 42 };
`);
const result = evalAndDump(`
const mod = require('test-module');
[mod.foo, mod.answer];
`);
expect(result).toEqual(['bar', 42]);
});
it('should support destructuring from required modules', () => {
addRequireShimToContext(vm, { enableLocalModules: false });
vm.evalCode(`
globalThis.requireObject['my-lib'] = {
greet: (name) => 'Hello, ' + name,
VERSION: '1.0.0'
};
`);
const [greeting, version] = evalAndDump(`
const { greet, VERSION } = require('my-lib');
[greet('World'), VERSION];
`);
expect(greeting).toBe('Hello, World');
expect(version).toBe('1.0.0');
});
it('should support aliased destructuring', () => {
addRequireShimToContext(vm);
vm.evalCode(`
globalThis.requireObject['utils'] = { v1: () => 'version-1' };
`);
const result = evalAndDump(`
const { v1: getVersion } = require('utils');
getVersion();
`);
expect(result).toBe('version-1');
});
it('should throw error for unknown modules', () => {
addRequireShimToContext(vm);
const result = vm.evalCode(`
try {
require('non-existent-module');
'no error';
} catch (e) {
e.message;
}
`);
const handle = vm.unwrapResult(result);
const errorMessage = vm.dump(handle);
handle.dispose();
expect(errorMessage).toContain('Cannot find module');
expect(errorMessage).toContain('non-existent-module');
});
it('should allow requiring the same module multiple times', () => {
addRequireShimToContext(vm);
vm.evalCode(`
globalThis.requireObject['counter'] = { count: 0 };
`);
const result = evalAndDump(`
const mod1 = require('counter');
const mod2 = require('counter');
mod1 === mod2;
`);
expect(result).toBe(true);
});
});
describe('enableLocalModules option', () => {
it('should include local module loading code when enabled', () => {
const code = getRequireCode();
expect(code).toContain('isModuleAPath');
expect(code).toContain('__brunoLoadLocalModule');
});
});
});

View File

@@ -0,0 +1,31 @@
/**
* Evaluates code in a QuickJS VM and returns the dumped result.
* Handles unwrapping and disposing of handles automatically.
*
* @param {Object} vm - QuickJS VM context
* @param {string} code - JavaScript code to evaluate
* @returns {*} The evaluated and dumped result
*/
function evalAndDump(vm, code) {
const result = vm.evalCode(code);
const handle = vm.unwrapResult(result);
const value = vm.dump(handle);
handle.dispose();
return value;
}
/**
* Creates a helper function bound to a specific VM instance.
* Useful in beforeEach to create a test-scoped helper.
*
* @param {Object} vm - QuickJS VM context
* @returns {Function} evalAndDump function bound to the VM
*/
function createEvalHelper(vm) {
return (code) => evalAndDump(vm, code);
}
module.exports = {
evalAndDump,
createEvalHelper
};