-
-
Notifications
You must be signed in to change notification settings - Fork 0
fix(web): load SQLite worker in VS Code Web (vscode.dev) (#418) #419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
9ce4ca4
fix(web): load SQLite worker in VS Code Web (vscode.dev) (#418)
zknpr d79cb47
chore(release): prep 1.3.6 — regression tests, changelog, pre-release…
zknpr af0f2d4
fix(web): key browser parentPort listeners by (handler, event) (#419 …
zknpr a4440ec
test(web): skip bundle test when out/ not built; drop dead types (#41…
zknpr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| /** | ||
| * Regression tests for the browser parentPort adapter. | ||
| * | ||
| * databaseWorker.ts expects Node's worker_threads parentPort shape, while a | ||
| * DedicatedWorkerGlobalScope exposes DOM event APIs. These tests exercise the | ||
| * adapter against a fake worker global scope so the browser behavior is covered | ||
| * without depending on a real browser. | ||
| */ | ||
| import { describe, it } from 'node:test'; | ||
| import assert from 'node:assert'; | ||
| import { createBrowserParentPort } from '../../src/platform/threadPool'; | ||
|
|
||
| describe('createBrowserParentPort', () => { | ||
| it('adapts browser worker scope events to Node-style parentPort methods', () => { | ||
| const registeredListeners: Array<{ | ||
| event: string; | ||
| listener: EventListenerOrEventListenerObject; | ||
| }> = []; | ||
| const removedListeners: Array<{ | ||
| event: string; | ||
| listener: EventListenerOrEventListenerObject; | ||
| }> = []; | ||
| const postedMessages: Array<{ data: unknown; transfer?: Transferable[] }> = []; | ||
|
|
||
| const fakeScope = { | ||
| postMessage(data: unknown, transfer?: Transferable[]) { | ||
| // The fake scope records the exact payload and transfer list forwarded by | ||
| // the adapter so the test can verify postMessage does not transform them. | ||
| postedMessages.push({ data, transfer }); | ||
| }, | ||
| addEventListener(event: string, listener: EventListenerOrEventListenerObject) { | ||
| // The fake scope stores listeners exactly as registered, allowing the | ||
| // test to invoke the wrapped DOM listener with controlled event objects. | ||
| registeredListeners.push({ event, listener }); | ||
| }, | ||
| removeEventListener(event: string, listener: EventListenerOrEventListenerObject) { | ||
| // The fake scope records removals so off() can be checked against the | ||
| // wrapped listener that on() installed. | ||
| removedListeners.push({ event, listener }); | ||
| }, | ||
| }; | ||
|
|
||
| const parentPort = createBrowserParentPort(fakeScope); | ||
|
|
||
| assert.strictEqual(typeof parentPort.postMessage, 'function'); | ||
| assert.strictEqual(typeof parentPort.on, 'function'); | ||
| assert.strictEqual(typeof parentPort.off, 'function'); | ||
|
|
||
| const receivedMessages: unknown[] = []; | ||
| const messageHandler = (payload: unknown) => { | ||
| receivedMessages.push(payload); | ||
| }; | ||
|
|
||
| parentPort.on('message', messageHandler); | ||
|
|
||
| assert.strictEqual(registeredListeners.length, 1); | ||
| assert.strictEqual(registeredListeners[0].event, 'message'); | ||
|
|
||
| const messageListener = registeredListeners[0].listener as EventListener; | ||
| messageListener({ data: 'database-bytes' } as MessageEvent); | ||
|
|
||
| assert.deepStrictEqual(receivedMessages, ['database-bytes']); | ||
|
|
||
| const receivedErrors: unknown[] = []; | ||
| const errorHandler = (event: unknown) => { | ||
| receivedErrors.push(event); | ||
| }; | ||
| const errorEvent = new Event('error'); | ||
|
|
||
| parentPort.on('error', errorHandler); | ||
|
|
||
| assert.strictEqual(registeredListeners.length, 2); | ||
| assert.strictEqual(registeredListeners[1].event, 'error'); | ||
|
|
||
| const errorListener = registeredListeners[1].listener as EventListener; | ||
| errorListener(errorEvent); | ||
|
|
||
| assert.deepStrictEqual(receivedErrors, [errorEvent]); | ||
|
|
||
| const transferBuffer = new ArrayBuffer(8); | ||
| parentPort.postMessage({ ready: true }, [transferBuffer]); | ||
|
|
||
| assert.deepStrictEqual(postedMessages, [ | ||
| { data: { ready: true }, transfer: [transferBuffer] }, | ||
| ]); | ||
|
|
||
| parentPort.off('message', messageHandler); | ||
|
|
||
| assert.strictEqual(removedListeners.length, 1); | ||
| assert.strictEqual(removedListeners[0].event, 'message'); | ||
| assert.strictEqual(removedListeners[0].listener, registeredListeners[0].listener); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }); | ||
|
|
||
| it('removes the correct wrapped listener when one handler is reused for two events', () => { | ||
| // Regression guard: the adapter must key wrapped listeners by (handler, event), | ||
| // not by handler alone. With a flat handler->wrapped map, registering the same | ||
| // handler for 'message' then 'error' would overwrite the first wrapper, so | ||
| // off('message', …) would remove the wrong (error) listener and leak the | ||
| // message one. This reuses a single handler across both events and asserts | ||
| // each off() removes the exact wrapper that on() installed for that event. | ||
| const registeredListeners: Array<{ event: string; listener: EventListener }> = []; | ||
| const removedListeners: Array<{ event: string; listener: EventListener }> = []; | ||
|
|
||
| const fakeScope = { | ||
| postMessage() {}, | ||
| addEventListener(event: string, listener: EventListenerOrEventListenerObject) { | ||
| registeredListeners.push({ event, listener: listener as EventListener }); | ||
| }, | ||
| removeEventListener(event: string, listener: EventListenerOrEventListenerObject) { | ||
| removedListeners.push({ event, listener: listener as EventListener }); | ||
| }, | ||
| }; | ||
|
|
||
| const parentPort = createBrowserParentPort(fakeScope); | ||
|
|
||
| // Same handler reference registered for two distinct events. | ||
| const sharedHandler = () => {}; | ||
| parentPort.on('message', sharedHandler); | ||
| parentPort.on('error', sharedHandler); | ||
|
|
||
| assert.strictEqual(registeredListeners.length, 2); | ||
| const messageWrapped = registeredListeners[0].listener; | ||
| const errorWrapped = registeredListeners[1].listener; | ||
| // Each event must get its OWN wrapper (the bug would reuse/overwrite one). | ||
| assert.notStrictEqual(messageWrapped, errorWrapped); | ||
|
|
||
| parentPort.off('message', sharedHandler); | ||
| parentPort.off('error', sharedHandler); | ||
|
|
||
| assert.strictEqual(removedListeners.length, 2); | ||
| assert.deepStrictEqual( | ||
| removedListeners.map(r => r.event), | ||
| ['message', 'error'] | ||
| ); | ||
| // The wrapper removed for each event matches the one registered for it. | ||
| assert.strictEqual(removedListeners[0].listener, messageWrapped); | ||
| assert.strictEqual(removedListeners[1].listener, errorWrapped); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /** | ||
| * Regression tests for the browser worker bundle format. | ||
| * | ||
| * VS Code Web loads out/worker-browser.js as a classic Web Worker from a blob | ||
| * URL. Classic workers parse scripts with the normal script grammar, so this | ||
| * test compiles the bundle with node:vm to catch module-only syntax such as a | ||
| * top-level export before it can ship. | ||
| */ | ||
| import { describe, it } from 'node:test'; | ||
| import assert from 'node:assert'; | ||
| import { existsSync, readFileSync } from 'node:fs'; | ||
| import path from 'node:path'; | ||
| import vm from 'node:vm'; | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| describe('browser worker bundle', () => { | ||
| it('parses as a classic worker script', (t) => { | ||
| const bundlePath = path.resolve(process.cwd(), 'out/worker-browser.js'); | ||
|
|
||
| // This validates a build artifact, not source. `out/` is gitignored and the | ||
| // `npm test` script does not build first, so on a clean checkout the bundle | ||
| // may be absent — skip (don't fail the suite) with a clear hint. CI runs | ||
| // `node scripts/build.mjs` before tests, so there the assertions always run. | ||
| if (!existsSync(bundlePath)) { | ||
| t.skip('out/worker-browser.js not built — run `node scripts/build.mjs` first (CI builds before tests).'); | ||
| return; | ||
| } | ||
|
|
||
| const source = readFileSync(bundlePath, 'utf8'); | ||
|
|
||
| const isIifeBundle = /^(?:"use strict";)?\s*\(\s*(?:\(\)\s*=>|function\s*\()/.test( | ||
| source.trimStart() | ||
| ); | ||
| assert.ok( | ||
| isIifeBundle, | ||
| 'out/worker-browser.js must be emitted as an IIFE classic-worker bundle' | ||
| ); | ||
|
|
||
| assert.doesNotThrow( | ||
| () => new vm.Script(source, { filename: bundlePath }), | ||
| 'out/worker-browser.js must parse as a classic worker script' | ||
| ); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.