diff --git a/packages/opvault.js/package.json b/packages/opvault.js/package.json index ae72857..7ccae87 100644 --- a/packages/opvault.js/package.json +++ b/packages/opvault.js/package.json @@ -8,6 +8,7 @@ "build:docs": "typedoc --out docs src/index.ts --excludePrivate" }, "dependencies": { + "buffer": "^6.0.3", "tiny-invariant": "1.2.0", "tslib": "2.3.1" }, diff --git a/packages/opvault.js/src/adapters/browser.ts b/packages/opvault.js/src/adapters/browser.ts index 2410a51..24a9a7a 100644 --- a/packages/opvault.js/src/adapters/browser.ts +++ b/packages/opvault.js/src/adapters/browser.ts @@ -14,11 +14,6 @@ function splitPath(path: string) { export class FileSystem implements IFileSystem { constructor(private handle: FileSystemDirectoryHandle) {} - static async create() { - const handle = await showDirectoryPicker() - return new FileSystem(handle) - } - private async getDirectoryHandle(segments: string[]) { if (!segments.length || (segments.length === 1 && !segments[0])) { return this.handle diff --git a/packages/opvault.js/src/adapters/webkit.ts b/packages/opvault.js/src/adapters/webkit.ts new file mode 100644 index 0000000..ef294b5 --- /dev/null +++ b/packages/opvault.js/src/adapters/webkit.ts @@ -0,0 +1,53 @@ +import { Buffer } from "buffer" +import type { IAdapter, IFileSystem } from "./index" + +export class FileSystem implements IFileSystem { + private paths = new Set() + private pathMap = new Map() + + constructor(list: FileList) { + for (const file of list) { + this.pathMap.set(file.webkitRelativePath, file) + this.paths.add(file.webkitRelativePath) + } + } + + async readFile(path: string) { + return this.pathMap.get(path)!.text() + } + + async exists(path: string): Promise { + return this.pathMap.has(path) + } + + async readBuffer(path: string): Promise { + const arrayBuffer = await this.pathMap.get(path)!.arrayBuffer() + return Buffer.from(arrayBuffer) + } + + // eslint-disable-next-line class-methods-use-this + async writeFile(path: string, data: string): Promise { + throw new Error("fs.writeFile is not supported with webkitdirectory") + } + + async readdir(path: string): Promise { + const paths = [...this.paths] + return paths + .filter(_ => _.startsWith(`${path}/`)) + .map(_ => _.slice(path.length + 1)) + .map(_ => _.split("/")[0]) + } + + async isDirectory(path: string) { + const paths = [...this.paths] + return paths.some(_ => _.startsWith(`${path}/`)) && !paths.includes(path) + } +} + +/** + * Default Browser adapter. + */ +export const getBrowserAdapter = (list: FileList): IAdapter => ({ + fs: new FileSystem(list), + subtle: crypto.subtle, +}) diff --git a/packages/web/.gitignore b/packages/web/.gitignore index 6a7c926..b5f4ba9 100644 --- a/packages/web/.gitignore +++ b/packages/web/.gitignore @@ -3,3 +3,4 @@ node_modules dist bundle *.local +*.yml.d.ts diff --git a/packages/web/esbuild.js b/packages/web/esbuild.js index 706eecd..e6535d5 100755 --- a/packages/web/esbuild.js +++ b/packages/web/esbuild.js @@ -8,10 +8,7 @@ const args = process.argv.slice(2) build({ bundle: true, define: {}, - entryPoints: [ - "./src/electron/index.ts", - // "./src/electron/preload.ts" - ], + entryPoints: ["./src/electron/index.ts"], outdir: "./dist/main", external: builtinModules.concat("electron"), target: ["chrome90"], diff --git a/packages/web/logo.svg b/packages/web/logo.svg new file mode 100644 index 0000000..3b8cb29 --- /dev/null +++ b/packages/web/logo.svg @@ -0,0 +1,263 @@ + + + + Adwaita Icon Template + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + GNOME Design Team + + + + + Adwaita Icon Template + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/web/package.json b/packages/web/package.json index 4c9ca1f..9b73559 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "opvault-web", - "version": "0.0.0", + "version": "1.0.0", "main": "dist/main/index.js", "author": "proteria", "license": "GPL-3.0-only", @@ -9,13 +9,14 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "start": "NODE_ENV=development electron --enable-transparent-visuals --disable-gpu ./dist/main/index.js", + "start": "./esbuild.js && NODE_ENV=development electron --enable-transparent-visuals --disable-gpu ./dist/main/index.js", "bundle": "./scripts/build.sh" }, "devDependencies": { "@emotion/css": "^11.5.0", "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", + "@rollup/plugin-yaml": "^3.1.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@vitejs/plugin-react": "^1.0.0", @@ -23,6 +24,7 @@ "electron": "^15.2.0", "electron-builder": "^22.13.1", "esbuild": "^0.13.6", + "js-yaml": "^4.1.0", "opvault.js": "*", "path-browserify": "^1.0.1", "react": "^17.0.0", @@ -38,14 +40,19 @@ "files": [ "**/*" ], + "icon": "dist/512x512.png", "directories": { "output": "bundle", - "app": "dist" + "app": "dist", + "buildResources": "build" }, "linux": { "executableName": "opvault", - "icon": "1p.png", - "category": "Utility" + "category": "Utility", + "icon": "512x512.png", + "target": [ + "AppImage" + ] } } } diff --git a/packages/web/scripts/build-i18n-yml-typedef.js b/packages/web/scripts/build-i18n-yml-typedef.js new file mode 100755 index 0000000..0025841 --- /dev/null +++ b/packages/web/scripts/build-i18n-yml-typedef.js @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +const fs = require("fs") +const { resolve } = require("path") +const { load } = require("js-yaml") + +const ymlPath = resolve(__dirname, "../src/i18n/texts.yml") +const json = load(fs.readFileSync(ymlPath, "utf-8")) + +const dtsPath = ymlPath + ".d.ts" +fs.writeFileSync( + dtsPath, + `type Translation = Record; +declare const exportee: { + ${Object.keys(json) + .map(x => `${x}: Translation;`) + .join("\n ")} +}; +export default exportee; +` +) diff --git a/packages/web/scripts/build.sh b/packages/web/scripts/build.sh index 1c45161..0d1d640 100755 --- a/packages/web/scripts/build.sh +++ b/packages/web/scripts/build.sh @@ -1,5 +1,5 @@ #!/bin/sh -yarn build +npx vite build NODE_ENV=production ./esbuild.js ./scripts/build-package-json.js ./node_modules/.bin/electron-builder build \ No newline at end of file diff --git a/packages/web/src/components/BigTextView.tsx b/packages/web/src/components/BigTextView.tsx new file mode 100644 index 0000000..14900fc --- /dev/null +++ b/packages/web/src/components/BigTextView.tsx @@ -0,0 +1,51 @@ +import styled from "@emotion/styled" +import { memo, useEffect } from "react" + +const Container = styled.div` + background: rgba(255, 255, 255, 0.6); + backdrop-filter: blur(4px); + border-radius: 20px; + font-family: var(--monospace); + letter-spacing: 2px; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 8em; + text-align: center; + padding: 20px 25px; + word-break: break-word; + @media (prefers-color-scheme: dark) { + background: rgba(0, 0, 0, 0.6); + } +` +const Letter = styled.span` + &:nth-of-type(even) { + opacity: 0.8; + } +` + +interface BigTextViewProps { + onClose(): void + children: string +} + +export const BigTextView = memo(({ onClose, children }) => { + useEffect(() => { + const fn = (e: KeyboardEvent) => { + if (e.code === "Escape") { + onClose() + } + } + document.addEventListener("keydown", fn) + return () => document.removeEventListener("keydown", fn) + }, [onClose]) + + return ( + + {children.split("").map((letter, i) => ( + {letter} + ))} + + ) +}) diff --git a/packages/web/src/components/ItemDates.tsx b/packages/web/src/components/ItemDates.tsx index 9a7e644..3a084bf 100644 --- a/packages/web/src/components/ItemDates.tsx +++ b/packages/web/src/components/ItemDates.tsx @@ -1,5 +1,6 @@ import styled from "@emotion/styled" import type { Item } from "opvault.js" +import { useTranslate } from "../i18n" const Container = styled.div` text-align: center; @@ -8,9 +9,16 @@ const Container = styled.div` opacity: 0.5; ` -export const ItemDates: React.FC<{ item: Item }> = ({ item }) => ( - -
Last Updated: {new Date(item.updatedAt).toLocaleString()}
-
Created: {new Date(item.createdAt).toLocaleString()}
-
-) +export const ItemDates: React.FC<{ item: Item }> = ({ item }) => { + const t = useTranslate() + return ( + +
+ {t.label_last_updated}: {new Date(item.updatedAt).toLocaleString()} +
+
+ {t.label_created_at}: {new Date(item.createdAt).toLocaleString()} +
+
+ ) +} diff --git a/packages/web/src/components/ItemFieldValue.tsx b/packages/web/src/components/ItemFieldValue.tsx index 2ae469f..25188a3 100644 --- a/packages/web/src/components/ItemFieldValue.tsx +++ b/packages/web/src/components/ItemFieldValue.tsx @@ -3,6 +3,7 @@ import type { ItemSection, ItemField } from "opvault.js" import { FieldType } from "opvault.js" import { useCallback, useMemo, useState } from "react" import { parseMonthYear } from "../utils" +import { BigTextView } from "./BigTextView" import { ErrorBoundary } from "./ErrorBoundary" import { useItemFieldContextMenu } from "./ItemFieldContextMenu" @@ -12,11 +13,19 @@ const Password: React.FC<{ field: ItemSection.Concealed }> = ({ field }) => { const [show, setShow] = useState(false) + const [bigText, showBigText] = useState(false) + const { onRightClick, ContextMenuContainer, Item } = useItemFieldContextMenu() const onToggle = useCallback(() => setShow(x => !x), []) const onCopy = useCallback(() => { navigator.clipboard.writeText(field.v) }, [field.v]) + const onOpenBigText = useCallback(() => { + showBigText(true) + }, []) + const onCloseBigText = useCallback(() => { + showBigText(false) + }, []) return ( <> @@ -30,9 +39,11 @@ const Password: React.FC<{ > {show ? field.v : "·".repeat(10)} + {bigText && {field.v}} Copier {show ? "Cacher" : "Afficher"} + {!bigText && Afficher en gros caractères} ) @@ -52,6 +63,22 @@ const DateView: React.FC<{ field: ItemSection.Date }> = ({ field }) => { return {date.toLocaleDateString()} } +const TextView: React.FC<{ value: string }> = ({ value }) => { + const { onRightClick, ContextMenuContainer, Item } = useItemFieldContextMenu() + const onCopy = useCallback(() => { + navigator.clipboard.writeText(value) + }, [value]) + + return ( + <> + {value} + + Copier + + + ) +} + export const ItemFieldValue: React.FC<{ field: ItemSection.Any }> = ({ field }) => { @@ -80,7 +107,7 @@ export const ItemFieldValue: React.FC<{ return ( - {field.v} + ) } diff --git a/packages/web/src/components/VaultPicker.tsx b/packages/web/src/components/VaultPicker.tsx index bc957a0..76cb1e0 100644 --- a/packages/web/src/components/VaultPicker.tsx +++ b/packages/web/src/components/VaultPicker.tsx @@ -1,8 +1,11 @@ import { useCallback } from "react" +import { useTranslate } from "../i18n" export const VaultPicker: React.FC<{ setHandle(handle: FileSystemDirectoryHandle): void }> = ({ setHandle }) => { + const t = useTranslate() + const onClick = useCallback(async () => { try { const handle = await showDirectoryPicker() @@ -15,5 +18,5 @@ export const VaultPicker: React.FC<{ } }, [setHandle]) - return + return } diff --git a/packages/web/src/electron/index.ts b/packages/web/src/electron/index.ts index 52b6557..763404d 100644 --- a/packages/web/src/electron/index.ts +++ b/packages/web/src/electron/index.ts @@ -1,6 +1,6 @@ // @ts-check // Modules to control application life and create native browser window -// import { join } from "path" +import { join } from "path" import { app, BrowserWindow, Menu } from "electron" function createWindow() { @@ -10,6 +10,7 @@ function createWindow() { height: 650, // frame: false, // transparent: true, + icon: join(__dirname, "../512x512.png"), webPreferences: { contextIsolation: true, // preload: join(__dirname, "preload.js"), diff --git a/packages/web/src/i18n/index.tsx b/packages/web/src/i18n/index.tsx new file mode 100644 index 0000000..ad7eff4 --- /dev/null +++ b/packages/web/src/i18n/index.tsx @@ -0,0 +1,73 @@ +import { createContext, memo, useContext, useEffect, useMemo, useState } from "react" +import texts from "./texts.yml" + +type Keys = keyof typeof texts + +const ALLOWED = new Set(["en", "fr"]) +const LOCALSTORAGE_KEY = "preferred-locale" + +function getLocaleFromStorage() { + try { + const key = localStorage.getItem(LOCALSTORAGE_KEY) + if (key && ALLOWED.has(key)) { + return key + } + } catch {} +} + +function getNavigatorLocale() { + if (typeof navigator !== "undefined") { + for (const lang of navigator.languages) { + if (ALLOWED.has(lang)) { + return lang + } + } + } +} + +function getEnvLocale() { + return getLocaleFromStorage() ?? getNavigatorLocale() ?? "en" +} + +const LocaleContext = createContext<{ + locale: string + setLocale(locale: string): void +}>(undefined!) + +export const useLocaleContext = () => useContext(LocaleContext) + +export function useTranslate() { + const { locale } = useContext(LocaleContext) + const t = useMemo( + () => + new Proxy( + {}, + { + get(_, p: string) { + if ( + process.env.NODE_ENV === "development" && + !Object.prototype.hasOwnProperty.call(texts, p) + ) { + throw new Error(`t.${p} does not exist.`) + } + return (texts as any)[p][locale] + }, + } + ) as { + [key in Keys]: string + }, + [locale] + ) + return t +} + +export const LocaleContextProvider = memo(({ children }) => { + const [locale, setLocale] = useState(getEnvLocale) + useEffect(() => { + try { + localStorage.setItem(LOCALSTORAGE_KEY, locale) + } catch {} + }, [locale]) + const value = useMemo(() => ({ locale, setLocale }), [locale]) + return {children} +}) diff --git a/packages/web/src/i18n/texts.yml b/packages/web/src/i18n/texts.yml new file mode 100644 index 0000000..0cef55f --- /dev/null +++ b/packages/web/src/i18n/texts.yml @@ -0,0 +1,28 @@ +# /* spellchecker: disable */ +label_choose_a_vault: + en: Pick a vault here. + fr: Choisir un coffre ici. + +label_no_vault_selected: + en: No vault is selected. + fr: Aucun coffre n’est sélectionné. + +label_last_updated: + en: Last Updated + fr: Dernière modification + +label_created_at: + en: Created At + fr: Créé + +noun_vault: + en: vault + fr: coffre + +action_lock: + en: Lock + fr: Vérouiller + +action_unlock: + en: Unlock + fr: Déverouiller diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx index 87824ed..518a412 100644 --- a/packages/web/src/main.tsx +++ b/packages/web/src/main.tsx @@ -1,12 +1,15 @@ import React from "react" import { render } from "react-dom" import { App } from "./App" +import { LocaleContextProvider } from "./i18n" import "./index.scss" render( {/* */} - + + + , document.getElementById("root") ) diff --git a/packages/web/src/pages/PickOPVault.tsx b/packages/web/src/pages/PickOPVault.tsx index b6be0a8..a2ea524 100644 --- a/packages/web/src/pages/PickOPVault.tsx +++ b/packages/web/src/pages/PickOPVault.tsx @@ -1,8 +1,8 @@ import styled from "@emotion/styled" import { VaultPicker } from "../components/VaultPicker" +import { useTranslate } from "../i18n" const Container = styled.div` - width: 800px; padding: 100px; text-align: center; ` @@ -12,9 +12,12 @@ const Info = styled.div` export const PickOPVault: React.FC<{ setHandle(handle: FileSystemDirectoryHandle): void -}> = ({ setHandle }) => ( - - - No vault is picked. - -) +}> = ({ setHandle }) => { + const t = useTranslate() + return ( + + + {t.label_no_vault_selected} + + ) +} diff --git a/packages/web/src/pages/Unlock.tsx b/packages/web/src/pages/Unlock.tsx index ff43006..1030ca9 100644 --- a/packages/web/src/pages/Unlock.tsx +++ b/packages/web/src/pages/Unlock.tsx @@ -1,8 +1,9 @@ import type { OnePassword } from "opvault.js" import styled from "@emotion/styled" import { useCallback, useEffect, useState } from "react" +import { useTranslate } from "../i18n" -const Container = styled.div` +const Container = styled.form` padding: 20px; text-align: center; ` @@ -11,15 +12,21 @@ export const Unlock: React.FC<{ instance: OnePassword onUnlock(profile: string, password: string): void }> = ({ onUnlock, instance }) => { + const t = useTranslate() const [profiles, setProfiles] = useState(() => []) const [profile, setProfile] = useState() const [password, setPassword] = useState("") - const unlock = useCallback(() => { - if (!profile) return - onUnlock(profile, password) - setPassword("") - }, [onUnlock, profile, password]) + const unlock = useCallback( + (e: React.FormEvent) => { + e.preventDefault() + + if (!profile) return + onUnlock(profile, password) + setPassword("") + }, + [onUnlock, profile, password] + ) useEffect(() => { instance.getProfileNames().then(profiles => { @@ -29,12 +36,12 @@ export const Unlock: React.FC<{ }, [instance]) return ( - +
@@ -46,8 +53,8 @@ export const Unlock: React.FC<{ onChange={e => setPassword(e.currentTarget.value)} />
-
) diff --git a/packages/web/src/pages/Vault.tsx b/packages/web/src/pages/Vault.tsx index 87b2d5b..e7e624e 100644 --- a/packages/web/src/pages/Vault.tsx +++ b/packages/web/src/pages/Vault.tsx @@ -6,6 +6,7 @@ import { IoSearch } from "react-icons/io5" import { ItemList } from "../components/ItemList" import { ItemView } from "../components/Item" import { reactIconClass } from "../components/CategoryIcon" +import { useTranslate } from "../i18n/index" const Container = styled.div` display: flex; @@ -54,6 +55,7 @@ export const VaultView: React.FC<{ vault: Vault; onLock(): void }> = ({ vault, onLock, }) => { + const t = useTranslate() const [items, setItems] = useState(() => []) const [item, setItem] = useState() const [sortBy, setSortBy] = useState(SortBy.Name) @@ -99,7 +101,7 @@ export const VaultView: React.FC<{ vault: Vault; onLock(): void }> = ({ margin: "10px 10px", }} > - + = 8.9.0'} @@ -1634,6 +1633,28 @@ packages: rollup: 2.58.0 dev: true + /@rollup/plugin-yaml/3.1.0: + resolution: {integrity: sha512-61PsAXqN7YNYdg/nezK3NkqAu6e3Qu2wjHYW3r52Nx0aLi+rG7gkkIqtvxG8EtSqE2rra5CUcWBZj+v362qt9A==} + engines: {node: '>=10.0.0'} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0 + js-yaml: 3.14.0 + tosource: 1.0.0 + dev: true + + /@rollup/pluginutils/3.1.0: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.0 + dev: true + /@rollup/pluginutils/3.1.0_rollup@2.58.0: resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} engines: {node: '>= 8.0.0'} @@ -2248,10 +2269,6 @@ packages: resolution: {integrity: sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=} dev: true - /async/3.2.2: - resolution: {integrity: sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==} - dev: true - /at-least-node/1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} @@ -2313,7 +2330,6 @@ packages: /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} @@ -2449,23 +2465,6 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - - /build/0.1.4: - resolution: {integrity: sha1-cH/gJv/O3crL/c3zVur9pk8VEEY=} - engines: {node: '>v0.4.12'} - dependencies: - cssmin: 0.3.2 - jsmin: 1.0.1 - jxLoader: 0.1.1 - moo-server: 1.3.0 - promised-io: 0.3.6 - timespan: 2.3.0 - uglify-js: 1.3.5 - walker: 1.0.8 - winston: 3.3.3 - wrench: 1.3.9 - dev: true /builder-util-runtime/8.8.1: resolution: {integrity: sha512-xHxAzdsJmMV8m/N+INzYUKfyJASeKyKHnA1uGkY8Y8JKLI/c4BG+If+L0If2YETv96CiRASkvd02tIt2pvrchQ==} @@ -2665,37 +2664,11 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /color-string/1.6.0: - resolution: {integrity: sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA==} - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - dev: true - - /color/3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} - dependencies: - color-convert: 1.9.3 - color-string: 1.6.0 - dev: true - /colors/1.0.3: resolution: {integrity: sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=} engines: {node: '>=0.1.90'} dev: true - /colors/1.4.0: - resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} - engines: {node: '>=0.1.90'} - dev: true - - /colorspace/1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} - dependencies: - color: 3.2.1 - text-hex: 1.0.0 - dev: true - /commander/2.9.0: resolution: {integrity: sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=} engines: {node: '>= 0.6.x'} @@ -2830,11 +2803,6 @@ packages: engines: {node: '>=8'} dev: true - /cssmin/0.3.2: - resolution: {integrity: sha1-3c5MVHtRCuDVlKjx+/iq+OLFwA0=} - hasBin: true - dev: true - /csstype/3.0.9: resolution: {integrity: sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==} dev: true @@ -3105,10 +3073,6 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true - /enabled/2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: true - /encodeurl/1.0.2: resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=} engines: {node: '>= 0.8'} @@ -3672,10 +3636,6 @@ packages: pend: 1.2.0 dev: true - /fecha/4.2.1: - resolution: {integrity: sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==} - dev: true - /file-entry-cache/6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3739,10 +3699,6 @@ packages: resolution: {integrity: sha512-XprP7lDrVT+kE2c2YlfiV+IfS9zxukiIOvNamPNsImNhXadSsQEbosItdL9bUQlCZXR13SvPk20BjWSWLA7m4A==} dev: true - /fn.name/1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: true - /fs-extra/10.0.0: resolution: {integrity: sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==} engines: {node: '>=12'} @@ -4066,7 +4022,6 @@ packages: /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true /ignore/4.0.6: resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} @@ -4129,10 +4084,6 @@ packages: resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} dev: true - /is-arrayish/0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: true - /is-bigint/1.0.2: resolution: {integrity: sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==} dev: true @@ -4271,11 +4222,6 @@ packages: resolution: {integrity: sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==} dev: true - /is-stream/2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: true - /is-string/1.0.6: resolution: {integrity: sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==} engines: {node: '>= 0.4'} @@ -4357,11 +4303,6 @@ packages: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true - /js-yaml/0.3.7: - resolution: {integrity: sha1-1znY7oZGHlSzVNan19HyrZoWf2I=} - engines: {node: '> 0.4.11'} - dev: true - /js-yaml/3.14.0: resolution: {integrity: sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==} hasBin: true @@ -4388,12 +4329,6 @@ packages: hasBin: true dev: true - /jsmin/1.0.1: - resolution: {integrity: sha1-570NzWSWw79IYyNb9GGj2YqjuYw=} - engines: {node: '>=0.1.93'} - hasBin: true - dev: true - /json-buffer/3.0.0: resolution: {integrity: sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=} dev: true @@ -4467,26 +4402,12 @@ packages: resolution: {integrity: sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==} dev: true - /jxLoader/0.1.1: - resolution: {integrity: sha1-ATTqUUTlM7WU/B/yX/GU4jXFPs0=} - engines: {node: '>v0.4.10'} - dependencies: - js-yaml: 0.3.7 - moo-server: 1.3.0 - promised-io: 0.3.6 - walker: 1.0.8 - dev: true - /keyv/3.1.0: resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} dependencies: json-buffer: 3.0.0 dev: true - /kuler/2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: true - /latest-version/5.1.0: resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} engines: {node: '>=8'} @@ -4591,16 +4512,6 @@ packages: is-unicode-supported: 0.1.0 dev: true - /logform/2.3.0: - resolution: {integrity: sha512-graeoWUH2knKbGthMtuG1EfaSPMZFZBIrhuJHhkS5ZseFBrc7DupCzihOQAzsK/qIKPQaPJ/lFQFctILUY5ARQ==} - dependencies: - colors: 1.4.0 - fecha: 4.2.1 - ms: 2.1.3 - safe-stable-stringify: 1.1.1 - triple-beam: 1.3.0 - dev: true - /loose-envify/1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -4652,12 +4563,6 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /makeerror/1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - dev: true - /marked/3.0.8: resolution: {integrity: sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==} engines: {node: '>= 12'} @@ -4780,11 +4685,6 @@ packages: uuid: 8.3.2 dev: true - /moo-server/1.3.0: - resolution: {integrity: sha1-XceVaVZaENbv7VQ5SR5p0jkuWPE=} - engines: {node: '>v0.4.10'} - dev: true - /ms/2.0.0: resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} dev: true @@ -4939,12 +4839,6 @@ packages: wrappy: 1.0.2 dev: true - /one-time/1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} - dependencies: - fn.name: 1.1.0 - dev: true - /onigasm/2.2.5: resolution: {integrity: sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA==} dependencies: @@ -5188,10 +5082,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /promised-io/0.3.6: - resolution: {integrity: sha512-bNwZusuNIW4m0SPR8jooSyndD35ggirHlxVl/UhIaZD/F0OBv9ebfc6tNmbpZts3QXHggkjIBH8lvtnzhtcz0A==} - dev: true - /prop-types/15.7.2: resolution: {integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==} dependencies: @@ -5316,15 +5206,6 @@ packages: util-deprecate: 1.0.2 dev: true - /readable-stream/3.6.0: - resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} - engines: {node: '>= 6'} - dependencies: - inherits: 2.0.4 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - dev: true - /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -5521,10 +5402,6 @@ packages: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true - /safe-stable-stringify/1.1.1: - resolution: {integrity: sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==} - dev: true - /safer-buffer/2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true @@ -5647,12 +5524,6 @@ packages: resolution: {integrity: sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==} dev: true - /simple-swizzle/0.2.2: - resolution: {integrity: sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=} - dependencies: - is-arrayish: 0.3.2 - dev: true - /sinon-chai/3.7.0_chai@4.3.4+sinon@11.1.2: resolution: {integrity: sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==} peerDependencies: @@ -5761,10 +5632,6 @@ packages: dev: true optional: true - /stack-trace/0.0.10: - resolution: {integrity: sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=} - dev: true - /stat-mode/1.0.0: resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} engines: {node: '>= 6'} @@ -5932,19 +5799,10 @@ packages: fs-extra: 10.0.0 dev: true - /text-hex/1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: true - /text-table/0.2.0: resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=} dev: true - /timespan/2.3.0: - resolution: {integrity: sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=} - engines: {node: '>= 0.2.0'} - dev: true - /tiny-invariant/1.2.0: resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==} dev: false @@ -5962,10 +5820,6 @@ packages: rimraf: 3.0.2 dev: true - /tmpl/1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - dev: true - /to-fast-properties/2.0.0: resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=} engines: {node: '>=4'} @@ -5983,8 +5837,9 @@ packages: is-number: 7.0.0 dev: true - /triple-beam/1.3.0: - resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} + /tosource/1.0.0: + resolution: {integrity: sha1-QtiN0RZhi88A1hBt1URvNCeQL/E=} + engines: {node: '>=0.4.0'} dev: true /truncate-utf8-bytes/1.0.2: @@ -6124,11 +5979,6 @@ packages: resolution: {integrity: sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==} dev: true - /uglify-js/1.3.5: - resolution: {integrity: sha1-S1v/+Rhu/7qoiOTJ6UvZ/EyUkp0=} - hasBin: true - dev: true - /unbox-primitive/1.0.1: resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} dependencies: @@ -6285,12 +6135,6 @@ packages: resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==} dev: true - /walker/1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - dependencies: - makeerror: 1.0.12 - dev: true - /which-boxed-primitive/1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: @@ -6320,29 +6164,6 @@ packages: string-width: 4.2.2 dev: true - /winston-transport/4.4.0: - resolution: {integrity: sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==} - engines: {node: '>= 6.4.0'} - dependencies: - readable-stream: 2.3.7 - triple-beam: 1.3.0 - dev: true - - /winston/3.3.3: - resolution: {integrity: sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==} - engines: {node: '>= 6.4.0'} - dependencies: - '@dabh/diagnostics': 2.0.2 - async: 3.2.2 - is-stream: 2.0.1 - logform: 2.3.0 - one-time: 1.0.0 - readable-stream: 3.6.0 - stack-trace: 0.0.10 - triple-beam: 1.3.0 - winston-transport: 4.4.0 - dev: true - /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} @@ -6374,12 +6195,6 @@ packages: resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} dev: true - /wrench/1.3.9: - resolution: {integrity: sha1-bxPsNRRTF+spLKX2UxORskQRFBE=} - engines: {node: '>=0.1.97'} - deprecated: wrench.js is deprecated! You should check out fs-extra (https://github.com/jprichardson/node-fs-extra) for any operations you were using wrench for. Thanks for all the usage over the years. - dev: true - /write-file-atomic/3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} dependencies: