├── screenshot.jpg ├── src ├── vite.env.d.ts ├── actions │ ├── autofocus.ts │ ├── clickOutside.ts │ ├── dragOver.ts │ └── tooltip.ts ├── functions │ ├── string.ts │ ├── promise.ts │ ├── url.ts │ ├── dom.ts │ ├── time.ts │ ├── folders.ts │ └── api.ts ├── svelte.d.ts ├── store │ ├── index.ts │ ├── flash.ts │ ├── uploads.ts │ └── files.ts ├── query │ ├── QueryClientProvider.svelte │ ├── useQuery.ts │ ├── index.ts │ ├── useMutation.ts │ ├── QueryClient.ts │ └── Query.ts ├── ui │ ├── icons │ │ ├── IconButton.svelte │ │ ├── IconNewFolder.svelte │ │ ├── IconCirclePlus.svelte │ │ ├── IconCircleExclamation.svelte │ │ ├── IconCopy.svelte │ │ ├── IconArrowRight.svelte │ │ ├── IconCircleCheck.svelte │ │ ├── IconSearch.svelte │ │ ├── IconLoader.svelte │ │ ├── IconDelete.svelte │ │ ├── IconFolder.svelte │ │ └── IconUpload.svelte │ ├── FilesListGrid.svelte │ ├── Alerts │ │ ├── Alerts.svelte │ │ └── Alert.svelte │ ├── Sidebar │ │ ├── Folders.svelte │ │ ├── Sidebar.svelte │ │ ├── Search.svelte │ │ ├── NewFolder.svelte │ │ └── Folder.svelte │ ├── FilesListRows.svelte │ ├── Progress │ │ └── UploadProgress.svelte │ ├── Dropzone.svelte │ ├── FileRow.svelte │ ├── FileCell.svelte │ └── FilesList.svelte ├── lang.ts ├── langs │ ├── fr.ts │ └── en.ts ├── types │ ├── index.ts │ ├── openapi.ts │ └── generated-schema.ts ├── hooks │ └── useFileActions.ts ├── config.ts ├── main.ts ├── FileManager.ts └── FileManager.svelte ├── .gitignore ├── index.d.ts ├── FileManager.d.ts ├── tsconfig.node.json ├── svelte.config.js ├── Makefile ├── tests ├── utils.ts ├── mockApi.ts └── file-manager.test.ts ├── example └── laravel │ ├── Requests │ ├── UploadFileRequest.php │ └── FolderCreateRequest.php │ └── Controllers │ ├── FolderController.php │ └── FileController.php ├── tsconfig.json ├── playwright.config.js ├── index.html ├── vite.config.ts ├── .github └── workflows │ ├── test.yml │ └── deploy.yml ├── LICENCE.md ├── package.json ├── README.md ├── openapi.yml └── pnpm-lock.yaml /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Grafikart/filemanager-element/HEAD/screenshot.jpg -------------------------------------------------------------------------------- /src/vite.env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | server 7 | test-results 8 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | type Registrable = { 2 | register(): void; 3 | }; 4 | 5 | export const FileManager: Registrable; 6 | -------------------------------------------------------------------------------- /FileManager.d.ts: -------------------------------------------------------------------------------- 1 | export declare class FileManager extends HTMLElement { 2 | static register(name?: string): void; 3 | } 4 | -------------------------------------------------------------------------------- /src/actions/autofocus.ts: -------------------------------------------------------------------------------- 1 | export function autofocus(node: HTMLInputElement) { 2 | node.focus() 3 | 4 | return {}; 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /src/functions/string.ts: -------------------------------------------------------------------------------- 1 | export function shorten (str: string, max: number) { 2 | if (str.length <= max) { 3 | return str; 4 | } 5 | return str.slice(0, max - 11) + '...' + str.slice(-8); 6 | } 7 | -------------------------------------------------------------------------------- /src/svelte.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace svelte.JSX { 2 | interface HTMLAttributes { 3 | ondropzoneover?: () => void 4 | ondropzoneleave?: () => void 5 | onoutclick?: (e: MouseEvent) => void 6 | } 7 | } -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import sveltePreprocess from "svelte-preprocess"; 2 | 3 | export default { 4 | // Consult https://github.com/sveltejs/svelte-preprocess 5 | // for more information about preprocessors 6 | preprocess: sveltePreprocess(), 7 | }; 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | build: 3 | pnpm run build 4 | cp dist/FileManager.js /home/jonathan/Sites/Boxraiser/Kumquat/nova-components/Editor/resources/js/components/FileManager.js 5 | cp dist/style.css /home/jonathan/Sites/Boxraiser/Kumquat/nova-components/Editor/resources/sass/field.scss 6 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { getContext } from "svelte"; 2 | import type { Options } from "../types"; 3 | 4 | export * from "./files"; 5 | export * from "./flash"; 6 | export { uploads } from "./uploads"; 7 | export function getOptions(): Options { 8 | return getContext("options"); 9 | } 10 | -------------------------------------------------------------------------------- /src/query/QueryClientProvider.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /src/functions/promise.ts: -------------------------------------------------------------------------------- 1 | // Detect if the parameter is a Promise 2 | export function isPromise(target: unknown): target is Promise { 3 | if ( 4 | typeof target === "object" && 5 | typeof (target as any)["then"] === "function" 6 | ) { 7 | return true; 8 | } 9 | return false; 10 | } 11 | -------------------------------------------------------------------------------- /src/functions/url.ts: -------------------------------------------------------------------------------- 1 | export function objToQueryParams>( 2 | o: O, 3 | p?: URLSearchParams 4 | ) { 5 | const params = p || new URLSearchParams(); 6 | Object.keys(o) 7 | .filter((k: keyof O) => o[k] !== undefined) 8 | .forEach((k: string) => params.set(k, o[k]!)); 9 | return params; 10 | } 11 | -------------------------------------------------------------------------------- /src/functions/dom.ts: -------------------------------------------------------------------------------- 1 | export function $on( 2 | el: HTMLElement, 3 | eventNames: string[], 4 | cb: (e: Event) => any 5 | ) { 6 | eventNames.forEach((eventName) => { 7 | el.addEventListener(eventName, cb); 8 | }); 9 | 10 | return () => { 11 | eventNames.forEach((eventName) => { 12 | el.removeEventListener(eventName, cb); 13 | }); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/query/useQuery.ts: -------------------------------------------------------------------------------- 1 | import type { QueryCallback, QueryOptions } from "./Query"; 2 | import { useQueryClient } from "./index"; 3 | 4 | export function useQuery( 5 | key: string, 6 | cb: QueryCallback, 7 | options: QueryOptions = {} 8 | ) { 9 | const query = useQueryClient().getQuery(key, cb, options); 10 | return { 11 | subscribe: query.store.subscribe, 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/ui/icons/IconButton.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /src/lang.ts: -------------------------------------------------------------------------------- 1 | import type { Lang } from "./types"; 2 | import FR from "./langs/fr"; 3 | import EN from "./langs/en"; 4 | 5 | const langs = { 6 | fr: FR, 7 | en: EN, 8 | }; 9 | let langMessages = EN; 10 | 11 | export function t(key: keyof Lang) { 12 | return langMessages[key]; 13 | } 14 | 15 | export function setLang(lang: string) { 16 | langMessages = lang in langs ? langs[lang as keyof typeof langs] : EN; 17 | } 18 | -------------------------------------------------------------------------------- /src/functions/time.ts: -------------------------------------------------------------------------------- 1 | export function debounce any>( 2 | callback: T, 3 | delay: number 4 | ): (...args: Parameters) => void { 5 | let timer: ReturnType | undefined; 6 | return function (...args: Parameters[]) { 7 | if (timer) { 8 | clearTimeout(timer); 9 | } 10 | timer = setTimeout(function () { 11 | callback(...args); 12 | }, delay); 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /src/ui/icons/IconNewFolder.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /src/ui/icons/IconCirclePlus.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /src/ui/icons/IconCircleExclamation.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /src/ui/icons/IconCopy.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /src/ui/icons/IconArrowRight.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/ui/icons/IconCircleCheck.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /src/langs/fr.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | delete: "Supprimer", 3 | deleteConfirm: "Voulez vous vraiment supprimer ce fichier ?", 4 | newFolderPlaceholder: "Nom du dossier", 5 | emptyTitle: "Ce dossier est vide", 6 | deleteFolder: "Supprimer le dossier", 7 | emptyDescription: "Déposer un fichier ici pour le téléverser", 8 | createFolder: "Créer un dossier", 9 | copy: "Copier le lien", 10 | size: "Taille", 11 | filename: "Nom", 12 | serverError: "Action impossible suite a une erreur serveur", 13 | }; 14 | -------------------------------------------------------------------------------- /src/actions/clickOutside.ts: -------------------------------------------------------------------------------- 1 | export function clickOutside(node: HTMLElement, eventName = 'outclick') { 2 | const handleClick = (event: MouseEvent) => { 3 | if (!node.contains(event.target as HTMLElement)) { 4 | node.dispatchEvent(new CustomEvent(eventName, { bubbles: eventName !== 'outclick' })); 5 | } 6 | }; 7 | 8 | document.addEventListener("click", handleClick, true); 9 | 10 | return { 11 | destroy() { 12 | document.removeEventListener("click", handleClick, true); 13 | }, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/langs/en.ts: -------------------------------------------------------------------------------- 1 | import type { Lang } from "../types"; 2 | 3 | const messages: Lang = { 4 | delete: "Delete", 5 | deleteConfirm: "Do you really want to delete this file ?", 6 | newFolderPlaceholder: "Folder name", 7 | deleteFolder: "Delete this folder", 8 | emptyTitle: "This folder is empty", 9 | emptyDescription: "Drop a file here to upload it", 10 | createFolder: "New folder", 11 | copy: "Copy link", 12 | size: "Size", 13 | filename: "Name", 14 | serverError: "Server error", 15 | }; 16 | 17 | export default messages; 18 | -------------------------------------------------------------------------------- /tests/utils.ts: -------------------------------------------------------------------------------- 1 | import type { Page } from "@playwright/test"; 2 | 3 | export async function dropFile(page: Page, selector: string) { 4 | // Create the DataTransfer and File 5 | const dataTransfer = await page.evaluateHandle(() => { 6 | const dt = new DataTransfer(); 7 | // Convert the buffer to a hex array 8 | const file = new File([""], "img.jpg", { type: "image/png" }); 9 | dt.items.add(file); 10 | return dt; 11 | }); 12 | 13 | // Now dispatch 14 | await page.dispatchEvent(selector, "drop", { dataTransfer }); 15 | } 16 | -------------------------------------------------------------------------------- /src/ui/FilesListGrid.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /src/ui/icons/IconSearch.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/query/index.ts: -------------------------------------------------------------------------------- 1 | import { getContext } from "svelte"; 2 | import type { QueryClient as QueryClientType } from "./QueryClient"; 3 | 4 | /** 5 | * This is a partial implementation of react-query for svelte with a limited set of feature 6 | */ 7 | export const contextKey = Symbol("queryClient"); 8 | export { QueryClient } from "./QueryClient"; 9 | export { useQuery } from "./useQuery"; 10 | export { useMutation } from "./useMutation"; 11 | export function useQueryClient(): QueryClientType { 12 | return getContext(contextKey); 13 | } 14 | export { default as QueryClientProvider } from "./QueryClientProvider.svelte"; 15 | -------------------------------------------------------------------------------- /src/ui/Alerts/Alerts.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 27 | -------------------------------------------------------------------------------- /src/ui/icons/IconLoader.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 25 | -------------------------------------------------------------------------------- /src/ui/icons/IconDelete.svelte: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /src/ui/Sidebar/Folders.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 29 | -------------------------------------------------------------------------------- /src/ui/Sidebar/Sidebar.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 15 | 29 | -------------------------------------------------------------------------------- /src/store/flash.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store' 2 | import type { FlashMessage } from '../types' 3 | 4 | export const flashMessages = writable([]) 5 | 6 | export const flash = (message: string, type: FlashMessage['type'] = 'success') => { 7 | const id = Date.now() 8 | flashMessages.update(messages => [{type, message, id}, ...messages]) 9 | if (type === 'success') { 10 | window.setTimeout(() => { 11 | flashMessages.update(messages => messages.filter(message => message.id !== id)) 12 | }, 2000) 13 | } 14 | } 15 | 16 | export const deleteFlashMessage = (id: FlashMessage['id']) => { 17 | flashMessages.update(messages => messages.filter(message => message.id !== id)) 18 | } 19 | 20 | -------------------------------------------------------------------------------- /example/laravel/Requests/UploadFileRequest.php: -------------------------------------------------------------------------------- 1 | 'required|image', 28 | 'folder' => '' 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "esnext", 5 | "useDefineForClassFields": true, 6 | "module": "esnext", 7 | "resolveJsonModule": true, 8 | "noEmit": true, 9 | "baseUrl": ".", 10 | "checkJs": false, 11 | "allowJs": false, 12 | "skipLibCheck": true, 13 | "esModuleInterop": false, 14 | "allowSyntheticDefaultImports": true, 15 | "noUncheckedIndexedAccess": true, 16 | "strict": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "lib": ["DOM", "DOM.Iterable", "ESNext"] 19 | }, 20 | "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], 21 | "references": [{ "path": "./tsconfig.node.json" }] 22 | } 23 | -------------------------------------------------------------------------------- /src/ui/icons/IconFolder.svelte: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { components } from "./generated-schema"; 2 | import type FR from "../langs/fr"; 3 | import type config from "../config"; 4 | 5 | export type Folder = components["schemas"]["Folder"] & { 6 | children?: Folder[]; 7 | }; 8 | export type NullableId = components["schemas"]["NullableID"]; 9 | export type Lang = typeof FR; 10 | export type File = components["schemas"]["File"]; 11 | export type FlashMessage = { 12 | type: "success" | "danger"; 13 | message: string; 14 | id: number; 15 | }; 16 | 17 | export enum HTTPStatus { 18 | OK = 200, 19 | Created = 201, 20 | MultipleChoices = 300, 21 | NoContent = 204, 22 | UnprocessableEntity = 422, 23 | Forbidden = 403, 24 | NotFound = 404, 25 | BadRequest = 400, 26 | } 27 | 28 | export type Options = typeof config; 29 | -------------------------------------------------------------------------------- /playwright.config.js: -------------------------------------------------------------------------------- 1 | import { devices } from "@playwright/test"; 2 | 3 | /** 4 | * @var {import("@playwright/test").PlaywrightTestConfig} config 5 | */ 6 | const config = { 7 | testDir: "./tests", 8 | timeout: 5_000, 9 | expect: { 10 | timeout: 5_000, 11 | }, 12 | forbidOnly: !!process.env.CI, 13 | retries: 1, 14 | workers: process.env.CI ? 1 : undefined, 15 | reporter: process.env.CI ? "github" : "list", 16 | use: { 17 | actionTimeout: 0, 18 | trace: process.env.CI ? "off" : "on-first-retry", 19 | }, 20 | webServer: { 21 | command: "npm run dev", 22 | timeout: 5_000, 23 | reuseExistingServer: !process.env.CI, 24 | url: "http://localhost:3000/index.html", 25 | }, 26 | projects: [ 27 | { 28 | name: "chromium", 29 | use: { ...devices["Desktop Chrome"] }, 30 | }, 31 | ], 32 | }; 33 | export default config; 34 | -------------------------------------------------------------------------------- /src/store/uploads.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | 3 | /** 4 | * If the server is fast enough we don't want to show upload progress 5 | * this custom store push file with a delay to avoid showing progress bars for short upload time 6 | */ 7 | const delay = 300; 8 | const uploadsDelayed = writable([] as File[]); 9 | const timerMap = new Map(); 10 | 11 | export const uploads = { 12 | push(file: File) { 13 | const timer = setTimeout(() => { 14 | uploadsDelayed.update((files) => [file, ...files]); 15 | }, delay); 16 | timerMap.set(file, timer); 17 | }, 18 | remove(file: File) { 19 | const timer = timerMap.get(file); 20 | if (timer !== undefined) { 21 | clearTimeout(timer); 22 | timerMap.delete(file); 23 | } 24 | uploadsDelayed.update((files) => files.filter((f) => f !== file)); 25 | }, 26 | subscribe: uploadsDelayed.subscribe, 27 | }; 28 | -------------------------------------------------------------------------------- /src/actions/dragOver.ts: -------------------------------------------------------------------------------- 1 | import { $on } from '../functions/dom' 2 | 3 | export function dragOver(node: HTMLElement) { 4 | const offPreventListeners = $on( 5 | node, 6 | [ 7 | "drag", 8 | "dragstart", 9 | "dragend", 10 | "dragover", 11 | "dragenter", 12 | "dragleave", 13 | "drop", 14 | ], 15 | function (e) { 16 | e.preventDefault(); 17 | e.stopPropagation(); 18 | }) 19 | 20 | const offOver = $on(node, ["dragover", "dragenter"], function () { 21 | node.dispatchEvent(new CustomEvent("dropzoneover")) 22 | }); 23 | const offLeave = $on( 24 | node, 25 | ["dragleave", "dragend", "drop"], 26 | function () { 27 | node.dispatchEvent(new CustomEvent("dropzoneleave")) 28 | } 29 | ); 30 | 31 | return { 32 | destroy() { 33 | offPreventListeners(); 34 | offOver(); 35 | offLeave(); 36 | } 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/hooks/useFileActions.ts: -------------------------------------------------------------------------------- 1 | import type { File } from "../types"; 2 | import { useQueryClient } from "../query"; 3 | import { flash, getOptions, removeFile } from "../store"; 4 | import { t } from "../lang"; 5 | 6 | export function useFileActions(file: File, element: HTMLElement) { 7 | const queryClient = useQueryClient(); 8 | const options = getOptions(); 9 | const handleDelete = () => { 10 | if (!confirm(t("deleteConfirm"))) { 11 | return; 12 | } 13 | removeFile(options, queryClient, file); 14 | }; 15 | const handleClick = () => { 16 | element.dispatchEvent( 17 | new CustomEvent("selectfile", { detail: file, bubbles: true }) 18 | ); 19 | }; 20 | 21 | const handleCopy = () => { 22 | navigator.clipboard.writeText(file.url); 23 | flash("Le lien a été copié dans votre presse papier"); 24 | }; 25 | 26 | return { 27 | handleClick, 28 | handleCopy, 29 | handleDelete, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/functions/folders.ts: -------------------------------------------------------------------------------- 1 | import type { Folder } from "../types"; 2 | 3 | /** 4 | * Add a new "children" property using the "parent" property 5 | * to create a tree instead of a flat list 6 | */ 7 | export function nestFolder(originalFolders: Folder[]): Folder[] { 8 | // Avoid mutating the original array, clone all the children 9 | const folders = originalFolders.map((folder) => ({ 10 | ...folder, 11 | children: [], 12 | })); 13 | // Creates a map to find folder faster during the next loop 14 | const foldersById = folders.reduce( 15 | (acc, folder) => acc.set(folder.id, folder), 16 | new Map() 17 | ); 18 | // Generate a children property for every folders 19 | for (const folder of folders) { 20 | const parent = foldersById.get(folder.parent); 21 | if (folder.parent && parent) { 22 | parent.children = parent.children 23 | ? [...parent.children, folder] 24 | : [folder]; 25 | } 26 | } 27 | return folders; 28 | } 29 | -------------------------------------------------------------------------------- /example/laravel/Requests/FolderCreateRequest.php: -------------------------------------------------------------------------------- 1 | 'nullable|regex:/^[0-9a-zA-Z_\-\/]+$/', 20 | 'name' => 'required|regex:/^[0-9a-zA-Z_\-]+$/' 21 | ]; 22 | } 23 | 24 | protected function failedValidation(Validator $validator) 25 | { 26 | $response = response()->json([ 27 | 'message' => $validator->errors()->first(), 28 | 'errors' => $validator->errors()->messages(), 29 | ], 422); 30 | 31 | throw new ValidationException($validator, $response); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/query/useMutation.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | 3 | type MutationOptions = { 4 | onSuccess?: (data: Data) => void; 5 | onError?: (e: unknown) => void; 6 | }; 7 | 8 | export function useMutation( 9 | cb: (arg: Arg) => Promise, 10 | options: MutationOptions = {} 11 | ) { 12 | const mutate = (arg: Arg) => { 13 | store.update((v) => ({ ...v, isLoading: true })); 14 | return cb(arg) 15 | .then((data) => { 16 | options.onSuccess?.(data); 17 | return data; 18 | }) 19 | .catch((reason) => { 20 | options.onError?.(reason); 21 | throw reason; 22 | }) 23 | .finally(() => { 24 | store.update((v) => ({ ...v, isLoading: false })); 25 | }); 26 | }; 27 | const store = writable({ 28 | isLoading: false, 29 | mutate: (arg: Arg) => { 30 | mutate(arg).catch(() => null); 31 | }, 32 | mutateAsync: mutate, 33 | }); 34 | 35 | return { subscribe: store.subscribe }; 36 | } 37 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 | 16 | 17 | 18 |

File Manager

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { svelte } from "@sveltejs/vite-plugin-svelte"; 3 | import path from "path"; 4 | 5 | const standalone = process.env.STANDALONE; 6 | // https://vitejs.dev/config/ 7 | export default defineConfig({ 8 | plugins: [svelte()], 9 | build: { 10 | rollupOptions: { 11 | output: { 12 | assetFileNames: (assetInfo) => { 13 | if (assetInfo.name == "style.css") return "FileManager.css"; 14 | return assetInfo.name; 15 | }, 16 | }, 17 | // Ignore node_modules dependencies 18 | external: standalone 19 | ? [] 20 | : (id) => 21 | !id.startsWith("\0") && !id.startsWith(".") && !id.startsWith("/"), 22 | }, 23 | emptyOutDir: !standalone, 24 | minify: false, 25 | lib: { 26 | entry: path.resolve("src/FileManager.ts"), 27 | name: "FileManager", 28 | formats: ["es"], 29 | fileName: () => 30 | standalone ? "FileManager.standalone.js" : "FileManager.js", 31 | }, 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest] 11 | node-version: [16.x] 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Use Node.js ${{ matrix.node-version }} 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: ${{ matrix.node-version }} 18 | - name: Cache pnpm modules 19 | uses: actions/cache@v2 20 | with: 21 | path: ~/.pnpm-store 22 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} 23 | restore-keys: | 24 | ${{ runner.os }}- 25 | - name: Use PNPM 26 | uses: pnpm/action-setup@v2.0.1 27 | with: 28 | version: 6.22.2 29 | run_install: true 30 | - name: Install Playwright 31 | run: pnpx playwright install --with-deps 32 | - name: test 33 | run: pnpm run test 34 | env: 35 | CI: true 36 | NOVE_ENV: test 37 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-30 [these people](https://github.com/Grafikart/FileManagerJS/graphs/contributors) 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /src/ui/FilesListRows.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 27 | 28 | 51 | -------------------------------------------------------------------------------- /src/query/QueryClient.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Query, 3 | QueryCallback, 4 | QueryOptions, 5 | QuerySetDataParam, 6 | QueryState, 7 | } from "./Query"; 8 | 9 | export class QueryClient { 10 | private queries = new Map() as Map>; 11 | 12 | public getQuery( 13 | key: string, 14 | cb: QueryCallback, 15 | options = {} as QueryOptions 16 | ): Query { 17 | if (!this.queries.has(key)) { 18 | this.queries.set(key, new Query(cb, options)); 19 | } 20 | return this.queries.get(key) as Query; 21 | } 22 | 23 | public setQueryData(key: string, updater: QuerySetDataParam) { 24 | const query = this.queries.get(key); 25 | if (query) { 26 | query.setData(updater); 27 | } 28 | } 29 | 30 | public getQueryData(key: string): Data | undefined { 31 | return this.queries.get(key)?.getData(); 32 | } 33 | 34 | public getQueryState(key: string): QueryState { 35 | const query = this.queries.get(key); 36 | if (query) { 37 | return query.getState(); 38 | } 39 | return { 40 | data: undefined, 41 | isLoading: false, 42 | isSuccess: false, 43 | refetch: () => {}, 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example/laravel/Controllers/FolderController.php: -------------------------------------------------------------------------------- 1 | allDirectories(); 15 | return collect($directories)->map([$this, 'toArray']); 16 | } 17 | 18 | public function store(FolderCreateRequest $request) 19 | { 20 | $data = $request->validated(); 21 | $path = ($data['parent'] ?? '') . '/' . $data['name']; 22 | Storage::disk()->makeDirectory($path); 23 | return $this->toArray($path); 24 | } 25 | 26 | public function delete(string $folder) 27 | { 28 | Storage::disk()->deleteDirectory($folder); 29 | return response('', Response::HTTP_NO_CONTENT); 30 | } 31 | 32 | public function toArray(string $file): array 33 | { 34 | $path = explode('/', trim($file, '/')); 35 | $dirname = array_pop($path); 36 | return [ 37 | // use the filepath as an ID 38 | 'id' => $file, 39 | 'name' => $dirname, 40 | 'parent' => implode('/', $path) ?: null, 41 | ]; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node-version: [14.x] 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | - name: Cache pnpm modules 18 | uses: actions/cache@v2 19 | with: 20 | path: ~/.pnpm-store 21 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} 22 | restore-keys: | 23 | ${{ runner.os }}- 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - uses: pnpm/action-setup@v2.0.1 29 | with: 30 | version: 6.22.2 31 | run_install: true 32 | - name: Build the library 33 | run: pnpm run build 34 | - uses: "marvinpinto/action-automatic-releases@latest" 35 | with: 36 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 37 | prerelease: false 38 | - name: Publish 39 | run: | 40 | npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN 41 | cd dist && npm publish --access=public 42 | env: 43 | CI: true 44 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 45 | -------------------------------------------------------------------------------- /src/ui/Sidebar/Search.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | 24 | 66 | -------------------------------------------------------------------------------- /src/actions/tooltip.ts: -------------------------------------------------------------------------------- 1 | export function tooltip(node: HTMLElement, title: string) { 2 | let tooltip = null as HTMLElement | null; 3 | const onMouveOver = () => { 4 | if (tooltip) { 5 | return; 6 | } 7 | const rect = node.getBoundingClientRect(); 8 | 9 | // Create the tooltip 10 | tooltip = document.createElement("div"); 11 | tooltip.classList.add("fm-tooltip"); 12 | tooltip.innerText = title; 13 | const root = node.closest(".fm-root")!; 14 | root.appendChild(tooltip); 15 | 16 | // Find the tooltip placement 17 | tooltip.style.setProperty( 18 | "transform", 19 | `translate(calc(${rect.left + rect.width / 2}px - 50%), calc(${ 20 | rect.top - 4 21 | }px - 100%))` 22 | ); 23 | // Fade the element 24 | tooltip.animate([{ opacity: 0 }, { opacity: 1 }], { 25 | duration: 200, 26 | easing: "ease-in-out", 27 | }); 28 | 29 | node.addEventListener( 30 | "pointerleave", 31 | () => { 32 | if (tooltip) { 33 | tooltip.animate([{ opacity: 1 }, { opacity: 0 }], { 34 | duration: 200, 35 | easing: "ease-in-out", 36 | }); 37 | window.setTimeout(() => { 38 | tooltip?.remove(); 39 | tooltip = null; 40 | }, 200); 41 | } 42 | }, 43 | { once: true } 44 | ); 45 | }; 46 | node.addEventListener("pointerenter", onMouveOver); 47 | 48 | return { 49 | destroy() { 50 | tooltip?.remove(); 51 | node.removeEventListener("pointerenter", onMouveOver); 52 | }, 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "filemanager-element", 3 | "version": "0.1.2", 4 | "type": "module", 5 | "main": "FileManager.js", 6 | "homepage": "https://github.com/Grafikart/filemanager-element#README", 7 | "keywords": [ 8 | "Custom Elements", 9 | "Web Components", 10 | "HTML", 11 | "File Manager" 12 | ], 13 | "bugs": { 14 | "url": "https://github.com/Grafikart/filemanager-element/issues" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/Grafikart/filemanager-element" 19 | }, 20 | "author": "Jonathan Boyer", 21 | "license": "MIT", 22 | "types": "FileManager.d.ts", 23 | "scripts": { 24 | "dev": "vite", 25 | "build": "svelte-check && tsc && vite build && STANDALONE=1 vite build && cp package.json dist/package.json && cp FileManager.d.ts dist/FileManager.d.ts && cp README.md dist/README.md", 26 | "preview": "vite preview", 27 | "check": "svelte-check --tsconfig ./tsconfig.json", 28 | "tsopenapi": "openapi-typescript openapi.yml --output src/types/generated-schema.ts", 29 | "test": "PW_EXPERIMENTAL_TS_ESM=1 playwright test", 30 | "test:headed": "PW_EXPERIMENTAL_TS_ESM=1 playwright test --headed" 31 | }, 32 | "devDependencies": { 33 | "@playwright/test": "^1.20.0", 34 | "@sveltejs/vite-plugin-svelte": "^1.0.0-next.30", 35 | "@tsconfig/svelte": "^2.0.1", 36 | "openapi-typescript": "^5.1.1", 37 | "prettier": "^2.5.1", 38 | "svelte-check": "^2.2.7", 39 | "svelte-preprocess": "^4.9.8", 40 | "tslib": "^2.3.1", 41 | "typescript": "^4.5.4", 42 | "utility-types": "^3.10.0", 43 | "vite": "^2.8.0" 44 | }, 45 | "dependencies": { 46 | "svelte": "^3.46.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/laravel/Controllers/FileController.php: -------------------------------------------------------------------------------- 1 | query->get('folder'); 16 | $files = Storage::disk()->files($folder); 17 | return collect($files) 18 | ->filter(fn(string $file) => !str_starts_with($file, '.')) 19 | ->values() 20 | ->map([$this, 'toArray']); 21 | } 22 | 23 | public function store(UploadFileRequest $request) 24 | { 25 | $file = $request->file('file'); 26 | $folder = $request->post('folder'); 27 | $filename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME); 28 | $path = $file->storeAs($folder, $filename . '_' . $file->hashName()); 29 | return $this->toArray($path); 30 | } 31 | 32 | public function delete(string $file) 33 | { 34 | Storage::disk()->delete($file); 35 | return response('', Response::HTTP_NO_CONTENT); 36 | } 37 | 38 | public function toArray(string $file): array 39 | { 40 | $info = pathinfo($file); 41 | $disk = Storage::disk(); 42 | return [ 43 | 'id' => $file, 44 | 'name' => $info['basename'], 45 | 'url' => $disk->url($file), 46 | 'size' => $disk->size($file), 47 | 'folder' => $info['dirname'] === '.' ? null : $info['dirname'], 48 | 'thumbnail' => $disk->url($file), 49 | ]; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/ui/Progress/UploadProgress.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 20 | 21 | 71 | -------------------------------------------------------------------------------- /src/functions/api.ts: -------------------------------------------------------------------------------- 1 | import { flash } from "../store"; 2 | import { HTTPStatus } from "../types"; 3 | import type { ApiOptions, ApiPaths, ApiResponse } from "src/types/openapi"; 4 | import { objToQueryParams } from "./url"; 5 | import { t } from "../lang"; 6 | 7 | export function fetchApi< 8 | Path extends ApiPaths, 9 | Options extends ApiOptions 10 | >(baseUrl: string, path: Path, options: Options) { 11 | const o = { ...options }; 12 | let url = new URL( 13 | (baseUrl.startsWith("/") ? window.location.origin : "") + baseUrl 14 | ); 15 | url.pathname = (url.pathname === '/' ? '' : url.pathname) + path; 16 | o.credentials = "include"; 17 | o.headers = { ...o.headers }; 18 | o.headers["Accept"] = "application/json"; 19 | if (o.json) { 20 | o.body = JSON.stringify(o.json); 21 | o.headers["Content-Type"] = "application/json"; 22 | } 23 | // Add query parameters into the URL 24 | if (o.query) { 25 | objToQueryParams(o.query, url.searchParams); 26 | } 27 | // Replace params in the URL ("/path/{id}" for instance) 28 | if (o.params) { 29 | Object.keys(o.params).forEach( 30 | (k) => (url.pathname = url.pathname.replace(`%7B${k}%7D`, o.params[k])) 31 | ); 32 | } 33 | return fetch(url.toString(), o).then((r) => { 34 | if (r.status === HTTPStatus.NoContent) { 35 | return null; 36 | } 37 | if (r.status >= HTTPStatus.OK && r.status < HTTPStatus.MultipleChoices) { 38 | return r.json() as Promise>; 39 | } 40 | r.json() 41 | .then((data) => { 42 | if (data?.message) { 43 | flash(data.message, "danger"); 44 | } else { 45 | flash(t("serverError"), "danger"); 46 | } 47 | }) 48 | .catch(() => flash(t("serverError"), "danger")); 49 | throw r; 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /src/types/openapi.ts: -------------------------------------------------------------------------------- 1 | import type { paths as ApiSchema } from "./generated-schema"; 2 | import type { Optional, RequiredKeys, ValuesType } from "utility-types"; 3 | 4 | /** 5 | * Utilities 6 | */ 7 | // Find a property in another type Get 8 | type Get = K extends [] 9 | ? T 10 | : K extends [infer A, ...infer B] 11 | ? A extends keyof T 12 | ? Get 13 | : D 14 | : D; 15 | // Extract keys 16 | type KeysWithOnlyOptionals = { 17 | [K in keyof T]: RequiredKeys extends never ? K : never; 18 | }[keyof T]; 19 | // Make the key that only have optionals, optional themself 20 | type OptionalDeep = Optional>; 21 | 22 | export enum HTTPStatus { 23 | OK = 200, 24 | Created = 201, 25 | MultipleChoices = 300, 26 | NoContent = 204, 27 | UnprocessableEntity = 422, 28 | Forbidden = 403, 29 | NotFound = 404, 30 | BadRequest = 400, 31 | } 32 | type HTTPSuccess = 200 | 201 | 204; 33 | export type ApiPaths = keyof ApiSchema; 34 | export type ApiOptions = ValuesType<{ 35 | [Method in keyof ApiSchema[Path]]: RequestInit & 36 | OptionalDeep< 37 | Optional< 38 | { 39 | method: Method; 40 | query: Get; 41 | params: Get; 42 | json: Get< 43 | ApiSchema[Path][Method], 44 | ["requestBody", "content", "application/json"] 45 | >; 46 | }, 47 | Method extends "get" ? "method" : never 48 | > 49 | >; 50 | }>; 51 | export type ApiResponse = Get< 52 | ApiSchema, 53 | [Path, Method, "responses", HTTPSuccess, "content", Type] 54 | >; 55 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import type { Folder, File as FileType } from "./types"; 2 | import { fetchApi } from "./functions/api"; 3 | 4 | const config = { 5 | endpoint: "", 6 | readOnly: false, 7 | httpHeaders: {}, 8 | getFolders(parent?: Folder | null): Promise { 9 | return fetchApi(this.endpoint, "/folders", { 10 | query: { 11 | parent: parent?.id?.toString(), 12 | }, 13 | headers: this.httpHeaders, 14 | }); 15 | }, 16 | 17 | createFolder(params: { 18 | name: Folder["name"]; 19 | parent?: Folder["parent"]; 20 | }): Promise { 21 | return fetchApi(this.endpoint, "/folders", { 22 | method: "post", 23 | headers: this.httpHeaders, 24 | json: params, 25 | }); 26 | }, 27 | 28 | deleteFolder(folder: Folder): Promise { 29 | return fetchApi(this.endpoint, "/folders/{id}", { 30 | method: "delete", 31 | headers: this.httpHeaders, 32 | params: { 33 | id: folder.id!.toString(), 34 | }, 35 | }); 36 | }, 37 | getFiles(folder?: Folder | null): Promise { 38 | return fetchApi(this.endpoint, "/files", { 39 | headers: this.httpHeaders, 40 | query: { 41 | folder: folder?.id ? folder.id.toString() : undefined, 42 | }, 43 | }); 44 | }, 45 | uploadFile(file: File, folder?: Folder | null): Promise { 46 | const form = new FormData(); 47 | form.set("file", file); 48 | if (folder?.id) { 49 | form.set("folder", folder.id.toString()); 50 | } 51 | return fetchApi(this.endpoint, "/files", { 52 | method: "post", 53 | headers: this.httpHeaders, 54 | body: form, 55 | }); 56 | }, 57 | deleteFile(file: FileType): Promise { 58 | return fetchApi(this.endpoint, `/files/{id}`, { 59 | method: "delete", 60 | headers: this.httpHeaders, 61 | params: { 62 | id: file.id.toString(), 63 | }, 64 | }); 65 | }, 66 | }; 67 | 68 | export default config; 69 | -------------------------------------------------------------------------------- /src/query/Query.ts: -------------------------------------------------------------------------------- 1 | import { get, Writable, writable } from "svelte/store"; 2 | import { isPromise } from "../functions/promise"; 3 | 4 | export type QueryOptions = { 5 | enabled?: boolean; 6 | onError?: () => void; 7 | }; 8 | 9 | export type QueryState = { 10 | isSuccess: boolean; 11 | isLoading: boolean; 12 | data: Data | undefined; 13 | refetch: () => void; 14 | }; 15 | 16 | export type QuerySetDataParam = Data | ((data: Data) => Data); 17 | 18 | export type QueryCallback = () => Promise | Data; 19 | 20 | export class Query { 21 | public store: Writable>; 22 | 23 | constructor(cb: QueryCallback, options: QueryOptions) { 24 | const fetchData = () => { 25 | // Do not refetch if we already have the data 26 | if (this.getData()) { 27 | return; 28 | } 29 | const response = cb(); 30 | if (isPromise(response)) { 31 | response.then(this.setData).catch((e: unknown) => { 32 | options.onError?.(); 33 | this.store.update((v) => ({ 34 | ...v, 35 | isLoading: false, 36 | isSuccess: false, 37 | })); 38 | }); 39 | } else { 40 | this.setData(response); 41 | } 42 | }; 43 | 44 | this.store = writable>({ 45 | isSuccess: false, 46 | isLoading: false, 47 | data: undefined, 48 | refetch: fetchData, 49 | }); 50 | 51 | if (options.enabled !== false) { 52 | fetchData(); 53 | } 54 | } 55 | 56 | getState = (): QueryState => { 57 | return get(this.store); 58 | }; 59 | 60 | /** 61 | * Manually update the data for the query 62 | */ 63 | setData = (newData: QuerySetDataParam) => { 64 | this.store.update((v) => ({ 65 | ...v, 66 | isLoading: false, 67 | isSuccess: true, 68 | data: 69 | typeof newData === "function" 70 | ? (newData as Function)(v.data!) 71 | : newData, 72 | })); 73 | }; 74 | 75 | getData = (): Data | undefined => { 76 | return this.getState().data; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /src/ui/Dropzone.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 40 | 41 | 88 | -------------------------------------------------------------------------------- /src/ui/icons/IconUpload.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | 25 | 42 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { FileManager } from "./FileManager"; 2 | import type { Folder, File as FileType } from "./types"; 3 | import { filesResponse, foldersResponse } from "../tests/mockApi"; 4 | 5 | /** 6 | * This code is for demo / test purpose, it's not used for the library 7 | */ 8 | FileManager.register(); 9 | FileManager.register("fn-file-manager", { 10 | getFiles(folder?: Folder | null) { 11 | if (folder?.name === "Empty") { 12 | return Promise.resolve([]); 13 | } 14 | return Promise.resolve(filesResponse(15, folder?.id)); 15 | }, 16 | getFolders(parent?: Folder | null) { 17 | return Promise.resolve(foldersResponse(10, parent?.id)); 18 | }, 19 | createFolder(params: Pick) { 20 | return Promise.resolve({ 21 | id: `Folder${Date.now()}`, 22 | name: params.name, 23 | parent: params.parent, 24 | }); 25 | }, 26 | deleteFile(file: FileType) { 27 | return Promise.resolve(); 28 | }, 29 | deleteFolder(folder: Folder) { 30 | return Promise.resolve(); 31 | }, 32 | uploadFile(file: File, folder: Folder) { 33 | const url = `https://picsum.photos`; 34 | return Promise.resolve({ 35 | id: folder?.name || "", 36 | name: `new_file.png`, 37 | url: url + "/1024/768", 38 | size: Math.random() * 100, 39 | folder: 1, 40 | thumbnail: url + "/100/100", 41 | }); 42 | }, 43 | }); 44 | 45 | const apiBasedManager = document.querySelector("file-manager")!; 46 | const fnBasedManager = document.querySelector("fn-file-manager")!; 47 | [apiBasedManager, fnBasedManager].forEach((el) => 48 | el.addEventListener("close", (e) => { 49 | (e.currentTarget as HTMLElement).setAttribute("hidden", ""); 50 | }) 51 | ); 52 | 53 | apiBasedManager.addEventListener("selectfile", ((e: CustomEvent) => { 54 | console.log("fileselect", e.detail); 55 | }) as EventListener); 56 | 57 | document.querySelector("#api")!.addEventListener("click", () => { 58 | apiBasedManager.removeAttribute("hidden"); 59 | }); 60 | 61 | document.querySelector("#function")!.addEventListener("click", () => { 62 | fnBasedManager.removeAttribute("hidden"); 63 | }); 64 | 65 | if (window.location.hash === "#function") { 66 | fnBasedManager.removeAttribute("hidden"); 67 | } else if (window.location.hash === "#readonly") { 68 | document.querySelector("[readonly]")!.removeAttribute("hidden"); 69 | } else { 70 | apiBasedManager.removeAttribute("hidden"); 71 | } 72 | -------------------------------------------------------------------------------- /src/ui/Sidebar/NewFolder.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | 54 | 55 | 88 | -------------------------------------------------------------------------------- /src/ui/FileRow.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 44 | 45 | 111 | -------------------------------------------------------------------------------- /src/ui/Alerts/Alert.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 26 | 27 | 103 | -------------------------------------------------------------------------------- /src/FileManager.ts: -------------------------------------------------------------------------------- 1 | import FileManagerComponent from "./FileManager.svelte"; 2 | import { setLang } from "./lang"; 3 | import type { Options } from "./types"; 4 | import config from "./config"; 5 | 6 | export class FileManager { 7 | private fm: FileManagerComponent | null = null; 8 | static registered = new Map(); 9 | private options: Options; 10 | 11 | constructor(private element: HTMLElement, options: Partial = {}) { 12 | this.options = { ...config, ...options }; 13 | } 14 | 15 | static get observedAttributes() { 16 | return ["hidden", "endpoint"]; 17 | } 18 | 19 | connectedCallback() { 20 | this.element.style.setProperty("display", "block"); 21 | const endpointAttr = this.element.getAttribute("endpoint"); 22 | if (endpointAttr) { 23 | this.options.endpoint = endpointAttr!; 24 | } 25 | this.options.readOnly = this.element.hasAttribute("readonly"); 26 | 27 | if (!this.options.endpoint && !this.options.getFiles) { 28 | throw new Error("You must define an endpoint for this custom element"); 29 | } 30 | 31 | setLang(document.documentElement.getAttribute("lang") || "en"); 32 | this.fm = new FileManagerComponent({ 33 | target: this.element, 34 | props: { 35 | hidden: this.element.hidden, 36 | layout: this.element.getAttribute("layout") || "grid", 37 | lazyFolders: this.element.hasAttribute("lazy-folders"), 38 | options: this.options, 39 | }, 40 | }); 41 | } 42 | 43 | attributeChangedCallback(name: string, oldValue: any, newValue: any) { 44 | if (name === "hidden" && this.fm) { 45 | this.fm.$set({ hidden: newValue !== null }); 46 | } 47 | if (name === "endpoint") { 48 | this.options.endpoint = newValue; 49 | } 50 | } 51 | 52 | disconnectedCallback() { 53 | this?.fm?.$destroy(); 54 | } 55 | 56 | static register(name = "file-manager", options?: Partial) { 57 | if (!this.registered.has(name)) { 58 | // A class cannot be used multiple time to declare a custom element so we need to creates 59 | // a fresh class for every "register" call 60 | class AnonymousFileManager extends HTMLElement { 61 | private decorated: FileManager; 62 | 63 | constructor() { 64 | super(); 65 | this.decorated = new FileManager(this, options); 66 | } 67 | 68 | static get observedAttributes() { 69 | return FileManager.observedAttributes; 70 | } 71 | 72 | connectedCallback() { 73 | return this.decorated.connectedCallback(); 74 | } 75 | 76 | attributeChangedCallback(name: string, oldValue: any, newValue: any) { 77 | return this.decorated.attributeChangedCallback( 78 | name, 79 | oldValue, 80 | newValue 81 | ); 82 | } 83 | } 84 | 85 | customElements.define(name, AnonymousFileManager); 86 | this.registered.set(name, true); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/ui/FileCell.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 32 | 33 | 94 | -------------------------------------------------------------------------------- /tests/mockApi.ts: -------------------------------------------------------------------------------- 1 | import type { Page } from "@playwright/test"; 2 | import type { File, Folder } from "../src/types"; 3 | 4 | function between(min: number, max: number): number { 5 | return Math.floor(Math.random() * (max - min + 1)) + min; 6 | } 7 | 8 | export const mockApi = async (page: Page) => { 9 | // Mock the folders API 10 | await page.route("**/api/folders*", (route) => { 11 | if (route.request().method() === "GET") { 12 | return route.fulfill({ 13 | status: 200, 14 | body: JSON.stringify(foldersResponse(5)), 15 | }); 16 | } else if (route.request().method() === "POST") { 17 | const data = route.request().postDataJSON(); 18 | return route.fulfill({ 19 | status: 200, 20 | body: JSON.stringify({ 21 | id: Date.now(), 22 | name: data.name, 23 | parent: data.parent, 24 | }), 25 | }); 26 | } else if (route.request().method() === "DELETE") { 27 | return route.fulfill({ 28 | status: 204, 29 | }); 30 | } 31 | }); 32 | // Mock the file API 33 | await page.route("**/api/files*", (route) => { 34 | if (route.request().method() === "GET") { 35 | const url = new URL(route.request().url()); 36 | const folder = url.searchParams.get("folder"); 37 | return route.fulfill({ 38 | status: 200, 39 | body: JSON.stringify( 40 | folder === "empty" 41 | ? [] 42 | : filesResponse(15, url.searchParams.get("folder")) 43 | ), 44 | }); 45 | } else { 46 | return route.fulfill({ 47 | status: 200, 48 | body: JSON.stringify({ 49 | id: Date.now(), 50 | name: `new_file.png`, 51 | url: "https://picsum.photos/1024/768", 52 | size: Math.round(Math.random() * 344189), 53 | folder: 1, 54 | thumbnail: "https://picsum.photos/100/100", 55 | }), 56 | }); 57 | } 58 | }); 59 | await page.route("**/api/files/*", (route) => { 60 | return route.fulfill({ 61 | status: 204, 62 | }); 63 | }); 64 | await page.route("**/api/folders/*", (route) => { 65 | return route.fulfill({ 66 | status: 204, 67 | }); 68 | }); 69 | }; 70 | 71 | export const foldersResponse = ( 72 | n: number, 73 | parent?: Folder["parent"] 74 | ): Folder[] => { 75 | return [ 76 | ...Array.from({ length: n }, (_, i) => ({ 77 | id: parent ? `${parent}_${i}` : i, 78 | name: parent ? `Child ${i}` : `Folder ${i}`, 79 | parent: parent, 80 | })), 81 | ...(parent === undefined ? foldersResponse(10, 2) : []), 82 | ...(parent === undefined 83 | ? [{ id: "empty", name: "Empty", parent: null }] 84 | : []), 85 | ]; 86 | }; 87 | 88 | export const filesResponse = (n: number, folder?: Folder["parent"]): File[] => { 89 | const seed = between(0, 500); 90 | return Array.from({ length: n }, (_, i) => { 91 | const url = `https://picsum.photos/id/${seed + i}`; 92 | return { 93 | id: i, 94 | name: `${folder ?? "root"}_${i}.jpg`, 95 | url: url + "/1024/768", 96 | size: Math.round(Math.random() * 344189), 97 | folder, 98 | thumbnail: url + "/100/100", 99 | }; 100 | }); 101 | }; 102 | -------------------------------------------------------------------------------- /src/ui/FilesList.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 | 75 | 76 | 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # file-manager custom element 2 | 3 | [![npm](https://img.shields.io/npm/v/filemanager-element.svg)](http://npm.im/filemanager-element) 4 | [![Test](https://github.com/Grafikart/FileManagerJS/actions/workflows/test.yml/badge.svg)](https://github.com/Grafikart/FileManagerJS/actions/workflows/test.yml) 5 | 6 | ![](./screenshot.jpg) 7 | 8 | You want a simple file browser for your website, without the hassle of a front-end framework ? Here is a simple custom 9 | element for you. 10 | 11 | - [Demonstration (codesandbox.io)](https://km7mr7.csb.app) 12 | 13 | First register the custom element (the lang is infered from the html "lang" attribute) 14 | 15 | ```js 16 | import {FileManager} from 'filemanager-element' 17 | import 'filemanager-element/FileManager.css' 18 | 19 | FileManager.register(); 20 | ``` 21 | 22 | Then you can simply use your custom element whenever you want 23 | 24 | ```html 25 | 26 | ``` 27 | 28 | You just have to implement the API following this [Open API specification](openapi.yml) and it will work out of the box. 29 | 30 | To interface the editor with your system (for instance when a file is selected) you can simply bind listeners 31 | 32 | ```js 33 | const filemanager = document.querySelector("file-manager"); 34 | filemanager.addEventListener("close", () => { 35 | console.log("close"); 36 | }); 37 | 38 | filemanager.addEventListener("selectfile", e => { 39 | console.log("fileSelected", e.detail); 40 | }); 41 | ``` 42 | 43 | ## Attributes 44 | 45 | | Attribute | Description | Default | 46 | |--------------|-------------------------------------------------------------|---------| 47 | | endpoint | The base url for the file and folder API | | 48 | | readonly | Do not allow file deletion or creation | false | 49 | | layout | Files layout "rows" or "grid" | grid | 50 | | lazy-folders | Should all folder be lazy loaded with a new call to the API | false | 51 | | hidden | Work like the default HTML attribute | false | 52 | 53 | ## Events 54 | 55 | | Name | Description | 56 | |-------------|----------------------------------------------------| 57 | | close | The user clicked on the overlay to close the modal | 58 | | fileselect | The use selected a file | 59 | 60 | ## Options 61 | 62 | Options can be set on the `register()` method as a second argument. All the options are optional 63 | 64 | | Name | Type | Description | 65 | |-----------------|----------|--------------------------------------------| 66 | | readOnly | bool | Do not allow file deletion or creation | 67 | | endpoint | string | Endpoint for the REST API | 68 | | httpHeaders | object | Additional headers to send to the endpoint | 69 | | getFiles() | function | Custom API to retrieve files | 70 | | getFolders() | function | Custom API to retrieve folders | 71 | | deleteFile() | function | Custom API to delete file | 72 | | deleteFolder() | function | Custom API to delete folder | 73 | | uploadFile() | function | Custom API to upload file | 74 | | createFolder() | function | Custom API to create folder | 75 | 76 | ## Custom API 77 | 78 | If you do not use a traditional REST API you can overwrite the method used to fetch the data. 79 | 80 | ```ts 81 | import type {File as FileType, Folder} from 'filemanager-element' 82 | 83 | FileManager.register('my-file-manager', { 84 | getFiles (folder?: Folder): Promise { 85 | }, 86 | getFolders (parent?: Folder): Promise { 87 | }, 88 | createFolder (params: Pick): Promise { 89 | }, 90 | deleteFile (file: FileType): Promise { 91 | }, 92 | deleteFolder (folder: Folder): Promise { 93 | }, 94 | uploadFile (file: File, folder: Folder): Promise { 95 | } 96 | }) 97 | ``` 98 | -------------------------------------------------------------------------------- /src/FileManager.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 44 | 45 | 139 | -------------------------------------------------------------------------------- /src/store/files.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | import type { File, Folder, NullableId, Options } from "../types"; 3 | import { HTTPStatus } from "../types"; 4 | import { QueryClient, useMutation, useQueryClient } from "../query"; 5 | import { t } from "../lang"; 6 | import { flash } from "./flash"; 7 | import { getOptions } from "./index"; 8 | 9 | /** 10 | * Helpers 11 | */ 12 | export const filesQueryKey = (folderId?: NullableId) => 13 | `files/${folderId ?? ""}`; 14 | export const foldersQueryKey = (parentId?: NullableId) => 15 | `folders/${parentId ?? ""}`; 16 | 17 | /** 18 | * Store 19 | */ 20 | const folderStore = writable(null); 21 | 22 | export const folder = folderStore; 23 | export const searchQuery = writable(""); 24 | 25 | /* 26 | * Methods 27 | */ 28 | export const removeFile = async ( 29 | options: Options, 30 | queryClient: QueryClient, 31 | file: File 32 | ) => { 33 | const queryKey = filesQueryKey(file.folder); 34 | const oldData = queryClient.getQueryData(queryKey); 35 | if (oldData) { 36 | queryClient.setQueryData(queryKey, (files) => 37 | files ? files.filter((f) => f.id !== file.id) : [] 38 | ); 39 | } 40 | try { 41 | await options.deleteFile(file); 42 | } catch (e) { 43 | if ( 44 | !(e instanceof Response) || 45 | e.status !== HTTPStatus.UnprocessableEntity 46 | ) { 47 | flash(t(`serverError`), "danger"); 48 | console.error(e); 49 | } 50 | queryClient.setQueryData(queryKey, oldData); 51 | } 52 | }; 53 | export const uploadFile = async ( 54 | options: Options, 55 | queryClient: QueryClient, 56 | file: any, 57 | folder?: Folder | null 58 | ) => { 59 | try { 60 | const newFile = await options.uploadFile(file, folder); 61 | const queryKey = filesQueryKey(folder?.id); 62 | const state = queryClient.getQueryState(queryKey); 63 | if (state?.data) { 64 | queryClient.setQueryData(queryKey, (files) => 65 | files ? [newFile, ...files] : [newFile] 66 | ); 67 | } 68 | } catch (e) { 69 | if ( 70 | !(e instanceof Response) || 71 | e.status !== HTTPStatus.UnprocessableEntity 72 | ) { 73 | flash(t(`serverError`), "danger"); 74 | console.error(e); 75 | } 76 | } 77 | }; 78 | 79 | export const useCreateFolderMutation = () => { 80 | const queryClient = useQueryClient(); 81 | const options = getOptions(); 82 | return useMutation( 83 | (params: Pick) => options.createFolder(params), 84 | { 85 | onSuccess(folder: Folder) { 86 | // Add the new folder into a specific cache 87 | const addToCache = (parent: Folder["parent"]) => { 88 | const queryKey = foldersQueryKey(parent); 89 | const state = queryClient.getQueryState(queryKey); 90 | if (state?.data) { 91 | queryClient.setQueryData(queryKey, (folders) => 92 | folders ? [folder, ...folders] : [folder] 93 | ); 94 | } 95 | }; 96 | addToCache(folder.parent); 97 | if (folder.parent) { 98 | addToCache(null); 99 | } 100 | }, 101 | } 102 | ); 103 | }; 104 | 105 | export const useDeleteFolderMutation = () => { 106 | const queryClient = useQueryClient(); 107 | const options = getOptions(); 108 | return useMutation( 109 | (folder: Folder) => options.deleteFolder(folder).then((r) => folder), 110 | { 111 | onSuccess: (folder: Folder) => { 112 | // If we are deleting the current directory, back to the parent 113 | folderStore.update(() => null); 114 | // Update the store (both the root and this depth 115 | const updateData = (parent?: Folder["id"] | null) => { 116 | const queryKey = foldersQueryKey(parent); 117 | const state = queryClient.getQueryState(queryKey); 118 | if (state?.data) { 119 | queryClient.setQueryData( 120 | foldersQueryKey(parent), 121 | (folders) => 122 | folders ? folders.filter((f) => f.id !== folder.id) : [] 123 | ); 124 | } 125 | }; 126 | updateData(folder.parent); 127 | updateData(); 128 | }, 129 | } 130 | ); 131 | }; 132 | -------------------------------------------------------------------------------- /src/types/generated-schema.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was auto-generated by openapi-typescript. 3 | * Do not make direct changes to the file. 4 | */ 5 | 6 | export interface paths { 7 | "/folders": { 8 | /** Retrieve a list of folders */ 9 | get: { 10 | parameters: { 11 | query: { 12 | parent?: string; 13 | }; 14 | }; 15 | responses: { 16 | /** The list of folders */ 17 | 200: { 18 | content: { 19 | "application/json": components["schemas"]["Folder"][]; 20 | }; 21 | }; 22 | 400: components["responses"]["400"]; 23 | }; 24 | }; 25 | /** Create a new folder */ 26 | post: { 27 | responses: { 28 | /** Created folder */ 29 | 201: { 30 | content: { 31 | "application/json": components["schemas"]["Folder"]; 32 | }; 33 | }; 34 | 422: components["responses"]["validationError"]; 35 | }; 36 | requestBody: { 37 | content: { 38 | "application/json": { 39 | name: string; 40 | parent?: (string | null) | (number | null); 41 | }; 42 | }; 43 | }; 44 | }; 45 | }; 46 | "/folders/{id}": { 47 | /** Delete a folder */ 48 | delete: { 49 | parameters: { 50 | path: { 51 | id: string; 52 | }; 53 | }; 54 | responses: { 55 | /** No Content */ 56 | 204: never; 57 | /** FolderResource cannot be deleted */ 58 | 403: unknown; 59 | /** FolderResource not found */ 60 | 404: unknown; 61 | }; 62 | }; 63 | }; 64 | "/files": { 65 | /** Retrieve a list of files */ 66 | get: { 67 | parameters: { 68 | query: { 69 | folder?: string; 70 | }; 71 | }; 72 | responses: { 73 | /** The list of files */ 74 | 200: { 75 | content: { 76 | "application/json": components["schemas"]["File"][]; 77 | }; 78 | }; 79 | 400: components["responses"]["400"]; 80 | 422: components["responses"]["validationError"]; 81 | }; 82 | }; 83 | /** Upload a new file */ 84 | post: { 85 | responses: { 86 | /** Uploaded file */ 87 | 201: { 88 | content: { 89 | "application/json": components["schemas"]["File"]; 90 | }; 91 | }; 92 | }; 93 | requestBody: { 94 | content: { 95 | "multipart/form-data": { 96 | /** Format: binary */ 97 | file: string; 98 | folder: string | number; 99 | }; 100 | }; 101 | }; 102 | }; 103 | }; 104 | "/files/{id}": { 105 | /** Delete a file */ 106 | delete: { 107 | parameters: { 108 | path: { 109 | id: string; 110 | }; 111 | }; 112 | responses: { 113 | /** No Content */ 114 | 204: never; 115 | /** File not found */ 116 | 404: unknown; 117 | }; 118 | }; 119 | }; 120 | } 121 | 122 | export interface components { 123 | schemas: { 124 | Folder: { 125 | id: components["schemas"]["ID"]; 126 | /** @description FolderResource name */ 127 | name: string; 128 | parent?: components["schemas"]["NullableID"]; 129 | }; 130 | File: { 131 | id: components["schemas"]["ID"]; 132 | /** @description FolderResource name */ 133 | name: string; 134 | /** @description Public url */ 135 | url: string; 136 | /** @description File size */ 137 | size?: number; 138 | folder?: components["schemas"]["NullableID"]; 139 | thumbnail: string; 140 | }; 141 | ID: string | number; 142 | NullableID: (string | null) | (number | null); 143 | }; 144 | responses: { 145 | /** Bad request */ 146 | 400: { 147 | content: { 148 | "application/json": unknown; 149 | }; 150 | }; 151 | /** ValidationError */ 152 | validationError: { 153 | content: { 154 | "application/json": { 155 | message?: string; 156 | errors?: { [key: string]: string[] }; 157 | }; 158 | }; 159 | }; 160 | }; 161 | } 162 | 163 | export interface operations {} 164 | 165 | export interface external {} 166 | -------------------------------------------------------------------------------- /tests/file-manager.test.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "@playwright/test"; 2 | import { dropFile } from "./utils"; 3 | import { mockApi } from "./mockApi"; 4 | 5 | const hashes = { 6 | "with API calls": "", 7 | "with functions": "#function", 8 | } as Record; 9 | Object.keys(hashes).forEach((key: keyof typeof hashes) => { 10 | test.describe(`FileManager ${key}`, () => { 11 | test.beforeEach(async ({ page }) => { 12 | await mockApi(page); 13 | return page.goto(`http://localhost:3000/index.html${hashes[key]}`); 14 | }); 15 | 16 | test.describe("Sidebar behaviour", () => { 17 | test("Show the root folder", async ({ page }) => { 18 | await expect(page.locator(".fm-folder-name").first()).toHaveText("/"); 19 | }); 20 | 21 | test("Show the list of folders", async ({ page }) => { 22 | await expect(page.locator("text=Folder 0").first()).toBeVisible(); 23 | await expect(page.locator("text=Folder 4").first()).toBeVisible(); 24 | await expect(page.locator("text=Child 2").first()).not.toBeVisible(); 25 | }); 26 | 27 | test("Unfold a folder on click", async ({ page }) => { 28 | await page.locator("text=Folder 2").first().click(); 29 | await expect(page.locator("text=Child 2").first()).toBeVisible(); 30 | }); 31 | 32 | test("Fold back the folder on click", async ({ page }) => { 33 | await page.locator("text=Folder 2").first().click(); 34 | await expect(page.locator("text=Child 2").first()).toBeVisible(); 35 | await page.locator("text=Folder 2").first().click({ force: true }); 36 | await expect(page.locator("text=Child 2").first()).not.toBeVisible(); 37 | }); 38 | 39 | test("Create a new folder on click", async ({ page }) => { 40 | await page.locator(".fm-new-folder").nth(3).click(); 41 | await page 42 | .locator(".fm-folder-form input") 43 | .waitFor({ state: "visible" }); 44 | await page.keyboard.type("hello"); 45 | await page.keyboard.press("Enter"); 46 | await page.locator("text=Folder 2").first().click({ force: true }); 47 | await expect(page.locator(".fm-folder-form")).not.toBeVisible(); 48 | await expect(page.locator("text=hello").first()).toBeVisible(); 49 | }); 50 | 51 | test("Delete a folder", async ({ page }) => { 52 | await page.locator("text=Empty").first().click(); 53 | await page.locator("text=Delete this folder").first().click(); 54 | await expect(page.locator("text=Empty").first()).toBeHidden(); 55 | }); 56 | }); 57 | 58 | test.describe("Files behaviour", () => { 59 | test("Load the file list when clicking on a specific folder", async ({ 60 | page, 61 | }) => { 62 | await page.locator("text=Folder 2").first().click(); 63 | await expect(page.locator("text=2_0.jpg").first()).toBeVisible(); 64 | }); 65 | test("Deleted file should be removed from the list", async ({ page }) => { 66 | page.on("dialog", (dialog) => dialog.accept()); 67 | await page.locator("text=Folder 2").first().click(); 68 | await expect(page.locator("text=2_0.jpg").first()).toBeVisible(); 69 | await page 70 | .locator('.fm-file:has-text("2_0.jpg")') 71 | .locator(".fm-delete") 72 | .click(); 73 | await expect(page.locator("text=2_0.jpg").first()).not.toBeVisible(); 74 | }); 75 | 76 | test("Drop file will upload it", async ({ page }) => { 77 | await page.locator("text=Folder 2").first().click(); 78 | await dropFile(page, ".fm-dropzone"); 79 | await expect(page.locator("text=new_file.png").first()).toBeVisible(); 80 | }); 81 | }); 82 | }); 83 | }); 84 | 85 | test.describe(`FileManager readonly`, () => { 86 | test.beforeEach(async ({ page }) => { 87 | return page.goto(`http://localhost:3000/index.html#readonly`); 88 | }); 89 | 90 | test.describe("Sidebar behaviour", () => { 91 | test("Hide the create folder button", async ({ page }) => { 92 | await expect(page.locator(".fm-new-folder")).toBeHidden(); 93 | }); 94 | 95 | test("Delete a folder", async ({ page }) => { 96 | await page.locator("text=Empty").first().click(); 97 | await expect( 98 | page.locator("text=Delete this folder").first() 99 | ).toBeHidden(); 100 | }); 101 | }); 102 | 103 | test.describe("Files behaviour", () => { 104 | test("Deleted file should be removed from the list", async ({ page }) => { 105 | page.on("dialog", (dialog) => dialog.accept()); 106 | await page.locator("text=Folder 2").first().click(); 107 | await expect(page.locator("text=2_0.jpg").first()).toBeVisible(); 108 | await expect( 109 | page.locator('.fm-file:has-text("2_0.jpg")').locator(".fm-delete") 110 | ).toBeHidden(); 111 | }); 112 | 113 | test("Drop file will upload it", async ({ page }) => { 114 | await page.locator("text=Folder 2").first().click(); 115 | await expect(page.locator(".fm-dropzone").first()).toBeHidden(); 116 | }); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /openapi.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | description: "API to manage files for website" 4 | version: 1.0.0 5 | title: "File manager API" 6 | paths: 7 | /folders: 8 | get: 9 | tags: 10 | - FolderResource 11 | description: "Retrieve a list of folders" 12 | parameters: 13 | - in: query 14 | name: parent 15 | required: false 16 | schema: 17 | type: string 18 | responses: 19 | "200": 20 | description: The list of folders 21 | content: 22 | application/json: 23 | schema: 24 | type: array 25 | items: 26 | $ref: '#/components/schemas/Folder' 27 | "400": 28 | $ref: '#/components/responses/400' 29 | post: 30 | tags: 31 | - FolderResource 32 | description: "Create a new folder" 33 | responses: 34 | "201": 35 | description: "Created folder" 36 | content: 37 | application/json: 38 | schema: 39 | $ref: '#/components/schemas/Folder' 40 | "422": 41 | $ref: '#/components/responses/validationError' 42 | requestBody: 43 | required: true 44 | content: 45 | application/json: 46 | schema: 47 | type: object 48 | required: 49 | - name 50 | properties: 51 | name: 52 | type: string 53 | parent: 54 | oneOf: 55 | - type: string 56 | nullable: true 57 | - type: integer 58 | nullable: true 59 | 60 | /folders/{id}: 61 | delete: 62 | tags: 63 | - FolderResource 64 | description: "Delete a folder" 65 | parameters: 66 | - in: path 67 | name: id 68 | required: true 69 | schema: 70 | type: string 71 | responses: 72 | "204": 73 | description: No Content 74 | "404": 75 | description: FolderResource not found 76 | "403": 77 | description: FolderResource cannot be deleted 78 | 79 | /files: 80 | get: 81 | tags: 82 | - File 83 | description: "Retrieve a list of files" 84 | parameters: 85 | - in: query 86 | name: folder 87 | required: false 88 | schema: 89 | type: string 90 | responses: 91 | "200": 92 | description: The list of files 93 | content: 94 | application/json: 95 | schema: 96 | type: array 97 | items: 98 | $ref: '#/components/schemas/File' 99 | "400": 100 | $ref: '#/components/responses/400' 101 | "422": 102 | $ref: '#/components/responses/validationError' 103 | post: 104 | tags: 105 | - File 106 | description: "Upload a new file" 107 | responses: 108 | "201": 109 | description: "Uploaded file" 110 | content: 111 | application/json: 112 | schema: 113 | $ref: '#/components/schemas/File' 114 | requestBody: 115 | required: true 116 | content: 117 | multipart/form-data: 118 | schema: 119 | type: object 120 | properties: 121 | file: 122 | type: string 123 | format: binary 124 | folder: 125 | oneOf: 126 | - type: string 127 | - type: integer 128 | required: 129 | - file 130 | - folder 131 | 132 | /files/{id}: 133 | delete: 134 | tags: 135 | - File 136 | description: "Delete a file" 137 | parameters: 138 | - in: path 139 | name: id 140 | required: true 141 | schema: 142 | type: string 143 | responses: 144 | "204": 145 | description: No Content 146 | "404": 147 | description: File not found 148 | 149 | 150 | components: 151 | responses: 152 | "400": 153 | description: Bad request 154 | content: 155 | application/json: {} 156 | validationError: 157 | description: ValidationError 158 | content: 159 | application/json: 160 | schema: 161 | type: object 162 | properties: 163 | message: 164 | type: string 165 | errors: 166 | type: object 167 | additionalProperties: 168 | type: array 169 | items: 170 | type: string 171 | schemas: 172 | Folder: 173 | type: object 174 | required: 175 | - id 176 | - name 177 | properties: 178 | id: 179 | $ref: '#/components/schemas/ID' 180 | name: 181 | type: string 182 | description: FolderResource name 183 | parent: 184 | $ref: '#/components/schemas/NullableID' 185 | File: 186 | type: object 187 | required: 188 | - id 189 | - name 190 | - url 191 | - thumbnail 192 | properties: 193 | id: 194 | $ref: '#/components/schemas/ID' 195 | name: 196 | type: string 197 | description: FolderResource name 198 | url: 199 | type: string 200 | description: Public url 201 | size: 202 | type: integer 203 | description: File size 204 | folder: 205 | $ref: '#/components/schemas/NullableID' 206 | thumbnail: 207 | type: string 208 | ID: 209 | oneOf: 210 | - type: string 211 | - type: integer 212 | NullableID: 213 | oneOf: 214 | - type: string 215 | nullable: true 216 | - type: integer 217 | nullable: true 218 | -------------------------------------------------------------------------------- /src/ui/Sidebar/Folder.svelte: -------------------------------------------------------------------------------- 1 | 77 | 78 | 121 | 122 | 206 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.3 2 | 3 | specifiers: 4 | '@playwright/test': ^1.20.0 5 | '@sveltejs/vite-plugin-svelte': ^1.0.0-next.30 6 | '@tsconfig/svelte': ^2.0.1 7 | openapi-typescript: ^5.1.1 8 | prettier: ^2.5.1 9 | svelte: ^3.46.4 10 | svelte-check: ^2.2.7 11 | svelte-preprocess: ^4.9.8 12 | tslib: ^2.3.1 13 | typescript: ^4.5.4 14 | utility-types: ^3.10.0 15 | vite: ^2.8.0 16 | 17 | dependencies: 18 | svelte: 3.46.4 19 | 20 | devDependencies: 21 | '@playwright/test': 1.20.0 22 | '@sveltejs/vite-plugin-svelte': 1.0.0-next.37_svelte@3.46.4+vite@2.8.0 23 | '@tsconfig/svelte': 2.0.1 24 | openapi-typescript: 5.1.1 25 | prettier: 2.5.1 26 | svelte-check: 2.4.3_svelte@3.46.4 27 | svelte-preprocess: 4.10.3_svelte@3.46.4+typescript@4.5.5 28 | tslib: 2.3.1 29 | typescript: 4.5.5 30 | utility-types: 3.10.0 31 | vite: 2.8.0 32 | 33 | packages: 34 | 35 | /@babel/code-frame/7.16.7: 36 | resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==} 37 | engines: {node: '>=6.9.0'} 38 | dependencies: 39 | '@babel/highlight': 7.16.10 40 | dev: true 41 | 42 | /@babel/compat-data/7.17.7: 43 | resolution: {integrity: sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==} 44 | engines: {node: '>=6.9.0'} 45 | dev: true 46 | 47 | /@babel/core/7.16.12: 48 | resolution: {integrity: sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==} 49 | engines: {node: '>=6.9.0'} 50 | dependencies: 51 | '@babel/code-frame': 7.16.7 52 | '@babel/generator': 7.17.7 53 | '@babel/helper-compilation-targets': 7.17.7_@babel+core@7.16.12 54 | '@babel/helper-module-transforms': 7.17.7 55 | '@babel/helpers': 7.17.8 56 | '@babel/parser': 7.17.8 57 | '@babel/template': 7.16.7 58 | '@babel/traverse': 7.17.3 59 | '@babel/types': 7.17.0 60 | convert-source-map: 1.8.0 61 | debug: 4.3.3 62 | gensync: 1.0.0-beta.2 63 | json5: 2.2.0 64 | semver: 6.3.0 65 | source-map: 0.5.7 66 | transitivePeerDependencies: 67 | - supports-color 68 | dev: true 69 | 70 | /@babel/generator/7.17.7: 71 | resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==} 72 | engines: {node: '>=6.9.0'} 73 | dependencies: 74 | '@babel/types': 7.17.0 75 | jsesc: 2.5.2 76 | source-map: 0.5.7 77 | dev: true 78 | 79 | /@babel/helper-annotate-as-pure/7.16.7: 80 | resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==} 81 | engines: {node: '>=6.9.0'} 82 | dependencies: 83 | '@babel/types': 7.17.0 84 | dev: true 85 | 86 | /@babel/helper-compilation-targets/7.17.7_@babel+core@7.16.12: 87 | resolution: {integrity: sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==} 88 | engines: {node: '>=6.9.0'} 89 | peerDependencies: 90 | '@babel/core': ^7.0.0 91 | dependencies: 92 | '@babel/compat-data': 7.17.7 93 | '@babel/core': 7.16.12 94 | '@babel/helper-validator-option': 7.16.7 95 | browserslist: 4.20.2 96 | semver: 6.3.0 97 | dev: true 98 | 99 | /@babel/helper-create-class-features-plugin/7.17.6_@babel+core@7.16.12: 100 | resolution: {integrity: sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==} 101 | engines: {node: '>=6.9.0'} 102 | peerDependencies: 103 | '@babel/core': ^7.0.0 104 | dependencies: 105 | '@babel/core': 7.16.12 106 | '@babel/helper-annotate-as-pure': 7.16.7 107 | '@babel/helper-environment-visitor': 7.16.7 108 | '@babel/helper-function-name': 7.16.7 109 | '@babel/helper-member-expression-to-functions': 7.17.7 110 | '@babel/helper-optimise-call-expression': 7.16.7 111 | '@babel/helper-replace-supers': 7.16.7 112 | '@babel/helper-split-export-declaration': 7.16.7 113 | transitivePeerDependencies: 114 | - supports-color 115 | dev: true 116 | 117 | /@babel/helper-environment-visitor/7.16.7: 118 | resolution: {integrity: sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==} 119 | engines: {node: '>=6.9.0'} 120 | dependencies: 121 | '@babel/types': 7.17.0 122 | dev: true 123 | 124 | /@babel/helper-function-name/7.16.7: 125 | resolution: {integrity: sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==} 126 | engines: {node: '>=6.9.0'} 127 | dependencies: 128 | '@babel/helper-get-function-arity': 7.16.7 129 | '@babel/template': 7.16.7 130 | '@babel/types': 7.17.0 131 | dev: true 132 | 133 | /@babel/helper-get-function-arity/7.16.7: 134 | resolution: {integrity: sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==} 135 | engines: {node: '>=6.9.0'} 136 | dependencies: 137 | '@babel/types': 7.17.0 138 | dev: true 139 | 140 | /@babel/helper-hoist-variables/7.16.7: 141 | resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==} 142 | engines: {node: '>=6.9.0'} 143 | dependencies: 144 | '@babel/types': 7.17.0 145 | dev: true 146 | 147 | /@babel/helper-member-expression-to-functions/7.17.7: 148 | resolution: {integrity: sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==} 149 | engines: {node: '>=6.9.0'} 150 | dependencies: 151 | '@babel/types': 7.17.0 152 | dev: true 153 | 154 | /@babel/helper-module-imports/7.16.7: 155 | resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==} 156 | engines: {node: '>=6.9.0'} 157 | dependencies: 158 | '@babel/types': 7.17.0 159 | dev: true 160 | 161 | /@babel/helper-module-transforms/7.17.7: 162 | resolution: {integrity: sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==} 163 | engines: {node: '>=6.9.0'} 164 | dependencies: 165 | '@babel/helper-environment-visitor': 7.16.7 166 | '@babel/helper-module-imports': 7.16.7 167 | '@babel/helper-simple-access': 7.17.7 168 | '@babel/helper-split-export-declaration': 7.16.7 169 | '@babel/helper-validator-identifier': 7.16.7 170 | '@babel/template': 7.16.7 171 | '@babel/traverse': 7.17.3 172 | '@babel/types': 7.17.0 173 | transitivePeerDependencies: 174 | - supports-color 175 | dev: true 176 | 177 | /@babel/helper-optimise-call-expression/7.16.7: 178 | resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==} 179 | engines: {node: '>=6.9.0'} 180 | dependencies: 181 | '@babel/types': 7.17.0 182 | dev: true 183 | 184 | /@babel/helper-plugin-utils/7.16.7: 185 | resolution: {integrity: sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==} 186 | engines: {node: '>=6.9.0'} 187 | dev: true 188 | 189 | /@babel/helper-replace-supers/7.16.7: 190 | resolution: {integrity: sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==} 191 | engines: {node: '>=6.9.0'} 192 | dependencies: 193 | '@babel/helper-environment-visitor': 7.16.7 194 | '@babel/helper-member-expression-to-functions': 7.17.7 195 | '@babel/helper-optimise-call-expression': 7.16.7 196 | '@babel/traverse': 7.17.3 197 | '@babel/types': 7.17.0 198 | transitivePeerDependencies: 199 | - supports-color 200 | dev: true 201 | 202 | /@babel/helper-simple-access/7.17.7: 203 | resolution: {integrity: sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==} 204 | engines: {node: '>=6.9.0'} 205 | dependencies: 206 | '@babel/types': 7.17.0 207 | dev: true 208 | 209 | /@babel/helper-skip-transparent-expression-wrappers/7.16.0: 210 | resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} 211 | engines: {node: '>=6.9.0'} 212 | dependencies: 213 | '@babel/types': 7.17.0 214 | dev: true 215 | 216 | /@babel/helper-split-export-declaration/7.16.7: 217 | resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==} 218 | engines: {node: '>=6.9.0'} 219 | dependencies: 220 | '@babel/types': 7.17.0 221 | dev: true 222 | 223 | /@babel/helper-validator-identifier/7.16.7: 224 | resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==} 225 | engines: {node: '>=6.9.0'} 226 | dev: true 227 | 228 | /@babel/helper-validator-option/7.16.7: 229 | resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==} 230 | engines: {node: '>=6.9.0'} 231 | dev: true 232 | 233 | /@babel/helpers/7.17.8: 234 | resolution: {integrity: sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==} 235 | engines: {node: '>=6.9.0'} 236 | dependencies: 237 | '@babel/template': 7.16.7 238 | '@babel/traverse': 7.17.3 239 | '@babel/types': 7.17.0 240 | transitivePeerDependencies: 241 | - supports-color 242 | dev: true 243 | 244 | /@babel/highlight/7.16.10: 245 | resolution: {integrity: sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==} 246 | engines: {node: '>=6.9.0'} 247 | dependencies: 248 | '@babel/helper-validator-identifier': 7.16.7 249 | chalk: 2.4.2 250 | js-tokens: 4.0.0 251 | dev: true 252 | 253 | /@babel/parser/7.17.8: 254 | resolution: {integrity: sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==} 255 | engines: {node: '>=6.0.0'} 256 | hasBin: true 257 | dev: true 258 | 259 | /@babel/plugin-proposal-class-properties/7.16.7_@babel+core@7.16.12: 260 | resolution: {integrity: sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==} 261 | engines: {node: '>=6.9.0'} 262 | peerDependencies: 263 | '@babel/core': ^7.0.0-0 264 | dependencies: 265 | '@babel/core': 7.16.12 266 | '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.16.12 267 | '@babel/helper-plugin-utils': 7.16.7 268 | transitivePeerDependencies: 269 | - supports-color 270 | dev: true 271 | 272 | /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.16.12: 273 | resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} 274 | engines: {node: '>=6.9.0'} 275 | peerDependencies: 276 | '@babel/core': ^7.0.0-0 277 | dependencies: 278 | '@babel/core': 7.16.12 279 | '@babel/helper-plugin-utils': 7.16.7 280 | '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.16.12 281 | dev: true 282 | 283 | /@babel/plugin-proposal-export-namespace-from/7.16.7_@babel+core@7.16.12: 284 | resolution: {integrity: sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==} 285 | engines: {node: '>=6.9.0'} 286 | peerDependencies: 287 | '@babel/core': ^7.0.0-0 288 | dependencies: 289 | '@babel/core': 7.16.12 290 | '@babel/helper-plugin-utils': 7.16.7 291 | '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.16.12 292 | dev: true 293 | 294 | /@babel/plugin-proposal-logical-assignment-operators/7.16.7_@babel+core@7.16.12: 295 | resolution: {integrity: sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==} 296 | engines: {node: '>=6.9.0'} 297 | peerDependencies: 298 | '@babel/core': ^7.0.0-0 299 | dependencies: 300 | '@babel/core': 7.16.12 301 | '@babel/helper-plugin-utils': 7.16.7 302 | '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.16.12 303 | dev: true 304 | 305 | /@babel/plugin-proposal-nullish-coalescing-operator/7.16.7_@babel+core@7.16.12: 306 | resolution: {integrity: sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==} 307 | engines: {node: '>=6.9.0'} 308 | peerDependencies: 309 | '@babel/core': ^7.0.0-0 310 | dependencies: 311 | '@babel/core': 7.16.12 312 | '@babel/helper-plugin-utils': 7.16.7 313 | '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.16.12 314 | dev: true 315 | 316 | /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.16.12: 317 | resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} 318 | engines: {node: '>=6.9.0'} 319 | peerDependencies: 320 | '@babel/core': ^7.0.0-0 321 | dependencies: 322 | '@babel/core': 7.16.12 323 | '@babel/helper-plugin-utils': 7.16.7 324 | '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.16.12 325 | dev: true 326 | 327 | /@babel/plugin-proposal-optional-chaining/7.16.7_@babel+core@7.16.12: 328 | resolution: {integrity: sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==} 329 | engines: {node: '>=6.9.0'} 330 | peerDependencies: 331 | '@babel/core': ^7.0.0-0 332 | dependencies: 333 | '@babel/core': 7.16.12 334 | '@babel/helper-plugin-utils': 7.16.7 335 | '@babel/helper-skip-transparent-expression-wrappers': 7.16.0 336 | '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.16.12 337 | dev: true 338 | 339 | /@babel/plugin-proposal-private-methods/7.16.11_@babel+core@7.16.12: 340 | resolution: {integrity: sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==} 341 | engines: {node: '>=6.9.0'} 342 | peerDependencies: 343 | '@babel/core': ^7.0.0-0 344 | dependencies: 345 | '@babel/core': 7.16.12 346 | '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.16.12 347 | '@babel/helper-plugin-utils': 7.16.7 348 | transitivePeerDependencies: 349 | - supports-color 350 | dev: true 351 | 352 | /@babel/plugin-proposal-private-property-in-object/7.16.7_@babel+core@7.16.12: 353 | resolution: {integrity: sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==} 354 | engines: {node: '>=6.9.0'} 355 | peerDependencies: 356 | '@babel/core': ^7.0.0-0 357 | dependencies: 358 | '@babel/core': 7.16.12 359 | '@babel/helper-annotate-as-pure': 7.16.7 360 | '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.16.12 361 | '@babel/helper-plugin-utils': 7.16.7 362 | '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.16.12 363 | transitivePeerDependencies: 364 | - supports-color 365 | dev: true 366 | 367 | /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.16.12: 368 | resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} 369 | peerDependencies: 370 | '@babel/core': ^7.0.0-0 371 | dependencies: 372 | '@babel/core': 7.16.12 373 | '@babel/helper-plugin-utils': 7.16.7 374 | dev: true 375 | 376 | /@babel/plugin-syntax-dynamic-import/7.8.3_@babel+core@7.16.12: 377 | resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} 378 | peerDependencies: 379 | '@babel/core': ^7.0.0-0 380 | dependencies: 381 | '@babel/core': 7.16.12 382 | '@babel/helper-plugin-utils': 7.16.7 383 | dev: true 384 | 385 | /@babel/plugin-syntax-export-namespace-from/7.8.3_@babel+core@7.16.12: 386 | resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} 387 | peerDependencies: 388 | '@babel/core': ^7.0.0-0 389 | dependencies: 390 | '@babel/core': 7.16.12 391 | '@babel/helper-plugin-utils': 7.16.7 392 | dev: true 393 | 394 | /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.16.12: 395 | resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} 396 | peerDependencies: 397 | '@babel/core': ^7.0.0-0 398 | dependencies: 399 | '@babel/core': 7.16.12 400 | '@babel/helper-plugin-utils': 7.16.7 401 | dev: true 402 | 403 | /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.16.12: 404 | resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} 405 | peerDependencies: 406 | '@babel/core': ^7.0.0-0 407 | dependencies: 408 | '@babel/core': 7.16.12 409 | '@babel/helper-plugin-utils': 7.16.7 410 | dev: true 411 | 412 | /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.16.12: 413 | resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} 414 | peerDependencies: 415 | '@babel/core': ^7.0.0-0 416 | dependencies: 417 | '@babel/core': 7.16.12 418 | '@babel/helper-plugin-utils': 7.16.7 419 | dev: true 420 | 421 | /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.16.12: 422 | resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} 423 | peerDependencies: 424 | '@babel/core': ^7.0.0-0 425 | dependencies: 426 | '@babel/core': 7.16.12 427 | '@babel/helper-plugin-utils': 7.16.7 428 | dev: true 429 | 430 | /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.16.12: 431 | resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} 432 | peerDependencies: 433 | '@babel/core': ^7.0.0-0 434 | dependencies: 435 | '@babel/core': 7.16.12 436 | '@babel/helper-plugin-utils': 7.16.7 437 | dev: true 438 | 439 | /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.16.12: 440 | resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} 441 | peerDependencies: 442 | '@babel/core': ^7.0.0-0 443 | dependencies: 444 | '@babel/core': 7.16.12 445 | '@babel/helper-plugin-utils': 7.16.7 446 | dev: true 447 | 448 | /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.16.12: 449 | resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} 450 | peerDependencies: 451 | '@babel/core': ^7.0.0-0 452 | dependencies: 453 | '@babel/core': 7.16.12 454 | '@babel/helper-plugin-utils': 7.16.7 455 | dev: true 456 | 457 | /@babel/plugin-syntax-private-property-in-object/7.14.5_@babel+core@7.16.12: 458 | resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} 459 | engines: {node: '>=6.9.0'} 460 | peerDependencies: 461 | '@babel/core': ^7.0.0-0 462 | dependencies: 463 | '@babel/core': 7.16.12 464 | '@babel/helper-plugin-utils': 7.16.7 465 | dev: true 466 | 467 | /@babel/plugin-syntax-typescript/7.16.7_@babel+core@7.16.12: 468 | resolution: {integrity: sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==} 469 | engines: {node: '>=6.9.0'} 470 | peerDependencies: 471 | '@babel/core': ^7.0.0-0 472 | dependencies: 473 | '@babel/core': 7.16.12 474 | '@babel/helper-plugin-utils': 7.16.7 475 | dev: true 476 | 477 | /@babel/plugin-transform-modules-commonjs/7.16.8_@babel+core@7.16.12: 478 | resolution: {integrity: sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==} 479 | engines: {node: '>=6.9.0'} 480 | peerDependencies: 481 | '@babel/core': ^7.0.0-0 482 | dependencies: 483 | '@babel/core': 7.16.12 484 | '@babel/helper-module-transforms': 7.17.7 485 | '@babel/helper-plugin-utils': 7.16.7 486 | '@babel/helper-simple-access': 7.17.7 487 | babel-plugin-dynamic-import-node: 2.3.3 488 | transitivePeerDependencies: 489 | - supports-color 490 | dev: true 491 | 492 | /@babel/plugin-transform-typescript/7.16.8_@babel+core@7.16.12: 493 | resolution: {integrity: sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==} 494 | engines: {node: '>=6.9.0'} 495 | peerDependencies: 496 | '@babel/core': ^7.0.0-0 497 | dependencies: 498 | '@babel/core': 7.16.12 499 | '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.16.12 500 | '@babel/helper-plugin-utils': 7.16.7 501 | '@babel/plugin-syntax-typescript': 7.16.7_@babel+core@7.16.12 502 | transitivePeerDependencies: 503 | - supports-color 504 | dev: true 505 | 506 | /@babel/preset-typescript/7.16.7_@babel+core@7.16.12: 507 | resolution: {integrity: sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==} 508 | engines: {node: '>=6.9.0'} 509 | peerDependencies: 510 | '@babel/core': ^7.0.0-0 511 | dependencies: 512 | '@babel/core': 7.16.12 513 | '@babel/helper-plugin-utils': 7.16.7 514 | '@babel/helper-validator-option': 7.16.7 515 | '@babel/plugin-transform-typescript': 7.16.8_@babel+core@7.16.12 516 | transitivePeerDependencies: 517 | - supports-color 518 | dev: true 519 | 520 | /@babel/template/7.16.7: 521 | resolution: {integrity: sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==} 522 | engines: {node: '>=6.9.0'} 523 | dependencies: 524 | '@babel/code-frame': 7.16.7 525 | '@babel/parser': 7.17.8 526 | '@babel/types': 7.17.0 527 | dev: true 528 | 529 | /@babel/traverse/7.17.3: 530 | resolution: {integrity: sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==} 531 | engines: {node: '>=6.9.0'} 532 | dependencies: 533 | '@babel/code-frame': 7.16.7 534 | '@babel/generator': 7.17.7 535 | '@babel/helper-environment-visitor': 7.16.7 536 | '@babel/helper-function-name': 7.16.7 537 | '@babel/helper-hoist-variables': 7.16.7 538 | '@babel/helper-split-export-declaration': 7.16.7 539 | '@babel/parser': 7.17.8 540 | '@babel/types': 7.17.0 541 | debug: 4.3.3 542 | globals: 11.12.0 543 | transitivePeerDependencies: 544 | - supports-color 545 | dev: true 546 | 547 | /@babel/types/7.17.0: 548 | resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} 549 | engines: {node: '>=6.9.0'} 550 | dependencies: 551 | '@babel/helper-validator-identifier': 7.16.7 552 | to-fast-properties: 2.0.0 553 | dev: true 554 | 555 | /@jest/types/27.5.1: 556 | resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} 557 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 558 | dependencies: 559 | '@types/istanbul-lib-coverage': 2.0.4 560 | '@types/istanbul-reports': 3.0.1 561 | '@types/node': 17.0.17 562 | '@types/yargs': 16.0.4 563 | chalk: 4.1.2 564 | dev: true 565 | 566 | /@nodelib/fs.scandir/2.1.5: 567 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 568 | engines: {node: '>= 8'} 569 | dependencies: 570 | '@nodelib/fs.stat': 2.0.5 571 | run-parallel: 1.2.0 572 | dev: true 573 | 574 | /@nodelib/fs.stat/2.0.5: 575 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 576 | engines: {node: '>= 8'} 577 | dev: true 578 | 579 | /@nodelib/fs.walk/1.2.8: 580 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 581 | engines: {node: '>= 8'} 582 | dependencies: 583 | '@nodelib/fs.scandir': 2.1.5 584 | fastq: 1.13.0 585 | dev: true 586 | 587 | /@playwright/test/1.20.0: 588 | resolution: {integrity: sha512-UpI5HTcgNLckR0kqXqwNvbcIXtRaDxk+hnO0OBwPSjfbBjRfRgAJ2ClA/b30C5E3UW5dJa17zhsy2qrk66l5cg==} 589 | engines: {node: '>=12'} 590 | hasBin: true 591 | dependencies: 592 | '@babel/code-frame': 7.16.7 593 | '@babel/core': 7.16.12 594 | '@babel/helper-plugin-utils': 7.16.7 595 | '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.16.12 596 | '@babel/plugin-proposal-dynamic-import': 7.16.7_@babel+core@7.16.12 597 | '@babel/plugin-proposal-export-namespace-from': 7.16.7_@babel+core@7.16.12 598 | '@babel/plugin-proposal-logical-assignment-operators': 7.16.7_@babel+core@7.16.12 599 | '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.16.12 600 | '@babel/plugin-proposal-numeric-separator': 7.16.7_@babel+core@7.16.12 601 | '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.16.12 602 | '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.16.12 603 | '@babel/plugin-proposal-private-property-in-object': 7.16.7_@babel+core@7.16.12 604 | '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.16.12 605 | '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.16.12 606 | '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.16.12 607 | '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.16.12 608 | '@babel/plugin-transform-modules-commonjs': 7.16.8_@babel+core@7.16.12 609 | '@babel/preset-typescript': 7.16.7_@babel+core@7.16.12 610 | colors: 1.4.0 611 | commander: 8.3.0 612 | debug: 4.3.3 613 | expect: 27.2.5 614 | jest-matcher-utils: 27.2.5 615 | json5: 2.2.0 616 | mime: 3.0.0 617 | minimatch: 3.0.4 618 | ms: 2.1.3 619 | open: 8.4.0 620 | pirates: 4.0.4 621 | playwright-core: 1.20.0 622 | rimraf: 3.0.2 623 | source-map-support: 0.4.18 624 | stack-utils: 2.0.5 625 | yazl: 2.5.1 626 | transitivePeerDependencies: 627 | - bufferutil 628 | - supports-color 629 | - utf-8-validate 630 | dev: true 631 | 632 | /@rollup/pluginutils/4.1.2: 633 | resolution: {integrity: sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==} 634 | engines: {node: '>= 8.0.0'} 635 | dependencies: 636 | estree-walker: 2.0.2 637 | picomatch: 2.3.1 638 | dev: true 639 | 640 | /@sveltejs/vite-plugin-svelte/1.0.0-next.37_svelte@3.46.4+vite@2.8.0: 641 | resolution: {integrity: sha512-EdSXw2rXeOahNrQfMJVZxa/NxZxW1a0TiBI3s+pVxnxU14hEQtnkLtdbTFhnceu22gJpNPFSIJRcIwRBBDQIeA==} 642 | engines: {node: ^14.13.1 || >= 16} 643 | peerDependencies: 644 | diff-match-patch: ^1.0.5 645 | svelte: ^3.44.0 646 | vite: ^2.7.0 647 | peerDependenciesMeta: 648 | diff-match-patch: 649 | optional: true 650 | dependencies: 651 | '@rollup/pluginutils': 4.1.2 652 | debug: 4.3.3 653 | kleur: 4.1.4 654 | magic-string: 0.25.7 655 | svelte: 3.46.4 656 | svelte-hmr: 0.14.9_svelte@3.46.4 657 | vite: 2.8.0 658 | transitivePeerDependencies: 659 | - supports-color 660 | dev: true 661 | 662 | /@tsconfig/svelte/2.0.1: 663 | resolution: {integrity: sha512-aqkICXbM1oX5FfgZd2qSSAGdyo/NRxjWCamxoyi3T8iVQnzGge19HhDYzZ6NrVOW7bhcWNSq9XexWFtMzbB24A==} 664 | dev: true 665 | 666 | /@types/istanbul-lib-coverage/2.0.4: 667 | resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} 668 | dev: true 669 | 670 | /@types/istanbul-lib-report/3.0.0: 671 | resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} 672 | dependencies: 673 | '@types/istanbul-lib-coverage': 2.0.4 674 | dev: true 675 | 676 | /@types/istanbul-reports/3.0.1: 677 | resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} 678 | dependencies: 679 | '@types/istanbul-lib-report': 3.0.0 680 | dev: true 681 | 682 | /@types/node/17.0.17: 683 | resolution: {integrity: sha512-e8PUNQy1HgJGV3iU/Bp2+D/DXh3PYeyli8LgIwsQcs1Ar1LoaWHSIT6Rw+H2rNJmiq6SNWiDytfx8+gYj7wDHw==} 684 | dev: true 685 | 686 | /@types/pug/2.0.6: 687 | resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} 688 | dev: true 689 | 690 | /@types/sass/1.43.1: 691 | resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} 692 | dependencies: 693 | '@types/node': 17.0.17 694 | dev: true 695 | 696 | /@types/stack-utils/2.0.1: 697 | resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} 698 | dev: true 699 | 700 | /@types/yargs-parser/21.0.0: 701 | resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} 702 | dev: true 703 | 704 | /@types/yargs/16.0.4: 705 | resolution: {integrity: sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==} 706 | dependencies: 707 | '@types/yargs-parser': 21.0.0 708 | dev: true 709 | 710 | /@types/yauzl/2.9.2: 711 | resolution: {integrity: sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==} 712 | requiresBuild: true 713 | dependencies: 714 | '@types/node': 17.0.17 715 | dev: true 716 | optional: true 717 | 718 | /agent-base/6.0.2: 719 | resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} 720 | engines: {node: '>= 6.0.0'} 721 | dependencies: 722 | debug: 4.3.3 723 | transitivePeerDependencies: 724 | - supports-color 725 | dev: true 726 | 727 | /ansi-regex/5.0.1: 728 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 729 | engines: {node: '>=8'} 730 | dev: true 731 | 732 | /ansi-styles/3.2.1: 733 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 734 | engines: {node: '>=4'} 735 | dependencies: 736 | color-convert: 1.9.3 737 | dev: true 738 | 739 | /ansi-styles/4.3.0: 740 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 741 | engines: {node: '>=8'} 742 | dependencies: 743 | color-convert: 2.0.1 744 | dev: true 745 | 746 | /ansi-styles/5.2.0: 747 | resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} 748 | engines: {node: '>=10'} 749 | dev: true 750 | 751 | /anymatch/3.1.2: 752 | resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} 753 | engines: {node: '>= 8'} 754 | dependencies: 755 | normalize-path: 3.0.0 756 | picomatch: 2.3.1 757 | dev: true 758 | 759 | /argparse/2.0.1: 760 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 761 | dev: true 762 | 763 | /babel-plugin-dynamic-import-node/2.3.3: 764 | resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} 765 | dependencies: 766 | object.assign: 4.1.2 767 | dev: true 768 | 769 | /balanced-match/1.0.2: 770 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 771 | dev: true 772 | 773 | /binary-extensions/2.2.0: 774 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 775 | engines: {node: '>=8'} 776 | dev: true 777 | 778 | /brace-expansion/1.1.11: 779 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 780 | dependencies: 781 | balanced-match: 1.0.2 782 | concat-map: 0.0.1 783 | dev: true 784 | 785 | /braces/3.0.2: 786 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 787 | engines: {node: '>=8'} 788 | dependencies: 789 | fill-range: 7.0.1 790 | dev: true 791 | 792 | /browserslist/4.20.2: 793 | resolution: {integrity: sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==} 794 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 795 | hasBin: true 796 | dependencies: 797 | caniuse-lite: 1.0.30001319 798 | electron-to-chromium: 1.4.88 799 | escalade: 3.1.1 800 | node-releases: 2.0.2 801 | picocolors: 1.0.0 802 | dev: true 803 | 804 | /buffer-crc32/0.2.13: 805 | resolution: {integrity: sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=} 806 | dev: true 807 | 808 | /call-bind/1.0.2: 809 | resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} 810 | dependencies: 811 | function-bind: 1.1.1 812 | get-intrinsic: 1.1.1 813 | dev: true 814 | 815 | /callsites/3.1.0: 816 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 817 | engines: {node: '>=6'} 818 | dev: true 819 | 820 | /caniuse-lite/1.0.30001319: 821 | resolution: {integrity: sha512-xjlIAFHucBRSMUo1kb5D4LYgcN1M45qdKP++lhqowDpwJwGkpIRTt5qQqnhxjj1vHcI7nrJxWhCC1ATrCEBTcw==} 822 | dev: true 823 | 824 | /chalk/2.4.2: 825 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 826 | engines: {node: '>=4'} 827 | dependencies: 828 | ansi-styles: 3.2.1 829 | escape-string-regexp: 1.0.5 830 | supports-color: 5.5.0 831 | dev: true 832 | 833 | /chalk/4.1.2: 834 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 835 | engines: {node: '>=10'} 836 | dependencies: 837 | ansi-styles: 4.3.0 838 | supports-color: 7.2.0 839 | dev: true 840 | 841 | /chokidar/3.5.3: 842 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 843 | engines: {node: '>= 8.10.0'} 844 | dependencies: 845 | anymatch: 3.1.2 846 | braces: 3.0.2 847 | glob-parent: 5.1.2 848 | is-binary-path: 2.1.0 849 | is-glob: 4.0.3 850 | normalize-path: 3.0.0 851 | readdirp: 3.6.0 852 | optionalDependencies: 853 | fsevents: 2.3.2 854 | dev: true 855 | 856 | /color-convert/1.9.3: 857 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 858 | dependencies: 859 | color-name: 1.1.3 860 | dev: true 861 | 862 | /color-convert/2.0.1: 863 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 864 | engines: {node: '>=7.0.0'} 865 | dependencies: 866 | color-name: 1.1.4 867 | dev: true 868 | 869 | /color-name/1.1.3: 870 | resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=} 871 | dev: true 872 | 873 | /color-name/1.1.4: 874 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 875 | dev: true 876 | 877 | /colors/1.4.0: 878 | resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} 879 | engines: {node: '>=0.1.90'} 880 | dev: true 881 | 882 | /commander/8.3.0: 883 | resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} 884 | engines: {node: '>= 12'} 885 | dev: true 886 | 887 | /concat-map/0.0.1: 888 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 889 | dev: true 890 | 891 | /convert-source-map/1.8.0: 892 | resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} 893 | dependencies: 894 | safe-buffer: 5.1.2 895 | dev: true 896 | 897 | /debug/4.3.3: 898 | resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} 899 | engines: {node: '>=6.0'} 900 | peerDependencies: 901 | supports-color: '*' 902 | peerDependenciesMeta: 903 | supports-color: 904 | optional: true 905 | dependencies: 906 | ms: 2.1.2 907 | dev: true 908 | 909 | /define-lazy-prop/2.0.0: 910 | resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} 911 | engines: {node: '>=8'} 912 | dev: true 913 | 914 | /define-properties/1.1.3: 915 | resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==} 916 | engines: {node: '>= 0.4'} 917 | dependencies: 918 | object-keys: 1.1.1 919 | dev: true 920 | 921 | /detect-indent/6.1.0: 922 | resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} 923 | engines: {node: '>=8'} 924 | dev: true 925 | 926 | /diff-sequences/27.5.1: 927 | resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} 928 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 929 | dev: true 930 | 931 | /electron-to-chromium/1.4.88: 932 | resolution: {integrity: sha512-oA7mzccefkvTNi9u7DXmT0LqvhnOiN2BhSrKerta7HeUC1cLoIwtbf2wL+Ah2ozh5KQd3/1njrGrwDBXx6d14Q==} 933 | dev: true 934 | 935 | /end-of-stream/1.4.4: 936 | resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} 937 | dependencies: 938 | once: 1.4.0 939 | dev: true 940 | 941 | /es6-promise/3.3.1: 942 | resolution: {integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=} 943 | dev: true 944 | 945 | /esbuild-android-64/0.14.27: 946 | resolution: {integrity: sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ==} 947 | engines: {node: '>=12'} 948 | cpu: [x64] 949 | os: [android] 950 | requiresBuild: true 951 | dev: true 952 | optional: true 953 | 954 | /esbuild-android-arm64/0.14.27: 955 | resolution: {integrity: sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ==} 956 | engines: {node: '>=12'} 957 | cpu: [arm64] 958 | os: [android] 959 | requiresBuild: true 960 | dev: true 961 | optional: true 962 | 963 | /esbuild-darwin-64/0.14.27: 964 | resolution: {integrity: sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g==} 965 | engines: {node: '>=12'} 966 | cpu: [x64] 967 | os: [darwin] 968 | requiresBuild: true 969 | dev: true 970 | optional: true 971 | 972 | /esbuild-darwin-arm64/0.14.27: 973 | resolution: {integrity: sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ==} 974 | engines: {node: '>=12'} 975 | cpu: [arm64] 976 | os: [darwin] 977 | requiresBuild: true 978 | dev: true 979 | optional: true 980 | 981 | /esbuild-freebsd-64/0.14.27: 982 | resolution: {integrity: sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA==} 983 | engines: {node: '>=12'} 984 | cpu: [x64] 985 | os: [freebsd] 986 | requiresBuild: true 987 | dev: true 988 | optional: true 989 | 990 | /esbuild-freebsd-arm64/0.14.27: 991 | resolution: {integrity: sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA==} 992 | engines: {node: '>=12'} 993 | cpu: [arm64] 994 | os: [freebsd] 995 | requiresBuild: true 996 | dev: true 997 | optional: true 998 | 999 | /esbuild-linux-32/0.14.27: 1000 | resolution: {integrity: sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw==} 1001 | engines: {node: '>=12'} 1002 | cpu: [ia32] 1003 | os: [linux] 1004 | requiresBuild: true 1005 | dev: true 1006 | optional: true 1007 | 1008 | /esbuild-linux-64/0.14.27: 1009 | resolution: {integrity: sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg==} 1010 | engines: {node: '>=12'} 1011 | cpu: [x64] 1012 | os: [linux] 1013 | requiresBuild: true 1014 | dev: true 1015 | optional: true 1016 | 1017 | /esbuild-linux-arm/0.14.27: 1018 | resolution: {integrity: sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw==} 1019 | engines: {node: '>=12'} 1020 | cpu: [arm] 1021 | os: [linux] 1022 | requiresBuild: true 1023 | dev: true 1024 | optional: true 1025 | 1026 | /esbuild-linux-arm64/0.14.27: 1027 | resolution: {integrity: sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ==} 1028 | engines: {node: '>=12'} 1029 | cpu: [arm64] 1030 | os: [linux] 1031 | requiresBuild: true 1032 | dev: true 1033 | optional: true 1034 | 1035 | /esbuild-linux-mips64le/0.14.27: 1036 | resolution: {integrity: sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A==} 1037 | engines: {node: '>=12'} 1038 | cpu: [mips64el] 1039 | os: [linux] 1040 | requiresBuild: true 1041 | dev: true 1042 | optional: true 1043 | 1044 | /esbuild-linux-ppc64le/0.14.27: 1045 | resolution: {integrity: sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA==} 1046 | engines: {node: '>=12'} 1047 | cpu: [ppc64] 1048 | os: [linux] 1049 | requiresBuild: true 1050 | dev: true 1051 | optional: true 1052 | 1053 | /esbuild-linux-riscv64/0.14.27: 1054 | resolution: {integrity: sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg==} 1055 | engines: {node: '>=12'} 1056 | cpu: [riscv64] 1057 | os: [linux] 1058 | requiresBuild: true 1059 | dev: true 1060 | optional: true 1061 | 1062 | /esbuild-linux-s390x/0.14.27: 1063 | resolution: {integrity: sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg==} 1064 | engines: {node: '>=12'} 1065 | cpu: [s390x] 1066 | os: [linux] 1067 | requiresBuild: true 1068 | dev: true 1069 | optional: true 1070 | 1071 | /esbuild-netbsd-64/0.14.27: 1072 | resolution: {integrity: sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q==} 1073 | engines: {node: '>=12'} 1074 | cpu: [x64] 1075 | os: [netbsd] 1076 | requiresBuild: true 1077 | dev: true 1078 | optional: true 1079 | 1080 | /esbuild-openbsd-64/0.14.27: 1081 | resolution: {integrity: sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw==} 1082 | engines: {node: '>=12'} 1083 | cpu: [x64] 1084 | os: [openbsd] 1085 | requiresBuild: true 1086 | dev: true 1087 | optional: true 1088 | 1089 | /esbuild-sunos-64/0.14.27: 1090 | resolution: {integrity: sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg==} 1091 | engines: {node: '>=12'} 1092 | cpu: [x64] 1093 | os: [sunos] 1094 | requiresBuild: true 1095 | dev: true 1096 | optional: true 1097 | 1098 | /esbuild-windows-32/0.14.27: 1099 | resolution: {integrity: sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw==} 1100 | engines: {node: '>=12'} 1101 | cpu: [ia32] 1102 | os: [win32] 1103 | requiresBuild: true 1104 | dev: true 1105 | optional: true 1106 | 1107 | /esbuild-windows-64/0.14.27: 1108 | resolution: {integrity: sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg==} 1109 | engines: {node: '>=12'} 1110 | cpu: [x64] 1111 | os: [win32] 1112 | requiresBuild: true 1113 | dev: true 1114 | optional: true 1115 | 1116 | /esbuild-windows-arm64/0.14.27: 1117 | resolution: {integrity: sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg==} 1118 | engines: {node: '>=12'} 1119 | cpu: [arm64] 1120 | os: [win32] 1121 | requiresBuild: true 1122 | dev: true 1123 | optional: true 1124 | 1125 | /esbuild/0.14.27: 1126 | resolution: {integrity: sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q==} 1127 | engines: {node: '>=12'} 1128 | hasBin: true 1129 | requiresBuild: true 1130 | optionalDependencies: 1131 | esbuild-android-64: 0.14.27 1132 | esbuild-android-arm64: 0.14.27 1133 | esbuild-darwin-64: 0.14.27 1134 | esbuild-darwin-arm64: 0.14.27 1135 | esbuild-freebsd-64: 0.14.27 1136 | esbuild-freebsd-arm64: 0.14.27 1137 | esbuild-linux-32: 0.14.27 1138 | esbuild-linux-64: 0.14.27 1139 | esbuild-linux-arm: 0.14.27 1140 | esbuild-linux-arm64: 0.14.27 1141 | esbuild-linux-mips64le: 0.14.27 1142 | esbuild-linux-ppc64le: 0.14.27 1143 | esbuild-linux-riscv64: 0.14.27 1144 | esbuild-linux-s390x: 0.14.27 1145 | esbuild-netbsd-64: 0.14.27 1146 | esbuild-openbsd-64: 0.14.27 1147 | esbuild-sunos-64: 0.14.27 1148 | esbuild-windows-32: 0.14.27 1149 | esbuild-windows-64: 0.14.27 1150 | esbuild-windows-arm64: 0.14.27 1151 | dev: true 1152 | 1153 | /escalade/3.1.1: 1154 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 1155 | engines: {node: '>=6'} 1156 | dev: true 1157 | 1158 | /escape-string-regexp/1.0.5: 1159 | resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=} 1160 | engines: {node: '>=0.8.0'} 1161 | dev: true 1162 | 1163 | /escape-string-regexp/2.0.0: 1164 | resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} 1165 | engines: {node: '>=8'} 1166 | dev: true 1167 | 1168 | /estree-walker/2.0.2: 1169 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 1170 | dev: true 1171 | 1172 | /expect/27.2.5: 1173 | resolution: {integrity: sha512-ZrO0w7bo8BgGoP/bLz+HDCI+0Hfei9jUSZs5yI/Wyn9VkG9w8oJ7rHRgYj+MA7yqqFa0IwHA3flJzZtYugShJA==} 1174 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 1175 | dependencies: 1176 | '@jest/types': 27.5.1 1177 | ansi-styles: 5.2.0 1178 | jest-get-type: 27.5.1 1179 | jest-matcher-utils: 27.2.5 1180 | jest-message-util: 27.5.1 1181 | jest-regex-util: 27.5.1 1182 | dev: true 1183 | 1184 | /extract-zip/2.0.1: 1185 | resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} 1186 | engines: {node: '>= 10.17.0'} 1187 | hasBin: true 1188 | dependencies: 1189 | debug: 4.3.3 1190 | get-stream: 5.2.0 1191 | yauzl: 2.10.0 1192 | optionalDependencies: 1193 | '@types/yauzl': 2.9.2 1194 | transitivePeerDependencies: 1195 | - supports-color 1196 | dev: true 1197 | 1198 | /fast-glob/3.2.11: 1199 | resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} 1200 | engines: {node: '>=8.6.0'} 1201 | dependencies: 1202 | '@nodelib/fs.stat': 2.0.5 1203 | '@nodelib/fs.walk': 1.2.8 1204 | glob-parent: 5.1.2 1205 | merge2: 1.4.1 1206 | micromatch: 4.0.4 1207 | dev: true 1208 | 1209 | /fastq/1.13.0: 1210 | resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} 1211 | dependencies: 1212 | reusify: 1.0.4 1213 | dev: true 1214 | 1215 | /fd-slicer/1.1.0: 1216 | resolution: {integrity: sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=} 1217 | dependencies: 1218 | pend: 1.2.0 1219 | dev: true 1220 | 1221 | /fill-range/7.0.1: 1222 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 1223 | engines: {node: '>=8'} 1224 | dependencies: 1225 | to-regex-range: 5.0.1 1226 | dev: true 1227 | 1228 | /fs.realpath/1.0.0: 1229 | resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} 1230 | dev: true 1231 | 1232 | /fsevents/2.3.2: 1233 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 1234 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1235 | os: [darwin] 1236 | requiresBuild: true 1237 | dev: true 1238 | optional: true 1239 | 1240 | /function-bind/1.1.1: 1241 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} 1242 | dev: true 1243 | 1244 | /gensync/1.0.0-beta.2: 1245 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 1246 | engines: {node: '>=6.9.0'} 1247 | dev: true 1248 | 1249 | /get-intrinsic/1.1.1: 1250 | resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} 1251 | dependencies: 1252 | function-bind: 1.1.1 1253 | has: 1.0.3 1254 | has-symbols: 1.0.3 1255 | dev: true 1256 | 1257 | /get-stream/5.2.0: 1258 | resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} 1259 | engines: {node: '>=8'} 1260 | dependencies: 1261 | pump: 3.0.0 1262 | dev: true 1263 | 1264 | /glob-parent/5.1.2: 1265 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1266 | engines: {node: '>= 6'} 1267 | dependencies: 1268 | is-glob: 4.0.3 1269 | dev: true 1270 | 1271 | /glob/7.2.0: 1272 | resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} 1273 | dependencies: 1274 | fs.realpath: 1.0.0 1275 | inflight: 1.0.6 1276 | inherits: 2.0.4 1277 | minimatch: 3.0.5 1278 | once: 1.4.0 1279 | path-is-absolute: 1.0.1 1280 | dev: true 1281 | 1282 | /globals/11.12.0: 1283 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 1284 | engines: {node: '>=4'} 1285 | dev: true 1286 | 1287 | /globalyzer/0.1.0: 1288 | resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} 1289 | dev: true 1290 | 1291 | /globrex/0.1.2: 1292 | resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} 1293 | dev: true 1294 | 1295 | /graceful-fs/4.2.9: 1296 | resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==} 1297 | dev: true 1298 | 1299 | /has-flag/3.0.0: 1300 | resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=} 1301 | engines: {node: '>=4'} 1302 | dev: true 1303 | 1304 | /has-flag/4.0.0: 1305 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 1306 | engines: {node: '>=8'} 1307 | dev: true 1308 | 1309 | /has-symbols/1.0.3: 1310 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 1311 | engines: {node: '>= 0.4'} 1312 | dev: true 1313 | 1314 | /has/1.0.3: 1315 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} 1316 | engines: {node: '>= 0.4.0'} 1317 | dependencies: 1318 | function-bind: 1.1.1 1319 | dev: true 1320 | 1321 | /https-proxy-agent/5.0.0: 1322 | resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==} 1323 | engines: {node: '>= 6'} 1324 | dependencies: 1325 | agent-base: 6.0.2 1326 | debug: 4.3.3 1327 | transitivePeerDependencies: 1328 | - supports-color 1329 | dev: true 1330 | 1331 | /import-fresh/3.3.0: 1332 | resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} 1333 | engines: {node: '>=6'} 1334 | dependencies: 1335 | parent-module: 1.0.1 1336 | resolve-from: 4.0.0 1337 | dev: true 1338 | 1339 | /inflight/1.0.6: 1340 | resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} 1341 | dependencies: 1342 | once: 1.4.0 1343 | wrappy: 1.0.2 1344 | dev: true 1345 | 1346 | /inherits/2.0.4: 1347 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 1348 | dev: true 1349 | 1350 | /ip/1.1.5: 1351 | resolution: {integrity: sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=} 1352 | dev: true 1353 | 1354 | /is-binary-path/2.1.0: 1355 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 1356 | engines: {node: '>=8'} 1357 | dependencies: 1358 | binary-extensions: 2.2.0 1359 | dev: true 1360 | 1361 | /is-core-module/2.8.1: 1362 | resolution: {integrity: sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==} 1363 | dependencies: 1364 | has: 1.0.3 1365 | dev: true 1366 | 1367 | /is-docker/2.2.1: 1368 | resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} 1369 | engines: {node: '>=8'} 1370 | hasBin: true 1371 | dev: true 1372 | 1373 | /is-extglob/2.1.1: 1374 | resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} 1375 | engines: {node: '>=0.10.0'} 1376 | dev: true 1377 | 1378 | /is-glob/4.0.3: 1379 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1380 | engines: {node: '>=0.10.0'} 1381 | dependencies: 1382 | is-extglob: 2.1.1 1383 | dev: true 1384 | 1385 | /is-number/7.0.0: 1386 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1387 | engines: {node: '>=0.12.0'} 1388 | dev: true 1389 | 1390 | /is-wsl/2.2.0: 1391 | resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} 1392 | engines: {node: '>=8'} 1393 | dependencies: 1394 | is-docker: 2.2.1 1395 | dev: true 1396 | 1397 | /jest-diff/27.5.1: 1398 | resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} 1399 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 1400 | dependencies: 1401 | chalk: 4.1.2 1402 | diff-sequences: 27.5.1 1403 | jest-get-type: 27.5.1 1404 | pretty-format: 27.5.1 1405 | dev: true 1406 | 1407 | /jest-get-type/27.5.1: 1408 | resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} 1409 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 1410 | dev: true 1411 | 1412 | /jest-matcher-utils/27.2.5: 1413 | resolution: {integrity: sha512-qNR/kh6bz0Dyv3m68Ck2g1fLW5KlSOUNcFQh87VXHZwWc/gY6XwnKofx76Qytz3x5LDWT09/2+yXndTkaG4aWg==} 1414 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 1415 | dependencies: 1416 | chalk: 4.1.2 1417 | jest-diff: 27.5.1 1418 | jest-get-type: 27.5.1 1419 | pretty-format: 27.5.1 1420 | dev: true 1421 | 1422 | /jest-message-util/27.5.1: 1423 | resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} 1424 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 1425 | dependencies: 1426 | '@babel/code-frame': 7.16.7 1427 | '@jest/types': 27.5.1 1428 | '@types/stack-utils': 2.0.1 1429 | chalk: 4.1.2 1430 | graceful-fs: 4.2.9 1431 | micromatch: 4.0.4 1432 | pretty-format: 27.5.1 1433 | slash: 3.0.0 1434 | stack-utils: 2.0.5 1435 | dev: true 1436 | 1437 | /jest-regex-util/27.5.1: 1438 | resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} 1439 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 1440 | dev: true 1441 | 1442 | /jpeg-js/0.4.3: 1443 | resolution: {integrity: sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==} 1444 | dev: true 1445 | 1446 | /js-tokens/4.0.0: 1447 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 1448 | dev: true 1449 | 1450 | /js-yaml/4.1.0: 1451 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 1452 | hasBin: true 1453 | dependencies: 1454 | argparse: 2.0.1 1455 | dev: true 1456 | 1457 | /jsesc/2.5.2: 1458 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} 1459 | engines: {node: '>=4'} 1460 | hasBin: true 1461 | dev: true 1462 | 1463 | /json5/2.2.0: 1464 | resolution: {integrity: sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==} 1465 | engines: {node: '>=6'} 1466 | hasBin: true 1467 | dependencies: 1468 | minimist: 1.2.5 1469 | dev: true 1470 | 1471 | /kleur/4.1.4: 1472 | resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==} 1473 | engines: {node: '>=6'} 1474 | dev: true 1475 | 1476 | /magic-string/0.25.7: 1477 | resolution: {integrity: sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==} 1478 | dependencies: 1479 | sourcemap-codec: 1.4.8 1480 | dev: true 1481 | 1482 | /merge2/1.4.1: 1483 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1484 | engines: {node: '>= 8'} 1485 | dev: true 1486 | 1487 | /micromatch/4.0.4: 1488 | resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} 1489 | engines: {node: '>=8.6'} 1490 | dependencies: 1491 | braces: 3.0.2 1492 | picomatch: 2.3.1 1493 | dev: true 1494 | 1495 | /mime/3.0.0: 1496 | resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} 1497 | engines: {node: '>=10.0.0'} 1498 | hasBin: true 1499 | dev: true 1500 | 1501 | /min-indent/1.0.1: 1502 | resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} 1503 | engines: {node: '>=4'} 1504 | dev: true 1505 | 1506 | /minimatch/3.0.4: 1507 | resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} 1508 | dependencies: 1509 | brace-expansion: 1.1.11 1510 | dev: true 1511 | 1512 | /minimatch/3.0.5: 1513 | resolution: {integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==} 1514 | dependencies: 1515 | brace-expansion: 1.1.11 1516 | dev: true 1517 | 1518 | /minimist/1.2.5: 1519 | resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} 1520 | dev: true 1521 | 1522 | /mkdirp/0.5.5: 1523 | resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==} 1524 | hasBin: true 1525 | dependencies: 1526 | minimist: 1.2.5 1527 | dev: true 1528 | 1529 | /mri/1.2.0: 1530 | resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} 1531 | engines: {node: '>=4'} 1532 | dev: true 1533 | 1534 | /ms/2.1.2: 1535 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 1536 | dev: true 1537 | 1538 | /ms/2.1.3: 1539 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 1540 | dev: true 1541 | 1542 | /nanoid/3.2.0: 1543 | resolution: {integrity: sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==} 1544 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1545 | hasBin: true 1546 | dev: true 1547 | 1548 | /node-releases/2.0.2: 1549 | resolution: {integrity: sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==} 1550 | dev: true 1551 | 1552 | /normalize-path/3.0.0: 1553 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 1554 | engines: {node: '>=0.10.0'} 1555 | dev: true 1556 | 1557 | /object-keys/1.1.1: 1558 | resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 1559 | engines: {node: '>= 0.4'} 1560 | dev: true 1561 | 1562 | /object.assign/4.1.2: 1563 | resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} 1564 | engines: {node: '>= 0.4'} 1565 | dependencies: 1566 | call-bind: 1.0.2 1567 | define-properties: 1.1.3 1568 | has-symbols: 1.0.3 1569 | object-keys: 1.1.1 1570 | dev: true 1571 | 1572 | /once/1.4.0: 1573 | resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} 1574 | dependencies: 1575 | wrappy: 1.0.2 1576 | dev: true 1577 | 1578 | /open/8.4.0: 1579 | resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} 1580 | engines: {node: '>=12'} 1581 | dependencies: 1582 | define-lazy-prop: 2.0.0 1583 | is-docker: 2.2.1 1584 | is-wsl: 2.2.0 1585 | dev: true 1586 | 1587 | /openapi-typescript/5.1.1: 1588 | resolution: {integrity: sha512-ixZMg4ufPwBblLLdCja2i3RSfrAzHrLQp+BvmW+MWTtrJnhr6xYVkYs9EWSp6u0rtBg+5GTLn+47DKDPhvB7mA==} 1589 | engines: {node: '>= 12.0.0', npm: '>= 7.0.0'} 1590 | hasBin: true 1591 | dependencies: 1592 | js-yaml: 4.1.0 1593 | mime: 3.0.0 1594 | prettier: 2.5.1 1595 | tiny-glob: 0.2.9 1596 | undici: 4.13.0 1597 | yargs-parser: 21.0.0 1598 | dev: true 1599 | 1600 | /parent-module/1.0.1: 1601 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 1602 | engines: {node: '>=6'} 1603 | dependencies: 1604 | callsites: 3.1.0 1605 | dev: true 1606 | 1607 | /path-is-absolute/1.0.1: 1608 | resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} 1609 | engines: {node: '>=0.10.0'} 1610 | dev: true 1611 | 1612 | /path-parse/1.0.7: 1613 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 1614 | dev: true 1615 | 1616 | /pend/1.2.0: 1617 | resolution: {integrity: sha1-elfrVQpng/kRUzH89GY9XI4AelA=} 1618 | dev: true 1619 | 1620 | /picocolors/1.0.0: 1621 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 1622 | dev: true 1623 | 1624 | /picomatch/2.3.1: 1625 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1626 | engines: {node: '>=8.6'} 1627 | dev: true 1628 | 1629 | /pirates/4.0.4: 1630 | resolution: {integrity: sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw==} 1631 | engines: {node: '>= 6'} 1632 | dev: true 1633 | 1634 | /pixelmatch/5.2.1: 1635 | resolution: {integrity: sha512-WjcAdYSnKrrdDdqTcVEY7aB7UhhwjYQKYhHiBXdJef0MOaQeYpUdQ+iVyBLa5YBKS8MPVPPMX7rpOByISLpeEQ==} 1636 | hasBin: true 1637 | dependencies: 1638 | pngjs: 4.0.1 1639 | dev: true 1640 | 1641 | /playwright-core/1.20.0: 1642 | resolution: {integrity: sha512-d25IRcdooS278Cijlp8J8A5fLQZ+/aY3dKRJvgX5yjXA69N0huIUdnh3xXSgn+LsQ9DCNmB7Ngof3eY630jgdA==} 1643 | engines: {node: '>=12'} 1644 | hasBin: true 1645 | dependencies: 1646 | colors: 1.4.0 1647 | commander: 8.3.0 1648 | debug: 4.3.3 1649 | extract-zip: 2.0.1 1650 | https-proxy-agent: 5.0.0 1651 | jpeg-js: 0.4.3 1652 | mime: 3.0.0 1653 | pixelmatch: 5.2.1 1654 | pngjs: 6.0.0 1655 | progress: 2.0.3 1656 | proper-lockfile: 4.1.2 1657 | proxy-from-env: 1.1.0 1658 | rimraf: 3.0.2 1659 | socks-proxy-agent: 6.1.1 1660 | stack-utils: 2.0.5 1661 | ws: 8.4.2 1662 | yauzl: 2.10.0 1663 | yazl: 2.5.1 1664 | transitivePeerDependencies: 1665 | - bufferutil 1666 | - supports-color 1667 | - utf-8-validate 1668 | dev: true 1669 | 1670 | /pngjs/4.0.1: 1671 | resolution: {integrity: sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg==} 1672 | engines: {node: '>=8.0.0'} 1673 | dev: true 1674 | 1675 | /pngjs/6.0.0: 1676 | resolution: {integrity: sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==} 1677 | engines: {node: '>=12.13.0'} 1678 | dev: true 1679 | 1680 | /postcss/8.4.6: 1681 | resolution: {integrity: sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==} 1682 | engines: {node: ^10 || ^12 || >=14} 1683 | dependencies: 1684 | nanoid: 3.2.0 1685 | picocolors: 1.0.0 1686 | source-map-js: 1.0.2 1687 | dev: true 1688 | 1689 | /prettier/2.5.1: 1690 | resolution: {integrity: sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==} 1691 | engines: {node: '>=10.13.0'} 1692 | hasBin: true 1693 | dev: true 1694 | 1695 | /pretty-format/27.5.1: 1696 | resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} 1697 | engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} 1698 | dependencies: 1699 | ansi-regex: 5.0.1 1700 | ansi-styles: 5.2.0 1701 | react-is: 17.0.2 1702 | dev: true 1703 | 1704 | /progress/2.0.3: 1705 | resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} 1706 | engines: {node: '>=0.4.0'} 1707 | dev: true 1708 | 1709 | /proper-lockfile/4.1.2: 1710 | resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} 1711 | dependencies: 1712 | graceful-fs: 4.2.9 1713 | retry: 0.12.0 1714 | signal-exit: 3.0.7 1715 | dev: true 1716 | 1717 | /proxy-from-env/1.1.0: 1718 | resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} 1719 | dev: true 1720 | 1721 | /pump/3.0.0: 1722 | resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} 1723 | dependencies: 1724 | end-of-stream: 1.4.4 1725 | once: 1.4.0 1726 | dev: true 1727 | 1728 | /queue-microtask/1.2.3: 1729 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1730 | dev: true 1731 | 1732 | /react-is/17.0.2: 1733 | resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} 1734 | dev: true 1735 | 1736 | /readdirp/3.6.0: 1737 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1738 | engines: {node: '>=8.10.0'} 1739 | dependencies: 1740 | picomatch: 2.3.1 1741 | dev: true 1742 | 1743 | /resolve-from/4.0.0: 1744 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 1745 | engines: {node: '>=4'} 1746 | dev: true 1747 | 1748 | /resolve/1.22.0: 1749 | resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} 1750 | hasBin: true 1751 | dependencies: 1752 | is-core-module: 2.8.1 1753 | path-parse: 1.0.7 1754 | supports-preserve-symlinks-flag: 1.0.0 1755 | dev: true 1756 | 1757 | /retry/0.12.0: 1758 | resolution: {integrity: sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=} 1759 | engines: {node: '>= 4'} 1760 | dev: true 1761 | 1762 | /reusify/1.0.4: 1763 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 1764 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1765 | dev: true 1766 | 1767 | /rimraf/2.7.1: 1768 | resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} 1769 | hasBin: true 1770 | dependencies: 1771 | glob: 7.2.0 1772 | dev: true 1773 | 1774 | /rimraf/3.0.2: 1775 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 1776 | hasBin: true 1777 | dependencies: 1778 | glob: 7.2.0 1779 | dev: true 1780 | 1781 | /rollup/2.67.2: 1782 | resolution: {integrity: sha512-hoEiBWwZtf1QdK3jZIq59L0FJj4Fiv4RplCO4pvCRC86qsoFurWB4hKQIjoRf3WvJmk5UZ9b0y5ton+62fC7Tw==} 1783 | engines: {node: '>=10.0.0'} 1784 | hasBin: true 1785 | optionalDependencies: 1786 | fsevents: 2.3.2 1787 | dev: true 1788 | 1789 | /run-parallel/1.2.0: 1790 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1791 | dependencies: 1792 | queue-microtask: 1.2.3 1793 | dev: true 1794 | 1795 | /sade/1.8.1: 1796 | resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} 1797 | engines: {node: '>=6'} 1798 | dependencies: 1799 | mri: 1.2.0 1800 | dev: true 1801 | 1802 | /safe-buffer/5.1.2: 1803 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 1804 | dev: true 1805 | 1806 | /sander/0.5.1: 1807 | resolution: {integrity: sha1-dB4kXiMfB8r7b98PEzrfohalAq0=} 1808 | dependencies: 1809 | es6-promise: 3.3.1 1810 | graceful-fs: 4.2.9 1811 | mkdirp: 0.5.5 1812 | rimraf: 2.7.1 1813 | dev: true 1814 | 1815 | /semver/6.3.0: 1816 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} 1817 | hasBin: true 1818 | dev: true 1819 | 1820 | /signal-exit/3.0.7: 1821 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 1822 | dev: true 1823 | 1824 | /slash/3.0.0: 1825 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 1826 | engines: {node: '>=8'} 1827 | dev: true 1828 | 1829 | /smart-buffer/4.2.0: 1830 | resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} 1831 | engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} 1832 | dev: true 1833 | 1834 | /socks-proxy-agent/6.1.1: 1835 | resolution: {integrity: sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==} 1836 | engines: {node: '>= 10'} 1837 | dependencies: 1838 | agent-base: 6.0.2 1839 | debug: 4.3.3 1840 | socks: 2.6.2 1841 | transitivePeerDependencies: 1842 | - supports-color 1843 | dev: true 1844 | 1845 | /socks/2.6.2: 1846 | resolution: {integrity: sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==} 1847 | engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} 1848 | dependencies: 1849 | ip: 1.1.5 1850 | smart-buffer: 4.2.0 1851 | dev: true 1852 | 1853 | /sorcery/0.10.0: 1854 | resolution: {integrity: sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=} 1855 | hasBin: true 1856 | dependencies: 1857 | buffer-crc32: 0.2.13 1858 | minimist: 1.2.5 1859 | sander: 0.5.1 1860 | sourcemap-codec: 1.4.8 1861 | dev: true 1862 | 1863 | /source-map-js/1.0.2: 1864 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 1865 | engines: {node: '>=0.10.0'} 1866 | dev: true 1867 | 1868 | /source-map-support/0.4.18: 1869 | resolution: {integrity: sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==} 1870 | dependencies: 1871 | source-map: 0.5.7 1872 | dev: true 1873 | 1874 | /source-map/0.5.7: 1875 | resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=} 1876 | engines: {node: '>=0.10.0'} 1877 | dev: true 1878 | 1879 | /source-map/0.7.3: 1880 | resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} 1881 | engines: {node: '>= 8'} 1882 | dev: true 1883 | 1884 | /sourcemap-codec/1.4.8: 1885 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} 1886 | dev: true 1887 | 1888 | /stack-utils/2.0.5: 1889 | resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==} 1890 | engines: {node: '>=10'} 1891 | dependencies: 1892 | escape-string-regexp: 2.0.0 1893 | dev: true 1894 | 1895 | /strip-indent/3.0.0: 1896 | resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} 1897 | engines: {node: '>=8'} 1898 | dependencies: 1899 | min-indent: 1.0.1 1900 | dev: true 1901 | 1902 | /supports-color/5.5.0: 1903 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 1904 | engines: {node: '>=4'} 1905 | dependencies: 1906 | has-flag: 3.0.0 1907 | dev: true 1908 | 1909 | /supports-color/7.2.0: 1910 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 1911 | engines: {node: '>=8'} 1912 | dependencies: 1913 | has-flag: 4.0.0 1914 | dev: true 1915 | 1916 | /supports-preserve-symlinks-flag/1.0.0: 1917 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 1918 | engines: {node: '>= 0.4'} 1919 | dev: true 1920 | 1921 | /svelte-check/2.4.3_svelte@3.46.4: 1922 | resolution: {integrity: sha512-0zJMMgqYHoP7QEG3tfc5DekpHAOqoy4QOL8scWMSdHIpVVDVC0MuYK57nFyj3XVTW8Zfm85FlgnAdQYsVmST2Q==} 1923 | hasBin: true 1924 | peerDependencies: 1925 | svelte: ^3.24.0 1926 | dependencies: 1927 | chokidar: 3.5.3 1928 | fast-glob: 3.2.11 1929 | import-fresh: 3.3.0 1930 | minimist: 1.2.5 1931 | picocolors: 1.0.0 1932 | sade: 1.8.1 1933 | source-map: 0.7.3 1934 | svelte: 3.46.4 1935 | svelte-preprocess: 4.10.3_svelte@3.46.4+typescript@4.5.5 1936 | typescript: 4.5.5 1937 | transitivePeerDependencies: 1938 | - '@babel/core' 1939 | - coffeescript 1940 | - less 1941 | - node-sass 1942 | - postcss 1943 | - postcss-load-config 1944 | - pug 1945 | - sass 1946 | - stylus 1947 | - sugarss 1948 | dev: true 1949 | 1950 | /svelte-hmr/0.14.9_svelte@3.46.4: 1951 | resolution: {integrity: sha512-bKE9+4qb4sAnA+TKHiYurUl970rjA0XmlP9TEP7K/ncyWz3m81kA4HOgmlZK/7irGK7gzZlaPDI3cmf8fp/+tg==} 1952 | peerDependencies: 1953 | svelte: '>=3.19.0' 1954 | dependencies: 1955 | svelte: 3.46.4 1956 | dev: true 1957 | 1958 | /svelte-preprocess/4.10.3_svelte@3.46.4+typescript@4.5.5: 1959 | resolution: {integrity: sha512-ttw17lJfb/dx2ZJT9sesaXT5l7mPQ9Apx1H496Kli3Hkk7orIRGpOw6rCPkRNzr6ueVPqb4vzodS5x7sBFhKHw==} 1960 | engines: {node: '>= 9.11.2'} 1961 | requiresBuild: true 1962 | peerDependencies: 1963 | '@babel/core': ^7.10.2 1964 | coffeescript: ^2.5.1 1965 | less: ^3.11.3 || ^4.0.0 1966 | node-sass: '*' 1967 | postcss: ^7 || ^8 1968 | postcss-load-config: ^2.1.0 || ^3.0.0 1969 | pug: ^3.0.0 1970 | sass: ^1.26.8 1971 | stylus: ^0.55.0 1972 | sugarss: ^2.0.0 1973 | svelte: ^3.23.0 1974 | typescript: ^3.9.5 || ^4.0.0 1975 | peerDependenciesMeta: 1976 | '@babel/core': 1977 | optional: true 1978 | coffeescript: 1979 | optional: true 1980 | less: 1981 | optional: true 1982 | node-sass: 1983 | optional: true 1984 | postcss: 1985 | optional: true 1986 | postcss-load-config: 1987 | optional: true 1988 | pug: 1989 | optional: true 1990 | sass: 1991 | optional: true 1992 | stylus: 1993 | optional: true 1994 | sugarss: 1995 | optional: true 1996 | typescript: 1997 | optional: true 1998 | dependencies: 1999 | '@types/pug': 2.0.6 2000 | '@types/sass': 1.43.1 2001 | detect-indent: 6.1.0 2002 | magic-string: 0.25.7 2003 | sorcery: 0.10.0 2004 | strip-indent: 3.0.0 2005 | svelte: 3.46.4 2006 | typescript: 4.5.5 2007 | dev: true 2008 | 2009 | /svelte/3.46.4: 2010 | resolution: {integrity: sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==} 2011 | engines: {node: '>= 8'} 2012 | dev: false 2013 | 2014 | /tiny-glob/0.2.9: 2015 | resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} 2016 | dependencies: 2017 | globalyzer: 0.1.0 2018 | globrex: 0.1.2 2019 | dev: true 2020 | 2021 | /to-fast-properties/2.0.0: 2022 | resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=} 2023 | engines: {node: '>=4'} 2024 | dev: true 2025 | 2026 | /to-regex-range/5.0.1: 2027 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 2028 | engines: {node: '>=8.0'} 2029 | dependencies: 2030 | is-number: 7.0.0 2031 | dev: true 2032 | 2033 | /tslib/2.3.1: 2034 | resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} 2035 | dev: true 2036 | 2037 | /typescript/4.5.5: 2038 | resolution: {integrity: sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==} 2039 | engines: {node: '>=4.2.0'} 2040 | hasBin: true 2041 | dev: true 2042 | 2043 | /undici/4.13.0: 2044 | resolution: {integrity: sha512-8lk8S/f2V0VUNGf2scU2b+KI2JSzEQLdCyRNRF3XmHu+5jectlSDaPSBCXAHFaUlt1rzngzOBVDgJS9/Gue/KA==} 2045 | engines: {node: '>=12.18'} 2046 | dev: true 2047 | 2048 | /utility-types/3.10.0: 2049 | resolution: {integrity: sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==} 2050 | engines: {node: '>= 4'} 2051 | dev: true 2052 | 2053 | /vite/2.8.0: 2054 | resolution: {integrity: sha512-ed5rjyeysttuPJX/aKSA0gTB/8ZKLM5xF6FtEuKy1B9DiQbDNFMVMQxnb9JesgBPUMMIJxC8w5KZ/KNWLKFXoA==} 2055 | engines: {node: '>=12.2.0'} 2056 | hasBin: true 2057 | peerDependencies: 2058 | less: '*' 2059 | sass: '*' 2060 | stylus: '*' 2061 | peerDependenciesMeta: 2062 | less: 2063 | optional: true 2064 | sass: 2065 | optional: true 2066 | stylus: 2067 | optional: true 2068 | dependencies: 2069 | esbuild: 0.14.27 2070 | postcss: 8.4.6 2071 | resolve: 1.22.0 2072 | rollup: 2.67.2 2073 | optionalDependencies: 2074 | fsevents: 2.3.2 2075 | dev: true 2076 | 2077 | /wrappy/1.0.2: 2078 | resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} 2079 | dev: true 2080 | 2081 | /ws/8.4.2: 2082 | resolution: {integrity: sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==} 2083 | engines: {node: '>=10.0.0'} 2084 | peerDependencies: 2085 | bufferutil: ^4.0.1 2086 | utf-8-validate: ^5.0.2 2087 | peerDependenciesMeta: 2088 | bufferutil: 2089 | optional: true 2090 | utf-8-validate: 2091 | optional: true 2092 | dev: true 2093 | 2094 | /yargs-parser/21.0.0: 2095 | resolution: {integrity: sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==} 2096 | engines: {node: '>=12'} 2097 | dev: true 2098 | 2099 | /yauzl/2.10.0: 2100 | resolution: {integrity: sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=} 2101 | dependencies: 2102 | buffer-crc32: 0.2.13 2103 | fd-slicer: 1.1.0 2104 | dev: true 2105 | 2106 | /yazl/2.5.1: 2107 | resolution: {integrity: sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==} 2108 | dependencies: 2109 | buffer-crc32: 0.2.13 2110 | dev: true 2111 | --------------------------------------------------------------------------------