From 3bd28d2090883956aed4b1c133bb04f7a2127666 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 26 Aug 2023 14:20:45 -0700 Subject: [PATCH 001/177] Partially building web support --- .eslintignore | 3 + .gitignore | 3 + packages/app-mobile/index.web.js | 15 + packages/app-mobile/package.json | 12 +- .../services/AlarmServiceDriver.web.ts | 33 + .../services/voiceTyping/vosk.web.ts | 35 + packages/app-mobile/setupQuickActions.ts | 6 + packages/app-mobile/utils/fs-driver-web.js | 397 ++++++ packages/app-mobile/utils/fs-driver-web.ts | 371 ++++++ .../app-mobile/utils/shim-init-react.web.ts | 275 ++++ packages/app-mobile/web/index.html | 12 + packages/app-mobile/web/mocks/empty.js | 2 + packages/app-mobile/web/webpack.config.js | 116 ++ packages/lib/shim.ts | 12 +- yarn.lock | 1147 ++++++++++++++++- 15 files changed, 2415 insertions(+), 24 deletions(-) create mode 100644 packages/app-mobile/index.web.js create mode 100644 packages/app-mobile/services/AlarmServiceDriver.web.ts create mode 100644 packages/app-mobile/services/voiceTyping/vosk.web.ts create mode 100644 packages/app-mobile/utils/fs-driver-web.js create mode 100644 packages/app-mobile/utils/fs-driver-web.ts create mode 100644 packages/app-mobile/utils/shim-init-react.web.ts create mode 100644 packages/app-mobile/web/index.html create mode 100644 packages/app-mobile/web/mocks/empty.js create mode 100644 packages/app-mobile/web/webpack.config.js diff --git a/.eslintignore b/.eslintignore index a5e7e5d9fbb..218e73a5906 100644 --- a/.eslintignore +++ b/.eslintignore @@ -444,10 +444,12 @@ packages/app-mobile/gulpfile.js packages/app-mobile/root.js packages/app-mobile/services/AlarmServiceDriver.android.js packages/app-mobile/services/AlarmServiceDriver.ios.js +packages/app-mobile/services/AlarmServiceDriver.web.js packages/app-mobile/services/e2ee/RSA.react-native.js packages/app-mobile/services/profiles/index.js packages/app-mobile/services/voiceTyping/vosk.android.js packages/app-mobile/services/voiceTyping/vosk.ios.js +packages/app-mobile/services/voiceTyping/vosk.web.js packages/app-mobile/setupQuickActions.js packages/app-mobile/tools/buildInjectedJs.js packages/app-mobile/utils/ShareExtension.js @@ -460,6 +462,7 @@ packages/app-mobile/utils/debounce.js packages/app-mobile/utils/fs-driver-rn.js packages/app-mobile/utils/setupNotifications.js packages/app-mobile/utils/shareHandler.js +packages/app-mobile/utils/shim-init-react.web.js packages/app-mobile/utils/types.js packages/fork-htmlparser2/src/CollectingHandler.js packages/fork-htmlparser2/src/FeedHandler.spec.js diff --git a/.gitignore b/.gitignore index d4aa75c28e0..2a37607acdb 100644 --- a/.gitignore +++ b/.gitignore @@ -430,10 +430,12 @@ packages/app-mobile/gulpfile.js packages/app-mobile/root.js packages/app-mobile/services/AlarmServiceDriver.android.js packages/app-mobile/services/AlarmServiceDriver.ios.js +packages/app-mobile/services/AlarmServiceDriver.web.js packages/app-mobile/services/e2ee/RSA.react-native.js packages/app-mobile/services/profiles/index.js packages/app-mobile/services/voiceTyping/vosk.android.js packages/app-mobile/services/voiceTyping/vosk.ios.js +packages/app-mobile/services/voiceTyping/vosk.web.js packages/app-mobile/setupQuickActions.js packages/app-mobile/tools/buildInjectedJs.js packages/app-mobile/utils/ShareExtension.js @@ -446,6 +448,7 @@ packages/app-mobile/utils/debounce.js packages/app-mobile/utils/fs-driver-rn.js packages/app-mobile/utils/setupNotifications.js packages/app-mobile/utils/shareHandler.js +packages/app-mobile/utils/shim-init-react.web.js packages/app-mobile/utils/types.js packages/fork-htmlparser2/src/CollectingHandler.js packages/fork-htmlparser2/src/FeedHandler.spec.js diff --git a/packages/app-mobile/index.web.js b/packages/app-mobile/index.web.js new file mode 100644 index 00000000000..cfbc9c0b2ce --- /dev/null +++ b/packages/app-mobile/index.web.js @@ -0,0 +1,15 @@ + +// Set up required for react-native-drawer-layout (See: https://reactnavigation.org/docs/drawer-layout/ v6.x) +import 'react-native-gesture-handler'; + +import { AppRegistry } from 'react-native'; +const Root = require('./root').default; + +AppRegistry.registerComponent('Joplin', () => Root);//Root); + +addEventListener('DOMContentLoaded', () => { + console.log('DOMContentLoaded ☺'); + AppRegistry.runApplication('Joplin', { + rootTag: document.querySelector('#root'), + }); +}); \ No newline at end of file diff --git a/packages/app-mobile/package.json b/packages/app-mobile/package.json index ba0136f70bb..dc59d85d9a4 100644 --- a/packages/app-mobile/package.json +++ b/packages/app-mobile/package.json @@ -7,6 +7,7 @@ "scripts": { "start": "BROWSERSLIST_IGNORE_OLD_DATA=true react-native start --reset-cache", "android": "react-native run-android", + "web": "webpack --config ./web/webpack.config.js", "build": "gulp build", "tsc": "tsc --project tsconfig.json", "watch": "tsc --watch --preserveWatchOutput --project tsconfig.json", @@ -87,6 +88,7 @@ }, "devDependencies": { "@babel/core": "7.20.2", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", "@babel/preset-env": "7.20.2", "@babel/runtime": "7.20.0", "@codemirror/commands": "6.2.2", @@ -114,7 +116,9 @@ "@types/react-redux": "7.1.25", "@types/tar-stream": "2.2.2", "babel-jest": "29.5.0", + "babel-loader": "9.1.3", "babel-plugin-module-resolver": "4.1.0", + "babel-plugin-react-native-web": "0.19.7", "execa": "4.1.0", "fs-extra": "11.1.1", "gulp": "4.0.2", @@ -125,13 +129,19 @@ "md5-file": "5.0.0", "metro-react-native-babel-preset": "0.73.9", "nodemon": "2.0.22", + "react-dom": "^18.2.0", + "react-native-web": "^0.19.7", "react-test-renderer": "18.2.0", "sqlite3": "5.1.6", + "timers-browserify": "^2.0.12", "ts-jest": "29.1.1", "ts-loader": "9.4.4", "ts-node": "10.9.1", "typescript": "5.1.3", "uglify-js": "3.17.4", - "webpack": "5.74.0" + "url-loader": "4.1.1", + "webpack": "5.74.0", + "webpack-cli": "5.1.4", + "webpack-dev-server": "^4.15.1" } } diff --git a/packages/app-mobile/services/AlarmServiceDriver.web.ts b/packages/app-mobile/services/AlarmServiceDriver.web.ts new file mode 100644 index 00000000000..007914075e7 --- /dev/null +++ b/packages/app-mobile/services/AlarmServiceDriver.web.ts @@ -0,0 +1,33 @@ +import Logger from '@joplin/utils/Logger'; + +export default class AlarmServiceDriver { + public constructor(logger: Logger) { + logger.warn('WARNING: AlarmServiceDriver is not implemented on web'); + } + + public hasPersistentNotifications() { + return false; + } + + public notificationIsSet() { + throw new Error('Available only for non-persistent alarms'); + } + + public setInAppNotificationHandler(_v: any) { + } + + public async hasPermissions(_perm: any = null) { + return false; + } + + public async requestPermissions() { + return false; + } + + public async clearNotification(_id: number) { + } + + public async scheduleNotification(_notification: Notification) { + + } +} diff --git a/packages/app-mobile/services/voiceTyping/vosk.web.ts b/packages/app-mobile/services/voiceTyping/vosk.web.ts new file mode 100644 index 00000000000..9c758811881 --- /dev/null +++ b/packages/app-mobile/services/voiceTyping/vosk.web.ts @@ -0,0 +1,35 @@ +// Currently disabled on web + +type Vosk = any; + +export { Vosk }; + +interface StartOptions { + onResult: (text: string)=> void; +} + +export interface Recorder { + stop: ()=> Promise; + cleanup: ()=> void; +} + +export const isSupportedLanguage = (_locale: string) => { + return false; +}; + +export const modelIsDownloaded = async (_locale: string) => { + return false; +}; + +export const getVosk = async (_locale: string) => { + return {} as any; +}; + +export const startRecording = (_vosk: Vosk, _options: StartOptions): Recorder => { + return { + stop: async () => { return ''; }, + cleanup: () => {}, + }; +}; + +export const voskEnabled = false; diff --git a/packages/app-mobile/setupQuickActions.ts b/packages/app-mobile/setupQuickActions.ts index 29c702ba99a..dcfadda41e7 100644 --- a/packages/app-mobile/setupQuickActions.ts +++ b/packages/app-mobile/setupQuickActions.ts @@ -14,6 +14,12 @@ type TData = { // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied export default (dispatch: Function, folderId: string) => { const userInfo = { url: '' }; + + if (!QuickActions.setShortcutItems) { + console.warn('QuickActions unsupported'); + return; + } + QuickActions.setShortcutItems([ { type: 'New note', title: _('New note'), icon: 'Compose', userInfo }, { type: 'New to-do', title: _('New to-do'), icon: 'Add', userInfo }, diff --git a/packages/app-mobile/utils/fs-driver-web.js b/packages/app-mobile/utils/fs-driver-web.js new file mode 100644 index 00000000000..4e7138aaaf9 --- /dev/null +++ b/packages/app-mobile/utils/fs-driver-web.js @@ -0,0 +1,397 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_driver_base_1 = require("@joplin/lib/fs-driver-base"); +const RNFetchBlob = require('rn-fetch-blob').default; +const RNFS = require("react-native-fs"); +const DocumentPicker = require('react-native-document-picker').default; +const react_native_saf_x_1 = require("@joplin/react-native-saf-x"); +const react_native_saf_x_2 = require("@joplin/react-native-saf-x"); +const react_native_1 = require("react-native"); +const tar = require("tar-stream"); +const path_1 = require("path"); +const buffer_1 = require("buffer"); +const Logger_1 = require("@joplin/utils/Logger"); +const logger = Logger_1.default.create('fs-driver-rn'); +const ANDROID_URI_PREFIX = 'content://'; +function isScopedUri(path) { + return path.includes(ANDROID_URI_PREFIX); +} +class FsDriverRN extends fs_driver_base_1.default { + appendFileSync() { + throw new Error('Not implemented'); + } + // Encoding can be either "utf8" or "base64" + appendFile(path, content, encoding = 'base64') { + if (isScopedUri(path)) { + return react_native_saf_x_2.default.writeFile(path, content, { encoding: encoding, append: true }); + } + return RNFS.appendFile(path, content, encoding); + } + // Encoding can be either "utf8" or "base64" + writeFile(path, content, encoding = 'base64') { + if (isScopedUri(path)) { + return react_native_saf_x_2.default.writeFile(path, content, { encoding: encoding }); + } + // We need to use rn-fetch-blob here due to this bug: + // https://github.com/itinance/react-native-fs/issues/700 + return RNFetchBlob.fs.writeFile(path, content, encoding); + } + // same as rm -rf + remove(path) { + return __awaiter(this, void 0, void 0, function* () { + return yield this.unlink(path); + }); + } + // Returns a format compatible with Node.js format + rnfsStatToStd_(stat, path) { + let birthtime; + const mtime = stat.lastModified ? new Date(stat.lastModified) : stat.mtime; + if (stat.lastModified) { + birthtime = new Date(stat.lastModified); + } + else if (stat.ctime) { + // Confusingly, "ctime" normally means "change time" but here it's used as "creation time". Also sometimes it is null + birthtime = stat.ctime; + } + else { + birthtime = stat.mtime; + } + return { + birthtime, + mtime, + isDirectory: () => stat.type ? stat.type === 'directory' : stat.isDirectory(), + path: path, + size: stat.size, + }; + } + readDirStats(path, options = null) { + return __awaiter(this, void 0, void 0, function* () { + if (!options) + options = {}; + if (!('recursive' in options)) + options.recursive = false; + const isScoped = isScopedUri(path); + let stats = []; + try { + if (isScoped) { + stats = yield react_native_saf_x_2.default.listFiles(path); + } + else { + stats = yield RNFS.readDir(path); + } + } + catch (error) { + throw new Error(`Could not read directory: ${path}: ${error.message}`); + } + let output = []; + for (let i = 0; i < stats.length; i++) { + const stat = stats[i]; + const relativePath = (isScoped ? stat.uri : stat.path).substr(path.length + 1); + const standardStat = this.rnfsStatToStd_(stat, relativePath); + output.push(standardStat); + if (isScoped) { + // readUriDirStatsHandleRecursion_ expects stat to have a URI property. + // Use the original stat. + output = yield this.readUriDirStatsHandleRecursion_(stat, output, options); + } + else { + output = yield this.readDirStatsHandleRecursion_(path, standardStat, output, options); + } + } + return output; + }); + } + readUriDirStatsHandleRecursion_(stat, output, options) { + return __awaiter(this, void 0, void 0, function* () { + if (options.recursive && stat.type === 'directory') { + const subStats = yield this.readDirStats(stat.uri, options); + for (let j = 0; j < subStats.length; j++) { + const subStat = subStats[j]; + output.push(subStat); + } + } + return output; + }); + } + move(source, dest) { + return __awaiter(this, void 0, void 0, function* () { + if (isScopedUri(source) || isScopedUri(dest)) { + yield react_native_saf_x_2.default.moveFile(source, dest, { replaceIfDestinationExists: true }); + } + return RNFS.moveFile(source, dest); + }); + } + rename(source, dest) { + return __awaiter(this, void 0, void 0, function* () { + if (isScopedUri(source) || isScopedUri(dest)) { + yield react_native_saf_x_2.default.rename(source, dest); + } + return RNFS.moveFile(source, dest); + }); + } + exists(path) { + return __awaiter(this, void 0, void 0, function* () { + if (isScopedUri(path)) { + return react_native_saf_x_2.default.exists(path); + } + return RNFS.exists(path); + }); + } + mkdir(path) { + return __awaiter(this, void 0, void 0, function* () { + if (isScopedUri(path)) { + yield react_native_saf_x_2.default.mkdir(path); + return; + } + // Also creates parent directories: Works like mkdir -p + return RNFS.mkdir(path); + }); + } + stat(path) { + return __awaiter(this, void 0, void 0, function* () { + try { + let r; + if (isScopedUri(path)) { + r = yield react_native_saf_x_2.default.stat(path); + } + else { + r = yield RNFS.stat(path); + } + return this.rnfsStatToStd_(r, path); + } + catch (error) { + if (error && (error.code === 'ENOENT' || !(yield this.exists(path)))) { + // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } + // or { [Error: The file {file} couldn’t be opened because there is no such file.], code: 'ENSCOCOAERRORDOMAIN260' } + // which unfortunately does not have a proper error code. Can be ignored. + return null; + } + else { + throw error; + } + } + }); + } + // NOTE: DOES NOT WORK - no error is thrown and the function is called with the right + // arguments but the function returns `false` and the timestamp is not set. + // Current setTimestamp is not really used so keep it that way, but careful if it + // becomes needed. + setTimestamp() { + return __awaiter(this, void 0, void 0, function* () { + // return RNFS.touch(path, timestampDate, timestampDate); + }); + } + open(path, mode) { + return __awaiter(this, void 0, void 0, function* () { + if (isScopedUri(path)) { + throw new Error('open() not implemented in FsDriverAndroid'); + } + // Note: RNFS.read() doesn't provide any way to know if the end of file has been reached. + // So instead we stat the file here and use stat.size to manually check for end of file. + // Bug: https://github.com/itinance/react-native-fs/issues/342 + const stat = yield this.stat(path); + return { + path: path, + offset: 0, + mode: mode, + stat: stat, + }; + }); + } + close() { + // Nothing + return null; + } + readFile(path, encoding = 'utf8') { + if (encoding === 'Buffer') + throw new Error('Raw buffer output not supported for FsDriverRN.readFile'); + if (isScopedUri(path)) { + return react_native_saf_x_2.default.readFile(path, { encoding: encoding }); + } + return RNFS.readFile(path, encoding); + } + // Always overwrite destination + copy(source, dest) { + return __awaiter(this, void 0, void 0, function* () { + let retry = false; + try { + if (isScopedUri(source) || isScopedUri(dest)) { + yield react_native_saf_x_2.default.copyFile(source, dest, { replaceIfDestinationExists: true }); + return; + } + yield RNFS.copyFile(source, dest); + } + catch (error) { + // On iOS it will throw an error if the file already exist + retry = true; + yield this.unlink(dest); + } + if (retry) { + if (isScopedUri(source) || isScopedUri(dest)) { + yield react_native_saf_x_2.default.copyFile(source, dest, { replaceIfDestinationExists: true }); + } + else { + yield RNFS.copyFile(source, dest); + } + } + }); + } + unlink(path) { + return __awaiter(this, void 0, void 0, function* () { + try { + if (isScopedUri(path)) { + yield react_native_saf_x_2.default.unlink(path); + return; + } + yield RNFS.unlink(path); + } + catch (error) { + if (error && ((error.message && error.message.indexOf('exist') >= 0) || error.code === 'ENOENT')) { + // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } + // which unfortunately does not have a proper error code. Can be ignored. + } + else { + throw error; + } + } + }); + } + readFileChunk(handle, length, encoding = 'base64') { + return __awaiter(this, void 0, void 0, function* () { + if (handle.offset + length > handle.stat.size) { + length = handle.stat.size - handle.offset; + } + if (!length) + return null; + const output = yield RNFS.read(handle.path, length, handle.offset, encoding); + // eslint-disable-next-line require-atomic-updates + handle.offset += length; + return output ? output : null; + }); + } + resolve(path) { + throw new Error(`Not implemented: resolve(): ${path}`); + } + resolveRelativePathWithinDir(_baseDir, relativePath) { + throw new Error(`Not implemented: resolveRelativePathWithinDir(): ${relativePath}`); + } + md5File(path) { + return __awaiter(this, void 0, void 0, function* () { + throw new Error(`Not implemented: md5File(): ${path}`); + }); + } + tarExtract(_options) { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('Not implemented: tarExtract'); + }); + } + tarCreate(options, filePaths) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + // Choose a default cwd if not given + const cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : RNFS.DocumentDirectoryPath; + const file = (0, path_1.resolve)(cwd, options.file); + if (yield this.exists(file)) { + throw new Error('Error! Destination already exists'); + } + const pack = tar.pack(); + for (const path of filePaths) { + const absPath = (0, path_1.resolve)(cwd, path); + const stat = yield this.stat(absPath); + const sizeBytes = stat.size; + const entry = pack.entry({ name: path, size: sizeBytes }, (error) => { + if (error) { + logger.error(`Tar error: ${error}`); + } + }); + const chunkSize = 1024 * 100; // 100 KiB + for (let offset = 0; offset < sizeBytes; offset += chunkSize) { + // The RNFS documentation suggests using base64 for binary files. + const part = yield RNFS.read(absPath, chunkSize, offset, 'base64'); + entry.write(buffer_1.Buffer.from(part, 'base64')); + } + entry.end(); + } + pack.finalize(); + // The streams used by tar-stream seem not to support a chunk size + // (it seems despite the typings provided). + let data = null; + while ((data = pack.read()) !== null) { + const buff = buffer_1.Buffer.from(data); + const base64Data = buff.toString('base64'); + yield this.appendFile(file, base64Data, 'base64'); + } + }); + } + getExternalDirectoryPath() { + return __awaiter(this, void 0, void 0, function* () { + let directory; + if (this.isUsingAndroidSAF()) { + const doc = yield (0, react_native_saf_x_2.openDocumentTree)(true); + if (doc === null || doc === void 0 ? void 0 : doc.uri) { + directory = doc === null || doc === void 0 ? void 0 : doc.uri; + } + } + else { + directory = RNFS.ExternalDirectoryPath; + } + return directory; + }); + } + isUsingAndroidSAF() { + return react_native_1.Platform.OS === 'android' && react_native_1.Platform.Version > 28; + } + /** always returns an array */ + pickDocument(options) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const { multiple = false } = options || {}; + let result; + try { + if (this.isUsingAndroidSAF()) { + result = yield (0, react_native_saf_x_1.openDocument)({ multiple }); + if (!result) { + // to catch the error down below using the 'cancel' keyword + throw new Error('User canceled document picker'); + } + result = result.map(r => { + r.type = r.mime; + r.fileCopyUri = r.uri; + return r; + }); + } + else { + // the result is an array + if (multiple) { + result = yield DocumentPicker.pick({ allowMultiSelection: true }); + } + else { + result = [yield DocumentPicker.pick()]; + } + } + } + catch (error) { + if (DocumentPicker.isCancel(error) || ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('cancel'))) { + // eslint-disable-next-line no-console + console.info('pickDocuments: user has cancelled'); + return null; + } + else { + throw error; + } + } + return result; + }); + } +} +exports.default = FsDriverRN; +//# sourceMappingURL=fs-driver-web.js.map \ No newline at end of file diff --git a/packages/app-mobile/utils/fs-driver-web.ts b/packages/app-mobile/utils/fs-driver-web.ts new file mode 100644 index 00000000000..a9e78db3d05 --- /dev/null +++ b/packages/app-mobile/utils/fs-driver-web.ts @@ -0,0 +1,371 @@ +import FsDriverBase, { ReadDirStatsOptions } from '@joplin/lib/fs-driver-base'; +const RNFetchBlob = require('rn-fetch-blob').default; +import * as RNFS from 'react-native-fs'; +const DocumentPicker = require('react-native-document-picker').default; +import { openDocument } from '@joplin/react-native-saf-x'; +import RNSAF, { Encoding, DocumentFileDetail, openDocumentTree } from '@joplin/react-native-saf-x'; +import { Platform } from 'react-native'; +import * as tar from 'tar-stream'; +import { resolve } from 'path'; +import { Buffer } from 'buffer'; +import Logger from '@joplin/utils/Logger'; + +const logger = Logger.create('fs-driver-rn'); + +const ANDROID_URI_PREFIX = 'content://'; + +function isScopedUri(path: string) { + return path.includes(ANDROID_URI_PREFIX); +} + +export default class FsDriverRN extends FsDriverBase { + public appendFileSync() { + throw new Error('Not implemented'); + } + + // Encoding can be either "utf8" or "base64" + public appendFile(path: string, content: any, encoding = 'base64') { + if (isScopedUri(path)) { + return RNSAF.writeFile(path, content, { encoding: encoding as Encoding, append: true }); + } + return RNFS.appendFile(path, content, encoding); + } + + // Encoding can be either "utf8" or "base64" + public writeFile(path: string, content: any, encoding = 'base64') { + if (isScopedUri(path)) { + return RNSAF.writeFile(path, content, { encoding: encoding as Encoding }); + } + // We need to use rn-fetch-blob here due to this bug: + // https://github.com/itinance/react-native-fs/issues/700 + return RNFetchBlob.fs.writeFile(path, content, encoding); + } + + // same as rm -rf + public async remove(path: string) { + return await this.unlink(path); + } + + // Returns a format compatible with Node.js format + private rnfsStatToStd_(stat: any, path: string) { + let birthtime; + const mtime = stat.lastModified ? new Date(stat.lastModified) : stat.mtime; + if (stat.lastModified) { + birthtime = new Date(stat.lastModified); + } else if (stat.ctime) { + // Confusingly, "ctime" normally means "change time" but here it's used as "creation time". Also sometimes it is null + birthtime = stat.ctime; + } else { + birthtime = stat.mtime; + } + return { + birthtime, + mtime, + isDirectory: () => stat.type ? stat.type === 'directory' : stat.isDirectory(), + path: path, + size: stat.size, + }; + } + + public async readDirStats(path: string, options: any = null) { + if (!options) options = {}; + if (!('recursive' in options)) options.recursive = false; + + const isScoped = isScopedUri(path); + + let stats: any[] = []; + try { + if (isScoped) { + stats = await RNSAF.listFiles(path); + } else { + stats = await RNFS.readDir(path); + } + } catch (error) { + throw new Error(`Could not read directory: ${path}: ${error.message}`); + } + + let output: any[] = []; + for (let i = 0; i < stats.length; i++) { + const stat = stats[i]; + const relativePath = (isScoped ? stat.uri : stat.path).substr(path.length + 1); + const standardStat = this.rnfsStatToStd_(stat, relativePath); + output.push(standardStat); + + if (isScoped) { + // readUriDirStatsHandleRecursion_ expects stat to have a URI property. + // Use the original stat. + output = await this.readUriDirStatsHandleRecursion_(stat, output, options); + } else { + output = await this.readDirStatsHandleRecursion_(path, standardStat, output, options); + } + } + return output; + } + + + protected async readUriDirStatsHandleRecursion_(stat: DocumentFileDetail, output: DocumentFileDetail[], options: ReadDirStatsOptions) { + if (options.recursive && stat.type === 'directory') { + const subStats = await this.readDirStats(stat.uri, options); + for (let j = 0; j < subStats.length; j++) { + const subStat = subStats[j]; + output.push(subStat); + } + } + return output; + } + + public async move(source: string, dest: string) { + if (isScopedUri(source) || isScopedUri(dest)) { + await RNSAF.moveFile(source, dest, { replaceIfDestinationExists: true }); + } + return RNFS.moveFile(source, dest); + } + + public async rename(source: string, dest: string) { + if (isScopedUri(source) || isScopedUri(dest)) { + await RNSAF.rename(source, dest); + } + return RNFS.moveFile(source, dest); + } + + public async exists(path: string) { + if (isScopedUri(path)) { + return RNSAF.exists(path); + } + return RNFS.exists(path); + } + + public async mkdir(path: string) { + if (isScopedUri(path)) { + await RNSAF.mkdir(path); + return; + } + + // Also creates parent directories: Works like mkdir -p + return RNFS.mkdir(path); + } + + public async stat(path: string) { + try { + let r; + if (isScopedUri(path)) { + r = await RNSAF.stat(path); + } else { + r = await RNFS.stat(path); + } + return this.rnfsStatToStd_(r, path); + } catch (error) { + if (error && (error.code === 'ENOENT' || !(await this.exists(path)))) { + // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } + // or { [Error: The file {file} couldn’t be opened because there is no such file.], code: 'ENSCOCOAERRORDOMAIN260' } + // which unfortunately does not have a proper error code. Can be ignored. + return null; + } else { + throw error; + } + } + } + + // NOTE: DOES NOT WORK - no error is thrown and the function is called with the right + // arguments but the function returns `false` and the timestamp is not set. + // Current setTimestamp is not really used so keep it that way, but careful if it + // becomes needed. + public async setTimestamp() { + // return RNFS.touch(path, timestampDate, timestampDate); + } + + public async open(path: string, mode: number) { + if (isScopedUri(path)) { + throw new Error('open() not implemented in FsDriverAndroid'); + } + // Note: RNFS.read() doesn't provide any way to know if the end of file has been reached. + // So instead we stat the file here and use stat.size to manually check for end of file. + // Bug: https://github.com/itinance/react-native-fs/issues/342 + const stat = await this.stat(path); + return { + path: path, + offset: 0, + mode: mode, + stat: stat, + }; + } + + public close(): Promise { + // Nothing + return null; + } + + public readFile(path: string, encoding = 'utf8') { + if (encoding === 'Buffer') throw new Error('Raw buffer output not supported for FsDriverRN.readFile'); + if (isScopedUri(path)) { + return RNSAF.readFile(path, { encoding: encoding as Encoding }); + } + return RNFS.readFile(path, encoding); + } + + // Always overwrite destination + public async copy(source: string, dest: string) { + let retry = false; + try { + if (isScopedUri(source) || isScopedUri(dest)) { + await RNSAF.copyFile(source, dest, { replaceIfDestinationExists: true }); + return; + } + await RNFS.copyFile(source, dest); + } catch (error) { + // On iOS it will throw an error if the file already exist + retry = true; + await this.unlink(dest); + } + + if (retry) { + if (isScopedUri(source) || isScopedUri(dest)) { + await RNSAF.copyFile(source, dest, { replaceIfDestinationExists: true }); + } else { + await RNFS.copyFile(source, dest); + } + } + } + + public async unlink(path: string) { + try { + if (isScopedUri(path)) { + await RNSAF.unlink(path); + return; + } + await RNFS.unlink(path); + } catch (error) { + if (error && ((error.message && error.message.indexOf('exist') >= 0) || error.code === 'ENOENT')) { + // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } + // which unfortunately does not have a proper error code. Can be ignored. + } else { + throw error; + } + } + } + + public async readFileChunk(handle: any, length: number, encoding = 'base64') { + if (handle.offset + length > handle.stat.size) { + length = handle.stat.size - handle.offset; + } + + if (!length) return null; + const output = await RNFS.read(handle.path, length, handle.offset, encoding); + // eslint-disable-next-line require-atomic-updates + handle.offset += length; + return output ? output : null; + } + + public resolve(path: string) { + throw new Error(`Not implemented: resolve(): ${path}`); + } + + public resolveRelativePathWithinDir(_baseDir: string, relativePath: string) { + throw new Error(`Not implemented: resolveRelativePathWithinDir(): ${relativePath}`); + } + + public async md5File(path: string): Promise { + throw new Error(`Not implemented: md5File(): ${path}`); + } + + public async tarExtract(_options: any) { + throw new Error('Not implemented: tarExtract'); + } + + public async tarCreate(options: any, filePaths: string[]) { + // Choose a default cwd if not given + const cwd = options.cwd ?? RNFS.DocumentDirectoryPath; + const file = resolve(cwd, options.file); + + if (await this.exists(file)) { + throw new Error('Error! Destination already exists'); + } + + const pack = tar.pack(); + + for (const path of filePaths) { + const absPath = resolve(cwd, path); + const stat = await this.stat(absPath); + const sizeBytes: number = stat.size; + + const entry = pack.entry({ name: path, size: sizeBytes }, (error) => { + if (error) { + logger.error(`Tar error: ${error}`); + } + }); + + const chunkSize = 1024 * 100; // 100 KiB + for (let offset = 0; offset < sizeBytes; offset += chunkSize) { + // The RNFS documentation suggests using base64 for binary files. + const part = await RNFS.read(absPath, chunkSize, offset, 'base64'); + entry.write(Buffer.from(part, 'base64')); + } + entry.end(); + } + + pack.finalize(); + + // The streams used by tar-stream seem not to support a chunk size + // (it seems despite the typings provided). + let data: number[]|null = null; + while ((data = pack.read()) !== null) { + const buff = Buffer.from(data); + const base64Data = buff.toString('base64'); + await this.appendFile(file, base64Data, 'base64'); + } + } + + public async getExternalDirectoryPath(): Promise { + let directory; + if (this.isUsingAndroidSAF()) { + const doc = await openDocumentTree(true); + if (doc?.uri) { + directory = doc?.uri; + } + } else { + directory = RNFS.ExternalDirectoryPath; + } + return directory; + } + + public isUsingAndroidSAF() { + return Platform.OS === 'android' && Platform.Version > 28; + } + + /** always returns an array */ + public async pickDocument(options: { multiple: false }) { + const { multiple = false } = options || {}; + let result; + try { + if (this.isUsingAndroidSAF()) { + result = await openDocument({ multiple }); + if (!result) { + // to catch the error down below using the 'cancel' keyword + throw new Error('User canceled document picker'); + } + result = result.map(r => { + (r.type as string) = r.mime; + ((r as any).fileCopyUri as string) = r.uri; + return r; + }); + } else { + // the result is an array + if (multiple) { + result = await DocumentPicker.pick({ allowMultiSelection: true }); + } else { + result = [await DocumentPicker.pick()]; + } + } + } catch (error) { + if (DocumentPicker.isCancel(error) || error?.message?.includes('cancel')) { + // eslint-disable-next-line no-console + console.info('pickDocuments: user has cancelled'); + return null; + } else { + throw error; + } + } + + return result; + } +} diff --git a/packages/app-mobile/utils/shim-init-react.web.ts b/packages/app-mobile/utils/shim-init-react.web.ts new file mode 100644 index 00000000000..b8ccdd5eac3 --- /dev/null +++ b/packages/app-mobile/utils/shim-init-react.web.ts @@ -0,0 +1,275 @@ +import type ShimType from '@joplin/lib/shim'; + +const shim: typeof ShimType = require('@joplin/lib/shim').default; +const PoorManIntervals = require('@joplin/lib/PoorManIntervals').default; +const RNFetchBlob = require('rn-fetch-blob').default; +import { generateSecureRandom } from 'react-native-securerandom'; +const FsDriverRN = require('./fs-driver-rn').default; +import { Buffer } from 'buffer'; +import { Linking, Platform } from 'react-native'; +const mimeUtils = require('@joplin/lib/mime-utils.js').mime; +import { basename, fileExtension } from '@joplin/lib/path-utils'; +import uuid from '@joplin/lib/uuid'; +import Resource from '@joplin/lib/models/Resource'; +import { getLocales } from 'react-native-localize'; +import { setLocale, defaultLocale, closestSupportedLocale } from '@joplin/lib/locale'; +import FsDriverBase from '@joplin/lib/fs-driver-base'; +import Setting from '@joplin/lib/models/Setting'; + +const injectedJs = { + webviewLib: require('@joplin/lib/rnInjectedJs/webviewLib'), + codeMirrorBundle: require('../lib/rnInjectedJs/CodeMirror.bundle'), +}; + +export const shimInit = () => { + shim.Geolocation = null; + shim.sjclModule = require('@joplin/lib/vendor/sjcl-rn.js'); + + let fsDriver_: FsDriverBase|null = null; + shim.fsDriver = () => { + if (!fsDriver_) { + fsDriver_ = new FsDriverRN(); + } + return fsDriver_; + }; + + shim.randomBytes = async (count: number) => { + const randomBytes = await generateSecureRandom(count); + const temp = []; + for (const n in randomBytes) { + if (!randomBytes.hasOwnProperty(n)) continue; + temp.push(randomBytes[n]); + } + return temp; + }; + + /* eslint-enable */ + + shim.detectAndSetLocale = (settings: typeof Setting) => { + // [ + // { + // "countryCode": "US", + // "isRTL": false, + // "languageCode": "fr", + // "languageTag": "fr-US" + // }, + // { + // "countryCode": "US", + // "isRTL": false, + // "languageCode": "en", + // "languageTag": "en-US" + // } + // ] + + const locales = getLocales(); + let locale = locales.length ? locales[0].languageTag : defaultLocale(); + locale = closestSupportedLocale(locale); + settings.setValue('locale', locale); + setLocale(locale); + return locale; + }; + + shim.fetch = async function(url, options = null) { + // The native fetch() throws an uncatchable error that crashes the + // app if calling it with an invalid URL such as '//.resource' or + // "http://ocloud. de" so detect if the URL is valid beforehand and + // throw a catchable error. Bug: + // https://github.com/facebook/react-native/issues/7436 + let validatedUrl = ''; + try { // Check if the url is valid + validatedUrl = new URL(url).href; + } catch (error) { // If the url is not valid, a TypeError will be thrown + throw new Error(`Not a valid URL: ${url}`); + } + + return shim.fetchWithRetry(() => { + // If the request has a body and it's not a GET call, and it + // doesn't have a Content-Type header we display a warning, + // because it could trigger a "Network request failed" error. + // https://github.com/facebook/react-native/issues/30176 + if (options?.body && options?.method && options.method !== 'GET' && !options?.headers?.['Content-Type']) { + console.warn('Done a non-GET fetch call without a Content-Type header. It may make the request fail.', url, options); + } + + // Among React Native `fetch()` many bugs, one of them is that + // it will truncate strings when they contain binary data. + // Browser fetch() or Node fetch() work fine but as always RN's + // one doesn't. There's no obvious way to fix this so we'll + // have to wait if it's eventually fixed upstream. See here for + // more info: + // https://github.com/laurent22/joplin/issues/3986#issuecomment-718019688 + + return fetch(validatedUrl, options); + }, options); + }; + + shim.fetchBlob = async function(url, options) { + if (!options || !options.path) throw new Error('fetchBlob: target file path is missing'); + + const headers = options.headers ? options.headers : {}; + const method = options.method ? options.method : 'GET'; + const overwrite = 'overwrite' in options ? options.overwrite : true; + + const dirs = RNFetchBlob.fs.dirs; + let localFilePath = options.path; + if (localFilePath.indexOf('/') !== 0) localFilePath = `${dirs.DocumentDir}/${localFilePath}`; + + if (!overwrite) { + if (await shim.fsDriver().exists(localFilePath)) { + return { ok: true }; + } + } + + delete options.path; + delete options.overwrite; + + const doFetchBlob = () => { + return RNFetchBlob.config({ + path: localFilePath, + trusty: options.ignoreTlsErrors, + }).fetch(method, url, headers); + }; + + try { + const response = await shim.fetchWithRetry(doFetchBlob, options); + + // Returns an object that's roughtly compatible with a standard Response object + const output = { + ok: response.respInfo.status < 400, + path: response.data, + status: response.respInfo.status, + headers: response.respInfo.headers, + // If response type is 'path' then calling text() or json() (or base64()) + // on RNFetchBlob response object will make it read the file on the native thread, + // serialize it, and send over the RN bridge. + // For larger files this can cause the app to crash. + // For these type of responses we're not using the response text anyway + // so can override it here to return empty values + text: response.type === 'path' ? () => '' : response.text, + json: response.type === 'path' ? () => {} : response.json, + }; + + return output; + } catch (error) { + throw new Error(`fetchBlob: ${method} ${url}: ${error.toString()}`); + } + }; + + shim.uploadBlob = async function(url, options) { + if (!options || !options.path) throw new Error('uploadBlob: source file path is missing'); + + const headers = options.headers ? options.headers : {}; + const method = options.method ? options.method : 'POST'; + + try { + const response = await RNFetchBlob.config({ + trusty: options.ignoreTlsErrors, + }).fetch(method, url, headers, RNFetchBlob.wrap(options.path)); + + // Returns an object that's roughtly compatible with a standard Response object + return { + ok: response.respInfo.status < 400, + data: response.data, + text: response.text, + json: response.json, + status: response.respInfo.status, + headers: response.respInfo.headers, + }; + } catch (error) { + throw new Error(`uploadBlob: ${method} ${url}: ${error.toString()}`); + } + }; + + shim.readLocalFileBase64 = async function(path) { + return RNFetchBlob.fs.readFile(path, 'base64'); + }; + + shim.stringByteLength = function(string) { + return Buffer.byteLength(string, 'utf-8'); + }; + + shim.Buffer = Buffer; + + shim.openUrl = url => { + return Linking.openURL(url); + }; + + shim.httpAgent = () => { + return null; + }; + + shim.waitForFrame = () => { + return new Promise((resolve) => { + requestAnimationFrame(() => { + resolve(); + }); + }); + }; + + shim.mobilePlatform = () => { + return Platform.OS; + }; + + shim.appVersion = () => { + const p = require('react-native-version-info').default; + return p.appVersion; + }; + + // NOTE: This is a limited version of createResourceFromPath - unlike the Node version, it + // only really works with images. It does not resize the image either. + shim.createResourceFromPath = async function(filePath, defaultProps = null) { + defaultProps = defaultProps ? defaultProps : {}; + const resourceId = defaultProps.id ? defaultProps.id : uuid.create(); + + const ext = fileExtension(filePath); + let mimeType = mimeUtils.fromFileExtension(ext); + if (!mimeType) mimeType = 'image/jpeg'; + + let resource = Resource.new(); + resource.id = resourceId; + resource.mime = mimeType; + resource.title = basename(filePath); + resource.file_extension = ext; + + const targetPath = Resource.fullPath(resource); + await shim.fsDriver().copy(filePath, targetPath); + + if (defaultProps) { + resource = { ...resource, ...defaultProps }; + } + + const itDoes = await shim.fsDriver().waitTillExists(targetPath); + if (!itDoes) throw new Error(`Resource file was not created: ${targetPath}`); + + const fileStat = await shim.fsDriver().stat(targetPath); + resource.size = fileStat.size; + + resource = await Resource.save(resource, { isNew: true }); + + return resource; + }; + + shim.injectedJs = function(name) { + if (!(name in injectedJs)) throw new Error(`Cannot find injectedJs file (add it to "injectedJs" object): ${name}`); + return injectedJs[name]; + }; + + shim.setTimeout = (fn, interval) => { + return PoorManIntervals.setTimeout(fn, interval); + }; + + shim.setInterval = (fn, interval) => { + return PoorManIntervals.setInterval(fn, interval); + }; + + shim.clearTimeout = (id) => { + return PoorManIntervals.clearTimeout(id); + }; + + shim.clearInterval = (id) => { + return PoorManIntervals.clearInterval(id); + }; + +} + +module.exports = { shimInit }; diff --git a/packages/app-mobile/web/index.html b/packages/app-mobile/web/index.html new file mode 100644 index 00000000000..de019ccb225 --- /dev/null +++ b/packages/app-mobile/web/index.html @@ -0,0 +1,12 @@ + + + + + + + +
+ + \ No newline at end of file diff --git a/packages/app-mobile/web/mocks/empty.js b/packages/app-mobile/web/mocks/empty.js new file mode 100644 index 00000000000..ced8860e3b6 --- /dev/null +++ b/packages/app-mobile/web/mocks/empty.js @@ -0,0 +1,2 @@ + +exports.default = {}; \ No newline at end of file diff --git a/packages/app-mobile/web/webpack.config.js b/packages/app-mobile/web/webpack.config.js new file mode 100644 index 00000000000..5919ca5cd83 --- /dev/null +++ b/packages/app-mobile/web/webpack.config.js @@ -0,0 +1,116 @@ +// web/webpack.config.js +// TODO: Copied from https://necolas.github.io/react-native-web/docs/multi-platform/ +// This file should be customised to only include parts we need +// See also https://dev.to/mikehamilton00/adding-web-support-to-a-react-native-project-in-2023-4m4l + +const path = require('path'); +const webpack = require('webpack'); + +const appDirectory = path.resolve(__dirname, '../'); + +const babelConfig = require('../babel.config'); + +// This is needed for webpack to compile JavaScript. +// Many OSS React Native packages are not compiled to ES5 before being +// published. If you depend on uncompiled packages they may cause webpack build +// errors. To fix this webpack can be configured to compile to the necessary +// `node_module`. +const babelLoaderConfiguration = { + test: /\.(tsx|jsx|ts|js)$/, + // Add every directory that needs to be compiled by Babel during the build. + exclude: [ + { + and: [ + path.resolve(appDirectory, 'ios'), + path.resolve(appDirectory, 'android'), + ], + + not: [] + } + //path.resolve(appDirectory, 'node_modules/react-native-uncompiled') + ], + + use: { + loader: 'babel-loader', + options: { + cacheDirectory: true, + presets: babelConfig.presets, + plugins: [ + 'react-native-web', + '@babel/plugin-transform-export-namespace-from', + ...babelConfig.plugins + ] + } + } +}; + +// This is needed for webpack to import static images in JavaScript files. +const imageLoaderConfiguration = { + test: /\.(gif|jpe?g|png|svg)$/, + use: { + loader: 'url-loader', + options: { + name: '[name].[ext]', + esModule: false, + } + } +}; + +module.exports = { + mode: 'development', + + entry: [ + // load any web API polyfills + // path.resolve(appDirectory, 'polyfills-web.js'), + // your web-specific entry file + path.resolve(appDirectory, 'index.web.js') + ], + + // configures where the build ends up + output: { + filename: 'bundle.web.js', + path: path.resolve(appDirectory, 'web/dist'), + }, + + // ...the rest of your config + + module: { + rules: [ + babelLoaderConfiguration, + imageLoaderConfiguration + ] + }, + + resolve: { + // This will only alias the exact import "react-native" + alias: { + 'react-native$': 'react-native-web', + + // Map some modules that don't work on web to the empty dictionary. + 'react-native-fingerprint-scanner': path.resolve(__dirname, 'mocks/empty.js'), + '@joplin/react-native-saf-x': path.resolve(__dirname, 'mocks/empty.js'), + 'react-native-quick-actions': path.resolve(__dirname, 'mocks/empty.js'), + }, + // If you're working on a multi-platform React Native app, web-specific + // module implementations should be written in files using the extension + // `.web.js`. + extensions: [ + '.web.js', + '.js', + '.web.ts', + '.ts', + '.web.jsx', + '.jsx', + '.web.tsx', + '.tsx', + ], + + fallback: { + "url": require.resolve("url/"), + "events": require.resolve("events/"), + "timers": require.resolve("timers-browserify"), + "path": require.resolve("path-browserify"), + "stream": require.resolve("stream-browserify"), + } + } +} diff --git a/packages/lib/shim.ts b/packages/lib/shim.ts index 13592b9facd..ea0c7bff20f 100644 --- a/packages/lib/shim.ts +++ b/packages/lib/shim.ts @@ -219,11 +219,11 @@ const shim = { sjclModule: null as any, - randomBytes: async (_count: number) => { + randomBytes: async (_count: number): Promise => { throw new Error('Not implemented'); }, - stringByteLength: (_s: string) => { + stringByteLength: (_s: string): number => { throw new Error('Not implemented'); }, @@ -252,11 +252,11 @@ const shim = { Buffer: null as any, - openUrl: () => { + openUrl: (url: string): void => { throw new Error('Not implemented'); }, - httpAgent: () => { + httpAgent: (): any => { throw new Error('Not implemented'); }, @@ -264,11 +264,11 @@ const shim = { throw new Error('Not implemented'); }, - waitForFrame: () => { + waitForFrame: (): Promise => { throw new Error('Not implemented'); }, - appVersion: () => { + appVersion: (): string => { throw new Error('Not implemented'); }, diff --git a/yarn.lock b/yarn.lock index 34b56570bfa..160dba52775 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2236,6 +2236,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-export-namespace-from@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3d197b788758044983c96b9c49bed4b456055f35a388521a405968db0f6e2ffb6fd59110e3931f4dcc5e126ae9e5e00e154a0afb47a7ea359d8d0dea79f480d7 + languageName: node + linkType: hard + "@babel/plugin-transform-flow-strip-types@npm:^7.0.0, @babel/plugin-transform-flow-strip-types@npm:^7.18.6": version: 7.19.0 resolution: "@babel/plugin-transform-flow-strip-types@npm:7.19.0" @@ -2800,6 +2812,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.18.6": + version: 7.22.11 + resolution: "@babel/runtime@npm:7.22.11" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: a5cd6683a8fcdb8065cb1677f221e22f6c67ec8f15ad1d273b180b93ab3bd86c66da2c48f500d4e72d8d2cfa85ff4872a3f350e5aa3855630036af5da765c001 + languageName: node + linkType: hard + "@babel/template@npm:^7.0.0, @babel/template@npm:^7.16.0, @babel/template@npm:^7.18.10, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" @@ -4500,6 +4521,7 @@ __metadata: resolution: "@joplin/app-mobile@workspace:packages/app-mobile" dependencies: "@babel/core": 7.20.2 + "@babel/plugin-transform-export-namespace-from": ^7.22.5 "@babel/preset-env": 7.20.2 "@babel/runtime": 7.20.0 "@codemirror/commands": 6.2.2 @@ -4539,7 +4561,9 @@ __metadata: "@types/tar-stream": 2.2.2 assert-browserify: 2.0.0 babel-jest: 29.5.0 + babel-loader: 9.1.3 babel-plugin-module-resolver: 4.1.0 + babel-plugin-react-native-web: 0.19.7 buffer: 6.0.3 constants-browserify: 1.0.0 crypto-browserify: 3.12.0 @@ -4562,6 +4586,7 @@ __metadata: prop-types: 15.8.1 punycode: 2.3.0 react: 18.2.0 + react-dom: ^18.2.0 react-native: 0.71.10 react-native-action-button: 2.8.5 react-native-camera: 4.2.1 @@ -4593,6 +4618,7 @@ __metadata: react-native-vector-icons: 9.2.0 react-native-version-info: 1.1.1 react-native-vosk: 0.1.12 + react-native-web: ^0.19.7 react-native-webview: 12.4.0 react-native-zip-archive: 6.0.9 react-redux: 8.1.1 @@ -4605,13 +4631,17 @@ __metadata: string-natural-compare: 3.0.1 tar-stream: 3.1.6 timers: 0.1.1 + timers-browserify: ^2.0.12 ts-jest: 29.1.1 ts-loader: 9.4.4 ts-node: 10.9.1 typescript: 5.1.3 uglify-js: 3.17.4 url: 0.11.1 + url-loader: 4.1.1 webpack: 5.74.0 + webpack-cli: 5.1.4 + webpack-dev-server: ^4.15.1 languageName: unknown linkType: soft @@ -5137,6 +5167,13 @@ __metadata: languageName: node linkType: hard +"@leichtgewicht/ip-codec@npm:^2.0.1": + version: 2.0.4 + resolution: "@leichtgewicht/ip-codec@npm:2.0.4" + checksum: 468de1f04d33de6d300892683d7c8aecbf96d1e2c5fe084f95f816e50a054d45b7c1ebfb141a1447d844b86a948733f6eebd92234da8581c84a1ad4de2946a2d + languageName: node + linkType: hard + "@lerna/add@npm:3.21.0": version: 3.21.0 resolution: "@lerna/add@npm:3.21.0" @@ -6909,7 +6946,7 @@ __metadata: languageName: node linkType: hard -"@react-native/normalize-color@npm:*, @react-native/normalize-color@npm:2.1.0": +"@react-native/normalize-color@npm:*, @react-native/normalize-color@npm:2.1.0, @react-native/normalize-color@npm:^2.1.0": version: 2.1.0 resolution: "@react-native/normalize-color@npm:2.1.0" checksum: 8ccbd40b3c7629f1dc97b3e9aadd95fd3507fcf2e37535a6299a70436ab891c34cbdc4240b07380553d6e85dd909e23d5773b5be1da2906b026312e0b0768838 @@ -7374,6 +7411,15 @@ __metadata: languageName: node linkType: hard +"@types/bonjour@npm:^3.5.9": + version: 3.5.10 + resolution: "@types/bonjour@npm:3.5.10" + dependencies: + "@types/node": "*" + checksum: bfcadb042a41b124c4e3de4925e3be6d35b78f93f27c4535d5ff86980dc0f8bc407ed99b9b54528952dc62834d5a779392f7a12c2947dd19330eb05a6bcae15a + languageName: node + linkType: hard + "@types/cacheable-request@npm:^6.0.1": version: 6.0.2 resolution: "@types/cacheable-request@npm:6.0.2" @@ -7386,6 +7432,16 @@ __metadata: languageName: node linkType: hard +"@types/connect-history-api-fallback@npm:^1.3.5": + version: 1.5.0 + resolution: "@types/connect-history-api-fallback@npm:1.5.0" + dependencies: + "@types/express-serve-static-core": "*" + "@types/node": "*" + checksum: f180e7c540728d6dd3a1eb2376e445fe7f9de4ee8a5b460d5ad80062cdb6de6efc91c6851f39e9d5933b3dcd5cd370673c52343a959aa091238b6f863ea4447c + languageName: node + linkType: hard + "@types/connect@npm:*": version: 3.4.35 resolution: "@types/connect@npm:3.4.35" @@ -7467,6 +7523,18 @@ __metadata: languageName: node linkType: hard +"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": + version: 4.17.35 + resolution: "@types/express-serve-static-core@npm:4.17.35" + dependencies: + "@types/node": "*" + "@types/qs": "*" + "@types/range-parser": "*" + "@types/send": "*" + checksum: cc8995d10c6feda475ec1b3a0e69eb0f35f21ab6b49129ad5c6f279e0bc5de8175bc04ec51304cb79a43eec3ed2f5a1e01472eb6d5f827b8c35c6ca8ad24eb6e + languageName: node + linkType: hard + "@types/express-serve-static-core@npm:^4.17.18": version: 4.17.26 resolution: "@types/express-serve-static-core@npm:4.17.26" @@ -7490,6 +7558,18 @@ __metadata: languageName: node linkType: hard +"@types/express@npm:^4.17.13": + version: 4.17.17 + resolution: "@types/express@npm:4.17.17" + dependencies: + "@types/body-parser": "*" + "@types/express-serve-static-core": ^4.17.33 + "@types/qs": "*" + "@types/serve-static": "*" + checksum: 0196dacc275ac3ce89d7364885cb08e7fb61f53ca101f65886dbf1daf9b7eb05c0943e2e4bbd01b0cc5e50f37e0eea7e4cbe97d0304094411ac73e1b7998f4da + languageName: node + linkType: hard + "@types/formidable@npm:2.0.6": version: 2.0.6 resolution: "@types/formidable@npm:2.0.6" @@ -7575,6 +7655,15 @@ __metadata: languageName: node linkType: hard +"@types/http-proxy@npm:^1.17.8": + version: 1.17.11 + resolution: "@types/http-proxy@npm:1.17.11" + dependencies: + "@types/node": "*" + checksum: 38ef4f8c91c7a5b664cf6dd4d90de7863f88549a9f8ef997f2f1184e4f8cf2e7b9b63c04f0b7b962f34a09983073a31a9856de5aae5159b2ddbb905a4c44dc9f + languageName: node + linkType: hard + "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.3 resolution: "@types/istanbul-lib-coverage@npm:2.0.3" @@ -7769,6 +7858,13 @@ __metadata: languageName: node linkType: hard +"@types/mime@npm:*": + version: 3.0.1 + resolution: "@types/mime@npm:3.0.1" + checksum: 4040fac73fd0cea2460e29b348c1a6173da747f3a87da0dbce80dd7a9355a3d0e51d6d9a401654f3e5550620e3718b5a899b2ec1debf18424e298a2c605346e7 + languageName: node + linkType: hard + "@types/mime@npm:^1": version: 1.3.2 resolution: "@types/mime@npm:1.3.2" @@ -8047,6 +8143,13 @@ __metadata: languageName: node linkType: hard +"@types/retry@npm:0.12.0": + version: 0.12.0 + resolution: "@types/retry@npm:0.12.0" + checksum: 61a072c7639f6e8126588bf1eb1ce8835f2cb9c2aba795c4491cf6310e013267b0c8488039857c261c387e9728c1b43205099223f160bb6a76b4374f741b5603 + languageName: node + linkType: hard + "@types/scheduler@npm:*": version: 0.16.2 resolution: "@types/scheduler@npm:0.16.2" @@ -8061,6 +8164,25 @@ __metadata: languageName: node linkType: hard +"@types/send@npm:*": + version: 0.17.1 + resolution: "@types/send@npm:0.17.1" + dependencies: + "@types/mime": ^1 + "@types/node": "*" + checksum: 10b620a5960058ef009afbc17686f680d6486277c62f640845381ec4baa0ea683fdd77c3afea4803daf5fcddd3fb2972c8aa32e078939f1d4e96f83195c89793 + languageName: node + linkType: hard + +"@types/serve-index@npm:^1.9.1": + version: 1.9.1 + resolution: "@types/serve-index@npm:1.9.1" + dependencies: + "@types/express": "*" + checksum: 026f3995fb500f6df7c3fe5009e53bad6d739e20b84089f58ebfafb2f404bbbb6162bbe33f72d2f2af32d5b8d3799c8e179793f90d9ed5871fb8591190bb6056 + languageName: node + linkType: hard + "@types/serve-static@npm:*": version: 1.13.10 resolution: "@types/serve-static@npm:1.13.10" @@ -8071,6 +8193,26 @@ __metadata: languageName: node linkType: hard +"@types/serve-static@npm:^1.13.10": + version: 1.15.2 + resolution: "@types/serve-static@npm:1.15.2" + dependencies: + "@types/http-errors": "*" + "@types/mime": "*" + "@types/node": "*" + checksum: 15c261dbfc57890f7cc17c04d5b22b418dfa0330c912b46c5d8ae2064da5d6f844ef7f41b63c7f4bbf07675e97ebe6ac804b032635ec742ae45d6f1274259b3e + languageName: node + linkType: hard + +"@types/sockjs@npm:^0.3.33": + version: 0.3.33 + resolution: "@types/sockjs@npm:0.3.33" + dependencies: + "@types/node": "*" + checksum: b9bbb2b5c5ead2fb884bb019f61a014e37410bddd295de28184e1b2e71ee6b04120c5ba7b9954617f0bdf962c13d06249ce65004490889c747c80d3f628ea842 + languageName: node + linkType: hard + "@types/stack-utils@npm:^2.0.0": version: 2.0.1 resolution: "@types/stack-utils@npm:2.0.1" @@ -8126,6 +8268,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.5": + version: 8.5.5 + resolution: "@types/ws@npm:8.5.5" + dependencies: + "@types/node": "*" + checksum: d00bf8070e6938e3ccf933010921c6ce78ac3606696ce37a393b27a9a603f7bd93ea64f3c5fa295a2f743575ba9c9a9fdb904af0f5fe2229bf2adf0630386e4a + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 20.2.1 resolution: "@types/yargs-parser@npm:20.2.1" @@ -8728,6 +8879,16 @@ __metadata: languageName: node linkType: hard +"@webpack-cli/configtest@npm:^2.1.1": + version: 2.1.1 + resolution: "@webpack-cli/configtest@npm:2.1.1" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + checksum: 9f9f9145c2d05471fc83d426db1df85cf49f329836b0c4b9f46b6948bed4b013464c00622b136d2a0a26993ce2306976682592245b08ee717500b1db45009a72 + languageName: node + linkType: hard + "@webpack-cli/info@npm:^1.5.0": version: 1.5.0 resolution: "@webpack-cli/info@npm:1.5.0" @@ -8739,6 +8900,16 @@ __metadata: languageName: node linkType: hard +"@webpack-cli/info@npm:^2.0.2": + version: 2.0.2 + resolution: "@webpack-cli/info@npm:2.0.2" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + checksum: 8f9a178afca5c82e113aed1efa552d64ee5ae4fdff63fe747c096a981ec74f18a5d07bd6e89bbe6715c3e57d96eea024a410e58977169489fe1df044c10dd94e + languageName: node + linkType: hard + "@webpack-cli/serve@npm:^1.7.0": version: 1.7.0 resolution: "@webpack-cli/serve@npm:1.7.0" @@ -8751,6 +8922,19 @@ __metadata: languageName: node linkType: hard +"@webpack-cli/serve@npm:^2.0.5": + version: 2.0.5 + resolution: "@webpack-cli/serve@npm:2.0.5" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + peerDependenciesMeta: + webpack-dev-server: + optional: true + checksum: 75f0e54681796d567a71ac3e2781d2901a8d8cf1cdfc82f261034dddac59a8343e8c3bc5e32b4bb9d6766759ba49fb29a5cd86ef1701d79c506fe886bb63ac75 + languageName: node + linkType: hard + "@xmldom/xmldom@npm:^0.8.8": version: 0.8.8 resolution: "@xmldom/xmldom@npm:0.8.8" @@ -8849,6 +9033,16 @@ __metadata: languageName: node linkType: hard +"accepts@npm:~1.3.4, accepts@npm:~1.3.8": + version: 1.3.8 + resolution: "accepts@npm:1.3.8" + dependencies: + mime-types: ~2.1.34 + negotiator: 0.6.3 + checksum: 50c43d32e7b50285ebe84b613ee4a3aa426715a7d131b65b786e2ead0fd76b6b60091b9916d3478a75f11f162628a2139991b6c03ab3f1d9ab7c86075dc8eab4 + languageName: node + linkType: hard + "acorn-globals@npm:^6.0.0": version: 6.0.0 resolution: "acorn-globals@npm:6.0.0" @@ -9052,6 +9246,20 @@ __metadata: languageName: node linkType: hard +"ajv-formats@npm:^2.1.1": + version: 2.1.1 + resolution: "ajv-formats@npm:2.1.1" + dependencies: + ajv: ^8.0.0 + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + checksum: 4a287d937f1ebaad4683249a4c40c0fa3beed30d9ddc0adba04859026a622da0d317851316ea64b3680dc60f5c3c708105ddd5d5db8fe595d9d0207fd19f90b7 + languageName: node + linkType: hard + "ajv-keywords@npm:^3.4.1, ajv-keywords@npm:^3.5.2": version: 3.5.2 resolution: "ajv-keywords@npm:3.5.2" @@ -9061,6 +9269,17 @@ __metadata: languageName: node linkType: hard +"ajv-keywords@npm:^5.1.0": + version: 5.1.0 + resolution: "ajv-keywords@npm:5.1.0" + dependencies: + fast-deep-equal: ^3.1.3 + peerDependencies: + ajv: ^8.8.2 + checksum: c35193940b853119242c6757787f09ecf89a2c19bcd36d03ed1a615e710d19d450cb448bfda407b939aba54b002368c8bff30529cc50a0536a8e10bcce300421 + languageName: node + linkType: hard + "ajv@npm:^6.10.0, ajv@npm:^6.12.0, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -9073,6 +9292,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^8.0.0, ajv@npm:^8.9.0": + version: 8.12.0 + resolution: "ajv@npm:8.12.0" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 + languageName: node + linkType: hard + "ajv@npm:^8.0.1": version: 8.8.2 resolution: "ajv@npm:8.8.2" @@ -9171,6 +9402,15 @@ __metadata: languageName: node linkType: hard +"ansi-html-community@npm:^0.0.8": + version: 0.0.8 + resolution: "ansi-html-community@npm:0.0.8" + bin: + ansi-html: bin/ansi-html + checksum: 04c568e8348a636963f915e48eaa3e01218322e1169acafdd79c384f22e5558c003f79bbc480c1563865497482817c7eed025f0653ebc17642fededa5cb42089 + languageName: node + linkType: hard + "ansi-regex@npm:^2.0.0": version: 2.1.1 resolution: "ansi-regex@npm:2.1.1" @@ -9562,6 +9802,20 @@ __metadata: languageName: node linkType: hard +"array-flatten@npm:1.1.1": + version: 1.1.1 + resolution: "array-flatten@npm:1.1.1" + checksum: a9925bf3512d9dce202112965de90c222cd59a4fbfce68a0951d25d965cf44642931f40aac72309c41f12df19afa010ecadceb07cfff9ccc1621e99d89ab5f3b + languageName: node + linkType: hard + +"array-flatten@npm:^2.1.2": + version: 2.1.2 + resolution: "array-flatten@npm:2.1.2" + checksum: e8988aac1fbfcdaae343d08c9a06a6fddd2c6141721eeeea45c3cf523bf4431d29a46602929455ed548c7a3e0769928cdc630405427297e7081bd118fdec9262 + languageName: node + linkType: hard + "array-ify@npm:^1.0.0": version: 1.0.0 resolution: "array-ify@npm:1.0.0" @@ -10041,6 +10295,19 @@ __metadata: languageName: node linkType: hard +"babel-loader@npm:9.1.3": + version: 9.1.3 + resolution: "babel-loader@npm:9.1.3" + dependencies: + find-cache-dir: ^4.0.0 + schema-utils: ^4.0.0 + peerDependencies: + "@babel/core": ^7.12.0 + webpack: ">=5" + checksum: b168dde5b8cf11206513371a79f86bb3faa7c714e6ec9fffd420876b61f3d7f5f4b976431095ef6a14bc4d324505126deb91045fd41e312ba49f4deaa166fe28 + languageName: node + linkType: hard + "babel-plugin-istanbul@npm:^6.1.1": version: 6.1.1 resolution: "babel-plugin-istanbul@npm:6.1.1" @@ -10162,6 +10429,13 @@ __metadata: languageName: node linkType: hard +"babel-plugin-react-native-web@npm:0.19.7": + version: 0.19.7 + resolution: "babel-plugin-react-native-web@npm:0.19.7" + checksum: c85e9800c119c5ff57b4e9a04b12a59284b148a53caa40b800947731152a4f746a6446110e0b875a89e403d084ad632e693e25b786b6920f85af84ab7027aa51 + languageName: node + linkType: hard + "babel-plugin-styled-components@npm:>= 1.12.0": version: 2.0.7 resolution: "babel-plugin-styled-components@npm:2.0.7" @@ -10338,6 +10612,13 @@ __metadata: languageName: node linkType: hard +"batch@npm:0.6.1": + version: 0.6.1 + resolution: "batch@npm:0.6.1" + checksum: 61f9934c7378a51dce61b915586191078ef7f1c3eca707fdd58b96ff2ff56d9e0af2bdab66b1462301a73c73374239e6542d9821c0af787f3209a23365d07e7f + languageName: node + linkType: hard + "bcrypt-pbkdf@npm:^1.0.0": version: 1.0.2 resolution: "bcrypt-pbkdf@npm:1.0.2" @@ -10368,6 +10649,13 @@ __metadata: languageName: node linkType: hard +"big.js@npm:^5.2.2": + version: 5.2.2 + resolution: "big.js@npm:5.2.2" + checksum: b89b6e8419b097a8fb4ed2399a1931a68c612bce3cfd5ca8c214b2d017531191070f990598de2fc6f3f993d91c0f08aa82697717f6b3b8732c9731866d233c9e + languageName: node + linkType: hard + "binary-extensions@npm:^1.0.0": version: 1.13.1 resolution: "binary-extensions@npm:1.13.1" @@ -10457,6 +10745,38 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:1.20.1": + version: 1.20.1 + resolution: "body-parser@npm:1.20.1" + dependencies: + bytes: 3.1.2 + content-type: ~1.0.4 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.1 + type-is: ~1.6.18 + unpipe: 1.0.0 + checksum: f1050dbac3bede6a78f0b87947a8d548ce43f91ccc718a50dd774f3c81f2d8b04693e52acf62659fad23101827dd318da1fb1363444ff9a8482b886a3e4a5266 + languageName: node + linkType: hard + +"bonjour-service@npm:^1.0.11": + version: 1.1.1 + resolution: "bonjour-service@npm:1.1.1" + dependencies: + array-flatten: ^2.1.2 + dns-equal: ^1.0.0 + fast-deep-equal: ^3.1.3 + multicast-dns: ^7.2.5 + checksum: 832d0cf78b91368fac8bb11fd7a714e46f4c4fb1bb14d7283bce614a6fb3aae2f3fe209aba5b4fa051811c1cab6921d073a83db8432fb23292f27dd4161fb0f1 + languageName: node + linkType: hard + "boolean@npm:^3.0.1": version: 3.1.4 resolution: "boolean@npm:3.1.4" @@ -11440,7 +11760,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.5.3, chokidar@npm:^3.3.0": +"chokidar@npm:3.5.3, chokidar@npm:^3.3.0, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -12005,6 +12325,13 @@ __metadata: languageName: node linkType: hard +"colorette@npm:^2.0.10": + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d + languageName: node + linkType: hard + "colorette@npm:^2.0.14": version: 2.0.16 resolution: "colorette@npm:2.0.16" @@ -12090,6 +12417,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^10.0.1": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 436901d64a818295803c1996cd856621a74f30b9f9e28a588e726b2b1670665bccd7c1a77007ebf328729f0139838a88a19265858a0fa7a8728c4656796db948 + languageName: node + linkType: hard + "commander@npm:^5.0.0": version: 5.1.0 resolution: "commander@npm:5.1.0" @@ -12199,7 +12533,7 @@ __metadata: languageName: node linkType: hard -"compression@npm:^1.7.1": +"compression@npm:^1.7.1, compression@npm:^1.7.4": version: 1.7.4 resolution: "compression@npm:1.7.4" dependencies: @@ -12290,6 +12624,13 @@ __metadata: languageName: node linkType: hard +"connect-history-api-fallback@npm:^2.0.0": + version: 2.0.0 + resolution: "connect-history-api-fallback@npm:2.0.0" + checksum: dc5368690f4a5c413889792f8df70d5941ca9da44523cde3f87af0745faee5ee16afb8195434550f0504726642734f2683d6c07f8b460f828a12c45fbd4c9a68 + languageName: node + linkType: hard + "connect@npm:^3.6.5": version: 3.7.0 resolution: "connect@npm:3.7.0" @@ -12334,6 +12675,15 @@ __metadata: languageName: node linkType: hard +"content-disposition@npm:0.5.4": + version: 0.5.4 + resolution: "content-disposition@npm:0.5.4" + dependencies: + safe-buffer: 5.2.1 + checksum: afb9d545e296a5171d7574fcad634b2fdf698875f4006a9dd04a3e1333880c5c0c98d47b560d01216fb6505a54a2ba6a843ee3a02ec86d7e911e8315255f56c3 + languageName: node + linkType: hard + "content-disposition@npm:^0.5.3, content-disposition@npm:~0.5.2": version: 0.5.3 resolution: "content-disposition@npm:0.5.3" @@ -12350,7 +12700,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:^1.0.5": +"content-type@npm:^1.0.5, content-type@npm:~1.0.4": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 @@ -12489,6 +12839,20 @@ __metadata: languageName: node linkType: hard +"cookie-signature@npm:1.0.6": + version: 1.0.6 + resolution: "cookie-signature@npm:1.0.6" + checksum: f4e1b0a98a27a0e6e66fd7ea4e4e9d8e038f624058371bf4499cfcd8f3980be9a121486995202ba3fca74fbed93a407d6d54d43a43f96fd28d0bd7a06761591a + languageName: node + linkType: hard + +"cookie@npm:0.5.0": + version: 0.5.0 + resolution: "cookie@npm:0.5.0" + checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 + languageName: node + linkType: hard + "cookies@npm:~0.8.0": version: 0.8.0 resolution: "cookies@npm:0.8.0" @@ -12721,6 +13085,15 @@ __metadata: languageName: node linkType: hard +"cross-fetch@npm:^3.1.5": + version: 3.1.8 + resolution: "cross-fetch@npm:3.1.8" + dependencies: + node-fetch: ^2.6.12 + checksum: 78f993fa099eaaa041122ab037fe9503ecbbcb9daef234d1d2e0b9230a983f64d645d088c464e21a247b825a08dc444a6e7064adfa93536d3a9454b4745b3632 + languageName: node + linkType: hard + "cross-spawn@npm:^6.0.0": version: 6.0.5 resolution: "cross-spawn@npm:6.0.5" @@ -12876,6 +13249,15 @@ __metadata: languageName: node linkType: hard +"css-in-js-utils@npm:^3.1.0": + version: 3.1.0 + resolution: "css-in-js-utils@npm:3.1.0" + dependencies: + hyphenate-style-name: ^1.0.3 + checksum: 066318e918c04a5e5bce46b38fe81052ea6ac051bcc6d3c369a1d59ceb1546cb2b6086901ab5d22be084122ee3732169996a3dfb04d3406eaee205af77aec61b + languageName: node + linkType: hard + "css-loader@npm:6.8.1": version: 6.8.1 resolution: "css-loader@npm:6.8.1" @@ -14004,6 +14386,15 @@ __metadata: languageName: node linkType: hard +"default-gateway@npm:^6.0.3": + version: 6.0.3 + resolution: "default-gateway@npm:6.0.3" + dependencies: + execa: ^5.0.0 + checksum: 126f8273ecac8ee9ff91ea778e8784f6cd732d77c3157e8c5bdd6ed03651b5291f71446d05bc02d04073b1e67583604db5394ea3cf992ede0088c70ea15b7378 + languageName: node + linkType: hard + "default-require-extensions@npm:^3.0.0": version: 3.0.1 resolution: "default-require-extensions@npm:3.0.1" @@ -14216,6 +14607,13 @@ __metadata: languageName: node linkType: hard +"destroy@npm:1.2.0": + version: 1.2.0 + resolution: "destroy@npm:1.2.0" + checksum: 0acb300b7478a08b92d810ab229d5afe0d2f4399272045ab22affa0d99dbaf12637659411530a6fcd597a9bdac718fc94373a61a95b4651bbc7b83684a565e38 + languageName: node + linkType: hard + "destroy@npm:^1.0.4, destroy@npm:~1.0.4": version: 1.0.4 resolution: "destroy@npm:1.0.4" @@ -14585,6 +14983,22 @@ __metadata: languageName: node linkType: hard +"dns-equal@npm:^1.0.0": + version: 1.0.0 + resolution: "dns-equal@npm:1.0.0" + checksum: a8471ac849c7c13824f053babea1bc26e2f359394dd5a460f8340d8abd13434be01e3327a5c59d212f8c8997817450efd3f3ac77bec709b21979cf0235644524 + languageName: node + linkType: hard + +"dns-packet@npm:^5.2.2": + version: 5.6.0 + resolution: "dns-packet@npm:5.6.0" + dependencies: + "@leichtgewicht/ip-codec": ^2.0.1 + checksum: 1b643814e5947a87620f8a906287079347492282964ce1c236d52c414e3e3941126b96581376b180ba6e66899e70b86b587bc1aa23e3acd9957765be952d83fc + languageName: node + linkType: hard + "doctrine@npm:^2.1.0": version: 2.1.0 resolution: "doctrine@npm:2.1.0" @@ -15023,6 +15437,13 @@ __metadata: languageName: node linkType: hard +"emojis-list@npm:^3.0.0": + version: 3.0.0 + resolution: "emojis-list@npm:3.0.0" + checksum: ddaaa02542e1e9436c03970eeed445f4ed29a5337dfba0fe0c38dfdd2af5da2429c2a0821304e8a8d1cadf27fdd5b22ff793571fa803ae16852a6975c65e8e70 + languageName: node + linkType: hard + "emphasize@npm:^1.5.0": version: 1.5.0 resolution: "emphasize@npm:1.5.0" @@ -16275,6 +16696,45 @@ __metadata: languageName: node linkType: hard +"express@npm:^4.17.3": + version: 4.18.2 + resolution: "express@npm:4.18.2" + dependencies: + accepts: ~1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.1 + content-disposition: 0.5.4 + content-type: ~1.0.4 + cookie: 0.5.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: ~1.0.2 + escape-html: ~1.0.3 + etag: ~1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: ~1.1.2 + on-finished: 2.4.1 + parseurl: ~1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: ~2.0.7 + qs: 6.11.0 + range-parser: ~1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: ~1.6.18 + utils-merge: 1.0.1 + vary: ~1.1.2 + checksum: 3c4b9b076879442f6b968fe53d85d9f1eeacbb4f4c41e5f16cc36d77ce39a2b0d81b3f250514982110d815b2f7173f5561367f9110fcc541f9371948e8c8b037 + languageName: node + linkType: hard + "ext@npm:^1.1.2": version: 1.6.0 resolution: "ext@npm:1.6.0" @@ -16483,6 +16943,13 @@ __metadata: languageName: node linkType: hard +"fast-loops@npm:^1.1.3": + version: 1.1.3 + resolution: "fast-loops@npm:1.1.3" + checksum: b674378ba2ed8364ca1a00768636e88b22201c8d010fa62a8588a4cace04f90bac46714c13cf638be82b03438d2fe813600da32291fb47297a1bd7fa6cef0cee + languageName: node + linkType: hard + "fast-safe-stringify@npm:^2.0.7": version: 2.1.1 resolution: "fast-safe-stringify@npm:2.1.1" @@ -16548,6 +17015,15 @@ __metadata: languageName: node linkType: hard +"faye-websocket@npm:^0.11.3": + version: 0.11.4 + resolution: "faye-websocket@npm:0.11.4" + dependencies: + websocket-driver: ">=0.5.1" + checksum: d49a62caf027f871149fc2b3f3c7104dc6d62744277eb6f9f36e2d5714e847d846b9f7f0d0b7169b25a012e24a594cde11a93034b30732e4c683f20b8a5019fa + languageName: node + linkType: hard + "fb-watchman@npm:^2.0.0": version: 2.0.1 resolution: "fb-watchman@npm:2.0.1" @@ -16557,6 +17033,13 @@ __metadata: languageName: node linkType: hard +"fbjs-css-vars@npm:^1.0.0": + version: 1.0.2 + resolution: "fbjs-css-vars@npm:1.0.2" + checksum: 72baf6d22c45b75109118b4daecb6c8016d4c83c8c0f23f683f22e9d7c21f32fff6201d288df46eb561e3c7d4bb4489b8ad140b7f56444c453ba407e8bd28511 + languageName: node + linkType: hard + "fbjs@npm:^0.8.9": version: 0.8.18 resolution: "fbjs@npm:0.8.18" @@ -16572,6 +17055,21 @@ __metadata: languageName: node linkType: hard +"fbjs@npm:^3.0.4": + version: 3.0.5 + resolution: "fbjs@npm:3.0.5" + dependencies: + cross-fetch: ^3.1.5 + fbjs-css-vars: ^1.0.0 + loose-envify: ^1.0.0 + object-assign: ^4.1.0 + promise: ^7.1.1 + setimmediate: ^1.0.5 + ua-parser-js: ^1.0.35 + checksum: e609b5b64686bc96495a5c67728ed9b2710b9b3d695c5759c5f5e47c9483d1c323543ac777a86459e3694efc5712c6ce7212e944feb19752867d699568bb0e54 + languageName: node + linkType: hard + "fd-slicer@npm:~1.1.0": version: 1.1.0 resolution: "fd-slicer@npm:1.1.0" @@ -16695,6 +17193,21 @@ __metadata: languageName: node linkType: hard +"finalhandler@npm:1.2.0": + version: 1.2.0 + resolution: "finalhandler@npm:1.2.0" + dependencies: + debug: 2.6.9 + encodeurl: ~1.0.2 + escape-html: ~1.0.3 + on-finished: 2.4.1 + parseurl: ~1.3.3 + statuses: 2.0.1 + unpipe: ~1.0.0 + checksum: 92effbfd32e22a7dff2994acedbd9bcc3aa646a3e919ea6a53238090e87097f8ef07cced90aa2cc421abdf993aefbdd5b00104d55c7c5479a8d00ed105b45716 + languageName: node + linkType: hard + "find-babel-config@npm:^1.2.0": version: 1.2.0 resolution: "find-babel-config@npm:1.2.0" @@ -17090,6 +17603,13 @@ __metadata: languageName: node linkType: hard +"forwarded@npm:0.2.0": + version: 0.2.0 + resolution: "forwarded@npm:0.2.0" + checksum: fd27e2394d8887ebd16a66ffc889dc983fbbd797d5d3f01087c020283c0f019a7d05ee85669383d8e0d216b116d720fc0cef2f6e9b7eb9f4c90c6e0bc7fd28e6 + languageName: node + linkType: hard + "fragment-cache@npm:^0.2.1": version: 0.2.1 resolution: "fragment-cache@npm:0.2.1" @@ -17243,6 +17763,13 @@ __metadata: languageName: node linkType: hard +"fs-monkey@npm:^1.0.4": + version: 1.0.4 + resolution: "fs-monkey@npm:1.0.4" + checksum: 8b254c982905c0b7e028eab22b410dc35a5c0019c1c860456f5f54ae6a61666e1cb8c6b700d6c88cc873694c00953c935847b9959cc4dcf274aacb8673c1e8bf + languageName: node + linkType: hard + "fs-write-stream-atomic@npm:^1.0.8": version: 1.0.10 resolution: "fs-write-stream-atomic@npm:1.0.10" @@ -18188,6 +18715,13 @@ __metadata: languageName: node linkType: hard +"handle-thing@npm:^2.0.0": + version: 2.0.1 + resolution: "handle-thing@npm:2.0.1" + checksum: 68071f313062315cd9dce55710e9496873945f1dd425107007058fc1629f93002a7649fcc3e464281ce02c7e809a35f5925504ab8105d972cf649f1f47cb7d6c + languageName: node + linkType: hard + "handlebars@npm:^4.7.6, handlebars@npm:^4.7.7": version: 4.7.7 resolution: "handlebars@npm:4.7.7" @@ -18527,6 +19061,18 @@ __metadata: languageName: node linkType: hard +"hpack.js@npm:^2.1.6": + version: 2.1.6 + resolution: "hpack.js@npm:2.1.6" + dependencies: + inherits: ^2.0.1 + obuf: ^1.0.0 + readable-stream: ^2.0.1 + wbuf: ^1.1.0 + checksum: 2de144115197967ad6eeee33faf41096c6ba87078703c5cb011632dcfbffeb45784569e0cf02c317bd79c48375597c8ec88c30fff5bb0b023e8f654fb6e9c06e + languageName: node + linkType: hard + "hpagent@npm:1.2.0": version: 1.2.0 resolution: "hpagent@npm:1.2.0" @@ -18559,6 +19105,13 @@ __metadata: languageName: node linkType: hard +"html-entities@npm:^2.3.2": + version: 2.4.0 + resolution: "html-entities@npm:2.4.0" + checksum: 25bea32642ce9ebd0eedc4d24381883ecb0335ccb8ac26379a0958b9b16652fdbaa725d70207ce54a51db24103436a698a8e454397d3ba8ad81460224751f1dc + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -18635,6 +19188,13 @@ __metadata: languageName: node linkType: hard +"http-deceiver@npm:^1.2.7": + version: 1.2.7 + resolution: "http-deceiver@npm:1.2.7" + checksum: 64d7d1ae3a6933eb0e9a94e6f27be4af45a53a96c3c34e84ff57113787105a89fff9d1c3df263ef63add823df019b0e8f52f7121e32393bb5ce9a713bf100b41 + languageName: node + linkType: hard + "http-errors@npm:2.0.0": version: 2.0.0 resolution: "http-errors@npm:2.0.0" @@ -18661,6 +19221,18 @@ __metadata: languageName: node linkType: hard +"http-errors@npm:~1.6.2": + version: 1.6.3 + resolution: "http-errors@npm:1.6.3" + dependencies: + depd: ~1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: ">= 1.4.0 < 2" + checksum: a9654ee027e3d5de305a56db1d1461f25709ac23267c6dc28cdab8323e3f96caa58a9a6a5e93ac15d7285cee0c2f019378c3ada9026e7fe19c872d695f27de7c + languageName: node + linkType: hard + "http-errors@npm:~1.7.2": version: 1.7.3 resolution: "http-errors@npm:1.7.3" @@ -18674,6 +19246,13 @@ __metadata: languageName: node linkType: hard +"http-parser-js@npm:>=0.5.1": + version: 0.5.8 + resolution: "http-parser-js@npm:0.5.8" + checksum: 6bbdf2429858e8cf13c62375b0bfb6dc3955ca0f32e58237488bc86cd2378f31d31785fd3ac4ce93f1c74e0189cf8823c91f5cb061696214fd368d2452dc871d + languageName: node + linkType: hard + "http-proxy-agent@npm:^2.1.0": version: 2.1.0 resolution: "http-proxy-agent@npm:2.1.0" @@ -18706,6 +19285,24 @@ __metadata: languageName: node linkType: hard +"http-proxy-middleware@npm:^2.0.3": + version: 2.0.6 + resolution: "http-proxy-middleware@npm:2.0.6" + dependencies: + "@types/http-proxy": ^1.17.8 + http-proxy: ^1.18.1 + is-glob: ^4.0.1 + is-plain-obj: ^3.0.0 + micromatch: ^4.0.2 + peerDependencies: + "@types/express": ^4.17.13 + peerDependenciesMeta: + "@types/express": + optional: true + checksum: 2ee85bc878afa6cbf34491e972ece0f5be0a3e5c98a60850cf40d2a9a5356e1fc57aab6cff33c1fc37691b0121c3a42602d2b1956c52577e87a5b77b62ae1c3a + languageName: node + linkType: hard + "http-proxy@npm:^1.18.1": version: 1.18.1 resolution: "http-proxy@npm:1.18.1" @@ -18850,6 +19447,13 @@ __metadata: languageName: node linkType: hard +"hyphenate-style-name@npm:^1.0.3": + version: 1.0.4 + resolution: "hyphenate-style-name@npm:1.0.4" + checksum: 4f5bf4b055089754924babebaa23c17845937bcca6aee95d5d015f8fa1e6814279002bd6a9e541e3fac2cd02519fc76305396727066c57c8e21a7e73e7a12137 + languageName: node + linkType: hard + "iconv-corefoundation@npm:^1.1.7": version: 1.1.7 resolution: "iconv-corefoundation@npm:1.1.7" @@ -19220,6 +19824,16 @@ __metadata: languageName: node linkType: hard +"inline-style-prefixer@npm:^6.0.1": + version: 6.0.4 + resolution: "inline-style-prefixer@npm:6.0.4" + dependencies: + css-in-js-utils: ^3.1.0 + fast-loops: ^1.1.3 + checksum: caf7a75d18acbedc7e3b8bfac17563082becd2df6b65accad964a6afdf490329b42315c37fe65ba0177cc10fd32809eb40d62aba23a0118c74d87d4fc58defa2 + languageName: node + linkType: hard + "inquirer@npm:^6.2.0": version: 6.5.2 resolution: "inquirer@npm:6.5.2" @@ -19371,6 +19985,13 @@ __metadata: languageName: node linkType: hard +"interpret@npm:^3.1.1": + version: 3.1.1 + resolution: "interpret@npm:3.1.1" + checksum: 35cebcf48c7351130437596d9ab8c8fe131ce4038da4561e6d665f25640e0034702a031cf7e3a5cea60ac7ac548bf17465e0571ede126f3d3a6933152171ac82 + languageName: node + linkType: hard + "invariant@npm:*, invariant@npm:2.2.4, invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" @@ -19415,6 +20036,20 @@ __metadata: languageName: node linkType: hard +"ipaddr.js@npm:1.9.1": + version: 1.9.1 + resolution: "ipaddr.js@npm:1.9.1" + checksum: f88d3825981486f5a1942414c8d77dd6674dd71c065adcfa46f578d677edcb99fda25af42675cb59db492fdf427b34a5abfcde3982da11a8fd83a500b41cfe77 + languageName: node + linkType: hard + +"ipaddr.js@npm:^2.0.1": + version: 2.1.0 + resolution: "ipaddr.js@npm:2.1.0" + checksum: 807a054f2bd720c4d97ee479d6c9e865c233bea21f139fb8dabd5a35c4226d2621c42e07b4ad94ff3f82add926a607d8d9d37c625ad0319f0e08f9f2bd1968e2 + languageName: node + linkType: hard + "irregular-plurals@npm:^3.2.0": version: 3.3.0 resolution: "irregular-plurals@npm:3.3.0" @@ -21794,6 +22429,16 @@ __metadata: languageName: node linkType: hard +"launch-editor@npm:^2.6.0": + version: 2.6.0 + resolution: "launch-editor@npm:2.6.0" + dependencies: + picocolors: ^1.0.0 + shell-quote: ^1.7.3 + checksum: 48e4230643e8fdb5c14c11314706d58d9f3fbafe2606be3d6e37da1918ad8bfe39dd87875c726a1b59b9f4da99d87ec3e36d4c528464f0b820f9e91e5cb1c02d + languageName: node + linkType: hard + "lazy-val@npm:^1.0.4, lazy-val@npm:^1.0.5": version: 1.0.5 resolution: "lazy-val@npm:1.0.5" @@ -22055,6 +22700,17 @@ __metadata: languageName: node linkType: hard +"loader-utils@npm:^2.0.0": + version: 2.0.4 + resolution: "loader-utils@npm:2.0.4" + dependencies: + big.js: ^5.2.2 + emojis-list: ^3.0.0 + json5: ^2.1.2 + checksum: a5281f5fff1eaa310ad5e1164095689443630f3411e927f95031ab4fb83b4a98f388185bb1fe949e8ab8d4247004336a625e9255c22122b815bb9a4c5d8fc3b7 + languageName: node + linkType: hard + "locate-path@npm:^2.0.0": version: 2.0.0 resolution: "locate-path@npm:2.0.0" @@ -22870,6 +23526,15 @@ __metadata: languageName: node linkType: hard +"memfs@npm:^3.4.3": + version: 3.5.3 + resolution: "memfs@npm:3.5.3" + dependencies: + fs-monkey: ^1.0.4 + checksum: 18dfdeacad7c8047b976a6ccd58bc98ba76e122ad3ca0e50a21837fe2075fc0d9aafc58ab9cf2576c2b6889da1dd2503083f2364191b695273f40969db2ecc44 + languageName: node + linkType: hard + "memoize-one@npm:^5.0.0": version: 5.2.1 resolution: "memoize-one@npm:5.2.1" @@ -22958,7 +23623,7 @@ __metadata: languageName: node linkType: hard -"merge-descriptors@npm:^1.0.1": +"merge-descriptors@npm:1.0.1, merge-descriptors@npm:^1.0.1": version: 1.0.1 resolution: "merge-descriptors@npm:1.0.1" checksum: 5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26 @@ -23000,7 +23665,7 @@ __metadata: languageName: node linkType: hard -"methods@npm:^1.1.2": +"methods@npm:^1.1.2, methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" checksum: 0917ff4041fa8e2f2fda5425a955fe16ca411591fbd123c0d722fcf02b73971ed6f764d85f0a6f547ce49ee0221ce2c19a5fa692157931cecb422984f1dcd13a @@ -23712,6 +24377,13 @@ __metadata: languageName: node linkType: hard +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f + languageName: node + linkType: hard + "mime-db@npm:~1.25.0": version: 1.25.0 resolution: "mime-db@npm:1.25.0" @@ -23737,6 +24409,15 @@ __metadata: languageName: node linkType: hard +"mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.34": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: 1.52.0 + checksum: 89a5b7f1def9f3af5dad6496c5ed50191ae4331cc5389d7c521c8ad28d5fdad2d06fd81baf38fed813dc4e46bb55c8145bb0ff406330818c9cf712fb2e9b3836 + languageName: node + linkType: hard + "mime@npm:1.6.0, mime@npm:^1.3.4, mime@npm:^1.6.0": version: 1.6.0 resolution: "mime@npm:1.6.0" @@ -24264,13 +24945,25 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.2": +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.2": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d languageName: node linkType: hard +"multicast-dns@npm:^7.2.5": + version: 7.2.5 + resolution: "multicast-dns@npm:7.2.5" + dependencies: + dns-packet: ^5.2.2 + thunky: ^1.0.2 + bin: + multicast-dns: cli.js + checksum: 00b8a57df152d4cd0297946320a94b7c3cdf75a46a2247f32f958a8927dea42958177f9b7fdae69fab2e4e033fb3416881af1f5e9055a3e1542888767139e2fb + languageName: node + linkType: hard + "multimatch@npm:^3.0.0": version: 3.0.0 resolution: "multimatch@npm:3.0.0" @@ -24467,7 +25160,7 @@ __metadata: languageName: node linkType: hard -"negotiator@npm:^0.6.3": +"negotiator@npm:0.6.3, negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 @@ -24667,6 +25360,27 @@ __metadata: languageName: node linkType: hard +"node-fetch@npm:^2.6.12": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: ^5.0.0 + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: d76d2f5edb451a3f05b15115ec89fc6be39de37c6089f1b6368df03b91e1633fd379a7e01b7ab05089a25034b2023d959b47e59759cb38d88341b2459e89d6e5 + languageName: node + linkType: hard + +"node-forge@npm:^1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 + languageName: node + linkType: hard + "node-gyp@npm:8.x": version: 8.4.1 resolution: "node-gyp@npm:8.4.1" @@ -25473,6 +26187,13 @@ __metadata: languageName: node linkType: hard +"obuf@npm:^1.0.0, obuf@npm:^1.1.2": + version: 1.1.2 + resolution: "obuf@npm:1.1.2" + checksum: 41a2ba310e7b6f6c3b905af82c275bf8854896e2e4c5752966d64cbcd2f599cfffd5932006bcf3b8b419dfdacebb3a3912d5d94e10f1d0acab59876c8757f27f + languageName: node + linkType: hard + "octokit-pagination-methods@npm:^1.1.0": version: 1.1.0 resolution: "octokit-pagination-methods@npm:1.1.0" @@ -25487,6 +26208,15 @@ __metadata: languageName: node linkType: hard +"on-finished@npm:2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: 1.1.1 + checksum: d20929a25e7f0bb62f937a425b5edeb4e4cde0540d77ba146ec9357f00b0d497cdb3b9b05b9c8e46222407d1548d08166bff69cc56dfa55ba0e4469228920ff0 + languageName: node + linkType: hard + "on-finished@npm:^2.3.0, on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -25546,7 +26276,7 @@ __metadata: languageName: node linkType: hard -"open@npm:8.4.2": +"open@npm:8.4.2, open@npm:^8.0.9": version: 8.4.2 resolution: "open@npm:8.4.2" dependencies: @@ -25897,6 +26627,16 @@ __metadata: languageName: node linkType: hard +"p-retry@npm:^4.5.0": + version: 4.6.2 + resolution: "p-retry@npm:4.6.2" + dependencies: + "@types/retry": 0.12.0 + retry: ^0.13.1 + checksum: 45c270bfddaffb4a895cea16cb760dcc72bdecb6cb45fef1971fa6ea2e91ddeafddefe01e444ac73e33b1b3d5d29fb0dd18a7effb294262437221ddc03ce0f2e + languageName: node + linkType: hard + "p-try@npm:^1.0.0": version: 1.0.0 resolution: "p-try@npm:1.0.0" @@ -26179,7 +26919,7 @@ __metadata: languageName: node linkType: hard -"parseurl@npm:^1.3.2, parseurl@npm:^1.3.3, parseurl@npm:~1.3.3": +"parseurl@npm:^1.3.2, parseurl@npm:^1.3.3, parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" checksum: 407cee8e0a3a4c5cd472559bca8b6a45b82c124e9a4703302326e9ab60fc1081442ada4e02628efef1eb16197ddc7f8822f5a91fd7d7c86b51f530aedb17dfa2 @@ -26373,6 +27113,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:0.1.7": + version: 0.1.7 + resolution: "path-to-regexp@npm:0.1.7" + checksum: 69a14ea24db543e8b0f4353305c5eac6907917031340e5a8b37df688e52accd09e3cebfe1660b70d76b6bd89152f52183f28c74813dbf454ba1a01c82a38abce + languageName: node + linkType: hard + "path-type@npm:^1.0.0": version: 1.1.0 resolution: "path-type@npm:1.1.0" @@ -27284,6 +28031,16 @@ __metadata: languageName: node linkType: hard +"proxy-addr@npm:~2.0.7": + version: 2.0.7 + resolution: "proxy-addr@npm:2.0.7" + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + checksum: 29c6990ce9364648255454842f06f8c46fcd124d3e6d7c5066df44662de63cdc0bad032e9bf5a3d653ff72141cc7b6019873d685708ac8210c30458ad99f2b74 + languageName: node + linkType: hard + "psl@npm:^1.1.28, psl@npm:^1.1.33": version: 1.8.0 resolution: "psl@npm:1.8.0" @@ -27396,7 +28153,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.3": +"qs@npm:6.11.0, qs@npm:^6.10.3": version: 6.11.0 resolution: "qs@npm:6.11.0" dependencies: @@ -27561,7 +28318,7 @@ __metadata: languageName: node linkType: hard -"range-parser@npm:^1.2.0, range-parser@npm:~1.2.1": +"range-parser@npm:^1.2.0, range-parser@npm:^1.2.1, range-parser@npm:~1.2.1": version: 1.2.1 resolution: "range-parser@npm:1.2.1" checksum: 0a268d4fea508661cf5743dfe3d5f47ce214fd6b7dec1de0da4d669dd4ef3d2144468ebe4179049eff253d9d27e719c88dae55be64f954e80135a0cada804ec9 @@ -27575,6 +28332,18 @@ __metadata: languageName: node linkType: hard +"raw-body@npm:2.5.1": + version: 2.5.1 + resolution: "raw-body@npm:2.5.1" + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e + languageName: node + linkType: hard + "raw-body@npm:2.5.2": version: 2.5.2 resolution: "raw-body@npm:2.5.2" @@ -27662,7 +28431,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:18.2.0": +"react-dom@npm:18.2.0, react-dom@npm:^18.2.0": version: 18.2.0 resolution: "react-dom@npm:18.2.0" dependencies: @@ -28129,6 +28898,25 @@ __metadata: languageName: node linkType: hard +"react-native-web@npm:^0.19.7": + version: 0.19.7 + resolution: "react-native-web@npm:0.19.7" + dependencies: + "@babel/runtime": ^7.18.6 + "@react-native/normalize-color": ^2.1.0 + fbjs: ^3.0.4 + inline-style-prefixer: ^6.0.1 + memoize-one: ^6.0.0 + nullthrows: ^1.1.1 + postcss-value-parser: ^4.2.0 + styleq: ^0.1.3 + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: b35d5554855f769c9c24932e9ae9fac1c2c1313cac06fe30de48fbdf9013e1da2ed82e28117f2dfa732c9f49e6bb4b58a3ac49001001e89973397036d3487674 + languageName: node + linkType: hard + "react-native-webview@npm:12.4.0": version: 12.4.0 resolution: "react-native-webview@npm:12.4.0" @@ -28631,6 +29419,17 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^3.0.6": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: ^2.0.3 + string_decoder: ^1.1.1 + util-deprecate: ^1.0.1 + checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d + languageName: node + linkType: hard + "readable-stream@npm:^4.3.0": version: 4.4.2 resolution: "readable-stream@npm:4.4.2" @@ -28837,6 +29636,13 @@ __metadata: languageName: node linkType: hard +"regenerator-runtime@npm:^0.14.0": + version: 0.14.0 + resolution: "regenerator-runtime@npm:0.14.0" + checksum: 1c977ad82a82a4412e4f639d65d22be376d3ebdd30da2c003eeafdaaacd03fc00c2320f18120007ee700900979284fc78a9f00da7fb593f6e6eeebc673fba9a3 + languageName: node + linkType: hard + "regenerator-transform@npm:^0.15.1": version: 0.15.1 resolution: "regenerator-transform@npm:0.15.1" @@ -29350,6 +30156,13 @@ __metadata: languageName: node linkType: hard +"retry@npm:^0.13.1": + version: 0.13.1 + resolution: "retry@npm:0.13.1" + checksum: 47c4d5be674f7c13eee4cfe927345023972197dbbdfba5d3af7e461d13b44de1bfd663bfc80d2f601f8ef3fc8164c16dd99655a221921954a65d044a2fc1233b + languageName: node + linkType: hard + "reusify@npm:^1.0.4": version: 1.0.4 resolution: "reusify@npm:1.0.4" @@ -29622,7 +30435,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -29749,6 +30562,17 @@ __metadata: languageName: node linkType: hard +"schema-utils@npm:^3.0.0": + version: 3.3.0 + resolution: "schema-utils@npm:3.3.0" + dependencies: + "@types/json-schema": ^7.0.8 + ajv: ^6.12.5 + ajv-keywords: ^3.5.2 + checksum: ea56971926fac2487f0757da939a871388891bc87c6a82220d125d587b388f1704788f3706e7f63a7b70e49fc2db974c41343528caea60444afd5ce0fe4b85c0 + languageName: node + linkType: hard + "schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": version: 3.1.1 resolution: "schema-utils@npm:3.1.1" @@ -29760,6 +30584,18 @@ __metadata: languageName: node linkType: hard +"schema-utils@npm:^4.0.0": + version: 4.2.0 + resolution: "schema-utils@npm:4.2.0" + dependencies: + "@types/json-schema": ^7.0.9 + ajv: ^8.9.0 + ajv-formats: ^2.1.1 + ajv-keywords: ^5.1.0 + checksum: 26a0463d47683258106e6652e9aeb0823bf0b85843039e068b57da1892f7ae6b6b1094d48e9ed5ba5cbe9f7166469d880858b9d91abe8bd249421eb813850cde + languageName: node + linkType: hard + "secure-compare@npm:3.0.1": version: 3.0.1 resolution: "secure-compare@npm:3.0.1" @@ -29767,6 +30603,22 @@ __metadata: languageName: node linkType: hard +"select-hose@npm:^2.0.0": + version: 2.0.0 + resolution: "select-hose@npm:2.0.0" + checksum: d7e5fcc695a4804209d232a1b18624a5134be334d4e1114b0721f7a5e72bd73da483dcf41528c1af4f4f4892ad7cfd6a1e55c8ffb83f9c9fe723b738db609dbb + languageName: node + linkType: hard + +"selfsigned@npm:^2.1.1": + version: 2.1.1 + resolution: "selfsigned@npm:2.1.1" + dependencies: + node-forge: ^1 + checksum: aa9ce2150a54838978d5c0aee54d7ebe77649a32e4e690eb91775f71fdff773874a4fbafd0ac73d8ec3b702ff8a395c604df4f8e8868528f36fd6c15076fb43a + languageName: node + linkType: hard + "semver-compare@npm:^1.0.0": version: 1.0.0 resolution: "semver-compare@npm:1.0.0" @@ -29886,6 +30738,27 @@ __metadata: languageName: node linkType: hard +"send@npm:0.18.0": + version: 0.18.0 + resolution: "send@npm:0.18.0" + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: ~1.0.2 + escape-html: ~1.0.3 + etag: ~1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: ~1.2.1 + statuses: 2.0.1 + checksum: 74fc07ebb58566b87b078ec63e5a3e41ecd987e4272ba67b7467e86c6ad51bc6b0b0154133b6d8b08a2ddda360464f71382f7ef864700f34844a76c8027817a8 + languageName: node + linkType: hard + "sentence-case@npm:^3.0.4": version: 3.0.4 resolution: "sentence-case@npm:3.0.4" @@ -29922,6 +30795,33 @@ __metadata: languageName: node linkType: hard +"serve-index@npm:^1.9.1": + version: 1.9.1 + resolution: "serve-index@npm:1.9.1" + dependencies: + accepts: ~1.3.4 + batch: 0.6.1 + debug: 2.6.9 + escape-html: ~1.0.3 + http-errors: ~1.6.2 + mime-types: ~2.1.17 + parseurl: ~1.3.2 + checksum: e2647ce13379485b98a53ba2ea3fbad4d44b57540d00663b02b976e426e6194d62ac465c0d862cb7057f65e0de8ab8a684aa095427a4b8612412eca0d300d22f + languageName: node + linkType: hard + +"serve-static@npm:1.15.0": + version: 1.15.0 + resolution: "serve-static@npm:1.15.0" + dependencies: + encodeurl: ~1.0.2 + escape-html: ~1.0.3 + parseurl: ~1.3.3 + send: 0.18.0 + checksum: af57fc13be40d90a12562e98c0b7855cf6e8bd4c107fe9a45c212bf023058d54a1871b1c89511c3958f70626fff47faeb795f5d83f8cf88514dbaeb2b724464d + languageName: node + linkType: hard + "serve-static@npm:^1.13.1": version: 1.14.1 resolution: "serve-static@npm:1.14.1" @@ -29960,13 +30860,20 @@ __metadata: languageName: node linkType: hard -"setimmediate@npm:^1.0.5": +"setimmediate@npm:^1.0.4, setimmediate@npm:^1.0.5": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd languageName: node linkType: hard +"setprototypeof@npm:1.1.0": + version: 1.1.0 + resolution: "setprototypeof@npm:1.1.0" + checksum: 27cb44304d6c9e1a23bc6c706af4acaae1a7aa1054d4ec13c05f01a99fd4887109a83a8042b67ad90dbfcd100d43efc171ee036eb080667172079213242ca36e + languageName: node + linkType: hard + "setprototypeof@npm:1.1.1": version: 1.1.1 resolution: "setprototypeof@npm:1.1.1" @@ -30438,6 +31345,17 @@ __metadata: languageName: node linkType: hard +"sockjs@npm:^0.3.24": + version: 0.3.24 + resolution: "sockjs@npm:0.3.24" + dependencies: + faye-websocket: ^0.11.3 + uuid: ^8.3.2 + websocket-driver: ^0.7.4 + checksum: 355309b48d2c4e9755349daa29cea1c0d9ee23e49b983841c6bf7a20276b00d3c02343f9f33f26d2ee8b261a5a02961b52a25c8da88b2538c5b68d3071b4934c + languageName: node + linkType: hard + "socks-proxy-agent@npm:^4.0.0": version: 4.0.2 resolution: "socks-proxy-agent@npm:4.0.2" @@ -30678,6 +31596,33 @@ __metadata: languageName: node linkType: hard +"spdy-transport@npm:^3.0.0": + version: 3.0.0 + resolution: "spdy-transport@npm:3.0.0" + dependencies: + debug: ^4.1.0 + detect-node: ^2.0.4 + hpack.js: ^2.1.6 + obuf: ^1.1.2 + readable-stream: ^3.0.6 + wbuf: ^1.7.3 + checksum: 0fcaad3b836fb1ec0bdd39fa7008b9a7a84a553f12be6b736a2512613b323207ffc924b9551cef0378f7233c85916cff1118652e03a730bdb97c0e042243d56c + languageName: node + linkType: hard + +"spdy@npm:^4.0.2": + version: 4.0.2 + resolution: "spdy@npm:4.0.2" + dependencies: + debug: ^4.1.0 + handle-thing: ^2.0.0 + http-deceiver: ^1.2.7 + select-hose: ^2.0.0 + spdy-transport: ^3.0.0 + checksum: 2c739d0ff6f56ad36d2d754d0261d5ec358457bea7cbf77b1b05b0c6464f2ce65b85f196305f50b7bd9120723eb94bae9933466f28e67e5cd8cde4e27f1d75f8 + languageName: node + linkType: hard + "speedometer@npm:~1.0.0": version: 1.0.0 resolution: "speedometer@npm:1.0.0" @@ -30920,7 +31865,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:>= 1.5.0 < 2, statuses@npm:^1.5.0, statuses@npm:~1.5.0": +"statuses@npm:>= 1.4.0 < 2, statuses@npm:>= 1.5.0 < 2, statuses@npm:^1.5.0, statuses@npm:~1.5.0": version: 1.5.0 resolution: "statuses@npm:1.5.0" checksum: c469b9519de16a4bb19600205cffb39ee471a5f17b82589757ca7bd40a8d92ebb6ed9f98b5a540c5d302ccbc78f15dc03cc0280dd6e00df1335568a5d5758a5c @@ -31492,6 +32437,13 @@ __metadata: languageName: node linkType: hard +"styleq@npm:^0.1.3": + version: 0.1.3 + resolution: "styleq@npm:0.1.3" + checksum: 14a8d23abd914166a9b4bd04ed753bd91363f0e029ee4a94ec2c7dc37d3213fe01fceee22dc655288da3ae89f5dc01cec42d5e2b58478b0dea33bf5bdf509be1 + languageName: node + linkType: hard + "stylis@npm:4.1.3, stylis@npm:^4.1.2": version: 4.1.3 resolution: "stylis@npm:4.1.3" @@ -32167,6 +33119,13 @@ __metadata: languageName: node linkType: hard +"thunky@npm:^1.0.2": + version: 1.1.0 + resolution: "thunky@npm:1.1.0" + checksum: 993096c472b6b8f30e29dc777a8d17720e4cab448375041f20c0cb802a09a7fb2217f2a3e8cdc11851faa71c957e2db309357367fc9d7af3cb7a4d00f4b66034 + languageName: node + linkType: hard + "tildify@npm:2.0.0": version: 2.0.0 resolution: "tildify@npm:2.0.0" @@ -32190,6 +33149,15 @@ __metadata: languageName: node linkType: hard +"timers-browserify@npm:^2.0.12": + version: 2.0.12 + resolution: "timers-browserify@npm:2.0.12" + dependencies: + setimmediate: ^1.0.4 + checksum: ec37ae299066bef6c464dcac29c7adafba1999e7227a9bdc4e105a459bee0f0b27234a46bfd7ab4041da79619e06a58433472867a913d01c26f8a203f87cee70 + languageName: node + linkType: hard + "timers@npm:0.1.1": version: 0.1.1 resolution: "timers@npm:0.1.1" @@ -32835,7 +33803,7 @@ __metadata: languageName: node linkType: hard -"type-is@npm:^1.6.16, type-is@npm:^1.6.18": +"type-is@npm:^1.6.16, type-is@npm:^1.6.18, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" dependencies: @@ -33020,6 +33988,13 @@ __metadata: languageName: node linkType: hard +"ua-parser-js@npm:^1.0.35": + version: 1.0.35 + resolution: "ua-parser-js@npm:1.0.35" + checksum: 02370d38a0c8b586f2503d1c3bbba5cbc0b97d407282f9023201a99e4c03eae4357a2800fdf50cf80d73ec25c0b0cc5bfbaa03975b0add4043d6e4c86712c9c1 + languageName: node + linkType: hard + "uc.micro@npm:^1.0.1, uc.micro@npm:^1.0.5": version: 1.0.6 resolution: "uc.micro@npm:1.0.6" @@ -33459,6 +34434,23 @@ __metadata: languageName: node linkType: hard +"url-loader@npm:4.1.1": + version: 4.1.1 + resolution: "url-loader@npm:4.1.1" + dependencies: + loader-utils: ^2.0.0 + mime-types: ^2.1.27 + schema-utils: ^3.0.0 + peerDependencies: + file-loader: "*" + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + file-loader: + optional: true + checksum: c1122a992c6cff70a7e56dfc2b7474534d48eb40b2cc75467cde0c6972e7597faf8e43acb4f45f93c2473645dfd803bcbc20960b57544dd1e4c96e77f72ba6fd + languageName: node + linkType: hard + "url-parse@npm:1.5.10, url-parse@npm:^1.5.3": version: 1.5.10 resolution: "url-parse@npm:1.5.10" @@ -33936,6 +34928,15 @@ __metadata: languageName: node linkType: hard +"wbuf@npm:^1.1.0, wbuf@npm:^1.7.3": + version: 1.7.3 + resolution: "wbuf@npm:1.7.3" + dependencies: + minimalistic-assert: ^1.0.0 + checksum: 2abc306c96930b757972a1c4650eb6b25b5d99f24088714957f88629e137db569368c5de0e57986c89ea70db2f1df9bba11a87cb6d0c8694b6f53a0159fab3bf + languageName: node + linkType: hard + "wcwidth@npm:^1.0.0, wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" @@ -34020,6 +35021,100 @@ __metadata: languageName: node linkType: hard +"webpack-cli@npm:5.1.4": + version: 5.1.4 + resolution: "webpack-cli@npm:5.1.4" + dependencies: + "@discoveryjs/json-ext": ^0.5.0 + "@webpack-cli/configtest": ^2.1.1 + "@webpack-cli/info": ^2.0.2 + "@webpack-cli/serve": ^2.0.5 + colorette: ^2.0.14 + commander: ^10.0.1 + cross-spawn: ^7.0.3 + envinfo: ^7.7.3 + fastest-levenshtein: ^1.0.12 + import-local: ^3.0.2 + interpret: ^3.1.1 + rechoir: ^0.8.0 + webpack-merge: ^5.7.3 + peerDependencies: + webpack: 5.x.x + peerDependenciesMeta: + "@webpack-cli/generators": + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + bin: + webpack-cli: bin/cli.js + checksum: 3a4ad0d0342a6815c850ee4633cc2a8a5dae04f918e7847f180bf24ab400803cf8a8943707ffbed03eb20fe6ce647f996f60a2aade87b0b4a9954da3da172ce0 + languageName: node + linkType: hard + +"webpack-dev-middleware@npm:^5.3.1": + version: 5.3.3 + resolution: "webpack-dev-middleware@npm:5.3.3" + dependencies: + colorette: ^2.0.10 + memfs: ^3.4.3 + mime-types: ^2.1.31 + range-parser: ^1.2.1 + schema-utils: ^4.0.0 + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + checksum: dd332cc6da61222c43d25e5a2155e23147b777ff32fdf1f1a0a8777020c072fbcef7756360ce2a13939c3f534c06b4992a4d659318c4a7fe2c0530b52a8a6621 + languageName: node + linkType: hard + +"webpack-dev-server@npm:^4.15.1": + version: 4.15.1 + resolution: "webpack-dev-server@npm:4.15.1" + dependencies: + "@types/bonjour": ^3.5.9 + "@types/connect-history-api-fallback": ^1.3.5 + "@types/express": ^4.17.13 + "@types/serve-index": ^1.9.1 + "@types/serve-static": ^1.13.10 + "@types/sockjs": ^0.3.33 + "@types/ws": ^8.5.5 + ansi-html-community: ^0.0.8 + bonjour-service: ^1.0.11 + chokidar: ^3.5.3 + colorette: ^2.0.10 + compression: ^1.7.4 + connect-history-api-fallback: ^2.0.0 + default-gateway: ^6.0.3 + express: ^4.17.3 + graceful-fs: ^4.2.6 + html-entities: ^2.3.2 + http-proxy-middleware: ^2.0.3 + ipaddr.js: ^2.0.1 + launch-editor: ^2.6.0 + open: ^8.0.9 + p-retry: ^4.5.0 + rimraf: ^3.0.2 + schema-utils: ^4.0.0 + selfsigned: ^2.1.1 + serve-index: ^1.9.1 + sockjs: ^0.3.24 + spdy: ^4.0.2 + webpack-dev-middleware: ^5.3.1 + ws: ^8.13.0 + peerDependencies: + webpack: ^4.37.0 || ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + bin: + webpack-dev-server: bin/webpack-dev-server.js + checksum: cd0063b068d2b938fd76c412d555374186ac2fa84bbae098265212ed50a5c15d6f03aa12a5a310c544a242943eb58c0bfde4c296d5c36765c182f53799e1bc71 + languageName: node + linkType: hard + "webpack-merge@npm:^5.7.3": version: 5.8.0 resolution: "webpack-merge@npm:5.8.0" @@ -34118,6 +35213,24 @@ __metadata: languageName: node linkType: hard +"websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": + version: 0.7.4 + resolution: "websocket-driver@npm:0.7.4" + dependencies: + http-parser-js: ">=0.5.1" + safe-buffer: ">=5.1.0" + websocket-extensions: ">=0.1.1" + checksum: fffe5a33fe8eceafd21d2a065661d09e38b93877eae1de6ab5d7d2734c6ed243973beae10ae48c6613cfd675f200e5a058d1e3531bc9e6c5d4f1396ff1f0bfb9 + languageName: node + linkType: hard + +"websocket-extensions@npm:>=0.1.1": + version: 0.1.4 + resolution: "websocket-extensions@npm:0.1.4" + checksum: 5976835e68a86afcd64c7a9762ed85f2f27d48c488c707e67ba85e717b90fa066b98ab33c744d64255c9622d349eedecf728e65a5f921da71b58d0e9591b9038 + languageName: node + linkType: hard + "whatwg-encoding@npm:^1.0.5": version: 1.0.5 resolution: "whatwg-encoding@npm:1.0.5" From 7434ac873dba9fcf91b1e8a417be45e6ecfb808b Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 14:39:44 -0700 Subject: [PATCH 002/177] Main screen loads --- .eslintignore | 7 + .gitignore | 7 + .../components/ScreenHeader/index.tsx | 2 +- .../utils/makeImportExportCacheDirectory.ts | 3 +- .../app-mobile/components/screens/Note.tsx | 8 +- .../voiceTyping/VoiceTypingDialog.tsx | 2 +- packages/app-mobile/index.web.js | 24 +- packages/app-mobile/package.json | 20 +- packages/app-mobile/root.tsx | 7 +- .../app-mobile/services/profiles/index.ts | 4 +- packages/app-mobile/setupQuickActions.ts | 2 +- packages/app-mobile/utils/ShareUtils.ts | 5 +- ...ive.js => database-driver-react-native.ts} | 43 +- .../utils/database-driver-react-native.web.ts | 96 + packages/app-mobile/utils/fs-driver-web.js | 397 --- packages/app-mobile/utils/fs-driver-web.ts | 371 --- .../utils/fs-driver/fs-driver-rn.ts | 4 + .../utils/fs-driver/fs-driver-rn.web.ts | 263 ++ .../app-mobile/utils/fs-driver/tarCreate.ts | 10 +- .../app-mobile/utils/fs-driver/tarExtract.ts | 2 +- .../index.ts} | 78 +- .../index.web.ts} | 136 +- .../utils/shim-init-react/injectedJs.ts | 9 + .../utils/shim-init-react/shimInitShared.ts | 63 + packages/app-mobile/web/index.html | 9 +- packages/app-mobile/web/webpack.config.js | 54 +- packages/lib/BaseModel.ts | 4 +- packages/lib/JoplinDatabase.ts | 4 +- packages/lib/database.ts | 2 +- packages/lib/fs-driver-base.ts | 8 + packages/lib/models/BaseItem.ts | 8 +- packages/lib/models/Folder.ts | 4 +- packages/lib/models/Note.ts | 2 +- packages/lib/models/NoteTag.ts | 2 +- packages/lib/models/Resource.ts | 10 +- packages/lib/models/Revision.ts | 2 +- packages/lib/models/Setting.ts | 2 +- packages/lib/models/Tag.ts | 6 +- packages/lib/package.json | 2 +- packages/lib/services/ResourceService.ts | 2 +- packages/lib/services/RevisionService.ts | 2 +- packages/lib/services/search/SearchEngine.ts | 4 +- .../lib/services/search/SearchEngineUtils.ts | 2 +- packages/lib/shim.ts | 10 +- .../renderer/MdToHtml/rules/katex_mhchem.js | 4 +- packages/tools/cspell/dictionary4.txt | 2 + yarn.lock | 2311 +++++++++++++---- 47 files changed, 2487 insertions(+), 1532 deletions(-) rename packages/app-mobile/utils/{database-driver-react-native.js => database-driver-react-native.ts} (57%) create mode 100644 packages/app-mobile/utils/database-driver-react-native.web.ts delete mode 100644 packages/app-mobile/utils/fs-driver-web.js delete mode 100644 packages/app-mobile/utils/fs-driver-web.ts create mode 100644 packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts rename packages/app-mobile/utils/{shim-init-react.js => shim-init-react/index.ts} (79%) rename packages/app-mobile/utils/{shim-init-react.web.ts => shim-init-react/index.web.ts} (53%) create mode 100644 packages/app-mobile/utils/shim-init-react/injectedJs.ts create mode 100644 packages/app-mobile/utils/shim-init-react/shimInitShared.ts diff --git a/.eslintignore b/.eslintignore index e5c110408cc..5eb5b7edff5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -708,9 +708,12 @@ packages/app-mobile/utils/appDefaultState.js packages/app-mobile/utils/autodetectTheme.js packages/app-mobile/utils/checkPermissions.js packages/app-mobile/utils/createRootStyle.js +packages/app-mobile/utils/database-driver-react-native.js +packages/app-mobile/utils/database-driver-react-native.web.js packages/app-mobile/utils/debounce.js packages/app-mobile/utils/fs-driver/constants.js packages/app-mobile/utils/fs-driver/fs-driver-rn.js +packages/app-mobile/utils/fs-driver/fs-driver-rn.web.js packages/app-mobile/utils/fs-driver/runOnDeviceTests.js packages/app-mobile/utils/fs-driver/tarCreate.js packages/app-mobile/utils/fs-driver/tarExtract.test.js @@ -727,6 +730,10 @@ packages/app-mobile/utils/polyfills/bufferPolyfill.js packages/app-mobile/utils/polyfills/index.js packages/app-mobile/utils/setupNotifications.js packages/app-mobile/utils/shareHandler.js +packages/app-mobile/utils/shim-init-react/index.js +packages/app-mobile/utils/shim-init-react/index.web.js +packages/app-mobile/utils/shim-init-react/injectedJs.js +packages/app-mobile/utils/shim-init-react/shimInitShared.js packages/app-mobile/utils/showMessageBox.js packages/app-mobile/utils/testing/createMockReduxStore.js packages/app-mobile/utils/types.js diff --git a/.gitignore b/.gitignore index 826f6f883d4..4d9fa0713e5 100644 --- a/.gitignore +++ b/.gitignore @@ -687,9 +687,12 @@ packages/app-mobile/utils/appDefaultState.js packages/app-mobile/utils/autodetectTheme.js packages/app-mobile/utils/checkPermissions.js packages/app-mobile/utils/createRootStyle.js +packages/app-mobile/utils/database-driver-react-native.js +packages/app-mobile/utils/database-driver-react-native.web.js packages/app-mobile/utils/debounce.js packages/app-mobile/utils/fs-driver/constants.js packages/app-mobile/utils/fs-driver/fs-driver-rn.js +packages/app-mobile/utils/fs-driver/fs-driver-rn.web.js packages/app-mobile/utils/fs-driver/runOnDeviceTests.js packages/app-mobile/utils/fs-driver/tarCreate.js packages/app-mobile/utils/fs-driver/tarExtract.test.js @@ -706,6 +709,10 @@ packages/app-mobile/utils/polyfills/bufferPolyfill.js packages/app-mobile/utils/polyfills/index.js packages/app-mobile/utils/setupNotifications.js packages/app-mobile/utils/shareHandler.js +packages/app-mobile/utils/shim-init-react/index.js +packages/app-mobile/utils/shim-init-react/index.web.js +packages/app-mobile/utils/shim-init-react/injectedJs.js +packages/app-mobile/utils/shim-init-react/shimInitShared.js packages/app-mobile/utils/showMessageBox.js packages/app-mobile/utils/testing/createMockReduxStore.js packages/app-mobile/utils/types.js diff --git a/packages/app-mobile/components/ScreenHeader/index.tsx b/packages/app-mobile/components/ScreenHeader/index.tsx index 78ac67dc945..69814274498 100644 --- a/packages/app-mobile/components/ScreenHeader/index.tsx +++ b/packages/app-mobile/components/ScreenHeader/index.tsx @@ -112,7 +112,7 @@ class ScreenHeaderComponent extends PureComponent { - const targetDir = `${CachesDirectoryPath}/exports`; + const targetDir = `${shim.fsDriver().getCacheDirectoryPath()}/exports`; await shim.fsDriver().mkdir(targetDir); return targetDir; diff --git a/packages/app-mobile/components/screens/Note.tsx b/packages/app-mobile/components/screens/Note.tsx index 5ff345f8b79..76a8a4a9afe 100644 --- a/packages/app-mobile/components/screens/Note.tsx +++ b/packages/app-mobile/components/screens/Note.tsx @@ -49,7 +49,7 @@ import promptRestoreAutosave from '../NoteEditor/ImageEditor/promptRestoreAutosa import isEditableResource from '../NoteEditor/ImageEditor/isEditableResource'; import VoiceTypingDialog from '../voiceTyping/VoiceTypingDialog'; import { voskEnabled } from '../../services/voiceTyping/vosk'; -import { isSupportedLanguage } from '../../services/voiceTyping/vosk.android'; +import { isSupportedLanguage } from '../../services/voiceTyping/vosk'; import { ChangeEvent as EditorChangeEvent, SelectionRangeChangeEvent, UndoRedoDepthChangeEvent } from '@joplin/editor/events'; import { join } from 'path'; import { Dispatch } from 'redux'; @@ -493,6 +493,7 @@ class NoteScreenComponent extends BaseScreenComponent implements B public async requestGeoLocationPermissions() { if (!Setting.value('trackLocation')) return; + if (Platform.OS === 'web') return; const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, { message: _('In order to associate a geo-location with the note, the app needs your permission to access your location.\n\nYou may turn off this option at any time in the Configuration screen.'), @@ -1033,6 +1034,11 @@ class NoteScreenComponent extends BaseScreenComponent implements B } public async onAlarmDialogAccept(date: Date) { + if (Platform.OS === 'web') { + alert('Not supported.'); + return; + } + const response = await checkPermissions(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS); // The POST_NOTIFICATIONS permission isn't supported on Android API < 33. diff --git a/packages/app-mobile/components/voiceTyping/VoiceTypingDialog.tsx b/packages/app-mobile/components/voiceTyping/VoiceTypingDialog.tsx index 0b943328cea..8989a5a89a9 100644 --- a/packages/app-mobile/components/voiceTyping/VoiceTypingDialog.tsx +++ b/packages/app-mobile/components/voiceTyping/VoiceTypingDialog.tsx @@ -5,7 +5,7 @@ import { _, languageName } from '@joplin/lib/locale'; import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; import { getVosk, Recorder, startRecording, Vosk } from '../../services/voiceTyping/vosk'; import { IconSource } from 'react-native-paper/lib/typescript/components/Icon'; -import { modelIsDownloaded } from '../../services/voiceTyping/vosk.android'; +import { modelIsDownloaded } from '../../services/voiceTyping/vosk'; interface Props { locale: string; diff --git a/packages/app-mobile/index.web.js b/packages/app-mobile/index.web.js index cfbc9c0b2ce..6da24e89b1b 100644 --- a/packages/app-mobile/index.web.js +++ b/packages/app-mobile/index.web.js @@ -1,6 +1,26 @@ -// Set up required for react-native-drawer-layout (See: https://reactnavigation.org/docs/drawer-layout/ v6.x) -import 'react-native-gesture-handler'; + +import fontAwesomeFont from 'react-native-vector-icons/Fonts/FontAwesome.ttf'; +import ioniconFont from 'react-native-vector-icons/Fonts/Ionicons.ttf'; + +// See https://www.npmjs.com/package/react-native-vector-icons +const setUpRnVectorIcons = () => { + const iconFontStyles = ` + @font-face { + src: url(${fontAwesomeFont}); + font-family: FontAwesome; + } + @font-face { + src: url(${ioniconFont}); + font-family: Ionicons; + } + `; + + const style = document.createElement('style'); + style.appendChild(document.createTextNode(iconFontStyles)); + document.head.appendChild(style); +}; +setUpRnVectorIcons(); import { AppRegistry } from 'react-native'; const Root = require('./root').default; diff --git a/packages/app-mobile/package.json b/packages/app-mobile/package.json index c15f407e24d..c9cfe798c7a 100644 --- a/packages/app-mobile/package.json +++ b/packages/app-mobile/package.json @@ -8,7 +8,8 @@ "start": "BROWSERSLIST_IGNORE_OLD_DATA=true react-native start --reset-cache", "android": "react-native run-android", "build": "NO_FLIPPER=1 gulp build", - "web": "webpack --config ./web/webpack.config.js", + "web": "webpack --config ./web/webpack.config.js && cp ./web/index.html ./web/dist/", + "serve-web": "webpack serve -o ./web/dist/ --static ./web/ --config ./web/webpack.config.js --progress", "tsc": "tsc --project tsconfig.json", "watch": "tsc --watch --preserveWatchOutput --project tsconfig.json", "clean": "node tools/clean.js", @@ -73,8 +74,8 @@ "react-native-vosk": "0.1.12", "react-native-webview": "13.8.1", "react-native-zip-archive": "6.1.0", - "react-redux": "8.1.3", - "redux": "4.2.1", + "react-redux": "9.1.2", + "redux": "5.0.1", "rn-fetch-blob": "0.12.0", "stream": "0.0.2", "stream-browserify": "3.0.0", @@ -84,14 +85,15 @@ "url": "0.11.3" }, "devDependencies": { - "@babel/core": "7.20.2", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/preset-env": "7.20.2", - "@babel/runtime": "7.20.0", + "@babel/core": "7.24.7", + "@babel/plugin-transform-export-namespace-from": "7.24.7", + "@babel/preset-env": "7.24.7", + "@babel/runtime": "7.24.7", "@joplin/tools": "~3.0", "@js-draw/material-icons": "1.20.0", "@react-native/babel-preset": "0.74.83", "@react-native/metro-config": "0.74.83", + "@sqlite.org/sqlite-wasm": "3.46.0-build2", "@testing-library/jest-native": "5.4.3", "@testing-library/react-native": "12.3.3", "@tsconfig/react-native": "2.0.2", @@ -125,9 +127,9 @@ "typescript": "5.2.2", "uglify-js": "3.17.4", "url-loader": "4.1.1", - "webpack": "5.74.0", + "webpack": "5.84.0", "webpack-cli": "5.1.4", - "webpack-dev-server": "^4.15.1" + "webpack-dev-server": "5.0.4" }, "engines": { "node": ">=18" diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index 0e2c6b398bf..cb3a65b9381 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -41,7 +41,7 @@ const { BackButtonService } = require('./services/back-button.js'); import NavService from '@joplin/lib/services/NavService'; import { createStore, applyMiddleware } from 'redux'; import reduxSharedMiddleware from '@joplin/lib/components/shared/reduxSharedMiddleware'; -const { shimInit } = require('./utils/shim-init-react.js'); +import { shimInit } from './utils/shim-init-react'; const { AppNav } = require('./components/app-nav.js'); import Note from '@joplin/lib/models/Note'; import Folder from '@joplin/lib/models/Folder'; @@ -69,7 +69,6 @@ import { MenuProvider } from 'react-native-popup-menu'; import SideMenu from './components/SideMenu'; import SideMenuContent from './components/side-menu-content'; const { SideMenuContentNote } = require('./components/side-menu-content-note.js'); -const { DatabaseDriverReactNative } = require('./utils/database-driver-react-native'); import { reg } from '@joplin/lib/registry'; const { defaultState } = require('@joplin/lib/reducer'); import FileApiDriverLocal from '@joplin/lib/file-api-driver-local'; @@ -88,6 +87,8 @@ import initLib from '@joplin/lib/initLib'; import { isCallbackUrl, parseCallbackUrl, CallbackUrlCommand } from '@joplin/lib/callbackUrlUtils'; import JoplinCloudLoginScreen from './components/screens/JoplinCloudLoginScreen'; +import SyncTargetNone from '@joplin/lib/SyncTargetNone'; + SyncTargetRegistry.addClass(SyncTargetNone); SyncTargetRegistry.addClass(SyncTargetOneDrive); SyncTargetRegistry.addClass(SyncTargetNextcloud); @@ -107,7 +108,6 @@ import setIgnoreTlsErrors from './utils/TlsUtils'; import ShareService from '@joplin/lib/services/share/ShareService'; import setupNotifications from './utils/setupNotifications'; import { loadMasterKeysFromSettings, migrateMasterPassword } from '@joplin/lib/services/e2ee/utils'; -import SyncTargetNone from '@joplin/lib/SyncTargetNone'; import { setRSA } from '@joplin/lib/services/e2ee/ppk'; import RSA from './services/e2ee/RSA.react-native'; import { runIntegrationTests as runRsaIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils'; @@ -131,6 +131,7 @@ import PlatformImplementation from './plugins/PlatformImplementation'; import ShareManager from './components/screens/ShareManager'; import appDefaultState, { DEFAULT_ROUTE } from './utils/appDefaultState'; import { setDateFormat, setTimeFormat, setTimeLocale } from '@joplin/utils/time'; +import DatabaseDriverReactNative from './utils/database-driver-react-native'; type SideMenuPosition = 'left' | 'right'; diff --git a/packages/app-mobile/services/profiles/index.ts b/packages/app-mobile/services/profiles/index.ts index 04a0c1b9aa4..36a2fe23722 100644 --- a/packages/app-mobile/services/profiles/index.ts +++ b/packages/app-mobile/services/profiles/index.ts @@ -4,7 +4,7 @@ const RNExitApp = require('react-native-exit-app').default; import { Profile, ProfileConfig } from '@joplin/lib/services/profileConfig/types'; import { loadProfileConfig as libLoadProfileConfig, saveProfileConfig as libSaveProfileConfig } from '@joplin/lib/services/profileConfig/index'; -import RNFetchBlob from 'rn-fetch-blob'; +import shim from '@joplin/lib/shim'; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied let dispatch_: Function = null; @@ -14,7 +14,7 @@ export const setDispatch = (dispatch: Function) => { }; export const getProfilesRootDir = () => { - return RNFetchBlob.fs.dirs.DocumentDir; + return shim.fsDriver().getAppDirectoryPath(); }; export const getProfilesConfigPath = () => { diff --git a/packages/app-mobile/setupQuickActions.ts b/packages/app-mobile/setupQuickActions.ts index 7783eb64801..1e3fb40625f 100644 --- a/packages/app-mobile/setupQuickActions.ts +++ b/packages/app-mobile/setupQuickActions.ts @@ -16,7 +16,7 @@ export default async (dispatch: Dispatch) => { if (!QuickActions.setShortcutItems) { console.warn('QuickActions unsupported'); - return; + return null; } QuickActions.setShortcutItems([ diff --git a/packages/app-mobile/utils/ShareUtils.ts b/packages/app-mobile/utils/ShareUtils.ts index 2b3978205b4..a92090434fa 100644 --- a/packages/app-mobile/utils/ShareUtils.ts +++ b/packages/app-mobile/utils/ShareUtils.ts @@ -1,13 +1,12 @@ import Resource from '@joplin/lib/models/Resource'; import { ResourceEntity } from '@joplin/lib/services/database/types'; import shim from '@joplin/lib/shim'; -import { CachesDirectoryPath } from 'react-native-fs'; // when refactoring this name, make sure to refactor the `SharePackage.java` (in android) as well const DIR_NAME = 'sharedFiles'; const makeShareCacheDirectory = async () => { - const targetDir = `${CachesDirectoryPath}/${DIR_NAME}`; + const targetDir = `${shim.fsDriver().getCacheDirectoryPath()}/${DIR_NAME}`; await shim.fsDriver().mkdir(targetDir); return targetDir; @@ -37,5 +36,5 @@ export const writeTextToCacheFile = async (text: string, fileName: string): Prom // Clear previously shared files from cache export async function clearSharedFilesCache(): Promise { - return shim.fsDriver().remove(`${CachesDirectoryPath}/sharedFiles`); + return shim.fsDriver().remove(`${shim.fsDriver().getCacheDirectoryPath()}/sharedFiles`); } diff --git a/packages/app-mobile/utils/database-driver-react-native.js b/packages/app-mobile/utils/database-driver-react-native.ts similarity index 57% rename from packages/app-mobile/utils/database-driver-react-native.js rename to packages/app-mobile/utils/database-driver-react-native.ts index 4a901880f02..c23b6f84f68 100644 --- a/packages/app-mobile/utils/database-driver-react-native.js +++ b/packages/app-mobile/utils/database-driver-react-native.ts @@ -1,46 +1,49 @@ const SQLite = require('react-native-sqlite-storage'); -class DatabaseDriverReactNative { - constructor() { + +export default class DatabaseDriverReactNative { + private lastInsertId_: string; + private db_: any; + public constructor() { this.lastInsertId_ = null; } - open(options) { + public open(options: { name: string }) { // SQLite.DEBUG(true); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { SQLite.openDatabase( { name: options.name }, - db => { + (db: any) => { this.db_ = db; resolve(); }, - error => { + (error: Error) => { reject(error); }, ); }); } - sqliteErrorToJsError(error) { + public sqliteErrorToJsError(error: Error) { return error; } - selectOne(sql, params = null) { - return new Promise((resolve, reject) => { + public selectOne(sql: string, params: any = null) { + return new Promise((resolve, reject) => { this.db_.executeSql( sql, params, - r => { + (r: any) => { resolve(r.rows.length ? r.rows.item(0) : null); }, - error => { + (error: Error) => { reject(error); }, ); }); } - selectAll(sql, params = null) { + public selectAll(sql: string, params: any = null) { // eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied return this.exec(sql, params).then(r => { const output = []; @@ -51,29 +54,27 @@ class DatabaseDriverReactNative { }); } - loadExtension(path) { + public loadExtension(path: string) { throw new Error(`No extension support for ${path} in react-native-sqlite-storage`); } - exec(sql, params = null) { - return new Promise((resolve, reject) => { + public exec(sql: string, params: any = null) { + return new Promise((resolve, reject) => { this.db_.executeSql( sql, params, - r => { + (r: { insertId: string }) => { if ('insertId' in r) this.lastInsertId_ = r.insertId; resolve(r); }, - error => { + (error: Error) => { reject(error); }, ); }); } - lastInsertId() { + public lastInsertId() { return this.lastInsertId_; } -} - -module.exports = { DatabaseDriverReactNative }; +} \ No newline at end of file diff --git a/packages/app-mobile/utils/database-driver-react-native.web.ts b/packages/app-mobile/utils/database-driver-react-native.web.ts new file mode 100644 index 00000000000..8da1716fa46 --- /dev/null +++ b/packages/app-mobile/utils/database-driver-react-native.web.ts @@ -0,0 +1,96 @@ +const { sqlite3Worker1Promiser } = require('@sqlite.org/sqlite-wasm'); +import { safeFilename } from "@joplin/utils/path"; + +type DbPromiser = (command: string, options: Record)=>Promise; +type DbId = unknown; +type RowResult = { rowNumber: number|null; row: unknown; }; + +export default class DatabaseDriverReactNative { + private lastInsertId_: string; + private db_: DbPromiser; + private dbId_: DbId; + public constructor() { + this.lastInsertId_ = null; + } + + public async open(options: { name: string }) { + // SQLite.DEBUG(true); + const db = await new Promise((resolve) => { + const db = sqlite3Worker1Promiser({ + onready: () => resolve(db), + }); + }); + const filename = `file:${safeFilename(options.name)}.sqlite3?vfs=opfs`; + const { dbId } = await db('open', { filename }); + console.log('initialized db with ID ', dbId, 'at', filename); + this.dbId_ = dbId; + this.db_ = db; + } + + public sqliteErrorToJsError(error: Error) { + return error; + } + + public selectOne(sql: string, params: string[] = []) { + console.log('selectOne', sql); + return new Promise(async (resolve, reject) => { + let resolved = false; + await this.db_('exec', { + dbId: this.dbId_, + sql, + bind: params, + rowMode: 'object', + callback: ((result: RowResult) => { + console.log('got', result) + if (result.rowNumber !== 1) return; + resolved = true; + resolve(result.row); + }), + }).catch(reject); + + if (!resolved) { + resolve(null); + } + }); + } + + public async selectAll(sql: string, params: any = null) { + const results: unknown[] = []; + await this.db_('exec', { + dbId: this.dbId_, + sql, + bind: params, + rowMode: 'object', + callback: ((rowResult: RowResult) => { + if (rowResult.rowNumber) { + while (results.length < rowResult.rowNumber) { + results.push(null); + } + results[rowResult.rowNumber - 1] = rowResult.row; + } + }), + }); + + return results; + } + + public loadExtension(path: string) { + throw new Error(`No extension support for ${path} in sqlite wasm`); + } + + public async exec(sql: string, params: string[]|null = null) { + console.log('preExec', sql); + + const result = await this.db_('exec', { + dbId: this.dbId_, + sql, + bind: params, + }); + console.log('exec', sql, result); + return result; + } + + public lastInsertId() { + return this.lastInsertId_; + } +} \ No newline at end of file diff --git a/packages/app-mobile/utils/fs-driver-web.js b/packages/app-mobile/utils/fs-driver-web.js deleted file mode 100644 index 4e7138aaaf9..00000000000 --- a/packages/app-mobile/utils/fs-driver-web.js +++ /dev/null @@ -1,397 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const fs_driver_base_1 = require("@joplin/lib/fs-driver-base"); -const RNFetchBlob = require('rn-fetch-blob').default; -const RNFS = require("react-native-fs"); -const DocumentPicker = require('react-native-document-picker').default; -const react_native_saf_x_1 = require("@joplin/react-native-saf-x"); -const react_native_saf_x_2 = require("@joplin/react-native-saf-x"); -const react_native_1 = require("react-native"); -const tar = require("tar-stream"); -const path_1 = require("path"); -const buffer_1 = require("buffer"); -const Logger_1 = require("@joplin/utils/Logger"); -const logger = Logger_1.default.create('fs-driver-rn'); -const ANDROID_URI_PREFIX = 'content://'; -function isScopedUri(path) { - return path.includes(ANDROID_URI_PREFIX); -} -class FsDriverRN extends fs_driver_base_1.default { - appendFileSync() { - throw new Error('Not implemented'); - } - // Encoding can be either "utf8" or "base64" - appendFile(path, content, encoding = 'base64') { - if (isScopedUri(path)) { - return react_native_saf_x_2.default.writeFile(path, content, { encoding: encoding, append: true }); - } - return RNFS.appendFile(path, content, encoding); - } - // Encoding can be either "utf8" or "base64" - writeFile(path, content, encoding = 'base64') { - if (isScopedUri(path)) { - return react_native_saf_x_2.default.writeFile(path, content, { encoding: encoding }); - } - // We need to use rn-fetch-blob here due to this bug: - // https://github.com/itinance/react-native-fs/issues/700 - return RNFetchBlob.fs.writeFile(path, content, encoding); - } - // same as rm -rf - remove(path) { - return __awaiter(this, void 0, void 0, function* () { - return yield this.unlink(path); - }); - } - // Returns a format compatible with Node.js format - rnfsStatToStd_(stat, path) { - let birthtime; - const mtime = stat.lastModified ? new Date(stat.lastModified) : stat.mtime; - if (stat.lastModified) { - birthtime = new Date(stat.lastModified); - } - else if (stat.ctime) { - // Confusingly, "ctime" normally means "change time" but here it's used as "creation time". Also sometimes it is null - birthtime = stat.ctime; - } - else { - birthtime = stat.mtime; - } - return { - birthtime, - mtime, - isDirectory: () => stat.type ? stat.type === 'directory' : stat.isDirectory(), - path: path, - size: stat.size, - }; - } - readDirStats(path, options = null) { - return __awaiter(this, void 0, void 0, function* () { - if (!options) - options = {}; - if (!('recursive' in options)) - options.recursive = false; - const isScoped = isScopedUri(path); - let stats = []; - try { - if (isScoped) { - stats = yield react_native_saf_x_2.default.listFiles(path); - } - else { - stats = yield RNFS.readDir(path); - } - } - catch (error) { - throw new Error(`Could not read directory: ${path}: ${error.message}`); - } - let output = []; - for (let i = 0; i < stats.length; i++) { - const stat = stats[i]; - const relativePath = (isScoped ? stat.uri : stat.path).substr(path.length + 1); - const standardStat = this.rnfsStatToStd_(stat, relativePath); - output.push(standardStat); - if (isScoped) { - // readUriDirStatsHandleRecursion_ expects stat to have a URI property. - // Use the original stat. - output = yield this.readUriDirStatsHandleRecursion_(stat, output, options); - } - else { - output = yield this.readDirStatsHandleRecursion_(path, standardStat, output, options); - } - } - return output; - }); - } - readUriDirStatsHandleRecursion_(stat, output, options) { - return __awaiter(this, void 0, void 0, function* () { - if (options.recursive && stat.type === 'directory') { - const subStats = yield this.readDirStats(stat.uri, options); - for (let j = 0; j < subStats.length; j++) { - const subStat = subStats[j]; - output.push(subStat); - } - } - return output; - }); - } - move(source, dest) { - return __awaiter(this, void 0, void 0, function* () { - if (isScopedUri(source) || isScopedUri(dest)) { - yield react_native_saf_x_2.default.moveFile(source, dest, { replaceIfDestinationExists: true }); - } - return RNFS.moveFile(source, dest); - }); - } - rename(source, dest) { - return __awaiter(this, void 0, void 0, function* () { - if (isScopedUri(source) || isScopedUri(dest)) { - yield react_native_saf_x_2.default.rename(source, dest); - } - return RNFS.moveFile(source, dest); - }); - } - exists(path) { - return __awaiter(this, void 0, void 0, function* () { - if (isScopedUri(path)) { - return react_native_saf_x_2.default.exists(path); - } - return RNFS.exists(path); - }); - } - mkdir(path) { - return __awaiter(this, void 0, void 0, function* () { - if (isScopedUri(path)) { - yield react_native_saf_x_2.default.mkdir(path); - return; - } - // Also creates parent directories: Works like mkdir -p - return RNFS.mkdir(path); - }); - } - stat(path) { - return __awaiter(this, void 0, void 0, function* () { - try { - let r; - if (isScopedUri(path)) { - r = yield react_native_saf_x_2.default.stat(path); - } - else { - r = yield RNFS.stat(path); - } - return this.rnfsStatToStd_(r, path); - } - catch (error) { - if (error && (error.code === 'ENOENT' || !(yield this.exists(path)))) { - // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } - // or { [Error: The file {file} couldn’t be opened because there is no such file.], code: 'ENSCOCOAERRORDOMAIN260' } - // which unfortunately does not have a proper error code. Can be ignored. - return null; - } - else { - throw error; - } - } - }); - } - // NOTE: DOES NOT WORK - no error is thrown and the function is called with the right - // arguments but the function returns `false` and the timestamp is not set. - // Current setTimestamp is not really used so keep it that way, but careful if it - // becomes needed. - setTimestamp() { - return __awaiter(this, void 0, void 0, function* () { - // return RNFS.touch(path, timestampDate, timestampDate); - }); - } - open(path, mode) { - return __awaiter(this, void 0, void 0, function* () { - if (isScopedUri(path)) { - throw new Error('open() not implemented in FsDriverAndroid'); - } - // Note: RNFS.read() doesn't provide any way to know if the end of file has been reached. - // So instead we stat the file here and use stat.size to manually check for end of file. - // Bug: https://github.com/itinance/react-native-fs/issues/342 - const stat = yield this.stat(path); - return { - path: path, - offset: 0, - mode: mode, - stat: stat, - }; - }); - } - close() { - // Nothing - return null; - } - readFile(path, encoding = 'utf8') { - if (encoding === 'Buffer') - throw new Error('Raw buffer output not supported for FsDriverRN.readFile'); - if (isScopedUri(path)) { - return react_native_saf_x_2.default.readFile(path, { encoding: encoding }); - } - return RNFS.readFile(path, encoding); - } - // Always overwrite destination - copy(source, dest) { - return __awaiter(this, void 0, void 0, function* () { - let retry = false; - try { - if (isScopedUri(source) || isScopedUri(dest)) { - yield react_native_saf_x_2.default.copyFile(source, dest, { replaceIfDestinationExists: true }); - return; - } - yield RNFS.copyFile(source, dest); - } - catch (error) { - // On iOS it will throw an error if the file already exist - retry = true; - yield this.unlink(dest); - } - if (retry) { - if (isScopedUri(source) || isScopedUri(dest)) { - yield react_native_saf_x_2.default.copyFile(source, dest, { replaceIfDestinationExists: true }); - } - else { - yield RNFS.copyFile(source, dest); - } - } - }); - } - unlink(path) { - return __awaiter(this, void 0, void 0, function* () { - try { - if (isScopedUri(path)) { - yield react_native_saf_x_2.default.unlink(path); - return; - } - yield RNFS.unlink(path); - } - catch (error) { - if (error && ((error.message && error.message.indexOf('exist') >= 0) || error.code === 'ENOENT')) { - // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } - // which unfortunately does not have a proper error code. Can be ignored. - } - else { - throw error; - } - } - }); - } - readFileChunk(handle, length, encoding = 'base64') { - return __awaiter(this, void 0, void 0, function* () { - if (handle.offset + length > handle.stat.size) { - length = handle.stat.size - handle.offset; - } - if (!length) - return null; - const output = yield RNFS.read(handle.path, length, handle.offset, encoding); - // eslint-disable-next-line require-atomic-updates - handle.offset += length; - return output ? output : null; - }); - } - resolve(path) { - throw new Error(`Not implemented: resolve(): ${path}`); - } - resolveRelativePathWithinDir(_baseDir, relativePath) { - throw new Error(`Not implemented: resolveRelativePathWithinDir(): ${relativePath}`); - } - md5File(path) { - return __awaiter(this, void 0, void 0, function* () { - throw new Error(`Not implemented: md5File(): ${path}`); - }); - } - tarExtract(_options) { - return __awaiter(this, void 0, void 0, function* () { - throw new Error('Not implemented: tarExtract'); - }); - } - tarCreate(options, filePaths) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - // Choose a default cwd if not given - const cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : RNFS.DocumentDirectoryPath; - const file = (0, path_1.resolve)(cwd, options.file); - if (yield this.exists(file)) { - throw new Error('Error! Destination already exists'); - } - const pack = tar.pack(); - for (const path of filePaths) { - const absPath = (0, path_1.resolve)(cwd, path); - const stat = yield this.stat(absPath); - const sizeBytes = stat.size; - const entry = pack.entry({ name: path, size: sizeBytes }, (error) => { - if (error) { - logger.error(`Tar error: ${error}`); - } - }); - const chunkSize = 1024 * 100; // 100 KiB - for (let offset = 0; offset < sizeBytes; offset += chunkSize) { - // The RNFS documentation suggests using base64 for binary files. - const part = yield RNFS.read(absPath, chunkSize, offset, 'base64'); - entry.write(buffer_1.Buffer.from(part, 'base64')); - } - entry.end(); - } - pack.finalize(); - // The streams used by tar-stream seem not to support a chunk size - // (it seems despite the typings provided). - let data = null; - while ((data = pack.read()) !== null) { - const buff = buffer_1.Buffer.from(data); - const base64Data = buff.toString('base64'); - yield this.appendFile(file, base64Data, 'base64'); - } - }); - } - getExternalDirectoryPath() { - return __awaiter(this, void 0, void 0, function* () { - let directory; - if (this.isUsingAndroidSAF()) { - const doc = yield (0, react_native_saf_x_2.openDocumentTree)(true); - if (doc === null || doc === void 0 ? void 0 : doc.uri) { - directory = doc === null || doc === void 0 ? void 0 : doc.uri; - } - } - else { - directory = RNFS.ExternalDirectoryPath; - } - return directory; - }); - } - isUsingAndroidSAF() { - return react_native_1.Platform.OS === 'android' && react_native_1.Platform.Version > 28; - } - /** always returns an array */ - pickDocument(options) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const { multiple = false } = options || {}; - let result; - try { - if (this.isUsingAndroidSAF()) { - result = yield (0, react_native_saf_x_1.openDocument)({ multiple }); - if (!result) { - // to catch the error down below using the 'cancel' keyword - throw new Error('User canceled document picker'); - } - result = result.map(r => { - r.type = r.mime; - r.fileCopyUri = r.uri; - return r; - }); - } - else { - // the result is an array - if (multiple) { - result = yield DocumentPicker.pick({ allowMultiSelection: true }); - } - else { - result = [yield DocumentPicker.pick()]; - } - } - } - catch (error) { - if (DocumentPicker.isCancel(error) || ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('cancel'))) { - // eslint-disable-next-line no-console - console.info('pickDocuments: user has cancelled'); - return null; - } - else { - throw error; - } - } - return result; - }); - } -} -exports.default = FsDriverRN; -//# sourceMappingURL=fs-driver-web.js.map \ No newline at end of file diff --git a/packages/app-mobile/utils/fs-driver-web.ts b/packages/app-mobile/utils/fs-driver-web.ts deleted file mode 100644 index a9e78db3d05..00000000000 --- a/packages/app-mobile/utils/fs-driver-web.ts +++ /dev/null @@ -1,371 +0,0 @@ -import FsDriverBase, { ReadDirStatsOptions } from '@joplin/lib/fs-driver-base'; -const RNFetchBlob = require('rn-fetch-blob').default; -import * as RNFS from 'react-native-fs'; -const DocumentPicker = require('react-native-document-picker').default; -import { openDocument } from '@joplin/react-native-saf-x'; -import RNSAF, { Encoding, DocumentFileDetail, openDocumentTree } from '@joplin/react-native-saf-x'; -import { Platform } from 'react-native'; -import * as tar from 'tar-stream'; -import { resolve } from 'path'; -import { Buffer } from 'buffer'; -import Logger from '@joplin/utils/Logger'; - -const logger = Logger.create('fs-driver-rn'); - -const ANDROID_URI_PREFIX = 'content://'; - -function isScopedUri(path: string) { - return path.includes(ANDROID_URI_PREFIX); -} - -export default class FsDriverRN extends FsDriverBase { - public appendFileSync() { - throw new Error('Not implemented'); - } - - // Encoding can be either "utf8" or "base64" - public appendFile(path: string, content: any, encoding = 'base64') { - if (isScopedUri(path)) { - return RNSAF.writeFile(path, content, { encoding: encoding as Encoding, append: true }); - } - return RNFS.appendFile(path, content, encoding); - } - - // Encoding can be either "utf8" or "base64" - public writeFile(path: string, content: any, encoding = 'base64') { - if (isScopedUri(path)) { - return RNSAF.writeFile(path, content, { encoding: encoding as Encoding }); - } - // We need to use rn-fetch-blob here due to this bug: - // https://github.com/itinance/react-native-fs/issues/700 - return RNFetchBlob.fs.writeFile(path, content, encoding); - } - - // same as rm -rf - public async remove(path: string) { - return await this.unlink(path); - } - - // Returns a format compatible with Node.js format - private rnfsStatToStd_(stat: any, path: string) { - let birthtime; - const mtime = stat.lastModified ? new Date(stat.lastModified) : stat.mtime; - if (stat.lastModified) { - birthtime = new Date(stat.lastModified); - } else if (stat.ctime) { - // Confusingly, "ctime" normally means "change time" but here it's used as "creation time". Also sometimes it is null - birthtime = stat.ctime; - } else { - birthtime = stat.mtime; - } - return { - birthtime, - mtime, - isDirectory: () => stat.type ? stat.type === 'directory' : stat.isDirectory(), - path: path, - size: stat.size, - }; - } - - public async readDirStats(path: string, options: any = null) { - if (!options) options = {}; - if (!('recursive' in options)) options.recursive = false; - - const isScoped = isScopedUri(path); - - let stats: any[] = []; - try { - if (isScoped) { - stats = await RNSAF.listFiles(path); - } else { - stats = await RNFS.readDir(path); - } - } catch (error) { - throw new Error(`Could not read directory: ${path}: ${error.message}`); - } - - let output: any[] = []; - for (let i = 0; i < stats.length; i++) { - const stat = stats[i]; - const relativePath = (isScoped ? stat.uri : stat.path).substr(path.length + 1); - const standardStat = this.rnfsStatToStd_(stat, relativePath); - output.push(standardStat); - - if (isScoped) { - // readUriDirStatsHandleRecursion_ expects stat to have a URI property. - // Use the original stat. - output = await this.readUriDirStatsHandleRecursion_(stat, output, options); - } else { - output = await this.readDirStatsHandleRecursion_(path, standardStat, output, options); - } - } - return output; - } - - - protected async readUriDirStatsHandleRecursion_(stat: DocumentFileDetail, output: DocumentFileDetail[], options: ReadDirStatsOptions) { - if (options.recursive && stat.type === 'directory') { - const subStats = await this.readDirStats(stat.uri, options); - for (let j = 0; j < subStats.length; j++) { - const subStat = subStats[j]; - output.push(subStat); - } - } - return output; - } - - public async move(source: string, dest: string) { - if (isScopedUri(source) || isScopedUri(dest)) { - await RNSAF.moveFile(source, dest, { replaceIfDestinationExists: true }); - } - return RNFS.moveFile(source, dest); - } - - public async rename(source: string, dest: string) { - if (isScopedUri(source) || isScopedUri(dest)) { - await RNSAF.rename(source, dest); - } - return RNFS.moveFile(source, dest); - } - - public async exists(path: string) { - if (isScopedUri(path)) { - return RNSAF.exists(path); - } - return RNFS.exists(path); - } - - public async mkdir(path: string) { - if (isScopedUri(path)) { - await RNSAF.mkdir(path); - return; - } - - // Also creates parent directories: Works like mkdir -p - return RNFS.mkdir(path); - } - - public async stat(path: string) { - try { - let r; - if (isScopedUri(path)) { - r = await RNSAF.stat(path); - } else { - r = await RNFS.stat(path); - } - return this.rnfsStatToStd_(r, path); - } catch (error) { - if (error && (error.code === 'ENOENT' || !(await this.exists(path)))) { - // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } - // or { [Error: The file {file} couldn’t be opened because there is no such file.], code: 'ENSCOCOAERRORDOMAIN260' } - // which unfortunately does not have a proper error code. Can be ignored. - return null; - } else { - throw error; - } - } - } - - // NOTE: DOES NOT WORK - no error is thrown and the function is called with the right - // arguments but the function returns `false` and the timestamp is not set. - // Current setTimestamp is not really used so keep it that way, but careful if it - // becomes needed. - public async setTimestamp() { - // return RNFS.touch(path, timestampDate, timestampDate); - } - - public async open(path: string, mode: number) { - if (isScopedUri(path)) { - throw new Error('open() not implemented in FsDriverAndroid'); - } - // Note: RNFS.read() doesn't provide any way to know if the end of file has been reached. - // So instead we stat the file here and use stat.size to manually check for end of file. - // Bug: https://github.com/itinance/react-native-fs/issues/342 - const stat = await this.stat(path); - return { - path: path, - offset: 0, - mode: mode, - stat: stat, - }; - } - - public close(): Promise { - // Nothing - return null; - } - - public readFile(path: string, encoding = 'utf8') { - if (encoding === 'Buffer') throw new Error('Raw buffer output not supported for FsDriverRN.readFile'); - if (isScopedUri(path)) { - return RNSAF.readFile(path, { encoding: encoding as Encoding }); - } - return RNFS.readFile(path, encoding); - } - - // Always overwrite destination - public async copy(source: string, dest: string) { - let retry = false; - try { - if (isScopedUri(source) || isScopedUri(dest)) { - await RNSAF.copyFile(source, dest, { replaceIfDestinationExists: true }); - return; - } - await RNFS.copyFile(source, dest); - } catch (error) { - // On iOS it will throw an error if the file already exist - retry = true; - await this.unlink(dest); - } - - if (retry) { - if (isScopedUri(source) || isScopedUri(dest)) { - await RNSAF.copyFile(source, dest, { replaceIfDestinationExists: true }); - } else { - await RNFS.copyFile(source, dest); - } - } - } - - public async unlink(path: string) { - try { - if (isScopedUri(path)) { - await RNSAF.unlink(path); - return; - } - await RNFS.unlink(path); - } catch (error) { - if (error && ((error.message && error.message.indexOf('exist') >= 0) || error.code === 'ENOENT')) { - // Probably { [Error: File does not exist] framesToPop: 1, code: 'EUNSPECIFIED' } - // which unfortunately does not have a proper error code. Can be ignored. - } else { - throw error; - } - } - } - - public async readFileChunk(handle: any, length: number, encoding = 'base64') { - if (handle.offset + length > handle.stat.size) { - length = handle.stat.size - handle.offset; - } - - if (!length) return null; - const output = await RNFS.read(handle.path, length, handle.offset, encoding); - // eslint-disable-next-line require-atomic-updates - handle.offset += length; - return output ? output : null; - } - - public resolve(path: string) { - throw new Error(`Not implemented: resolve(): ${path}`); - } - - public resolveRelativePathWithinDir(_baseDir: string, relativePath: string) { - throw new Error(`Not implemented: resolveRelativePathWithinDir(): ${relativePath}`); - } - - public async md5File(path: string): Promise { - throw new Error(`Not implemented: md5File(): ${path}`); - } - - public async tarExtract(_options: any) { - throw new Error('Not implemented: tarExtract'); - } - - public async tarCreate(options: any, filePaths: string[]) { - // Choose a default cwd if not given - const cwd = options.cwd ?? RNFS.DocumentDirectoryPath; - const file = resolve(cwd, options.file); - - if (await this.exists(file)) { - throw new Error('Error! Destination already exists'); - } - - const pack = tar.pack(); - - for (const path of filePaths) { - const absPath = resolve(cwd, path); - const stat = await this.stat(absPath); - const sizeBytes: number = stat.size; - - const entry = pack.entry({ name: path, size: sizeBytes }, (error) => { - if (error) { - logger.error(`Tar error: ${error}`); - } - }); - - const chunkSize = 1024 * 100; // 100 KiB - for (let offset = 0; offset < sizeBytes; offset += chunkSize) { - // The RNFS documentation suggests using base64 for binary files. - const part = await RNFS.read(absPath, chunkSize, offset, 'base64'); - entry.write(Buffer.from(part, 'base64')); - } - entry.end(); - } - - pack.finalize(); - - // The streams used by tar-stream seem not to support a chunk size - // (it seems despite the typings provided). - let data: number[]|null = null; - while ((data = pack.read()) !== null) { - const buff = Buffer.from(data); - const base64Data = buff.toString('base64'); - await this.appendFile(file, base64Data, 'base64'); - } - } - - public async getExternalDirectoryPath(): Promise { - let directory; - if (this.isUsingAndroidSAF()) { - const doc = await openDocumentTree(true); - if (doc?.uri) { - directory = doc?.uri; - } - } else { - directory = RNFS.ExternalDirectoryPath; - } - return directory; - } - - public isUsingAndroidSAF() { - return Platform.OS === 'android' && Platform.Version > 28; - } - - /** always returns an array */ - public async pickDocument(options: { multiple: false }) { - const { multiple = false } = options || {}; - let result; - try { - if (this.isUsingAndroidSAF()) { - result = await openDocument({ multiple }); - if (!result) { - // to catch the error down below using the 'cancel' keyword - throw new Error('User canceled document picker'); - } - result = result.map(r => { - (r.type as string) = r.mime; - ((r as any).fileCopyUri as string) = r.uri; - return r; - }); - } else { - // the result is an array - if (multiple) { - result = await DocumentPicker.pick({ allowMultiSelection: true }); - } else { - result = [await DocumentPicker.pick()]; - } - } - } catch (error) { - if (DocumentPicker.isCancel(error) || error?.message?.includes('cancel')) { - // eslint-disable-next-line no-console - console.info('pickDocuments: user has cancelled'); - return null; - } else { - throw error; - } - } - - return result; - } -} diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts index 8bf4f989957..516bd6277b1 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts @@ -351,6 +351,10 @@ export default class FsDriverRN extends FsDriverBase { return directory; } + public getCacheDirectoryPath() { + return RNFS.CachesDirectoryPath; + } + public isUsingAndroidSAF() { return Platform.OS === 'android' && Platform.Version > 28; } diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts new file mode 100644 index 00000000000..0721302cce7 --- /dev/null +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -0,0 +1,263 @@ +import { dirname, basename, resolve } from 'path'; +import FsDriverBase, { ReadDirStatsOptions, Stat } from '@joplin/lib/fs-driver-base'; +import tarExtract, { TarExtractOptions } from './tarExtract'; +import tarCreate, { TarCreateOptions } from './tarCreate'; +import { Buffer } from 'buffer'; +const md5 = require('md5'); + +type FileHandle = { + reader: ReadableStreamDefaultReader; + handle: FileSystemFileHandle; + buffered: Buffer; + done: boolean; +}; + +const removeReservedWords = (path: string) => { + return path.replace(/(tmp)/g, '_$1'); +}; + +declare global { + interface FileSystemDirectoryHandle { + keys(): AsyncIterable; + } +} + +export default class FsDriverWeb extends FsDriverBase { + private fsRoot_: FileSystemDirectoryHandle; + private directoryHandleCache_: Map = new Map(); + private initPromise_: Promise; + + public constructor() { + super(); + this.initPromise_ = (async () => { + console.log('Get root'); + try { + this.fsRoot_ = await (await navigator.storage.getDirectory()).getDirectoryHandle('joplin-web', { create: true }); + } catch (error) { + console.error('Failed to create fsDriver:', error); + throw error; + } + })(); + } + + private async pathToDirectoryHandle_(path: string, create: boolean = false): Promise { + await this.initPromise_; + + if (this.directoryHandleCache_.has(path)) { + return this.directoryHandleCache_.get(path); + } + + const parentDirs = dirname(path); + if (parentDirs && !['/', '.'].includes(parentDirs)) { + const parent = await this.pathToDirectoryHandle_(parentDirs, create); + console.log('get dir handle', basename(path)); + const folderName = removeReservedWords(basename(path)); + + let handle: FileSystemDirectoryHandle; + try { + handle = await parent.getDirectoryHandle(folderName, { create }); + console.log('handle', handle); + } catch (error) { + // TODO: Handle this better + console.warn(error); + handle = null; + } + + if (handle) { + this.directoryHandleCache_.set(path, handle); + } + + return handle; + } + return this.fsRoot_; + } + + private async pathToFileHandle_(path: string, create = false): Promise { + await this.initPromise_; + + const parent = await this.pathToDirectoryHandle_(dirname(path)); + console.log('GETname', basename(path)) + try { + return parent.getFileHandle(removeReservedWords(basename(path)), { create }); + } catch (error) { + if (!await this.exists(path)) { + return null; + } else { + throw error; + } + } + } + + private async openWriteStream_(path: string, options?: FileSystemCreateWritableOptions) { + const handle = await this.pathToFileHandle_(path, true); + const writer = (await handle.createWritable(options)).getWriter(); + await writer.ready; + return { writer, handle }; + } + + public override async writeFile( + path: string, + string: string, + encoding: BufferEncoding = 'base64', + options?: FileSystemCreateWritableOptions, + ) { + const { writer } = await this.openWriteStream_(path, options); + if (encoding === 'utf-8' || encoding === 'utf8') { + const encoder = new TextEncoder(); + await writer.write(encoder.encode(string)); + } else { + await writer.write(Buffer.from(string, encoding).buffer); + } + await writer.close(); + } + + public override async appendFile(path: string, content: string, encoding?: BufferEncoding) { + return this.writeFile(path, content, encoding, { keepExistingData: true }); + } + + public override async remove(path: string) { + this.directoryHandleCache_.clear(); + const dirHandle = await this.pathToDirectoryHandle_(dirname(path)); + if (dirHandle) { + await dirHandle.removeEntry(basename(path), { recursive: true }); + } else { + throw new Error(`ENOENT: Parent directory of path ${JSON.stringify(path)} does not exist.`); + } + } + + public async readFile(path: string, encoding: BufferEncoding = 'utf-8') { + const handle = await this.pathToFileHandle_(path); + const file = await handle.getFile(); + if (encoding === 'utf-8' || encoding === 'utf8') { + return await file.text(); + } else { + const buffer = Buffer.from(await file.arrayBuffer()); + return buffer.toString(encoding); + } + } + + public override async open(path: string, _mode: string = 'r'): Promise { + const handle = await this.pathToFileHandle_(path); + return { + handle, + // TODO: Extra casting required by NodeJS types conflicting with DOM types. + reader: ((await handle.getFile()).stream() as unknown as ReadableStream).getReader(), + buffered: Buffer.from([]), + done: false, + }; + } + + public override async readFileChunk(handle: FileHandle, length: number, encoding: BufferEncoding = 'base64') { + let read: Buffer; + + if (handle.buffered.byteLength < length && !handle.done) { + const { done, value } = await handle.reader.read(); + handle.done = done; + read = Buffer.concat([handle.buffered, Buffer.from(value)]); + } else { + read = handle.buffered; + } + + const result = read.subarray(0, length); + handle.buffered = result.subarray(length); + return result.toString(encoding); + } + + public override async close(handle: FileHandle) { + handle.reader.releaseLock(); + } + + public override async mkdir(path: string) { + await this.pathToDirectoryHandle_(path, true); + } + + public override async copy(from: string, to: string) { + const fromHandle = await this.pathToFileHandle_(from); + const toHandle = await this.pathToFileHandle_(to, true); + + const fromFile = await fromHandle.getFile(); + const writer = (await toHandle.createWritable()).getWriter(); + writer.write(fromFile); + writer.close(); + } + + public override async stat(path: string): Promise { + const dirHandle = await this.pathToDirectoryHandle_(path); + const fileHandle = await this.pathToFileHandle_(path); + if (!dirHandle && !fileHandle) return null; + + + const size = await (async () => { + if (dirHandle) return 0; + return (await fileHandle.getFile()).size + })(); + + return { + birthtime: new Date(0), + mtime: new Date(0), + path: path, + size, + isDirectory: () => !!dirHandle, + }; + } + + public override async readDirStats(path: string, options: ReadDirStatsOptions = { recursive: false }): Promise { + const dirHandle = await this.pathToDirectoryHandle_(path); + if (!dirHandle) return null; + + const result: Stat[] = []; + for await (const child of dirHandle.keys()) { + const childPath = `${path}/${child}`; + const stat = await this.stat(childPath); + result.push(stat); + if (options.recursive) { + result.push(...await this.readDirStats(childPath)); + } + } + return result; + } + + public override async exists(path: string) { + const parentDir = await this.pathToDirectoryHandle_(dirname(path)); + console.log('exists', path); + if (!parentDir) return false; + + const target = basename(path); + for await (const key of (parentDir as any).keys()) { + if (key === target) return true; + } + return false; + } + + public resolve(...paths: string[]): string { + return resolve(...paths); + } + + public override async md5File(path: string): Promise { + const fileData = Buffer.from(await this.readFile(path, 'base64'), 'base64'); + return md5(fileData); + } + + public override async tarExtract(options: TarExtractOptions) { + await tarExtract({ + cwd: '.', + ...options, + }); + } + + public override async tarCreate(options: TarCreateOptions, filePaths: string[]) { + await tarCreate({ + cwd: '.', + ...options, + }, filePaths); + } + + public override getCacheDirectoryPath(): string { + return '/cache/'; + } + + public override getAppDirectoryPath(): string { + return '/app/'; + } +} + diff --git a/packages/app-mobile/utils/fs-driver/tarCreate.ts b/packages/app-mobile/utils/fs-driver/tarCreate.ts index bf6d7695f1f..322d6474607 100644 --- a/packages/app-mobile/utils/fs-driver/tarCreate.ts +++ b/packages/app-mobile/utils/fs-driver/tarCreate.ts @@ -1,6 +1,5 @@ import { pack as tarStreamPack } from 'tar-stream'; import { resolve } from 'path'; -import * as RNFS from 'react-native-fs'; import Logger from '@joplin/utils/Logger'; import { chunkSize } from './constants'; @@ -8,7 +7,7 @@ import shim from '@joplin/lib/shim'; const logger = Logger.create('fs-driver-rn'); -interface TarCreateOptions { +export interface TarCreateOptions { cwd: string; file: string; } @@ -18,7 +17,7 @@ interface TarCreateOptions { const tarCreate = async (options: TarCreateOptions, filePaths: string[]) => { // Choose a default cwd if not given - const cwd = options.cwd ?? RNFS.DocumentDirectoryPath; + const cwd = options.cwd ?? shim.fsDriver().getAppDirectoryPath(); const file = resolve(cwd, options.file); const fsDriver = shim.fsDriver(); @@ -39,9 +38,10 @@ const tarCreate = async (options: TarCreateOptions, filePaths: string[]) => { } }); + const handle = await shim.fsDriver().open(absPath, 'rw'); + for (let offset = 0; offset < sizeBytes; offset += chunkSize) { - // The RNFS documentation suggests using base64 for binary files. - const part = await RNFS.read(absPath, chunkSize, offset, 'base64'); + const part = await shim.fsDriver().readFileChunk(handle, chunkSize, 'base64'); entry.write(Buffer.from(part, 'base64')); } entry.end(); diff --git a/packages/app-mobile/utils/fs-driver/tarExtract.ts b/packages/app-mobile/utils/fs-driver/tarExtract.ts index 0fedfc129c5..85daf86744a 100644 --- a/packages/app-mobile/utils/fs-driver/tarExtract.ts +++ b/packages/app-mobile/utils/fs-driver/tarExtract.ts @@ -3,7 +3,7 @@ import { resolve, dirname } from 'path'; import shim from '@joplin/lib/shim'; import { chunkSize } from './constants'; -interface TarExtractOptions { +export interface TarExtractOptions { cwd: string; file: string; } diff --git a/packages/app-mobile/utils/shim-init-react.js b/packages/app-mobile/utils/shim-init-react/index.ts similarity index 79% rename from packages/app-mobile/utils/shim-init-react.js rename to packages/app-mobile/utils/shim-init-react/index.ts index 16bd2b0536d..7ffe7effb75 100644 --- a/packages/app-mobile/utils/shim-init-react.js +++ b/packages/app-mobile/utils/shim-init-react/index.ts @@ -1,12 +1,11 @@ -const shim = require('@joplin/lib/shim').default; +import type ShimType from '@joplin/lib/shim'; +import shimInitShared from './shimInitShared'; + +const shim: typeof ShimType = require('@joplin/lib/shim').default; const { GeolocationReact } = require('./geolocation-react.js'); -const PoorManIntervals = require('@joplin/lib/PoorManIntervals').default; const RNFetchBlob = require('rn-fetch-blob').default; const { generateSecureRandom } = require('react-native-securerandom'); -const FsDriverRN = require('./fs-driver/fs-driver-rn').default; -const { Buffer } = require('buffer'); -const { Linking, Platform } = require('react-native'); -const showMessageBox = require('./showMessageBox.js').default; +import FsDriverRN from '../fs-driver/fs-driver-rn'; const mimeUtils = require('@joplin/lib/mime-utils.js'); const { basename, fileExtension } = require('@joplin/lib/path-utils'); const uuid = require('@joplin/lib/uuid').default; @@ -14,17 +13,9 @@ const Resource = require('@joplin/lib/models/Resource').default; const { getLocales } = require('react-native-localize'); const { setLocale, defaultLocale, closestSupportedLocale } = require('@joplin/lib/locale'); -const injectedJs = { - webviewLib: require('@joplin/lib/rnInjectedJs/webviewLib'), - codeMirrorBundle: require('../lib/rnInjectedJs/codeMirrorBundle.bundle'), - svgEditorBundle: require('../lib/rnInjectedJs/svgEditorBundle.bundle'), - pluginBackgroundPage: require('../lib/rnInjectedJs/pluginBackgroundPage.bundle'), - noteBodyViewerBundle: require('../lib/rnInjectedJs/noteBodyViewerBundle.bundle'), -}; -function shimInit() { +export function shimInit() { shim.Geolocation = GeolocationReact; - shim.sjclModule = require('@joplin/lib/vendor/sjcl-rn.js'); shim.fsDriver = () => { if (!shim.fsDriver_) { @@ -49,7 +40,7 @@ function shimInit() { /* eslint-disable no-console */ - shim.debugFetch = async (url, options = null) => { + (shim as any).debugFetch = async (url: string, options: any = null) => { options = { method: 'GET', headers: {}, @@ -60,7 +51,7 @@ function shimInit() { const xhr = new XMLHttpRequest(); xhr.open(options.method, url, true); - for (const [key, value] of Object.entries(options.headers)) { + for (const [key, value] of Object.entries(options.headers as Record)) { xhr.setRequestHeader(key, value); } @@ -91,7 +82,7 @@ function shimInit() { /* eslint-enable */ - shim.detectAndSetLocale = (Setting) => { + shim.detectAndSetLocale = (Setting: any) => { // [ // { // "countryCode": "US", @@ -179,7 +170,7 @@ function shimInit() { try { const response = await shim.fetchWithRetry(doFetchBlob, options); - // Returns an object that's roughtly compatible with a standard Response object + // Returns an object that's roughly compatible with a standard Response object const output = { ok: response.respInfo.status < 400, path: response.data, @@ -212,7 +203,7 @@ function shimInit() { trusty: options.ignoreTlsErrors, }).fetch(method, url, headers, RNFetchBlob.wrap(options.path)); - // Returns an object that's roughtly compatible with a standard Response object + // Returns an object that's roughly compatible with a standard Response object return { ok: response.respInfo.status < 400, data: response.data, @@ -230,34 +221,10 @@ function shimInit() { return RNFetchBlob.fs.readFile(path, 'base64'); }; - shim.stringByteLength = function(string) { - return Buffer.byteLength(string, 'utf-8'); - }; - - shim.Buffer = Buffer; - - shim.showMessageBox = showMessageBox; - - shim.openUrl = url => { - Linking.openURL(url); - }; - shim.httpAgent = () => { return null; }; - shim.waitForFrame = () => { - return new Promise((resolve) => { - requestAnimationFrame(() => { - resolve(); - }); - }); - }; - - shim.mobilePlatform = () => { - return Platform.OS; - }; - shim.appVersion = () => { const p = require('react-native-version-info').default; return p.appVersion; @@ -297,27 +264,6 @@ function shimInit() { return resource; }; - shim.injectedJs = function(name) { - if (!(name in injectedJs)) throw new Error(`Cannot find injectedJs file (add it to "injectedJs" object): ${name}`); - return injectedJs[name]; - }; - - shim.setTimeout = (fn, interval) => { - return PoorManIntervals.setTimeout(fn, interval); - }; - - shim.setInterval = (fn, interval) => { - return PoorManIntervals.setInterval(fn, interval); - }; - - shim.clearTimeout = (id) => { - return PoorManIntervals.clearTimeout(id); - }; - - shim.clearInterval = (id) => { - return PoorManIntervals.clearInterval(id); - }; - + shimInitShared(); } -module.exports = { shimInit }; diff --git a/packages/app-mobile/utils/shim-init-react.web.ts b/packages/app-mobile/utils/shim-init-react/index.web.ts similarity index 53% rename from packages/app-mobile/utils/shim-init-react.web.ts rename to packages/app-mobile/utils/shim-init-react/index.web.ts index b8ccdd5eac3..00c4fb64de9 100644 --- a/packages/app-mobile/utils/shim-init-react.web.ts +++ b/packages/app-mobile/utils/shim-init-react/index.web.ts @@ -1,13 +1,8 @@ import type ShimType from '@joplin/lib/shim'; const shim: typeof ShimType = require('@joplin/lib/shim').default; -const PoorManIntervals = require('@joplin/lib/PoorManIntervals').default; -const RNFetchBlob = require('rn-fetch-blob').default; import { generateSecureRandom } from 'react-native-securerandom'; -const FsDriverRN = require('./fs-driver-rn').default; -import { Buffer } from 'buffer'; -import { Linking, Platform } from 'react-native'; -const mimeUtils = require('@joplin/lib/mime-utils.js').mime; +import * as mimeUtils from '@joplin/lib/mime-utils'; import { basename, fileExtension } from '@joplin/lib/path-utils'; import uuid from '@joplin/lib/uuid'; import Resource from '@joplin/lib/models/Resource'; @@ -15,20 +10,15 @@ import { getLocales } from 'react-native-localize'; import { setLocale, defaultLocale, closestSupportedLocale } from '@joplin/lib/locale'; import FsDriverBase from '@joplin/lib/fs-driver-base'; import Setting from '@joplin/lib/models/Setting'; - -const injectedJs = { - webviewLib: require('@joplin/lib/rnInjectedJs/webviewLib'), - codeMirrorBundle: require('../lib/rnInjectedJs/CodeMirror.bundle'), -}; +import shimInitShared from './shimInitShared'; +import FsDriverWeb from '../fs-driver/fs-driver-rn.web'; export const shimInit = () => { - shim.Geolocation = null; - shim.sjclModule = require('@joplin/lib/vendor/sjcl-rn.js'); - let fsDriver_: FsDriverBase|null = null; + shim.fsDriver = () => { if (!fsDriver_) { - fsDriver_ = new FsDriverRN(); + fsDriver_ = new FsDriverWeb(); } return fsDriver_; }; @@ -103,95 +93,16 @@ export const shimInit = () => { }, options); }; - shim.fetchBlob = async function(url, options) { - if (!options || !options.path) throw new Error('fetchBlob: target file path is missing'); - - const headers = options.headers ? options.headers : {}; - const method = options.method ? options.method : 'GET'; - const overwrite = 'overwrite' in options ? options.overwrite : true; - - const dirs = RNFetchBlob.fs.dirs; - let localFilePath = options.path; - if (localFilePath.indexOf('/') !== 0) localFilePath = `${dirs.DocumentDir}/${localFilePath}`; - - if (!overwrite) { - if (await shim.fsDriver().exists(localFilePath)) { - return { ok: true }; - } - } - - delete options.path; - delete options.overwrite; - - const doFetchBlob = () => { - return RNFetchBlob.config({ - path: localFilePath, - trusty: options.ignoreTlsErrors, - }).fetch(method, url, headers); - }; - - try { - const response = await shim.fetchWithRetry(doFetchBlob, options); - - // Returns an object that's roughtly compatible with a standard Response object - const output = { - ok: response.respInfo.status < 400, - path: response.data, - status: response.respInfo.status, - headers: response.respInfo.headers, - // If response type is 'path' then calling text() or json() (or base64()) - // on RNFetchBlob response object will make it read the file on the native thread, - // serialize it, and send over the RN bridge. - // For larger files this can cause the app to crash. - // For these type of responses we're not using the response text anyway - // so can override it here to return empty values - text: response.type === 'path' ? () => '' : response.text, - json: response.type === 'path' ? () => {} : response.json, - }; - - return output; - } catch (error) { - throw new Error(`fetchBlob: ${method} ${url}: ${error.toString()}`); - } + shim.fetchBlob = async function(_url, _options) { + throw new Error('fetchBlob: Not implemented'); }; - shim.uploadBlob = async function(url, options) { - if (!options || !options.path) throw new Error('uploadBlob: source file path is missing'); - - const headers = options.headers ? options.headers : {}; - const method = options.method ? options.method : 'POST'; - - try { - const response = await RNFetchBlob.config({ - trusty: options.ignoreTlsErrors, - }).fetch(method, url, headers, RNFetchBlob.wrap(options.path)); - - // Returns an object that's roughtly compatible with a standard Response object - return { - ok: response.respInfo.status < 400, - data: response.data, - text: response.text, - json: response.json, - status: response.respInfo.status, - headers: response.respInfo.headers, - }; - } catch (error) { - throw new Error(`uploadBlob: ${method} ${url}: ${error.toString()}`); - } + shim.uploadBlob = async function(_url, _options) { + throw new Error('uploadBlob: Not implemented'); }; shim.readLocalFileBase64 = async function(path) { - return RNFetchBlob.fs.readFile(path, 'base64'); - }; - - shim.stringByteLength = function(string) { - return Buffer.byteLength(string, 'utf-8'); - }; - - shim.Buffer = Buffer; - - shim.openUrl = url => { - return Linking.openURL(url); + return shim.fsDriver().readFile(path, 'base64'); }; shim.httpAgent = () => { @@ -206,10 +117,6 @@ export const shimInit = () => { }); }; - shim.mobilePlatform = () => { - return Platform.OS; - }; - shim.appVersion = () => { const p = require('react-native-version-info').default; return p.appVersion; @@ -249,27 +156,6 @@ export const shimInit = () => { return resource; }; - shim.injectedJs = function(name) { - if (!(name in injectedJs)) throw new Error(`Cannot find injectedJs file (add it to "injectedJs" object): ${name}`); - return injectedJs[name]; - }; - - shim.setTimeout = (fn, interval) => { - return PoorManIntervals.setTimeout(fn, interval); - }; - - shim.setInterval = (fn, interval) => { - return PoorManIntervals.setInterval(fn, interval); - }; - - shim.clearTimeout = (id) => { - return PoorManIntervals.clearTimeout(id); - }; - - shim.clearInterval = (id) => { - return PoorManIntervals.clearInterval(id); - }; - + shimInitShared(); } -module.exports = { shimInit }; diff --git a/packages/app-mobile/utils/shim-init-react/injectedJs.ts b/packages/app-mobile/utils/shim-init-react/injectedJs.ts new file mode 100644 index 00000000000..404e55fcf8d --- /dev/null +++ b/packages/app-mobile/utils/shim-init-react/injectedJs.ts @@ -0,0 +1,9 @@ + +const injectedJs = { + webviewLib: require('@joplin/lib/rnInjectedJs/webviewLib'), + codeMirrorBundle: require('../../lib/rnInjectedJs/codeMirrorBundle.bundle'), + svgEditorBundle: require('../../lib/rnInjectedJs/svgEditorBundle.bundle'), + pluginBackgroundPage: require('../../lib/rnInjectedJs/pluginBackgroundPage.bundle'), + noteBodyViewerBundle: require('../../lib/rnInjectedJs/noteBodyViewerBundle.bundle'), +}; +export default injectedJs; \ No newline at end of file diff --git a/packages/app-mobile/utils/shim-init-react/shimInitShared.ts b/packages/app-mobile/utils/shim-init-react/shimInitShared.ts new file mode 100644 index 00000000000..7742a2431da --- /dev/null +++ b/packages/app-mobile/utils/shim-init-react/shimInitShared.ts @@ -0,0 +1,63 @@ +import { Linking, Platform } from 'react-native'; +import injectedJs from './injectedJs'; +import type ShimType from '@joplin/lib/shim'; +import PoorManIntervals from '@joplin/lib/PoorManIntervals'; +import showMessageBox from '../showMessageBox'; +import { Buffer } from 'buffer'; +const shim: typeof ShimType = require('@joplin/lib/shim').default; + +const shimInitShared = () => { + shim.Geolocation = null; + shim.sjclModule = require('@joplin/lib/vendor/sjcl-rn.js'); + + shim.stringByteLength = function(string) { + return Buffer.byteLength(string, 'utf-8'); + }; + + shim.Buffer = Buffer; + + shim.stringByteLength = function(string) { + return Buffer.byteLength(string, 'utf-8'); + }; + + shim.openUrl = url => { + return Linking.openURL(url); + }; + + shim.showMessageBox = showMessageBox; + + shim.waitForFrame = () => { + return new Promise((resolve) => { + requestAnimationFrame(() => { + resolve(); + }); + }); + }; + + shim.mobilePlatform = () => { + return Platform.OS; + }; + + shim.injectedJs = function(name) { + if (!(name in injectedJs)) throw new Error(`Cannot find injectedJs file (add it to "injectedJs" object): ${name}`); + return injectedJs[name as keyof typeof injectedJs]; + }; + + shim.setTimeout = (fn, interval) => { + return PoorManIntervals.setTimeout(fn, interval); + }; + + shim.setInterval = (fn, interval) => { + return PoorManIntervals.setInterval(fn, interval); + }; + + shim.clearTimeout = (id) => { + return PoorManIntervals.clearTimeout(id); + }; + + shim.clearInterval = (id) => { + return PoorManIntervals.clearInterval(id); + }; +}; + +export default shimInitShared; \ No newline at end of file diff --git a/packages/app-mobile/web/index.html b/packages/app-mobile/web/index.html index de019ccb225..62aba5b785a 100644 --- a/packages/app-mobile/web/index.html +++ b/packages/app-mobile/web/index.html @@ -3,10 +3,17 @@ - +
+ \ No newline at end of file diff --git a/packages/app-mobile/web/webpack.config.js b/packages/app-mobile/web/webpack.config.js index 5919ca5cd83..e27e9257a27 100644 --- a/packages/app-mobile/web/webpack.config.js +++ b/packages/app-mobile/web/webpack.config.js @@ -4,10 +4,7 @@ // See also https://dev.to/mikehamilton00/adding-web-support-to-a-react-native-project-in-2023-4m4l const path = require('path'); -const webpack = require('webpack'); - const appDirectory = path.resolve(__dirname, '../'); - const babelConfig = require('../babel.config'); // This is needed for webpack to compile JavaScript. @@ -16,17 +13,14 @@ const babelConfig = require('../babel.config'); // errors. To fix this webpack can be configured to compile to the necessary // `node_module`. const babelLoaderConfiguration = { - test: /\.(tsx|jsx|ts|js)$/, + test: /\.(tsx|jsx|ts|js|mjs)$/, // Add every directory that needs to be compiled by Babel during the build. exclude: [ - { - and: [ - path.resolve(appDirectory, 'ios'), - path.resolve(appDirectory, 'android'), - ], + path.resolve(appDirectory, 'ios'), + path.resolve(appDirectory, 'android'), - not: [] - } + /.*node_modules\/@babel.*/, + /.*node_modules\/@sqlite\.org\/.*/, //path.resolve(appDirectory, 'node_modules/react-native-uncompiled') ], @@ -38,7 +32,7 @@ const babelLoaderConfiguration = { plugins: [ 'react-native-web', '@babel/plugin-transform-export-namespace-from', - ...babelConfig.plugins + ...(babelConfig.plugins ?? []), ] } } @@ -46,7 +40,7 @@ const babelLoaderConfiguration = { // This is needed for webpack to import static images in JavaScript files. const imageLoaderConfiguration = { - test: /\.(gif|jpe?g|png|svg)$/, + test: /\.(gif|jpe?g|png|svg|ttf)$/, use: { loader: 'url-loader', options: { @@ -56,8 +50,12 @@ const imageLoaderConfiguration = { } }; +const emptyLibraryMock = path.resolve(__dirname, 'mocks/empty.js'); + module.exports = { mode: 'development', + //devtool: 'inline-cheap-source-map', + target: 'web', entry: [ // load any web API polyfills @@ -77,8 +75,8 @@ module.exports = { module: { rules: [ babelLoaderConfiguration, - imageLoaderConfiguration - ] + imageLoaderConfiguration, + ], }, resolve: { @@ -87,9 +85,15 @@ module.exports = { 'react-native$': 'react-native-web', // Map some modules that don't work on web to the empty dictionary. - 'react-native-fingerprint-scanner': path.resolve(__dirname, 'mocks/empty.js'), - '@joplin/react-native-saf-x': path.resolve(__dirname, 'mocks/empty.js'), - 'react-native-quick-actions': path.resolve(__dirname, 'mocks/empty.js'), + 'react-native-fingerprint-scanner': emptyLibraryMock, + '@joplin/react-native-saf-x': emptyLibraryMock, + 'react-native-quick-actions': emptyLibraryMock, + 'uglifycss': emptyLibraryMock, + 'react-native-share': emptyLibraryMock, + 'react-native-camera': emptyLibraryMock, + 'react-native-zip-archive': emptyLibraryMock, + 'react-native-document-picker': emptyLibraryMock, + 'react-native-exit-app': emptyLibraryMock, }, // If you're working on a multi-platform React Native app, web-specific // module implementations should be written in files using the extension @@ -97,12 +101,15 @@ module.exports = { extensions: [ '.web.js', '.js', + '.web.mjs', + '.mjs', '.web.ts', '.ts', '.web.jsx', '.jsx', '.web.tsx', '.tsx', + '.wasm', ], fallback: { @@ -112,5 +119,14 @@ module.exports = { "path": require.resolve("path-browserify"), "stream": require.resolve("stream-browserify"), } - } + }, + + devServer: { + // Required by @sqlite.org/sqlite-wasm + // See https://www.npmjs.com/package/@sqlite.org/sqlite-wasm#user-content-in-a-wrapped-worker-with-opfs-if-available + headers: { + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Embedder-Policy': 'require-corp', + }, + }, } diff --git a/packages/lib/BaseModel.ts b/packages/lib/BaseModel.ts index ec5eda40a8f..92fe1fd61c3 100644 --- a/packages/lib/BaseModel.ts +++ b/packages/lib/BaseModel.ts @@ -346,7 +346,7 @@ class BaseModel { if (!options.fields) options.fields = '*'; let sql = `SELECT ${this.db().escapeFields(options.fields)} FROM \`${this.tableName()}\``; - sql += ` WHERE id IN ("${ids.join('","')}")`; + sql += ` WHERE id IN ('${ids.join('\',\'')}')`; const q = this.applySqlOptions(options, sql); return this.modelSelectAll(q.sql); } @@ -745,7 +745,7 @@ class BaseModel { options = this.modOptions(options); const idFieldName = options.idFieldName ? options.idFieldName : 'id'; - const sql = `DELETE FROM ${this.tableName()} WHERE ${idFieldName} IN ("${ids.join('","')}")`; + const sql = `DELETE FROM ${this.tableName()} WHERE ${idFieldName} IN ('${ids.join('\',\'')}')`; await this.db().exec(sql); } diff --git a/packages/lib/JoplinDatabase.ts b/packages/lib/JoplinDatabase.ts index abf56c18fde..b7c7c7d1004 100644 --- a/packages/lib/JoplinDatabase.ts +++ b/packages/lib/JoplinDatabase.ts @@ -312,7 +312,7 @@ export default class JoplinDatabase extends Database { throw new Error(`\`notes_fts\` (${countFieldsNotesFts} fields) must have the same number of fields as \`items_fts\` (${countFieldsItemsFts} fields) for the search engine BM25 algorithm to work`); } - const tableRows = await this.selectAll('SELECT name FROM sqlite_master WHERE type="table"'); + const tableRows = await this.selectAll('SELECT name FROM sqlite_master WHERE type=\'table\''); for (let i = 0; i < tableRows.length; i++) { const tableName = tableRows[i].name; @@ -416,7 +416,7 @@ export default class JoplinDatabase extends Database { if (targetVersion === 4) { queries.push('INSERT INTO settings (`key`, `value`) VALUES (\'sync.3.context\', (SELECT `value` FROM settings WHERE `key` = \'sync.context\'))'); - queries.push('DELETE FROM settings WHERE `key` = "sync.context"'); + queries.push('DELETE FROM settings WHERE `key` = \'sync.context\''); } if (targetVersion === 5) { diff --git a/packages/lib/database.ts b/packages/lib/database.ts index e98cd77098b..a9e5dd4f1c1 100644 --- a/packages/lib/database.ts +++ b/packages/lib/database.ts @@ -59,7 +59,7 @@ export default class Database { try { await this.driver().open(options); } catch (error) { - throw new Error(`Cannot open database: ${error.message}: ${JSON.stringify(options)}`); + throw new Error(`Cannot open database: ${error.message ?? error}: ${JSON.stringify(options)}`); } this.logger().info('Database was open successfully'); diff --git a/packages/lib/fs-driver-base.ts b/packages/lib/fs-driver-base.ts index 9a212220550..677dd88efaa 100644 --- a/packages/lib/fs-driver-base.ts +++ b/packages/lib/fs-driver-base.ts @@ -119,6 +119,14 @@ export default class FsDriverBase { throw new Error('Not implemented: getExternalDirectoryPath'); } + public getCacheDirectoryPath(): string { + throw new Error('Not implemented: getCacheDirectoryPath'); + } + + public getAppDirectoryPath(): string { + throw new Error('Not implemented: getCacheDirectoryPath'); + } + public isUsingAndroidSAF() { return false; } diff --git a/packages/lib/models/BaseItem.ts b/packages/lib/models/BaseItem.ts index 1759e5cb2aa..6b796007d3b 100644 --- a/packages/lib/models/BaseItem.ts +++ b/packages/lib/models/BaseItem.ts @@ -247,7 +247,7 @@ export default class BaseItem extends BaseModel { let output: any[] = []; for (let i = 0; i < classes.length; i++) { const ItemClass = this.getClass(classes[i]); - const sql = `SELECT * FROM ${ItemClass.tableName()} WHERE id IN ("${ids.join('","')}")`; + const sql = `SELECT * FROM ${ItemClass.tableName()} WHERE id IN ('${ids.join('\',\'')}')`; const models = await ItemClass.modelSelectAll(sql); output = output.concat(models); } @@ -261,7 +261,7 @@ export default class BaseItem extends BaseModel { const fields = options && options.fields ? options.fields : []; const ItemClass = this.getClassByItemType(itemType); const fieldsSql = fields.length ? this.db().escapeFields(fields) : '*'; - const sql = `SELECT ${fieldsSql} FROM ${ItemClass.tableName()} WHERE id IN ("${ids.join('","')}")`; + const sql = `SELECT ${fieldsSql} FROM ${ItemClass.tableName()} WHERE id IN ('${ids.join('\',\'')}')`; return ItemClass.modelSelectAll(sql); } @@ -300,7 +300,7 @@ export default class BaseItem extends BaseModel { // since no other client have (or should have) them. let conflictNoteIds: string[] = []; if (this.modelType() === BaseModel.TYPE_NOTE) { - const conflictNotes = await this.db().selectAll(`SELECT id FROM notes WHERE id IN ("${ids.join('","')}") AND is_conflict = 1`); + const conflictNotes = await this.db().selectAll(`SELECT id FROM notes WHERE id IN ('${ids.join('\',\'')}') AND is_conflict = 1`); conflictNoteIds = conflictNotes.map((n: NoteEntity) => { return n.id; }); @@ -936,7 +936,7 @@ export default class BaseItem extends BaseModel { }); if (!ids.length) continue; - await this.db().exec(`UPDATE sync_items SET force_sync = 1 WHERE item_id IN ("${ids.join('","')}")`); + await this.db().exec(`UPDATE sync_items SET force_sync = 1 WHERE item_id IN ('${ids.join('\',\'')}')`); } } diff --git a/packages/lib/models/Folder.ts b/packages/lib/models/Folder.ts index 19e82eb137f..4ca92f016c6 100644 --- a/packages/lib/models/Folder.ts +++ b/packages/lib/models/Folder.ts @@ -433,7 +433,7 @@ export default class Folder extends BaseItem { const sql = ['SELECT id, parent_id FROM folders WHERE share_id != ""']; if (sharedFolderIds.length) { - sql.push(` AND id NOT IN ("${sharedFolderIds.join('","')}")`); + sql.push(` AND id NOT IN ('${sharedFolderIds.join('\',\'')}')`); } const foldersToUnshare: FolderEntity[] = await this.db().selectAll(sql.join(' ')); @@ -650,7 +650,7 @@ export default class Folder extends BaseItem { const query = activeShareIds.length ? ` SELECT ${this.db().escapeFields(fields)} FROM ${tableName} - WHERE share_id != "" AND share_id NOT IN ("${activeShareIds.join('","')}") + WHERE share_id != '' AND share_id NOT IN ('${activeShareIds.join('\',\'')}') ` : ` SELECT ${this.db().escapeFields(fields)} FROM ${tableName} WHERE share_id != '' diff --git a/packages/lib/models/Note.ts b/packages/lib/models/Note.ts index a346fd53597..0a28fe3579b 100644 --- a/packages/lib/models/Note.ts +++ b/packages/lib/models/Note.ts @@ -896,7 +896,7 @@ export default class Note extends BaseItem { const sql = ` UPDATE notes SET ${updateSql.join(', ')} - WHERE id IN ("${processIds.join('","')}") + WHERE id IN ('${processIds.join('\',\'')}') `; await this.db().exec({ sql, params }); diff --git a/packages/lib/models/NoteTag.ts b/packages/lib/models/NoteTag.ts index 07580318837..0714a4be387 100644 --- a/packages/lib/models/NoteTag.ts +++ b/packages/lib/models/NoteTag.ts @@ -12,7 +12,7 @@ export default class NoteTag extends BaseItem { public static async byNoteIds(noteIds: string[]) { if (!noteIds.length) return []; - return this.modelSelectAll(`SELECT * FROM note_tags WHERE note_id IN ("${noteIds.join('","')}")`); + return this.modelSelectAll(`SELECT * FROM note_tags WHERE note_id IN ('${noteIds.join('\',\'')}')`); } public static async tagIdsByNoteId(noteId: string) { diff --git a/packages/lib/models/Resource.ts b/packages/lib/models/Resource.ts index b14d715e8da..9a947e0d15b 100644 --- a/packages/lib/models/Resource.ts +++ b/packages/lib/models/Resource.ts @@ -76,7 +76,7 @@ export default class Resource extends BaseItem { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied public static fetchStatuses(resourceIds: string[]): Promise { if (!resourceIds.length) return Promise.resolve([]); - return this.db().selectAll(`SELECT resource_id, fetch_status FROM resource_local_states WHERE resource_id IN ("${resourceIds.join('","')}")`); + return this.db().selectAll(`SELECT resource_id, fetch_status FROM resource_local_states WHERE resource_id IN ('${resourceIds.join('\',\'')}')`); } public static sharedResourceIds(): Promise { @@ -368,7 +368,7 @@ export default class Resource extends BaseItem { public static async downloadedButEncryptedBlobCount(excludedIds: string[] = null) { let excludedSql = ''; if (excludedIds && excludedIds.length) { - excludedSql = `AND resource_id NOT IN ("${excludedIds.join('","')}")`; + excludedSql = `AND resource_id NOT IN ('${excludedIds.join('\',\'')}')`; } const r = await this.db().selectOne(` @@ -536,7 +536,7 @@ export default class Resource extends BaseItem { public static async needOcr(supportedMimeTypes: string[], skippedResourceIds: string[], limit: number, options: LoadOptions): Promise { const query = this.baseNeedOcrQuery(this.selectFields(options), supportedMimeTypes); - const skippedResourcesSql = skippedResourceIds.length ? `AND resources.id NOT IN ("${skippedResourceIds.join('","')}")` : ''; + const skippedResourcesSql = skippedResourceIds.length ? `AND resources.id NOT IN ('${skippedResourceIds.join('\',\'')}')` : ''; return await this.db().selectAll(` ${query.sql} @@ -576,7 +576,7 @@ export default class Resource extends BaseItem { public static async resourceOcrTextsByIds(ids: string[]): Promise { if (!ids.length) return []; ids = unique(ids); - return this.modelSelectAll(`SELECT id, ocr_text FROM resources WHERE id IN ("${ids.join('","')}")`); + return this.modelSelectAll(`SELECT id, ocr_text FROM resources WHERE id IN ('${ids.join('\',\'')}')`); } public static async allForNormalization(updatedTime: number, id: string, limit = 100, options: LoadOptions = null) { @@ -595,7 +595,7 @@ export default class Resource extends BaseItem { sql: ` SELECT ${this.selectFields(options)} FROM resources WHERE ${whereSql} - AND ocr_text != "" + AND ocr_text != '' AND ocr_status = ? ORDER BY updated_time ASC, id ASC LIMIT ? diff --git a/packages/lib/models/Revision.ts b/packages/lib/models/Revision.ts index 75453c9b9b8..748771e0f27 100644 --- a/packages/lib/models/Revision.ts +++ b/packages/lib/models/Revision.ts @@ -214,7 +214,7 @@ export default class Revision extends BaseItem { public static async itemsWithRevisions(itemType: ModelType, itemIds: string[]) { if (!itemIds.length) return []; - const rows = await this.db().selectAll(`SELECT distinct item_id FROM revisions WHERE item_type = ? AND item_id IN ("${itemIds.join('","')}")`, [itemType]); + const rows = await this.db().selectAll(`SELECT distinct item_id FROM revisions WHERE item_type = ? AND item_id IN ('${itemIds.join('\',\'')}')`, [itemType]); return rows.map((r: RevisionEntity) => r.item_id); } diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts index f0ae8d062cb..aad89e26b56 100644 --- a/packages/lib/models/Setting.ts +++ b/packages/lib/models/Setting.ts @@ -2585,7 +2585,7 @@ class Setting extends BaseModel { } const queries = []; - queries.push(`DELETE FROM settings WHERE key IN ("${keys.join('","')}")`); + queries.push(`DELETE FROM settings WHERE key IN (\'${keys.join('\',\'')}\')`); for (let i = 0; i < this.cache_.length; i++) { const s = { ...this.cache_[i] }; diff --git a/packages/lib/models/Tag.ts b/packages/lib/models/Tag.ts index c43e7d150ef..220934e2dfc 100644 --- a/packages/lib/models/Tag.ts +++ b/packages/lib/models/Tag.ts @@ -39,7 +39,7 @@ export default class Tag extends BaseItem { return Note.previews( null, - { ...options, conditions: [`id IN ("${noteIds.join('","')}")`] }, + { ...options, conditions: [`id IN ('${noteIds.join('\',\'')}')`] }, ); } @@ -153,7 +153,7 @@ export default class Tag extends BaseItem { const tagIds = await NoteTag.tagIdsByNoteId(noteId); if (!tagIds.length) return []; - return this.modelSelectAll(`SELECT ${options.fields ? this.db().escapeFields(options.fields) : '*'} FROM tags WHERE id IN ("${tagIds.join('","')}")`); + return this.modelSelectAll(`SELECT ${options.fields ? this.db().escapeFields(options.fields) : '*'} FROM tags WHERE id IN ('${tagIds.join('\',\'')}')`); } public static async commonTagsByNoteIds(noteIds: string[]) { @@ -168,7 +168,7 @@ export default class Tag extends BaseItem { break; } } - return this.modelSelectAll(`SELECT * FROM tags WHERE id IN ("${commonTagIds.join('","')}")`); + return this.modelSelectAll(`SELECT * FROM tags WHERE id IN ('${commonTagIds.join('\',\'')}')`); } public static async loadByTitle(title: string): Promise { diff --git a/packages/lib/package.json b/packages/lib/package.json index 501405585a2..0bd7f41ba5c 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -84,7 +84,7 @@ "query-string": "7.1.3", "re-reselect": "4.0.1", "read-chunk": "2.1.0", - "redux": "4.2.1", + "redux": "5.0.1", "relative": "3.0.2", "reselect": "4.1.8", "server-destroy": "1.0.1", diff --git a/packages/lib/services/ResourceService.ts b/packages/lib/services/ResourceService.ts index db91d7a68fa..cc37c6af1a2 100644 --- a/packages/lib/services/ResourceService.ts +++ b/packages/lib/services/ResourceService.ts @@ -54,7 +54,7 @@ export default class ResourceService extends BaseService { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied const noteIds = changes.map((a: any) => a.item_id); - const notes = await Note.modelSelectAll(`SELECT id, title, body, encryption_applied FROM notes WHERE id IN ("${noteIds.join('","')}")`); + const notes = await Note.modelSelectAll(`SELECT id, title, body, encryption_applied FROM notes WHERE id IN ('${noteIds.join('\',\'')}')`); const noteById = (noteId: string) => { for (let i = 0; i < notes.length; i++) { diff --git a/packages/lib/services/RevisionService.ts b/packages/lib/services/RevisionService.ts index 9fa02867c35..2e0e9545373 100644 --- a/packages/lib/services/RevisionService.ts +++ b/packages/lib/services/RevisionService.ts @@ -138,7 +138,7 @@ export default class RevisionService extends BaseService { if (!changes.length) break; const noteIds = changes.map((a) => a.item_id); - const notes = await Note.modelSelectAll(`SELECT * FROM notes WHERE is_conflict = 0 AND encryption_applied = 0 AND id IN ("${noteIds.join('","')}")`); + const notes = await Note.modelSelectAll(`SELECT * FROM notes WHERE is_conflict = 0 AND encryption_applied = 0 AND id IN ('${noteIds.join('\',\'')}')`); for (let i = 0; i < changes.length; i++) { const change = changes[i]; diff --git a/packages/lib/services/search/SearchEngine.ts b/packages/lib/services/search/SearchEngine.ts index 09a1c08ad73..b92696f54d1 100644 --- a/packages/lib/services/search/SearchEngine.ts +++ b/packages/lib/services/search/SearchEngine.ts @@ -136,7 +136,7 @@ export default class SearchEngine { const notes = await Note.modelSelectAll(` SELECT ${SearchEngine.relevantFields} FROM notes - WHERE id IN ("${currentIds.join('","')}") AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`); + WHERE id IN ('${currentIds.join('\',\'')}\') AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`); const queries = []; for (let i = 0; i < notes.length; i++) { @@ -219,7 +219,7 @@ export default class SearchEngine { const noteIds = changes.map(a => a.item_id); const notes = await Note.modelSelectAll(` SELECT ${SearchEngine.relevantFields} - FROM notes WHERE id IN ("${noteIds.join('","')}") AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`, + FROM notes WHERE id IN (\'${noteIds.join('\',\'')}\') AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`, ); for (let i = 0; i < changes.length; i++) { diff --git a/packages/lib/services/search/SearchEngineUtils.ts b/packages/lib/services/search/SearchEngineUtils.ts index 99cccdb7655..9a41fdf3cd9 100644 --- a/packages/lib/services/search/SearchEngineUtils.ts +++ b/packages/lib/services/search/SearchEngineUtils.ts @@ -58,7 +58,7 @@ export default class SearchEngineUtils { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied const previewOptions: any = { order: [], fields: fields, - conditions: [`id IN ("${noteIds.join('","')}")`], ...options }; + conditions: [`id IN ('${noteIds.join('\',\'')}')`], ...options }; const notes = await Note.previews(null, previewOptions); diff --git a/packages/lib/shim.ts b/packages/lib/shim.ts index 8de50090d4b..a82a61a7bc1 100644 --- a/packages/lib/shim.ts +++ b/packages/lib/shim.ts @@ -83,7 +83,7 @@ const shim = { }, isLinux: () => { - return process && process.platform === 'linux'; + return typeof process !== 'undefined' && process.platform === 'linux'; }, isGNOME: () => { @@ -110,15 +110,15 @@ const shim = { }, isFreeBSD: () => { - return process && process.platform === 'freebsd'; + return typeof process !== 'undefined' && process.platform === 'freebsd'; }, isWindows: () => { - return process && process.platform === 'win32'; + return typeof process !== 'undefined' && process.platform === 'win32'; }, isMac: () => { - return process && process.platform === 'darwin'; + return typeof process !== 'undefined' && process.platform === 'darwin'; }, platformName: () => { @@ -127,7 +127,7 @@ const shim = { if (shim.isWindows()) return 'win32'; if (shim.isLinux()) return 'linux'; if (shim.isFreeBSD()) return 'freebsd'; - if (process && process.platform) return process.platform; + if (typeof process !== 'undefined' && process.platform) return process.platform; throw new Error('Cannot determine platform'); }, diff --git a/packages/renderer/MdToHtml/rules/katex_mhchem.js b/packages/renderer/MdToHtml/rules/katex_mhchem.js index 97887e4f8bc..359c2b7cb20 100644 --- a/packages/renderer/MdToHtml/rules/katex_mhchem.js +++ b/packages/renderer/MdToHtml/rules/katex_mhchem.js @@ -1723,9 +1723,9 @@ var mhchemModule = function(katex) { return katex; } -if (this.katex) { +if ((this || self).kagex) { // We're running in a browser and the global Katex variable is defined - this.katex = mhchemModule(this.katex); + (this || self).katex = mhchemModule((this || self).katex); } else if (typeof module !== 'undefined' && module.exports) { // We're running in Node.js module.exports = mhchemModule; diff --git a/packages/tools/cspell/dictionary4.txt b/packages/tools/cspell/dictionary4.txt index 8490ae179f9..4a7ba824a99 100644 --- a/packages/tools/cspell/dictionary4.txt +++ b/packages/tools/cspell/dictionary4.txt @@ -110,3 +110,5 @@ ENOTFOUND Scaleway Inkscape Ionicon +onready +opfs diff --git a/yarn.lock b/yarn.lock index 71a225b44bc..108384e1865 100644 --- a/yarn.lock +++ b/yarn.lock @@ -208,16 +208,6 @@ __metadata: languageName: node linkType: hard -"@ampproject/remapping@npm:^2.1.0": - version: 2.2.0 - resolution: "@ampproject/remapping@npm:2.2.0" - dependencies: - "@jridgewell/gen-mapping": ^0.1.0 - "@jridgewell/trace-mapping": ^0.3.9 - checksum: d74d170d06468913921d72430259424b7e4c826b5a7d39ff839a29d547efb97dc577caa8ba3fb5cf023624e9af9d09651afc3d4112a45e2050328abc9b3a2292 - languageName: node - linkType: hard - "@ampproject/remapping@npm:^2.2.0": version: 2.2.1 resolution: "@ampproject/remapping@npm:2.2.1" @@ -1430,7 +1420,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.22.5": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.5": version: 7.22.5 resolution: "@babel/code-frame@npm:7.22.5" dependencies: @@ -1449,7 +1439,17 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.1, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.5": +"@babel/code-frame@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/code-frame@npm:7.24.7" + dependencies: + "@babel/highlight": ^7.24.7 + picocolors: ^1.0.0 + checksum: 830e62cd38775fdf84d612544251ce773d544a8e63df667728cc9e0126eeef14c6ebda79be0f0bc307e8318316b7f58c27ce86702e0a1f5c321d842eb38ffda4 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.5": version: 7.22.5 resolution: "@babel/compat-data@npm:7.22.5" checksum: eb1a47ebf79ae268b4a16903e977be52629339806e248455eb9973897c503a04b701f36a9de64e19750d6e081d0561e77a514c8dc470babbeba59ae94298ed18 @@ -1463,6 +1463,13 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/compat-data@npm:7.24.7" + checksum: 1fc276825dd434fe044877367dfac84171328e75a8483a6976aa28bf833b32367e90ee6df25bdd97c287d1aa8019757adcccac9153de70b1932c0d243a978ae9 + languageName: node + linkType: hard + "@babel/core@npm:7.12.9": version: 7.12.9 resolution: "@babel/core@npm:7.12.9" @@ -1510,26 +1517,26 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:7.20.2": - version: 7.20.2 - resolution: "@babel/core@npm:7.20.2" +"@babel/core@npm:7.24.7": + version: 7.24.7 + resolution: "@babel/core@npm:7.24.7" dependencies: - "@ampproject/remapping": ^2.1.0 - "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.20.2 - "@babel/helper-compilation-targets": ^7.20.0 - "@babel/helper-module-transforms": ^7.20.2 - "@babel/helpers": ^7.20.1 - "@babel/parser": ^7.20.2 - "@babel/template": ^7.18.10 - "@babel/traverse": ^7.20.1 - "@babel/types": ^7.20.2 - convert-source-map: ^1.7.0 + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.24.7 + "@babel/generator": ^7.24.7 + "@babel/helper-compilation-targets": ^7.24.7 + "@babel/helper-module-transforms": ^7.24.7 + "@babel/helpers": ^7.24.7 + "@babel/parser": ^7.24.7 + "@babel/template": ^7.24.7 + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + convert-source-map: ^2.0.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 - json5: ^2.2.1 - semver: ^6.3.0 - checksum: 98faaaef26103a276a30a141b951a93bc8418d100d1f668bf7a69d12f3e25df57958e8b6b9100d95663f720db62da85ade736f6629a5ebb1e640251a1b43c0e4 + json5: ^2.2.3 + semver: ^6.3.1 + checksum: 017497e2a1b4683a885219eef7d2aee83c1c0cf353506b2e180b73540ec28841d8ef1ea1837fa69f8c561574b24ddd72f04764b27b87afedfe0a07299ccef24d languageName: node linkType: hard @@ -1591,7 +1598,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.14.0, @babel/generator@npm:^7.16.0, @babel/generator@npm:^7.20.0, @babel/generator@npm:^7.20.2, @babel/generator@npm:^7.22.5, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.14.0, @babel/generator@npm:^7.16.0, @babel/generator@npm:^7.20.0, @babel/generator@npm:^7.22.5, @babel/generator@npm:^7.7.2": version: 7.22.5 resolution: "@babel/generator@npm:7.22.5" dependencies: @@ -1603,6 +1610,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/generator@npm:7.24.7" + dependencies: + "@babel/types": ^7.24.7 + "@jridgewell/gen-mapping": ^0.3.5 + "@jridgewell/trace-mapping": ^0.3.25 + jsesc: ^2.5.1 + checksum: 0ff31a73b15429f1287e4d57b439bba4a266f8c673bb445fe313b82f6d110f586776997eb723a777cd7adad9d340edd162aea4973a90112c5d0cfcaf6686844b + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.16.0, @babel/helper-annotate-as-pure@npm:^7.18.6, @babel/helper-annotate-as-pure@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" @@ -1612,6 +1631,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-annotate-as-pure@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.24.7" + dependencies: + "@babel/types": ^7.24.7 + checksum: 6178566099a6a0657db7a7fa601a54fb4731ca0b8614fbdccfd8e523c210c13963649bc8fdfd53ce7dd14d05e3dda2fb22dea5b30113c488b9eb1a906d60212e + languageName: node + linkType: hard + "@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.5" @@ -1621,7 +1649,17 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.16.0, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.20.0, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.5": +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.24.7" + dependencies: + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 71a6158a9fdebffb82fdc400d5555ba8f2e370cea81a0d578155877bdc4db7d5252b75c43b2fdf3f72b3f68348891f99bd35ae315542daad1b7ace8322b1abcb + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.16.0, @babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.20.7, @babel/helper-compilation-targets@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-compilation-targets@npm:7.22.5" dependencies: @@ -1649,7 +1687,20 @@ __metadata: languageName: node linkType: hard -"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.20.7, @babel/helper-create-class-features-plugin@npm:^7.21.0": +"@babel/helper-compilation-targets@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-compilation-targets@npm:7.24.7" + dependencies: + "@babel/compat-data": ^7.24.7 + "@babel/helper-validator-option": ^7.24.7 + browserslist: ^4.22.2 + lru-cache: ^5.1.1 + semver: ^6.3.1 + checksum: dfc88bc35e223ade796c7267901728217c665adc5bc2e158f7b0ae850de14f1b7941bec4fe5950ae46236023cfbdeddd9c747c276acf9b39ca31f8dd97dc6cc6 + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.20.7": version: 7.22.5 resolution: "@babel/helper-create-class-features-plugin@npm:7.22.5" dependencies: @@ -1687,6 +1738,25 @@ __metadata: languageName: node linkType: hard +"@babel/helper-create-class-features-plugin@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-create-class-features-plugin@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-function-name": ^7.24.7 + "@babel/helper-member-expression-to-functions": ^7.24.7 + "@babel/helper-optimise-call-expression": ^7.24.7 + "@babel/helper-replace-supers": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 + "@babel/helper-split-export-declaration": ^7.24.7 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 371a181a1717a9b0cebc97727c8ea9ca6afa34029476a684b6030f9d1ad94dcdafd7de175da10b63ae3ba79e4e82404db8ed968ebf264b768f097e5d64faab71 + languageName: node + linkType: hard + "@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.5" @@ -1700,7 +1770,20 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:^0.3.0, @babel/helper-define-polyfill-provider@npm:^0.3.3": +"@babel/helper-create-regexp-features-plugin@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": ^7.24.7 + regexpu-core: ^5.3.1 + semver: ^6.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 17c59fa222af50f643946eca940ce1d474ff2da1f4afed2312687ab9d708ebbb8c9372754ddbdf44b6e21ead88b8fc144644f3a7b63ccb886de002458cef3974 + languageName: node + linkType: hard + +"@babel/helper-define-polyfill-provider@npm:^0.3.0": version: 0.3.3 resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" dependencies: @@ -1731,6 +1814,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-define-polyfill-provider@npm:^0.6.1, @babel/helper-define-polyfill-provider@npm:^0.6.2": + version: 0.6.2 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" + dependencies: + "@babel/helper-compilation-targets": ^7.22.6 + "@babel/helper-plugin-utils": ^7.22.5 + debug: ^4.1.1 + lodash.debounce: ^4.0.8 + resolve: ^1.14.2 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 2bba965ea9a4887ddf9c11d51d740ab473bd7597b787d042c325f6a45912dfe908c2d6bb1d837bf82f7e9fa51e6ad5150563c58131d2bb85515e63d971414a9c + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.18.9, @babel/helper-environment-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-environment-visitor@npm:7.22.5" @@ -1745,6 +1843,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-environment-visitor@npm:7.24.7" + dependencies: + "@babel/types": ^7.24.7 + checksum: 079d86e65701b29ebc10baf6ed548d17c19b808a07aa6885cc141b690a78581b180ee92b580d755361dc3b16adf975b2d2058b8ce6c86675fcaf43cf22f2f7c6 + languageName: node + linkType: hard + "@babel/helper-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-function-name@npm:7.22.5" @@ -1765,6 +1872,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-function-name@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-function-name@npm:7.24.7" + dependencies: + "@babel/template": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 142ee08922074dfdc0ff358e09ef9f07adf3671ab6eef4fca74dcf7a551f1a43717e7efa358c9e28d7eea84c28d7f177b7a58c70452fc312ae3b1893c5dab2a4 + languageName: node + linkType: hard + "@babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" @@ -1774,6 +1891,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-hoist-variables@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-hoist-variables@npm:7.24.7" + dependencies: + "@babel/types": ^7.24.7 + checksum: 6cfdcf2289cd12185dcdbdf2435fa8d3447b797ac75851166de9fc8503e2fd0021db6baf8dfbecad3753e582c08e6a3f805c8d00cbed756060a877d705bd8d8d + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.22.15": version: 7.23.0 resolution: "@babel/helper-member-expression-to-functions@npm:7.23.0" @@ -1792,6 +1918,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-member-expression-to-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-member-expression-to-functions@npm:7.24.7" + dependencies: + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 9fecf412f85fa23b7cf55d19eb69de39f8240426a028b141c9df2aed8cfedf20b3ec3318d40312eb7a3dec9eea792828ce0d590e0ff62da3da532482f537192c + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.16.0, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.18.6, @babel/helper-module-imports@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-imports@npm:7.22.5" @@ -1810,6 +1946,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-imports@npm:7.24.7" + dependencies: + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 8ac15d96d262b8940bc469052a048e06430bba1296369be695fabdf6799f201dd0b00151762b56012a218464e706bc033f27c07f6cec20c6f8f5fd6543c67054 + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.12.1, @babel/helper-module-transforms@npm:^7.23.0": version: 7.23.0 resolution: "@babel/helper-module-transforms@npm:7.23.0" @@ -1825,7 +1971,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.16.0, @babel/helper-module-transforms@npm:^7.20.2, @babel/helper-module-transforms@npm:^7.22.5": +"@babel/helper-module-transforms@npm:^7.16.0, @babel/helper-module-transforms@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-module-transforms@npm:7.22.5" dependencies: @@ -1841,6 +1987,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-transforms@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-module-imports": ^7.24.7 + "@babel/helper-simple-access": ^7.24.7 + "@babel/helper-split-export-declaration": ^7.24.7 + "@babel/helper-validator-identifier": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: ddff3b41c2667876b4e4e73d961168f48a5ec9560c95c8c2d109e6221f9ca36c6f90c6317eb7a47f2a3c99419c356e529a86b79174cad0d4f7a61960866b88ca + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" @@ -1850,6 +2011,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-optimise-call-expression@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-optimise-call-expression@npm:7.24.7" + dependencies: + "@babel/types": ^7.24.7 + checksum: 280654eaf90e92bf383d7eed49019573fb35a98c9e992668f701ad099957246721044be2068cf6840cb2299e0ad393705a1981c88c23a1048096a8d59e5f79a3 + languageName: node + linkType: hard + "@babel/helper-plugin-utils@npm:7.10.4": version: 7.10.4 resolution: "@babel/helper-plugin-utils@npm:7.10.4" @@ -1857,13 +2027,20 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": version: 7.22.5 resolution: "@babel/helper-plugin-utils@npm:7.22.5" checksum: c0fc7227076b6041acd2f0e818145d2e8c41968cc52fb5ca70eed48e21b8fe6dd88a0a91cbddf4951e33647336eb5ae184747ca706817ca3bef5e9e905151ff5 languageName: node linkType: hard +"@babel/helper-plugin-utils@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-plugin-utils@npm:7.24.7" + checksum: 81f2a15751d892e4a8fce25390f973363a5b27596167861d2d6eab0f61856eb2ba389b031a9f19f669c0bd4dd601185828d3cebafd25431be7a1696f2ce3ef68 + languageName: node + linkType: hard + "@babel/helper-remap-async-to-generator@npm:^7.18.9, @babel/helper-remap-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-remap-async-to-generator@npm:7.22.5" @@ -1891,6 +2068,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-remap-async-to-generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-remap-async-to-generator@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-wrap-function": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: bab7be178f875350f22a2cb9248f67fe3a8a8128db77a25607096ca7599fd972bc7049fb11ed9e95b45a3f1dd1fac3846a3279f9cbac16f337ecb0e6ca76e1fc + languageName: node + linkType: hard + "@babel/helper-replace-supers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-replace-supers@npm:7.22.5" @@ -1918,6 +2108,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-replace-supers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-replace-supers@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-member-expression-to-functions": ^7.24.7 + "@babel/helper-optimise-call-expression": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 2bf0d113355c60d86a04e930812d36f5691f26c82d4ec1739e5ec0a4c982c9113dad3167f7c74f888a96328bd5e696372232406d8200e5979e6e0dc2af5e7c76 + languageName: node + linkType: hard + "@babel/helper-simple-access@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-simple-access@npm:7.22.5" @@ -1927,6 +2130,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-simple-access@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-simple-access@npm:7.24.7" + dependencies: + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: ddbf55f9dea1900213f2a1a8500fabfd21c5a20f44dcfa957e4b0d8638c730f88751c77f678644f754f1a1dc73f4eb8b766c300deb45a9daad000e4247957819 + languageName: node + linkType: hard + "@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0, @babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" @@ -1936,6 +2149,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.24.7" + dependencies: + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 11b28fe534ce2b1a67c4d8e51a7b5711a2a0a0cae802f74614eee54cca58c744d9a62f6f60103c41759e81c537d270bfd665bf368a6bea214c6052f2094f8407 + languageName: node + linkType: hard + "@babel/helper-split-export-declaration@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-split-export-declaration@npm:7.22.5" @@ -1954,6 +2177,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-split-export-declaration@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-split-export-declaration@npm:7.24.7" + dependencies: + "@babel/types": ^7.24.7 + checksum: e3ddc91273e5da67c6953f4aa34154d005a00791dc7afa6f41894e768748540f6ebcac5d16e72541aea0c89bee4b89b4da6a3d65972a0ea8bfd2352eda5b7e22 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-string-parser@npm:7.22.5" @@ -1961,6 +2193,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-string-parser@npm:7.24.7" + checksum: 09568193044a578743dd44bf7397940c27ea693f9812d24acb700890636b376847a611cdd0393a928544e79d7ad5b8b916bd8e6e772bc8a10c48a647a96e7b1a + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-validator-identifier@npm:7.22.20" @@ -1975,6 +2214,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-identifier@npm:7.24.7" + checksum: 6799ab117cefc0ecd35cd0b40ead320c621a298ecac88686a14cffceaac89d80cdb3c178f969861bf5fa5e4f766648f9161ea0752ecfe080d8e89e3147270257 + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.18.6, @babel/helper-validator-option@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-validator-option@npm:7.22.5" @@ -1989,6 +2235,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-option@npm:7.24.7" + checksum: 9689166bf3f777dd424c026841c8cd651e41b21242dbfd4569a53086179a3e744c8eddd56e9d10b54142270141c91581b53af0d7c00c82d552d2540e2a919f7e + languageName: node + linkType: hard + "@babel/helper-wrap-function@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-wrap-function@npm:7.22.20" @@ -2012,6 +2265,18 @@ __metadata: languageName: node linkType: hard +"@babel/helper-wrap-function@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-wrap-function@npm:7.24.7" + dependencies: + "@babel/helper-function-name": ^7.24.7 + "@babel/template": ^7.24.7 + "@babel/traverse": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 085bf130ed08670336e3976f5841ae44e3e10001131632e22ef234659341978d2fd37e65785f59b6cb1745481347fc3bce84b33a685cacb0a297afbe1d2b03af + languageName: node + linkType: hard + "@babel/helpers@npm:^7.12.5, @babel/helpers@npm:^7.23.2": version: 7.23.2 resolution: "@babel/helpers@npm:7.23.2" @@ -2023,7 +2288,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.16.0, @babel/helpers@npm:^7.20.1, @babel/helpers@npm:^7.22.5": +"@babel/helpers@npm:^7.16.0, @babel/helpers@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helpers@npm:7.22.5" dependencies: @@ -2034,6 +2299,16 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helpers@npm:7.24.7" + dependencies: + "@babel/template": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: 934da58098a3670ca7f9f42425b9c44d0ca4f8fad815c0f51d89fc7b64c5e0b4c7d5fec038599de691229ada737edeaf72fad3eba8e16dd5842e8ea447f76b66 + languageName: node + linkType: hard + "@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.22.5": version: 7.22.5 resolution: "@babel/highlight@npm:7.22.5" @@ -2056,7 +2331,19 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.0, @babel/parser@npm:^7.20.0, @babel/parser@npm:^7.20.2, @babel/parser@npm:^7.22.5": +"@babel/highlight@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/highlight@npm:7.24.7" + dependencies: + "@babel/helper-validator-identifier": ^7.24.7 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + picocolors: ^1.0.0 + checksum: 5cd3a89f143671c4ac129960024ba678b669e6fc673ce078030f5175002d1d3d52bc10b22c5b916a6faf644b5028e9a4bd2bb264d053d9b05b6a98690f1d46f1 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.0, @babel/parser@npm:^7.20.0, @babel/parser@npm:^7.22.5": version: 7.22.5 resolution: "@babel/parser@npm:7.22.5" bin: @@ -2074,14 +2361,24 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": - version: 7.22.5 - resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.22.5" +"@babel/parser@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/parser@npm:7.24.7" + bin: + parser: ./bin/babel-parser.js + checksum: fc9d2c4c8712f89672edc55c0dc5cf640dcec715b56480f111f85c2bc1d507e251596e4110d65796690a96ac37a4b60432af90b3e97bb47e69d4ef83872dbbd6 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0 - checksum: 1e353a060fb2cd8f1256d28cd768f16fb02513f905b9b6d656fb0242c96c341a196fa188b27c2701506a6e27515359fbcc1a5ca7fa8b9b530cf88fbd137baefc + checksum: 68d315642b53af143aa17a71eb976cf431b51339aee584e29514a462b81c998636dd54219c2713b5f13e1df89eaf130dfab59683f9116825608708c81696b96c languageName: node linkType: hard @@ -2096,16 +2393,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.18.9": - version: 7.22.5 - resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.22.5" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 - "@babel/plugin-transform-optional-chaining": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: - "@babel/core": ^7.13.0 - checksum: 16e7a5f3bf2f2ac0ca032a70bf0ebd7e886d84dbb712b55c0643c04c495f0f221fbcbca14b5f8f8027fa6c87a3dafae0934022ad2b409384af6c5c356495b7bd + "@babel/core": ^7.0.0 + checksum: 7eb4e7ce5e3d6db4b0fdbdfaaa301c2e58f38a7ee39d5a4259a1fda61a612e83d3e4bc90fc36fb0345baf57e1e1a071e0caffeb80218623ad163f2fdc2e53a54 languageName: node linkType: hard @@ -2122,54 +2417,54 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-async-generator-functions@npm:^7.0.0, @babel/plugin-proposal-async-generator-functions@npm:^7.20.1": - version: 7.20.7 - resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.7" dependencies: - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-remap-async-to-generator": ^7.18.9 - "@babel/plugin-syntax-async-generators": ^7.8.4 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 + "@babel/plugin-transform-optional-chaining": ^7.24.7 peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 + "@babel/core": ^7.13.0 + checksum: 07b92878ac58a98ea1fdf6a8b4ec3413ba4fa66924e28b694d63ec5b84463123fbf4d7153b56cf3cedfef4a3482c082fe3243c04f8fb2c041b32b0e29b4a9e21 languageName: node linkType: hard -"@babel/plugin-proposal-class-properties@npm:^7.0.0, @babel/plugin-proposal-class-properties@npm:^7.13.0, @babel/plugin-proposal-class-properties@npm:^7.18.0, @babel/plugin-proposal-class-properties@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.24.7" dependencies: - "@babel/helper-create-class-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 + "@babel/core": ^7.0.0 + checksum: 8324d458db57060590942c7c2e9603880d07718ccb6450ec935105b8bd3c4393c4b8ada88e178c232258d91f33ffdcf2b1043d54e07a86989e50667ee100a32e languageName: node linkType: hard -"@babel/plugin-proposal-class-static-block@npm:^7.18.6": - version: 7.21.0 - resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" +"@babel/plugin-proposal-async-generator-functions@npm:^7.0.0": + version: 7.20.7 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" dependencies: - "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-environment-visitor": ^7.18.9 "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-class-static-block": ^7.14.5 + "@babel/helper-remap-async-to-generator": ^7.18.9 + "@babel/plugin-syntax-async-generators": ^7.8.4 peerDependencies: - "@babel/core": ^7.12.0 - checksum: 236c0ad089e7a7acab776cc1d355330193314bfcd62e94e78f2df35817c6144d7e0e0368976778afd6b7c13e70b5068fa84d7abbf967d4f182e60d03f9ef802b + "@babel/core": ^7.0.0-0 + checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 languageName: node linkType: hard -"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": +"@babel/plugin-proposal-class-properties@npm:^7.0.0, @babel/plugin-proposal-class-properties@npm:^7.13.0, @babel/plugin-proposal-class-properties@npm:^7.18.0": version: 7.18.6 - resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" + resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" dependencies: + "@babel/helper-create-class-features-plugin": ^7.18.6 "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-dynamic-import": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f + checksum: 49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 languageName: node linkType: hard @@ -2185,31 +2480,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": - version: 7.18.9 - resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" - dependencies: - "@babel/helper-plugin-utils": ^7.18.9 - "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef - languageName: node - linkType: hard - -"@babel/plugin-proposal-json-strings@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" - dependencies: - "@babel/helper-plugin-utils": ^7.18.6 - "@babel/plugin-syntax-json-strings": ^7.8.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 - languageName: node - linkType: hard - -"@babel/plugin-proposal-logical-assignment-operators@npm:^7.18.0, @babel/plugin-proposal-logical-assignment-operators@npm:^7.18.9": +"@babel/plugin-proposal-logical-assignment-operators@npm:^7.18.0": version: 7.20.7 resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" dependencies: @@ -2221,7 +2492,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.0.0, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.13.8, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.0, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.0.0, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.13.8, @babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.0": version: 7.18.6 resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" dependencies: @@ -2233,7 +2504,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-numeric-separator@npm:^7.0.0, @babel/plugin-proposal-numeric-separator@npm:^7.18.6": +"@babel/plugin-proposal-numeric-separator@npm:^7.0.0": version: 7.18.6 resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" dependencies: @@ -2258,7 +2529,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-object-rest-spread@npm:^7.0.0, @babel/plugin-proposal-object-rest-spread@npm:^7.20.0, @babel/plugin-proposal-object-rest-spread@npm:^7.20.2, @babel/plugin-proposal-object-rest-spread@npm:^7.5.5": +"@babel/plugin-proposal-object-rest-spread@npm:^7.0.0, @babel/plugin-proposal-object-rest-spread@npm:^7.20.0, @babel/plugin-proposal-object-rest-spread@npm:^7.5.5": version: 7.20.7 resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" dependencies: @@ -2273,7 +2544,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-catch-binding@npm:^7.0.0, @babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": +"@babel/plugin-proposal-optional-catch-binding@npm:^7.0.0": version: 7.18.6 resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" dependencies: @@ -2285,7 +2556,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-optional-chaining@npm:^7.0.0, @babel/plugin-proposal-optional-chaining@npm:^7.13.12, @babel/plugin-proposal-optional-chaining@npm:^7.18.9, @babel/plugin-proposal-optional-chaining@npm:^7.20.0": +"@babel/plugin-proposal-optional-chaining@npm:^7.0.0, @babel/plugin-proposal-optional-chaining@npm:^7.13.12, @babel/plugin-proposal-optional-chaining@npm:^7.20.0": version: 7.21.0 resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" dependencies: @@ -2298,18 +2569,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-methods@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" - dependencies: - "@babel/helper-create-class-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 22d8502ee96bca99ad2c8393e8493e2b8d4507576dd054490fd8201a36824373440106f5b098b6d821b026c7e72b0424ff4aeca69ed5f42e48f029d3a156d5ad - languageName: node - linkType: hard - "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": version: 7.21.0-placeholder-for-preset-env.2 resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" @@ -2319,32 +2578,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-proposal-private-property-in-object@npm:^7.18.6": - version: 7.21.11 - resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.11" - dependencies: - "@babel/helper-annotate-as-pure": ^7.18.6 - "@babel/helper-create-class-features-plugin": ^7.21.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/plugin-syntax-private-property-in-object": ^7.14.5 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 1b880543bc5f525b360b53d97dd30807302bb82615cd42bf931968f59003cac75629563d6b104868db50abd22235b3271fdf679fea5db59a267181a99cc0c265 - languageName: node - linkType: hard - -"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6, @babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": - version: 7.18.6 - resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" - dependencies: - "@babel/helper-create-regexp-features-plugin": ^7.18.6 - "@babel/helper-plugin-utils": ^7.18.6 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: a8575ecb7ff24bf6c6e94808d5c84bb5a0c6dd7892b54f09f4646711ba0ee1e1668032b3c43e3e1dfec2c5716c302e851ac756c1645e15882d73df6ad21ae951 - languageName: node - linkType: hard - "@babel/plugin-syntax-async-generators@npm:^7.8.4": version: 7.8.4 resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" @@ -2444,7 +2677,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.20.0, @babel/plugin-syntax-import-assertions@npm:^7.22.5": +"@babel/plugin-syntax-import-assertions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-import-assertions@npm:7.22.5" dependencies: @@ -2455,6 +2688,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-assertions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c4d67be4eb1d4637e361477dbe01f5b392b037d17c1f861cfa0faa120030e137aab90a9237931b8040fd31d1e5d159e11866fa1165f78beef7a3be876a391a17 + languageName: node + linkType: hard + "@babel/plugin-syntax-import-attributes@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-syntax-import-attributes@npm:7.22.5" @@ -2466,6 +2710,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-import-attributes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 590dbb5d1a15264f74670b427b8d18527672c3d6c91d7bae7e65f80fd810edbc83d90e68065088644cbad3f2457ed265a54a9956fb789fcb9a5b521822b3a275 + languageName: node + linkType: hard + "@babel/plugin-syntax-import-meta@npm:^7.10.4, @babel/plugin-syntax-import-meta@npm:^7.8.3": version: 7.10.4 resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" @@ -2643,7 +2898,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.0.0, @babel/plugin-transform-arrow-functions@npm:^7.18.6, @babel/plugin-transform-arrow-functions@npm:^7.22.5": +"@babel/plugin-transform-arrow-functions@npm:^7.0.0, @babel/plugin-transform-arrow-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-arrow-functions@npm:7.22.5" dependencies: @@ -2654,6 +2909,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-arrow-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 707c209b5331c7dc79bd326128c6a6640dbd62a78da1653c844db20c4f36bf7b68454f1bc4d2d051b3fde9136fa291f276ec03a071bb00ee653069ff82f91010 + languageName: node + linkType: hard + "@babel/plugin-transform-async-generator-functions@npm:^7.23.2": version: 7.23.2 resolution: "@babel/plugin-transform-async-generator-functions@npm:7.23.2" @@ -2668,7 +2934,21 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:^7.0.0, @babel/plugin-transform-async-to-generator@npm:^7.18.6, @babel/plugin-transform-async-to-generator@npm:^7.22.5": +"@babel/plugin-transform-async-generator-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-remap-async-to-generator": ^7.24.7 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 112e3b18f9c496ebc01209fc27f0b41a3669c479c7bc44f7249383172b432ebaae1e523caa7c6ecbd2d0d7adcb7e5769fe2798f8cb01c08cd57232d1bb6d8ad4 + languageName: node + linkType: hard + +"@babel/plugin-transform-async-to-generator@npm:^7.0.0, @babel/plugin-transform-async-to-generator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-async-to-generator@npm:7.22.5" dependencies: @@ -2694,7 +2974,20 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.0.0, @babel/plugin-transform-block-scoped-functions@npm:^7.18.6, @babel/plugin-transform-block-scoped-functions@npm:^7.22.5": +"@babel/plugin-transform-async-to-generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.7" + dependencies: + "@babel/helper-module-imports": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-remap-async-to-generator": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 13704fb3b83effc868db2b71bfb2c77b895c56cb891954fc362e95e200afd523313b0e7cf04ce02f45b05e76017c5b5fa8070c92613727a35131bb542c253a36 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoped-functions@npm:^7.0.0, @babel/plugin-transform-block-scoped-functions@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.22.5" dependencies: @@ -2705,7 +2998,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.0.0, @babel/plugin-transform-block-scoping@npm:^7.20.2": +"@babel/plugin-transform-block-scoped-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 249cdcbff4e778b177245f9652b014ea4f3cd245d83297f10a7bf6d97790074089aa62bcde8c08eb299c5e68f2faed346b587d3ebac44d625ba9a83a4ee27028 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoping@npm:^7.0.0": version: 7.22.5 resolution: "@babel/plugin-transform-block-scoping@npm:7.22.5" dependencies: @@ -2727,6 +3031,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-block-scoping@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-block-scoping@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 039206155533600f079f3a455f85888dd7d4970ff7ffa85ef44760f4f5acb9f19c9d848cc1fec1b9bdbc0dfec9e8a080b90d0ab66ad2bdc7138b5ca4ba96e61c + languageName: node + linkType: hard + "@babel/plugin-transform-class-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-class-properties@npm:7.22.5" @@ -2739,6 +3054,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-class-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-class-properties@npm:7.24.7" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1348d7ce74da38ba52ea85b3b4289a6a86913748569ef92ef0cff30702a9eb849e5eaf59f1c6f3517059aa68115fb3067e389735dccacca39add4e2b0c67e291 + languageName: node + linkType: hard + "@babel/plugin-transform-class-static-block@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-class-static-block@npm:7.22.11" @@ -2752,7 +3079,20 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.0.0, @babel/plugin-transform-classes@npm:^7.20.2": +"@babel/plugin-transform-class-static-block@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-class-static-block@npm:7.24.7" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 324049263504f18416f1c3e24033baebfafd05480fdd885c8ebe6f2b415b0fc8e0b98d719360f9e30743cc78ac387fabc0b3c6606d2b54135756ffb92963b382 + languageName: node + linkType: hard + +"@babel/plugin-transform-classes@npm:^7.0.0": version: 7.22.5 resolution: "@babel/plugin-transform-classes@npm:7.22.5" dependencies: @@ -2790,7 +3130,25 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.0.0, @babel/plugin-transform-computed-properties@npm:^7.18.9, @babel/plugin-transform-computed-properties@npm:^7.22.5": +"@babel/plugin-transform-classes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-classes@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-compilation-targets": ^7.24.7 + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-function-name": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-replace-supers": ^7.24.7 + "@babel/helper-split-export-declaration": ^7.24.7 + globals: ^11.1.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f01cb31143730d425681e9816020cbb519c7ddb3b6ca308dfaf2821eda5699a746637fc6bf19811e2fb42cfdf8b00a21b31c754da83771a5c280077925677354 + languageName: node + linkType: hard + +"@babel/plugin-transform-computed-properties@npm:^7.0.0, @babel/plugin-transform-computed-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-computed-properties@npm:7.22.5" dependencies: @@ -2802,7 +3160,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.0.0, @babel/plugin-transform-destructuring@npm:^7.20.2, @babel/plugin-transform-destructuring@npm:^7.5.0": +"@babel/plugin-transform-computed-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/template": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0cf8c1b1e4ea57dec8d4612460d84fd4cdbf71a7499bb61ee34632cf89018a59eee818ffca88a8d99ee7057c20a4257044d7d463fda6daef9bf1db9fa81563cb + languageName: node + linkType: hard + +"@babel/plugin-transform-destructuring@npm:^7.0.0, @babel/plugin-transform-destructuring@npm:^7.5.0": version: 7.22.5 resolution: "@babel/plugin-transform-destructuring@npm:7.22.5" dependencies: @@ -2835,7 +3205,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.18.6, @babel/plugin-transform-dotall-regex@npm:^7.22.5, @babel/plugin-transform-dotall-regex@npm:^7.4.4": +"@babel/plugin-transform-destructuring@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-destructuring@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b9637b27faf9d24a8119bc5a1f98a2f47c69e6441bd8fc71163500be316253a72173308a93122bcf27d8d314ace43344c976f7291cf6376767f408350c8149d4 + languageName: node + linkType: hard + +"@babel/plugin-transform-dotall-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-dotall-regex@npm:7.22.5" dependencies: @@ -2847,7 +3228,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.18.9, @babel/plugin-transform-duplicate-keys@npm:^7.22.5": +"@babel/plugin-transform-dotall-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 67b10fc6abb1f61f0e765288eb4c6d63d1d0f9fc0660e69f6f2170c56fa16bc74e49857afc644beda112b41771cd90cf52df0940d11e97e52617c77c7dcff171 + languageName: node + linkType: hard + +"@babel/plugin-transform-duplicate-keys@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.22.5" dependencies: @@ -2858,6 +3251,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-duplicate-keys@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d1da2ff85ecb56a63f4ccfd9dc9ae69400d85f0dadf44ecddd9e71c6e5c7a9178e74e3a9637555f415a2bb14551e563f09f98534ab54f53d25e8439fdde6ba2d + languageName: node + linkType: hard + "@babel/plugin-transform-dynamic-import@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-dynamic-import@npm:7.22.11" @@ -2870,7 +3274,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.0.0, @babel/plugin-transform-exponentiation-operator@npm:^7.18.6, @babel/plugin-transform-exponentiation-operator@npm:^7.22.5": +"@babel/plugin-transform-dynamic-import@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 776509ff62ab40c12be814a342fc56a5cc09b91fb63032b2633414b635875fd7da03734657be0f6db2891fe6e3033b75d5ddb6f2baabd1a02e4443754a785002 + languageName: node + linkType: hard + +"@babel/plugin-transform-exponentiation-operator@npm:^7.0.0, @babel/plugin-transform-exponentiation-operator@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.22.5" dependencies: @@ -2882,6 +3298,30 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-exponentiation-operator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.7" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 23c84a23eb56589fdd35a3540f9a1190615be069110a2270865223c03aee3ba4e0fc68fe14850800cf36f0712b26e4964d3026235261f58f0405a29fe8dac9b1 + languageName: node + linkType: hard + +"@babel/plugin-transform-export-namespace-from@npm:7.24.7, @babel/plugin-transform-export-namespace-from@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3bd3a10038f10ae0dea1ee42137f3edcf7036b5e9e570a0d1cbd0865f03658990c6c2d84fa2475f87a754e7dc5b46766c16f7ce5c9b32c3040150b6a21233a80 + languageName: node + linkType: hard + "@babel/plugin-transform-export-namespace-from@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-export-namespace-from@npm:7.22.11" @@ -2918,7 +3358,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.0.0, @babel/plugin-transform-for-of@npm:^7.18.8": +"@babel/plugin-transform-for-of@npm:^7.0.0": version: 7.22.5 resolution: "@babel/plugin-transform-for-of@npm:7.22.5" dependencies: @@ -2940,7 +3380,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.0.0, @babel/plugin-transform-function-name@npm:^7.18.9, @babel/plugin-transform-function-name@npm:^7.22.5": +"@babel/plugin-transform-for-of@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-for-of@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a53b42dc93ab4b7d1ebd3c695b52be22b3d592f6a3dbdb3dc2fea2c8e0a7e1508fe919864c455cde552aec44ce7518625fccbb70c7063373ca228d884f4f49ea + languageName: node + linkType: hard + +"@babel/plugin-transform-function-name@npm:^7.0.0, @babel/plugin-transform-function-name@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-function-name@npm:7.22.5" dependencies: @@ -2953,6 +3405,19 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-function-name@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-function-name@npm:7.24.7" + dependencies: + "@babel/helper-compilation-targets": ^7.24.7 + "@babel/helper-function-name": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8eb1a67894a124910b5a67630bed4307757504381f39f0fb5cf82afc7ae8647dbc03b256d13865b73a749b9071b68e9fb8a28cef2369917b4299ebb93fd66146 + languageName: node + linkType: hard + "@babel/plugin-transform-json-strings@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-json-strings@npm:7.22.11" @@ -2965,7 +3430,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.0.0, @babel/plugin-transform-literals@npm:^7.18.9, @babel/plugin-transform-literals@npm:^7.22.5": +"@babel/plugin-transform-json-strings@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-json-strings@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-json-strings": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 88874d0b7a1ddea66c097fc0abb68801ffae194468aa44b828dde9a0e20ac5d8647943793de86092eabaa2911c96f67a6b373793d4bb9c932ef81b2711c06c2e + languageName: node + linkType: hard + +"@babel/plugin-transform-literals@npm:^7.0.0, @babel/plugin-transform-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-literals@npm:7.22.5" dependencies: @@ -2976,6 +3453,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-literals@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3c075cc093a3dd9e294b8b7d6656e65f889e7ca2179ca27978dcd65b4dc4885ebbfb327408d7d8f483c55547deed00ba840956196f3ac8a3c3d2308a330a8c23 + languageName: node + linkType: hard + "@babel/plugin-transform-logical-assignment-operators@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.22.11" @@ -2988,7 +3476,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.0.0, @babel/plugin-transform-member-expression-literals@npm:^7.18.6, @babel/plugin-transform-member-expression-literals@npm:^7.22.5": +"@babel/plugin-transform-logical-assignment-operators@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3367ce0be243704dc6fce23e86a592c4380f01998ee5dd9f94c54b1ef7b971ac6f8a002901eb51599ac6cbdc0d067af8d1a720224fca1c40fde8bb8aab804aac + languageName: node + linkType: hard + +"@babel/plugin-transform-member-expression-literals@npm:^7.0.0, @babel/plugin-transform-member-expression-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.22.5" dependencies: @@ -2999,15 +3499,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.19.6": - version: 7.22.5 - resolution: "@babel/plugin-transform-modules-amd@npm:7.22.5" +"@babel/plugin-transform-member-expression-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.7" dependencies: - "@babel/helper-module-transforms": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 7da4c4ebbbcf7d182abb59b2046b22d86eee340caf8a22a39ef6a727da2d8acfec1f714fcdcd5054110b280e4934f735e80a6848d192b6834c5d4459a014f04d + checksum: 2720c57aa3bf70576146ba7d6ea03227f4611852122d76d237924f7b008dafc952e6ae61a19e5024f26c665f44384bbd378466f01b6bd1305b3564a3b7fb1a5d languageName: node linkType: hard @@ -3023,7 +3522,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.0.0, @babel/plugin-transform-modules-commonjs@npm:^7.13.8, @babel/plugin-transform-modules-commonjs@npm:^7.19.6": +"@babel/plugin-transform-modules-amd@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-amd@npm:7.24.7" + dependencies: + "@babel/helper-module-transforms": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f1dd0fb2f46c0f8f21076b8c7ccd5b33a85ce6dcb31518ea4c648d9a5bb2474cd4bd87c9b1b752e68591e24b022e334ba0d07631fef2b6b4d8a4b85cf3d581f5 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-commonjs@npm:^7.0.0, @babel/plugin-transform-modules-commonjs@npm:^7.13.8": version: 7.22.5 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.22.5" dependencies: @@ -3049,17 +3560,16 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.19.6": - version: 7.22.5 - resolution: "@babel/plugin-transform-modules-systemjs@npm:7.22.5" +"@babel/plugin-transform-modules-commonjs@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.7" dependencies: - "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-module-transforms": ^7.22.5 - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-validator-identifier": ^7.22.5 + "@babel/helper-module-transforms": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-simple-access": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 04f4178589543396b3c24330a67a59c5e69af5e96119c9adda730c0f20122deaff54671ebbc72ad2df6495a5db8a758bd96942de95fba7ad427de9c80b1b38c8 + checksum: bfda2a0297197ed342e2a02e5f9847a489a3ae40a4a7d7f00f4aeb8544a85e9006e0c5271c8f61f39bc97975ef2717b5594cf9486694377a53433162909d64c1 languageName: node linkType: hard @@ -3077,7 +3587,21 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.18.6, @babel/plugin-transform-modules-umd@npm:^7.22.5": +"@babel/plugin-transform-modules-systemjs@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.24.7" + dependencies: + "@babel/helper-hoist-variables": ^7.24.7 + "@babel/helper-module-transforms": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-validator-identifier": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8af7a9db2929991d82cfdf41fb175dee344274d39b39122f8c35f24b5d682f98368e3d8f5130401298bd21412df21d416a7d8b33b59c334fae3d3c762118b1d8 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-umd@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-modules-umd@npm:7.22.5" dependencies: @@ -3089,7 +3613,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.0.0, @babel/plugin-transform-named-capturing-groups-regex@npm:^7.19.1, @babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": +"@babel/plugin-transform-modules-umd@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-umd@npm:7.24.7" + dependencies: + "@babel/helper-module-transforms": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 9ff1c464892efe042952ba778468bda6131b196a2729615bdcc3f24cdc94014f016a4616ee5643c5845bade6ba698f386833e61056d7201314b13a7fd69fac88 + languageName: node + linkType: hard + +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.0.0, @babel/plugin-transform-named-capturing-groups-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.22.5" dependencies: @@ -3101,7 +3637,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.18.6, @babel/plugin-transform-new-target@npm:^7.22.5": +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: f1c6c7b5d60a86b6d7e4dd098798e1d393d55e993a0b57a73b53640c7a94985b601a96bdacee063f809a9a700bcea3a2ff18e98fa561554484ac56b761d774bd + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-new-target@npm:7.22.5" dependencies: @@ -3112,6 +3660,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-new-target@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-new-target@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3cb94cd1076b270f768f91fdcf9dd2f6d487f8dbfff3df7ca8d07b915900b86d02769a35ba1407d16fe49499012c8f055e1741299e2c880798b953d942a8fa1b + languageName: node + linkType: hard + "@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.22.11" @@ -3124,6 +3683,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4a9221356401d87762afbc37a9e8e764afc2daf09c421117537820f8cfbed6876888372ad3a7bcfae2d45c95f026651f050ab4020b777be31d3ffb00908dbdd3 + languageName: node + linkType: hard + "@babel/plugin-transform-numeric-separator@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-numeric-separator@npm:7.22.11" @@ -3136,6 +3707,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-numeric-separator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 561b5f1d08b2c3f92ce849f092751558b5e6cfeb7eb55c79e7375c34dd9c3066dce5e630bb439affef6adcf202b6cbcaaa23870070276fa5bb429c8f5b8c7514 + languageName: node + linkType: hard + "@babel/plugin-transform-object-rest-spread@npm:^7.22.15": version: 7.22.15 resolution: "@babel/plugin-transform-object-rest-spread@npm:7.22.15" @@ -3151,7 +3734,21 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.0.0, @babel/plugin-transform-object-super@npm:^7.18.6, @babel/plugin-transform-object-super@npm:^7.22.5": +"@babel/plugin-transform-object-rest-spread@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.7" + dependencies: + "@babel/helper-compilation-targets": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-transform-parameters": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 169d257b9800c13e1feb4c37fb05dae84f702e58b342bb76e19e82e6692b7b5337c9923ee89e3916a97c0dd04a3375bdeca14f5e126f110bbacbeb46d1886ca2 + languageName: node + linkType: hard + +"@babel/plugin-transform-object-super@npm:^7.0.0, @babel/plugin-transform-object-super@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-object-super@npm:7.22.5" dependencies: @@ -3163,6 +3760,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-object-super@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-object-super@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-replace-supers": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f71e607a830ee50a22fa1a2686524d3339440cf9dea63032f6efbd865cfe4e35000e1e3f3492459e5c986f7c0c07dc36938bf3ce61fc9ba5f8ab732d0b64ab37 + languageName: node + linkType: hard + "@babel/plugin-transform-optional-catch-binding@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.22.11" @@ -3175,6 +3784,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-optional-catch-binding@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7229f3a5a4facaab40f4fdfc7faabc157dc38a67d66bed7936599f4bc509e0bff636f847ac2aa45294881fce9cf8a0a460b85d2a465b7b977de9739fce9b18f6 + languageName: node + linkType: hard + "@babel/plugin-transform-optional-chaining@npm:^7.22.15, @babel/plugin-transform-optional-chaining@npm:^7.23.0": version: 7.23.0 resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.0" @@ -3188,20 +3809,20 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.22.5" +"@babel/plugin-transform-optional-chaining@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 - "@babel/helper-skip-transparent-expression-wrappers": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 "@babel/plugin-syntax-optional-chaining": ^7.8.3 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 57b9c05fb22ae881b8a334b184fc6ee966661ed5d1eb4eed8c2fb9a12e68150d90b229efcb1aa777e246999830844fee06d7365f8bb4bb262fdcd23876ff3ea2 + checksum: 877e7ce9097d475132c7f4d1244de50bb2fd37993dc4580c735f18f8cbc49282f6e77752821bcad5ca9d3528412d2c8a7ee0aa7ca71bb680ff82648e7a5fed25 languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.0.0, @babel/plugin-transform-parameters@npm:^7.20.1, @babel/plugin-transform-parameters@npm:^7.20.7": +"@babel/plugin-transform-parameters@npm:^7.0.0, @babel/plugin-transform-parameters@npm:^7.20.7": version: 7.22.5 resolution: "@babel/plugin-transform-parameters@npm:7.22.5" dependencies: @@ -3223,6 +3844,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-parameters@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-parameters@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ab534b03ac2eff94bc79342b8f39a4584666f5305a6c63c1964afda0b1b004e6b861e49d1683548030defe248e3590d3ff6338ee0552cb90c064f7e1479968c3 + languageName: node + linkType: hard + "@babel/plugin-transform-private-methods@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-private-methods@npm:7.22.5" @@ -3235,6 +3867,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-private-methods@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-private-methods@npm:7.24.7" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c151548e34909be2adcceb224d8fdd70bafa393bc1559a600906f3f647317575bf40db670470934a360e90ee8084ef36dffa34ec25d387d414afd841e74cf3fe + languageName: node + linkType: hard + "@babel/plugin-transform-private-property-in-object@npm:^7.22.11": version: 7.22.11 resolution: "@babel/plugin-transform-private-property-in-object@npm:7.22.11" @@ -3249,7 +3893,21 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.0.0, @babel/plugin-transform-property-literals@npm:^7.18.6, @babel/plugin-transform-property-literals@npm:^7.22.5": +"@babel/plugin-transform-private-property-in-object@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": ^7.24.7 + "@babel/helper-create-class-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8cee9473095305cc787bb653fd681719b49363281feabf677db8a552e8e41c94441408055d7e5fd5c7d41b315e634fa70b145ad0c7c54456216049df4ed57350 + languageName: node + linkType: hard + +"@babel/plugin-transform-property-literals@npm:^7.0.0, @babel/plugin-transform-property-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-property-literals@npm:7.22.5" dependencies: @@ -3260,6 +3918,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-property-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-property-literals@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 9aeefc3aab6c6bf9d1fae1cf3a2d38c7d886fd3c6c81b7c608c477f5758aee2e7abf52f32724310fe861da61af934ee2508b78a5b5f234b9740c9134e1c14437 + languageName: node + linkType: hard + "@babel/plugin-transform-react-constant-elements@npm:^7.18.12": version: 7.22.5 resolution: "@babel/plugin-transform-react-constant-elements@npm:7.22.5" @@ -3368,31 +4037,31 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.18.6": - version: 7.22.5 - resolution: "@babel/plugin-transform-regenerator@npm:7.22.5" +"@babel/plugin-transform-regenerator@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/plugin-transform-regenerator@npm:7.22.10" dependencies: "@babel/helper-plugin-utils": ^7.22.5 - regenerator-transform: ^0.15.1 + regenerator-transform: ^0.15.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: f7c5ca5151321963df777cc02725d10d1ccc3b3b8323da0423aecd9ac6144cbdd2274af5281a5580db2fc2f8b234e318517b5d76b85669118906533a559f2b6a + checksum: e13678d62d6fa96f11cb8b863f00e8693491e7adc88bfca3f2820f80cbac8336e7dec3a596eee6a1c4663b7ececc3564f2cd7fb44ed6d4ce84ac2bb7f39ecc6e languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.22.10": - version: 7.22.10 - resolution: "@babel/plugin-transform-regenerator@npm:7.22.10" +"@babel/plugin-transform-regenerator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-regenerator@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 regenerator-transform: ^0.15.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e13678d62d6fa96f11cb8b863f00e8693491e7adc88bfca3f2820f80cbac8336e7dec3a596eee6a1c4663b7ececc3564f2cd7fb44ed6d4ce84ac2bb7f39ecc6e + checksum: 20c6c3fb6fc9f407829087316653388d311e8c1816b007609bb09aeef254092a7157adace8b3aaa8f34be752503717cb85c88a5fe482180a9b11bcbd676063be languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.18.6, @babel/plugin-transform-reserved-words@npm:^7.22.5": +"@babel/plugin-transform-reserved-words@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-reserved-words@npm:7.22.5" dependencies: @@ -3403,6 +4072,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-reserved-words@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-reserved-words@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3d5876954d5914d7270819479504f30c4bf5452a65c677f44e2dab2db50b3c9d4b47793c45dfad7abf4f377035dd79e4b3f554ae350df9f422201d370ce9f8dd + languageName: node + linkType: hard + "@babel/plugin-transform-runtime@npm:^7.0.0": version: 7.16.4 resolution: "@babel/plugin-transform-runtime@npm:7.16.4" @@ -3435,7 +4115,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.0.0, @babel/plugin-transform-shorthand-properties@npm:^7.18.6, @babel/plugin-transform-shorthand-properties@npm:^7.22.5": +"@babel/plugin-transform-shorthand-properties@npm:^7.0.0, @babel/plugin-transform-shorthand-properties@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.22.5" dependencies: @@ -3446,7 +4126,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.0.0, @babel/plugin-transform-spread@npm:^7.19.0, @babel/plugin-transform-spread@npm:^7.22.5": +"@babel/plugin-transform-shorthand-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7b524245814607188212b8eb86d8c850e5974203328455a30881b4a92c364b93353fae14bc2af5b614ef16300b75b8c1d3b8f3a08355985b4794a7feb240adc3 + languageName: node + linkType: hard + +"@babel/plugin-transform-spread@npm:^7.0.0, @babel/plugin-transform-spread@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-spread@npm:7.22.5" dependencies: @@ -3458,7 +4149,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.0.0, @babel/plugin-transform-sticky-regex@npm:^7.18.6, @babel/plugin-transform-sticky-regex@npm:^7.22.5": +"@babel/plugin-transform-spread@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-spread@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4c4254c8b9cceb1a8f975fa9b92257ddb08380a35c0a3721b8f4b9e13a3d82e403af2e0fba577b9f2452dd8f06bc3dea71cc53b1e2c6af595af5db52a13429d6 + languageName: node + linkType: hard + +"@babel/plugin-transform-sticky-regex@npm:^7.0.0, @babel/plugin-transform-sticky-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-sticky-regex@npm:7.22.5" dependencies: @@ -3469,7 +4172,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.0.0, @babel/plugin-transform-template-literals@npm:^7.18.9, @babel/plugin-transform-template-literals@npm:^7.22.5": +"@babel/plugin-transform-sticky-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 118fc7a7ebf7c20411b670c8a030535fdfe4a88bc5643bb625a584dbc4c8a468da46430a20e6bf78914246962b0f18f1b9d6a62561a7762c4f34a038a5a77179 + languageName: node + linkType: hard + +"@babel/plugin-transform-template-literals@npm:^7.0.0, @babel/plugin-transform-template-literals@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-template-literals@npm:7.22.5" dependencies: @@ -3480,7 +4194,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.18.9, @babel/plugin-transform-typeof-symbol@npm:^7.22.5": +"@babel/plugin-transform-template-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-template-literals@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ad44e5826f5a98c1575832dbdbd033adfe683cdff195e178528ead62507564bf02f479b282976cfd3caebad8b06d5fd7349c1cdb880dec3c56daea4f1f179619 + languageName: node + linkType: hard + +"@babel/plugin-transform-typeof-symbol@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.22.5" dependencies: @@ -3491,6 +4216,17 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-typeof-symbol@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6bd16b9347614d44187d8f8ee23ebd7be30dabf3632eed5ff0415f35a482e827de220527089eae9cdfb75e85aa72db0e141ebc2247c4b1187c1abcdacdc34895 + languageName: node + linkType: hard + "@babel/plugin-transform-typescript@npm:^7.18.6, @babel/plugin-transform-typescript@npm:^7.5.0": version: 7.20.7 resolution: "@babel/plugin-transform-typescript@npm:7.20.7" @@ -3518,25 +4254,25 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.18.10": - version: 7.22.5 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.5" +"@babel/plugin-transform-unicode-escapes@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.10" dependencies: "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: da5e85ab3bb33a75cbf6181bfd236b208dc934702fd304db127232f17b4e0f42c6d3f238de8589470b4190906967eea8ca27adf3ae9d8ee4de2a2eae906ed186 + checksum: 807f40ed1324c8cb107c45358f1903384ca3f0ef1d01c5a3c5c9b271c8d8eec66936a3dcc8d75ddfceea9421420368c2e77ae3adef0a50557e778dfe296bf382 languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.22.10": - version: 7.22.10 - resolution: "@babel/plugin-transform-unicode-escapes@npm:7.22.10" +"@babel/plugin-transform-unicode-escapes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.7" dependencies: - "@babel/helper-plugin-utils": ^7.22.5 + "@babel/helper-plugin-utils": ^7.24.7 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 807f40ed1324c8cb107c45358f1903384ca3f0ef1d01c5a3c5c9b271c8d8eec66936a3dcc8d75ddfceea9421420368c2e77ae3adef0a50557e778dfe296bf382 + checksum: 4af0a193e1ddea6ff82b2b15cc2501b872728050bd625740b813c8062fec917d32d530ff6b41de56c15e7296becdf3336a58db81f5ca8e7c445c1306c52f3e01 languageName: node linkType: hard @@ -3552,7 +4288,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.0.0, @babel/plugin-transform-unicode-regex@npm:^7.18.6, @babel/plugin-transform-unicode-regex@npm:^7.22.5": +"@babel/plugin-transform-unicode-property-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: aae13350c50973f5802ca7906d022a6a0cc0e3aebac9122d0450bbd51e78252d4c2032ad69385e2759fcbdd3aac5d571bd7e26258907f51f8e1a51b53be626c2 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-regex@npm:^7.0.0, @babel/plugin-transform-unicode-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-regex@npm:7.22.5" dependencies: @@ -3564,6 +4312,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-unicode-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1cb4e70678906e431da0a05ac3f8350025fee290304ad7482d9cfaa1ca67b2e898654de537c9268efbdad5b80d3ebadf42b4a88ea84609bd8a4cce7b11b48afd + languageName: node + linkType: hard + "@babel/plugin-transform-unicode-sets-regex@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.22.5" @@ -3576,37 +4336,39 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:7.20.2": - version: 7.20.2 - resolution: "@babel/preset-env@npm:7.20.2" +"@babel/plugin-transform-unicode-sets-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.24.7" dependencies: - "@babel/compat-data": ^7.20.1 - "@babel/helper-compilation-targets": ^7.20.0 - "@babel/helper-plugin-utils": ^7.20.2 - "@babel/helper-validator-option": ^7.18.6 - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.18.9 - "@babel/plugin-proposal-async-generator-functions": ^7.20.1 - "@babel/plugin-proposal-class-properties": ^7.18.6 - "@babel/plugin-proposal-class-static-block": ^7.18.6 - "@babel/plugin-proposal-dynamic-import": ^7.18.6 - "@babel/plugin-proposal-export-namespace-from": ^7.18.9 - "@babel/plugin-proposal-json-strings": ^7.18.6 - "@babel/plugin-proposal-logical-assignment-operators": ^7.18.9 - "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 - "@babel/plugin-proposal-numeric-separator": ^7.18.6 - "@babel/plugin-proposal-object-rest-spread": ^7.20.2 - "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 - "@babel/plugin-proposal-optional-chaining": ^7.18.9 - "@babel/plugin-proposal-private-methods": ^7.18.6 - "@babel/plugin-proposal-private-property-in-object": ^7.18.6 - "@babel/plugin-proposal-unicode-property-regex": ^7.18.6 + "@babel/helper-create-regexp-features-plugin": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 08a2844914f33dacd2ce1ab021ce8c1cc35dc6568521a746d8bf29c21571ee5be78787b454231c4bb3526cbbe280f1893223c82726cec5df2be5dae0a3b51837 + languageName: node + linkType: hard + +"@babel/preset-env@npm:7.24.7": + version: 7.24.7 + resolution: "@babel/preset-env@npm:7.24.7" + dependencies: + "@babel/compat-data": ^7.24.7 + "@babel/helper-compilation-targets": ^7.24.7 + "@babel/helper-plugin-utils": ^7.24.7 + "@babel/helper-validator-option": ^7.24.7 + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ^7.24.7 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.24.7 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.24.7 + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ^7.24.7 + "@babel/plugin-proposal-private-property-in-object": 7.21.0-placeholder-for-preset-env.2 "@babel/plugin-syntax-async-generators": ^7.8.4 "@babel/plugin-syntax-class-properties": ^7.12.13 "@babel/plugin-syntax-class-static-block": ^7.14.5 "@babel/plugin-syntax-dynamic-import": ^7.8.3 "@babel/plugin-syntax-export-namespace-from": ^7.8.3 - "@babel/plugin-syntax-import-assertions": ^7.20.0 + "@babel/plugin-syntax-import-assertions": ^7.24.7 + "@babel/plugin-syntax-import-attributes": ^7.24.7 + "@babel/plugin-syntax-import-meta": ^7.10.4 "@babel/plugin-syntax-json-strings": ^7.8.3 "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 @@ -3616,48 +4378,64 @@ __metadata: "@babel/plugin-syntax-optional-chaining": ^7.8.3 "@babel/plugin-syntax-private-property-in-object": ^7.14.5 "@babel/plugin-syntax-top-level-await": ^7.14.5 - "@babel/plugin-transform-arrow-functions": ^7.18.6 - "@babel/plugin-transform-async-to-generator": ^7.18.6 - "@babel/plugin-transform-block-scoped-functions": ^7.18.6 - "@babel/plugin-transform-block-scoping": ^7.20.2 - "@babel/plugin-transform-classes": ^7.20.2 - "@babel/plugin-transform-computed-properties": ^7.18.9 - "@babel/plugin-transform-destructuring": ^7.20.2 - "@babel/plugin-transform-dotall-regex": ^7.18.6 - "@babel/plugin-transform-duplicate-keys": ^7.18.9 - "@babel/plugin-transform-exponentiation-operator": ^7.18.6 - "@babel/plugin-transform-for-of": ^7.18.8 - "@babel/plugin-transform-function-name": ^7.18.9 - "@babel/plugin-transform-literals": ^7.18.9 - "@babel/plugin-transform-member-expression-literals": ^7.18.6 - "@babel/plugin-transform-modules-amd": ^7.19.6 - "@babel/plugin-transform-modules-commonjs": ^7.19.6 - "@babel/plugin-transform-modules-systemjs": ^7.19.6 - "@babel/plugin-transform-modules-umd": ^7.18.6 - "@babel/plugin-transform-named-capturing-groups-regex": ^7.19.1 - "@babel/plugin-transform-new-target": ^7.18.6 - "@babel/plugin-transform-object-super": ^7.18.6 - "@babel/plugin-transform-parameters": ^7.20.1 - "@babel/plugin-transform-property-literals": ^7.18.6 - "@babel/plugin-transform-regenerator": ^7.18.6 - "@babel/plugin-transform-reserved-words": ^7.18.6 - "@babel/plugin-transform-shorthand-properties": ^7.18.6 - "@babel/plugin-transform-spread": ^7.19.0 - "@babel/plugin-transform-sticky-regex": ^7.18.6 - "@babel/plugin-transform-template-literals": ^7.18.9 - "@babel/plugin-transform-typeof-symbol": ^7.18.9 - "@babel/plugin-transform-unicode-escapes": ^7.18.10 - "@babel/plugin-transform-unicode-regex": ^7.18.6 - "@babel/preset-modules": ^0.1.5 - "@babel/types": ^7.20.2 - babel-plugin-polyfill-corejs2: ^0.3.3 - babel-plugin-polyfill-corejs3: ^0.6.0 - babel-plugin-polyfill-regenerator: ^0.4.1 - core-js-compat: ^3.25.1 - semver: ^6.3.0 + "@babel/plugin-syntax-unicode-sets-regex": ^7.18.6 + "@babel/plugin-transform-arrow-functions": ^7.24.7 + "@babel/plugin-transform-async-generator-functions": ^7.24.7 + "@babel/plugin-transform-async-to-generator": ^7.24.7 + "@babel/plugin-transform-block-scoped-functions": ^7.24.7 + "@babel/plugin-transform-block-scoping": ^7.24.7 + "@babel/plugin-transform-class-properties": ^7.24.7 + "@babel/plugin-transform-class-static-block": ^7.24.7 + "@babel/plugin-transform-classes": ^7.24.7 + "@babel/plugin-transform-computed-properties": ^7.24.7 + "@babel/plugin-transform-destructuring": ^7.24.7 + "@babel/plugin-transform-dotall-regex": ^7.24.7 + "@babel/plugin-transform-duplicate-keys": ^7.24.7 + "@babel/plugin-transform-dynamic-import": ^7.24.7 + "@babel/plugin-transform-exponentiation-operator": ^7.24.7 + "@babel/plugin-transform-export-namespace-from": ^7.24.7 + "@babel/plugin-transform-for-of": ^7.24.7 + "@babel/plugin-transform-function-name": ^7.24.7 + "@babel/plugin-transform-json-strings": ^7.24.7 + "@babel/plugin-transform-literals": ^7.24.7 + "@babel/plugin-transform-logical-assignment-operators": ^7.24.7 + "@babel/plugin-transform-member-expression-literals": ^7.24.7 + "@babel/plugin-transform-modules-amd": ^7.24.7 + "@babel/plugin-transform-modules-commonjs": ^7.24.7 + "@babel/plugin-transform-modules-systemjs": ^7.24.7 + "@babel/plugin-transform-modules-umd": ^7.24.7 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.24.7 + "@babel/plugin-transform-new-target": ^7.24.7 + "@babel/plugin-transform-nullish-coalescing-operator": ^7.24.7 + "@babel/plugin-transform-numeric-separator": ^7.24.7 + "@babel/plugin-transform-object-rest-spread": ^7.24.7 + "@babel/plugin-transform-object-super": ^7.24.7 + "@babel/plugin-transform-optional-catch-binding": ^7.24.7 + "@babel/plugin-transform-optional-chaining": ^7.24.7 + "@babel/plugin-transform-parameters": ^7.24.7 + "@babel/plugin-transform-private-methods": ^7.24.7 + "@babel/plugin-transform-private-property-in-object": ^7.24.7 + "@babel/plugin-transform-property-literals": ^7.24.7 + "@babel/plugin-transform-regenerator": ^7.24.7 + "@babel/plugin-transform-reserved-words": ^7.24.7 + "@babel/plugin-transform-shorthand-properties": ^7.24.7 + "@babel/plugin-transform-spread": ^7.24.7 + "@babel/plugin-transform-sticky-regex": ^7.24.7 + "@babel/plugin-transform-template-literals": ^7.24.7 + "@babel/plugin-transform-typeof-symbol": ^7.24.7 + "@babel/plugin-transform-unicode-escapes": ^7.24.7 + "@babel/plugin-transform-unicode-property-regex": ^7.24.7 + "@babel/plugin-transform-unicode-regex": ^7.24.7 + "@babel/plugin-transform-unicode-sets-regex": ^7.24.7 + "@babel/preset-modules": 0.1.6-no-external-plugins + babel-plugin-polyfill-corejs2: ^0.4.10 + babel-plugin-polyfill-corejs3: ^0.10.4 + babel-plugin-polyfill-regenerator: ^0.6.1 + core-js-compat: ^3.31.0 + semver: ^6.3.1 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ece2d7e9c7789db6116e962b8e1a55eb55c110c44c217f0c8f6ffea4ca234954e66557f7bd019b7affadf7fbb3a53ccc807e93fc935aacd48146234b73b6947e + checksum: 1a82c883c7404359b19b7436d0aab05f8dd4e89e8b1f7de127cc65d0ff6a9b1c345211d9c038f5b6e8f93d26f091fa9c73812d82851026ab4ec93f5ed0f0d675 languageName: node linkType: hard @@ -3777,21 +4555,6 @@ __metadata: languageName: node linkType: hard -"@babel/preset-modules@npm:^0.1.5": - version: 0.1.5 - resolution: "@babel/preset-modules@npm:0.1.5" - dependencies: - "@babel/helper-plugin-utils": ^7.0.0 - "@babel/plugin-proposal-unicode-property-regex": ^7.4.4 - "@babel/plugin-transform-dotall-regex": ^7.4.4 - "@babel/types": ^7.4.4 - esutils: ^2.0.2 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 8430e0e9e9d520b53e22e8c4c6a5a080a12b63af6eabe559c2310b187bd62ae113f3da82ba33e9d1d0f3230930ca702843aae9dd226dec51f7d7114dc1f51c10 - languageName: node - linkType: hard - "@babel/preset-react@npm:^7.18.6": version: 7.22.15 resolution: "@babel/preset-react@npm:7.22.15" @@ -3868,12 +4631,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.20.0": - version: 7.20.0 - resolution: "@babel/runtime@npm:7.20.0" +"@babel/runtime@npm:7.24.7": + version: 7.24.7 + resolution: "@babel/runtime@npm:7.24.7" dependencies: - regenerator-runtime: ^0.13.10 - checksum: 637fca51db34f3a59d329b7e0d01163769fe94915fdb04e4ac4ba62de9f1ca637ce3a564fe3b0166ccdd7f02f14b6a5707ee3e550b3e01c72327c6620d8e6a8b + regenerator-runtime: ^0.14.0 + checksum: d17f29eed6f848ac15cdf4202a910b741facfb0419a9d79e5c7fa37df6362fc3227f1cc2e248cc6db5e53ddffb4caa6686c488e6e80ce3d29c36a4e74c8734ea languageName: node linkType: hard @@ -3895,7 +4658,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.0.0, @babel/template@npm:^7.16.0, @babel/template@npm:^7.18.10, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": +"@babel/template@npm:^7.0.0, @babel/template@npm:^7.16.0, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" dependencies: @@ -3917,6 +4680,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/template@npm:7.24.7" + dependencies: + "@babel/code-frame": ^7.24.7 + "@babel/parser": ^7.24.7 + "@babel/types": ^7.24.7 + checksum: ea90792fae708ddf1632e54c25fe1a86643d8c0132311f81265d2bdbdd42f9f4fac65457056c1b6ca87f7aa0d6a795b549566774bba064bdcea2034ab3960ee9 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.18.8, @babel/traverse@npm:^7.23.2": version: 7.23.2 resolution: "@babel/traverse@npm:7.23.2" @@ -3935,7 +4709,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.0, @babel/traverse@npm:^7.20.0, @babel/traverse@npm:^7.20.1, @babel/traverse@npm:^7.22.5, @babel/traverse@npm:^7.4.5": +"@babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.0, @babel/traverse@npm:^7.20.0, @babel/traverse@npm:^7.22.5, @babel/traverse@npm:^7.4.5": version: 7.22.5 resolution: "@babel/traverse@npm:7.22.5" dependencies: @@ -3953,7 +4727,25 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.16.0, @babel/types@npm:^7.19.0, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/traverse@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/traverse@npm:7.24.7" + dependencies: + "@babel/code-frame": ^7.24.7 + "@babel/generator": ^7.24.7 + "@babel/helper-environment-visitor": ^7.24.7 + "@babel/helper-function-name": ^7.24.7 + "@babel/helper-hoist-variables": ^7.24.7 + "@babel/helper-split-export-declaration": ^7.24.7 + "@babel/parser": ^7.24.7 + "@babel/types": ^7.24.7 + debug: ^4.3.1 + globals: ^11.1.0 + checksum: 7cd366afe9e7ee77e493779fdf24f67bf5595247289364f4689e29688572505eaeb886d7a8f20ebb9c29fc2de7d0895e4ff9e203e78e39ac67239724d45aa83b + languageName: node + linkType: hard + +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.16.0, @babel/types@npm:^7.19.0, @babel/types@npm:^7.20.0, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.22.5 resolution: "@babel/types@npm:7.22.5" dependencies: @@ -3975,6 +4767,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/types@npm:7.24.7" + dependencies: + "@babel/helper-string-parser": ^7.24.7 + "@babel/helper-validator-identifier": ^7.24.7 + to-fast-properties: ^2.0.0 + checksum: 3e4437fced97e02982972ce5bebd318c47d42c9be2152c0fd28c6f786cc74086cc0a8fb83b602b846e41df37f22c36254338eada1a47ef9d8a1ec92332ca3ea8 + languageName: node + linkType: hard + "@bam.tech/react-native-image-resizer@npm:3.0.9": version: 3.0.9 resolution: "@bam.tech/react-native-image-resizer@npm:3.0.9" @@ -6638,10 +7441,10 @@ __metadata: version: 0.0.0-use.local resolution: "@joplin/app-mobile@workspace:packages/app-mobile" dependencies: - "@babel/core": 7.20.2 - "@babel/plugin-transform-export-namespace-from": ^7.22.5 - "@babel/preset-env": 7.20.2 - "@babel/runtime": 7.20.0 + "@babel/core": 7.24.7 + "@babel/plugin-transform-export-namespace-from": 7.24.7 + "@babel/preset-env": 7.24.7 + "@babel/runtime": 7.24.7 "@bam.tech/react-native-image-resizer": 3.0.9 "@joplin/editor": ~3.0 "@joplin/lib": ~3.0 @@ -6659,6 +7462,7 @@ __metadata: "@react-native-community/slider": 4.5.0 "@react-native/babel-preset": 0.74.83 "@react-native/metro-config": 0.74.83 + "@sqlite.org/sqlite-wasm": 3.46.0-build2 "@testing-library/jest-native": 5.4.3 "@testing-library/react-native": 12.3.3 "@tsconfig/react-native": 2.0.2 @@ -6672,7 +7476,7 @@ __metadata: babel-jest: 29.7.0 babel-loader: 9.1.3 babel-plugin-module-resolver: 4.1.0 - babel-plugin-react-native-web: 0.19.7 + babel-plugin-react-native-web: 0.19.12 buffer: 6.0.3 constants-browserify: 1.0.0 crypto-browserify: 3.12.0 @@ -6692,6 +7496,7 @@ __metadata: prop-types: 15.8.1 punycode: 2.3.1 react: 18.2.0 + react-dom: 18.3.1 react-native: 0.74.1 react-native-camera: 4.2.1 react-native-device-info: 10.12.1 @@ -6719,11 +7524,12 @@ __metadata: react-native-vector-icons: 10.1.0 react-native-version-info: 1.1.1 react-native-vosk: 0.1.12 + react-native-web: 0.19.12 react-native-webview: 13.8.1 react-native-zip-archive: 6.1.0 - react-redux: 8.1.3 + react-redux: 9.1.2 react-test-renderer: 18.2.0 - redux: 4.2.1 + redux: 5.0.1 rn-fetch-blob: 0.12.0 sqlite3: 5.1.6 stream: 0.0.2 @@ -6738,9 +7544,10 @@ __metadata: typescript: 5.2.2 uglify-js: 3.17.4 url: 0.11.3 - webpack: 5.74.0 + url-loader: 4.1.1 + webpack: 5.84.0 webpack-cli: 5.1.4 - webpack-dev-server: ^4.15.1 + webpack-dev-server: 5.0.4 languageName: unknown linkType: soft @@ -6932,7 +7739,7 @@ __metadata: react: 18.2.0 react-test-renderer: 18.2.0 read-chunk: 2.1.0 - redux: 4.2.1 + redux: 5.0.1 relative: 3.0.2 reselect: 4.1.8 server-destroy: 1.0.1 @@ -7259,16 +8066,6 @@ __metadata: languageName: unknown linkType: soft -"@jridgewell/gen-mapping@npm:^0.1.0": - version: 0.1.1 - resolution: "@jridgewell/gen-mapping@npm:0.1.1" - dependencies: - "@jridgewell/set-array": ^1.0.0 - "@jridgewell/sourcemap-codec": ^1.4.10 - checksum: 3bcc21fe786de6ffbf35c399a174faab05eb23ce6a03e8769569de28abbf4facc2db36a9ddb0150545ae23a8d35a7cf7237b2aa9e9356a7c626fb4698287d5cc - languageName: node - linkType: hard - "@jridgewell/gen-mapping@npm:^0.3.0": version: 0.3.3 resolution: "@jridgewell/gen-mapping@npm:0.3.3" @@ -7291,6 +8088,17 @@ __metadata: languageName: node linkType: hard +"@jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" + dependencies: + "@jridgewell/set-array": ^1.2.1 + "@jridgewell/sourcemap-codec": ^1.4.10 + "@jridgewell/trace-mapping": ^0.3.24 + checksum: ff7a1764ebd76a5e129c8890aa3e2f46045109dabde62b0b6c6a250152227647178ff2069ea234753a690d8f3c4ac8b5e7b267bbee272bffb7f3b0a370ab6e52 + languageName: node + linkType: hard + "@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": version: 3.1.0 resolution: "@jridgewell/resolve-uri@npm:3.1.0" @@ -7305,13 +8113,20 @@ __metadata: languageName: node linkType: hard -"@jridgewell/set-array@npm:^1.0.0, @jridgewell/set-array@npm:^1.0.1": +"@jridgewell/set-array@npm:^1.0.1": version: 1.1.2 resolution: "@jridgewell/set-array@npm:1.1.2" checksum: 69a84d5980385f396ff60a175f7177af0b8da4ddb81824cb7016a9ef914eee9806c72b6b65942003c63f7983d4f39a5c6c27185bbca88eb4690b62075602e28e languageName: node linkType: hard +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 832e513a85a588f8ed4f27d1279420d8547743cc37fcad5a5a76fc74bb895b013dfe614d0eed9cb860048e6546b798f8f2652020b4b2ba0561b05caa8c654b10 + languageName: node + linkType: hard + "@jridgewell/source-map@npm:^0.3.3": version: 0.3.3 resolution: "@jridgewell/source-map@npm:0.3.3" @@ -7376,6 +8191,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: 9d3c40d225e139987b50c48988f8717a54a8c994d8a948ee42e1412e08988761d0754d7d10b803061cc3aebf35f92a5dbbab493bd0e1a9ef9e89a2130e83ba34 + languageName: node + linkType: hard + "@js-draw/material-icons@npm:1.20.0": version: 1.20.0 resolution: "@js-draw/material-icons@npm:1.20.0" @@ -7394,6 +8219,38 @@ __metadata: languageName: node linkType: hard +"@jsonjoy.com/base64@npm:^1.1.1": + version: 1.1.2 + resolution: "@jsonjoy.com/base64@npm:1.1.2" + peerDependencies: + tslib: 2 + checksum: 00dbf9cbc6ecb3af0e58288a305cc4ee3dfca9efa24443d98061756e8f6de4d6d2d3764bdfde07f2b03e6ce56db27c8a59b490bd134bf3d8122b4c6b394c7010 + languageName: node + linkType: hard + +"@jsonjoy.com/json-pack@npm:^1.0.3": + version: 1.0.4 + resolution: "@jsonjoy.com/json-pack@npm:1.0.4" + dependencies: + "@jsonjoy.com/base64": ^1.1.1 + "@jsonjoy.com/util": ^1.1.2 + hyperdyperid: ^1.2.0 + thingies: ^1.20.0 + peerDependencies: + tslib: 2 + checksum: 21e5166d5b5f4856791c2c7019dfba0e8313d2501937543691cdffd5fbe1f9680548a456d2c8aa78929aa69b2ac4c787ca8dbc7cf8e4926330decedcd0d9b8ea + languageName: node + linkType: hard + +"@jsonjoy.com/util@npm:^1.1.2": + version: 1.2.0 + resolution: "@jsonjoy.com/util@npm:1.2.0" + peerDependencies: + tslib: 2 + checksum: 1af590ffc34a8b2112134bda821e9fddf616c66327f18df3f13dcdaad3b86678022427b4233c8c9ec1ddb5cdc4a26ce0571e105593d22eb98590e724be789373 + languageName: node + linkType: hard + "@koa/cors@npm:3.4.3": version: 3.4.3 resolution: "@koa/cors@npm:3.4.3" @@ -9635,6 +10492,13 @@ __metadata: languageName: node linkType: hard +"@react-native/normalize-colors@npm:^0.74.1": + version: 0.74.84 + resolution: "@react-native/normalize-colors@npm:0.74.84" + checksum: e9a7b3020e6a298ba1c7310d267ef90c39327cb2ed7899bf3778224e52b280802899420dbf36fb8c1a37914f410be0187a9796c1790c1dca86404a40a948235a + languageName: node + linkType: hard + "@react-native/polyfills@npm:2.0.0": version: 2.0.0 resolution: "@react-native/polyfills@npm:2.0.0" @@ -10089,6 +10953,15 @@ __metadata: languageName: node linkType: hard +"@sqlite.org/sqlite-wasm@npm:3.46.0-build2": + version: 3.46.0-build2 + resolution: "@sqlite.org/sqlite-wasm@npm:3.46.0-build2" + bin: + sqlite-wasm: bin/index.js + checksum: f427cfe9b99bc9afcc84ca4afcffe0f9d31b38bd4bfd3de0153b712291d962c52ff69e3e4dbd720dc5bc3d4a0985cb7445ab282f48bdc6d24daf9457acb2b61e + languageName: node + linkType: hard + "@styled-system/background@npm:^5.1.2": version: 5.1.2 resolution: "@styled-system/background@npm:5.1.2" @@ -10593,6 +11466,15 @@ __metadata: languageName: node linkType: hard +"@types/bonjour@npm:^3.5.13": + version: 3.5.13 + resolution: "@types/bonjour@npm:3.5.13" + dependencies: + "@types/node": "*" + checksum: e827570e097bd7d625a673c9c208af2d1a22fa3885c0a1646533cf24394c839c3e5f60ac1bc60c0ddcc69c0615078c9fb2c01b42596c7c582d895d974f2409ee + languageName: node + linkType: hard + "@types/bonjour@npm:^3.5.9": version: 3.5.12 resolution: "@types/bonjour@npm:3.5.12" @@ -10624,6 +11506,16 @@ __metadata: languageName: node linkType: hard +"@types/connect-history-api-fallback@npm:^1.5.4": + version: 1.5.4 + resolution: "@types/connect-history-api-fallback@npm:1.5.4" + dependencies: + "@types/express-serve-static-core": "*" + "@types/node": "*" + checksum: e1dee43b8570ffac02d2d47a2b4ba80d3ca0dd1840632dafb221da199e59dbe3778d3d7303c9e23c6b401f37c076935a5bc2aeae1c4e5feaefe1c371fe2073fd + languageName: node + linkType: hard + "@types/connect@npm:*": version: 3.4.35 resolution: "@types/connect@npm:3.4.35" @@ -11474,7 +12366,7 @@ __metadata: languageName: node linkType: hard -"@types/retry@npm:*": +"@types/retry@npm:*, @types/retry@npm:0.12.2": version: 0.12.2 resolution: "@types/retry@npm:0.12.2" checksum: e5675035717b39ce4f42f339657cae9637cf0c0051cf54314a6a2c44d38d91f6544be9ddc0280587789b6afd056be5d99dbe3e9f4df68c286c36321579b1bf4a @@ -11537,6 +12429,15 @@ __metadata: languageName: node linkType: hard +"@types/serve-index@npm:^1.9.4": + version: 1.9.4 + resolution: "@types/serve-index@npm:1.9.4" + dependencies: + "@types/express": "*" + checksum: 72727c88d54da5b13275ebfb75dcdc4aa12417bbe9da1939e017c4c5f0c906fae843aa4e0fbfe360e7ee9df2f3d388c21abfc488f77ce58693fb57809f8ded92 + languageName: node + linkType: hard + "@types/serve-static@npm:*": version: 1.13.10 resolution: "@types/serve-static@npm:1.13.10" @@ -11558,6 +12459,17 @@ __metadata: languageName: node linkType: hard +"@types/serve-static@npm:^1.15.5": + version: 1.15.7 + resolution: "@types/serve-static@npm:1.15.7" + dependencies: + "@types/http-errors": "*" + "@types/node": "*" + "@types/send": "*" + checksum: bbbf00dbd84719da2250a462270dc68964006e8d62f41fe3741abd94504ba3688f420a49afb2b7478921a1544d3793183ffa097c5724167da777f4e0c7f1a7d6 + languageName: node + linkType: hard + "@types/sockjs@npm:^0.3.33": version: 0.3.35 resolution: "@types/sockjs@npm:0.3.35" @@ -11567,6 +12479,15 @@ __metadata: languageName: node linkType: hard +"@types/sockjs@npm:^0.3.36": + version: 0.3.36 + resolution: "@types/sockjs@npm:0.3.36" + dependencies: + "@types/node": "*" + checksum: b4b5381122465d80ea8b158537c00bc82317222d3fb31fd7229ff25b31fa89134abfbab969118da55622236bf3d8fee75759f3959908b5688991f492008f29bc + languageName: node + linkType: hard + "@types/stack-utils@npm:^2.0.0": version: 2.0.1 resolution: "@types/stack-utils@npm:2.0.1" @@ -11652,6 +12573,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.10": + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" + dependencies: + "@types/node": "*" + checksum: 3ec416ea2be24042ebd677932a462cf16d2080393d8d7d0b1b3f5d6eaa4a7387aaf0eefb99193c0bfd29444857cf2e0c3ac89899e130550dc6c14ada8a46d25e + languageName: node + linkType: hard + "@types/ws@npm:^8.5.5": version: 8.5.8 resolution: "@types/ws@npm:8.5.8" @@ -14057,19 +14987,6 @@ __metadata: languageName: node linkType: hard -"babel-loader@npm:9.1.3": - version: 9.1.3 - resolution: "babel-loader@npm:9.1.3" - dependencies: - find-cache-dir: ^4.0.0 - schema-utils: ^4.0.0 - peerDependencies: - "@babel/core": ^7.12.0 - webpack: ">=5" - checksum: b168dde5b8cf11206513371a79f86bb3faa7c714e6ec9fffd420876b61f3d7f5f4b976431095ef6a14bc4d324505126deb91045fd41e312ba49f4deaa166fe28 - languageName: node - linkType: hard - "babel-plugin-istanbul@npm:^6.1.1": version: 6.1.1 resolution: "babel-plugin-istanbul@npm:6.1.1" @@ -14132,16 +15049,16 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.3.3": - version: 0.3.3 - resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" +"babel-plugin-polyfill-corejs2@npm:^0.4.10": + version: 0.4.11 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.11" dependencies: - "@babel/compat-data": ^7.17.7 - "@babel/helper-define-polyfill-provider": ^0.3.3 - semver: ^6.1.1 + "@babel/compat-data": ^7.22.6 + "@babel/helper-define-polyfill-provider": ^0.6.2 + semver: ^6.3.1 peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 7db3044993f3dddb3cc3d407bc82e640964a3bfe22de05d90e1f8f7a5cb71460011ab136d3c03c6c1ba428359ebf635688cd6205e28d0469bba221985f5c6179 + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: f098353ce7c7dde1a1d2710858e01b471e85689110c9e37813e009072347eb8c55d5f84d20d3bf1cab31755f20078ba90f8855fdc4686a9daa826a95ff280bd7 languageName: node linkType: hard @@ -14158,6 +15075,18 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-corejs3@npm:^0.10.4": + version: 0.10.4 + resolution: "babel-plugin-polyfill-corejs3@npm:0.10.4" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.6.1 + core-js-compat: ^3.36.1 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: b96a54495f7cc8b3797251c8c15f5ed015edddc3110fc122f6b32c94bec33af1e8bc56fa99091808f500bde0cccaaa266889cdc5935d9e6e9cf09898214f02dd + languageName: node + linkType: hard + "babel-plugin-polyfill-corejs3@npm:^0.4.0": version: 0.4.0 resolution: "babel-plugin-polyfill-corejs3@npm:0.4.0" @@ -14170,18 +15099,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.6.0": - version: 0.6.0 - resolution: "babel-plugin-polyfill-corejs3@npm:0.6.0" - dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.3 - core-js-compat: ^3.25.1 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 470bb8c59f7c0912bd77fe1b5a2e72f349b3f65bbdee1d60d6eb7e1f4a085c6f24b2dd5ab4ac6c2df6444a96b070ef6790eccc9edb6a2668c60d33133bfb62c6 - languageName: node - linkType: hard - "babel-plugin-polyfill-corejs3@npm:^0.8.5": version: 0.8.6 resolution: "babel-plugin-polyfill-corejs3@npm:0.8.6" @@ -14205,17 +15122,6 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.4.1": - version: 0.4.1 - resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" - dependencies: - "@babel/helper-define-polyfill-provider": ^0.3.3 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: ab0355efbad17d29492503230387679dfb780b63b25408990d2e4cf421012dae61d6199ddc309f4d2409ce4e9d3002d187702700dd8f4f8770ebbba651ed066c - languageName: node - linkType: hard - "babel-plugin-polyfill-regenerator@npm:^0.5.3": version: 0.5.3 resolution: "babel-plugin-polyfill-regenerator@npm:0.5.3" @@ -14227,6 +15133,24 @@ __metadata: languageName: node linkType: hard +"babel-plugin-polyfill-regenerator@npm:^0.6.1": + version: 0.6.2 + resolution: "babel-plugin-polyfill-regenerator@npm:0.6.2" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.6.2 + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 150233571072b6b3dfe946242da39cba8587b7f908d1c006f7545fc88b0e3c3018d445739beb61e7a75835f0c2751dbe884a94ff9b245ec42369d9267e0e1b3f + languageName: node + linkType: hard + +"babel-plugin-react-native-web@npm:0.19.12": + version: 0.19.12 + resolution: "babel-plugin-react-native-web@npm:0.19.12" + checksum: bf5378f9ed3477f0165e989cc389da60681032680c5b8147f88905c65bba5267bb296943f87d4885c22a3fcdebfa815f7e7c25ae8f8192c4579f291994a1d946 + languageName: node + linkType: hard + "babel-plugin-styled-components@npm:>= 1.12.0": version: 2.0.7 resolution: "babel-plugin-styled-components@npm:2.0.7" @@ -14626,6 +15550,16 @@ __metadata: languageName: node linkType: hard +"bonjour-service@npm:^1.2.1": + version: 1.2.1 + resolution: "bonjour-service@npm:1.2.1" + dependencies: + fast-deep-equal: ^3.1.3 + multicast-dns: ^7.2.5 + checksum: b65b3e6e3a07e97f2da5806afb76f3946d5a6426b72e849a0236dc3c9d3612fb8c5359ebade4be7eb63f74a37670c53a53be2ff17f4f709811fda77f600eb25b + languageName: node + linkType: hard + "boolbase@npm:^1.0.0": version: 1.0.0 resolution: "boolbase@npm:1.0.0" @@ -14991,17 +15925,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.5": - version: 4.21.7 - resolution: "browserslist@npm:4.21.7" +"browserslist@npm:^4.22.2, browserslist@npm:^4.23.0": + version: 4.23.1 + resolution: "browserslist@npm:4.23.1" dependencies: - caniuse-lite: ^1.0.30001489 - electron-to-chromium: ^1.4.411 - node-releases: ^2.0.12 - update-browserslist-db: ^1.0.11 + caniuse-lite: ^1.0.30001629 + electron-to-chromium: ^1.4.796 + node-releases: ^2.0.14 + update-browserslist-db: ^1.0.16 bin: browserslist: cli.js - checksum: 3d0d025e6d381c4db5e71b538258952660ba574c060832095f182a9877ca798836fa550736269e669a2080e486f0cfdf5d3bcf2769b9f7cf123f6c6b8c005f8f + checksum: 06189e2d6666a203ce097cc0e713a40477d08420927b79af139211e5712f3cf676fdc4dd6af3aa493d47c09206a344b3420a8315577dbe88c58903132de9b0f5 languageName: node linkType: hard @@ -15183,6 +16117,15 @@ __metadata: languageName: node linkType: hard +"bundle-name@npm:^4.1.0": + version: 4.1.0 + resolution: "bundle-name@npm:4.1.0" + dependencies: + run-applescript: ^7.0.0 + checksum: 1d966c8d2dbf4d9d394e53b724ac756c2414c45c01340b37743621f59cc565a435024b394ddcb62b9b335d1c9a31f4640eb648c3fec7f97ee74dc0694c9beb6c + languageName: node + linkType: hard + "byline@npm:^5.0.0": version: 5.0.0 resolution: "byline@npm:5.0.0" @@ -15597,13 +16540,6 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001489": - version: 1.0.30001497 - resolution: "caniuse-lite@npm:1.0.30001497" - checksum: 6721120f9a588c442a81cf32f911b4e97a88cb129c27bd2cb0fce6447ad058baa12affa1ee09c517f9e088c7ce74964154d032b6631f66d75dd37c6bc59a67f6 - languageName: node - linkType: hard - "caniuse-lite@npm:^1.0.30001503": version: 1.0.30001509 resolution: "caniuse-lite@npm:1.0.30001509" @@ -15611,6 +16547,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001629": + version: 1.0.30001636 + resolution: "caniuse-lite@npm:1.0.30001636" + checksum: b0347fd2c8d346680a64d98b061c59cb8fbf149cdd03005a447fae4d21e6286d5bd161b43eefe3221c6624aacb3cda4e838ae83c95ff5313a547f84ca93bcc70 + languageName: node + linkType: hard + "canvas@npm:2.11.2, canvas@npm:^2.11.2": version: 2.11.2 resolution: "canvas@npm:2.11.2" @@ -15849,7 +16792,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.6.0": +"chokidar@npm:3.6.0, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -16570,6 +17513,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^10.0.1": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 436901d64a818295803c1996cd856621a74f30b9f9e28a588e726b2b1670665bccd7c1a77007ebf328729f0139838a88a19265858a0fa7a8728c4656796db948 + languageName: node + linkType: hard + "commander@npm:^2.16.0, commander@npm:^2.19.0, commander@npm:^2.20.0, commander@npm:^2.20.3, commander@npm:^2.8.1, commander@npm:^2.9.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -17104,15 +18054,6 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.25.1": - version: 3.30.2 - resolution: "core-js-compat@npm:3.30.2" - dependencies: - browserslist: ^4.21.5 - checksum: 4c81d635559eebc2f81db60f5095a235f580a2f90698113c4124c72761393592b139e30974cce6095a9a6aad6bb3cd467b24b20c32e77ed24ca74eb5944d0638 - languageName: node - linkType: hard - "core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.33.1": version: 3.33.1 resolution: "core-js-compat@npm:3.33.1" @@ -17122,6 +18063,15 @@ __metadata: languageName: node linkType: hard +"core-js-compat@npm:^3.36.1": + version: 3.37.1 + resolution: "core-js-compat@npm:3.37.1" + dependencies: + browserslist: ^4.23.0 + checksum: 5e7430329358bced08c30950512d2081aea0a5652b4c5892cbb3c4a6db05b0d3893a191a955162a07fdb5f4fe74e61b6429fdb503f54e062336d76e43c9555d9 + languageName: node + linkType: hard + "core-js-pure@npm:^3.30.2": version: 3.33.1 resolution: "core-js-pure@npm:3.33.1" @@ -17534,6 +18484,15 @@ __metadata: languageName: node linkType: hard +"css-in-js-utils@npm:^3.1.0": + version: 3.1.0 + resolution: "css-in-js-utils@npm:3.1.0" + dependencies: + hyphenate-style-name: ^1.0.3 + checksum: 066318e918c04a5e5bce46b38fe81052ea6ac051bcc6d3c369a1d59ceb1546cb2b6086901ab5d22be084122ee3732169996a3dfb04d3406eaee205af77aec61b + languageName: node + linkType: hard + "css-loader@npm:6.10.0": version: 6.10.0 resolution: "css-loader@npm:6.10.0" @@ -18680,6 +19639,13 @@ __metadata: languageName: node linkType: hard +"default-browser-id@npm:^5.0.0": + version: 5.0.0 + resolution: "default-browser-id@npm:5.0.0" + checksum: 185bfaecec2c75fa423544af722a3469b20704c8d1942794a86e4364fe7d9e8e9f63241a5b769d61c8151993bc65833a5b959026fa1ccea343b3db0a33aa6deb + languageName: node + linkType: hard + "default-browser@npm:^4.0.0": version: 4.0.0 resolution: "default-browser@npm:4.0.0" @@ -18692,6 +19658,16 @@ __metadata: languageName: node linkType: hard +"default-browser@npm:^5.2.1": + version: 5.2.1 + resolution: "default-browser@npm:5.2.1" + dependencies: + bundle-name: ^4.1.0 + default-browser-id: ^5.0.0 + checksum: afab7eff7b7f5f7a94d9114d1ec67273d3fbc539edf8c0f80019879d53aa71e867303c6f6d7cffeb10a6f3cfb59d4f963dba3f9c96830b4540cc7339a1bf9840 + languageName: node + linkType: hard + "default-compare@npm:^1.0.0": version: 1.0.0 resolution: "default-compare@npm:1.0.0" @@ -19837,13 +20813,6 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.411": - version: 1.4.425 - resolution: "electron-to-chromium@npm:1.4.425" - checksum: 1ec2e80601eb49982c51f562f74dc9fa1a80f3006c7d508f3bc37d2d12c726df99ff60d7f013e38c374ae81414e0b76d5e7a97f406cdea8b5e8e3dfb51c23f72 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.4.431": version: 1.4.446 resolution: "electron-to-chromium@npm:1.4.446" @@ -19858,6 +20827,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.4.796": + version: 1.4.808 + resolution: "electron-to-chromium@npm:1.4.808" + checksum: 2da5e1644b4172762aa75760d8413b22389763e4730b1e6367e5a26c5d573c1ab6c99da12a4d29f4287a4d0a4345dd26ebc8b089a04acf06543fda1f3ee0df24 + languageName: node + linkType: hard + "electron-window-state@npm:5.0.3": version: 5.0.3 resolution: "electron-window-state@npm:5.0.3" @@ -20021,6 +20997,16 @@ __metadata: languageName: node linkType: hard +"enhanced-resolve@npm:^5.14.1": + version: 5.17.0 + resolution: "enhanced-resolve@npm:5.17.0" + dependencies: + graceful-fs: ^4.2.4 + tapable: ^2.2.0 + checksum: 1066000454da6a7aeabdbe1f433d912d1e39e6892142a78a37b6577aab27e0436091fa1399d857ad87085b1c3b73a0f811c8874da3dbdc40fbd5ebe89a5568e6 + languageName: node + linkType: hard + "enhanced-resolve@npm:^5.15.0": version: 5.15.0 resolution: "enhanced-resolve@npm:5.15.0" @@ -20438,6 +21424,13 @@ __metadata: languageName: node linkType: hard +"escalade@npm:^3.1.2": + version: 3.1.2 + resolution: "escalade@npm:3.1.2" + checksum: 1ec0977aa2772075493002bdbd549d595ff6e9393b1cb0d7d6fcaf78c750da0c158f180938365486f75cb69fba20294351caddfce1b46552a7b6c3cde52eaa02 + languageName: node + linkType: hard + "escape-goat@npm:^2.0.0": version: 2.1.1 resolution: "escape-goat@npm:2.1.1" @@ -21830,7 +22823,7 @@ __metadata: languageName: node linkType: hard -"fbjs@npm:^3.0.0, fbjs@npm:^3.0.1": +"fbjs@npm:^3.0.0, fbjs@npm:^3.0.1, fbjs@npm:^3.0.4": version: 3.0.5 resolution: "fbjs@npm:3.0.5" dependencies: @@ -21852,21 +22845,6 @@ __metadata: languageName: node linkType: hard -"fbjs@npm:^3.0.4": - version: 3.0.5 - resolution: "fbjs@npm:3.0.5" - dependencies: - cross-fetch: ^3.1.5 - fbjs-css-vars: ^1.0.0 - loose-envify: ^1.0.0 - object-assign: ^4.1.0 - promise: ^7.1.1 - setimmediate: ^1.0.5 - ua-parser-js: ^1.0.35 - checksum: e609b5b64686bc96495a5c67728ed9b2710b9b3d695c5759c5f5e47c9483d1c323543ac777a86459e3694efc5712c6ce7212e944feb19752867d699568bb0e54 - languageName: node - linkType: hard - "fd-slicer@npm:~1.1.0": version: 1.1.0 resolution: "fd-slicer@npm:1.1.0" @@ -23360,6 +24338,22 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.3.7": + version: 10.4.2 + resolution: "glob@npm:10.4.2" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^3.1.2 + minimatch: ^9.0.4 + minipass: ^7.1.2 + package-json-from-dist: ^1.0.0 + path-scurry: ^1.11.1 + bin: + glob: dist/esm/bin.mjs + checksum: bd7c0e30701136e936f414e5f6f82c7f04503f01df77408f177aa584927412f0bde0338e6ec541618cd21eacc57dde33e7b3c6c0a779cc1c6e6a0e14f3d15d9b + languageName: node + linkType: hard + "glob@npm:^7.2.3": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -24358,6 +25352,13 @@ __metadata: languageName: node linkType: hard +"html-entities@npm:^2.4.0": + version: 2.5.2 + resolution: "html-entities@npm:2.5.2" + checksum: b23f4a07d33d49ade1994069af4e13d31650e3fb62621e92ae10ecdf01d1a98065c78fd20fdc92b4c7881612210b37c275f2c9fba9777650ab0d6f2ceb3b99b6 + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -24812,6 +25813,20 @@ __metadata: languageName: node linkType: hard +"hyperdyperid@npm:^1.2.0": + version: 1.2.0 + resolution: "hyperdyperid@npm:1.2.0" + checksum: 210029d1c86926f09109f6317d143f8b056fc38e8dd11b0c3e3205fc6c6ff8429fb55b4b9c2bce065462719ed9d34366eced387aaa0035d93eb76b306a8547ef + languageName: node + linkType: hard + +"hyphenate-style-name@npm:^1.0.3": + version: 1.1.0 + resolution: "hyphenate-style-name@npm:1.1.0" + checksum: b9ed74e29181d96bd58a2d0e62fc4a19879db591dba268275829ff0ae595fcdf11faafaeaa63330a45c3004664d7db1f0fc7cdb372af8ee4615ed8260302c207 + languageName: node + linkType: hard + "iconv-corefoundation@npm:^1.1.7": version: 1.1.7 resolution: "iconv-corefoundation@npm:1.1.7" @@ -25253,6 +26268,16 @@ __metadata: languageName: node linkType: hard +"inline-style-prefixer@npm:^6.0.1": + version: 6.0.4 + resolution: "inline-style-prefixer@npm:6.0.4" + dependencies: + css-in-js-utils: ^3.1.0 + fast-loops: ^1.1.3 + checksum: caf7a75d18acbedc7e3b8bfac17563082becd2df6b65accad964a6afdf490329b42315c37fe65ba0177cc10fd32809eb40d62aba23a0118c74d87d4fc58defa2 + languageName: node + linkType: hard + "inquirer@npm:^6.2.0": version: 6.5.2 resolution: "inquirer@npm:6.5.2" @@ -25422,6 +26447,13 @@ __metadata: languageName: node linkType: hard +"interpret@npm:^3.1.1": + version: 3.1.1 + resolution: "interpret@npm:3.1.1" + checksum: 35cebcf48c7351130437596d9ab8c8fe131ce4038da4561e6d665f25640e0034702a031cf7e3a5cea60ac7ac548bf17465e0571ede126f3d3a6933152171ac82 + languageName: node + linkType: hard + "invariant@npm:2.2.4, invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" @@ -25487,6 +26519,13 @@ __metadata: languageName: node linkType: hard +"ipaddr.js@npm:^2.1.0": + version: 2.2.0 + resolution: "ipaddr.js@npm:2.2.0" + checksum: 770ba8451fd9bf78015e8edac0d5abd7a708cbf75f9429ca9147a9d2f3a2d60767cd5de2aab2b1e13ca6e4445bdeff42bf12ef6f151c07a5c6cf8a44328e2859 + languageName: node + linkType: hard + "irregular-plurals@npm:^3.2.0": version: 3.3.0 resolution: "irregular-plurals@npm:3.3.0" @@ -26000,6 +27039,13 @@ __metadata: languageName: node linkType: hard +"is-network-error@npm:^1.0.0": + version: 1.1.0 + resolution: "is-network-error@npm:1.1.0" + checksum: b2fe6aac07f814a9de275efd05934c832c129e7ba292d27614e9e8eec9e043b7a0bbeaeca5d0916b0f462edbec2aa2eaee974ee0a12ac095040e9515c222c251 + languageName: node + linkType: hard + "is-npm@npm:^5.0.0": version: 5.0.0 resolution: "is-npm@npm:5.0.0" @@ -26392,6 +27438,15 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^3.1.0": + version: 3.1.0 + resolution: "is-wsl@npm:3.1.0" + dependencies: + is-inside-container: ^1.0.0 + checksum: f9734c81f2f9cf9877c5db8356bfe1ff61680f1f4c1011e91278a9c0564b395ae796addb4bf33956871041476ec82c3e5260ed57b22ac91794d4ae70a1d2f0a9 + languageName: node + linkType: hard + "is-yarn-global@npm:^0.3.0": version: 0.3.0 resolution: "is-yarn-global@npm:0.3.0" @@ -26632,6 +27687,19 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^3.1.2": + version: 3.4.0 + resolution: "jackspeak@npm:3.4.0" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 350f6f311018bb175ffbe736b19c26ac0b134bb5a17a638169e89594eb0c24ab1c658ab3a2fda24ff63b3b19292e1a5ec19d2255bc526df704e8168d392bef85 + languageName: node + linkType: hard + "jake@npm:^10.8.5": version: 10.8.5 resolution: "jake@npm:10.8.5" @@ -27828,15 +28896,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.2.1": - version: 2.2.1 - resolution: "json5@npm:2.2.1" - bin: - json5: lib/cli.js - checksum: 74b8a23b102a6f2bf2d224797ae553a75488b5adbaee9c9b6e5ab8b510a2fc6e38f876d4c77dea672d4014a44b2399e15f2051ac2b37b87f74c0c7602003543b - languageName: node - linkType: hard - "jsonc-parser@npm:^3.2.0": version: 3.2.0 resolution: "jsonc-parser@npm:3.2.0" @@ -28251,6 +29310,16 @@ __metadata: languageName: node linkType: hard +"launch-editor@npm:^2.6.1": + version: 2.8.0 + resolution: "launch-editor@npm:2.8.0" + dependencies: + picocolors: ^1.0.0 + shell-quote: ^1.8.1 + checksum: 495009163fd4879fbc576323d1da3b821379ec66e9c20ed3297ea65b3eceb720fe9409cbd2819d6ff5dd0115325e6b6716d473dd729d5aa8ddd67810e3545477 + languageName: node + linkType: hard + "layout-base@npm:^1.0.0": version: 1.0.2 resolution: "layout-base@npm:1.0.2" @@ -28948,6 +30017,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.2.0": + version: 10.2.2 + resolution: "lru-cache@npm:10.2.2" + checksum: 98e8fc93691c546f719a76103ef2bee5a3ac823955c755a47641ec41f8c7fafa1baeaba466937cc1cbfa9cfd47e03536d10e2db3158a64ad91ff3a58a32c893e + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -29566,6 +30642,18 @@ __metadata: languageName: node linkType: hard +"memfs@npm:^4.6.0": + version: 4.9.3 + resolution: "memfs@npm:4.9.3" + dependencies: + "@jsonjoy.com/json-pack": ^1.0.3 + "@jsonjoy.com/util": ^1.1.2 + tree-dump: ^1.0.1 + tslib: ^2.0.0 + checksum: 65af465dd07d7859c2dd5a50d7d2cb3177d3e5b1d3be3c85361ef561a13728ae8404902ef14f0d5c8330c5b9730ce6b1723c375753b4cb2b9729762d8abb5550 + languageName: node + linkType: hard + "memoize-one@npm:^5.0.0": version: 5.2.1 resolution: "memoize-one@npm:5.2.1" @@ -30828,6 +31916,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.4": + version: 9.0.4 + resolution: "minimatch@npm:9.0.4" + dependencies: + brace-expansion: ^2.0.1 + checksum: cf717f597ec3eed7dabc33153482a2e8d49f4fd3c26e58fd9c71a94c5029a0838728841b93f46bf1263b65a8010e2ee800d0dc9b004ab8ba8b6d1ec07cc115b5 + languageName: node + linkType: hard + "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -31003,6 +32100,13 @@ __metadata: languageName: node linkType: hard +"minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 2bfd325b95c555f2b4d2814d49325691c7bee937d753814861b0b49d5edcda55cbbf22b6b6a60bb91eddac8668771f03c5ff647dcd9d0f798e9548b9cdc46ee3 + languageName: node + linkType: hard + "minizlib@npm:^1.3.3": version: 1.3.3 resolution: "minizlib@npm:1.3.3" @@ -31811,6 +32915,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 + languageName: node + linkType: hard + "node-releases@npm:^2.0.6": version: 2.0.6 resolution: "node-releases@npm:2.0.6" @@ -32601,7 +33712,7 @@ __metadata: languageName: node linkType: hard -"on-finished@npm:2.4.1": +"on-finished@npm:2.4.1, on-finished@npm:^2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" dependencies: @@ -32680,6 +33791,18 @@ __metadata: languageName: node linkType: hard +"open@npm:^10.0.3": + version: 10.1.0 + resolution: "open@npm:10.1.0" + dependencies: + default-browser: ^5.2.1 + define-lazy-prop: ^3.0.0 + is-inside-container: ^1.0.0 + is-wsl: ^3.1.0 + checksum: 079b0771616bac13b08129b0300032dc9328d72f345e460dd0416b8a8196a5bdf5e0251fefec8aa2a6a97c736734ac65dd8f1d29ab3fc9a13e85624aa5bc4470 + languageName: node + linkType: hard + "open@npm:^6.2.0": version: 6.4.0 resolution: "open@npm:6.4.0" @@ -33035,6 +34158,17 @@ __metadata: languageName: node linkType: hard +"p-retry@npm:^6.2.0": + version: 6.2.0 + resolution: "p-retry@npm:6.2.0" + dependencies: + "@types/retry": 0.12.2 + is-network-error: ^1.0.0 + retry: ^0.13.1 + checksum: 6003573c559ee812329c9c3ede7ba12a783fdc8dd70602116646e850c920b4597dc502fe001c3f9526fca4e93275045db7a27341c458e51db179c1374a01ac44 + languageName: node + linkType: hard + "p-try@npm:^1.0.0": version: 1.0.0 resolution: "p-try@npm:1.0.0" @@ -33097,6 +34231,13 @@ __metadata: languageName: node linkType: hard +"package-json-from-dist@npm:^1.0.0": + version: 1.0.0 + resolution: "package-json-from-dist@npm:1.0.0" + checksum: ac706ec856a5a03f5261e4e48fa974f24feb044d51f84f8332e2af0af04fbdbdd5bbbfb9cbbe354190409bc8307c83a9e38c6672c3c8855f709afb0006a009ea + languageName: node + linkType: hard + "package-json@npm:^6.3.0": version: 6.5.0 resolution: "package-json@npm:6.5.0" @@ -33571,6 +34712,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: ^10.2.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + checksum: 890d5abcd593a7912dcce7cf7c6bf7a0b5648e3dee6caf0712c126ca0a65c7f3d7b9d769072a4d1baf370f61ce493ab5b038d59988688e0c5f3f646ee3c69023 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -33796,6 +34947,13 @@ __metadata: languageName: node linkType: hard +"picocolors@npm:^1.0.1": + version: 1.0.1 + resolution: "picocolors@npm:1.0.1" + checksum: fa68166d1f56009fc02a34cdfd112b0dd3cf1ef57667ac57281f714065558c01828cdf4f18600ad6851cbe0093952ed0660b1e0156bddf2184b6aaf5817553a5 + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3": version: 2.3.0 resolution: "picomatch@npm:2.3.0" @@ -35496,18 +36654,6 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.5.1": - version: 2.5.1 - resolution: "raw-body@npm:2.5.1" - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - checksum: 5362adff1575d691bb3f75998803a0ffed8c64eabeaa06e54b4ada25a0cd1b2ae7f4f5ec46565d1bec337e08b5ac90c76eaa0758de6f72a633f025d754dec29e - languageName: node - linkType: hard - "raw-body@npm:2.5.2": version: 2.5.2 resolution: "raw-body@npm:2.5.2" @@ -35639,7 +36785,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:18.2.0, react-dom@npm:^18.2.0": +"react-dom@npm:18.2.0": version: 18.2.0 resolution: "react-dom@npm:18.2.0" dependencies: @@ -35651,6 +36797,18 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:18.3.1": + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" + dependencies: + loose-envify: ^1.1.0 + scheduler: ^0.23.2 + peerDependencies: + react: ^18.3.1 + checksum: 298954ecd8f78288dcaece05e88b570014d8f6dce5db6f66e6ee91448debeb59dcd31561dddb354eee47e6c1bb234669459060deb238ed0213497146e555a0b9 + languageName: node + linkType: hard + "react-dom@npm:^17.0.2": version: 17.0.2 resolution: "react-dom@npm:17.0.2" @@ -36080,6 +37238,25 @@ __metadata: languageName: node linkType: hard +"react-native-web@npm:0.19.12": + version: 0.19.12 + resolution: "react-native-web@npm:0.19.12" + dependencies: + "@babel/runtime": ^7.18.6 + "@react-native/normalize-colors": ^0.74.1 + fbjs: ^3.0.4 + inline-style-prefixer: ^6.0.1 + memoize-one: ^6.0.0 + nullthrows: ^1.1.1 + postcss-value-parser: ^4.2.0 + styleq: ^0.1.3 + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 676b1ba510c92e01dc69cb3102080f83976d2d209647323fb0a3a14113a455a6a506cf78d3e392c3fa33015135b61e2d6a3eed837a6876665064a6eb87516781 + languageName: node + linkType: hard + "react-native-webview@npm:13.8.1": version: 13.8.1 resolution: "react-native-webview@npm:13.8.1" @@ -36298,6 +37475,25 @@ __metadata: languageName: node linkType: hard +"react-redux@npm:9.1.2": + version: 9.1.2 + resolution: "react-redux@npm:9.1.2" + dependencies: + "@types/use-sync-external-store": ^0.0.3 + use-sync-external-store: ^1.0.0 + peerDependencies: + "@types/react": ^18.2.25 + react: ^18.0 + redux: ^5.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + redux: + optional: true + checksum: 1ee9cf41f29f68267320b4fc3bcf6a76a3825c82441612582678ddd827a2b60834f687d2a8b755c905885dfce476a1eb41af42b36f4dd71f8ee9991296a1e515 + languageName: node + linkType: hard + "react-refresh@npm:^0.14.0": version: 0.14.0 resolution: "react-refresh@npm:0.14.0" @@ -36852,6 +38048,13 @@ __metadata: languageName: node linkType: hard +"redux@npm:5.0.1": + version: 5.0.1 + resolution: "redux@npm:5.0.1" + checksum: e74affa9009dd5d994878b9a1ce30d6569d986117175056edb003de2651c05b10fe7819d6fa94aea1a94de9a82f252f986547f007a2fbeb35c317a2e5f5ecf2c + languageName: node + linkType: hard + "redux@npm:^4.0.0": version: 4.1.2 resolution: "redux@npm:4.1.2" @@ -36891,13 +38094,6 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.13.10": - version: 0.13.10 - resolution: "regenerator-runtime@npm:0.13.10" - checksum: 09893f5a9e82932642d9a999716b6c626dc53ef2a01307c952ebbf8e011802360163a37c304c18a6c358548be5a72b448e37209954a18696f21e438c81cbb4b9 - languageName: node - linkType: hard - "regenerator-runtime@npm:^0.13.11, regenerator-runtime@npm:^0.13.3": version: 0.13.11 resolution: "regenerator-runtime@npm:0.13.11" @@ -36919,15 +38115,6 @@ __metadata: languageName: node linkType: hard -"regenerator-transform@npm:^0.15.1": - version: 0.15.1 - resolution: "regenerator-transform@npm:0.15.1" - dependencies: - "@babel/runtime": ^7.8.4 - checksum: 2d15bdeadbbfb1d12c93f5775493d85874dbe1d405bec323da5c61ec6e701bc9eea36167483e1a5e752de9b2df59ab9a2dfff6bf3784f2b28af2279a673d29a4 - languageName: node - linkType: hard - "regenerator-transform@npm:^0.15.2": version: 0.15.2 resolution: "regenerator-transform@npm:0.15.2" @@ -37663,6 +38850,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^5.0.5": + version: 5.0.7 + resolution: "rimraf@npm:5.0.7" + dependencies: + glob: ^10.3.7 + bin: + rimraf: dist/esm/bin.mjs + checksum: 884852abf8aefd4667448d87bdab04120a8641266c828cf382ac811713547eda18f81799d2146ffec3178f357d83d44ec01c10095949c82e23551660732bf14f + languageName: node + linkType: hard + "rimraf@npm:~2.2.6": version: 2.2.8 resolution: "rimraf@npm:2.2.8" @@ -37871,6 +39069,13 @@ __metadata: languageName: node linkType: hard +"run-applescript@npm:^7.0.0": + version: 7.0.0 + resolution: "run-applescript@npm:7.0.0" + checksum: b02462454d8b182ad4117e5d4626e9e6782eb2072925c9fac582170b0627ae3c1ea92ee9b2df7daf84b5e9ffe14eb1cf5fb70bc44b15c8a0bfcdb47987e2410c + languageName: node + linkType: hard + "run-async@npm:^2.0.0, run-async@npm:^2.2.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -38101,6 +39306,15 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" + dependencies: + loose-envify: ^1.1.0 + checksum: 3e82d1f419e240ef6219d794ff29c7ee415fbdc19e038f680a10c067108e06284f1847450a210b29bbaf97b9d8a97ced5f624c31c681248ac84c80d56ad5a2c4 + languageName: node + linkType: hard + "schema-utils@npm:2.7.0": version: 2.7.0 resolution: "schema-utils@npm:2.7.0" @@ -38123,7 +39337,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.0.0, schema-utils@npm:^3.2.0": +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.2, schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" dependencies: @@ -38145,7 +39359,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^4.0.0": +"schema-utils@npm:^4.0.0, schema-utils@npm:^4.2.0": version: 4.2.0 resolution: "schema-utils@npm:4.2.0" dependencies: @@ -40358,6 +41572,13 @@ __metadata: languageName: node linkType: hard +"styleq@npm:^0.1.3": + version: 0.1.3 + resolution: "styleq@npm:0.1.3" + checksum: 14a8d23abd914166a9b4bd04ed753bd91363f0e029ee4a94ec2c7dc37d3213fe01fceee22dc655288da3ae89f5dc01cec42d5e2b58478b0dea33bf5bdf509be1 + languageName: node + linkType: hard + "stylis@npm:4.1.3": version: 4.1.3 resolution: "stylis@npm:4.1.3" @@ -41133,6 +42354,15 @@ __metadata: languageName: node linkType: hard +"thingies@npm:^1.20.0": + version: 1.21.0 + resolution: "thingies@npm:1.21.0" + peerDependencies: + tslib: ^2 + checksum: 283a2785e513dc892822dd0bbadaa79e873a7fc90b84798164717bf7cf837553e0b4518d8027b2307d8f6fc6caab088fa717112cd9196c6222763cc3cc1b7e79 + languageName: node + linkType: hard + "throat@npm:^5.0.0": version: 5.0.0 resolution: "throat@npm:5.0.0" @@ -41526,6 +42756,15 @@ __metadata: languageName: node linkType: hard +"tree-dump@npm:^1.0.1": + version: 1.0.1 + resolution: "tree-dump@npm:1.0.1" + peerDependencies: + tslib: 2 + checksum: 256f2e066ab8743672795822731410d9b9036ef449499f528df1a638ad99af45f345bfbddeaf1cc46b7b9279db3b5f83e1a4cb21bc086ef25ce6add975a3c490 + languageName: node + linkType: hard + "tree-kit@npm:^0.7.0": version: 0.7.4 resolution: "tree-kit@npm:0.7.4" @@ -42239,13 +43478,6 @@ __metadata: languageName: node linkType: hard -"ua-parser-js@npm:^1.0.35": - version: 1.0.36 - resolution: "ua-parser-js@npm:1.0.36" - checksum: 5b2c8a5e3443dfbba7624421805de946457c26ae167cb2275781a2729d1518f7067c9d5c74c3b0acac4b9ff3278cae4eace08ca6eecb63848bc3b2f6a63cc975 - languageName: node - linkType: hard - "ua-parser-js@npm:^1.0.35": version: 1.0.35 resolution: "ua-parser-js@npm:1.0.35" @@ -42799,6 +44031,20 @@ __metadata: languageName: node linkType: hard +"update-browserslist-db@npm:^1.0.16": + version: 1.0.16 + resolution: "update-browserslist-db@npm:1.0.16" + dependencies: + escalade: ^3.1.2 + picocolors: ^1.0.1 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 51b1f7189c9ea5925c80154b0a6fd3ec36106d07858d8f69826427d8edb4735d1801512c69eade38ba0814d7407d11f400d74440bbf3da0309f3d788017f35b2 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.9": version: 1.0.10 resolution: "update-browserslist-db@npm:1.0.10" @@ -42892,7 +44138,7 @@ __metadata: languageName: node linkType: hard -"url-loader@npm:^4.1.1": +"url-loader@npm:4.1.1, url-loader@npm:^4.1.1": version: 4.1.1 resolution: "url-loader@npm:4.1.1" dependencies: @@ -43645,6 +44891,38 @@ __metadata: languageName: node linkType: hard +"webpack-cli@npm:5.1.4": + version: 5.1.4 + resolution: "webpack-cli@npm:5.1.4" + dependencies: + "@discoveryjs/json-ext": ^0.5.0 + "@webpack-cli/configtest": ^2.1.1 + "@webpack-cli/info": ^2.0.2 + "@webpack-cli/serve": ^2.0.5 + colorette: ^2.0.14 + commander: ^10.0.1 + cross-spawn: ^7.0.3 + envinfo: ^7.7.3 + fastest-levenshtein: ^1.0.12 + import-local: ^3.0.2 + interpret: ^3.1.1 + rechoir: ^0.8.0 + webpack-merge: ^5.7.3 + peerDependencies: + webpack: 5.x.x + peerDependenciesMeta: + "@webpack-cli/generators": + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + bin: + webpack-cli: bin/cli.js + checksum: 3a4ad0d0342a6815c850ee4633cc2a8a5dae04f918e7847f180bf24ab400803cf8a8943707ffbed03eb20fe6ce647f996f60a2aade87b0b4a9954da3da172ce0 + languageName: node + linkType: hard + "webpack-dev-middleware@npm:^5.3.1": version: 5.3.3 resolution: "webpack-dev-middleware@npm:5.3.3" @@ -43660,6 +44938,72 @@ __metadata: languageName: node linkType: hard +"webpack-dev-middleware@npm:^7.1.0": + version: 7.2.1 + resolution: "webpack-dev-middleware@npm:7.2.1" + dependencies: + colorette: ^2.0.10 + memfs: ^4.6.0 + mime-types: ^2.1.31 + on-finished: ^2.4.1 + range-parser: ^1.2.1 + schema-utils: ^4.0.0 + peerDependencies: + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + checksum: bb8c75f7ceabc13ee2c3bc9648190e05a0a8c6d40b940ef72b09ea858a63d16bcb434b49995f1025125a1c3a1c8d40274beb5d26ef2fb1458b19e7f6fe3a91fe + languageName: node + linkType: hard + +"webpack-dev-server@npm:5.0.4": + version: 5.0.4 + resolution: "webpack-dev-server@npm:5.0.4" + dependencies: + "@types/bonjour": ^3.5.13 + "@types/connect-history-api-fallback": ^1.5.4 + "@types/express": ^4.17.21 + "@types/serve-index": ^1.9.4 + "@types/serve-static": ^1.15.5 + "@types/sockjs": ^0.3.36 + "@types/ws": ^8.5.10 + ansi-html-community: ^0.0.8 + bonjour-service: ^1.2.1 + chokidar: ^3.6.0 + colorette: ^2.0.10 + compression: ^1.7.4 + connect-history-api-fallback: ^2.0.0 + default-gateway: ^6.0.3 + express: ^4.17.3 + graceful-fs: ^4.2.6 + html-entities: ^2.4.0 + http-proxy-middleware: ^2.0.3 + ipaddr.js: ^2.1.0 + launch-editor: ^2.6.1 + open: ^10.0.3 + p-retry: ^6.2.0 + rimraf: ^5.0.5 + schema-utils: ^4.2.0 + selfsigned: ^2.4.1 + serve-index: ^1.9.1 + sockjs: ^0.3.24 + spdy: ^4.0.2 + webpack-dev-middleware: ^7.1.0 + ws: ^8.16.0 + peerDependencies: + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + bin: + webpack-dev-server: bin/webpack-dev-server.js + checksum: b3535d01e8d895f4ce6d74b5f76e29398b712476216cd6d459365e5cc2f2fb1e49240aef6c23b2b943b04dbf768d7d18301af3eb064038bde4e11d03c241202d + languageName: node + linkType: hard + "webpack-dev-server@npm:^4.9.3": version: 4.15.1 resolution: "webpack-dev-server@npm:4.15.1" @@ -43816,6 +45160,43 @@ __metadata: languageName: node linkType: hard +"webpack@npm:5.84.0": + version: 5.84.0 + resolution: "webpack@npm:5.84.0" + dependencies: + "@types/eslint-scope": ^3.7.3 + "@types/estree": ^1.0.0 + "@webassemblyjs/ast": ^1.11.5 + "@webassemblyjs/wasm-edit": ^1.11.5 + "@webassemblyjs/wasm-parser": ^1.11.5 + acorn: ^8.7.1 + acorn-import-assertions: ^1.9.0 + browserslist: ^4.14.5 + chrome-trace-event: ^1.0.2 + enhanced-resolve: ^5.14.1 + es-module-lexer: ^1.2.1 + eslint-scope: 5.1.1 + events: ^3.2.0 + glob-to-regexp: ^0.4.1 + graceful-fs: ^4.2.9 + json-parse-even-better-errors: ^2.3.1 + loader-runner: ^4.2.0 + mime-types: ^2.1.27 + neo-async: ^2.6.2 + schema-utils: ^3.1.2 + tapable: ^2.1.1 + terser-webpack-plugin: ^5.3.7 + watchpack: ^2.4.0 + webpack-sources: ^3.2.3 + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: 5837983d81d55edf621317f23de3a7ae49bbf24c9f8605beeba35ea62be5cc4b3b63ace0ec9785912b9ffe70fd969e613597fae21b2334a3913054c41189ef75 + languageName: node + linkType: hard + "webpack@npm:^5.73.0": version: 5.89.0 resolution: "webpack@npm:5.89.0" @@ -44403,7 +45784,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.14.2": +"ws@npm:^8.14.2, ws@npm:^8.16.0": version: 8.17.1 resolution: "ws@npm:8.17.1" peerDependencies: From 85983db6a1e67ff41565005bef445fe483a5dc4b Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 16:51:28 -0700 Subject: [PATCH 003/177] Working note viewer and editor --- .eslintignore | 3 +- .gitignore | 3 +- .../components/ExtendedWebView.web.tsx | 156 ++++++++++++++++++ .../NoteBodyViewer/hooks/useEditPopup.ts | 3 + .../NoteEditor/MarkdownToolbar/Toolbar.tsx | 19 ++- .../MarkdownToolbar/ToolbarOverflowRows.tsx | 6 +- .../ConfigScreen/configScreenStyles.ts | 1 + .../app-mobile/components/screens/Note.tsx | 1 + .../components/side-menu-content.tsx | 6 +- packages/app-mobile/index.web.js | 14 +- .../plugins/PlatformImplementation.ts | 3 +- .../backgroundPage/startStopPlugin.ts | 2 +- packages/app-mobile/root.tsx | 13 +- .../utils/database-driver-react-native.web.ts | 6 - .../utils/fs-driver/fs-driver-rn.web.ts | 18 +- .../utils/shim-init-react/index.web.ts | 3 +- packages/app-mobile/web/index.html | 30 ++++ .../utils/dom}/makeSandboxedIframe.ts | 2 +- 18 files changed, 246 insertions(+), 43 deletions(-) create mode 100644 packages/app-mobile/components/ExtendedWebView.web.tsx rename packages/{app-mobile/plugins/PluginRunner/backgroundPage/utils => lib/utils/dom}/makeSandboxedIframe.ts (96%) diff --git a/.eslintignore b/.eslintignore index 5eb5b7edff5..685ce82a699 100644 --- a/.eslintignore +++ b/.eslintignore @@ -521,6 +521,7 @@ packages/app-mobile/components/DismissibleDialog.js packages/app-mobile/components/Dropdown.test.js packages/app-mobile/components/Dropdown.js packages/app-mobile/components/ExtendedWebView.js +packages/app-mobile/components/ExtendedWebView.web.js packages/app-mobile/components/FolderPicker.js packages/app-mobile/components/Icon.js packages/app-mobile/components/IconButton.js @@ -669,7 +670,6 @@ packages/app-mobile/plugins/PluginRunner/backgroundPage/pluginRunnerBackgroundPa packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.test.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.js -packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/makeSandboxedIframe.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/reportUnhandledErrors.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/wrapConsoleLog.js packages/app-mobile/plugins/PluginRunner/dialogs/PluginDialogManager.js @@ -1261,6 +1261,7 @@ packages/lib/urlUtils.js packages/lib/utils/ActionLogger.test.js packages/lib/utils/ActionLogger.js packages/lib/utils/credentialFiles.js +packages/lib/utils/dom/makeSandboxedIframe.js packages/lib/utils/focusHandler.js packages/lib/utils/frontMatter.js packages/lib/utils/ipc/RemoteMessenger.test.js diff --git a/.gitignore b/.gitignore index 4d9fa0713e5..ffa6ae09b2d 100644 --- a/.gitignore +++ b/.gitignore @@ -500,6 +500,7 @@ packages/app-mobile/components/DismissibleDialog.js packages/app-mobile/components/Dropdown.test.js packages/app-mobile/components/Dropdown.js packages/app-mobile/components/ExtendedWebView.js +packages/app-mobile/components/ExtendedWebView.web.js packages/app-mobile/components/FolderPicker.js packages/app-mobile/components/Icon.js packages/app-mobile/components/IconButton.js @@ -648,7 +649,6 @@ packages/app-mobile/plugins/PluginRunner/backgroundPage/pluginRunnerBackgroundPa packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.test.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/getFormData.js -packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/makeSandboxedIframe.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/reportUnhandledErrors.js packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/wrapConsoleLog.js packages/app-mobile/plugins/PluginRunner/dialogs/PluginDialogManager.js @@ -1240,6 +1240,7 @@ packages/lib/urlUtils.js packages/lib/utils/ActionLogger.test.js packages/lib/utils/ActionLogger.js packages/lib/utils/credentialFiles.js +packages/lib/utils/dom/makeSandboxedIframe.js packages/lib/utils/focusHandler.js packages/lib/utils/frontMatter.js packages/lib/utils/ipc/RemoteMessenger.test.js diff --git a/packages/app-mobile/components/ExtendedWebView.web.tsx b/packages/app-mobile/components/ExtendedWebView.web.tsx new file mode 100644 index 00000000000..a526c4fbc91 --- /dev/null +++ b/packages/app-mobile/components/ExtendedWebView.web.tsx @@ -0,0 +1,156 @@ +// Wraps react-native-webview. Allows loading HTML directly. + +import * as React from 'react'; + +import { + forwardRef, Ref, useEffect, useImperativeHandle, useRef, useState, +} from 'react'; +import { WebViewErrorEvent } from 'react-native-webview/lib/WebViewTypes'; + +import { StyleProp, View, ViewStyle } from 'react-native'; +import makeSandboxedIframe from '@joplin/lib/utils/dom/makeSandboxedIframe'; + +export interface WebViewControl { + // Evaluate the given [script] in the context of the page. + // Unlike react-native-webview/WebView, this does not need to return true. + injectJS(script: string): void; + + // message must be convertible to JSON + postMessage(message: unknown): void; +} + +export type OnMessageCallback = (event: { nativeEvent: { data: any } })=> void; +export type OnErrorCallback = (event: WebViewErrorEvent)=> void; +export type OnLoadCallback = ()=> void; + +interface Props { + // A name to be associated with the WebView (e.g. NoteEditor) + // This name should be unique. + webviewInstanceId: string; + + // If HTML is still being loaded, [html] should be an empty string. + html: string; + + // Initial javascript. Must evaluate to true. + injectedJavaScript: string; + + style?: StyleProp; + onMessage: OnMessageCallback; + onError?: OnErrorCallback; + onLoadStart?: OnLoadCallback; + onLoadEnd?: OnLoadCallback; + + // Defaults to the resource directory + baseDirectory?: string; +} + +const iframeContainerStyles = { height: '100%', width: '100%' }; + +const ExtendedWebView = (props: Props, ref: Ref) => { + const iframeRef = useRef(null); + + useImperativeHandle(ref, (): WebViewControl => { + return { + injectJS(js: string) { + if (!iframeRef.current) { + console.error(`ExtendedWebView(${props.webviewInstanceId}): WebView not loaded?`); + console.error('tried', js) + return; + } + + iframeRef.current.contentWindow.postMessage({ + injectJs: js, + }, '*'); + }, + postMessage(message: unknown) { + iframeRef.current.contentWindow.postMessage({ + postMessage: message, + }, '*'); + }, + }; + }, [props.webviewInstanceId]); + + const [containerRef, setContainerRef] = useState(); + + const onMessageRef = useRef(props.onMessage); + onMessageRef.current = props.onMessage; + const onLoadEndRef = useRef(props.onLoadEnd); + onLoadEndRef.current = props.onLoadEnd; + const onLoadStartRef = useRef(props.onLoadStart); + onLoadStartRef.current = props.onLoadStart; + + useEffect(() => { + if (!containerRef) return () => {}; + + const { iframe } = makeSandboxedIframe(props.html, [ + ` + window.addEventListener('message', (event) => { + console.log('got message', event); + if (event.source !== parent || event.origin === 'react-native') { + console.log('Ignoring message: wrong source'); + return; + } + + if (event.data.postMessage) { + window.dispatchEvent( + new MessageEvent( + 'message', + { + data: event.data.postMessage, + origin: 'react-native' + }, + ), + ); + } else if (event.data.injectJs) { + eval('(() => { ' + event.data.injectJs + ' })()'); + } + }); + + window.ReactNativeWebView = { + postMessage: (message) => { + parent.postMessage(message, '*'); + }, + }; + `, + props.injectedJavaScript, + ]); + containerRef.replaceChildren(iframe); + iframeRef.current = iframe; + + iframe.style.height = '100%'; + iframe.style.width = '100%'; + iframe.style.border = 'none'; + + const messageListener = (event: MessageEvent) => { + if (event.source !== iframe.contentWindow) { + return; + } + + onMessageRef.current?.({ nativeEvent: { data: event.data } }); + }; + window.addEventListener('message', messageListener); + if (!iframe.loading) { + onLoadStartRef.current?.(); + onLoadEndRef.current?.(); + } else { + iframe.onload = () => onLoadEndRef.current?.(); + iframe.onloadstart = () => onLoadStartRef.current?.(); + } + + return () => { + window.removeEventListener('message', messageListener); + }; + }, [containerRef]); + + return ( + +
+
+ ); +}; + +export default forwardRef(ExtendedWebView); diff --git a/packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.ts b/packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.ts index 80680bebb21..2b5e6f13ac2 100644 --- a/packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.ts +++ b/packages/app-mobile/components/NoteBodyViewer/hooks/useEditPopup.ts @@ -5,11 +5,14 @@ import { Theme } from '@joplin/lib/themes/type'; import { useMemo } from 'react'; import { extname } from 'path'; import shim from '@joplin/lib/shim'; +import { Platform } from 'react-native'; const Icon = require('react-native-vector-icons/Ionicons').default; export const editPopupClass = 'joplin-editPopup'; const getEditIconSrc = (theme: Theme) => { + if (Platform.OS === 'web') return ''; + const iconUri = Icon.getImageSourceSync('pencil', 20, theme.color2).uri; // Copy to a location that can be read within a WebView diff --git a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.tsx b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.tsx index ac6ed5215af..f84299c37d8 100644 --- a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.tsx +++ b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/Toolbar.tsx @@ -92,6 +92,16 @@ const Toolbar: React.FC = (props: ToolbarProps) => { ); + const overflow = ( + + + + ); + return ( = (props: ToolbarProps) => { }} onLayout={onContainerLayout} > - - - + { overflowButtonsVisible ? overflow : null } { !overflowButtonsVisible ? mainButtonRow : null } ); diff --git a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.tsx b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.tsx index aa7fd975034..5f3901d5c22 100644 --- a/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.tsx +++ b/packages/app-mobile/components/NoteEditor/MarkdownToolbar/ToolbarOverflowRows.tsx @@ -11,7 +11,6 @@ type OnToggleOverflowCallback = ()=> void; interface OverflowPopupProps { buttonGroups: ButtonGroup[]; styleSheet: StyleSheetData; - visible: boolean; // Should be created using useCallback onToggleOverflow: OnToggleOverflowCallback; @@ -117,16 +116,13 @@ const ToolbarOverflowRows: React.FC = (props: OverflowPopupP /> ); - if (!props.visible) { - return null; - } return ( diff --git a/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts b/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts index 8ebf4dfba58..168984bb960 100644 --- a/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts +++ b/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts @@ -184,6 +184,7 @@ const configScreenStyles = (themeId: number): ConfigScreenStyles => { ...settingControlStyle, color: undefined, flex: 0, + flexBasis: 'auto', }, diff --git a/packages/app-mobile/components/screens/Note.tsx b/packages/app-mobile/components/screens/Note.tsx index 76a8a4a9afe..c35597420c3 100644 --- a/packages/app-mobile/components/screens/Note.tsx +++ b/packages/app-mobile/components/screens/Note.tsx @@ -460,6 +460,7 @@ class NoteScreenComponent extends BaseScreenComponent implements B styles.titleContainer = { flex: 0, flexDirection: 'row', + flexBasis: 'auto', paddingLeft: theme.marginLeft, paddingRight: theme.marginRight, borderBottomColor: theme.dividerColor, diff --git a/packages/app-mobile/components/side-menu-content.tsx b/packages/app-mobile/components/side-menu-content.tsx index b03c25d139e..a738fbac721 100644 --- a/packages/app-mobile/components/side-menu-content.tsx +++ b/packages/app-mobile/components/side-menu-content.tsx @@ -71,6 +71,7 @@ const SideMenuContentComponent = (props: Props) => { button: { flex: 1, flexDirection: 'row', + flexBasis: 'auto', height: 36, alignItems: 'center', paddingLeft: theme.marginLeft, @@ -400,6 +401,7 @@ const SideMenuContentComponent = (props: Props) => { const folderButtonStyle: any = { flex: 1, flexDirection: 'row', + flexBasis: 'auto', height: 36, alignItems: 'center', paddingRight: theme.marginRight, @@ -439,7 +441,7 @@ const SideMenuContentComponent = (props: Props) => { return ( { folder_press(folder); }} @@ -543,7 +545,7 @@ const SideMenuContentComponent = (props: Props) => { ); } - return {items}; + return {items}; }; let items = []; diff --git a/packages/app-mobile/index.web.js b/packages/app-mobile/index.web.js index 6da24e89b1b..46799f556be 100644 --- a/packages/app-mobile/index.web.js +++ b/packages/app-mobile/index.web.js @@ -1,7 +1,7 @@ - - import fontAwesomeFont from 'react-native-vector-icons/Fonts/FontAwesome.ttf'; +import fontAwesomeSolidFont from 'react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf'; import ioniconFont from 'react-native-vector-icons/Fonts/Ionicons.ttf'; +import materialCommunityIconsFont from 'react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf'; // See https://www.npmjs.com/package/react-native-vector-icons const setUpRnVectorIcons = () => { @@ -10,10 +10,18 @@ const setUpRnVectorIcons = () => { src: url(${fontAwesomeFont}); font-family: FontAwesome; } + @font-face { + src: url(${fontAwesomeSolidFont}); + font-family: FontAwesome5_Solid; + } @font-face { src: url(${ioniconFont}); font-family: Ionicons; } + @font-face { + src: url(${materialCommunityIconsFont}); + font-family: MaterialCommunityIcons; + } `; const style = document.createElement('style'); @@ -25,7 +33,7 @@ setUpRnVectorIcons(); import { AppRegistry } from 'react-native'; const Root = require('./root').default; -AppRegistry.registerComponent('Joplin', () => Root);//Root); +AppRegistry.registerComponent('Joplin', () => Root); addEventListener('DOMContentLoaded', () => { console.log('DOMContentLoaded ☺'); diff --git a/packages/app-mobile/plugins/PlatformImplementation.ts b/packages/app-mobile/plugins/PlatformImplementation.ts index c3344666ce3..c5805255ac7 100644 --- a/packages/app-mobile/plugins/PlatformImplementation.ts +++ b/packages/app-mobile/plugins/PlatformImplementation.ts @@ -4,7 +4,6 @@ import Setting from '@joplin/lib/models/Setting'; import { reg } from '@joplin/lib/registry'; import BasePlatformImplementation, { Joplin } from '@joplin/lib/services/plugins/BasePlatformImplementation'; import { CreateFromPdfOptions, Implementation as ImagingImplementation } from '@joplin/lib/services/plugins/api/JoplinImaging'; -import RNVersionInfo from 'react-native-version-info'; import { _ } from '@joplin/lib/locale'; import shim from '@joplin/lib/shim'; import Clipboard from '@react-native-clipboard/clipboard'; @@ -32,7 +31,7 @@ export default class PlatformImplementation extends BasePlatformImplementation { public get versionInfo(): VersionInfo { return { - version: RNVersionInfo.appVersion, + version: shim.appVersion(), syncVersion: Setting.value('syncVersion'), profileVersion: reg.db().version(), platform: 'mobile', diff --git a/packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.ts b/packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.ts index d7e8282ca55..af6554c5027 100644 --- a/packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.ts +++ b/packages/app-mobile/plugins/PluginRunner/backgroundPage/startStopPlugin.ts @@ -2,7 +2,7 @@ import RemoteMessenger from '@joplin/lib/utils/ipc/RemoteMessenger'; import { PluginMainProcessApi, PluginWebViewApi } from '../types'; import WebViewToRNMessenger from '../../../utils/ipc/WebViewToRNMessenger'; import WindowMessenger from '@joplin/lib/utils/ipc/WindowMessenger'; -import makeSandboxedIframe from './utils/makeSandboxedIframe'; +import makeSandboxedIframe from '@joplin/lib/utils/dom/makeSandboxedIframe'; type PluginRecord = { iframe: HTMLIFrameElement; diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index cb3a65b9381..f098f70ffc7 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -544,11 +544,16 @@ async function initialize(dispatch: Function) { reg.logger().info(`Starting application ${Setting.value('appId')} v${VersionInfo.appVersion} (${Setting.value('env')})`); const dbLogger = new Logger(); - dbLogger.addTarget(TargetType.Database, { database: logDatabase, source: 'm' }); - if (Setting.value('env') === 'dev') { - dbLogger.addTarget(TargetType.Console); - dbLogger.setLevel(Logger.LEVEL_INFO); // Set to LEVEL_DEBUG for full SQL queries + if (Platform.OS !== 'web') { + dbLogger.addTarget(TargetType.Database, { database: logDatabase, source: 'm' }); + if (Setting.value('env') === 'dev') { + dbLogger.addTarget(TargetType.Console); + dbLogger.setLevel(Logger.LEVEL_INFO); // Set to LEVEL_DEBUG for full SQL queries + } else { + dbLogger.setLevel(Logger.LEVEL_INFO); + } } else { + dbLogger.addTarget(TargetType.Console); dbLogger.setLevel(Logger.LEVEL_INFO); } diff --git a/packages/app-mobile/utils/database-driver-react-native.web.ts b/packages/app-mobile/utils/database-driver-react-native.web.ts index 8da1716fa46..5c1190458ae 100644 --- a/packages/app-mobile/utils/database-driver-react-native.web.ts +++ b/packages/app-mobile/utils/database-driver-react-native.web.ts @@ -22,7 +22,6 @@ export default class DatabaseDriverReactNative { }); const filename = `file:${safeFilename(options.name)}.sqlite3?vfs=opfs`; const { dbId } = await db('open', { filename }); - console.log('initialized db with ID ', dbId, 'at', filename); this.dbId_ = dbId; this.db_ = db; } @@ -32,7 +31,6 @@ export default class DatabaseDriverReactNative { } public selectOne(sql: string, params: string[] = []) { - console.log('selectOne', sql); return new Promise(async (resolve, reject) => { let resolved = false; await this.db_('exec', { @@ -41,7 +39,6 @@ export default class DatabaseDriverReactNative { bind: params, rowMode: 'object', callback: ((result: RowResult) => { - console.log('got', result) if (result.rowNumber !== 1) return; resolved = true; resolve(result.row); @@ -79,14 +76,11 @@ export default class DatabaseDriverReactNative { } public async exec(sql: string, params: string[]|null = null) { - console.log('preExec', sql); - const result = await this.db_('exec', { dbId: this.dbId_, sql, bind: params, }); - console.log('exec', sql, result); return result; } diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts index 0721302cce7..46fb54e4113 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -18,7 +18,7 @@ const removeReservedWords = (path: string) => { declare global { interface FileSystemDirectoryHandle { - keys(): AsyncIterable; + keys(): AsyncIterable; } } @@ -59,7 +59,7 @@ export default class FsDriverWeb extends FsDriverBase { console.log('handle', handle); } catch (error) { // TODO: Handle this better - console.warn(error); + console.warn(error, 'for', path); handle = null; } @@ -76,15 +76,19 @@ export default class FsDriverWeb extends FsDriverBase { await this.initPromise_; const parent = await this.pathToDirectoryHandle_(dirname(path)); - console.log('GETname', basename(path)) + console.error('Get name', basename(path), path, create) try { return parent.getFileHandle(removeReservedWords(basename(path)), { create }); } catch (error) { - if (!await this.exists(path)) { - return null; - } else { + if (create) { throw error; } + + console.warn(error); + + // TODO: This should return null when a file doesn't exist, but should + // also report errors in other cases. + return null; } } @@ -223,7 +227,7 @@ export default class FsDriverWeb extends FsDriverBase { if (!parentDir) return false; const target = basename(path); - for await (const key of (parentDir as any).keys()) { + for await (const key of parentDir.keys()) { if (key === target) return true; } return false; diff --git a/packages/app-mobile/utils/shim-init-react/index.web.ts b/packages/app-mobile/utils/shim-init-react/index.web.ts index 00c4fb64de9..d0d47b71e1c 100644 --- a/packages/app-mobile/utils/shim-init-react/index.web.ts +++ b/packages/app-mobile/utils/shim-init-react/index.web.ts @@ -118,8 +118,7 @@ export const shimInit = () => { }; shim.appVersion = () => { - const p = require('react-native-version-info').default; - return p.appVersion; + return require('../../package.json').version; }; // NOTE: This is a limited version of createResourceFromPath - unlike the Node version, it diff --git a/packages/app-mobile/web/index.html b/packages/app-mobile/web/index.html index 62aba5b785a..4f6af9d3ef0 100644 --- a/packages/app-mobile/web/index.html +++ b/packages/app-mobile/web/index.html @@ -10,6 +10,36 @@ height: 100vh; display: flex; } + + *::-webkit-scrollbar { + width: 7px; + height: 7px; + } + + *::-webkit-scrollbar-corner { + background: none; + } + + *::-webkit-scrollbar-track { + border: none; + } + + *::-webkit-scrollbar-thumb { + background: rgba(100, 100, 100, 0.3); + border-radius: 5px; + } + + *::-webkit-scrollbar-track:hover { + background: rgba(0, 0, 0, 0.1); + } + + *::-webkit-scrollbar-thumb:hover { + background: rgba(100, 100, 100, 0.7); + } + + * { + scrollbar-width: 7px; + } diff --git a/packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/makeSandboxedIframe.ts b/packages/lib/utils/dom/makeSandboxedIframe.ts similarity index 96% rename from packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/makeSandboxedIframe.ts rename to packages/lib/utils/dom/makeSandboxedIframe.ts index 93432435d0a..6316d384ba8 100644 --- a/packages/app-mobile/plugins/PluginRunner/backgroundPage/utils/makeSandboxedIframe.ts +++ b/packages/lib/utils/dom/makeSandboxedIframe.ts @@ -33,7 +33,7 @@ const makeSandboxedIframe = ( return; } - console.log('Adding plugin scripts...'); + console.log('Adding scripts...'); window.onmessage = undefined; for (const scriptText of event.data.scripts) { const scriptElem = document.createElement('script'); From 27e50db8f40c882eacf0c5381e14648ffb9ea5f9 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 17:10:26 -0700 Subject: [PATCH 004/177] Fix linter errors --- .eslintrc.js | 6 ++++ .../components/ExtendedWebView.web.tsx | 17 +++++++---- .../components/ScreenHeader/index.tsx | 2 +- packages/app-mobile/index.web.js | 3 +- packages/app-mobile/root.tsx | 2 +- .../services/AlarmServiceDriver.web.ts | 6 ++-- .../services/voiceTyping/vosk.web.ts | 2 ++ .../utils/database-driver-react-native.ts | 12 +++++--- .../utils/database-driver-react-native.web.ts | 16 ++++++---- .../utils/fs-driver/fs-driver-rn.web.ts | 27 ++++++++--------- .../app-mobile/utils/shim-init-react/index.ts | 6 ++-- .../utils/shim-init-react/index.web.ts | 5 ++-- .../utils/shim-init-react/injectedJs.ts | 2 +- .../utils/shim-init-react/shimInitShared.ts | 2 +- packages/app-mobile/web/mocks/empty.js | 2 +- packages/app-mobile/web/webpack.config.js | 30 +++++++++---------- packages/lib/models/Setting.ts | 2 +- packages/lib/services/search/SearchEngine.ts | 4 +-- 18 files changed, 85 insertions(+), 61 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index ce8065678a2..5bf27e9c49a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,6 +15,12 @@ module.exports = { 'globals': { 'Atomics': 'readonly', 'SharedArrayBuffer': 'readonly', + 'BufferEncoding': 'readonly', + 'AsyncIterable': 'readonly', + 'FileSystemFileHandle': 'readonly', + 'FileSystemDirectoryHandle': 'readonly', + 'ReadableStreamDefaultReader': 'readonly', + 'FileSystemCreateWritableOptions': 'readonly', // Jest variables 'test': 'readonly', diff --git a/packages/app-mobile/components/ExtendedWebView.web.tsx b/packages/app-mobile/components/ExtendedWebView.web.tsx index a526c4fbc91..3e88da9eaee 100644 --- a/packages/app-mobile/components/ExtendedWebView.web.tsx +++ b/packages/app-mobile/components/ExtendedWebView.web.tsx @@ -19,7 +19,10 @@ export interface WebViewControl { postMessage(message: unknown): void; } -export type OnMessageCallback = (event: { nativeEvent: { data: any } })=> void; +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Needs to interface with old code from before rule was applied. +type OnMessageEvent = { nativeEvent: { data: any } }; + +export type OnMessageCallback = (event: OnMessageEvent)=> void; export type OnErrorCallback = (event: WebViewErrorEvent)=> void; export type OnLoadCallback = ()=> void; @@ -54,7 +57,7 @@ const ExtendedWebView = (props: Props, ref: Ref) => { injectJS(js: string) { if (!iframeRef.current) { console.error(`ExtendedWebView(${props.webviewInstanceId}): WebView not loaded?`); - console.error('tried', js) + console.error('tried', js); return; } @@ -79,6 +82,10 @@ const ExtendedWebView = (props: Props, ref: Ref) => { const onLoadStartRef = useRef(props.onLoadStart); onLoadStartRef.current = props.onLoadStart; + // Don't re-load when injected JS changes. This should match the behavior of the native webview. + const injectedJavaScriptRef = useRef(props.injectedJavaScript); + injectedJavaScriptRef.current = props.injectedJavaScript; + useEffect(() => { if (!containerRef) return () => {}; @@ -112,7 +119,7 @@ const ExtendedWebView = (props: Props, ref: Ref) => { }, }; `, - props.injectedJavaScript, + injectedJavaScriptRef.current, ]); containerRef.replaceChildren(iframe); iframeRef.current = iframe; @@ -125,7 +132,7 @@ const ExtendedWebView = (props: Props, ref: Ref) => { if (event.source !== iframe.contentWindow) { return; } - + onMessageRef.current?.({ nativeEvent: { data: event.data } }); }; window.addEventListener('message', messageListener); @@ -140,7 +147,7 @@ const ExtendedWebView = (props: Props, ref: Ref) => { return () => { window.removeEventListener('message', messageListener); }; - }, [containerRef]); + }, [containerRef, props.html]); return ( diff --git a/packages/app-mobile/components/ScreenHeader/index.tsx b/packages/app-mobile/components/ScreenHeader/index.tsx index 69814274498..951a4861552 100644 --- a/packages/app-mobile/components/ScreenHeader/index.tsx +++ b/packages/app-mobile/components/ScreenHeader/index.tsx @@ -112,7 +112,7 @@ class ScreenHeaderComponent extends PureComponent Root); addEventListener('DOMContentLoaded', () => { - console.log('DOMContentLoaded ☺'); AppRegistry.runApplication('Joplin', { rootTag: document.querySelector('#root'), }); -}); \ No newline at end of file +}); diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index f098f70ffc7..c3a3ad983eb 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -41,7 +41,7 @@ const { BackButtonService } = require('./services/back-button.js'); import NavService from '@joplin/lib/services/NavService'; import { createStore, applyMiddleware } from 'redux'; import reduxSharedMiddleware from '@joplin/lib/components/shared/reduxSharedMiddleware'; -import { shimInit } from './utils/shim-init-react'; +import shimInit from './utils/shim-init-react'; const { AppNav } = require('./components/app-nav.js'); import Note from '@joplin/lib/models/Note'; import Folder from '@joplin/lib/models/Folder'; diff --git a/packages/app-mobile/services/AlarmServiceDriver.web.ts b/packages/app-mobile/services/AlarmServiceDriver.web.ts index 007914075e7..ea65c5c6801 100644 --- a/packages/app-mobile/services/AlarmServiceDriver.web.ts +++ b/packages/app-mobile/services/AlarmServiceDriver.web.ts @@ -13,10 +13,10 @@ export default class AlarmServiceDriver { throw new Error('Available only for non-persistent alarms'); } - public setInAppNotificationHandler(_v: any) { + public setInAppNotificationHandler(_v: unknown) { } - public async hasPermissions(_perm: any = null) { + public async hasPermissions(_perm: unknown = null) { return false; } @@ -28,6 +28,6 @@ export default class AlarmServiceDriver { } public async scheduleNotification(_notification: Notification) { - + } } diff --git a/packages/app-mobile/services/voiceTyping/vosk.web.ts b/packages/app-mobile/services/voiceTyping/vosk.web.ts index 9c758811881..0da3d842d6c 100644 --- a/packages/app-mobile/services/voiceTyping/vosk.web.ts +++ b/packages/app-mobile/services/voiceTyping/vosk.web.ts @@ -1,5 +1,6 @@ // Currently disabled on web +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied type Vosk = any; export { Vosk }; @@ -22,6 +23,7 @@ export const modelIsDownloaded = async (_locale: string) => { }; export const getVosk = async (_locale: string) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied return {} as any; }; diff --git a/packages/app-mobile/utils/database-driver-react-native.ts b/packages/app-mobile/utils/database-driver-react-native.ts index c23b6f84f68..ecebf6f4379 100644 --- a/packages/app-mobile/utils/database-driver-react-native.ts +++ b/packages/app-mobile/utils/database-driver-react-native.ts @@ -3,6 +3,7 @@ const SQLite = require('react-native-sqlite-storage'); export default class DatabaseDriverReactNative { private lastInsertId_: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied private db_: any; public constructor() { this.lastInsertId_ = null; @@ -13,6 +14,7 @@ export default class DatabaseDriverReactNative { return new Promise((resolve, reject) => { SQLite.openDatabase( { name: options.name }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied (db: any) => { this.db_ = db; resolve(); @@ -28,11 +30,12 @@ export default class DatabaseDriverReactNative { return error; } - public selectOne(sql: string, params: any = null) { + public selectOne(sql: string, params: unknown = null) { return new Promise((resolve, reject) => { this.db_.executeSql( sql, params, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied (r: any) => { resolve(r.rows.length ? r.rows.item(0) : null); }, @@ -43,7 +46,7 @@ export default class DatabaseDriverReactNative { }); } - public selectAll(sql: string, params: any = null) { + public selectAll(sql: string, params: unknown = null) { // eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied return this.exec(sql, params).then(r => { const output = []; @@ -58,7 +61,8 @@ export default class DatabaseDriverReactNative { throw new Error(`No extension support for ${path} in react-native-sqlite-storage`); } - public exec(sql: string, params: any = null) { + public exec(sql: string, params: unknown = null) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of old code from before rule was applied return new Promise((resolve, reject) => { this.db_.executeSql( sql, @@ -77,4 +81,4 @@ export default class DatabaseDriverReactNative { public lastInsertId() { return this.lastInsertId_; } -} \ No newline at end of file +} diff --git a/packages/app-mobile/utils/database-driver-react-native.web.ts b/packages/app-mobile/utils/database-driver-react-native.web.ts index 5c1190458ae..f9418530397 100644 --- a/packages/app-mobile/utils/database-driver-react-native.web.ts +++ b/packages/app-mobile/utils/database-driver-react-native.web.ts @@ -1,9 +1,9 @@ const { sqlite3Worker1Promiser } = require('@sqlite.org/sqlite-wasm'); -import { safeFilename } from "@joplin/utils/path"; +import { safeFilename } from '@joplin/utils/path'; -type DbPromiser = (command: string, options: Record)=>Promise; +type DbPromiser = (command: string, options: Record)=> Promise; type DbId = unknown; -type RowResult = { rowNumber: number|null; row: unknown; }; +type RowResult = { rowNumber: number|null; row: unknown }; export default class DatabaseDriverReactNative { private lastInsertId_: string; @@ -21,7 +21,9 @@ export default class DatabaseDriverReactNative { }); }); const filename = `file:${safeFilename(options.name)}.sqlite3?vfs=opfs`; - const { dbId } = await db('open', { filename }); + + type OpenResult = { dbId: number }; + const { dbId } = await db('open', { filename }) as OpenResult; this.dbId_ = dbId; this.db_ = db; } @@ -31,8 +33,10 @@ export default class DatabaseDriverReactNative { } public selectOne(sql: string, params: string[] = []) { + // eslint-disable-next-line no-async-promise-executor -- Wraps an API that mixes callbacks and promises. return new Promise(async (resolve, reject) => { let resolved = false; + await this.db_('exec', { dbId: this.dbId_, sql, @@ -51,7 +55,7 @@ export default class DatabaseDriverReactNative { }); } - public async selectAll(sql: string, params: any = null) { + public async selectAll(sql: string, params: string[] = null) { const results: unknown[] = []; await this.db_('exec', { dbId: this.dbId_, @@ -87,4 +91,4 @@ export default class DatabaseDriverReactNative { public lastInsertId() { return this.lastInsertId_; } -} \ No newline at end of file +} diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts index 46fb54e4113..836e9b1f398 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -3,6 +3,7 @@ import FsDriverBase, { ReadDirStatsOptions, Stat } from '@joplin/lib/fs-driver-b import tarExtract, { TarExtractOptions } from './tarExtract'; import tarCreate, { TarCreateOptions } from './tarCreate'; import { Buffer } from 'buffer'; +import Logger from '@joplin/utils/Logger'; const md5 = require('md5'); type FileHandle = { @@ -22,6 +23,8 @@ declare global { } } +const logger = Logger.create('FsDriverWeb'); + export default class FsDriverWeb extends FsDriverBase { private fsRoot_: FileSystemDirectoryHandle; private directoryHandleCache_: Map = new Map(); @@ -30,17 +33,16 @@ export default class FsDriverWeb extends FsDriverBase { public constructor() { super(); this.initPromise_ = (async () => { - console.log('Get root'); try { this.fsRoot_ = await (await navigator.storage.getDirectory()).getDirectoryHandle('joplin-web', { create: true }); } catch (error) { - console.error('Failed to create fsDriver:', error); + logger.warn('Failed to create fs-driver:', error); throw error; } })(); } - private async pathToDirectoryHandle_(path: string, create: boolean = false): Promise { + private async pathToDirectoryHandle_(path: string, create = false): Promise { await this.initPromise_; if (this.directoryHandleCache_.has(path)) { @@ -50,16 +52,14 @@ export default class FsDriverWeb extends FsDriverBase { const parentDirs = dirname(path); if (parentDirs && !['/', '.'].includes(parentDirs)) { const parent = await this.pathToDirectoryHandle_(parentDirs, create); - console.log('get dir handle', basename(path)); const folderName = removeReservedWords(basename(path)); let handle: FileSystemDirectoryHandle; try { handle = await parent.getDirectoryHandle(folderName, { create }); - console.log('handle', handle); } catch (error) { // TODO: Handle this better - console.warn(error, 'for', path); + logger.warn('Error getting directory handle', error, 'for', path); handle = null; } @@ -76,7 +76,7 @@ export default class FsDriverWeb extends FsDriverBase { await this.initPromise_; const parent = await this.pathToDirectoryHandle_(dirname(path)); - console.error('Get name', basename(path), path, create) + logger.debug('Get name', basename(path), path, create); try { return parent.getFileHandle(removeReservedWords(basename(path)), { create }); } catch (error) { @@ -84,7 +84,7 @@ export default class FsDriverWeb extends FsDriverBase { throw error; } - console.warn(error); + logger.warn(error); // TODO: This should return null when a file doesn't exist, but should // also report errors in other cases. @@ -140,7 +140,7 @@ export default class FsDriverWeb extends FsDriverBase { } } - public override async open(path: string, _mode: string = 'r'): Promise { + public override async open(path: string, _mode = 'r'): Promise { const handle = await this.pathToFileHandle_(path); return { handle, @@ -181,8 +181,8 @@ export default class FsDriverWeb extends FsDriverBase { const fromFile = await fromHandle.getFile(); const writer = (await toHandle.createWritable()).getWriter(); - writer.write(fromFile); - writer.close(); + await writer.write(fromFile); + await writer.close(); } public override async stat(path: string): Promise { @@ -193,7 +193,7 @@ export default class FsDriverWeb extends FsDriverBase { const size = await (async () => { if (dirHandle) return 0; - return (await fileHandle.getFile()).size + return (await fileHandle.getFile()).size; })(); return { @@ -223,7 +223,6 @@ export default class FsDriverWeb extends FsDriverBase { public override async exists(path: string) { const parentDir = await this.pathToDirectoryHandle_(dirname(path)); - console.log('exists', path); if (!parentDir) return false; const target = basename(path); @@ -257,7 +256,7 @@ export default class FsDriverWeb extends FsDriverBase { } public override getCacheDirectoryPath(): string { - return '/cache/'; + return '/cache/'; } public override getAppDirectoryPath(): string { diff --git a/packages/app-mobile/utils/shim-init-react/index.ts b/packages/app-mobile/utils/shim-init-react/index.ts index 7ffe7effb75..f9d5b895a30 100644 --- a/packages/app-mobile/utils/shim-init-react/index.ts +++ b/packages/app-mobile/utils/shim-init-react/index.ts @@ -6,6 +6,7 @@ const { GeolocationReact } = require('./geolocation-react.js'); const RNFetchBlob = require('rn-fetch-blob').default; const { generateSecureRandom } = require('react-native-securerandom'); import FsDriverRN from '../fs-driver/fs-driver-rn'; +import type SettingType from '@joplin/lib/models/Setting'; const mimeUtils = require('@joplin/lib/mime-utils.js'); const { basename, fileExtension } = require('@joplin/lib/path-utils'); const uuid = require('@joplin/lib/uuid').default; @@ -14,7 +15,7 @@ const { getLocales } = require('react-native-localize'); const { setLocale, defaultLocale, closestSupportedLocale } = require('@joplin/lib/locale'); -export function shimInit() { +export default function shimInit() { shim.Geolocation = GeolocationReact; shim.fsDriver = () => { @@ -40,6 +41,7 @@ export function shimInit() { /* eslint-disable no-console */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied (shim as any).debugFetch = async (url: string, options: any = null) => { options = { method: 'GET', @@ -82,7 +84,7 @@ export function shimInit() { /* eslint-enable */ - shim.detectAndSetLocale = (Setting: any) => { + shim.detectAndSetLocale = (Setting: typeof SettingType) => { // [ // { // "countryCode": "US", diff --git a/packages/app-mobile/utils/shim-init-react/index.web.ts b/packages/app-mobile/utils/shim-init-react/index.web.ts index d0d47b71e1c..27e5351f43f 100644 --- a/packages/app-mobile/utils/shim-init-react/index.web.ts +++ b/packages/app-mobile/utils/shim-init-react/index.web.ts @@ -13,7 +13,7 @@ import Setting from '@joplin/lib/models/Setting'; import shimInitShared from './shimInitShared'; import FsDriverWeb from '../fs-driver/fs-driver-rn.web'; -export const shimInit = () => { +const shimInit = () => { let fsDriver_: FsDriverBase|null = null; shim.fsDriver = () => { @@ -156,5 +156,6 @@ export const shimInit = () => { }; shimInitShared(); -} +}; +export default shimInit; diff --git a/packages/app-mobile/utils/shim-init-react/injectedJs.ts b/packages/app-mobile/utils/shim-init-react/injectedJs.ts index 404e55fcf8d..02927505652 100644 --- a/packages/app-mobile/utils/shim-init-react/injectedJs.ts +++ b/packages/app-mobile/utils/shim-init-react/injectedJs.ts @@ -6,4 +6,4 @@ const injectedJs = { pluginBackgroundPage: require('../../lib/rnInjectedJs/pluginBackgroundPage.bundle'), noteBodyViewerBundle: require('../../lib/rnInjectedJs/noteBodyViewerBundle.bundle'), }; -export default injectedJs; \ No newline at end of file +export default injectedJs; diff --git a/packages/app-mobile/utils/shim-init-react/shimInitShared.ts b/packages/app-mobile/utils/shim-init-react/shimInitShared.ts index 7742a2431da..3be1a90c083 100644 --- a/packages/app-mobile/utils/shim-init-react/shimInitShared.ts +++ b/packages/app-mobile/utils/shim-init-react/shimInitShared.ts @@ -60,4 +60,4 @@ const shimInitShared = () => { }; }; -export default shimInitShared; \ No newline at end of file +export default shimInitShared; diff --git a/packages/app-mobile/web/mocks/empty.js b/packages/app-mobile/web/mocks/empty.js index ced8860e3b6..3d9608d3466 100644 --- a/packages/app-mobile/web/mocks/empty.js +++ b/packages/app-mobile/web/mocks/empty.js @@ -1,2 +1,2 @@ -exports.default = {}; \ No newline at end of file +exports.default = {}; diff --git a/packages/app-mobile/web/webpack.config.js b/packages/app-mobile/web/webpack.config.js index e27e9257a27..acde3b27837 100644 --- a/packages/app-mobile/web/webpack.config.js +++ b/packages/app-mobile/web/webpack.config.js @@ -21,7 +21,7 @@ const babelLoaderConfiguration = { /.*node_modules\/@babel.*/, /.*node_modules\/@sqlite\.org\/.*/, - //path.resolve(appDirectory, 'node_modules/react-native-uncompiled') + // path.resolve(appDirectory, 'node_modules/react-native-uncompiled') ], use: { @@ -33,9 +33,9 @@ const babelLoaderConfiguration = { 'react-native-web', '@babel/plugin-transform-export-namespace-from', ...(babelConfig.plugins ?? []), - ] - } - } + ], + }, + }, }; // This is needed for webpack to import static images in JavaScript files. @@ -46,22 +46,22 @@ const imageLoaderConfiguration = { options: { name: '[name].[ext]', esModule: false, - } - } + }, + }, }; const emptyLibraryMock = path.resolve(__dirname, 'mocks/empty.js'); module.exports = { mode: 'development', - //devtool: 'inline-cheap-source-map', + // devtool: 'inline-cheap-source-map', target: 'web', entry: [ // load any web API polyfills // path.resolve(appDirectory, 'polyfills-web.js'), // your web-specific entry file - path.resolve(appDirectory, 'index.web.js') + path.resolve(appDirectory, 'index.web.js'), ], // configures where the build ends up @@ -113,12 +113,12 @@ module.exports = { ], fallback: { - "url": require.resolve("url/"), - "events": require.resolve("events/"), - "timers": require.resolve("timers-browserify"), - "path": require.resolve("path-browserify"), - "stream": require.resolve("stream-browserify"), - } + 'url': require.resolve('url/'), + 'events': require.resolve('events/'), + 'timers': require.resolve('timers-browserify'), + 'path': require.resolve('path-browserify'), + 'stream': require.resolve('stream-browserify'), + }, }, devServer: { @@ -129,4 +129,4 @@ module.exports = { 'Cross-Origin-Embedder-Policy': 'require-corp', }, }, -} +}; diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts index aad89e26b56..30f5306eb43 100644 --- a/packages/lib/models/Setting.ts +++ b/packages/lib/models/Setting.ts @@ -2585,7 +2585,7 @@ class Setting extends BaseModel { } const queries = []; - queries.push(`DELETE FROM settings WHERE key IN (\'${keys.join('\',\'')}\')`); + queries.push(`DELETE FROM settings WHERE key IN ('${keys.join('\',\'')}')`); for (let i = 0; i < this.cache_.length; i++) { const s = { ...this.cache_[i] }; diff --git a/packages/lib/services/search/SearchEngine.ts b/packages/lib/services/search/SearchEngine.ts index b92696f54d1..43225a5317a 100644 --- a/packages/lib/services/search/SearchEngine.ts +++ b/packages/lib/services/search/SearchEngine.ts @@ -136,7 +136,7 @@ export default class SearchEngine { const notes = await Note.modelSelectAll(` SELECT ${SearchEngine.relevantFields} FROM notes - WHERE id IN ('${currentIds.join('\',\'')}\') AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`); + WHERE id IN ('${currentIds.join('\',\'')}') AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`); const queries = []; for (let i = 0; i < notes.length; i++) { @@ -219,7 +219,7 @@ export default class SearchEngine { const noteIds = changes.map(a => a.item_id); const notes = await Note.modelSelectAll(` SELECT ${SearchEngine.relevantFields} - FROM notes WHERE id IN (\'${noteIds.join('\',\'')}\') AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`, + FROM notes WHERE id IN ('${noteIds.join('\',\'')}') AND is_conflict = 0 AND encryption_applied = 0 AND deleted_time = 0`, ); for (let i = 0; i < changes.length; i++) { From 6260956932aacac65043fa76c3658b5c93be9934 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 17:17:48 -0700 Subject: [PATCH 005/177] Fix Firefox startup --- .../utils/fs-driver/fs-driver-rn.web.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts index 836e9b1f398..9739b4dd60f 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -3,7 +3,7 @@ import FsDriverBase, { ReadDirStatsOptions, Stat } from '@joplin/lib/fs-driver-b import tarExtract, { TarExtractOptions } from './tarExtract'; import tarCreate, { TarCreateOptions } from './tarCreate'; import { Buffer } from 'buffer'; -import Logger from '@joplin/utils/Logger'; +import Logger, { LogLevel, TargetType } from '@joplin/utils/Logger'; const md5 = require('md5'); type FileHandle = { @@ -14,7 +14,7 @@ type FileHandle = { }; const removeReservedWords = (path: string) => { - return path.replace(/(tmp)/g, '_$1'); + return path.replace(/\/(tmp)/g, '_$1'); }; declare global { @@ -23,7 +23,9 @@ declare global { } } -const logger = Logger.create('FsDriverWeb'); +const logger = new Logger(); +logger.addTarget(TargetType.Console); +logger.setLevel(LogLevel.Debug); export default class FsDriverWeb extends FsDriverBase { private fsRoot_: FileSystemDirectoryHandle; @@ -46,8 +48,10 @@ export default class FsDriverWeb extends FsDriverBase { await this.initPromise_; if (this.directoryHandleCache_.has(path)) { + logger.debug('pathToDirectoryHandle_ from cache'); return this.directoryHandleCache_.get(path); } + logger.debug('pathToDirectoryHandle_', 'path:', path, 'create:', create); const parentDirs = dirname(path); if (parentDirs && !['/', '.'].includes(parentDirs)) { @@ -63,10 +67,7 @@ export default class FsDriverWeb extends FsDriverBase { handle = null; } - if (handle) { - this.directoryHandleCache_.set(path, handle); - } - + this.directoryHandleCache_.set(path, handle); return handle; } return this.fsRoot_; @@ -74,9 +75,9 @@ export default class FsDriverWeb extends FsDriverBase { private async pathToFileHandle_(path: string, create = false): Promise { await this.initPromise_; - + logger.debug('pathToFileHandle_', 'path:', path, 'create:', create); const parent = await this.pathToDirectoryHandle_(dirname(path)); - logger.debug('Get name', basename(path), path, create); + try { return parent.getFileHandle(removeReservedWords(basename(path)), { create }); } catch (error) { @@ -105,6 +106,7 @@ export default class FsDriverWeb extends FsDriverBase { encoding: BufferEncoding = 'base64', options?: FileSystemCreateWritableOptions, ) { + logger.debug('writeFile', path); const { writer } = await this.openWriteStream_(path, options); if (encoding === 'utf-8' || encoding === 'utf8') { const encoder = new TextEncoder(); @@ -113,6 +115,7 @@ export default class FsDriverWeb extends FsDriverBase { await writer.write(Buffer.from(string, encoding).buffer); } await writer.close(); + logger.debug('writeFile done', path); } public override async appendFile(path: string, content: string, encoding?: BufferEncoding) { @@ -130,6 +133,8 @@ export default class FsDriverWeb extends FsDriverBase { } public async readFile(path: string, encoding: BufferEncoding = 'utf-8') { + logger.debug('readFile', path); + const handle = await this.pathToFileHandle_(path); const file = await handle.getFile(); if (encoding === 'utf-8' || encoding === 'utf8') { @@ -222,6 +227,8 @@ export default class FsDriverWeb extends FsDriverBase { } public override async exists(path: string) { + logger.debug('exists?', path); + const parentDir = await this.pathToDirectoryHandle_(dirname(path)); if (!parentDir) return false; From e687363b3e04286d56477f778cac9d3645d762fe Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 18:08:11 -0700 Subject: [PATCH 006/177] Styling and sync fixes --- packages/app-mobile/components/Dropdown.tsx | 1 + .../ConfigScreen/plugins/PluginBox/index.tsx | 7 +- packages/app-mobile/index.web.js | 20 +++++- packages/app-mobile/root.tsx | 2 +- .../utils/fs-driver/fs-driver-rn.web.ts | 19 ++++-- .../app-mobile/utils/shim-init-react/index.ts | 38 ----------- .../utils/shim-init-react/index.web.ts | 65 ++++++------------- .../utils/shim-init-react/shimInitShared.ts | 38 +++++++++++ packages/app-mobile/web/webpack.config.js | 8 +-- packages/lib/models/Folder.ts | 6 +- packages/lib/models/Resource.ts | 2 +- 11 files changed, 102 insertions(+), 104 deletions(-) diff --git a/packages/app-mobile/components/Dropdown.tsx b/packages/app-mobile/components/Dropdown.tsx index b7084187b0f..06ac6934bc1 100644 --- a/packages/app-mobile/components/Dropdown.tsx +++ b/packages/app-mobile/components/Dropdown.tsx @@ -115,6 +115,7 @@ class Dropdown extends Component { const itemWrapperStyle: ViewStyle = { ...(this.props.itemWrapperStyle ? this.props.itemWrapperStyle : {}), flex: 1, + flexBasis: 'auto', justifyContent: 'center', height: itemHeight, paddingLeft: 20, diff --git a/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.tsx b/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.tsx index 7f7c9e5ae5e..bdcace49525 100644 --- a/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.tsx +++ b/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginBox/index.tsx @@ -80,9 +80,10 @@ const PluginBox: React.FC = props => { const styles = useStyles(props.isCompatible); + const CardWrapper = props.onShowPluginInfo ? TouchableRipple : View; return ( - = props => { {props.onInstall ? installButton : null} - + ); }; diff --git a/packages/app-mobile/index.web.js b/packages/app-mobile/index.web.js index a39b422e6e2..65232b71cf3 100644 --- a/packages/app-mobile/index.web.js +++ b/packages/app-mobile/index.web.js @@ -1,7 +1,12 @@ +import { AppRegistry } from 'react-native'; + import fontAwesomeFont from 'react-native-vector-icons/Fonts/FontAwesome.ttf'; import fontAwesomeSolidFont from 'react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf'; +import fontAwesomeRegularFont from 'react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf'; +import fontAwesomeBrandsFont from 'react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf'; import ioniconFont from 'react-native-vector-icons/Fonts/Ionicons.ttf'; import materialCommunityIconsFont from 'react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf'; +import antDesignFont from 'react-native-vector-icons/Fonts/AntDesign.ttf'; // See https://www.npmjs.com/package/react-native-vector-icons const setUpRnVectorIcons = () => { @@ -14,6 +19,14 @@ const setUpRnVectorIcons = () => { src: url(${fontAwesomeSolidFont}); font-family: FontAwesome5_Solid; } + @font-face { + src: url(${fontAwesomeRegularFont}); + font-family: FontAwesome5_Regular; + } + @font-face { + src: url(${fontAwesomeBrandsFont}); + font-family: FontAwesome5_Brands; + } @font-face { src: url(${ioniconFont}); font-family: Ionicons; @@ -22,15 +35,20 @@ const setUpRnVectorIcons = () => { src: url(${materialCommunityIconsFont}); font-family: MaterialCommunityIcons; } + @font-face { + src: url(${antDesignFont}); + font-family: AntDesign; + } `; const style = document.createElement('style'); style.appendChild(document.createTextNode(iconFontStyles)); document.head.appendChild(style); }; + setUpRnVectorIcons(); -import { AppRegistry } from 'react-native'; + const Root = require('./root').default; AppRegistry.registerComponent('Joplin', () => Root); diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index c3a3ad983eb..c21edeb129a 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -526,7 +526,7 @@ async function initialize(dispatch: Function) { mainLogger.addTarget(TargetType.Database, { database: logDatabase, source: 'm' }); mainLogger.setLevel(Logger.LEVEL_INFO); - if (Setting.value('env') === 'dev') { + if (Setting.value('env') === 'dev' || Platform.OS === 'web') { mainLogger.addTarget(TargetType.Console); mainLogger.setLevel(Logger.LEVEL_DEBUG); } diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts index 9739b4dd60f..dcf55896eb7 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -102,17 +102,21 @@ export default class FsDriverWeb extends FsDriverBase { public override async writeFile( path: string, - string: string, - encoding: BufferEncoding = 'base64', + data: string|ArrayBuffer, + encoding: BufferEncoding|'buffer' = 'base64', options?: FileSystemCreateWritableOptions, ) { logger.debug('writeFile', path); const { writer } = await this.openWriteStream_(path, options); - if (encoding === 'utf-8' || encoding === 'utf8') { + if (encoding === 'buffer') { + await writer.write(data); + } else if (data instanceof ArrayBuffer) { + throw new Error('Cannot write ArrayBuffer to file without encoding = buffer'); + } else if (encoding === 'utf-8' || encoding === 'utf8') { const encoder = new TextEncoder(); - await writer.write(encoder.encode(string)); + await writer.write(encoder.encode(data)); } else { - await writer.write(Buffer.from(string, encoding).buffer); + await writer.write(Buffer.from(data, encoding).buffer); } await writer.close(); logger.debug('writeFile done', path); @@ -145,6 +149,11 @@ export default class FsDriverWeb extends FsDriverBase { } } + public async readToFile(path: string) { + const handle = await this.pathToFileHandle_(path); + return await handle.getFile(); + } + public override async open(path: string, _mode = 'r'): Promise { const handle = await this.pathToFileHandle_(path); return { diff --git a/packages/app-mobile/utils/shim-init-react/index.ts b/packages/app-mobile/utils/shim-init-react/index.ts index f9d5b895a30..b30e041c45d 100644 --- a/packages/app-mobile/utils/shim-init-react/index.ts +++ b/packages/app-mobile/utils/shim-init-react/index.ts @@ -7,10 +7,6 @@ const RNFetchBlob = require('rn-fetch-blob').default; const { generateSecureRandom } = require('react-native-securerandom'); import FsDriverRN from '../fs-driver/fs-driver-rn'; import type SettingType from '@joplin/lib/models/Setting'; -const mimeUtils = require('@joplin/lib/mime-utils.js'); -const { basename, fileExtension } = require('@joplin/lib/path-utils'); -const uuid = require('@joplin/lib/uuid').default; -const Resource = require('@joplin/lib/models/Resource').default; const { getLocales } = require('react-native-localize'); const { setLocale, defaultLocale, closestSupportedLocale } = require('@joplin/lib/locale'); @@ -232,40 +228,6 @@ export default function shimInit() { return p.appVersion; }; - // NOTE: This is a limited version of createResourceFromPath - unlike the Node version, it - // only really works with images. It does not resize the image either. - shim.createResourceFromPath = async function(filePath, defaultProps = null) { - defaultProps = defaultProps ? defaultProps : {}; - const resourceId = defaultProps.id ? defaultProps.id : uuid.create(); - - const ext = fileExtension(filePath); - let mimeType = mimeUtils.fromFileExtension(ext); - if (!mimeType) mimeType = 'image/jpeg'; - - let resource = Resource.new(); - resource.id = resourceId; - resource.mime = mimeType; - resource.title = basename(filePath); - resource.file_extension = ext; - - const targetPath = Resource.fullPath(resource); - await shim.fsDriver().copy(filePath, targetPath); - - if (defaultProps) { - resource = { ...resource, ...defaultProps }; - } - - const itDoes = await shim.fsDriver().waitTillExists(targetPath); - if (!itDoes) throw new Error(`Resource file was not created: ${targetPath}`); - - const fileStat = await shim.fsDriver().stat(targetPath); - resource.size = fileStat.size; - - resource = await Resource.save(resource, { isNew: true }); - - return resource; - }; - shimInitShared(); } diff --git a/packages/app-mobile/utils/shim-init-react/index.web.ts b/packages/app-mobile/utils/shim-init-react/index.web.ts index 27e5351f43f..a461200ccd3 100644 --- a/packages/app-mobile/utils/shim-init-react/index.web.ts +++ b/packages/app-mobile/utils/shim-init-react/index.web.ts @@ -2,26 +2,24 @@ import type ShimType from '@joplin/lib/shim'; const shim: typeof ShimType = require('@joplin/lib/shim').default; import { generateSecureRandom } from 'react-native-securerandom'; -import * as mimeUtils from '@joplin/lib/mime-utils'; -import { basename, fileExtension } from '@joplin/lib/path-utils'; -import uuid from '@joplin/lib/uuid'; -import Resource from '@joplin/lib/models/Resource'; import { getLocales } from 'react-native-localize'; import { setLocale, defaultLocale, closestSupportedLocale } from '@joplin/lib/locale'; -import FsDriverBase from '@joplin/lib/fs-driver-base'; import Setting from '@joplin/lib/models/Setting'; import shimInitShared from './shimInitShared'; import FsDriverWeb from '../fs-driver/fs-driver-rn.web'; +import { FetchBlobOptions } from '@joplin/lib/types'; +import JoplinError from '@joplin/lib/JoplinError'; const shimInit = () => { - let fsDriver_: FsDriverBase|null = null; + let fsDriver_: FsDriverWeb|null = null; - shim.fsDriver = () => { + const fsDriver = () => { if (!fsDriver_) { fsDriver_ = new FsDriverWeb(); } return fsDriver_; }; + shim.fsDriver = fsDriver; shim.randomBytes = async (count: number) => { const randomBytes = await generateSecureRandom(count); @@ -93,12 +91,23 @@ const shimInit = () => { }, options); }; - shim.fetchBlob = async function(_url, _options) { - throw new Error('fetchBlob: Not implemented'); + shim.fetchBlob = async function(url, options: FetchBlobOptions) { + const outputPath = options.path; + if (!outputPath) throw new Error('fetchBlob: Missing outputPath'); + + const result = await fetch(url, { method: options.method, headers: options.headers }); + if (!result.ok) { + throw new JoplinError(`fetch failed: ${result.statusText}`, result.status); + } + + const blob = await result.blob(); + await fsDriver().writeFile(outputPath, await blob.arrayBuffer(), 'buffer'); }; - shim.uploadBlob = async function(_url, _options) { - throw new Error('uploadBlob: Not implemented'); + shim.uploadBlob = async function(url, options) { + if (!options || !options.path) throw new Error('uploadBlob: source file path is missing'); + const content = await fsDriver().readToFile(options.path); + return fetch(url, { ...options, body: content }); }; shim.readLocalFileBase64 = async function(path) { @@ -121,40 +130,6 @@ const shimInit = () => { return require('../../package.json').version; }; - // NOTE: This is a limited version of createResourceFromPath - unlike the Node version, it - // only really works with images. It does not resize the image either. - shim.createResourceFromPath = async function(filePath, defaultProps = null) { - defaultProps = defaultProps ? defaultProps : {}; - const resourceId = defaultProps.id ? defaultProps.id : uuid.create(); - - const ext = fileExtension(filePath); - let mimeType = mimeUtils.fromFileExtension(ext); - if (!mimeType) mimeType = 'image/jpeg'; - - let resource = Resource.new(); - resource.id = resourceId; - resource.mime = mimeType; - resource.title = basename(filePath); - resource.file_extension = ext; - - const targetPath = Resource.fullPath(resource); - await shim.fsDriver().copy(filePath, targetPath); - - if (defaultProps) { - resource = { ...resource, ...defaultProps }; - } - - const itDoes = await shim.fsDriver().waitTillExists(targetPath); - if (!itDoes) throw new Error(`Resource file was not created: ${targetPath}`); - - const fileStat = await shim.fsDriver().stat(targetPath); - resource.size = fileStat.size; - - resource = await Resource.save(resource, { isNew: true }); - - return resource; - }; - shimInitShared(); }; diff --git a/packages/app-mobile/utils/shim-init-react/shimInitShared.ts b/packages/app-mobile/utils/shim-init-react/shimInitShared.ts index 3be1a90c083..e0e942c51e9 100644 --- a/packages/app-mobile/utils/shim-init-react/shimInitShared.ts +++ b/packages/app-mobile/utils/shim-init-react/shimInitShared.ts @@ -4,6 +4,10 @@ import type ShimType from '@joplin/lib/shim'; import PoorManIntervals from '@joplin/lib/PoorManIntervals'; import showMessageBox from '../showMessageBox'; import { Buffer } from 'buffer'; +import { basename, fileExtension } from '@joplin/utils/path'; +import uuid from '@joplin/lib/uuid'; +import * as mimeUtils from '@joplin/lib/mime-utils'; +import Resource from '@joplin/lib/models/Resource'; const shim: typeof ShimType = require('@joplin/lib/shim').default; const shimInitShared = () => { @@ -58,6 +62,40 @@ const shimInitShared = () => { shim.clearInterval = (id) => { return PoorManIntervals.clearInterval(id); }; + + // NOTE: This is a limited version of createResourceFromPath - unlike the Node version, it + // only really works with images. It does not resize the image either. + shim.createResourceFromPath = async function(filePath, defaultProps = null) { + defaultProps = defaultProps ? defaultProps : {}; + const resourceId = defaultProps.id ? defaultProps.id : uuid.create(); + + const ext = fileExtension(filePath); + let mimeType = mimeUtils.fromFileExtension(ext); + if (!mimeType) mimeType = 'image/jpeg'; + + let resource = Resource.new(); + resource.id = resourceId; + resource.mime = mimeType; + resource.title = basename(filePath); + resource.file_extension = ext; + + const targetPath = Resource.fullPath(resource); + await shim.fsDriver().copy(filePath, targetPath); + + if (defaultProps) { + resource = { ...resource, ...defaultProps }; + } + + const itDoes = await shim.fsDriver().waitTillExists(targetPath); + if (!itDoes) throw new Error(`Resource file was not created: ${targetPath}`); + + const fileStat = await shim.fsDriver().stat(targetPath); + resource.size = fileStat.size; + + resource = await Resource.save(resource, { isNew: true }); + + return resource; + }; }; export default shimInitShared; diff --git a/packages/app-mobile/web/webpack.config.js b/packages/app-mobile/web/webpack.config.js index acde3b27837..9e39e2bbcec 100644 --- a/packages/app-mobile/web/webpack.config.js +++ b/packages/app-mobile/web/webpack.config.js @@ -41,13 +41,7 @@ const babelLoaderConfiguration = { // This is needed for webpack to import static images in JavaScript files. const imageLoaderConfiguration = { test: /\.(gif|jpe?g|png|svg|ttf)$/, - use: { - loader: 'url-loader', - options: { - name: '[name].[ext]', - esModule: false, - }, - }, + type: 'asset/resource', }; const emptyLibraryMock = path.resolve(__dirname, 'mocks/empty.js'); diff --git a/packages/lib/models/Folder.ts b/packages/lib/models/Folder.ts index 4ca92f016c6..40f9d04e5aa 100644 --- a/packages/lib/models/Folder.ts +++ b/packages/lib/models/Folder.ts @@ -247,7 +247,7 @@ export default class Folder extends BaseItem { // The remaining folders, that don't contain any notes are sorted by their own user_updated_time public static async orderByLastModified(folders: FolderEntity[], dir = 'DESC') { dir = dir.toUpperCase(); - const sql = 'select parent_id, max(user_updated_time) content_updated_time from notes where parent_id != "" group by parent_id'; + const sql = 'select parent_id, max(user_updated_time) content_updated_time from notes where parent_id != \'\' group by parent_id'; const rows = await this.db().selectAll(sql); const folderIdToTime: Record = {}; @@ -388,7 +388,7 @@ export default class Folder extends BaseItem { } public static async rootSharedFolders(): Promise { - return this.db().selectAll('SELECT id, share_id FROM folders WHERE parent_id = "" AND share_id != ""'); + return this.db().selectAll('SELECT id, share_id FROM folders WHERE parent_id = \'\' AND share_id != \'\''); } public static async rootShareFoldersByKeyId(keyId: string): Promise { @@ -431,7 +431,7 @@ export default class Folder extends BaseItem { // if they've been moved out of a shared folder. // await this.unshareItems(ModelType.Folder, sharedFolderIds); - const sql = ['SELECT id, parent_id FROM folders WHERE share_id != ""']; + const sql = ['SELECT id, parent_id FROM folders WHERE share_id != \'\'']; if (sharedFolderIds.length) { sql.push(` AND id NOT IN ('${sharedFolderIds.join('\',\'')}')`); } diff --git a/packages/lib/models/Resource.ts b/packages/lib/models/Resource.ts index 9a947e0d15b..5fe4e33da1b 100644 --- a/packages/lib/models/Resource.ts +++ b/packages/lib/models/Resource.ts @@ -107,7 +107,7 @@ export default class Resource extends BaseItem { } public static async resetFetchErrorStatus(resourceId: string) { - await this.db().exec('UPDATE resource_local_states SET fetch_status = ?, fetch_error = "" WHERE resource_id = ?', [Resource.FETCH_STATUS_IDLE, resourceId]); + await this.db().exec('UPDATE resource_local_states SET fetch_status = ?, fetch_error = \'\' WHERE resource_id = ?', [Resource.FETCH_STATUS_IDLE, resourceId]); await this.resetOcrStatus(resourceId); } From 5a86531d2026343a933f10fe28f5ea071688ad4e Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 18:37:14 -0700 Subject: [PATCH 007/177] Fix mobile build --- packages/app-mobile/utils/fs-driver/fs-driver-rn.ts | 4 ++++ packages/app-mobile/utils/shim-init-react/index.ts | 2 +- packages/utils/time.ts | 6 +++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts index 516bd6277b1..82349591bfd 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.ts @@ -355,6 +355,10 @@ export default class FsDriverRN extends FsDriverBase { return RNFS.CachesDirectoryPath; } + public getAppDirectoryPath() { + return RNFetchBlob.fs.dirs.DocumentDir; + } + public isUsingAndroidSAF() { return Platform.OS === 'android' && Platform.Version > 28; } diff --git a/packages/app-mobile/utils/shim-init-react/index.ts b/packages/app-mobile/utils/shim-init-react/index.ts index b30e041c45d..2928e7e1d9f 100644 --- a/packages/app-mobile/utils/shim-init-react/index.ts +++ b/packages/app-mobile/utils/shim-init-react/index.ts @@ -2,7 +2,7 @@ import type ShimType from '@joplin/lib/shim'; import shimInitShared from './shimInitShared'; const shim: typeof ShimType = require('@joplin/lib/shim').default; -const { GeolocationReact } = require('./geolocation-react.js'); +const { GeolocationReact } = require('../geolocation-react.js'); const RNFetchBlob = require('rn-fetch-blob').default; const { generateSecureRandom } = require('react-native-securerandom'); import FsDriverRN from '../fs-driver/fs-driver-rn'; diff --git a/packages/utils/time.ts b/packages/utils/time.ts index ade8857f91e..531719eb1b8 100644 --- a/packages/utils/time.ts +++ b/packages/utils/time.ts @@ -4,7 +4,11 @@ // ----------------------------------------------------------------------------------------------- import * as dayjs from 'dayjs'; -import * as dayJsRelativeTime from 'dayjs/plugin/relativeTime'; + +// Separating this into a type import and a require seems to be necessary to support mobile +// (import = require syntax doesn't work there). +import type * as dayJsRelativeTimeType from 'dayjs/plugin/relativeTime'; +const dayJsRelativeTime: typeof dayJsRelativeTimeType = require('dayjs/plugin/relativeTime'); const supportedLocales: Record = { 'ar': require('dayjs/locale/ar'), From 3c2f34b0ea692ab2c99a4ca82ba829fbe1fec18d Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 18:38:49 -0700 Subject: [PATCH 008/177] Improve styles --- .../components/NoteEditor/NoteEditor.tsx | 30 +++++++++++++++++++ .../ConfigScreen/configScreenStyles.ts | 1 + packages/app-mobile/web/index.html | 8 ++--- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/app-mobile/components/NoteEditor/NoteEditor.tsx b/packages/app-mobile/components/NoteEditor/NoteEditor.tsx index db457a90996..edfab4bfa30 100644 --- a/packages/app-mobile/components/NoteEditor/NoteEditor.tsx +++ b/packages/app-mobile/components/NoteEditor/NoteEditor.tsx @@ -80,6 +80,36 @@ function useCss(themeId: number): string { font-size: 13pt; } + + * { + scrollbar-width: thin; + } + + *::-webkit-scrollbar { + width: 7px; + height: 7px; + } + + *::-webkit-scrollbar-corner { + background: none; + } + + *::-webkit-scrollbar-track { + border: none; + } + + *::-webkit-scrollbar-thumb { + background: rgba(100, 100, 100, 0.3); + border-radius: 5px; + } + + *::-webkit-scrollbar-track:hover { + background: rgba(0, 0, 0, 0.1); + } + + *::-webkit-scrollbar-thumb:hover { + background: rgba(100, 100, 100, 0.7); + } `; }, [themeId]); } diff --git a/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts b/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts index 168984bb960..8029baf18d3 100644 --- a/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts +++ b/packages/app-mobile/components/screens/ConfigScreen/configScreenStyles.ts @@ -80,6 +80,7 @@ const configScreenStyles = (themeId: number): ConfigScreenStyles => { const sidebarButton: SidebarButtonStyle = { height: sidebarButtonHeight, flex: 1, + flexBasis: 'auto', flexDirection: 'row', alignItems: 'center', paddingEnd: theme.marginRight, diff --git a/packages/app-mobile/web/index.html b/packages/app-mobile/web/index.html index 4f6af9d3ef0..98bd30a4d36 100644 --- a/packages/app-mobile/web/index.html +++ b/packages/app-mobile/web/index.html @@ -11,6 +11,10 @@ display: flex; } + * { + scrollbar-width: thin; + } + *::-webkit-scrollbar { width: 7px; height: 7px; @@ -36,10 +40,6 @@ *::-webkit-scrollbar-thumb:hover { background: rgba(100, 100, 100, 0.7); } - - * { - scrollbar-width: 7px; - } From 3dec27ee9a80a4a5bd3416920ce9ebc6e34b09d3 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 21 Jun 2024 20:48:33 -0700 Subject: [PATCH 009/177] Fix installing plugins from file & plugin panels --- .../components/ExtendedWebView.web.tsx | 2 - .../plugins/PluginUploadButton.tsx | 4 +- .../app-mobile/components/screens/Note.tsx | 2 +- .../backgroundPage/initializeDialogWebView.ts | 16 ++++ .../dialogs/PluginUserWebView.tsx | 1 + .../dialogs/hooks/useWebViewSetup.ts | 12 ++- .../app-mobile/plugins/PluginRunner/types.ts | 1 + .../utils/fs-driver/fs-driver-rn.web.ts | 84 ++++++++++++------- .../app-mobile/utils/fs-driver/tarExtract.ts | 1 + packages/app-mobile/utils/pickDocument.ts | 37 +++++++- .../utils/shim-init-react/index.web.ts | 2 +- packages/app-mobile/web/index.html | 4 + packages/lib/utils/dom/makeSandboxedIframe.ts | 1 - 13 files changed, 127 insertions(+), 40 deletions(-) diff --git a/packages/app-mobile/components/ExtendedWebView.web.tsx b/packages/app-mobile/components/ExtendedWebView.web.tsx index 3e88da9eaee..08a95d9fa05 100644 --- a/packages/app-mobile/components/ExtendedWebView.web.tsx +++ b/packages/app-mobile/components/ExtendedWebView.web.tsx @@ -92,9 +92,7 @@ const ExtendedWebView = (props: Props, ref: Ref) => { const { iframe } = makeSandboxedIframe(props.html, [ ` window.addEventListener('message', (event) => { - console.log('got message', event); if (event.source !== parent || event.origin === 'react-native') { - console.log('Ignoring message: wrong source'); return; } diff --git a/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginUploadButton.tsx b/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginUploadButton.tsx index 63c80ec9c58..24540848367 100644 --- a/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginUploadButton.tsx +++ b/packages/app-mobile/components/screens/ConfigScreen/plugins/PluginUploadButton.tsx @@ -43,8 +43,8 @@ const PluginUploadButton: React.FC = props => { const selectedFile = pluginFiles[0]; const localFilePath = Platform.select({ - android: selectedFile.uri, ios: decodeURI(selectedFile.uri), + default: selectedFile.uri, }); logger.info('Installing plugin from file', localFilePath); @@ -73,6 +73,8 @@ const PluginUploadButton: React.FC = props => { logger.info('Copying to', targetFile); await fsDriver.copy(localFilePath, targetFile); + logger.debug('Copied. Now installing.'); + const plugin = await pluginService.installPlugin(targetFile); const pluginSettings = pluginService.unserializePluginSettings(props.pluginSettings); diff --git a/packages/app-mobile/components/screens/Note.tsx b/packages/app-mobile/components/screens/Note.tsx index c35597420c3..780d0431271 100644 --- a/packages/app-mobile/components/screens/Note.tsx +++ b/packages/app-mobile/components/screens/Note.tsx @@ -767,8 +767,8 @@ class NoteScreenComponent extends BaseScreenComponent implements B } const localFilePath = Platform.select({ - android: pickerResponse.uri, ios: decodeURI(pickerResponse.uri), + default: pickerResponse.uri, }); let mimeType = pickerResponse.type; diff --git a/packages/app-mobile/plugins/PluginRunner/backgroundPage/initializeDialogWebView.ts b/packages/app-mobile/plugins/PluginRunner/backgroundPage/initializeDialogWebView.ts index 59b69625d17..59fc18dce82 100644 --- a/packages/app-mobile/plugins/PluginRunner/backgroundPage/initializeDialogWebView.ts +++ b/packages/app-mobile/plugins/PluginRunner/backgroundPage/initializeDialogWebView.ts @@ -47,6 +47,22 @@ const initializeDialogWebView = (messageChannelId: string) => { includeJsFiles: async (paths: string[]) => { return includeScriptsOrStyles('js', paths); }, + runScript: async (key: string, scriptData: string) => { + if (loadedPaths.has(key)) { + return; + } + loadedPaths.add(key); + + if (key.endsWith('.css')) { + const stylesheetLink = document.createElement('style'); + stylesheetLink.appendChild(document.createTextNode(scriptData)); + document.head.appendChild(stylesheetLink); + } else { + const script = document.createElement('script'); + script.appendChild(document.createTextNode(scriptData)); + document.head.appendChild(script); + } + }, getFormData: async () => { return getFormData(); }, diff --git a/packages/app-mobile/plugins/PluginRunner/dialogs/PluginUserWebView.tsx b/packages/app-mobile/plugins/PluginRunner/dialogs/PluginUserWebView.tsx index 24682635e5f..0ec8509249e 100644 --- a/packages/app-mobile/plugins/PluginRunner/dialogs/PluginUserWebView.tsx +++ b/packages/app-mobile/plugins/PluginRunner/dialogs/PluginUserWebView.tsx @@ -46,6 +46,7 @@ const PluginUserWebView = (props: Props) => { setThemeCss: messenger.remoteApi.setThemeCss, getFormData: messenger.remoteApi.getFormData, getContentSize: messenger.remoteApi.getContentSize, + runScript: messenger.remoteApi.runScript, }); }, [messenger, props.setDialogControl]); diff --git a/packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useWebViewSetup.ts b/packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useWebViewSetup.ts index 6688698af0d..51fc29ea5e1 100644 --- a/packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useWebViewSetup.ts +++ b/packages/app-mobile/plugins/PluginRunner/dialogs/hooks/useWebViewSetup.ts @@ -29,8 +29,16 @@ const useWebViewSetup = (props: Props) => { jsPaths.push(resolvedPath); } } - void dialogControl.includeCssFiles(cssPaths); - void dialogControl.includeJsFiles(jsPaths); + if (shim.mobilePlatform() === 'web') { + void (async () => { + for (const path of [...jsPaths, ...cssPaths]) { + void dialogControl.runScript(path, await shim.fsDriver().readFile(path, 'utf-8')); + } + })(); + } else { + void dialogControl.includeCssFiles(cssPaths); + void dialogControl.includeJsFiles(jsPaths); + } }, [dialogControl, scriptPaths, props.webViewLoadCount, pluginBaseDir]); useEffect(() => { diff --git a/packages/app-mobile/plugins/PluginRunner/types.ts b/packages/app-mobile/plugins/PluginRunner/types.ts index 53b0acd7918..10eba3bee2c 100644 --- a/packages/app-mobile/plugins/PluginRunner/types.ts +++ b/packages/app-mobile/plugins/PluginRunner/types.ts @@ -57,6 +57,7 @@ export interface DialogWebViewApi { // does not reload styles/scripts). includeCssFiles: (paths: string[])=> Promise; includeJsFiles: (paths: string[])=> Promise; + runScript: (key: string, content: string)=> Promise; setThemeCss: (css: string)=> Promise; getFormData: ()=> Promise; diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts index dcf55896eb7..b7afce51504 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -1,4 +1,4 @@ -import { dirname, basename, resolve } from 'path'; +import { dirname, basename, resolve, normalize } from 'path'; import FsDriverBase, { ReadDirStatsOptions, Stat } from '@joplin/lib/fs-driver-base'; import tarExtract, { TarExtractOptions } from './tarExtract'; import tarCreate, { TarCreateOptions } from './tarCreate'; @@ -8,7 +8,6 @@ const md5 = require('md5'); type FileHandle = { reader: ReadableStreamDefaultReader; - handle: FileSystemFileHandle; buffered: Buffer; done: boolean; }; @@ -30,6 +29,7 @@ logger.setLevel(LogLevel.Debug); export default class FsDriverWeb extends FsDriverBase { private fsRoot_: FileSystemDirectoryHandle; private directoryHandleCache_: Map = new Map(); + private virtualFiles_: Map = new Map(); private initPromise_: Promise; public constructor() { @@ -48,7 +48,7 @@ export default class FsDriverWeb extends FsDriverBase { await this.initPromise_; if (this.directoryHandleCache_.has(path)) { - logger.debug('pathToDirectoryHandle_ from cache'); + logger.debug('pathToDirectoryHandle_ from cache for', path); return this.directoryHandleCache_.get(path); } logger.debug('pathToDirectoryHandle_', 'path:', path, 'create:', create); @@ -61,13 +61,13 @@ export default class FsDriverWeb extends FsDriverBase { let handle: FileSystemDirectoryHandle; try { handle = await parent.getDirectoryHandle(folderName, { create }); + this.directoryHandleCache_.set(path, handle); } catch (error) { // TODO: Handle this better logger.warn('Error getting directory handle', error, 'for', path); handle = null; } - this.directoryHandleCache_.set(path, handle); return handle; } return this.fsRoot_; @@ -81,12 +81,11 @@ export default class FsDriverWeb extends FsDriverBase { try { return parent.getFileHandle(removeReservedWords(basename(path)), { create }); } catch (error) { + logger.warn(error, 'getting file handle at path', path); if (create) { throw error; } - logger.warn(error); - // TODO: This should return null when a file doesn't exist, but should // also report errors in other cases. return null; @@ -127,20 +126,35 @@ export default class FsDriverWeb extends FsDriverBase { } public override async remove(path: string) { + path = normalize(path); + this.directoryHandleCache_.clear(); const dirHandle = await this.pathToDirectoryHandle_(dirname(path)); if (dirHandle) { await dirHandle.removeEntry(basename(path), { recursive: true }); } else { - throw new Error(`ENOENT: Parent directory of path ${JSON.stringify(path)} does not exist.`); + console.warn(`remove: ENOENT: Parent directory of path ${JSON.stringify(path)} does not exist.`); + } + } + + public async fileAtPath(path: string) { + path = normalize(path); + + let file: File; + if (this.virtualFiles_.has(path)) { + file = this.virtualFiles_.get(path); + } else { + const handle = await this.pathToFileHandle_(path); + file = await handle.getFile(); } + return file; } public async readFile(path: string, encoding: BufferEncoding = 'utf-8') { + path = normalize(path); logger.debug('readFile', path); + const file = await this.fileAtPath(path); - const handle = await this.pathToFileHandle_(path); - const file = await handle.getFile(); if (encoding === 'utf-8' || encoding === 'utf8') { return await file.text(); } else { @@ -149,36 +163,38 @@ export default class FsDriverWeb extends FsDriverBase { } } - public async readToFile(path: string) { - const handle = await this.pathToFileHandle_(path); - return await handle.getFile(); - } - public override async open(path: string, _mode = 'r'): Promise { - const handle = await this.pathToFileHandle_(path); + const file = await this.fileAtPath(path); return { - handle, // TODO: Extra casting required by NodeJS types conflicting with DOM types. - reader: ((await handle.getFile()).stream() as unknown as ReadableStream).getReader(), + reader: (file.stream() as unknown as ReadableStream).getReader(), buffered: Buffer.from([]), done: false, }; } public override async readFileChunk(handle: FileHandle, length: number, encoding: BufferEncoding = 'base64') { - let read: Buffer; + let read: Buffer = handle.buffered; if (handle.buffered.byteLength < length && !handle.done) { const { done, value } = await handle.reader.read(); handle.done = done; - read = Buffer.concat([handle.buffered, Buffer.from(value)]); - } else { - read = handle.buffered; + if (value) { + if (read.byteLength > 0) { + read = Buffer.concat([read, value]); + } else { + read = Buffer.from(value); + } + } } const result = read.subarray(0, length); - handle.buffered = result.subarray(length); - return result.toString(encoding); + handle.buffered = read.subarray(length, result.length); + if (result.length === 0 && handle.done) { + return null; + } else { + return result.toString(encoding); + } } public override async close(handle: FileHandle) { @@ -186,14 +202,14 @@ export default class FsDriverWeb extends FsDriverBase { } public override async mkdir(path: string) { + logger.debug('mkdir', path); await this.pathToDirectoryHandle_(path, true); } public override async copy(from: string, to: string) { - const fromHandle = await this.pathToFileHandle_(from); + const fromFile = await this.fileAtPath(from); const toHandle = await this.pathToFileHandle_(to, true); - const fromFile = await fromHandle.getFile(); const writer = (await toHandle.createWritable()).getWriter(); await writer.write(fromFile); await writer.close(); @@ -202,12 +218,12 @@ export default class FsDriverWeb extends FsDriverBase { public override async stat(path: string): Promise { const dirHandle = await this.pathToDirectoryHandle_(path); const fileHandle = await this.pathToFileHandle_(path); - if (!dirHandle && !fileHandle) return null; - + const virtualFile = this.virtualFiles_.get(normalize(path)); + if (!dirHandle && !fileHandle && !virtualFile) return null; const size = await (async () => { if (dirHandle) return 0; - return (await fileHandle.getFile()).size; + return (virtualFile ?? await fileHandle.getFile()).size; })(); return { @@ -227,7 +243,7 @@ export default class FsDriverWeb extends FsDriverBase { for await (const child of dirHandle.keys()) { const childPath = `${path}/${child}`; const stat = await this.stat(childPath); - result.push(stat); + result.push({ ...stat, path: child }); if (options.recursive) { result.push(...await this.readDirStats(childPath)); } @@ -238,6 +254,10 @@ export default class FsDriverWeb extends FsDriverBase { public override async exists(path: string) { logger.debug('exists?', path); + if (this.virtualFiles_.has(normalize(path))) { + return true; + } + const parentDir = await this.pathToDirectoryHandle_(dirname(path)); if (!parentDir) return false; @@ -253,7 +273,7 @@ export default class FsDriverWeb extends FsDriverBase { } public override async md5File(path: string): Promise { - const fileData = Buffer.from(await this.readFile(path, 'base64'), 'base64'); + const fileData = Buffer.from(await (await this.fileAtPath(path)).arrayBuffer()); return md5(fileData); } @@ -278,5 +298,9 @@ export default class FsDriverWeb extends FsDriverBase { public override getAppDirectoryPath(): string { return '/app/'; } + + public createReadOnlyVirtualFile(path: string, content: File) { + this.virtualFiles_.set(normalize(path), content); + } } diff --git a/packages/app-mobile/utils/fs-driver/tarExtract.ts b/packages/app-mobile/utils/fs-driver/tarExtract.ts index 85daf86744a..1c52e3b9fa0 100644 --- a/packages/app-mobile/utils/fs-driver/tarExtract.ts +++ b/packages/app-mobile/utils/fs-driver/tarExtract.ts @@ -2,6 +2,7 @@ import { extract as tarStreamExtract } from 'tar-stream'; import { resolve, dirname } from 'path'; import shim from '@joplin/lib/shim'; import { chunkSize } from './constants'; +import { Buffer } from 'buffer'; export interface TarExtractOptions { cwd: string; diff --git a/packages/app-mobile/utils/pickDocument.ts b/packages/app-mobile/utils/pickDocument.ts index 6ff20c2ba12..8bc9b0098c1 100644 --- a/packages/app-mobile/utils/pickDocument.ts +++ b/packages/app-mobile/utils/pickDocument.ts @@ -2,6 +2,8 @@ import shim from '@joplin/lib/shim'; import DocumentPicker, { DocumentPickerResponse } from 'react-native-document-picker'; import { openDocument } from '@joplin/react-native-saf-x'; import Logger from '@joplin/utils/Logger'; +import type FsDriverWeb from './fs-driver/fs-driver-rn.web'; +import uuid from '@joplin/lib/uuid'; interface SelectedDocument { type: string; @@ -15,7 +17,38 @@ const logger = Logger.create('pickDocument'); const pickDocument = async (multiple: boolean): Promise => { let result: SelectedDocument[] = []; try { - if (shim.fsDriver().isUsingAndroidSAF()) { + if (shim.mobilePlatform() === 'web') { + await new Promise((resolve, reject) => { + const input = document.createElement('input'); + input.type = 'file'; + document.body.appendChild(input); + + input.onchange = async () => { + try { + const fsDriver = shim.fsDriver() as FsDriverWeb; + if (input.files.length > 0) { + for (const file of input.files) { + const path = `/tmp/${uuid.create()}`; + fsDriver.createReadOnlyVirtualFile(path, file); + + result.push({ + type: file.type, + mime: file.type, + uri: path, + fileName: file.name, + }); + } + } + input.remove(); + resolve(); + } catch (error) { + reject(error); + } + }; + + input.click(); + }); + } else if (shim.fsDriver().isUsingAndroidSAF()) { const openDocResult = await openDocument({ multiple }); if (!openDocResult) { throw new Error('User canceled document picker'); @@ -48,7 +81,7 @@ const pickDocument = async (multiple: boolean): Promise => { }); } } catch (error) { - if (DocumentPicker.isCancel(error) || error?.message?.includes('cancel')) { + if (DocumentPicker?.isCancel?.(error) || error?.message?.includes('cancel')) { logger.info('user has cancelled'); return []; } else { diff --git a/packages/app-mobile/utils/shim-init-react/index.web.ts b/packages/app-mobile/utils/shim-init-react/index.web.ts index a461200ccd3..656f69f13ce 100644 --- a/packages/app-mobile/utils/shim-init-react/index.web.ts +++ b/packages/app-mobile/utils/shim-init-react/index.web.ts @@ -106,7 +106,7 @@ const shimInit = () => { shim.uploadBlob = async function(url, options) { if (!options || !options.path) throw new Error('uploadBlob: source file path is missing'); - const content = await fsDriver().readToFile(options.path); + const content = await fsDriver().fileAtPath(options.path); return fetch(url, { ...options, body: content }); }; diff --git a/packages/app-mobile/web/index.html b/packages/app-mobile/web/index.html index 98bd30a4d36..12f07fbeeac 100644 --- a/packages/app-mobile/web/index.html +++ b/packages/app-mobile/web/index.html @@ -1,6 +1,10 @@ + + + Joplin + + \ No newline at end of file diff --git a/packages/app-mobile/web/serviceWorker.js b/packages/app-mobile/web/serviceWorker.js new file mode 100644 index 00000000000..66cb2e9b8b2 --- /dev/null +++ b/packages/app-mobile/web/serviceWorker.js @@ -0,0 +1,170 @@ +// From https://github.com/gzuidhof/coi-serviceworker. Allows use of SQLite. +// +// MIT License +// +// Copyright (c) 2021 Guido Zuidhof +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ +let coepCredentialless = false; +if (typeof window === 'undefined') { + self.addEventListener("install", () => self.skipWaiting()); + self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); + + self.addEventListener("message", (ev) => { + if (!ev.data) { + return; + } else if (ev.data.type === "deregister") { + self.registration + .unregister() + .then(() => { + return self.clients.matchAll(); + }) + .then(clients => { + clients.forEach((client) => client.navigate(client.url)); + }); + } else if (ev.data.type === "coepCredentialless") { + coepCredentialless = ev.data.value; + } + }); + + self.addEventListener("fetch", function (event) { + const r = event.request; + if (r.cache === "only-if-cached" && r.mode !== "same-origin") { + return; + } + + const request = (coepCredentialless && r.mode === "no-cors") + ? new Request(r, { + credentials: "omit", + }) + : r; + event.respondWith( + fetch(request) + .then((response) => { + if (response.status === 0) { + return response; + } + + const newHeaders = new Headers(response.headers); + newHeaders.set("Cross-Origin-Embedder-Policy", + coepCredentialless ? "credentialless" : "require-corp" + ); + if (!coepCredentialless) { + newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); + } + newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders, + }); + }) + .catch((e) => console.error(e)) + ); + }); + +} else { + (() => { + const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf"); + window.sessionStorage.removeItem("coiReloadedBySelf"); + const coepDegrading = (reloadedBySelf == "coepdegrade"); + + // You can customize the behavior of this script through a global `coi` variable. + const coi = { + shouldRegister: () => !reloadedBySelf, + shouldDeregister: () => false, + coepCredentialless: () => true, + coepDegrade: () => true, + doReload: () => window.location.reload(), + quiet: false, + ...window.coi + }; + + const n = navigator; + const controlling = n.serviceWorker && n.serviceWorker.controller; + + // Record the failure if the page is served by serviceWorker. + if (controlling && !window.crossOriginIsolated) { + window.sessionStorage.setItem("coiCoepHasFailed", "true"); + } + const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed"); + + if (controlling) { + // Reload only on the first failure. + const reloadToDegrade = coi.coepDegrade() && !( + coepDegrading || window.crossOriginIsolated + ); + n.serviceWorker.controller.postMessage({ + type: "coepCredentialless", + value: (reloadToDegrade || coepHasFailed && coi.coepDegrade()) + ? false + : coi.coepCredentialless(), + }); + if (reloadToDegrade) { + !coi.quiet && console.log("Reloading page to degrade COEP."); + window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade"); + coi.doReload("coepdegrade"); + } + + if (coi.shouldDeregister()) { + n.serviceWorker.controller.postMessage({ type: "deregister" }); + } + } + + // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are + // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. + if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; + + if (!window.isSecureContext) { + !coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required."); + return; + } + + // In some environments (e.g. Firefox private mode) this won't be available + if (!n.serviceWorker) { + !coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode."); + return; + } + + n.serviceWorker.register(window.document.currentScript.src).then( + (registration) => { + !coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope); + + registration.addEventListener("updatefound", () => { + !coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker."); + window.sessionStorage.setItem("coiReloadedBySelf", "updatefound"); + coi.doReload(); + }); + + // If the registration is active, but it's not controlling the page + if (registration.active && !n.serviceWorker.controller) { + !coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker."); + window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling"); + coi.doReload(); + } + }, + (err) => { + !coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err); + } + ); + })(); +} \ No newline at end of file From bfa6da68d8b4bbb2cd0473009c021b4712a50215 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 13:11:10 -0700 Subject: [PATCH 020/177] Fix test --- packages/app-mobile/jest.setup.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app-mobile/jest.setup.js b/packages/app-mobile/jest.setup.js index d7b90f27123..157f1bee877 100644 --- a/packages/app-mobile/jest.setup.js +++ b/packages/app-mobile/jest.setup.js @@ -18,6 +18,7 @@ window.setImmediate = setImmediate; shimInit({ nodeSqlite: sqlite3, + appVersion: () => require('./package.json').version, React, }); From 9dd6e8bd9e2b2f8546557b79d5986d676547978d Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 13:47:57 -0700 Subject: [PATCH 021/177] Create release builds in CI --- packages/app-mobile/package.json | 4 ++-- packages/app-mobile/web/webpack.config.js | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/app-mobile/package.json b/packages/app-mobile/package.json index f0c760ca953..8487e9d3cd5 100644 --- a/packages/app-mobile/package.json +++ b/packages/app-mobile/package.json @@ -8,8 +8,8 @@ "start": "BROWSERSLIST_IGNORE_OLD_DATA=true react-native start --reset-cache", "android": "react-native run-android", "build": "NO_FLIPPER=1 gulp build", - "web": "webpack --config ./web/webpack.config.js && cp ./web/index.html ./web/dist/ && cp ./web/serviceWorker.js ./web/dist/", - "serve-web": "webpack serve --static ./web/ --config ./web/webpack.config.js --progress", + "web": "webpack --config ./web/webpack.config.js --progress && cp ./web/index.html ./web/dist/ && cp ./web/serviceWorker.js ./web/dist/", + "serve-web": "webpack serve --mode development --static ./web/ --config ./web/webpack.config.js --progress", "tsc": "tsc --project tsconfig.json", "watch": "tsc --watch --preserveWatchOutput --project tsconfig.json", "clean": "node tools/clean.js", diff --git a/packages/app-mobile/web/webpack.config.js b/packages/app-mobile/web/webpack.config.js index f9f6c4a6a51..373eca9f9de 100644 --- a/packages/app-mobile/web/webpack.config.js +++ b/packages/app-mobile/web/webpack.config.js @@ -47,8 +47,6 @@ const imageLoaderConfiguration = { const emptyLibraryMock = path.resolve(__dirname, 'mocks/empty.js'); module.exports = { - mode: 'development', - // devtool: 'inline-cheap-source-map', target: 'web', entry: [ From fd9512f482884649dd1e8889959697457bfe9743 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 13:51:33 -0700 Subject: [PATCH 022/177] pages deploy: Checkout correct branch --- .github/workflows/deploy-github-pages.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy-github-pages.yml b/.github/workflows/deploy-github-pages.yml index bca1d3ee06d..f6fc7254118 100644 --- a/.github/workflows/deploy-github-pages.yml +++ b/.github/workflows/deploy-github-pages.yml @@ -33,6 +33,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + ref: work/mobile-web - name: Prepare yarn run: corepack enable - name: Install dependencies From 2552134d2b0d774a2c2870d38ce69e9ff0eac1c6 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 14:26:51 -0700 Subject: [PATCH 023/177] Fix test --- packages/app-mobile/jest.setup.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/app-mobile/jest.setup.js b/packages/app-mobile/jest.setup.js index 157f1bee877..5fb984f513b 100644 --- a/packages/app-mobile/jest.setup.js +++ b/packages/app-mobile/jest.setup.js @@ -7,6 +7,7 @@ const path = require('path'); const { tmpdir } = require('os'); const uuid = require('@joplin/lib/uuid').default; const sqlite3 = require('sqlite3'); +const shim = require('@joplin/lib/shim').default; const React = require('react'); require('../../jest.base-setup.js')(); @@ -62,6 +63,10 @@ jest.doMock('react-native-fs', () => { }; }); +shim.fsDriver().getCacheDirectoryPath = () => { + return tempDirectoryPath; +}; + beforeAll(async () => { await mkdir(tempDirectoryPath); }); From 2aa3e984b1bd64943c4160f4eee0bf337b08d9a7 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 14:28:10 -0700 Subject: [PATCH 024/177] Try to handle cross origin isolation failure in a better way --- packages/app-mobile/index.web.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/app-mobile/index.web.ts b/packages/app-mobile/index.web.ts index c60ea935731..540825864c4 100644 --- a/packages/app-mobile/index.web.ts +++ b/packages/app-mobile/index.web.ts @@ -7,6 +7,15 @@ require('./web/rnVectorIconsSetup.js'); AppRegistry.registerComponent('Joplin', () => Root as any); addEventListener('DOMContentLoaded', () => { + if (window.crossOriginIsolated === false) { + // Currently, reloading might help because a service worker loaded by index.html + // tries to enable crossOriginIsolated. + // TODO: Handle this in a better way. + document.body.appendChild( + document.createTextNode('Warning: crossOriginIsolated is false. SharedArrayBuffer and similar APIs may not work correctly. Try refreshing the page.'), + ); + } + AppRegistry.runApplication('Joplin', { rootTag: document.querySelector('#root'), }); From 6456142cf601dfd5a7c0b25ae0950a2b5a017fff Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 15:06:20 -0700 Subject: [PATCH 025/177] Renderer: Asset loading --- .../NoteBodyViewer/bundledJs/Renderer.test.ts | 1 + .../NoteBodyViewer/bundledJs/Renderer.ts | 7 +++- .../bundledJs/utils/addPluginAssets.ts | 37 ++++++++++++++----- .../hooks/useRerenderHandler.ts | 7 ++++ .../utils/fs-driver/fs-driver-rn.web.ts | 9 ++++- 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.test.ts b/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.test.ts index 180783c0ee7..0897fb7de5b 100644 --- a/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.test.ts +++ b/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.test.ts @@ -11,6 +11,7 @@ const defaultRendererSettings: RendererSettings = { codeTheme: 'atom-one-light.css', noteHash: '', initialScroll: 0, + readAssetFile: async (_path: string)=>'', createEditPopupSyntax: '', destroyEditPopupSyntax: '', diff --git a/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.ts b/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.ts index d6a07ab8347..d793ed18e78 100644 --- a/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.ts +++ b/packages/app-mobile/components/NoteBodyViewer/bundledJs/Renderer.ts @@ -13,6 +13,7 @@ export interface RendererSetupOptions { resourceDownloadMode: string; }; useTransferredFiles: boolean; + fsDriver: RendererFsDriver; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied pluginOptions: Record; @@ -34,6 +35,7 @@ export interface RendererSettings { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied pluginSettings: Record; requestPluginSetting: (pluginId: string, settingKey: string)=> void; + readAssetFile: (assetPath: string)=> Promise; } export interface MarkupRecord { @@ -166,7 +168,10 @@ export default class Renderer { } contentContainer.innerHTML = html; - addPluginAssets(pluginAssets); + void addPluginAssets(pluginAssets, { + inlineAssets: this.setupOptions.useTransferredFiles, + readAssetFile: settings.readAssetFile, + }); this.afterRender(settings); } diff --git a/packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/addPluginAssets.ts b/packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/addPluginAssets.ts index e55d75c02e4..6398f309b54 100644 --- a/packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/addPluginAssets.ts +++ b/packages/app-mobile/components/NoteBodyViewer/bundledJs/utils/addPluginAssets.ts @@ -5,13 +5,18 @@ type PluginAssetRecord = { }; const pluginAssetsAdded_: Record = {}; +interface Options { + inlineAssets: boolean; + readAssetFile?(path: string): Promise; +} + // Note that this function keeps track of what's been added so as not to // add the same CSS files multiple times. // // Shared with app-desktop/gui-note-viewer. // // TODO: If possible, refactor such that this function is not duplicated. -const addPluginAssets = (assets: RenderResultPluginAsset[]) => { +const addPluginAssets = async (assets: RenderResultPluginAsset[], options: Options) => { if (!assets) return; const pluginAssetsContainer = document.getElementById('joplin-container-pluginAssetsContainer'); @@ -34,14 +39,28 @@ const addPluginAssets = (assets: RenderResultPluginAsset[]) => { let element = null; - if (asset.mime === 'application/javascript') { - element = document.createElement('script'); - element.src = encodedPath; - pluginAssetsContainer.appendChild(element); - } else if (asset.mime === 'text/css') { - element = document.createElement('link'); - element.rel = 'stylesheet'; - element.href = encodedPath; + if (options.inlineAssets) { + if (asset.mime === 'application/javascript') { + element = document.createElement('script'); + } else if (asset.mime === 'text/css') { + element = document.createElement('style'); + } + + if (element) { + const assetContent = await options.readAssetFile(asset.name ?? asset.path); + element.appendChild(document.createTextNode(assetContent)); + } + } else { + if (asset.mime === 'application/javascript') { + element = document.createElement('script'); + element.src = encodedPath; + } else if (asset.mime === 'text/css') { + element = document.createElement('link'); + element.rel = 'stylesheet'; + element.href = encodedPath; + } + } + if (element) { pluginAssetsContainer.appendChild(element); } diff --git a/packages/app-mobile/components/NoteBodyViewer/hooks/useRerenderHandler.ts b/packages/app-mobile/components/NoteBodyViewer/hooks/useRerenderHandler.ts index 746593a7295..3cbce908ff2 100644 --- a/packages/app-mobile/components/NoteBodyViewer/hooks/useRerenderHandler.ts +++ b/packages/app-mobile/components/NoteBodyViewer/hooks/useRerenderHandler.ts @@ -178,6 +178,13 @@ const useRerenderHandler = (props: Props) => { setPluginSettingKeys(newPluginSettingKeys); } }, + readAssetFile: (assetPath: string) => { + if (event.cancelled) return null; + + const assetsDir = `${Setting.value('resourceDir')}/pluginAssets`; + const path = shim.fsDriver().resolveRelativePathWithinDir(assetsDir, assetPath); + return shim.fsDriver().readFile(path, 'utf-8'); + }, createEditPopupSyntax, destroyEditPopupSyntax, diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts index a3b3742a62e..17db00e8646 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -151,7 +151,14 @@ export default class FsDriverWeb extends FsDriverBase { } public override async unlink(path: string) { - return this.remove(path, { recursive: false }); + try { + return this.remove(path, { recursive: false }); + } catch (error) { + // unlink should pass even if the item doesn't exist. + if (error.name !== 'NotFoundError') { + throw error; + } + } } public async fileAtPath(path: string) { From 8a48149cac19387123e279657707d1177430042f Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 20:28:06 -0700 Subject: [PATCH 026/177] Fix Firefox plugin loading; possible Safari startup fix --- .eslintignore | 3 + .gitignore | 3 + .../utils/fs-driver/fs-driver-rn.web.ts | 225 +++----------- .../fs-driver/fs-driver-rn.web.worker.ts | 289 ++++++++++++++++++ packages/lib/fs-driver-base.ts | 7 +- packages/lib/utils/ipc/RemoteMessenger.ts | 2 +- packages/lib/utils/ipc/WindowMessenger.ts | 9 +- packages/lib/utils/ipc/WorkerMessenger.ts | 24 ++ .../lib/utils/ipc/WorkerToWindowMessenger.ts | 23 ++ packages/lib/utils/ipc/types.ts | 6 +- .../utils/mergeCallbacksAndSerializable.ts | 2 +- .../separateCallbacksFromSerializable.ts | 2 +- 12 files changed, 397 insertions(+), 198 deletions(-) create mode 100644 packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.ts create mode 100644 packages/lib/utils/ipc/WorkerMessenger.ts create mode 100644 packages/lib/utils/ipc/WorkerToWindowMessenger.ts diff --git a/.eslintignore b/.eslintignore index 67af9749b77..bb1f2dbdd46 100644 --- a/.eslintignore +++ b/.eslintignore @@ -716,6 +716,7 @@ packages/app-mobile/utils/debounce.js packages/app-mobile/utils/fs-driver/constants.js packages/app-mobile/utils/fs-driver/fs-driver-rn.js packages/app-mobile/utils/fs-driver/fs-driver-rn.web.js +packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.js packages/app-mobile/utils/fs-driver/runOnDeviceTests.js packages/app-mobile/utils/fs-driver/tarCreate.js packages/app-mobile/utils/fs-driver/tarExtract.test.js @@ -1271,6 +1272,8 @@ packages/lib/utils/ipc/RemoteMessenger.test.js packages/lib/utils/ipc/RemoteMessenger.js packages/lib/utils/ipc/TestMessenger.js packages/lib/utils/ipc/WindowMessenger.js +packages/lib/utils/ipc/WorkerMessenger.js +packages/lib/utils/ipc/WorkerToWindowMessenger.js packages/lib/utils/ipc/types.js packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.test.js packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.js diff --git a/.gitignore b/.gitignore index 27aa2ab6162..019d44af686 100644 --- a/.gitignore +++ b/.gitignore @@ -694,6 +694,7 @@ packages/app-mobile/utils/debounce.js packages/app-mobile/utils/fs-driver/constants.js packages/app-mobile/utils/fs-driver/fs-driver-rn.js packages/app-mobile/utils/fs-driver/fs-driver-rn.web.js +packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.js packages/app-mobile/utils/fs-driver/runOnDeviceTests.js packages/app-mobile/utils/fs-driver/tarCreate.js packages/app-mobile/utils/fs-driver/tarExtract.test.js @@ -1249,6 +1250,8 @@ packages/lib/utils/ipc/RemoteMessenger.test.js packages/lib/utils/ipc/RemoteMessenger.js packages/lib/utils/ipc/TestMessenger.js packages/lib/utils/ipc/WindowMessenger.js +packages/lib/utils/ipc/WorkerMessenger.js +packages/lib/utils/ipc/WorkerToWindowMessenger.js packages/lib/utils/ipc/types.js packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.test.js packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.js diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts index 17db00e8646..7e8f12da78c 100644 --- a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.ts @@ -1,10 +1,12 @@ -import { dirname, basename, resolve, normalize, join } from 'path'; -import FsDriverBase, { ReadDirStatsOptions, Stat } from '@joplin/lib/fs-driver-base'; +import { resolve, normalize } from 'path'; +import FsDriverBase, { ReadDirStatsOptions, RemoveOptions, Stat } from '@joplin/lib/fs-driver-base'; import tarExtract, { TarExtractOptions } from './tarExtract'; import tarCreate, { TarCreateOptions } from './tarCreate'; import { Buffer } from 'buffer'; import Logger, { LogLevel, TargetType } from '@joplin/utils/Logger'; -const md5 = require('md5'); +import RemoteMessenger from '@joplin/lib/utils/ipc/RemoteMessenger'; +import type { WorkerApi } from './fs-driver-rn.web.worker'; +import WorkerMessenger from '@joplin/lib/utils/ipc/WorkerMessenger'; type FileHandle = { reader: ReadableStreamDefaultReader; @@ -12,14 +14,6 @@ type FileHandle = { done: boolean; }; -const removeReservedWords = (fileName: string) => { - return fileName.replace(/(tmp)$/g, '_$1'); -}; - -const restoreReservedWords = (fileName: string) => { - return fileName.replace(/_tmp$/g, 'tmp'); -}; - declare global { interface FileSystemDirectoryHandle { entries(): AsyncIterable<[string, FileSystemFileHandle|FileSystemDirectoryHandle]>; @@ -27,88 +21,32 @@ declare global { } } -type RemoveOptions = { recursive?: boolean }; - const logger = new Logger(); logger.addTarget(TargetType.Console); logger.setLevel(LogLevel.Warn); +interface LocalWorkerApi { } + export default class FsDriverWeb extends FsDriverBase { - private fsRoot_: FileSystemDirectoryHandle; - private directoryHandleCache_: Map = new Map(); - private virtualFiles_: Map = new Map(); - private initPromise_: Promise; + private messenger_: RemoteMessenger; public constructor() { super(); - this.initPromise_ = (async () => { - try { - this.fsRoot_ = await (await navigator.storage.getDirectory()).getDirectoryHandle('joplin-web', { create: true }); - } catch (error) { - logger.warn('Failed to create fs-driver:', error); - throw error; - } - })(); - } - - private async pathToDirectoryHandle_(path: string, create = false): Promise { - await this.initPromise_; - path = resolve('/', path); - - if (path === '/') { - return this.fsRoot_; - } - if (this.directoryHandleCache_.has(path)) { - logger.debug('pathToDirectoryHandle_ from cache for', path); - return this.directoryHandleCache_.get(path); - } - logger.debug('pathToDirectoryHandle_', 'path:', path, 'create:', create); - - const parentDirs = dirname(path); - const parent = await this.pathToDirectoryHandle_(parentDirs, create); - const folderName = removeReservedWords(basename(path)); - - let handle: FileSystemDirectoryHandle; - try { - handle = await parent.getDirectoryHandle(folderName, { create }); - this.directoryHandleCache_.set(path, handle); - } catch (error) { - // TODO: Handle this better - logger.warn('Error getting directory handle', error, 'for', path, 'create:', create); - handle = null; - } - - return handle; - } - - private async pathToFileHandle_(path: string, create = false): Promise { - await this.initPromise_; - logger.debug('pathToFileHandle_', 'path:', path, 'create:', create); - const parent = await this.pathToDirectoryHandle_(dirname(path)); - if (!parent) { - throw new Error(`Can't get file handle for path ${path} -- parent doesn't exist (create: ${create}).`); - } - - try { - return parent.getFileHandle(removeReservedWords(basename(path)), { create }); - } catch (error) { - logger.warn(error, 'getting file handle at path', path, create); - if (create) { - throw new Error(`${error} while getting file at path ${path}.`); - } - - // TODO: This should return null when a file doesn't exist, but should - // also report errors in other cases. - return null; - } - } - - private async openWriteStream_(path: string, options?: FileSystemCreateWritableOptions) { - const handle = await this.pathToFileHandle_(path, true); - const writer = (await handle.createWritable(options)).getWriter(); - await writer.ready; - return { writer, handle }; + const worker = new Worker( + // Webpack has special syntax for creating a worker. It requires use + // of import.meta.url, which is prohibited by TypeScript in CommonJS + // modules. + // + // See also https://github.com/webpack/webpack/discussions/13655#discussioncomment-8382152 + // + // TODO: Remove this after migrating to ESM. + // + // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- Required for webpack build (see above) + // @ts-ignore + new URL('./fs-driver-rn.web.worker.ts', import.meta.url), + ); + this.messenger_ = new WorkerMessenger('fs-worker', worker, {}); } public override async writeFile( @@ -117,20 +55,7 @@ export default class FsDriverWeb extends FsDriverBase { encoding: BufferEncoding|'buffer' = 'base64', options?: FileSystemCreateWritableOptions, ) { - logger.debug('writeFile', path); - const { writer } = await this.openWriteStream_(path, options); - if (encoding === 'buffer') { - await writer.write(data); - } else if (data instanceof ArrayBuffer) { - throw new Error('Cannot write ArrayBuffer to file without encoding = buffer'); - } else if (encoding === 'utf-8' || encoding === 'utf8') { - const encoder = new TextEncoder(); - await writer.write(encoder.encode(data)); - } else { - await writer.write(Buffer.from(data, encoding).buffer); - } - await writer.close(); - logger.debug('writeFile done', path); + await this.messenger_.remoteApi.writeFile(path, data, encoding, options); } public override async appendFile(path: string, content: string, encoding?: BufferEncoding) { @@ -138,40 +63,17 @@ export default class FsDriverWeb extends FsDriverBase { } public override async remove(path: string, { recursive = true }: RemoveOptions = {}) { - path = normalize(path); - - this.directoryHandleCache_.clear(); - const dirHandle = await this.pathToDirectoryHandle_(dirname(path)); - - if (dirHandle) { - await dirHandle.removeEntry(basename(path), { recursive }); - } else { - console.warn(`remove: ENOENT: Parent directory of path ${JSON.stringify(path)} does not exist.`); - } + await this.messenger_.remoteApi.remove(path, { recursive }); } public override async unlink(path: string) { - try { - return this.remove(path, { recursive: false }); - } catch (error) { - // unlink should pass even if the item doesn't exist. - if (error.name !== 'NotFoundError') { - throw error; - } - } + return this.messenger_.remoteApi.unlink(path); } public async fileAtPath(path: string) { path = normalize(path); - let file: File; - if (this.virtualFiles_.has(path)) { - file = this.virtualFiles_.get(path); - } else { - const handle = await this.pathToFileHandle_(path); - file = await handle.getFile(); - } - return file; + return await this.messenger_.remoteApi.fileAtPath(path); } public async readFile(path: string, encoding: BufferEncoding = 'utf-8') { @@ -231,79 +133,23 @@ export default class FsDriverWeb extends FsDriverBase { public override async mkdir(path: string) { logger.debug('mkdir', path); - await this.pathToDirectoryHandle_(path, true); + await this.messenger_.remoteApi.mkdir(path); } public override async copy(from: string, to: string) { - const fromFile = await this.fileAtPath(from); - const toHandle = await this.pathToFileHandle_(to, true); - - const writer = (await toHandle.createWritable()).getWriter(); - await writer.write(fromFile); - await writer.close(); + await this.messenger_.remoteApi.copy(from, to); } - public override async stat(path: string, handle?: FileSystemDirectoryHandle|FileSystemFileHandle): Promise { - handle ??= await this.pathToDirectoryHandle_(path) || await this.pathToFileHandle_(path); - const virtualFile = this.virtualFiles_.get(normalize(path)); - if (!handle && !virtualFile) return null; - - const size = await (async () => { - if (handle.kind === 'directory') return 0; - return (virtualFile ?? await handle.getFile()).size; - })(); - - return { - birthtime: new Date(0), - mtime: new Date(0), - path: normalize(path), - size, - isDirectory: () => handle.kind === 'directory', - }; + public override async stat(path: string): Promise { + return await this.messenger_.remoteApi.stat(path); } public override async readDirStats(path: string, options: ReadDirStatsOptions = { recursive: false }): Promise { - const readDirStats = async (basePath: string, path: string, dirHandle?: FileSystemDirectoryHandle) => { - dirHandle ??= await this.pathToDirectoryHandle_(path); - if (!dirHandle) return null; - - const result: Stat[] = []; - try { - for await (const [childInternalName, childHandle] of dirHandle.entries()) { - const childFileName = restoreReservedWords(childInternalName); - const childPath = join(path, childFileName); - - const stat = await this.stat(childPath, childHandle); - result.push({ ...stat, path: join(basePath, childFileName) }); - - if (options.recursive && childHandle.kind === 'directory') { - const childBasePath = join(basePath, childFileName); - result.push(...await readDirStats(childBasePath, childPath, childHandle)); - } - } - } catch (error) { - throw new Error(`readDirStats error: ${error}, path: ${basePath},${path}`); - } - return result; - }; - return readDirStats('', path); + return await this.messenger_.remoteApi.readDirStats(path, options); } public override async exists(path: string) { - logger.debug('exists?', path); - - if (this.virtualFiles_.has(normalize(path))) { - return true; - } - - const parentDir = await this.pathToDirectoryHandle_(dirname(path)); - if (!parentDir) return false; - - const target = basename(path); - for await (const key of parentDir.keys()) { - if (key === target) return true; - } - return false; + return await this.messenger_.remoteApi.exists(path); } public resolve(...paths: string[]): string { @@ -311,8 +157,7 @@ export default class FsDriverWeb extends FsDriverBase { } public override async md5File(path: string): Promise { - const fileData = Buffer.from(await (await this.fileAtPath(path)).arrayBuffer()); - return md5(fileData); + return await this.messenger_.remoteApi.md5File(path); } public override async tarExtract(options: TarExtractOptions) { @@ -337,8 +182,8 @@ export default class FsDriverWeb extends FsDriverBase { return '/app/'; } - public createReadOnlyVirtualFile(path: string, content: File) { - this.virtualFiles_.set(normalize(path), content); + public async createReadOnlyVirtualFile(path: string, content: File) { + return this.messenger_.remoteApi.createReadOnlyVirtualFile(path, content); } } diff --git a/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.ts b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.ts new file mode 100644 index 00000000000..3f2cfd55e4a --- /dev/null +++ b/packages/app-mobile/utils/fs-driver/fs-driver-rn.web.worker.ts @@ -0,0 +1,289 @@ +import type { ReadDirStatsOptions, RemoveOptions, Stat } from '@joplin/lib/fs-driver-base'; +import WorkerToWindowMessenger from '@joplin/lib/utils/ipc/WorkerToWindowMessenger'; +import Logger, { LogLevel, TargetType } from '@joplin/utils/Logger'; +import { resolve, dirname, basename, normalize, join } from 'path'; +import { Buffer } from 'buffer'; +const md5 = require('md5'); + +const removeReservedWords = (fileName: string) => { + return fileName.replace(/(tmp)$/g, '_$1'); +}; + +const restoreReservedWords = (fileName: string) => { + return fileName.replace(/_tmp$/g, 'tmp'); +}; + +declare global { + interface FileSystemSyncAccessHandle { + close(): void; + truncate(to: number): void; + write(buffer: ArrayBuffer|ArrayBufferView, options?: { at: number }): void; + read(buffer: ArrayBuffer|ArrayBufferView, options?: { at: number }): number; + getSize(): number; + flush(): void; + } + + interface FileSystemFileHandle { + createSyncAccessHandle(): Promise; + } + + interface FileSystemDirectoryHandle { + entries(): AsyncIterable<[string, FileSystemFileHandle|FileSystemDirectoryHandle]>; + keys(): AsyncIterable; + } +} + +type WriteFileOptions = { keepExistingData?: boolean }; + +const logger = new Logger(); +logger.addTarget(TargetType.Console); +logger.setLevel(LogLevel.Info); + +// eslint-disable-next-line import/prefer-default-export -- This file is an entrypoint -- WorkerApi should only be used as a type. +export class WorkerApi { + private fsRoot_: FileSystemDirectoryHandle; + private directoryHandleCache_: Map = new Map(); + private virtualFiles_: Map = new Map(); + private initPromise_: Promise; + + public constructor() { + this.initPromise_ = (async () => { + try { + this.fsRoot_ = await (await navigator.storage.getDirectory()).getDirectoryHandle('joplin-web', { create: true }); + } catch (error) { + logger.warn('Failed to create fs-driver:', error); + throw error; + } + })(); + } + + private async pathToDirectoryHandle_(path: string, create = false): Promise { + await this.initPromise_; + path = resolve('/', path); + + if (path === '/') { + return this.fsRoot_; + } + + if (this.directoryHandleCache_.has(path)) { + logger.debug('pathToDirectoryHandle_ from cache for', path); + return this.directoryHandleCache_.get(path); + } + logger.debug('pathToDirectoryHandle_', 'path:', path, 'create:', create); + + const parentDirs = dirname(path); + const parent = await this.pathToDirectoryHandle_(parentDirs, create); + const folderName = removeReservedWords(basename(path)); + + let handle: FileSystemDirectoryHandle; + try { + handle = await parent.getDirectoryHandle(folderName, { create }); + this.directoryHandleCache_.set(path, handle); + } catch (error) { + // TODO: Handle this better + logger.warn('Error getting directory handle', error, 'for', path, 'create:', create); + handle = null; + } + + return handle; + } + + private async pathToFileHandle_(path: string, create = false): Promise { + await this.initPromise_; + logger.debug('pathToFileHandle_', 'path:', path, 'create:', create); + const parent = await this.pathToDirectoryHandle_(dirname(path)); + if (!parent) { + throw new Error(`Can't get file handle for path ${path} -- parent doesn't exist (create: ${create}).`); + } + + try { + return parent.getFileHandle(removeReservedWords(basename(path)), { create }); + } catch (error) { + logger.warn(error, 'getting file handle at path', path, create); + if (create) { + throw new Error(`${error} while getting file at path ${path}.`); + } + + // TODO: This should return null when a file doesn't exist, but should + // also report errors in other cases. + return null; + } + } + + public async writeFile( + path: string, + data: string|ArrayBuffer, + encoding: BufferEncoding|'buffer' = 'base64', + options?: WriteFileOptions, + ) { + logger.debug('writeFile', path); + const handle = await this.pathToFileHandle_(path, true); + const writer = await handle.createSyncAccessHandle(); + + let at = 0; + if (!options?.keepExistingData) { + writer.truncate(0); + } else { + at = writer.getSize(); + } + + if (encoding === 'buffer') { + writer.write(data as ArrayBuffer, { at }); + } else if (data instanceof ArrayBuffer) { + throw new Error('Cannot write ArrayBuffer to file without encoding = buffer'); + } else if (encoding === 'utf-8' || encoding === 'utf8') { + const encoder = new TextEncoder(); + writer.write(encoder.encode(data), { at }); + } else { + writer.write(Buffer.from(data, encoding).buffer, { at }); + } + writer.close(); + logger.debug('writeFile done', path); + } + + public async remove(path: string, { recursive = true }: RemoveOptions = {}) { + path = normalize(path); + + this.directoryHandleCache_.clear(); + + try { + const dirHandle = await this.pathToDirectoryHandle_(dirname(path)); + + if (dirHandle) { + await dirHandle.removeEntry(basename(path), { recursive }); + } else { + console.warn(`remove: ENOENT: Parent directory of path ${JSON.stringify(path)} does not exist.`); + } + } catch (error) { + // remove should pass even if the item doesn't exist. + // This matches the behavior of fs-extra's remove. + if (error.name !== 'NotFoundError') { + throw error; + } + } + } + + public async unlink(path: string) { + return await this.remove(path, { recursive: false }); + } + + public async fileAtPath(path: string) { + path = normalize(path); + + let file: File; + if (this.virtualFiles_.has(path)) { + file = this.virtualFiles_.get(path); + } else { + const handle = await this.pathToFileHandle_(path); + file = await handle.getFile(); + } + return file; + } + + public async readFile(path: string, encoding: BufferEncoding = 'utf-8') { + path = normalize(path); + logger.debug('readFile', path); + const file = await this.fileAtPath(path); + + if (encoding === 'utf-8' || encoding === 'utf8') { + return await file.text(); + } else { + const buffer = Buffer.from(await file.arrayBuffer()); + return buffer.toString(encoding); + } + } + + public async mkdir(path: string) { + logger.debug('mkdir', path); + await this.pathToDirectoryHandle_(path, true); + } + + public async copy(from: string, to: string) { + logger.debug('copy', from, to); + const toHandle = await this.pathToFileHandle_(to, true); + const fromFile = await this.fileAtPath(from); + + const writer = await toHandle.createSyncAccessHandle(); + writer.write(await fromFile.arrayBuffer()); + writer.close(); + } + + public async stat(path: string, handle?: FileSystemDirectoryHandle|FileSystemFileHandle): Promise { + handle ??= await this.pathToDirectoryHandle_(path) || await this.pathToFileHandle_(path); + const virtualFile = this.virtualFiles_.get(normalize(path)); + if (!handle && !virtualFile) return null; + + const size = await (async () => { + if (handle.kind === 'directory') return 0; + return (virtualFile ?? await handle.getFile()).size; + })(); + + return { + birthtime: new Date(0), + mtime: new Date(0), + path: normalize(path), + size, + isDirectory: () => handle.kind === 'directory', + }; + } + + public async readDirStats(path: string, options: ReadDirStatsOptions = { recursive: false }): Promise { + const readDirStats = async (basePath: string, path: string, dirHandle?: FileSystemDirectoryHandle) => { + dirHandle ??= await this.pathToDirectoryHandle_(path); + if (!dirHandle) return null; + + const result: Stat[] = []; + try { + for await (const [childInternalName, childHandle] of dirHandle.entries()) { + const childFileName = restoreReservedWords(childInternalName); + const childPath = join(path, childFileName); + + const stat = await this.stat(childPath, childHandle); + result.push({ ...stat, path: join(basePath, childFileName) }); + + if (options.recursive && childHandle.kind === 'directory') { + const childBasePath = join(basePath, childFileName); + result.push(...await readDirStats(childBasePath, childPath, childHandle)); + } + } + } catch (error) { + throw new Error(`readDirStats error: ${error}, path: ${basePath},${path}`); + } + return result; + }; + return readDirStats('', path); + } + + public async exists(path: string) { + logger.debug('exists?', path); + + if (this.virtualFiles_.has(normalize(path))) { + return true; + } + + const parentDir = await this.pathToDirectoryHandle_(dirname(path)); + if (!parentDir) return false; + + const target = basename(path); + for await (const key of parentDir.keys()) { + if (key === target) return true; + } + return false; + } + + public resolve(...paths: string[]): string { + return resolve(...paths); + } + + public async md5File(path: string): Promise { + const fileData = Buffer.from(await (await this.fileAtPath(path)).arrayBuffer()); + return md5(fileData); + } + + public async createReadOnlyVirtualFile(path: string, content: File) { + this.virtualFiles_.set(normalize(path), content); + } +} + +interface RemoteApi { } +new WorkerToWindowMessenger('fs-worker', new WorkerApi()); diff --git a/packages/lib/fs-driver-base.ts b/packages/lib/fs-driver-base.ts index 125fb45ac7b..ba97f12e70b 100644 --- a/packages/lib/fs-driver-base.ts +++ b/packages/lib/fs-driver-base.ts @@ -16,6 +16,11 @@ export interface ReadDirStatsOptions { recursive: boolean; } +export interface RemoveOptions { + recursive?: boolean; +} + + export default class FsDriverBase { public async stat(_path: string): Promise { @@ -90,7 +95,7 @@ export default class FsDriverBase { throw new Error('Not implemented: exists'); } - public async remove(_path: string): Promise { + public async remove(_path: string, _options: RemoveOptions = null): Promise { throw new Error('Not implemented: remove'); } diff --git a/packages/lib/utils/ipc/RemoteMessenger.ts b/packages/lib/utils/ipc/RemoteMessenger.ts index 8f07c10fd00..2859ac42676 100644 --- a/packages/lib/utils/ipc/RemoteMessenger.ts +++ b/packages/lib/utils/ipc/RemoteMessenger.ts @@ -470,7 +470,7 @@ export default abstract class RemoteMessenger { throw new Error('Message must be an object (is an array).'); } - if (message instanceof Blob) { + if (message instanceof Blob || message instanceof ArrayBuffer) { throw new Error('Message must be a key-value pair object.'); } diff --git a/packages/lib/utils/ipc/WindowMessenger.ts b/packages/lib/utils/ipc/WindowMessenger.ts index f06bc1634f3..b627605b8e0 100644 --- a/packages/lib/utils/ipc/WindowMessenger.ts +++ b/packages/lib/utils/ipc/WindowMessenger.ts @@ -1,11 +1,16 @@ import RemoteMessenger from './RemoteMessenger'; import { SerializableData } from './types'; +// This allows using a WindowMessenger in a web worker, with window-like objects. +const getLocalWindow = () => { + return typeof window !== 'undefined' ? window : self; +}; + export default class WindowMessenger extends RemoteMessenger { public constructor(channelId: string, private remoteWindow: Window, localApi: LocalInterface|null) { super(channelId, localApi); - window.addEventListener('message', this.handleMessageEvent); + getLocalWindow().addEventListener('message', this.handleMessageEvent); this.onReadyToReceive(); } @@ -23,6 +28,6 @@ export default class WindowMessenger extends Re } protected override onClose(): void { - window.removeEventListener('message', this.handleMessageEvent); + getLocalWindow().removeEventListener('message', this.handleMessageEvent); } } diff --git a/packages/lib/utils/ipc/WorkerMessenger.ts b/packages/lib/utils/ipc/WorkerMessenger.ts new file mode 100644 index 00000000000..af99938df28 --- /dev/null +++ b/packages/lib/utils/ipc/WorkerMessenger.ts @@ -0,0 +1,24 @@ +import RemoteMessenger from './RemoteMessenger'; +import { SerializableData } from './types'; + +export default class WorkerMessenger extends RemoteMessenger { + public constructor(channelId: string, private worker: Worker, localApi: LocalInterface|null) { + super(channelId, localApi); + + worker.addEventListener('message', this.handleMessageEvent); + + this.onReadyToReceive(); + } + + private handleMessageEvent = (event: MessageEvent) => { + void this.onMessage(event.data); + }; + + protected override postMessage(message: SerializableData): void { + this.worker.postMessage(message); + } + + protected override onClose(): void { + this.worker.removeEventListener('message', this.handleMessageEvent); + } +} diff --git a/packages/lib/utils/ipc/WorkerToWindowMessenger.ts b/packages/lib/utils/ipc/WorkerToWindowMessenger.ts new file mode 100644 index 00000000000..411d3bcd041 --- /dev/null +++ b/packages/lib/utils/ipc/WorkerToWindowMessenger.ts @@ -0,0 +1,23 @@ +import RemoteMessenger from './RemoteMessenger'; +import { SerializableData } from './types'; + +export default class WorkerToWindowMessenger extends RemoteMessenger { + public constructor(channelId: string, localApi: LocalInterface|null) { + super(channelId, localApi); + + self.addEventListener('message', this.handleMessageEvent); + this.onReadyToReceive(); + } + + private handleMessageEvent = (event: MessageEvent) => { + void this.onMessage(event.data); + }; + + protected override postMessage(message: SerializableData): void { + self.postMessage(message); + } + + protected override onClose(): void { + self.removeEventListener('message', this.handleMessageEvent); + } +} diff --git a/packages/lib/utils/ipc/types.ts b/packages/lib/utils/ipc/types.ts index 41f4b654582..be361d576b5 100644 --- a/packages/lib/utils/ipc/types.ts +++ b/packages/lib/utils/ipc/types.ts @@ -1,12 +1,14 @@ +type TransferableObjects = ArrayBuffer|Blob; + // Data that can be sent/received by a RemoteMessenger export type SerializableData = - number|boolean|string|File|undefined|null|SerializableData[]|{ readonly [key: string]: SerializableData }; + number|boolean|string|TransferableObjects|undefined|null|SerializableData[]|{ readonly [key: string]: SerializableData }; export type TransferableCallback = (...args: SerializableDataAndCallbacks[])=> Promise; export type SerializableDataAndCallbacks = - number|boolean|string|File|undefined|null|TransferableCallback|SerializableDataAndCallbacks[]|{ readonly [key: string]: SerializableDataAndCallbacks }; + number|boolean|string|TransferableObjects|undefined|null|TransferableCallback|SerializableDataAndCallbacks[]|{ readonly [key: string]: SerializableDataAndCallbacks }; export type CallbackIds = null|string|CallbackIds[]|Readonly<{ [propertyName: string]: CallbackIds; diff --git a/packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.ts b/packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.ts index d205162c392..231a99d4b4a 100644 --- a/packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.ts +++ b/packages/lib/utils/ipc/utils/mergeCallbacksAndSerializable.ts @@ -68,7 +68,7 @@ function mergeCallbacksAndSerializable( result.push(mergeCallbackAndSerializable(serializableObj[i], callbackObj[i])); } return result; - } else if (serializableObj instanceof Blob) { + } else if (serializableObj instanceof Blob || serializableObj instanceof ArrayBuffer) { return serializableObj; } else { if (Array.isArray(callbackObj)) { diff --git a/packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.ts b/packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.ts index 36a52b99098..16ce770a4b1 100644 --- a/packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.ts +++ b/packages/lib/utils/ipc/utils/separateCallbacksFromSerializable.ts @@ -43,7 +43,7 @@ const separateCallbacksFromSerializable = (data: SerializableDataAndCallbacks): } return { serializableData, callbacks }; - } else if (object instanceof Blob) { + } else if (object instanceof Blob || object instanceof ArrayBuffer) { return { serializableData: object, callbacks: [] }; } else { const argsWithoutCallbacks = Object.create(null); From 79a762265b708b001acae10734a810eea0b15e55 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sat, 22 Jun 2024 21:17:27 -0700 Subject: [PATCH 027/177] Trying to fix CI --- packages/app-mobile/utils/pickDocument.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/app-mobile/utils/pickDocument.ts b/packages/app-mobile/utils/pickDocument.ts index 8bc9b0098c1..8187d9448fb 100644 --- a/packages/app-mobile/utils/pickDocument.ts +++ b/packages/app-mobile/utils/pickDocument.ts @@ -29,7 +29,7 @@ const pickDocument = async (multiple: boolean): Promise => { if (input.files.length > 0) { for (const file of input.files) { const path = `/tmp/${uuid.create()}`; - fsDriver.createReadOnlyVirtualFile(path, file); + await fsDriver.createReadOnlyVirtualFile(path, file); result.push({ type: file.type, @@ -39,10 +39,11 @@ const pickDocument = async (multiple: boolean): Promise => { }); } } - input.remove(); resolve(); } catch (error) { reject(error); + } finally { + input.remove(); } }; From 6c70d1b49a035692c4293f5621a55e675cbc5c20 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sun, 23 Jun 2024 12:07:21 -0700 Subject: [PATCH 028/177] Better handling of case where no document is selected --- packages/app-mobile/utils/pickDocument.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/app-mobile/utils/pickDocument.ts b/packages/app-mobile/utils/pickDocument.ts index 8187d9448fb..692ba2aba98 100644 --- a/packages/app-mobile/utils/pickDocument.ts +++ b/packages/app-mobile/utils/pickDocument.ts @@ -21,6 +21,7 @@ const pickDocument = async (multiple: boolean): Promise => { await new Promise((resolve, reject) => { const input = document.createElement('input'); input.type = 'file'; + input.style.display = 'none'; document.body.appendChild(input); input.onchange = async () => { @@ -47,6 +48,11 @@ const pickDocument = async (multiple: boolean): Promise => { } }; + input.oncancel = () => { + input.remove(); + resolve(); + }; + input.click(); }); } else if (shim.fsDriver().isUsingAndroidSAF()) { From eff8a981fa0b5db5741de8f95c2426aa44cceb52 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Sun, 23 Jun 2024 12:09:22 -0700 Subject: [PATCH 029/177] Attempt to improve container sizing when loaded in a mobile browser --- packages/app-mobile/web/index.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/app-mobile/web/index.html b/packages/app-mobile/web/index.html index 5fb588cb05f..ca66bd5a129 100644 --- a/packages/app-mobile/web/index.html +++ b/packages/app-mobile/web/index.html @@ -11,8 +11,12 @@ window.exports = {}; + + +
+

⚠️ Error: Only one copy of Joplin can be open at a time ⚠️

+

+ At present, the Joplin web client only supports one open copy at a time in the same browser. This restriction is present to keep your data safe. +

+

+ To use Joplin in this tab, please tabs, + then this page. +

+
+ + + \ No newline at end of file diff --git a/packages/app-mobile/web/public/serviceWorker.js b/packages/app-mobile/web/public/serviceWorker.js deleted file mode 100644 index 40f7dab1acf..00000000000 --- a/packages/app-mobile/web/public/serviceWorker.js +++ /dev/null @@ -1,200 +0,0 @@ -// From https://github.com/gzuidhof/coi-serviceworker. This script enables the necessary -// headers on GitHub pages to allow the use of SQLite. It has been modified to add support -// for using the app while offline. -// -// MIT License -// -// Copyright (c) 2021 Guido Zuidhof -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - -/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ -let coepCredentialless = false; -if (typeof window === 'undefined') { - self.addEventListener("install", () => self.skipWaiting()); - self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); - - self.addEventListener("message", (ev) => { - if (!ev.data) { - return; - } else if (ev.data.type === "deregister") { - self.registration - .unregister() - .then(() => { - return self.clients.matchAll(); - }) - .then(clients => { - clients.forEach((client) => client.navigate(client.url)); - }); - } else if (ev.data.type === "coepCredentialless") { - coepCredentialless = ev.data.value; - } - }); - - self.addEventListener("fetch", function (event) { - const r = event.request; - const needsExtraHeaders = r.cache !== "only-if-cached" || r.mode === "same-origin"; - - const request = (coepCredentialless && r.mode === "no-cors" && !needsExtraHeaders) - ? new Request(r, { - credentials: "omit", - }) - : r; - - // Joplin modification: Always call event.respondWith to allow caching. - event.respondWith((async () => { - const cache = await caches.open('v1'); - try { - let response = await fetch(request); - - if (response.status !== 0 && needsExtraHeaders) { - const newHeaders = new Headers(response.headers); - newHeaders.set("Cross-Origin-Embedder-Policy", - coepCredentialless ? "credentialless" : "require-corp" - ); - if (!coepCredentialless) { - newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); - } - newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); - - response = new Response(response.body, { - status: response.status, - statusText: response.statusText, - headers: newHeaders, - }); - } - - - // Joplin modification: Store the response in the cache to support offline mode - try { - if ( - request.method === 'GET' && - response.ok && - ( - event.request.url?.match(/\.(js|css|wasm|json|ttf|html|png)$/) || - // Also cache HTML responses (e.g. for index.html, when requested with a directory - // URL). - (response.headers?.get('Content-Type') ?? '').startsWith('text/html') - ) - ) { - console.log('Service worker: cached', event.request.url); - cache.put(request, response.clone()); - } - } catch (error) { - console.warn('Failed to save ', event.request?.url, 'to the cache. Error: ', error); - } - - return response; - } catch (error) { - console.error('ERROR requesting', event.request.url, ':', error); - // Joplin modification: Restore from the cache to support offline mode. - return cache.match(request); - } - })()); - }); - -} else { - (() => { - const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf"); - window.sessionStorage.removeItem("coiReloadedBySelf"); - const coepDegrading = (reloadedBySelf == "coepdegrade"); - - // You can customize the behavior of this script through a global `coi` variable. - const coi = { - shouldRegister: () => !reloadedBySelf, - shouldDeregister: () => false, - coepCredentialless: () => true, - coepDegrade: () => true, - doReload: () => window.location.reload(), - quiet: false, - ...window.coi - }; - - const n = navigator; - const controlling = n.serviceWorker && n.serviceWorker.controller; - - // Record the failure if the page is served by serviceWorker. - if (controlling && !window.crossOriginIsolated) { - window.sessionStorage.setItem("coiCoepHasFailed", "true"); - } - const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed"); - - if (controlling) { - // Reload only on the first failure. - const reloadToDegrade = coi.coepDegrade() && !( - coepDegrading || window.crossOriginIsolated - ); - n.serviceWorker.controller.postMessage({ - type: "coepCredentialless", - value: (reloadToDegrade || coepHasFailed && coi.coepDegrade()) - ? false - : coi.coepCredentialless(), - }); - if (reloadToDegrade) { - !coi.quiet && console.log("Reloading page to degrade COEP."); - window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade"); - coi.doReload("coepdegrade"); - } - - if (coi.shouldDeregister()) { - n.serviceWorker.controller.postMessage({ type: "deregister" }); - } - } - - // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are - // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. - // - // Joplin modification: Always register the service worker. - // if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; - - if (!window.isSecureContext) { - !coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required."); - return; - } - - // In some environments (e.g. Firefox private mode) this won't be available - if (!n.serviceWorker) { - !coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode."); - return; - } - - n.serviceWorker.register(window.document.currentScript.src).then( - (registration) => { - !coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope); - - registration.addEventListener("updatefound", () => { - !coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker."); - window.sessionStorage.setItem("coiReloadedBySelf", "updatefound"); - coi.doReload(); - }); - - // If the registration is active, but it's not controlling the page - if (registration.active && !n.serviceWorker.controller) { - !coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker."); - window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling"); - coi.doReload(); - } - }, - (err) => { - !coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err); - } - ); - })(); -} \ No newline at end of file diff --git a/packages/app-mobile/web/serviceWorker.ts b/packages/app-mobile/web/serviceWorker.ts new file mode 100644 index 00000000000..2afa3aeede6 --- /dev/null +++ b/packages/app-mobile/web/serviceWorker.ts @@ -0,0 +1,262 @@ +/* eslint-disable no-console */ + +// This is a type-only import that gives access to ServiceWorker types. +// For this to work with Webpack, an import alias for 'serviceworker' may +// also be present in webpack.config.js. +import 'serviceworker'; + +// From https://github.com/gzuidhof/coi-serviceworker. This script enables the necessary +// headers on GitHub pages to allow the use of SQLite. It has been modified and refactored +// to add support for using the app while offline. +// +// eslint-disable-next-line multiline-comment-style -- Preserve license +/* ! + * @license + * + * Based on coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT + * + * MIT License + * + * Copyright (c) 2021 Guido Zuidhof + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +let coepCredentialless = false; +if (typeof window === 'undefined') { + self.addEventListener('install', () => self.skipWaiting()); + self.addEventListener('activate', (event: ExtendableEvent) => event.waitUntil(self.clients.claim())); + + const mainPageBasePath = new URL(self.location.href ?? '').pathname.replace(/\/.*\.js$/, '/'); + const waitingForClientPath = `${mainPageBasePath}just-one-client.html`; + const mainPagePaths = [mainPageBasePath, `${mainPageBasePath}index.html`]; + + const isJoplinWebClientPage = (client: WindowClient) => { + const clientUrl = new URL(client.url); + return mainPagePaths.includes(clientUrl.pathname); + }; + + self.addEventListener('message', async (ev) => { + if (!ev.data) { + return; + } else if (ev.data.type === 'deregister') { + await self.registration.unregister(); + + const clients = await self.clients.matchAll(); + for (const client of clients) { + if (client instanceof WindowClient) { + void client.navigate(client.url); + } + } + } else if (ev.data.type === 'coepCredentialless') { + coepCredentialless = ev.data.value; + } else if (ev.data.type === 'closeAllJoplinWebTabs') { + for (const client of await self.clients.matchAll()) { + if (client instanceof WindowClient && isJoplinWebClientPage(client)) { + void client.navigate(`${mainPageBasePath}closed.html`); + } + } + } + }); + + self.addEventListener('fetch', (event: FetchEvent) => { + const originalRequest = event.request; + const needsExtraHeaders = originalRequest.cache !== 'only-if-cached' || originalRequest.mode === 'same-origin'; + + const request = (coepCredentialless && originalRequest.mode === 'no-cors' && !needsExtraHeaders) + ? new Request(originalRequest, { + credentials: 'omit', + }) + : originalRequest; + + // Joplin modification: Redirect users to prevent multiple clients from being open at the same time. + const handleRedirects = async (event: FetchEvent) => { + const targetUrl = new URL(request.url); + + const redirectable = [...mainPagePaths, waitingForClientPath].includes(targetUrl.pathname) && self.location.origin === targetUrl.origin; + if (redirectable) { + const allClients = await self.clients.matchAll({ includeUncontrolled: true }); + + let hasLockingClient = false; + for (const client of allClients) { + if (!(client instanceof WindowClient)) continue; + + const clientUrl = new URL(client.url); + if (mainPagePaths.includes(clientUrl.pathname) && event.resultingClientId !== client.id && !client.focused) { + hasLockingClient = true; + } + } + + let redirectUrl = null; + if (targetUrl.pathname === waitingForClientPath && !hasLockingClient) { + redirectUrl = `${self.location.origin}${mainPageBasePath}`; + } else if (targetUrl.pathname !== waitingForClientPath && hasLockingClient) { + redirectUrl = `${self.location.origin}${waitingForClientPath}`; + } + + if (redirectUrl) { + return new Response(`Redirecting to ${redirectUrl}...`, { status: 302, headers: new Headers({ 'Location': redirectUrl }) }); // 302 = Found + } + } + return null; + }; + + const withExtraResponseHeaders = (response: Response) => { + if (response.status !== 0 && needsExtraHeaders) { + const newHeaders = new Headers(response.headers); + newHeaders.set('Cross-Origin-Embedder-Policy', + coepCredentialless ? 'credentialless' : 'require-corp', + ); + if (!coepCredentialless) { + newHeaders.set('Cross-Origin-Resource-Policy', 'cross-origin'); + } + newHeaders.set('Cross-Origin-Opener-Policy', 'same-origin'); + + response = new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders, + }); + } + return response; + }; + + const cacheResponse = (cache: Cache, response: Response) => { + try { + if ( + request.method === 'GET' && + response.ok && + ( + event.request.url?.match(/\.(js|css|wasm|json|ttf|html|png)$/) || + // Also cache HTML responses (e.g. for index.html, when requested with a directory + // URL). + (response.headers?.get('Content-Type') ?? '').startsWith('text/html') + ) + ) { + console.log('Service worker: cached', event.request.url); + void cache.put(request, response.clone()); + } + } catch (error) { + console.warn('Failed to save ', event.request?.url, 'to the cache. Error: ', error); + } + }; + + event.respondWith((async () => { + const redirectResponse = await handleRedirects(event); + if (redirectResponse) { + return redirectResponse; + } + + const cache = await caches.open('v1'); + try { + const response = withExtraResponseHeaders(await fetch(request)); + + // Joplin modification: Store the response in the cache to support offline mode + cacheResponse(cache, response); + + return response; + } catch (error) { + console.error('ERROR requesting', event.request.url, ':', error); + // Joplin modification: Restore from the cache to support offline mode. + return cache.match(request); + } + })()); + }); + +} else { + void (async () => { + const reloadedBySelf = window.sessionStorage.getItem('coiReloadedBySelf'); + window.sessionStorage.removeItem('coiReloadedBySelf'); + const coepDegrading = reloadedBySelf === 'coepDegrade'; + + // You can customize the behavior of this script through a global `coi` variable. + const coi = { + shouldRegister: () => !reloadedBySelf, + shouldDeregister: () => false, + coepCredentialless: () => true, + coepDegrade: () => true, + doReload: () => window.location.reload(), + quiet: false, + }; + + const n = navigator; + const controlling = n.serviceWorker && n.serviceWorker.controller; + + // Record the failure if the page is served by serviceWorker. + if (controlling && !window.crossOriginIsolated) { + window.sessionStorage.setItem('coiCoepHasFailed', 'true'); + } + const coepHasFailed = window.sessionStorage.getItem('coiCoepHasFailed'); + + if (controlling) { + // Reload only on the first failure. + const reloadToDegrade = coi.coepDegrade() && !( + coepDegrading || window.crossOriginIsolated + ); + n.serviceWorker.controller.postMessage({ + type: 'coepCredentialless', + value: (reloadToDegrade || coepHasFailed && coi.coepDegrade()) + ? false + : coi.coepCredentialless(), + }); + if (reloadToDegrade) { + !coi.quiet && console.log('Reloading page to degrade COEP.'); + window.sessionStorage.setItem('coiReloadedBySelf', 'coepDegrade'); + coi.doReload(); + } + + if (coi.shouldDeregister()) { + n.serviceWorker.controller.postMessage({ type: 'deregister' }); + } + } + + // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are + // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. + // + // Joplin modification: Always register the service worker. + // if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; + + if (!window.isSecureContext) { + !coi.quiet && console.log('COOP/COEP Service Worker not registered, a secure context is required.'); + return; + } + + // In some environments (e.g. Firefox private mode) this won't be available + if (!n.serviceWorker) { + !coi.quiet && console.error('COOP/COEP Service Worker not registered, perhaps due to private mode.'); + return; + } + + const registration = await n.serviceWorker.register(window.document.currentScript.getAttribute('src')); + !coi.quiet && console.log('COOP/COEP Service Worker registered', registration.scope); + + registration.addEventListener('updatefound', () => { + !coi.quiet && console.log('Reloading page to make use of updated COOP/COEP Service Worker.'); + window.sessionStorage.setItem('coiReloadedBySelf', 'updatefound'); + coi.doReload(); + }); + + // If the registration is active, but it's not controlling the page + if (registration.active && !n.serviceWorker.controller) { + !coi.quiet && console.log('Reloading page to make use of COOP/COEP Service Worker.'); + window.sessionStorage.setItem('coiReloadedBySelf', 'notControlling'); + coi.doReload(); + } + })(); +} diff --git a/packages/app-mobile/web/webpack.config.js b/packages/app-mobile/web/webpack.config.js index f16f6bcdeb2..6481fbcc24f 100644 --- a/packages/app-mobile/web/webpack.config.js +++ b/packages/app-mobile/web/webpack.config.js @@ -49,16 +49,14 @@ const emptyLibraryMock = path.resolve(__dirname, 'mocks/empty.js'); module.exports = { target: 'web', - entry: [ - // load any web API polyfills - // path.resolve(appDirectory, 'polyfills-web.js'), - // your web-specific entry file - path.resolve(appDirectory, 'index.web.ts'), - ], + entry: { + app: path.resolve(appDirectory, 'index.web.ts'), + serviceWorker: path.resolve(appDirectory, 'web/serviceWorker.ts'), + }, // configures where the build ends up output: { - filename: 'bundle.web.js', + filename: '[name].bundle.js', path: path.resolve(appDirectory, 'web/dist'), }, @@ -86,6 +84,11 @@ module.exports = { 'react-native-zip-archive': emptyLibraryMock, 'react-native-document-picker': emptyLibraryMock, 'react-native-exit-app': emptyLibraryMock, + + // Workaround for applying serviceworker types to a single file. + // See https://joshuatz.com/posts/2021/strongly-typed-service-workers/. + // See https://github.com/microsoft/TypeScript/issues/37053 + 'serviceworker': emptyLibraryMock, }, // If you're working on a multi-platform React Native app, web-specific // module implementations should be written in files using the extension diff --git a/packages/tools/cspell/dictionary4.txt b/packages/tools/cspell/dictionary4.txt index 8bf7ad622ae..254a3e6ea7e 100644 --- a/packages/tools/cspell/dictionary4.txt +++ b/packages/tools/cspell/dictionary4.txt @@ -113,3 +113,8 @@ Ionicon onready opfs popups +Zuidhof +serviceworker +Credentialless +coepdegrade +COEP diff --git a/yarn.lock b/yarn.lock index 108384e1865..f91b3cf4e19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7471,6 +7471,7 @@ __metadata: "@types/react": 18.2.58 "@types/react-native": 0.70.6 "@types/react-redux": 7.1.33 + "@types/serviceworker": ^0.0.88 "@types/tar-stream": 3.1.3 assert-browserify: 2.0.0 babel-jest: 29.7.0 @@ -12470,6 +12471,13 @@ __metadata: languageName: node linkType: hard +"@types/serviceworker@npm:^0.0.88": + version: 0.0.88 + resolution: "@types/serviceworker@npm:0.0.88" + checksum: 2ce6bb34f7d488e1beeed7f42a943d75e033f7df5992d4c23b12ed3afd1696d634213cbbfe7089f7466e783fc454652b53ad6ac2925e559acf885e9e1e6b5576 + languageName: node + linkType: hard + "@types/sockjs@npm:^0.3.33": version: 0.3.35 resolution: "@types/sockjs@npm:0.3.35" From 722fdeb1e1bfec497a716ff458d4692af8a92001 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Thu, 27 Jun 2024 15:31:55 -0700 Subject: [PATCH 094/177] Add the closed.html page --- packages/app-mobile/web/public/closed.html | 18 ++++++++ packages/app-mobile/web/public/info-page.css | 32 ++++++++++++++ .../web/public/just-one-client.html | 44 ++----------------- 3 files changed, 53 insertions(+), 41 deletions(-) create mode 100644 packages/app-mobile/web/public/closed.html create mode 100644 packages/app-mobile/web/public/info-page.css diff --git a/packages/app-mobile/web/public/closed.html b/packages/app-mobile/web/public/closed.html new file mode 100644 index 00000000000..2bad2688089 --- /dev/null +++ b/packages/app-mobile/web/public/closed.html @@ -0,0 +1,18 @@ + + + + + + + + Closed - Joplin + + + + +
+

Closed externally

+

The Joplin Web Client has been closed by a client in another tab.

+
+ + \ No newline at end of file diff --git a/packages/app-mobile/web/public/info-page.css b/packages/app-mobile/web/public/info-page.css new file mode 100644 index 00000000000..59acc6b1cec --- /dev/null +++ b/packages/app-mobile/web/public/info-page.css @@ -0,0 +1,32 @@ +/* Styles secondary pages (e.g. 404, just-one-client). Not intended for the primary + web client page. */ + +:root { + --bg-secondary: white; + --bg-primary: #f7f7f8; + --fg-primary: black; + background-color: var(--bg-secondary); +} + +main { + max-width: 500px; + background-color: var(--bg-primary); + color: var(--fg-primary); + font-family: sans-serif; + + padding: 14px; + padding-left: 32px; + padding-right: 32px; + + border-radius: 14px; + margin-left: auto; + margin-right: auto; +} + +@media screen and (prefers-color-scheme: dark) { + :root { + --bg-secondary: black; + --bg-primary: #111; + --fg-primary: #fafaff; + } +} \ No newline at end of file diff --git a/packages/app-mobile/web/public/just-one-client.html b/packages/app-mobile/web/public/just-one-client.html index 045da20694b..9e5db843f8a 100644 --- a/packages/app-mobile/web/public/just-one-client.html +++ b/packages/app-mobile/web/public/just-one-client.html @@ -7,47 +7,9 @@ - Joplin + Error - Joplin - - - +
@@ -56,7 +18,7 @@

⚠️ Error: Only one copy of Joplin can be open at a time ⚠️

At present, the Joplin web client only supports one open copy at a time in the same browser. This restriction is present to keep your data safe.

- To use Joplin in this tab, please tabs, + To use Joplin in this tab, please , then this page.

From c498b843fc1aeb645588b9f5ef65eaf7c4aed997 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Thu, 27 Jun 2024 15:51:33 -0700 Subject: [PATCH 095/177] Fix image resize logic --- packages/app-mobile/utils/image/resizeImage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app-mobile/utils/image/resizeImage.ts b/packages/app-mobile/utils/image/resizeImage.ts index 05802593e10..dc76fd7ba2a 100644 --- a/packages/app-mobile/utils/image/resizeImage.ts +++ b/packages/app-mobile/utils/image/resizeImage.ts @@ -26,8 +26,8 @@ const resizeImage = async (options: Options) => { // Choose a scale factor such that the resized image fits within a // maxWidth x maxHeight box. const scale = Math.min( - image.image.width / options.maxWidth, - image.image.height / options.maxHeight, + options.maxWidth / image.image.width, + options.maxHeight / image.image.height, ); canvas.width = image.image.width * scale; canvas.height = image.image.height * scale; From 3b37776b88fb89a00887b415b1aaccb48e11dc94 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Thu, 27 Jun 2024 16:12:41 -0700 Subject: [PATCH 096/177] Fix service worker doesn't ensure that only one client is running when served from a subdirectory of the root domain. --- packages/app-mobile/web/public/just-one-client.html | 2 +- packages/app-mobile/web/serviceWorker.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/app-mobile/web/public/just-one-client.html b/packages/app-mobile/web/public/just-one-client.html index 9e5db843f8a..c621db42f31 100644 --- a/packages/app-mobile/web/public/just-one-client.html +++ b/packages/app-mobile/web/public/just-one-client.html @@ -31,7 +31,7 @@

⚠️ Error: Only one copy of Joplin can be open at a time ⚠️

for (const closeLink of document.querySelectorAll('#close-all-other-web-clients')) { if (navigator.serviceWorker) { closeLink.onclick = () => { - window.navigator.serviceWorker.controller.postMessage({ type: 'close-all-web-client-tabs' }); + window.navigator.serviceWorker.controller.postMessage({ type: 'closeAllJoplinWebTabs' }); }; } else { closeLink.replaceWith(document.createTextNode(closeLink.textContent)); diff --git a/packages/app-mobile/web/serviceWorker.ts b/packages/app-mobile/web/serviceWorker.ts index 2afa3aeede6..3cfd82e0810 100644 --- a/packages/app-mobile/web/serviceWorker.ts +++ b/packages/app-mobile/web/serviceWorker.ts @@ -43,7 +43,8 @@ if (typeof window === 'undefined') { self.addEventListener('install', () => self.skipWaiting()); self.addEventListener('activate', (event: ExtendableEvent) => event.waitUntil(self.clients.claim())); - const mainPageBasePath = new URL(self.location.href ?? '').pathname.replace(/\/.*\.js$/, '/'); + const serviceWorkerPath = new URL(self.location.href ?? '').pathname; + const mainPageBasePath = serviceWorkerPath.replace(/\/[^/]+$/, '/'); const waitingForClientPath = `${mainPageBasePath}just-one-client.html`; const mainPagePaths = [mainPageBasePath, `${mainPageBasePath}index.html`]; From 6a50bbb5eaa9849117a448ed2eeebe09c05814f5 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Thu, 27 Jun 2024 16:25:30 -0700 Subject: [PATCH 097/177] iOS+Safari: Fix note editor scrolls partially out of view when editing near its end --- packages/app-mobile/index.web.ts | 8 ++++++-- packages/app-mobile/web/public/index.html | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/app-mobile/index.web.ts b/packages/app-mobile/index.web.ts index 7845de8ac4f..6243c764654 100644 --- a/packages/app-mobile/index.web.ts +++ b/packages/app-mobile/index.web.ts @@ -35,9 +35,13 @@ const keepAppAboveKeyboard = () => { // The visual viewport changes as the user zooms in and out. Only adjust the body's height // when the user's zoom level is 100%. if (window.visualViewport.scale === 1) { - document.documentElement.style.height = `${window.visualViewport.height}px`; + document.body.style.height = `${window.visualViewport.height}px`; + + // Additional scroll space can also be added by the browser when focusing a text input (e.g. + // the markdown editor). Make sure that the top of the editor is still visible: + document.scrollingElement.scrollTop = 0; } else { - document.documentElement.style.height = ''; + document.body.style.height = ''; } }); }; diff --git a/packages/app-mobile/web/public/index.html b/packages/app-mobile/web/public/index.html index 9a31a749531..79e6da4223f 100644 --- a/packages/app-mobile/web/public/index.html +++ b/packages/app-mobile/web/public/index.html @@ -2,7 +2,7 @@ - + From c9e36a93006ea8ca16fc6b04cfcf569387668964 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Thu, 27 Jun 2024 21:28:03 -0700 Subject: [PATCH 098/177] Hide the "share" option on unsupported platforms --- packages/app-mobile/components/screens/Note.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/app-mobile/components/screens/Note.tsx b/packages/app-mobile/components/screens/Note.tsx index 5390423c848..4eb8dac868b 100644 --- a/packages/app-mobile/components/screens/Note.tsx +++ b/packages/app-mobile/components/screens/Note.tsx @@ -1211,13 +1211,16 @@ class NoteScreenComponent extends BaseScreenComponent implements B }); } - output.push({ - title: _('Share'), - onPress: () => { - void this.share_onPress(); - }, - disabled: readOnly, - }); + const shareSupported = Platform.OS !== 'web' || !!navigator.share; + if (shareSupported) { + output.push({ + title: _('Share'), + onPress: () => { + void this.share_onPress(); + }, + disabled: readOnly, + }); + } // Voice typing is enabled only for French language and on Android for now if (voskEnabled && shim.mobilePlatform() === 'android' && isSupportedLanguage(currentLocale())) { From 8d3de9924824dacbd592f5c3b9c7893c81e85edf Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Thu, 27 Jun 2024 22:15:21 -0700 Subject: [PATCH 099/177] Fix broken alarm dialog. (Still no support for showing notifications). --- .../components/SelectDateTimeDialog.tsx | 75 +++++++++++-------- .../app-mobile/components/screens/Note.tsx | 23 +++--- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/packages/app-mobile/components/SelectDateTimeDialog.tsx b/packages/app-mobile/components/SelectDateTimeDialog.tsx index dfef3945f68..f7bae1050a2 100644 --- a/packages/app-mobile/components/SelectDateTimeDialog.tsx +++ b/packages/app-mobile/components/SelectDateTimeDialog.tsx @@ -1,8 +1,11 @@ import * as React from 'react'; import { themeStyle } from './global-style'; import { _ } from '@joplin/lib/locale'; -const { Modal, View, Button, Text, StyleSheet } = require('react-native'); +const { View, Button, Text, StyleSheet } = require('react-native'); import time from '@joplin/lib/time'; +import { Platform } from 'react-native'; +import Modal from './Modal'; +import { formatMsToLocal } from '@joplin/utils/time'; const DateTimePickerModal = require('react-native-modal-datetime-picker').default; const styles = StyleSheet.create({ @@ -10,7 +13,6 @@ const styles = StyleSheet.create({ flex: 1, justifyContent: 'center', alignItems: 'center', - marginTop: 22, }, modalView: { display: 'flex', @@ -100,9 +102,26 @@ export default class SelectDateTimeDialog extends React.PureComponent this.setState({ showPicker: true }); } + // web + private onInputChange = (event: React.ChangeEvent) => { + this.setState({ date: new Date(event.target.value) }); + }; + public renderContent() { const theme = themeStyle(this.props.themeId); + // DateTimePickerModal doesn't support web. + if (Platform.OS === 'web') { + // See https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats#local_date_and_time_strings + // for the expected date input format: + const dateString = this.state.date ? formatMsToLocal(this.state.date.getTime(), 'YYYY-MM-DD[T]HH:mm:ss') : ''; + return ; + } + return ( @@ -129,36 +148,32 @@ export default class SelectDateTimeDialog extends React.PureComponent const theme = themeStyle(this.props.themeId); return ( - - { - this.onReject(); - }} - > - - - - {_('Set alarm')} - - {this.renderContent()} - - -