From 6ea40c9895a74449c81977807ac9a28b955d90e9 Mon Sep 17 00:00:00 2001 From: asrient <44570278+asrient@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:42:22 +0530 Subject: [PATCH] Desktop: New Embedded Pdf Viewer (#6681) --- .eslintignore | 22 + .gitignore | 21 + .../NoteBody/CodeMirror/CodeMirror.tsx | 2 + .../gui/NoteEditor/utils/useMarkupToHtml.ts | 1 + .../app-desktop/gui/note-viewer/index.html | 11 + packages/app-desktop/package.json | 1 + packages/app-desktop/tools/compileScripts.js | 77 +- .../tools/copyApplicationAssets.js | 8 + packages/pdf-viewer/.gitignore | 1 + packages/pdf-viewer/Page.tsx | 100 ++ packages/pdf-viewer/README.md | 17 + packages/pdf-viewer/VerticalPages.tsx | 59 + packages/pdf-viewer/config/cssTransform.js | 14 + packages/pdf-viewer/config/welcome.pdf | Bin 0 -> 6736 bytes packages/pdf-viewer/hooks/useIsFocused.ts | 26 + packages/pdf-viewer/hooks/useIsVisible.ts | 37 + packages/pdf-viewer/index.html | 19 + packages/pdf-viewer/jest.config.js | 172 +++ packages/pdf-viewer/miniViewer.tsx | 60 + packages/pdf-viewer/package-lock.json | 52 + packages/pdf-viewer/package.json | 42 + packages/pdf-viewer/pages.css | 76 ++ packages/pdf-viewer/pdfSource.test.ts | 56 + packages/pdf-viewer/pdfSource.ts | 74 ++ packages/pdf-viewer/tsconfig.json | 17 + packages/pdf-viewer/viewer.css | 98 ++ packages/pdf-viewer/webpack.config.js | 29 + packages/renderer/MdToHtml.ts | 3 +- packages/renderer/MdToHtml/renderMedia.ts | 12 + packages/renderer/noteStyle.ts | 7 +- packages/tools/setupNewRelease.ts | 1 + yarn.lock | 1055 ++++++++++++++++- 32 files changed, 2133 insertions(+), 37 deletions(-) create mode 100644 packages/pdf-viewer/.gitignore create mode 100644 packages/pdf-viewer/Page.tsx create mode 100644 packages/pdf-viewer/README.md create mode 100644 packages/pdf-viewer/VerticalPages.tsx create mode 100644 packages/pdf-viewer/config/cssTransform.js create mode 100644 packages/pdf-viewer/config/welcome.pdf create mode 100644 packages/pdf-viewer/hooks/useIsFocused.ts create mode 100644 packages/pdf-viewer/hooks/useIsVisible.ts create mode 100644 packages/pdf-viewer/index.html create mode 100644 packages/pdf-viewer/jest.config.js create mode 100644 packages/pdf-viewer/miniViewer.tsx create mode 100644 packages/pdf-viewer/package-lock.json create mode 100644 packages/pdf-viewer/package.json create mode 100644 packages/pdf-viewer/pages.css create mode 100644 packages/pdf-viewer/pdfSource.test.ts create mode 100644 packages/pdf-viewer/pdfSource.ts create mode 100644 packages/pdf-viewer/tsconfig.json create mode 100644 packages/pdf-viewer/viewer.css create mode 100644 packages/pdf-viewer/webpack.config.js diff --git a/.eslintignore b/.eslintignore index 95566a0578..6f5e677389 100644 --- a/.eslintignore +++ b/.eslintignore @@ -69,6 +69,7 @@ packages/tools/node_modules packages/tools/PortableAppsLauncher packages/turndown-plugin-gfm/ packages/turndown/ +packages/pdf-viewer/dist plugin_types/ readme/ @@ -1909,6 +1910,27 @@ packages/lib/uuid.js.map packages/lib/versionInfo.d.ts packages/lib/versionInfo.js packages/lib/versionInfo.js.map +packages/pdf-viewer/Page.d.ts +packages/pdf-viewer/Page.js +packages/pdf-viewer/Page.js.map +packages/pdf-viewer/VerticalPages.d.ts +packages/pdf-viewer/VerticalPages.js +packages/pdf-viewer/VerticalPages.js.map +packages/pdf-viewer/hooks/useIsFocused.d.ts +packages/pdf-viewer/hooks/useIsFocused.js +packages/pdf-viewer/hooks/useIsFocused.js.map +packages/pdf-viewer/hooks/useIsVisible.d.ts +packages/pdf-viewer/hooks/useIsVisible.js +packages/pdf-viewer/hooks/useIsVisible.js.map +packages/pdf-viewer/miniViewer.d.ts +packages/pdf-viewer/miniViewer.js +packages/pdf-viewer/miniViewer.js.map +packages/pdf-viewer/pdfSource.d.ts +packages/pdf-viewer/pdfSource.js +packages/pdf-viewer/pdfSource.js.map +packages/pdf-viewer/pdfSource.test.d.ts +packages/pdf-viewer/pdfSource.test.js +packages/pdf-viewer/pdfSource.test.js.map packages/plugin-repo-cli/commands/updateRelease.d.ts packages/plugin-repo-cli/commands/updateRelease.js packages/plugin-repo-cli/commands/updateRelease.js.map diff --git a/.gitignore b/.gitignore index e7e6205c2a..88a24a4f4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1899,6 +1899,27 @@ packages/lib/uuid.js.map packages/lib/versionInfo.d.ts packages/lib/versionInfo.js packages/lib/versionInfo.js.map +packages/pdf-viewer/Page.d.ts +packages/pdf-viewer/Page.js +packages/pdf-viewer/Page.js.map +packages/pdf-viewer/VerticalPages.d.ts +packages/pdf-viewer/VerticalPages.js +packages/pdf-viewer/VerticalPages.js.map +packages/pdf-viewer/hooks/useIsFocused.d.ts +packages/pdf-viewer/hooks/useIsFocused.js +packages/pdf-viewer/hooks/useIsFocused.js.map +packages/pdf-viewer/hooks/useIsVisible.d.ts +packages/pdf-viewer/hooks/useIsVisible.js +packages/pdf-viewer/hooks/useIsVisible.js.map +packages/pdf-viewer/miniViewer.d.ts +packages/pdf-viewer/miniViewer.js +packages/pdf-viewer/miniViewer.js.map +packages/pdf-viewer/pdfSource.d.ts +packages/pdf-viewer/pdfSource.js +packages/pdf-viewer/pdfSource.js.map +packages/pdf-viewer/pdfSource.test.d.ts +packages/pdf-viewer/pdfSource.test.js +packages/pdf-viewer/pdfSource.test.js.map packages/plugin-repo-cli/commands/updateRelease.d.ts packages/plugin-repo-cli/commands/updateRelease.js packages/plugin-repo-cli/commands/updateRelease.js.map diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx index 1421f8ec80..879d34de99 100644 --- a/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx +++ b/packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx @@ -614,6 +614,8 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) { resourceInfos: props.resourceInfos, contentMaxWidth: props.contentMaxWidth, mapsToLine: true, + // Always using useCustomPdfViewer for now, we can add a new setting for it in future if we need to. + useCustomPdfViewer: true, })); if (cancelled) return; diff --git a/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts b/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts index 8703442def..f8b529864c 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts @@ -20,6 +20,7 @@ export interface MarkupToHtmlOptions { plugins?: Record; bodyOnly?: boolean; mapsToLine?: boolean; + useCustomPdfViewer?: boolean; } export default function useMarkupToHtml(deps: HookDependencies) { diff --git a/packages/app-desktop/gui/note-viewer/index.html b/packages/app-desktop/gui/note-viewer/index.html index 5697c393a7..6219cb72e5 100644 --- a/packages/app-desktop/gui/note-viewer/index.html +++ b/packages/app-desktop/gui/note-viewer/index.html @@ -653,6 +653,17 @@ e.preventDefault(); })); + document.addEventListener('click', webviewLib.logEnabledEventHandler(e => { + document.querySelectorAll('.media-pdf').forEach(element => { + if(!!element.contentWindow){ + element.contentWindow.postMessage({ + type: 'blur' + }, '*'); + } + } + ); + })); + let lastClientWidth_ = NaN, lastClientHeight_ = NaN, lastScrollTop_ = NaN; window.addEventListener('resize', webviewLib.logEnabledEventHandler(() => { diff --git a/packages/app-desktop/package.json b/packages/app-desktop/package.json index d14d2b606b..515b0b6cac 100644 --- a/packages/app-desktop/package.json +++ b/packages/app-desktop/package.json @@ -138,6 +138,7 @@ "@fortawesome/fontawesome-free": "^5.13.0", "@joeattardi/emoji-button": "^4.6.0", "@joplin/lib": "~2.9", + "@joplin/pdf-viewer": "~2.9", "@joplin/renderer": "~2.9", "async-mutex": "^0.1.3", "codemirror": "^5.56.0", diff --git a/packages/app-desktop/tools/compileScripts.js b/packages/app-desktop/tools/compileScripts.js index 080b129e82..24d0f3175a 100644 --- a/packages/app-desktop/tools/compileScripts.js +++ b/packages/app-desktop/tools/compileScripts.js @@ -13,46 +13,65 @@ function fileIsNewerThan(path1, path2) { return stat1.mtime > stat2.mtime; } -function convertJsx(path) { +function convertJsx(paths) { chdir(`${__dirname}/..`); - fs.readdirSync(path).forEach((filename) => { - const jsxPath = `${path}/${filename}`; - const p = jsxPath.split('.'); - if (p.length <= 1) return; - const ext = p[p.length - 1]; - if (ext !== 'jsx') return; - p.pop(); + paths.forEach(path => { + fs.readdirSync(path).forEach((filename) => { + const jsxPath = `${path}/${filename}`; + const p = jsxPath.split('.'); + if (p.length <= 1) return; + const ext = p[p.length - 1]; + if (ext !== 'jsx') return; + p.pop(); - const basePath = p.join('.'); + const basePath = p.join('.'); - const jsPath = `${basePath}.min.js`; + const jsPath = `${basePath}.min.js`; - if (fileIsNewerThan(jsxPath, jsPath)) { - console.info(`Compiling ${jsxPath}...`); + if (fileIsNewerThan(jsxPath, jsPath)) { + console.info(`Compiling ${jsxPath}...`); - // { shell: true } is needed to get it working on Windows: - // https://discourse.joplinapp.org/t/attempting-to-build-on-windows/22559/12 - const result = spawnSync('yarn', ['run', 'babel', '--presets', 'react', '--out-file', jsPath, jsxPath], { shell: true }); - if (result.status !== 0) { - const msg = []; - if (result.stdout) msg.push(result.stdout.toString()); - if (result.stderr) msg.push(result.stderr.toString()); - console.error(msg.join('\n')); - if (result.error) console.error(result.error); - process.exit(result.status); + // { shell: true } is needed to get it working on Windows: + // https://discourse.joplinapp.org/t/attempting-to-build-on-windows/22559/12 + const result = spawnSync('yarn', ['run', 'babel', '--presets', 'react', '--out-file', jsPath, jsxPath], { shell: true }); + if (result.status !== 0) { + const msg = []; + if (result.stdout) msg.push(result.stdout.toString()); + if (result.stderr) msg.push(result.stderr.toString()); + console.error(msg.join('\n')); + if (result.error) console.error(result.error); + process.exit(result.status); + } } - } + }); }); } -module.exports = function() { - convertJsx(`${__dirname}/../gui`); - convertJsx(`${__dirname}/../gui/MainScreen`); - convertJsx(`${__dirname}/../gui/NoteList`); - convertJsx(`${__dirname}/../plugins`); +function build(path) { + chdir(path); + + const result = spawnSync('yarn', ['run', 'build'], { shell: true }); + if (result.status !== 0) { + const msg = []; + if (result.stdout) msg.push(result.stdout.toString()); + if (result.stderr) msg.push(result.stderr.toString()); + console.error(msg.join('\n')); + if (result.error) console.error(result.error); + process.exit(result.status); + } +} + +module.exports = function() { + convertJsx([ + `${__dirname}/../gui`, + `${__dirname}/../gui/MainScreen`, + `${__dirname}/../gui/NoteList`, + `${__dirname}/../plugins`, + ]); + + build(`${__dirname}/../../pdf-viewer`); - // TODO: should get from node_modules @joplin/lib const libContent = [ fs.readFileSync(`${basePath}/packages/lib/string-utils-common.js`, 'utf8'), fs.readFileSync(`${basePath}/packages/lib/markJsUtils.js`, 'utf8'), diff --git a/packages/app-desktop/tools/copyApplicationAssets.js b/packages/app-desktop/tools/copyApplicationAssets.js index 0a6bfc76b7..1cd813399c 100644 --- a/packages/app-desktop/tools/copyApplicationAssets.js +++ b/packages/app-desktop/tools/copyApplicationAssets.js @@ -72,6 +72,10 @@ async function main() { src: langSourceDir, dest: `${buildLibDir}/tinymce/langs`, }, + { + src: resolve(__dirname, '../../pdf-viewer/dist'), + dest: `${buildLibDir}/@joplin/pdf-viewer`, + }, ]; const files = [ @@ -87,6 +91,10 @@ async function main() { src: resolve(__dirname, '../../lib/services/plugins/sandboxProxy.js'), dest: `${buildLibDir}/@joplin/lib/services/plugins/sandboxProxy.js`, }, + { + src: resolve(__dirname, '../../pdf-viewer/index.html'), + dest: `${buildLibDir}/@joplin/pdf-viewer/index.html`, + }, ]; // First we delete all the destination directories, then we copy the files. diff --git a/packages/pdf-viewer/.gitignore b/packages/pdf-viewer/.gitignore new file mode 100644 index 0000000000..4be6e160a1 --- /dev/null +++ b/packages/pdf-viewer/.gitignore @@ -0,0 +1 @@ +dist/* \ No newline at end of file diff --git a/packages/pdf-viewer/Page.tsx b/packages/pdf-viewer/Page.tsx new file mode 100644 index 0000000000..9cf0bd08b2 --- /dev/null +++ b/packages/pdf-viewer/Page.tsx @@ -0,0 +1,100 @@ +import React, { useEffect, useRef, useState } from 'react'; +import useIsVisible from './hooks/useIsVisible'; +import { PdfData, ScaledSize } from './pdfSource'; +import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; + +require('./pages.css'); + +export interface PageProps { + pdf: PdfData; + pageNo: number; + focusOnLoad: boolean; + isAnchored: boolean; + scaledSize: ScaledSize; + isDarkTheme: boolean; + container: React.MutableRefObject; +} + + +export default function Page(props: PageProps) { + const [error, setError] = useState(null); + const [page, setPage] = useState(null); + const [scale, setScale] = useState(null); + const [timestamp, setTimestamp] = useState(null); + const canvasRef = useRef(null); + const wrapperRef = useRef(null); + const isVisible = useIsVisible(canvasRef, props.container); + + useEffect(() => { + if (!isVisible || !page || !props.scaledSize || (scale && props.scaledSize.scale === scale)) return; + try { + const viewport = page.getViewport({ scale: props.scaledSize.scale || 1.0 }); + const canvas = canvasRef.current; + canvas.width = viewport.width; + canvas.height = viewport.height; + const ctx = canvas.getContext('2d'); + const pageTimestamp = new Date().getTime(); + setTimestamp(pageTimestamp); + page.render({ + canvasContext: ctx, + viewport, + // Used so that the page rendering is throttled to some extent. + // https://stackoverflow.com/questions/18069448/halting-pdf-js-page-rendering + continueCallback: function(cont: any) { + if (timestamp !== pageTimestamp) { + return; + } + cont(); + }, + }); + setScale(props.scaledSize.scale); + + } catch (error) { + error.message = `Error rendering page no. ${props.pageNo}: ${error.message}`; + setError(error); + throw error; + } + }, [page, props.scaledSize, isVisible]); + + useAsyncEffect(async (event: AsyncEffectEvent) => { + if (page || !isVisible || !props.pdf) return; + try { + const _page = await props.pdf.getPage(props.pageNo); + if (event.cancelled) return; + setPage(_page); + } catch (error) { + console.error('Page load error', props.pageNo, error); + setError(error); + } + }, [page, props.scaledSize, isVisible]); + + useEffect(() => { + if (props.focusOnLoad) { + props.container.current.scrollTop = wrapperRef.current.offsetTop; + // console.warn('setting focus on page', props.pageNo, wrapperRef.current.offsetTop); + } + }, [props.focusOnLoad]); + + let style: any = {}; + if (props.scaledSize) { + style = { + ...style, + width: props.scaledSize.width, + height: props.scaledSize.height, + }; + } + + return ( +
+ +
+ {error ? 'ERROR' : 'Loading..'} +
+ Page {props.pageNo} +
+
+ {props.isAnchored ? '📌' : ''} Page {props.pageNo} +
+
+ ); +} diff --git a/packages/pdf-viewer/README.md b/packages/pdf-viewer/README.md new file mode 100644 index 0000000000..441d0eb49e --- /dev/null +++ b/packages/pdf-viewer/README.md @@ -0,0 +1,17 @@ +# PDF VIEWER + +//Todo + +## Usage + +```javascript +import viewer from '@joplin/pdf-viewer'; +``` + +## Notes + +//Todo + +## License + +MIT diff --git a/packages/pdf-viewer/VerticalPages.tsx b/packages/pdf-viewer/VerticalPages.tsx new file mode 100644 index 0000000000..53c211a475 --- /dev/null +++ b/packages/pdf-viewer/VerticalPages.tsx @@ -0,0 +1,59 @@ +import React, { useLayoutEffect, useRef, useState } from 'react'; +import { PdfData, ScaledSize } from './pdfSource'; +import Page from './Page'; + +require('./pages.css'); + + +export interface VerticalPagesProps { + pdf: PdfData; + isDarkTheme: boolean; + anchorPage: number; + container: React.MutableRefObject; +} + + +export default function VerticalPages(props: VerticalPagesProps) { + const [scaledSize, setScaledSize] = useState(null); + const innerContainerEl = useRef(null); + useLayoutEffect(() => { + let resizeTimer: number = null; + let cancelled = false; + const updateSize = async () => { + if (props.pdf) { + const innerWidth = innerContainerEl.current.clientWidth; + const scaledSize = await props.pdf.getScaledSize(null, innerWidth - 10); + if (cancelled) return; + setScaledSize(scaledSize); + } + }; + const onResize = () => { + if (resizeTimer) { + clearTimeout(resizeTimer); + resizeTimer = null; + } + resizeTimer = window.setTimeout(updateSize, 200); + }; + window.addEventListener('resize', onResize); + updateSize() + .catch(console.error); + return () => { + cancelled = true; + window.removeEventListener('resize', onResize); + if (resizeTimer) { + clearTimeout(resizeTimer); + resizeTimer = null; + } + }; + }, [props.pdf]); + + return (
+ {Array.from(Array(props.pdf.pageCount).keys()).map((i: number) => { + // setting focusOnLoad only after scaledSize is set so that the container height is set correctly + return ; + } + )} +
); +} diff --git a/packages/pdf-viewer/config/cssTransform.js b/packages/pdf-viewer/config/cssTransform.js new file mode 100644 index 0000000000..9459da3e73 --- /dev/null +++ b/packages/pdf-viewer/config/cssTransform.js @@ -0,0 +1,14 @@ +'use strict'; + +// This is a custom Jest transformer turning style imports into empty objects. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process() { + return 'module.exports = {};'; + }, + getCacheKey() { + // The output is always the same. + return 'cssTransform'; + }, +}; diff --git a/packages/pdf-viewer/config/welcome.pdf b/packages/pdf-viewer/config/welcome.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cf34d95226cf4f2dbc2fd55a1ad00a9438b49e70 GIT binary patch literal 6736 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 Pgn+|XPMkP>PWOKRT0nVW literal 0 HcmV?d00001 diff --git a/packages/pdf-viewer/hooks/useIsFocused.ts b/packages/pdf-viewer/hooks/useIsFocused.ts new file mode 100644 index 0000000000..9b9887b821 --- /dev/null +++ b/packages/pdf-viewer/hooks/useIsFocused.ts @@ -0,0 +1,26 @@ +import { useEffect, useState } from 'react'; + +const useIsFocused = () => { + const [isFocused, setIsFocused] = useState(false); + + useEffect(() => { + const onMessage = (event: MessageEvent) => { + if (event.data.type === 'blur') { + setIsFocused(false); + } + }; + const onClick = (_event: MouseEvent) => { + setIsFocused(true); + }; + window.addEventListener('message', onMessage); + document.addEventListener('click', onClick); + return () => { + window.removeEventListener('message', onMessage); + document.removeEventListener('click', onClick); + }; + }, []); + + return isFocused; +}; + +export default useIsFocused; diff --git a/packages/pdf-viewer/hooks/useIsVisible.ts b/packages/pdf-viewer/hooks/useIsVisible.ts new file mode 100644 index 0000000000..d9259e4133 --- /dev/null +++ b/packages/pdf-viewer/hooks/useIsVisible.ts @@ -0,0 +1,37 @@ +import React, { useEffect, useState } from 'react'; + + +const useIsVisible = (elementRef: React.MutableRefObject, rootRef: React.MutableRefObject) => { + const [isVisible, setIsVisible] = useState(false); + useEffect(() => { + let observer: IntersectionObserver = null; + if (elementRef.current) { + observer = new IntersectionObserver((entries, _observer) => { + let visible = false; + entries.forEach((entry) => { + if (entry.isIntersecting) { + visible = true; + setIsVisible(true); + } + }); + if (!visible) { + setIsVisible(false); + } + }, { + root: rootRef.current, + rootMargin: '0px 0px 0px 0px', + threshold: 0, + }); + observer.observe(elementRef.current); + } + return () => { + if (observer) { + observer.disconnect(); + } + }; + }, []); + + return isVisible; +}; + +export default useIsVisible; diff --git a/packages/pdf-viewer/index.html b/packages/pdf-viewer/index.html new file mode 100644 index 0000000000..7b730058a5 --- /dev/null +++ b/packages/pdf-viewer/index.html @@ -0,0 +1,19 @@ +
+
+ Loading PDF... +
+
+ + \ No newline at end of file diff --git a/packages/pdf-viewer/jest.config.js b/packages/pdf-viewer/jest.config.js new file mode 100644 index 0000000000..c37813ce6f --- /dev/null +++ b/packages/pdf-viewer/jest.config.js @@ -0,0 +1,172 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls and instances between every test + // clearMocks: false, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + // coverageDirectory: undefined, + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: 'v8', + + preset: 'ts-jest', + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + roots: [ + '', + ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: 'jsdom', + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + testMatch: [ + '**/*.test.ts', + ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/packages/pdf-viewer/miniViewer.tsx b/packages/pdf-viewer/miniViewer.tsx new file mode 100644 index 0000000000..f5e31c33d2 --- /dev/null +++ b/packages/pdf-viewer/miniViewer.tsx @@ -0,0 +1,60 @@ +import React, { useRef, useState } from 'react'; +import shim from '@joplin/lib/shim'; +shim.setReact(React); +import { render } from 'react-dom'; +import * as pdfjsLib from 'pdfjs-dist'; +import useIsFocused from './hooks/useIsFocused'; +import { PdfData } from './pdfSource'; +import VerticalPages from './VerticalPages'; +import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; + +require('./viewer.css'); + +// Setting worker path to worker bundle. +pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.js'; + + +function MiniViewerApp(props: { pdfPath: string; isDarkTheme: boolean; anchorPage: number }) { + const [pdf, setPdf] = useState(null); + const isFocused = useIsFocused(); + const containerEl = useRef(null); + + useAsyncEffect(async (event: AsyncEffectEvent) => { + const pdfData = new PdfData(); + await pdfData.loadDoc(props.pdfPath); + if (event.cancelled) return; + setPdf(pdfData); + }, []); + + if (!pdf) { + return ( +
+
Loading pdf..
+
); + } + + return ( +
+
+ +
+
+
+ {pdf.pageCount} pages +
+
{isFocused ? '' : 'Click to enable scroll'}
+
+
+ ); +} + +const url = window.frameElement.getAttribute('url'); +const appearance = window.frameElement.getAttribute('appearance'); +const anchorPage = window.frameElement.getAttribute('anchorPage'); + +document.documentElement.setAttribute('data-theme', appearance); + +render( + , + document.getElementById('pdf-root') +); diff --git a/packages/pdf-viewer/package-lock.json b/packages/pdf-viewer/package-lock.json new file mode 100644 index 0000000000..b740b0441d --- /dev/null +++ b/packages/pdf-viewer/package-lock.json @@ -0,0 +1,52 @@ +{ + "name": "@joplin/pdf-viewer", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "@types/react": { + "version": "18.0.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.14.tgz", + "integrity": "sha512-x4gGuASSiWmo0xjDLpm5mPb52syZHJx02VKbqUKdLmKtAwIh63XClGsiTI1K6DO5q7ox4xAsQrU+Gl3+gGXF9Q==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.0.tgz", + "integrity": "sha512-OL2lk7LYGjxn4b0efW3Pvf2KBVP0y1v3wip1Bp7nA79NkOpElH98q3WdCEdDj93b2b0zaeBG9DvriuKjIK5xDA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==", + "dev": true + }, + "typescript": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "dev": true + } + } +} diff --git a/packages/pdf-viewer/package.json b/packages/pdf-viewer/package.json new file mode 100644 index 0000000000..ec3cf0c653 --- /dev/null +++ b/packages/pdf-viewer/package.json @@ -0,0 +1,42 @@ +{ + "name": "@joplin/pdf-viewer", + "version": "2.9.0", + "description": "Provides embedded PDF viewers for Joplin", + "main": "dist/main.js", + "types": "src/main.ts", + "private": true, + "publishConfig": { + "access": "restricted" + }, + "scripts": { + "tsc": "tsc --project tsconfig.json", + "watch": "tsc --watch --preserveWatchOutput --project tsconfig.json", + "build": "webpack --config webpack.config.js", + "test": "jest", + "test-ci": "yarn test" + }, + "author": "Joplin", + "license": "MIT", + "devDependencies": { + "@types/jest": "^28.1.6", + "@types/pdfjs-dist": "^2.10.378", + "@types/react": "^18.0.14", + "@types/react-dom": "^16.9.0", + "babel-jest": "^28.1.3", + "css-loader": "^6.7.1", + "jest": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "style-loader": "^3.3.1", + "ts-jest": "^28.0.7", + "ts-loader": "^9.3.0", + "typescript": "^4.0.5", + "webpack": "^5.73.0", + "webpack-cli": "^4.10.0" + }, + "dependencies": { + "@joplin/lib": "workspace:^", + "pdfjs-dist": "^2.14.305", + "react": "16.13.1", + "react-dom": "16.9.0" + } +} diff --git a/packages/pdf-viewer/pages.css b/packages/pdf-viewer/pages.css new file mode 100644 index 0000000000..ba262c730a --- /dev/null +++ b/packages/pdf-viewer/pages.css @@ -0,0 +1,76 @@ +::-webkit-scrollbar { + width: 7px; + height: 7px; +} + +::-webkit-scrollbar-corner { + background: none; +} + +::-webkit-scrollbar-track { + border: none; +} + +::-webkit-scrollbar-thumb { + background: var(--grey); + border-radius: 5px; +} + +.app-pages { + display: flex; + justify-content: center; + align-items: start; + overflow-x: hidden; + width: 100%; + height: 100%; + padding: 0.5rem; + padding-top: 0px; + overflow-y: hidden; + padding-right: 0.25rem; + position: relative; +} + +.app-pages.focused { + padding-right: 0px; + overflow-y: auto; +} + +.pages-holder { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + flex-flow: column; + width: 100%; + min-height: 100%; + height: auto; + row-gap: 2px; +} + +.page-wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + overflow: hidden; + border: solid thin rgba(120, 120, 120, 0.498); + background: rgb(233, 233, 233); + position: relative; +} + +.page-info { + position: absolute; + top: 0.5rem; + left: 0.5rem; + padding: 0.3rem; + background: rgba(203, 203, 203, 0.509); + border-radius: 0.3rem; + font-size: 0.8rem; + color: rgba(91, 91, 91, 0.829); + backdrop-filter: blur(0.5rem); + cursor: default; +} + +.page-info:hover { + opacity: 0.3; +} \ No newline at end of file diff --git a/packages/pdf-viewer/pdfSource.test.ts b/packages/pdf-viewer/pdfSource.test.ts new file mode 100644 index 0000000000..2bcd708e72 --- /dev/null +++ b/packages/pdf-viewer/pdfSource.test.ts @@ -0,0 +1,56 @@ +import { PdfData } from './pdfSource'; +import * as pdfjsLib from 'pdfjs-dist'; +import { readFile } from 'fs'; +import { resolve } from 'path'; + +pdfjsLib.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/legacy/build/pdf.worker.entry'); + +const pdfFilePath1 = resolve('config/welcome.pdf'); + + +function loadFile(filePath: string) { + return new Promise((resolve, reject) => { + readFile(filePath, (err, data) => { + if (err) { + reject(err); + } else { + resolve(new Uint8Array((data))); + } + }); + }); +} + +describe('pdfData', () => { + + test('Should have correct page count', async () => { + const file = await loadFile(pdfFilePath1); + const pdf = new PdfData(); + await pdf.loadDoc(file); + expect(pdf.pageCount).toBe(1); + }); + + test('Should throw error on invalid file', async () => { + const pdf = new PdfData(); + await expect(async () => { + await pdf.loadDoc(''); + }).rejects.toThrowError(); + }); + + test('Should get correct page size', async () => { + const file = await loadFile(pdfFilePath1); + const pdf = new PdfData(); + await pdf.loadDoc(file); + const size = await pdf.getPageSize(); + expect(size.height).toBeCloseTo(841.91998); + expect(size.width).toBeCloseTo(594.95996); + }); + + test('Should calculate scaled size', async () => { + const file = await loadFile(pdfFilePath1); + const pdf = new PdfData(); + await pdf.loadDoc(file); + const scaledSize = await pdf.getScaledSize(null, 200); + expect(scaledSize.scale).toBeCloseTo(0.336157); + }); + +}); diff --git a/packages/pdf-viewer/pdfSource.ts b/packages/pdf-viewer/pdfSource.ts new file mode 100644 index 0000000000..9dfb7f62e2 --- /dev/null +++ b/packages/pdf-viewer/pdfSource.ts @@ -0,0 +1,74 @@ +import * as pdfjsLib from 'pdfjs-dist'; + + +export interface ScaledSize { + height: number; + width: number; + scale: number; +} + +export class PdfData { + public url: string | Uint8Array; + private doc: any = null; + public pageCount: number = null; + private pages: any = {}; + private pageSize: { + height: number; + width: number; + } = null; + + public constructor() { + } + + public loadDoc = async (url: string | Uint8Array) => { + this.url = url; + const loadingTask = pdfjsLib.getDocument(url); + try { + const pdfDocument: any = await loadingTask.promise; + this.doc = pdfDocument; + this.pageCount = pdfDocument.numPages; + } catch (error) { + error.message = `Could not load document: ${error.message}`; + throw error; + } + }; + + public getPage = async (pageNo: number) => { + if (!this.doc) { + throw new Error('Document not loaded'); + } + if (!this.pages[pageNo]) { + this.pages[pageNo] = await this.doc.getPage(pageNo); + } + return this.pages[pageNo]; + }; + + public getPageSize = async () => { + if (!this.pageSize) { + const page = await this.getPage(1); + const viewport = page.getViewport({ scale: 1.0 }); + this.pageSize = { + height: viewport.height, + width: viewport.width, + }; + } + return this.pageSize; + }; + + public getScaledSize = async (height: number = null, width: number = null): Promise => { + const actualSize = await this.getPageSize(); + let scale = 1.0; + if (height && width) { + scale = Math.min(height / actualSize.height, width / actualSize.width); + } else if (height) { + scale = height / actualSize.height; + } else if (width) { + scale = width / actualSize.width; + } + return { + height: actualSize.height * scale, + width: actualSize.width * scale, + scale, + }; + }; +} diff --git a/packages/pdf-viewer/tsconfig.json b/packages/pdf-viewer/tsconfig.json new file mode 100644 index 0000000000..0770cf1c6a --- /dev/null +++ b/packages/pdf-viewer/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, + /* Required since pdf.js does not have a ts binding.*/ + "allowJs": true, + }, + "rootDir": ".", + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "**/node_modules", + ], +} \ No newline at end of file diff --git a/packages/pdf-viewer/viewer.css b/packages/pdf-viewer/viewer.css new file mode 100644 index 0000000000..41d0551399 --- /dev/null +++ b/packages/pdf-viewer/viewer.css @@ -0,0 +1,98 @@ +*, +*::before, +*::after { + box-sizing: border-box; + outline: none; +} + +:root { + --white: rgb(255, 255, 255); + --light: rgb(219, 219, 219); + --grey: rgb(128, 128, 128); + --dark: rgb(1, 0, 34); + --black: rgb(24, 24, 24); + + --red: #ff000d; + --blue: #00A8FF; + --green: rgb(0, 167, 28); + --purple: rgb(132, 0, 255); + --orange: rgb(255, 164, 27); + + --primary: var(--black); + --secondary: var(--dark); + --tertiary: var(--light); + --bg: var(--white); +} + +[data-theme="dark"] { + --primary: var(--white); + --secondary: var(--light); + --tertiary: var(--dark); + --bg: var(--black); +} + +html { + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + -ms-overflow-style: scrollbar; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +body { + margin: 0px; + padding: 0px; + -webkit-overflow-scrolling: touch; + scroll-behavior: smooth; +} + +html, +body { + font-family: 'Segoe UI', sans-serif; + font-size: var(--s); + font-weight: 300; + color: var(--secondary); +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +.mini-app { + display: grid; + grid-template-rows: auto 2rem; + height: 100vh; + width: 100vw; + background-color: var(--bg); + overflow: hidden; + border-radius: 0.4rem; + border: solid thin var(--tertiary); + padding-top: 0.6rem; + padding-right: 0.25rem; + background-color: rgb(240, 241, 245); +} + +.mini-app.focused { + border: solid thin var(--grey); +} + +.mini-app.loading { + display: flex; + justify-content: center; + align-items: center; +} + +[data-theme="dark"] .mini-app { + background-color: rgb(54, 54, 54); +} + +.app-bottom-bar { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 0.2rem 0.5rem; + font-size: 0.8rem; + color: var(--grey); +} \ No newline at end of file diff --git a/packages/pdf-viewer/webpack.config.js b/packages/pdf-viewer/webpack.config.js new file mode 100644 index 0000000000..8f19cc38e9 --- /dev/null +++ b/packages/pdf-viewer/webpack.config.js @@ -0,0 +1,29 @@ +const path = require('path'); + +module.exports = { + entry: { + main: './miniViewer.tsx', + 'pdf.worker': 'pdfjs-dist/build/pdf.worker.entry', + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, + { + test: /\.css$/i, + use: ['style-loader', 'css-loader'], + }, + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, + output: { + filename: '[name].js', + path: path.resolve(__dirname, 'dist'), + clean: true, + }, +}; diff --git a/packages/renderer/MdToHtml.ts b/packages/renderer/MdToHtml.ts index b14d0d1436..ecaa898a51 100644 --- a/packages/renderer/MdToHtml.ts +++ b/packages/renderer/MdToHtml.ts @@ -27,6 +27,7 @@ export interface RenderOptions { codeHighlightCacheKey?: string; plainResourceRendering?: boolean; mapsToLine?: boolean; + useCustomPdfViewer?: boolean; } interface RendererRule { @@ -170,7 +171,7 @@ export interface RuleOptions { audioPlayerEnabled: boolean; videoPlayerEnabled: boolean; pdfViewerEnabled: boolean; - + useCustomPdfViewer: boolean; itemIdToUrl?: ItemIdToUrlHandler; } diff --git a/packages/renderer/MdToHtml/renderMedia.ts b/packages/renderer/MdToHtml/renderMedia.ts index 123defa36c..9d03f0f523 100644 --- a/packages/renderer/MdToHtml/renderMedia.ts +++ b/packages/renderer/MdToHtml/renderMedia.ts @@ -7,6 +7,8 @@ export interface Options { audioPlayerEnabled: boolean; videoPlayerEnabled: boolean; pdfViewerEnabled: boolean; + useCustomPdfViewer: boolean; + theme: any; } function resourceUrl(resourceFullPath: string): string { @@ -42,6 +44,16 @@ export default function(link: Link, options: Options) { } if (options.pdfViewerEnabled && resource.mime === 'application/pdf') { + if (options.useCustomPdfViewer) { + let anchorPageNo = null; + if (link.href.indexOf('#') > 0) { + anchorPageNo = Number(link.href.split('#').pop()); + if (anchorPageNo < 1) anchorPageNo = null; + } + return ``; + } return ``; } diff --git a/packages/renderer/noteStyle.ts b/packages/renderer/noteStyle.ts index 67fb31cd16..b8d7870062 100644 --- a/packages/renderer/noteStyle.ts +++ b/packages/renderer/noteStyle.ts @@ -375,7 +375,12 @@ export default function(theme: any, options: Options = null) { } .media-player.media-pdf { - min-height: 100vh; + min-height: 35rem; + width: 100%; + max-width: 1000px; + margin: 0; + border: 0; + display: block; } /* Clear the CODE style if the element is within a joplin-editable block */ diff --git a/packages/tools/setupNewRelease.ts b/packages/tools/setupNewRelease.ts index c83be1d167..dfc4e5ac54 100644 --- a/packages/tools/setupNewRelease.ts +++ b/packages/tools/setupNewRelease.ts @@ -135,6 +135,7 @@ async function main() { await updatePackageVersion(`${rootDir}/packages/renderer/package.json`, majorMinorVersion, options); await updatePackageVersion(`${rootDir}/packages/server/package.json`, majorMinorVersion, options); await updatePackageVersion(`${rootDir}/packages/tools/package.json`, majorMinorVersion, options); + await updatePackageVersion(`${rootDir}/packages/pdf-viewer/package.json`, majorMinorVersion, options); if (options.updateVersion) { await updateGradleVersion(`${rootDir}/packages/app-mobile/android/app/build.gradle`, majorMinorVersion); diff --git a/yarn.lock b/yarn.lock index 88a450a29d..03bbea7251 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3254,6 +3254,20 @@ __metadata: languageName: node linkType: hard +"@jest/console@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/console@npm:28.1.3" + dependencies: + "@jest/types": ^28.1.3 + "@types/node": "*" + chalk: ^4.0.0 + jest-message-util: ^28.1.3 + jest-util: ^28.1.3 + slash: ^3.0.0 + checksum: fe50d98d26d02ce2901c76dff4bd5429a33c13affb692c9ebf8a578ca2f38a5dd854363d40d6c394f215150791fd1f692afd8e730a4178dda24107c8dfd9750a + languageName: node + linkType: hard + "@jest/core@npm:^26.6.3": version: 26.6.3 resolution: "@jest/core@npm:26.6.3" @@ -3332,6 +3346,48 @@ __metadata: languageName: node linkType: hard +"@jest/core@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/core@npm:28.1.3" + dependencies: + "@jest/console": ^28.1.3 + "@jest/reporters": ^28.1.3 + "@jest/test-result": ^28.1.3 + "@jest/transform": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/node": "*" + ansi-escapes: ^4.2.1 + chalk: ^4.0.0 + ci-info: ^3.2.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + jest-changed-files: ^28.1.3 + jest-config: ^28.1.3 + jest-haste-map: ^28.1.3 + jest-message-util: ^28.1.3 + jest-regex-util: ^28.0.2 + jest-resolve: ^28.1.3 + jest-resolve-dependencies: ^28.1.3 + jest-runner: ^28.1.3 + jest-runtime: ^28.1.3 + jest-snapshot: ^28.1.3 + jest-util: ^28.1.3 + jest-validate: ^28.1.3 + jest-watcher: ^28.1.3 + micromatch: ^4.0.4 + pretty-format: ^28.1.3 + rimraf: ^3.0.0 + slash: ^3.0.0 + strip-ansi: ^6.0.0 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: cb79f34bafc4637e7130df12257f5b29075892a2be2c7f45c6d4c0420853e80b5dae11016e652530eb234f4c44c00910cdca3c2cd86275721860725073f7d9b4 + languageName: node + linkType: hard + "@jest/create-cache-key-function@npm:^27.0.1": version: 27.4.2 resolution: "@jest/create-cache-key-function@npm:27.4.2" @@ -3365,6 +3421,18 @@ __metadata: languageName: node linkType: hard +"@jest/environment@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/environment@npm:28.1.3" + dependencies: + "@jest/fake-timers": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/node": "*" + jest-mock: ^28.1.3 + checksum: 14c496b84aef951df33128cea68988e9de43b2e9d62be9f9c4308d4ac307fa345642813679f80d0a4cedeb900cf6f0b6bb2b92ce089528e8721f72295fdc727f + languageName: node + linkType: hard + "@jest/expect-utils@npm:^28.1.1": version: 28.1.1 resolution: "@jest/expect-utils@npm:28.1.1" @@ -3374,6 +3442,15 @@ __metadata: languageName: node linkType: hard +"@jest/expect-utils@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/expect-utils@npm:28.1.3" + dependencies: + jest-get-type: ^28.0.2 + checksum: 808ea3a68292a7e0b95490fdd55605c430b4cf209ea76b5b61bfb2a1badcb41bc046810fe4e364bd5fe04663978aa2bd73d8f8465a761dd7c655aeb44cf22987 + languageName: node + linkType: hard + "@jest/expect@npm:^28.1.2": version: 28.1.2 resolution: "@jest/expect@npm:28.1.2" @@ -3384,6 +3461,16 @@ __metadata: languageName: node linkType: hard +"@jest/expect@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/expect@npm:28.1.3" + dependencies: + expect: ^28.1.3 + jest-snapshot: ^28.1.3 + checksum: 4197f6fdddc33dc45ba4e838f992fc61839c421d7aed0dfe665ef9c2f172bb1df8a8cac9cecee272b40e744a326da521d5e182709fe82a0b936055bfffa3b473 + languageName: node + linkType: hard + "@jest/fake-timers@npm:^26.6.2": version: 26.6.2 resolution: "@jest/fake-timers@npm:26.6.2" @@ -3412,6 +3499,20 @@ __metadata: languageName: node linkType: hard +"@jest/fake-timers@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/fake-timers@npm:28.1.3" + dependencies: + "@jest/types": ^28.1.3 + "@sinonjs/fake-timers": ^9.1.2 + "@types/node": "*" + jest-message-util: ^28.1.3 + jest-mock: ^28.1.3 + jest-util: ^28.1.3 + checksum: cec14d5b14913a54dce64a62912c5456235f5d90b509ceae19c727565073114dae1aaf960ac6be96b3eb94789a3a758b96b72c8fca7e49a6ccac415fbc0321e1 + languageName: node + linkType: hard + "@jest/globals@npm:^26.6.2": version: 26.6.2 resolution: "@jest/globals@npm:26.6.2" @@ -3434,6 +3535,17 @@ __metadata: languageName: node linkType: hard +"@jest/globals@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/globals@npm:28.1.3" + dependencies: + "@jest/environment": ^28.1.3 + "@jest/expect": ^28.1.3 + "@jest/types": ^28.1.3 + checksum: 3504bb23de629d466c6f2b6b75d2e1c1b10caccbbcfb7eaa82d22cc37711c8e364c243929581184846605c023b475ea6c42c2e3ea5994429a988d8d527af32cd + languageName: node + linkType: hard + "@jest/reporters@npm:^26.6.2": version: 26.6.2 resolution: "@jest/reporters@npm:26.6.2" @@ -3508,6 +3620,44 @@ __metadata: languageName: node linkType: hard +"@jest/reporters@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/reporters@npm:28.1.3" + dependencies: + "@bcoe/v8-coverage": ^0.2.3 + "@jest/console": ^28.1.3 + "@jest/test-result": ^28.1.3 + "@jest/transform": ^28.1.3 + "@jest/types": ^28.1.3 + "@jridgewell/trace-mapping": ^0.3.13 + "@types/node": "*" + chalk: ^4.0.0 + collect-v8-coverage: ^1.0.0 + exit: ^0.1.2 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + istanbul-lib-coverage: ^3.0.0 + istanbul-lib-instrument: ^5.1.0 + istanbul-lib-report: ^3.0.0 + istanbul-lib-source-maps: ^4.0.0 + istanbul-reports: ^3.1.3 + jest-message-util: ^28.1.3 + jest-util: ^28.1.3 + jest-worker: ^28.1.3 + slash: ^3.0.0 + string-length: ^4.0.1 + strip-ansi: ^6.0.0 + terminal-link: ^2.0.0 + v8-to-istanbul: ^9.0.1 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: a7440887ce837922cbeaa64c3232eb48aae02aa9123f29fc4280ad3e1afe4b35dcba171ba1d5fd219037c396c5152d9c2d102cff1798dd5ae3bd33ac4759ae0a + languageName: node + linkType: hard + "@jest/schemas@npm:^28.0.2": version: 28.0.2 resolution: "@jest/schemas@npm:28.0.2" @@ -3517,6 +3667,15 @@ __metadata: languageName: node linkType: hard +"@jest/schemas@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/schemas@npm:28.1.3" + dependencies: + "@sinclair/typebox": ^0.24.1 + checksum: 3cf1d4b66c9c4ffda58b246de1ddcba8e6ad085af63dccdf07922511f13b68c0cc480a7bc620cb4f3099a6f134801c747e1df7bfc7a4ef4dceefbdea3e31e1de + languageName: node + linkType: hard + "@jest/source-map@npm:^26.6.2": version: 26.6.2 resolution: "@jest/source-map@npm:26.6.2" @@ -3563,6 +3722,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-result@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/test-result@npm:28.1.3" + dependencies: + "@jest/console": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/istanbul-lib-coverage": ^2.0.0 + collect-v8-coverage: ^1.0.0 + checksum: 957a5dd2fd2e84aabe86698f93c0825e96128ccaa23abf548b159a9b08ac74e4bde7acf4bec48479243dbdb27e4ea1b68c171846d21fb64855c6b55cead9ef27 + languageName: node + linkType: hard + "@jest/test-sequencer@npm:^26.6.3": version: 26.6.3 resolution: "@jest/test-sequencer@npm:26.6.3" @@ -3588,6 +3759,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-sequencer@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/test-sequencer@npm:28.1.3" + dependencies: + "@jest/test-result": ^28.1.3 + graceful-fs: ^4.2.9 + jest-haste-map: ^28.1.3 + slash: ^3.0.0 + checksum: 13f8905e6d1ec8286694146f7be3cf90eff801bbdea5e5c403e6881444bb390ed15494c7b9948aa94bd7e9c9a851e0d3002ed6e7371d048b478596e5b23df953 + languageName: node + linkType: hard + "@jest/transform@npm:^26.6.2": version: 26.6.2 resolution: "@jest/transform@npm:26.6.2" @@ -3634,6 +3817,29 @@ __metadata: languageName: node linkType: hard +"@jest/transform@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/transform@npm:28.1.3" + dependencies: + "@babel/core": ^7.11.6 + "@jest/types": ^28.1.3 + "@jridgewell/trace-mapping": ^0.3.13 + babel-plugin-istanbul: ^6.1.1 + chalk: ^4.0.0 + convert-source-map: ^1.4.0 + fast-json-stable-stringify: ^2.0.0 + graceful-fs: ^4.2.9 + jest-haste-map: ^28.1.3 + jest-regex-util: ^28.0.2 + jest-util: ^28.1.3 + micromatch: ^4.0.4 + pirates: ^4.0.4 + slash: ^3.0.0 + write-file-atomic: ^4.0.1 + checksum: dadf618936e0aa84342f07f532801d5bed43cdf95d1417b929e4f8782c872cff1adc84096d5a287a796d0039a2691c06d8450cce5a713a8b52fbb9f872a1e760 + languageName: node + linkType: hard + "@jest/types@npm:^25.5.0": version: 25.5.0 resolution: "@jest/types@npm:25.5.0" @@ -3686,6 +3892,20 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:^28.1.3": + version: 28.1.3 + resolution: "@jest/types@npm:28.1.3" + dependencies: + "@jest/schemas": ^28.1.3 + "@types/istanbul-lib-coverage": ^2.0.0 + "@types/istanbul-reports": ^3.0.0 + "@types/node": "*" + "@types/yargs": ^17.0.8 + chalk: ^4.0.0 + checksum: 1e258d9c063fcf59ebc91e46d5ea5984674ac7ae6cae3e50aa780d22b4405bf2c925f40350bf30013839eb5d4b5e521d956ddf8f3b7c78debef0e75a07f57350 + languageName: node + linkType: hard + "@joeattardi/emoji-button@npm:^4.6.0": version: 4.6.2 resolution: "@joeattardi/emoji-button@npm:4.6.2" @@ -3722,6 +3942,7 @@ __metadata: "@fortawesome/fontawesome-free": ^5.13.0 "@joeattardi/emoji-button": ^4.6.0 "@joplin/lib": ~2.9 + "@joplin/pdf-viewer": ~2.9 "@joplin/renderer": ~2.9 "@joplin/tools": ~2.9 "@testing-library/react-hooks": ^3.4.2 @@ -3937,7 +4158,7 @@ __metadata: languageName: unknown linkType: soft -"@joplin/lib@workspace:packages/lib, @joplin/lib@~2.9": +"@joplin/lib@workspace:^, @joplin/lib@workspace:packages/lib, @joplin/lib@~2.9": version: 0.0.0-use.local resolution: "@joplin/lib@workspace:packages/lib" dependencies: @@ -4018,6 +4239,31 @@ __metadata: languageName: unknown linkType: soft +"@joplin/pdf-viewer@workspace:packages/pdf-viewer, @joplin/pdf-viewer@~2.9": + version: 0.0.0-use.local + resolution: "@joplin/pdf-viewer@workspace:packages/pdf-viewer" + dependencies: + "@joplin/lib": "workspace:^" + "@types/jest": ^28.1.6 + "@types/pdfjs-dist": ^2.10.378 + "@types/react": ^18.0.14 + "@types/react-dom": ^16.9.0 + babel-jest: ^28.1.3 + css-loader: ^6.7.1 + jest: ^28.1.3 + jest-environment-jsdom: ^28.1.3 + pdfjs-dist: ^2.14.305 + react: 16.13.1 + react-dom: 16.9.0 + style-loader: ^3.3.1 + ts-jest: ^28.0.7 + ts-loader: ^9.3.0 + typescript: ^4.0.5 + webpack: ^5.73.0 + webpack-cli: ^4.10.0 + languageName: unknown + linkType: soft + "@joplin/plugin-repo-cli@workspace:packages/plugin-repo-cli": version: 0.0.0-use.local resolution: "@joplin/plugin-repo-cli@workspace:packages/plugin-repo-cli" @@ -5752,6 +5998,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.24.1": + version: 0.24.22 + resolution: "@sinclair/typebox@npm:0.24.22" + checksum: a21638c2058295602ed726eb1aa52fb585c6e866ebdeefb182a3b3c994370464ebd03b6d3b56c8c79b21df5d2231734fdb8dba8ddbca2c98f0005d6b774c2c5e + languageName: node + linkType: hard + "@sindresorhus/is@npm:^0.14.0": version: 0.14.0 resolution: "@sindresorhus/is@npm:0.14.0" @@ -6302,6 +6555,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^28.1.6": + version: 28.1.6 + resolution: "@types/jest@npm:28.1.6" + dependencies: + jest-matcher-utils: ^28.0.0 + pretty-format: ^28.0.0 + checksum: f2ba5fbefc8f44d1c16ee19d8d2811bca75754a2846e222287f2788d96062801c568215e6b81eb532a48e8cb2a7282729da1d4f6fb496831da8269c5abaad4c5 + languageName: node + linkType: hard + "@types/js-yaml@npm:^4.0.2": version: 4.0.5 resolution: "@types/js-yaml@npm:4.0.5" @@ -6551,6 +6814,15 @@ __metadata: languageName: node linkType: hard +"@types/pdfjs-dist@npm:^2.10.378": + version: 2.10.378 + resolution: "@types/pdfjs-dist@npm:2.10.378" + dependencies: + pdfjs-dist: "*" + checksum: 36dd6010f7d23a995efdf11ea4ecb56f371f8bfb3e83a5c311666726e13238597ed1519701d0e2e6fb297270d01ad6aece9582b036fd4cb3aa301e61ea364978 + languageName: node + linkType: hard + "@types/plist@npm:^3.0.1": version: 3.0.2 resolution: "@types/plist@npm:3.0.2" @@ -6596,6 +6868,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:^16.9.0": + version: 16.9.16 + resolution: "@types/react-dom@npm:16.9.16" + dependencies: + "@types/react": ^16 + checksum: ff65a2a36d493ed6a9032f8a96f06074d940ca63c82d400e21e6ba16d25762b4f5c8cd352b93eb3b7aa820e158b7a6b16e5daffec2a512fa6327b00036cde0a8 + languageName: node + linkType: hard + "@types/react-native@npm:*": version: 0.66.8 resolution: "@types/react-native@npm:0.66.8" @@ -6656,6 +6937,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^16": + version: 16.14.28 + resolution: "@types/react@npm:16.14.28" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: c3ef0c479e0c53f62057cae6dfe89785c855e697ea106e7c71da18210307a8d90a7f6bd285ca1370ee1ce859b1c241e173f4c6d0fab73c35e1d321d30b4dc4cc + languageName: node + linkType: hard + "@types/react@npm:^16.9.55": version: 16.14.21 resolution: "@types/react@npm:16.14.21" @@ -6667,6 +6959,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18.0.14": + version: 18.0.15 + resolution: "@types/react@npm:18.0.15" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: e22cc388d1c145aa184787e44dc28db4789976c704cd5db475c170bb76a560eb81def5f346cfe750949bb3d43ad88822b8cbb9f19b1286e3795892a8263e7715 + languageName: node + linkType: hard + "@types/responselike@npm:*, @types/responselike@npm:^1.0.0": version: 1.0.0 resolution: "@types/responselike@npm:1.0.0" @@ -7122,6 +7425,16 @@ __metadata: languageName: node linkType: hard +"@webpack-cli/configtest@npm:^1.2.0": + version: 1.2.0 + resolution: "@webpack-cli/configtest@npm:1.2.0" + peerDependencies: + webpack: 4.x.x || 5.x.x + webpack-cli: 4.x.x + checksum: a2726cd9ec601d2b57e5fc15e0ebf5200a8892065e735911269ac2038e62be4bfc176ea1f88c2c46ff09b4d05d4c10ae045e87b3679372483d47da625a327e28 + languageName: node + linkType: hard + "@webpack-cli/info@npm:^1.4.0": version: 1.4.0 resolution: "@webpack-cli/info@npm:1.4.0" @@ -7133,6 +7446,17 @@ __metadata: languageName: node linkType: hard +"@webpack-cli/info@npm:^1.5.0": + version: 1.5.0 + resolution: "@webpack-cli/info@npm:1.5.0" + dependencies: + envinfo: ^7.7.3 + peerDependencies: + webpack-cli: 4.x.x + checksum: 7f56fe037cd7d1fd5c7428588519fbf04a0cad33925ee4202ffbafd00f8ec1f2f67d991245e687d50e0f3e23f7b7814273d56cb9f7da4b05eed47c8d815c6296 + languageName: node + linkType: hard + "@webpack-cli/serve@npm:^1.6.0": version: 1.6.0 resolution: "@webpack-cli/serve@npm:1.6.0" @@ -7145,6 +7469,18 @@ __metadata: languageName: node linkType: hard +"@webpack-cli/serve@npm:^1.7.0": + version: 1.7.0 + resolution: "@webpack-cli/serve@npm:1.7.0" + peerDependencies: + webpack-cli: 4.x.x + peerDependenciesMeta: + webpack-dev-server: + optional: true + checksum: d475e8effa23eb7ff9a48b14d4de425989fd82f906ce71c210921cc3852327c22873be00c35e181a25a6bd03d424ae2b83e7f3b3f410ac7ee31b128ab4ac7713 + languageName: node + linkType: hard + "@xtuc/ieee754@npm:^1.2.0": version: 1.2.0 resolution: "@xtuc/ieee754@npm:1.2.0" @@ -8519,6 +8855,23 @@ __metadata: languageName: node linkType: hard +"babel-jest@npm:^28.1.3": + version: 28.1.3 + resolution: "babel-jest@npm:28.1.3" + dependencies: + "@jest/transform": ^28.1.3 + "@types/babel__core": ^7.1.14 + babel-plugin-istanbul: ^6.1.1 + babel-preset-jest: ^28.1.3 + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + slash: ^3.0.0 + peerDependencies: + "@babel/core": ^7.8.0 + checksum: 57ccd2296e1839687b5df2fd138c3d00717e0369e385254b012ccd4ee70e75f5d5c8e6cfcdf92d155015b468cfebb847b38e69bb5805d8aaf730e20575127cc6 + languageName: node + linkType: hard + "babel-messages@npm:^6.23.0": version: 6.23.0 resolution: "babel-messages@npm:6.23.0" @@ -8594,6 +8947,18 @@ __metadata: languageName: node linkType: hard +"babel-plugin-jest-hoist@npm:^28.1.3": + version: 28.1.3 + resolution: "babel-plugin-jest-hoist@npm:28.1.3" + dependencies: + "@babel/template": ^7.3.3 + "@babel/types": ^7.3.3 + "@types/babel__core": ^7.1.14 + "@types/babel__traverse": ^7.0.6 + checksum: 648d89f9d80f6450ce7e50d0c32eb91b7f26269b47c3e37aaf2e0f2f66a980978345bd6b8c9b8c3aa6a8252ad2bc2c9fb50630e9895622c9a0972af5f70ed20e + languageName: node + linkType: hard + "babel-plugin-macros@npm:^2.0.0": version: 2.8.0 resolution: "babel-plugin-macros@npm:2.8.0" @@ -8842,6 +9207,18 @@ __metadata: languageName: node linkType: hard +"babel-preset-jest@npm:^28.1.3": + version: 28.1.3 + resolution: "babel-preset-jest@npm:28.1.3" + dependencies: + babel-plugin-jest-hoist: ^28.1.3 + babel-preset-current-node-syntax: ^1.0.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 8248a4a5ca4242cc06ad13b10b9183ad2664da8fb0da060c352223dcf286f0ce9c708fa17901dc44ecabec25e6d309e5e5b9830a61dd777c3925f187a345a47d + languageName: node + linkType: hard + "babel-preset-react@npm:^6.24.1": version: 6.24.1 resolution: "babel-preset-react@npm:6.24.1" @@ -11567,6 +11944,24 @@ __metadata: languageName: node linkType: hard +"css-loader@npm:^6.7.1": + version: 6.7.1 + resolution: "css-loader@npm:6.7.1" + dependencies: + icss-utils: ^5.1.0 + postcss: ^8.4.7 + postcss-modules-extract-imports: ^3.0.0 + postcss-modules-local-by-default: ^4.0.0 + postcss-modules-scope: ^3.0.0 + postcss-modules-values: ^4.0.0 + postcss-value-parser: ^4.2.0 + semver: ^7.3.5 + peerDependencies: + webpack: ^5.0.0 + checksum: 170fdbc630a05a43679ef60fa97694766b568dbde37adccc0faafa964fc675f08b976bc68837bb73b61d60240e8d2cbcbf51540fe94ebc9dafc56e7c46ba5527 + languageName: node + linkType: hard + "css-selector-parser@npm:^1.3": version: 1.4.1 resolution: "css-selector-parser@npm:1.4.1" @@ -11608,6 +12003,15 @@ __metadata: languageName: node linkType: hard +"cssesc@npm:^3.0.0": + version: 3.0.0 + resolution: "cssesc@npm:3.0.0" + bin: + cssesc: bin/cssesc + checksum: f8c4ababffbc5e2ddf2fa9957dda1ee4af6048e22aeda1869d0d00843223c1b13ad3f5d88b51caa46c994225eacb636b764eb807a8883e2fb6f99b4f4e8c48b2 + languageName: node + linkType: hard + "cssom@npm:0.3.x, cssom@npm:>= 0.3.2 < 0.4.0, cssom@npm:~0.3.6": version: 0.3.8 resolution: "cssom@npm:0.3.8" @@ -13380,6 +13784,13 @@ __metadata: languageName: node linkType: hard +"dommatrix@npm:^1.0.1": + version: 1.0.3 + resolution: "dommatrix@npm:1.0.3" + checksum: 8ac727c1a14cf8de30a5b49a3bd6b2622a661b391fe1ac54e855eaa14a857ed86d63492150b5f70f912acc24fa3acc31d750259c47e9b5801de237624b0a319f + languageName: node + linkType: hard + "dompurify@npm:2.3.4": version: 2.3.4 resolution: "dompurify@npm:2.3.4" @@ -15078,6 +15489,19 @@ __metadata: languageName: node linkType: hard +"expect@npm:^28.1.3": + version: 28.1.3 + resolution: "expect@npm:28.1.3" + dependencies: + "@jest/expect-utils": ^28.1.3 + jest-get-type: ^28.0.2 + jest-matcher-utils: ^28.1.3 + jest-message-util: ^28.1.3 + jest-util: ^28.1.3 + checksum: 101e0090de300bcafedb7dbfd19223368a2251ce5fe0105bbb6de5720100b89fb6b64290ebfb42febc048324c76d6a4979cdc4b61eb77747857daf7a5de9b03d + languageName: node + linkType: hard + "ext@npm:^1.1.2": version: 1.6.0 resolution: "ext@npm:1.6.0" @@ -17839,6 +18263,15 @@ __metadata: languageName: node linkType: hard +"icss-utils@npm:^5.0.0, icss-utils@npm:^5.1.0": + version: 5.1.0 + resolution: "icss-utils@npm:5.1.0" + peerDependencies: + postcss: ^8.1.0 + checksum: 5c324d283552b1269cfc13a503aaaa172a280f914e5b81544f3803bc6f06a3b585fb79f66f7c771a2c052db7982c18bf92d001e3b47282e3abbbb4c4cc488d68 + languageName: node + linkType: hard + "ieee754@npm:1.1.13": version: 1.1.13 resolution: "ieee754@npm:1.1.13" @@ -19469,6 +19902,16 @@ __metadata: languageName: node linkType: hard +"jest-changed-files@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-changed-files@npm:28.1.3" + dependencies: + execa: ^5.0.0 + p-limit: ^3.1.0 + checksum: c78af14a68b9b19101623ae7fde15a2488f9b3dbe8cca12a05c4a223bc9bfd3bf41ee06830f20fb560c52434435d6153c9cc6cf450b1f7b03e5e7f96a953a6a6 + languageName: node + linkType: hard + "jest-circus@npm:^28.1.2": version: 28.1.2 resolution: "jest-circus@npm:28.1.2" @@ -19496,6 +19939,33 @@ __metadata: languageName: node linkType: hard +"jest-circus@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-circus@npm:28.1.3" + dependencies: + "@jest/environment": ^28.1.3 + "@jest/expect": ^28.1.3 + "@jest/test-result": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/node": "*" + chalk: ^4.0.0 + co: ^4.6.0 + dedent: ^0.7.0 + is-generator-fn: ^2.0.0 + jest-each: ^28.1.3 + jest-matcher-utils: ^28.1.3 + jest-message-util: ^28.1.3 + jest-runtime: ^28.1.3 + jest-snapshot: ^28.1.3 + jest-util: ^28.1.3 + p-limit: ^3.1.0 + pretty-format: ^28.1.3 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: b635e60a9c92adaefc3f24def8eba691e7c2fdcf6c9fa640cddf2eb8c8b26ee62eab73ebb88798fd7c52a74c1495a984e39b748429b610426f02e9d3d56e09b2 + languageName: node + linkType: hard + "jest-cli@npm:^26.6.3": version: 26.6.3 resolution: "jest-cli@npm:26.6.3" @@ -19546,6 +20016,33 @@ __metadata: languageName: node linkType: hard +"jest-cli@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-cli@npm:28.1.3" + dependencies: + "@jest/core": ^28.1.3 + "@jest/test-result": ^28.1.3 + "@jest/types": ^28.1.3 + chalk: ^4.0.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + import-local: ^3.0.2 + jest-config: ^28.1.3 + jest-util: ^28.1.3 + jest-validate: ^28.1.3 + prompts: ^2.0.1 + yargs: ^17.3.1 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: fb424576bf38346318daddee3fcc597cd78cb8dda1759d09c529d8ba1a748f2765c17b00671072a838826e59465a810ff8a232bc6ba2395c131bf3504425a363 + languageName: node + linkType: hard + "jest-config@npm:^26.6.3": version: 26.6.3 resolution: "jest-config@npm:26.6.3" @@ -19615,6 +20112,44 @@ __metadata: languageName: node linkType: hard +"jest-config@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-config@npm:28.1.3" + dependencies: + "@babel/core": ^7.11.6 + "@jest/test-sequencer": ^28.1.3 + "@jest/types": ^28.1.3 + babel-jest: ^28.1.3 + chalk: ^4.0.0 + ci-info: ^3.2.0 + deepmerge: ^4.2.2 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + jest-circus: ^28.1.3 + jest-environment-node: ^28.1.3 + jest-get-type: ^28.0.2 + jest-regex-util: ^28.0.2 + jest-resolve: ^28.1.3 + jest-runner: ^28.1.3 + jest-util: ^28.1.3 + jest-validate: ^28.1.3 + micromatch: ^4.0.4 + parse-json: ^5.2.0 + pretty-format: ^28.1.3 + slash: ^3.0.0 + strip-json-comments: ^3.1.1 + peerDependencies: + "@types/node": "*" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + ts-node: + optional: true + checksum: ddabffd3a3a8cb6c2f58f06cdf3535157dbf8c70bcde3e5c3de7bee6a8d617840ffc8cffb0083e38c6814f2a08c225ca19f58898efaf4f351af94679f22ce6bc + languageName: node + linkType: hard + "jest-diff@npm:^25.2.1": version: 25.5.0 resolution: "jest-diff@npm:25.5.0" @@ -19663,6 +20198,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-diff@npm:28.1.3" + dependencies: + chalk: ^4.0.0 + diff-sequences: ^28.1.1 + jest-get-type: ^28.0.2 + pretty-format: ^28.1.3 + checksum: fa8583e0ccbe775714ce850b009be1b0f6b17a4b6759f33ff47adef27942ebc610dbbcc8a5f7cfb7f12b3b3b05afc9fb41d5f766674616025032ff1e4f9866e0 + languageName: node + linkType: hard + "jest-docblock@npm:^26.0.0": version: 26.0.0 resolution: "jest-docblock@npm:26.0.0" @@ -19707,6 +20254,19 @@ __metadata: languageName: node linkType: hard +"jest-each@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-each@npm:28.1.3" + dependencies: + "@jest/types": ^28.1.3 + chalk: ^4.0.0 + jest-get-type: ^28.0.2 + jest-util: ^28.1.3 + pretty-format: ^28.1.3 + checksum: 5c5b8ccb1484e58b027bea682cfa020a45e5bf5379cc7c23bdec972576c1dc3c3bf03df2b78416cefc1a58859dd33b7cf5fff54c370bc3c0f14a3e509eb87282 + languageName: node + linkType: hard + "jest-environment-jsdom@npm:^26.6.2": version: 26.6.2 resolution: "jest-environment-jsdom@npm:26.6.2" @@ -19738,6 +20298,22 @@ __metadata: languageName: node linkType: hard +"jest-environment-jsdom@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-environment-jsdom@npm:28.1.3" + dependencies: + "@jest/environment": ^28.1.3 + "@jest/fake-timers": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/jsdom": ^16.2.4 + "@types/node": "*" + jest-mock: ^28.1.3 + jest-util: ^28.1.3 + jsdom: ^19.0.0 + checksum: 32758f9b9a1fd04ec3ebaaa608d740a36b960d37d00bd3d4d83fdc4b527afc474c14f04fa860817e1fa22923e2dc3cd2b497db41af6a5d73e91327951612025e + languageName: node + linkType: hard + "jest-environment-node@npm:^26.6.2": version: 26.6.2 resolution: "jest-environment-node@npm:26.6.2" @@ -19766,6 +20342,20 @@ __metadata: languageName: node linkType: hard +"jest-environment-node@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-environment-node@npm:28.1.3" + dependencies: + "@jest/environment": ^28.1.3 + "@jest/fake-timers": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/node": "*" + jest-mock: ^28.1.3 + jest-util: ^28.1.3 + checksum: 1048fe306a6a8b0880a4c66278ebb57479f29c12cff89aab3aa79ab77a8859cf17ab8aa9919fd21c329a7db90e35581b43664e694ad453d5b04e00f3c6420469 + languageName: node + linkType: hard + "jest-expect-message@npm:^1.0.2": version: 1.0.2 resolution: "jest-expect-message@npm:1.0.2" @@ -19849,6 +20439,29 @@ __metadata: languageName: node linkType: hard +"jest-haste-map@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-haste-map@npm:28.1.3" + dependencies: + "@jest/types": ^28.1.3 + "@types/graceful-fs": ^4.1.3 + "@types/node": "*" + anymatch: ^3.0.3 + fb-watchman: ^2.0.0 + fsevents: ^2.3.2 + graceful-fs: ^4.2.9 + jest-regex-util: ^28.0.2 + jest-util: ^28.1.3 + jest-worker: ^28.1.3 + micromatch: ^4.0.4 + walker: ^1.0.8 + dependenciesMeta: + fsevents: + optional: true + checksum: d05fdc108645fc2b39fcd4001952cc7a8cb550e93494e98c1e9ab1fc542686f6ac67177c132e564cf94fe8f81503f3f8db8b825b9b713dc8c5748aec63ba4688 + languageName: node + linkType: hard + "jest-jasmine2@npm:^26.6.3": version: 26.6.3 resolution: "jest-jasmine2@npm:26.6.3" @@ -19895,6 +20508,16 @@ __metadata: languageName: node linkType: hard +"jest-leak-detector@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-leak-detector@npm:28.1.3" + dependencies: + jest-get-type: ^28.0.2 + pretty-format: ^28.1.3 + checksum: 2e976a4880cf9af11f53a19f6a3820e0f90b635a900737a5427fc42e337d5628ba446dcd7c020ecea3806cf92bc0bbf6982ed62a9cd84e5a13d8751aa30fbbb7 + languageName: node + linkType: hard + "jest-matcher-utils@npm:^26.6.2": version: 26.6.2 resolution: "jest-matcher-utils@npm:26.6.2" @@ -19919,6 +20542,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-matcher-utils@npm:28.1.3" + dependencies: + chalk: ^4.0.0 + jest-diff: ^28.1.3 + jest-get-type: ^28.0.2 + pretty-format: ^28.1.3 + checksum: 6b34f0cf66f6781e92e3bec97bf27796bd2ba31121e5c5997218d9adba6deea38a30df5203937d6785b68023ed95cbad73663cc9aad6fb0cb59aeb5813a58daf + languageName: node + linkType: hard + "jest-message-util@npm:^26.6.2": version: 26.6.2 resolution: "jest-message-util@npm:26.6.2" @@ -19953,6 +20588,23 @@ __metadata: languageName: node linkType: hard +"jest-message-util@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-message-util@npm:28.1.3" + dependencies: + "@babel/code-frame": ^7.12.13 + "@jest/types": ^28.1.3 + "@types/stack-utils": ^2.0.0 + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + micromatch: ^4.0.4 + pretty-format: ^28.1.3 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: 1f266854166dcc6900d75a88b54a25225a2f3710d463063ff1c99021569045c35c7d58557b25447a17eb3a65ce763b2f9b25550248b468a9d4657db365f39e96 + languageName: node + linkType: hard + "jest-mock@npm:^26.6.2": version: 26.6.2 resolution: "jest-mock@npm:26.6.2" @@ -19973,6 +20625,16 @@ __metadata: languageName: node linkType: hard +"jest-mock@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-mock@npm:28.1.3" + dependencies: + "@jest/types": ^28.1.3 + "@types/node": "*" + checksum: a573bf8e5f12f4c29c661266c31b5c6b69a28d3195b83049983bce025b2b1a0152351567e89e63b102ef817034c2a3aa97eda4e776f3bae2aee54c5765573aa7 + languageName: node + linkType: hard + "jest-pnp-resolver@npm:^1.2.2": version: 1.2.2 resolution: "jest-pnp-resolver@npm:1.2.2" @@ -20020,6 +20682,16 @@ __metadata: languageName: node linkType: hard +"jest-resolve-dependencies@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-resolve-dependencies@npm:28.1.3" + dependencies: + jest-regex-util: ^28.0.2 + jest-snapshot: ^28.1.3 + checksum: 4eea9ec33aefc1c71dc5956391efbcc7be76bda986b366ab3931d99c5f7ed01c9ebd7520e405ea2c76e1bb2c7ce504be6eca2b9831df16564d1e625500f3bfe7 + languageName: node + linkType: hard + "jest-resolve@npm:^26.6.2": version: 26.6.2 resolution: "jest-resolve@npm:26.6.2" @@ -20053,6 +20725,23 @@ __metadata: languageName: node linkType: hard +"jest-resolve@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-resolve@npm:28.1.3" + dependencies: + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + jest-haste-map: ^28.1.3 + jest-pnp-resolver: ^1.2.2 + jest-util: ^28.1.3 + jest-validate: ^28.1.3 + resolve: ^1.20.0 + resolve.exports: ^1.1.0 + slash: ^3.0.0 + checksum: df61a490c93f4f4cf52135e43d6a4fcacb07b0b7d4acc6319e9289529c1d14f2d8e1638e095dbf96f156834802755e38db68caca69dba21a3261ee711d4426b6 + languageName: node + linkType: hard + "jest-runner@npm:^26.6.3": version: 26.6.3 resolution: "jest-runner@npm:26.6.3" @@ -20110,6 +20799,35 @@ __metadata: languageName: node linkType: hard +"jest-runner@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-runner@npm:28.1.3" + dependencies: + "@jest/console": ^28.1.3 + "@jest/environment": ^28.1.3 + "@jest/test-result": ^28.1.3 + "@jest/transform": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/node": "*" + chalk: ^4.0.0 + emittery: ^0.10.2 + graceful-fs: ^4.2.9 + jest-docblock: ^28.1.1 + jest-environment-node: ^28.1.3 + jest-haste-map: ^28.1.3 + jest-leak-detector: ^28.1.3 + jest-message-util: ^28.1.3 + jest-resolve: ^28.1.3 + jest-runtime: ^28.1.3 + jest-util: ^28.1.3 + jest-watcher: ^28.1.3 + jest-worker: ^28.1.3 + p-limit: ^3.1.0 + source-map-support: 0.5.13 + checksum: 32405cd970fa6b11e039192dae699fd1bcc6f61f67d50605af81d193f24dd4373b25f5fcc1c571a028ec1b02174e8a4b6d0d608772063fb06f08a5105693533b + languageName: node + linkType: hard + "jest-runtime@npm:^26.6.3": version: 26.6.3 resolution: "jest-runtime@npm:26.6.3" @@ -20177,6 +20895,36 @@ __metadata: languageName: node linkType: hard +"jest-runtime@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-runtime@npm:28.1.3" + dependencies: + "@jest/environment": ^28.1.3 + "@jest/fake-timers": ^28.1.3 + "@jest/globals": ^28.1.3 + "@jest/source-map": ^28.1.2 + "@jest/test-result": ^28.1.3 + "@jest/transform": ^28.1.3 + "@jest/types": ^28.1.3 + chalk: ^4.0.0 + cjs-module-lexer: ^1.0.0 + collect-v8-coverage: ^1.0.0 + execa: ^5.0.0 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + jest-haste-map: ^28.1.3 + jest-message-util: ^28.1.3 + jest-mock: ^28.1.3 + jest-regex-util: ^28.0.2 + jest-resolve: ^28.1.3 + jest-snapshot: ^28.1.3 + jest-util: ^28.1.3 + slash: ^3.0.0 + strip-bom: ^4.0.0 + checksum: b17c40af858e74dafa4f515ef3711c1e9ef3d4ad7d74534ee0745422534bc04fd166d4eceb62a3aa7dc951505d6f6d2a81d16e90bebb032be409ec0500974a36 + languageName: node + linkType: hard + "jest-serializer@npm:^26.6.2": version: 26.6.2 resolution: "jest-serializer@npm:26.6.2" @@ -20242,6 +20990,37 @@ __metadata: languageName: node linkType: hard +"jest-snapshot@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-snapshot@npm:28.1.3" + dependencies: + "@babel/core": ^7.11.6 + "@babel/generator": ^7.7.2 + "@babel/plugin-syntax-typescript": ^7.7.2 + "@babel/traverse": ^7.7.2 + "@babel/types": ^7.3.3 + "@jest/expect-utils": ^28.1.3 + "@jest/transform": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/babel__traverse": ^7.0.6 + "@types/prettier": ^2.1.5 + babel-preset-current-node-syntax: ^1.0.0 + chalk: ^4.0.0 + expect: ^28.1.3 + graceful-fs: ^4.2.9 + jest-diff: ^28.1.3 + jest-get-type: ^28.0.2 + jest-haste-map: ^28.1.3 + jest-matcher-utils: ^28.1.3 + jest-message-util: ^28.1.3 + jest-util: ^28.1.3 + natural-compare: ^1.4.0 + pretty-format: ^28.1.3 + semver: ^7.3.5 + checksum: 2a46a5493f1fb50b0a236a21f25045e7f46a244f9f3ae37ef4fbcd40249d0d68bb20c950ce77439e4e2cac985b05c3061c90b34739bf6069913a1199c8c716e1 + languageName: node + linkType: hard + "jest-util@npm:^26.6.2": version: 26.6.2 resolution: "jest-util@npm:26.6.2" @@ -20270,6 +21049,20 @@ __metadata: languageName: node linkType: hard +"jest-util@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-util@npm:28.1.3" + dependencies: + "@jest/types": ^28.1.3 + "@types/node": "*" + chalk: ^4.0.0 + ci-info: ^3.2.0 + graceful-fs: ^4.2.9 + picomatch: ^2.2.3 + checksum: fd6459742c941f070223f25e38a2ac0719aad92561591e9fb2a50d602a5d19d754750b79b4074327a42b00055662b95da3b006542ceb8b54309da44d4a62e721 + languageName: node + linkType: hard + "jest-validate@npm:^26.5.2, jest-validate@npm:^26.6.2": version: 26.6.2 resolution: "jest-validate@npm:26.6.2" @@ -20298,6 +21091,20 @@ __metadata: languageName: node linkType: hard +"jest-validate@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-validate@npm:28.1.3" + dependencies: + "@jest/types": ^28.1.3 + camelcase: ^6.2.0 + chalk: ^4.0.0 + jest-get-type: ^28.0.2 + leven: ^3.1.0 + pretty-format: ^28.1.3 + checksum: 95e0513b3803c3372a145cda86edbdb33d9dfeaa18818176f2d581e821548ceac9a179f065b6d4671a941de211354efd67f1fff8789a4fb89962565c85f646db + languageName: node + linkType: hard + "jest-watcher@npm:^26.6.2": version: 26.6.2 resolution: "jest-watcher@npm:26.6.2" @@ -20329,6 +21136,22 @@ __metadata: languageName: node linkType: hard +"jest-watcher@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-watcher@npm:28.1.3" + dependencies: + "@jest/test-result": ^28.1.3 + "@jest/types": ^28.1.3 + "@types/node": "*" + ansi-escapes: ^4.2.1 + chalk: ^4.0.0 + emittery: ^0.10.2 + jest-util: ^28.1.3 + string-length: ^4.0.1 + checksum: 8f6d674a4865e7df251f71544f1b51f06fd36b5a3a61f2ac81aeb81fa2a196be354fba51d0f97911c88f67cd254583b3a22ee124bf2c5b6ee2fadec27356c207 + languageName: node + linkType: hard + "jest-worker@npm:^26.0.0, jest-worker@npm:^26.6.2": version: 26.6.2 resolution: "jest-worker@npm:26.6.2" @@ -20362,6 +21185,17 @@ __metadata: languageName: node linkType: hard +"jest-worker@npm:^28.1.3": + version: 28.1.3 + resolution: "jest-worker@npm:28.1.3" + dependencies: + "@types/node": "*" + merge-stream: ^2.0.0 + supports-color: ^8.0.0 + checksum: e921c9a1b8f0909da9ea07dbf3592f95b653aef3a8bb0cbcd20fc7f9a795a1304adecac31eecb308992c167e8d7e75c522061fec38a5928ace0f9571c90169ca + languageName: node + linkType: hard + "jest@npm:26.6.3, jest@npm:^26.6.3": version: 26.6.3 resolution: "jest@npm:26.6.3" @@ -20394,6 +21228,25 @@ __metadata: languageName: node linkType: hard +"jest@npm:^28.1.3": + version: 28.1.3 + resolution: "jest@npm:28.1.3" + dependencies: + "@jest/core": ^28.1.3 + "@jest/types": ^28.1.3 + import-local: ^3.0.2 + jest-cli: ^28.1.3 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: b9dcb542eb7c16261c281cdc2bf37155dbb3f1205bae0b567f05051db362c85ddd4b765f126591efb88f6d298eb10336d0aa6c7d5373b4d53f918137a9a70182 + languageName: node + linkType: hard + "jetifier@npm:^1.6.2, jetifier@npm:^1.6.5": version: 1.6.8 resolution: "jetifier@npm:1.6.8" @@ -23654,6 +24507,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.4": + version: 3.3.4 + resolution: "nanoid@npm:3.3.4" + bin: + nanoid: bin/nanoid.cjs + checksum: 2fddd6dee994b7676f008d3ffa4ab16035a754f4bb586c61df5a22cf8c8c94017aadd360368f47d653829e0569a92b129979152ff97af23a558331e47e37cd9c + languageName: node + linkType: hard + "nanomatch@npm:^1.2.9": version: 1.2.13 resolution: "nanomatch@npm:1.2.13" @@ -24970,7 +25832,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.2": +"p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -25519,6 +26381,21 @@ __metadata: languageName: node linkType: hard +"pdfjs-dist@npm:*, pdfjs-dist@npm:^2.14.305": + version: 2.14.305 + resolution: "pdfjs-dist@npm:2.14.305" + dependencies: + dommatrix: ^1.0.1 + web-streams-polyfill: ^3.2.1 + peerDependencies: + worker-loader: ^3.0.8 + peerDependenciesMeta: + worker-loader: + optional: true + checksum: b75443f81e500856e3a7b61303d1f621f81e82b19fc6216f74d33a70d1ef392bb8b2ca4cfa39f11e7c0a877e6d6d74b474768988dcf3299d5d8a1d996d48f856 + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -25847,7 +26724,61 @@ __metadata: languageName: node linkType: hard -"postcss-value-parser@npm:^4.0.2": +"postcss-modules-extract-imports@npm:^3.0.0": + version: 3.0.0 + resolution: "postcss-modules-extract-imports@npm:3.0.0" + peerDependencies: + postcss: ^8.1.0 + checksum: 4b65f2f1382d89c4bc3c0a1bdc5942f52f3cb19c110c57bd591ffab3a5fee03fcf831604168205b0c1b631a3dce2255c70b61aaae3ef39d69cd7eb450c2552d2 + languageName: node + linkType: hard + +"postcss-modules-local-by-default@npm:^4.0.0": + version: 4.0.0 + resolution: "postcss-modules-local-by-default@npm:4.0.0" + dependencies: + icss-utils: ^5.0.0 + postcss-selector-parser: ^6.0.2 + postcss-value-parser: ^4.1.0 + peerDependencies: + postcss: ^8.1.0 + checksum: 6cf570badc7bc26c265e073f3ff9596b69bb954bc6ac9c5c1b8cba2995b80834226b60e0a3cbb87d5f399dbb52e6466bba8aa1d244f6218f99d834aec431a69d + languageName: node + linkType: hard + +"postcss-modules-scope@npm:^3.0.0": + version: 3.0.0 + resolution: "postcss-modules-scope@npm:3.0.0" + dependencies: + postcss-selector-parser: ^6.0.4 + peerDependencies: + postcss: ^8.1.0 + checksum: 330b9398dbd44c992c92b0dc612c0626135e2cc840fee41841eb61247a6cfed95af2bd6f67ead9dd9d0bb41f5b0367129d93c6e434fa3e9c58ade391d9a5a138 + languageName: node + linkType: hard + +"postcss-modules-values@npm:^4.0.0": + version: 4.0.0 + resolution: "postcss-modules-values@npm:4.0.0" + dependencies: + icss-utils: ^5.0.0 + peerDependencies: + postcss: ^8.1.0 + checksum: f7f2cdf14a575b60e919ad5ea52fed48da46fe80db2733318d71d523fc87db66c835814940d7d05b5746b0426e44661c707f09bdb83592c16aea06e859409db6 + languageName: node + linkType: hard + +"postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4": + version: 6.0.10 + resolution: "postcss-selector-parser@npm:6.0.10" + dependencies: + cssesc: ^3.0.0 + util-deprecate: ^1.0.2 + checksum: 46afaa60e3d1998bd7adf6caa374baf857cc58d3ff944e29459c9a9e4680a7fe41597bd5b755fc81d7c388357e9bf67c0251d047c640a09f148e13606b8a8608 + languageName: node + linkType: hard + +"postcss-value-parser@npm:^4.0.2, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": version: 4.2.0 resolution: "postcss-value-parser@npm:4.2.0" checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f @@ -25876,6 +26807,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.7": + version: 8.4.14 + resolution: "postcss@npm:8.4.14" + dependencies: + nanoid: ^3.3.4 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816 + languageName: node + linkType: hard + "postgres-array@npm:~2.0.0": version: 2.0.0 resolution: "postgres-array@npm:2.0.0" @@ -26106,6 +27048,18 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^28.1.3": + version: 28.1.3 + resolution: "pretty-format@npm:28.1.3" + dependencies: + "@jest/schemas": ^28.1.3 + ansi-regex: ^5.0.1 + ansi-styles: ^5.0.0 + react-is: ^18.0.0 + checksum: e69f857358a3e03d271252d7524bec758c35e44680287f36c1cb905187fbc82da9981a6eb07edfd8a03bc3cbeebfa6f5234c13a3d5b59f2bbdf9b4c4053e0a7f + languageName: node + linkType: hard + "pretty-hrtime@npm:^1.0.0": version: 1.0.3 resolution: "pretty-hrtime@npm:1.0.3" @@ -29280,6 +30234,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.0.2": + version: 1.0.2 + resolution: "source-map-js@npm:1.0.2" + checksum: c049a7fc4deb9a7e9b481ae3d424cc793cb4845daa690bc5a05d428bf41bf231ced49b4cf0c9e77f9d42fdb3d20d6187619fc586605f5eabe995a316da8d377c + languageName: node + linkType: hard + "source-map-loader@npm:^3.0.0": version: 3.0.0 resolution: "source-map-loader@npm:3.0.0" @@ -30203,6 +31164,15 @@ __metadata: languageName: node linkType: hard +"style-loader@npm:^3.3.1": + version: 3.3.1 + resolution: "style-loader@npm:3.3.1" + peerDependencies: + webpack: ^5.0.0 + checksum: 470feef680f59e2fce4d6601b5c55b88c01ad8d1dd693c528ffd591ff5fd7c01a4eff3bdbe62f26f847d6bd2430c9ab594be23307cfe7a3446ab236683f0d066 + languageName: node + linkType: hard + "style-mod@npm:^4.0.0": version: 4.0.0 resolution: "style-mod@npm:4.0.0" @@ -31338,7 +32308,40 @@ __metadata: languageName: node linkType: hard -"ts-loader@npm:^9.3.1": +"ts-jest@npm:^28.0.7": + version: 28.0.7 + resolution: "ts-jest@npm:28.0.7" + dependencies: + bs-logger: 0.x + fast-json-stable-stringify: 2.x + jest-util: ^28.0.0 + json5: ^2.2.1 + lodash.memoize: 4.x + make-error: 1.x + semver: 7.x + yargs-parser: ^21.0.1 + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/types": ^28.0.0 + babel-jest: ^28.0.0 + jest: ^28.0.0 + typescript: ">=4.3" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + bin: + ts-jest: cli.js + checksum: be6ad6382e3b2e7b0c45d06616a4a02aeb6815bad2026fe8eeb4e0941205faa50ac3f5930adb7ba2fda5fea6a5739bfa507e2eac8764d2c729ddc8010681707a + languageName: node + linkType: hard + +"ts-loader@npm:^9.3.0, ts-loader@npm:^9.3.1": version: 9.3.1 resolution: "ts-loader@npm:9.3.1" dependencies: @@ -32308,7 +33311,7 @@ __metadata: languageName: node linkType: hard -"util-deprecate@npm:^1.0.1, util-deprecate@npm:~1.0.1": +"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 @@ -32700,6 +33703,13 @@ __metadata: languageName: node linkType: hard +"web-streams-polyfill@npm:^3.2.1": + version: 3.2.1 + resolution: "web-streams-polyfill@npm:3.2.1" + checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02 + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -32735,6 +33745,39 @@ __metadata: languageName: node linkType: hard +"webpack-cli@npm:^4.10.0": + version: 4.10.0 + resolution: "webpack-cli@npm:4.10.0" + dependencies: + "@discoveryjs/json-ext": ^0.5.0 + "@webpack-cli/configtest": ^1.2.0 + "@webpack-cli/info": ^1.5.0 + "@webpack-cli/serve": ^1.7.0 + colorette: ^2.0.14 + commander: ^7.0.0 + cross-spawn: ^7.0.3 + fastest-levenshtein: ^1.0.12 + import-local: ^3.0.2 + interpret: ^2.2.0 + rechoir: ^0.7.0 + webpack-merge: ^5.7.3 + peerDependencies: + webpack: 4.x.x || 5.x.x + peerDependenciesMeta: + "@webpack-cli/generators": + optional: true + "@webpack-cli/migrate": + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + bin: + webpack-cli: bin/cli.js + checksum: 2ff5355ac348e6b40f2630a203b981728834dca96d6d621be96249764b2d0fc01dd54edfcc37f02214d02935de2cf0eefd6ce689d970d154ef493f01ba922390 + languageName: node + linkType: hard + "webpack-cli@npm:^4.9.1": version: 4.9.1 resolution: "webpack-cli@npm:4.9.1" @@ -32829,7 +33872,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:^5.74.0": +"webpack@npm:^5.73.0, webpack@npm:^5.74.0": version: 5.74.0 resolution: "webpack@npm:5.74.0" dependencies: