From b8e8bbe482b66e8cbe9167d03e9d8dedd2d3b6c5 Mon Sep 17 00:00:00 2001 From: Alireza Date: Thu, 5 Dec 2024 11:30:21 -0500 Subject: [PATCH] fix(CinePlayer): always show cine player for dynamic data (#4575) Co-authored-by: Ibrahim --- .../cornerstone-dicom-pmap/package.json | 4 +-- extensions/cornerstone-dicom-seg/package.json | 4 +-- extensions/cornerstone-dicom-sr/package.json | 6 ++-- .../cornerstone-dynamic-volume/package.json | 4 +-- extensions/cornerstone/package.json | 8 ++--- .../Overlays/ViewportImageScrollbar.tsx | 16 ++++++---- .../src/utils/ActiveViewportBehavior.tsx | 3 +- .../default/src/DicomWebDataSource/index.js | 3 +- extensions/measurement-tracking/package.json | 4 +-- platform/app/package.json | 2 +- platform/core/package.json | 2 +- playwright.config.ts | 8 +++-- tests/3DFourUp.spec.ts | 12 +++++-- tests/3DMain.spec.ts | 10 ++++-- tests/3DOnly.spec.ts | 10 ++++-- tests/3DPrimary.spec.ts | 10 ++++-- tests/Crosshairs.spec.ts | 20 ++++++++---- tests/utils/attemptAction.ts | 21 ++++++++++++ tests/utils/index.ts | 2 ++ yarn.lock | 32 +++++++++---------- 20 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 tests/utils/attemptAction.ts diff --git a/extensions/cornerstone-dicom-pmap/package.json b/extensions/cornerstone-dicom-pmap/package.json index a4ac1163dfb..66c78df6122 100644 --- a/extensions/cornerstone-dicom-pmap/package.json +++ b/extensions/cornerstone-dicom-pmap/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^2.2.20", - "@cornerstonejs/core": "^2.2.20", + "@cornerstonejs/adapters": "^2.7.3", + "@cornerstonejs/core": "^2.7.3", "@kitware/vtk.js": "32.1.1", "react-color": "^2.19.3" } diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json index 6e669a67b81..2ca8ceb8d4e 100644 --- a/extensions/cornerstone-dicom-seg/package.json +++ b/extensions/cornerstone-dicom-seg/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^2.2.20", - "@cornerstonejs/core": "^2.2.20", + "@cornerstonejs/adapters": "^2.7.3", + "@cornerstonejs/core": "^2.7.3", "@kitware/vtk.js": "32.1.1", "react-color": "^2.19.3" } diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json index 9ee44502610..3a2ed1722ea 100644 --- a/extensions/cornerstone-dicom-sr/package.json +++ b/extensions/cornerstone-dicom-sr/package.json @@ -46,9 +46,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^2.2.20", - "@cornerstonejs/core": "^2.2.20", - "@cornerstonejs/tools": "^2.2.20", + "@cornerstonejs/adapters": "^2.7.3", + "@cornerstonejs/core": "^2.7.3", + "@cornerstonejs/tools": "^2.7.3", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone-dynamic-volume/package.json b/extensions/cornerstone-dynamic-volume/package.json index 4a1d7dcba58..1e075eb4d4c 100644 --- a/extensions/cornerstone-dynamic-volume/package.json +++ b/extensions/cornerstone-dynamic-volume/package.json @@ -42,8 +42,8 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/core": "^2.2.20", - "@cornerstonejs/tools": "^2.2.20", + "@cornerstonejs/core": "^2.7.3", + "@cornerstonejs/tools": "^2.7.3", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index 23ac5b3f135..b9cd56390a9 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -38,7 +38,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^2.2.20", + "@cornerstonejs/dicom-image-loader": "^2.7.3", "@icr/polyseg-wasm": "^0.4.0", "@ohif/core": "3.10.0-beta.14", "@ohif/ui": "3.10.0-beta.14", @@ -55,9 +55,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^2.2.20", - "@cornerstonejs/core": "^2.2.20", - "@cornerstonejs/tools": "^2.2.20", + "@cornerstonejs/adapters": "^2.7.3", + "@cornerstonejs/core": "^2.7.3", + "@cornerstonejs/tools": "^2.7.3", "@icr/polyseg-wasm": "^0.4.0", "@kitware/vtk.js": "32.1.1", "html2canvas": "^1.4.1", diff --git a/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx b/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx index 6edc5066576..a09e49c9be9 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx +++ b/extensions/cornerstone/src/Viewport/Overlays/ViewportImageScrollbar.tsx @@ -44,13 +44,17 @@ function CornerstoneImageScrollbar({ return; } - const imageIndex = viewport.getCurrentImageIdIndex(); - const numberOfSlices = viewport.getNumberOfSlices(); + try { + const imageIndex = viewport.getCurrentImageIdIndex(); + const numberOfSlices = viewport.getNumberOfSlices(); - setImageSliceData({ - imageIndex: imageIndex, - numberOfSlices, - }); + setImageSliceData({ + imageIndex: imageIndex, + numberOfSlices, + }); + } catch (error) { + console.warn(error); + } }, [viewportId, viewportData]); useEffect(() => { diff --git a/extensions/cornerstone/src/utils/ActiveViewportBehavior.tsx b/extensions/cornerstone/src/utils/ActiveViewportBehavior.tsx index 3f1d4699e32..2a6ed10a326 100644 --- a/extensions/cornerstone/src/utils/ActiveViewportBehavior.tsx +++ b/extensions/cornerstone/src/utils/ActiveViewportBehavior.tsx @@ -39,6 +39,7 @@ const ActiveViewportBehavior = memo( } const modalities = displaySets.map(displaySet => displaySet?.Modality); + const isDynamicVolume = displaySets.some(displaySet => displaySet?.isDynamicVolume); const { modalities: sourceModalities } = customizationService.getModeCustomization( 'autoCineModalities', @@ -50,7 +51,7 @@ const ActiveViewportBehavior = memo( const requiresCine = modalities.some(modality => sourceModalities.includes(modality)); - if (requiresCine && !cineService.getState().isCineEnabled) { + if ((requiresCine || isDynamicVolume) && !cineService.getState().isCineEnabled) { cineService.setIsCineEnabled(true); } }, [ diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index 29dbdd32474..206df45a51a 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -392,7 +392,8 @@ function createDicomWebApi(dicomWebConfig, servicesManager) { if (Array.isArray(value) && typeof value[0] === 'object') { // Fix recursive values - value.forEach(child => addRetrieveBulkDataNaturalized(child, instance)); + const validValues = value.filter(Boolean); + validValues.forEach(child => addRetrieveBulkDataNaturalized(child, instance)); continue; } diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json index 074f1546eb2..b53942ddcc4 100644 --- a/extensions/measurement-tracking/package.json +++ b/extensions/measurement-tracking/package.json @@ -32,8 +32,8 @@ "start": "yarn run dev" }, "peerDependencies": { - "@cornerstonejs/core": "^2.2.20", - "@cornerstonejs/tools": "^2.2.20", + "@cornerstonejs/core": "^2.7.3", + "@cornerstonejs/tools": "^2.7.3", "@ohif/core": "3.10.0-beta.14", "@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.14", "@ohif/extension-default": "3.10.0-beta.14", diff --git a/platform/app/package.json b/platform/app/package.json index d79f51b57f8..89b01c32edd 100644 --- a/platform/app/package.json +++ b/platform/app/package.json @@ -53,7 +53,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^2.2.20", + "@cornerstonejs/dicom-image-loader": "^2.7.3", "@emotion/serialize": "^1.1.3", "@ohif/core": "3.10.0-beta.14", "@ohif/extension-cornerstone": "3.10.0-beta.14", diff --git a/platform/core/package.json b/platform/core/package.json index 26880a1c56a..b2a30ab76a8 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -37,7 +37,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.4", "@cornerstonejs/codec-openjph": "^2.4.5", - "@cornerstonejs/dicom-image-loader": "^2.2.20", + "@cornerstonejs/dicom-image-loader": "^2.7.3", "@ohif/ui": "3.10.0-beta.14", "cornerstone-math": "0.1.9", "dicom-parser": "^1.8.21" diff --git a/playwright.config.ts b/playwright.config.ts index 4245ce76235..4f1bca9409d 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,19 +2,21 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', - fullyParallel: true, + fullyParallel: !!process.env.CI, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, snapshotPathTemplate: './tests/screenshots{/projectName}/{testFilePath}/{arg}{ext}', outputDir: './tests/test-results', reporter: [[process.env.CI ? 'blob' : 'html', { outputFolder: './tests/playwright-report' }]], - timeout: 720 * 1000, + globalTimeout: 800_000, + timeout: 800_000, use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry', video: 'on', testIdAttribute: 'data-cy', + actionTimeout: 10_000, }, projects: [ @@ -39,6 +41,6 @@ export default defineConfig({ command: 'yarn test:e2e:serve', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, - timeout: 240 * 1000, + timeout: 360_000, }, }); diff --git a/tests/3DFourUp.spec.ts b/tests/3DFourUp.spec.ts index e196856d9ca..f54e8c2fc6b 100644 --- a/tests/3DFourUp.spec.ts +++ b/tests/3DFourUp.spec.ts @@ -1,5 +1,11 @@ import { test } from '@playwright/test'; -import { visitStudy, checkForScreenshot, screenShotPaths, reduce3DViewportSize } from './utils'; +import { + visitStudy, + checkForScreenshot, + screenShotPaths, + reduce3DViewportSize, + attemptAction, +} from './utils'; test.beforeEach(async ({ page }) => { const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785'; @@ -15,7 +21,9 @@ test.describe('3D four up Test', async () => { .filter({ hasText: /^3D four up$/ }) .first() .click(); - await reduce3DViewportSize(page); + + await attemptAction(() => reduce3DViewportSize(page), 10, 100); + await checkForScreenshot( page, page, diff --git a/tests/3DMain.spec.ts b/tests/3DMain.spec.ts index f0ce18f34aa..84164e93a44 100644 --- a/tests/3DMain.spec.ts +++ b/tests/3DMain.spec.ts @@ -1,5 +1,11 @@ import { test } from '@playwright/test'; -import { visitStudy, checkForScreenshot, screenShotPaths, reduce3DViewportSize } from './utils'; +import { + visitStudy, + checkForScreenshot, + screenShotPaths, + reduce3DViewportSize, + attemptAction, +} from './utils'; test.beforeEach(async ({ page }) => { const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785'; @@ -15,7 +21,7 @@ test.describe('3D main Test', async () => { .filter({ hasText: /^3D main$/ }) .first() .click(); - await reduce3DViewportSize(page); + await attemptAction(() => reduce3DViewportSize(page), 10, 100); await checkForScreenshot( page, page, diff --git a/tests/3DOnly.spec.ts b/tests/3DOnly.spec.ts index 28489d02477..0f2905834ab 100644 --- a/tests/3DOnly.spec.ts +++ b/tests/3DOnly.spec.ts @@ -1,5 +1,11 @@ import { test } from '@playwright/test'; -import { visitStudy, checkForScreenshot, screenShotPaths, reduce3DViewportSize } from './utils'; +import { + visitStudy, + checkForScreenshot, + screenShotPaths, + reduce3DViewportSize, + attemptAction, +} from './utils'; test.beforeEach(async ({ page }) => { const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785'; @@ -15,7 +21,7 @@ test.describe('3D only Test', async () => { .filter({ hasText: /^3D only$/ }) .first() .click(); - await reduce3DViewportSize(page); + await attemptAction(() => reduce3DViewportSize(page), 10, 100); await checkForScreenshot( page, page, diff --git a/tests/3DPrimary.spec.ts b/tests/3DPrimary.spec.ts index f384815884a..4b9bb335b67 100644 --- a/tests/3DPrimary.spec.ts +++ b/tests/3DPrimary.spec.ts @@ -1,5 +1,11 @@ import { test } from '@playwright/test'; -import { visitStudy, checkForScreenshot, screenShotPaths, reduce3DViewportSize } from './utils'; +import { + visitStudy, + checkForScreenshot, + screenShotPaths, + reduce3DViewportSize, + attemptAction, +} from './utils'; test.beforeEach(async ({ page }) => { const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785'; @@ -16,7 +22,7 @@ test.describe('3D primary Test', async () => { .first() .click(); - await reduce3DViewportSize(page); + await attemptAction(() => reduce3DViewportSize(page), 10, 100); await checkForScreenshot( page, page, diff --git a/tests/Crosshairs.spec.ts b/tests/Crosshairs.spec.ts index c711177bc98..6a98a91bb18 100644 --- a/tests/Crosshairs.spec.ts +++ b/tests/Crosshairs.spec.ts @@ -1,6 +1,11 @@ import { Page, test } from '@playwright/test'; -import { visitStudy, checkForScreenshot, screenShotPaths, initilizeMousePositionTracker, getMousePosition } from './utils/index.js'; - +import { + visitStudy, + checkForScreenshot, + screenShotPaths, + initilizeMousePositionTracker, + getMousePosition, +} from './utils/index.js'; const rotateCrosshairs = async (page: Page, id: string, lineNumber: number) => { const locator = await page.locator(id).locator('line').nth(lineNumber); @@ -12,10 +17,10 @@ const rotateCrosshairs = async (page: Page, id: string, lineNumber: number) => { const position = await getMousePosition(page); await page.mouse.move(position.x, position.y + 100); await page.mouse.up(); -} +}; const increaseSlabThickness = async (page: Page, id: string, lineNumber: number, axis: string) => { - const locator = await page.locator(id).locator('line').nth(lineNumber) + const locator = await page.locator(id).locator('line').nth(lineNumber); await locator.click({ force: true }); await locator.hover({ force: true }); const circleLocator = await page.locator(id).locator('rect').first(); @@ -31,7 +36,7 @@ const increaseSlabThickness = async (page: Page, id: string, lineNumber: number, break; } await page.mouse.up(); -} +}; test.beforeEach(async ({ page }) => { const studyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785'; @@ -73,7 +78,9 @@ test.describe('Crosshairs Test', async () => { await checkForScreenshot(page, page, screenShotPaths.crosshairs.crosshairsSlabThickness); }); - test('should reset the crosshairs to the initial position when reset is clicked', async ({ page }) => { + test('should reset the crosshairs to the initial position when reset is clicked', async ({ + page, + }) => { await page.getByTestId('Layout').click(); await page.locator('div').filter({ hasText: /^MPR$/ }).first().click(); await page.getByTestId('Crosshairs').click(); @@ -100,5 +107,4 @@ test.describe('Crosshairs Test', async () => { await checkForScreenshot(page, page, screenShotPaths.crosshairs.crosshairsNewDisplayset); }); - }); diff --git a/tests/utils/attemptAction.ts b/tests/utils/attemptAction.ts new file mode 100644 index 00000000000..d9ea5c05bdc --- /dev/null +++ b/tests/utils/attemptAction.ts @@ -0,0 +1,21 @@ +/** + * + * @param action The action function to attempt + * @param attempts The number of attempts to try the action + * @param delay delay between attempts + * @returns True if the action is successful, otherwise throws an error + */ + +export const attemptAction = async (action: () => Promise, attempts = 10, delay = 100) => { + for (let i = 1; i < attempts; i++) { + try { + await action(); + return true; + } catch (error) { + if (i === attempts) { + throw new Error('Action failed.'); + } + await new Promise(resolve => setTimeout(resolve, delay)); + } + } +}; diff --git a/tests/utils/index.ts b/tests/utils/index.ts index ff5acf51047..ddfe4bcac35 100644 --- a/tests/utils/index.ts +++ b/tests/utils/index.ts @@ -8,6 +8,7 @@ import { getSUV } from './getSUV'; import { getTMTVModalityUnit } from './getTMTVModalityUnit'; import { clearAllAnnotations } from './clearAllAnnotations'; import { scrollVolumeViewport } from './scrollVolumeViewport'; +import { attemptAction } from './attemptAction'; export { visitStudy, @@ -21,4 +22,5 @@ export { getTMTVModalityUnit, clearAllAnnotations, scrollVolumeViewport, + attemptAction, }; diff --git a/yarn.lock b/yarn.lock index d3cff34acca..33ddb09b87b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2580,10 +2580,10 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cornerstonejs/adapters@^2.2.20": - version "2.2.20" - resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.2.20.tgz#82fd0775a6ac4e138bda4f3dad412593133b16a9" - integrity sha512-o9c3eTJa7D0wgdkAdg33/BET5+BgM5qjt6Xmd/QSXsghPyJcmvnxIzKS+upt2///u+f8q4aaiL3dgsVzNc6L9g== +"@cornerstonejs/adapters@^2.7.3": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-2.7.3.tgz#96d2ab568bfc987a6c7bb44f849ad2f94be9014a" + integrity sha512-hEJusn3DynPrgyaAidOoYboFJ/t5dd1TDaIiIr2FQQbfkIhun6EE5UkLOcdP8dqiKujpsHm9LcdCFh/b3/7K8g== dependencies: "@babel/runtime-corejs2" "^7.17.8" buffer "^6.0.3" @@ -2616,19 +2616,19 @@ resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0" integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg== -"@cornerstonejs/core@^2.2.20": - version "2.2.20" - resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.2.20.tgz#a39176c0f832ce2d91f414a2f06010c54c3b99b0" - integrity sha512-1gfARz1tqoAnic1rTGILF9MbwC0YICCNWS+k5vCkf+1ZX3XYepMt3bV3GPPAcTDJ2t6t20Ii1xdKueDn3BSIXw== +"@cornerstonejs/core@^2.7.3": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-2.7.3.tgz#0e580e3052b84530f0da4fb4b94e23f580415f47" + integrity sha512-q3Sc1eiRlC6kAzgqkJ/cGFGpJe62YwinS2nbZceqDKoAno3C4W81h5jrolENKH4HEOS6qvuz3sPP/sh6ryg/zA== dependencies: "@kitware/vtk.js" "32.1.1" comlink "^4.4.1" gl-matrix "^3.4.3" -"@cornerstonejs/dicom-image-loader@^2.2.20": - version "2.2.20" - resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.2.20.tgz#3d1d448ac2c7010ae29359209f5a6b1790777930" - integrity sha512-pYyYzPbbwOU9tH84WAhBK/+XdJ/Gc3hYfsrsuGk3ufVsdjRywvjGviSOYN4My0w++ezanaSFdPNcghtqdL8etQ== +"@cornerstonejs/dicom-image-loader@^2.7.3": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-2.7.3.tgz#04466643bd3802933378460d60a731d9cc3a34e3" + integrity sha512-P5wZovDo5onMIMILwNRrlv75GYnDFIFVQfnvepJKTfNTeQbHhTaNRfx8MHOHNYCa8JBtrR6JSeD2FkMoZE/vsg== dependencies: "@cornerstonejs/codec-charls" "^1.2.3" "@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2" @@ -2639,10 +2639,10 @@ pako "^2.0.4" uuid "^9.0.0" -"@cornerstonejs/tools@^2.2.20": - version "2.2.20" - resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.2.20.tgz#d2fcbc1b6f585f4094bb5d03d3ae87862e1df092" - integrity sha512-rS7KNfLjwtZ0MzP9eT5EDKBDoEh/Nt5htJxj9b/BvL2W6X/vyF361tWYf/L2dIo0NdR6LBYds3Gdm31y+b7drA== +"@cornerstonejs/tools@^2.7.3": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-2.7.3.tgz#9e2cbbb4d5d6e3b6b6f42f631d51a433a337b201" + integrity sha512-03WRQ5AX5RQmaqc7uSkgv9npruCBbRExjp4FOlOHU7Tq16q7CJlqIPF1wWEqecoxn7T1IXL3bokXOTPvh8zSFQ== dependencies: "@types/offscreencanvas" "2019.7.3" comlink "^4.4.1"