Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/mongodb-constants/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"reformat": "npm run prettier -- --write ."
},
"peerDependencies": {
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0"
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2"
},
"devDependencies": {
"@mongodb-js/eslint-config-devtools": "^0.11.13",
Expand All @@ -63,7 +63,7 @@
"@types/mocha": "^9.1.1",
"@types/semver": "^7.8.0",
"acorn": "^8.18.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2",
"chai": "^4.5.0",
"depcheck": "^1.4.7",
"eslint": "^7.25.0 || ^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/mongodb-log-writer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@
"typescript": "^5.9.3"
},
"optionalDependencies": {
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0"
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2"
}
}
4 changes: 2 additions & 2 deletions packages/mongodb-schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"reservoir": "^0.1.2"
},
"optionalDependencies": {
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2",
"cli-table": "^0.3.4",
"js-yaml": "^5.2.2",
"mongodb": "^6.9.0 || ^7.0.0",
Expand All @@ -93,7 +93,7 @@
"@types/node": "^25.5.2",
"@types/reservoir": "^0.1.0",
"ajv": "^8.20.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2",
"chai": "^4.5.0",
"depcheck": "^1.4.7",
"eslint": "^7.25.0 || ^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/mongodb-ts-autocomplete/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"@types/chai": "^4.2.21",
"@types/mocha": "^9.1.1",
"@types/node": "^25.5.2",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2",
"chai": "^4.5.0",
"depcheck": "^1.4.7",
"eslint": "^7.25.0 || ^8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/mql-typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
"zod": "^3.24.2"
},
"dependencies": {
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2",
"mongodb": "^7.5.0"
}
}
2 changes: 0 additions & 2 deletions packages/shell-bson-parser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,3 @@ const query = await parse('{ a: 1 }');

terminateWorker();
```

This package is ESM only.
4 changes: 2 additions & 2 deletions packages/shell-bson-parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"web-worker": "^1.5.0"
},
"peerDependencies": {
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0"
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2"
},
"devDependencies": {
"@mongodb-js/eslint-config-devtools": "^0.11.13",
Expand All @@ -68,7 +68,7 @@
"@types/estree": "^0.0.41",
"@types/mocha": "^9.1.1",
"benchmark": "^2.1.4",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.0.0",
"bson": "^4.6.3 || ^5 || ^6.10.3 || ^7.3.2",
"chai": "^4.5.0",
"depcheck": "^1.4.7",
"eslint": "^7.25.0 || ^8.0.0",
Expand Down
52 changes: 50 additions & 2 deletions packages/shell-bson-parser/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
terminateWorker,
callWorker,
getExecutionTimeoutMs,
getWorkerScriptUrl,
} from './worker-client.js';
import {
restrictGlobalScope,
Expand Down Expand Up @@ -39,6 +40,23 @@ describe('shell-bson-parser with webworker processing', function () {
terminateWorker();
});

it('returns the file URL for the worker script', async function () {
const initialWorkerScriptUrl = process.env.TEST_WORKER_SCRIPT_URL;
delete process.env.TEST_WORKER_SCRIPT_URL;

try {
expect(await getWorkerScriptUrl()).to.equal(
new URL('./worker.js', import.meta.url).toString(),
);
} finally {
if (initialWorkerScriptUrl) {
process.env.TEST_WORKER_SCRIPT_URL = initialWorkerScriptUrl;
} else {
delete process.env.TEST_WORKER_SCRIPT_URL;
}
}
});

it('keeps the error type of a failed parse', async function () {
try {
await api.parse('{ a: }');
Expand All @@ -56,6 +74,16 @@ describe('shell-bson-parser with webworker processing', function () {
expect(res).to.deep.equal(expected);
});
}

it('keeps the error type of a failed parse', async function () {
try {
await api.parse('{ a: }');
expect.fail('Expected parse to throw');
} catch (err) {
expect(err).to.be.instanceOf(SyntaxError);
expect((err as Error).name).to.equal('SyntaxError');
}
});
});

describe('restrictGlobalScope', function () {
Expand Down Expand Up @@ -176,7 +204,20 @@ describe('shell-bson-parser with webworker processing', function () {
const code = await fs.readFile(workerBundlePath, 'utf8');

const sandbox: Record<string, unknown> = Object.create(null);
sandbox.postMessage = function postMessage() {};
let messageHandler: ((event: MessageEvent) => void) | undefined;
let response: any;
sandbox.postMessage = function postMessage(value: unknown) {
expect(this).to.equal(workerScope);
response = value;
};
sandbox.addEventListener = function addEventListener(
name: string,
handler: (event: MessageEvent) => void,
) {
expect(name).to.equal('message');
messageHandler = handler;
};
sandbox.onmessage = null;
sandbox.fetch = function fetch() {};
sandbox.require = function require() {};
sandbox.importScripts = function importScripts() {};
Expand All @@ -186,6 +227,7 @@ describe('shell-bson-parser with webworker processing', function () {
sandbox.globalThis = sandbox;

vm.createContext(sandbox);
const workerScope = vm.runInContext('globalThis', sandbox);

expect(sandbox).to.have.property('fetch');
expect(sandbox).to.have.property('require');
Expand All @@ -199,7 +241,13 @@ describe('shell-bson-parser with webworker processing', function () {
expect(sandbox).to.not.have.property('importScripts');
expect(sandbox).to.not.have.property('XMLHttpRequest');

expect(typeof sandbox.onmessage).to.equal('function');
expect(sandbox).to.not.have.property('onmessage');
expect(messageHandler).to.be.a('function');
messageHandler!({
data: { id: 7, args: { data: ['{ a: 1 }'], bsonTypes: new Map() } },
} as MessageEvent);
expect(response).to.include({ id: 7, ok: true });
expect(response.result.data).to.deep.equal({ a: 1 });

const stillHasProtoAccessor = vm.runInContext(
`Object.prototype.hasOwnProperty('__proto__')`,
Expand Down
40 changes: 21 additions & 19 deletions packages/shell-bson-parser/src/worker-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,32 @@ const pending = new Map<
}
>();

const isNodeEnv =
typeof window === 'undefined' &&
typeof process !== 'undefined' &&
!!process.versions?.node;

async function getWorkerScriptUrl(): Promise<string> {
if (process.env.TEST_WORKER_SCRIPT_URL) {
return new URL(
process.env.TEST_WORKER_SCRIPT_URL,
import.meta.url,
).toString();
}
if (isNodeEnv) {
return new URL('./worker.js', import.meta.url).toString();
/** Exported for tests */
export async function getWorkerScriptUrl(): Promise<string> {
const override =
typeof process !== 'undefined'
? process.env.TEST_WORKER_SCRIPT_URL
: undefined;
if (override) {
return new URL(override, import.meta.url).toString();
}

// On browser env we want to fetch and blob so that the worker
// script can run on the web running locally.
const scriptUrl = new URL('./worker.js', import.meta.url);
if (scriptUrl.protocol === 'file:') {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test this if file: path?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in 9c6fcdd

return scriptUrl.toString();
}
const response = await fetch(scriptUrl);
if (!response.ok) {
throw new Error(
`Failed to fetch shell-bson-parser worker script: ${response.status} ${response.statusText}`,
);
}
const code = await response.text();
blobUrl = URL.createObjectURL(new Blob([code], { type: 'text/javascript' }));
// Compass Web injects whatwg-url for bare URL references; it lacks blob APIs.
// Use the native constructor for blob creation and revocation.
blobUrl = globalThis.URL.createObjectURL(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is globalThis.URL.createObjectURL always a function?

new Blob([code], { type: 'text/javascript' }),
);
return blobUrl;
}

Expand All @@ -73,7 +72,10 @@ function createWorker(): Promise<Worker> {

workerPromise = (async () => {
const scriptUrl = await getWorkerScriptUrl();
const newWorker = new WebWorker(scriptUrl, { type: 'module' });
const newWorker = new WebWorker(
scriptUrl,
typeof globalThis.Worker === 'function' ? undefined : { type: 'module' },
);
const onMessageHandler = (event: MessageEvent<WorkerResponse>) => {
const response = event.data;
const entry = pending.get(response.id);
Expand Down Expand Up @@ -153,7 +155,7 @@ export function terminateWorker(
reason: Error = new Error('Worker terminated'),
): void {
if (worker) worker.terminate();
if (blobUrl) URL.revokeObjectURL(blobUrl);
if (blobUrl) globalThis.URL.revokeObjectURL(blobUrl);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding this back?
#891 (comment)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this showed in the integration with compass-web. There we alias url to whatwg-url and it does not have these methods.


worker = null;
workerPromise = null;
Expand Down
7 changes: 4 additions & 3 deletions packages/shell-bson-parser/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ export function handleRequest(request: WorkerRequest): WorkerResponse {
}

if (typeof self !== 'undefined') {
const sendResponse = postMessage.bind(self);
self.addEventListener('message', (event: MessageEvent<WorkerRequest>) => {
sendResponse(handleRequest(event.data));
});
restrictObjectPrototype();
restrictGlobalScope(globalThis);
self.onmessage = (event: MessageEvent<WorkerRequest>) => {
postMessage(handleRequest(event.data));
};
}
8 changes: 4 additions & 4 deletions packages/shell-bson-parser/test/fixtures/slow-worker.mjs
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
// Avoiding the import of these two functions,
// adding them here to keep this file self-contained
function unmarkBSON(value) {
function untrackBSON(value) {
return value.data;
}
function markBSON(value) {
function trackBSON(value) {
return { data: value, bsonTypes: new Map() };
}
self.onmessage = (event) => {
const { id, args } = event.data;
const [delayMs] = unmarkBSON(args);
const [delayMs] = untrackBSON(args);
const start = Date.now();
while (Date.now() - start < delayMs) {
// noop
}
self.postMessage({
id,
ok: true,
result: markBSON('done'),
result: trackBSON('done'),
});
};
Loading
Loading