mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
fix: add uuid v7 support in pre-request scripts (#7377)
This commit is contained in:
committed by
GitHub
parent
073b1ef036
commit
c273c10f0c
178
package-lock.json
generated
178
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()}
|
||||
`
|
||||
);
|
||||
|
||||
|
||||
166
packages/bruno-js/src/sandbox/quickjs/shims/lib/uuid.spec.js
Normal file
166
packages/bruno-js/src/sandbox/quickjs/shims/lib/uuid.spec.js
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
56
packages/bruno-js/src/sandbox/quickjs/shims/require.js
Normal file
56
packages/bruno-js/src/sandbox/quickjs/shims/require.js
Normal 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
|
||||
};
|
||||
154
packages/bruno-js/src/sandbox/quickjs/shims/require.spec.js
Normal file
154
packages/bruno-js/src/sandbox/quickjs/shims/require.spec.js
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
31
packages/bruno-js/src/sandbox/quickjs/utils/test-helpers.js
Normal file
31
packages/bruno-js/src/sandbox/quickjs/utils/test-helpers.js
Normal 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
|
||||
};
|
||||
Reference in New Issue
Block a user