diff --git a/.gitignore b/.gitignore index 2d08fce..6565849 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,10 @@ db.sqlite3 build/** app/** settings.json -*config.json +comic_config.json +**/comic_config.json +compiled/ +deploy-scripts/ -.pnpm-store/** \ No newline at end of file +.pnpm-store/** +.env \ No newline at end of file diff --git a/app.ts b/app.ts deleted file mode 100644 index 27c6636..0000000 --- a/app.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { app, BrowserWindow, dialog, session } from "electron"; -import { ipcMain } from "electron"; -import { join } from "path"; -import { accessTokenName, getAdminAccessTokenValue, getAdminRefreshTokenValue, refreshTokenName } from "./src/login"; -import { UserAccessor } from "./src/model/mod"; -import { create_server } from "./src/server"; -import { get_setting } from "./src/SettingConfig"; - -function registerChannel(cntr: UserAccessor) { - ipcMain.handle("reset_password", async (event, username: string, password: string) => { - const user = await cntr.findUser(username); - if (user === undefined) { - return false; - } - user.reset_password(password); - return true; - }); -} -const setting = get_setting(); -if (!setting.cli) { - let wnd: BrowserWindow | null = null; - - const createWindow = async () => { - wnd = new BrowserWindow({ - width: 800, - height: 600, - center: true, - useContentSize: true, - webPreferences: { - preload: join(__dirname, "preload.js"), - contextIsolation: true, - }, - }); - await wnd.loadURL(`data:text/html;base64,` + Buffer.from(loading_html).toString("base64")); - // await wnd.loadURL('../loading.html'); - // set admin cookies. - await session.defaultSession.cookies.set({ - url: `http://localhost:${setting.port}`, - name: accessTokenName, - value: getAdminAccessTokenValue(), - httpOnly: true, - secure: false, - sameSite: "strict", - }); - await session.defaultSession.cookies.set({ - url: `http://localhost:${setting.port}`, - name: refreshTokenName, - value: getAdminRefreshTokenValue(), - httpOnly: true, - secure: false, - sameSite: "strict", - }); - try { - const server = await create_server(); - const app = server.start_server(); - registerChannel(server.userController); - await wnd.loadURL(`http://localhost:${setting.port}`); - } catch (e) { - if (e instanceof Error) { - await dialog.showMessageBox({ - type: "error", - title: "error!", - message: e.message, - }); - } else { - await dialog.showMessageBox({ - type: "error", - title: "error!", - message: String(e), - }); - } - } - wnd.on("closed", () => { - wnd = null; - }); - }; - - const isPrimary = app.requestSingleInstanceLock(); - if (!isPrimary) { - app.quit(); // exit window - app.exit(); - } - app.on("second-instance", () => { - if (wnd != null) { - if (wnd.isMinimized()) { - wnd.restore(); - } - wnd.focus(); - } - }); - app.on("ready", (event, info) => { - createWindow(); - }); - - app.on("window-all-closed", () => { // quit when all windows are closed - if (process.platform != "darwin") app.quit(); // (except leave MacOS app active until Cmd+Q) - }); - - app.on("activate", () => { // re-recreate window when dock icon is clicked and no other windows open - if (wnd == null) createWindow(); - }); -} else { - (async () => { - try { - const server = await create_server(); - server.start_server(); - } catch (error) { - console.log(error); - } - })(); -} -const loading_html = ` - - -loading - - - - - -

Loading...

-
- -`; diff --git a/biome.jsonc b/biome.jsonc new file mode 100644 index 0000000..df0035c --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,21 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.6.3/schema.json", + "organizeImports": { + "enabled": true + }, + "formatter": { + "enabled": true, + "lineWidth": 120 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + } +} diff --git a/dprint.json b/dprint.json deleted file mode 100644 index 3911cdd..0000000 --- a/dprint.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "incremental": true, - "typescript": { - "indentWidth": 2 - }, - "json": { - }, - "markdown": { - }, - "includes": ["**/*.{ts,tsx,js,jsx,cjs,mjs,json,md}"], - "excludes": [ - "**/node_modules", - "**/*-lock.json", - "**/dist", - "build/", - "app/" - ], - "plugins": [ - "https://plugins.dprint.dev/typescript-0.84.4.wasm", - "https://plugins.dprint.dev/json-0.17.2.wasm", - "https://plugins.dprint.dev/markdown-0.15.2.wasm" - ] -} diff --git a/gen_conf_schema.ts b/gen_conf_schema.ts deleted file mode 100644 index 460665b..0000000 --- a/gen_conf_schema.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { promises } from "fs"; -const { readdir, writeFile } = promises; -import { dirname, join } from "path"; -import { createGenerator } from "ts-json-schema-generator"; - -async function genSchema(path: string, typename: string) { - const gen = createGenerator({ - path: path, - type: typename, - tsconfig: "tsconfig.json", - }); - const schema = gen.createSchema(typename); - if (schema.definitions != undefined) { - const definitions = schema.definitions; - const definition = definitions[typename]; - if (typeof definition == "object") { - let property = definition.properties; - if (property) { - property["$schema"] = { - type: "string", - }; - } - } - } - const text = JSON.stringify(schema); - await writeFile(join(dirname(path), `${typename}.schema.json`), text); -} -function capitalize(s: string) { - return s.charAt(0).toUpperCase() + s.slice(1); -} -async function setToALL(path: string) { - console.log(`scan ${path}`); - const direntry = await readdir(path, { withFileTypes: true }); - const works = direntry.filter(x => x.isFile() && x.name.endsWith("Config.ts")).map(x => { - const name = x.name; - const m = /(.+)\.ts/.exec(name); - if (m !== null) { - const typename = m[1]; - return genSchema(join(path, typename), capitalize(typename)); - } - }); - await Promise.all(works); - const subdir = direntry.filter(x => x.isDirectory()).map(x => x.name); - for (const x of subdir) { - await setToALL(join(path, x)); - } -} -setToALL("src"); diff --git a/index.html b/index.html deleted file mode 100644 index 48a8430..0000000 --- a/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Ionian - - - - - - - -
- - - \ No newline at end of file diff --git a/knexfile.js b/knexfile.js deleted file mode 100644 index 76f47c9..0000000 --- a/knexfile.js +++ /dev/null @@ -1,5 +0,0 @@ -require("ts-node").register(); -const { Knex } = require("./src/config"); -// Update with your config settings. - -module.exports = Knex.config; diff --git a/migrations/initial.ts b/migrations/initial.ts deleted file mode 100644 index 816926d..0000000 --- a/migrations/initial.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Knex } from "knex"; - -export async function up(knex: Knex) { - await knex.schema.createTable("schema_migration", (b) => { - b.string("version"); - b.boolean("dirty"); - }); - - await knex.schema.createTable("users", (b) => { - b.string("username").primary().comment("user's login id"); - b.string("password_hash", 64).notNullable(); - b.string("password_salt", 64).notNullable(); - }); - await knex.schema.createTable("document", (b) => { - b.increments("id").primary(); - b.string("title").notNullable(); - b.string("content_type", 16).notNullable(); - b.string("basepath", 256).notNullable().comment("directory path for resource"); - b.string("filename", 256).notNullable().comment("filename"); - b.string("content_hash").nullable(); - b.json("additional").nullable(); - b.integer("created_at").notNullable(); - b.integer("modified_at").notNullable(); - b.integer("deleted_at"); - b.index("content_type", "content_type_index"); - }); - await knex.schema.createTable("tags", (b) => { - b.string("name").primary(); - b.text("description"); - }); - await knex.schema.createTable("doc_tag_relation", (b) => { - b.integer("doc_id").unsigned().notNullable(); - b.string("tag_name").notNullable(); - b.foreign("doc_id").references("document.id"); - b.foreign("tag_name").references("tags.name"); - b.primary(["doc_id", "tag_name"]); - }); - await knex.schema.createTable("permissions", b => { - b.string("username").notNullable(); - b.string("name").notNullable(); - b.primary(["username", "name"]); - b.foreign("username").references("users.username"); - }); - // create admin account. - await knex.insert({ - username: "admin", - password_hash: "unchecked", - password_salt: "unchecked", - }).into("users"); -} - -export async function down(knex: Knex) { - throw new Error("Downward migrations are not supported. Restore from backup."); -} diff --git a/package.json b/package.json index 2d0c444..d798872 100644 --- a/package.json +++ b/package.json @@ -1,86 +1,20 @@ { - "name": "followed", + "name": "ionian", "version": "1.0.0", "description": "", - "main": "build/app.js", + "main": "index.js", "scripts": { - "compile": "tsc", - "compile:watch": "tsc -w", - "build": "cd src/client && pnpm run build:prod", - "build:watch": "cd src/client && pnpm run build:watch", - "fmt": "dprint fmt", - "app": "electron build/app.js", - "app:build": "electron-builder", - "app:pack": "electron-builder --dir", - "app:build:win64": "electron-builder --win --x64", - "app:pack:win64": "electron-builder --win --x64 --dir", - "cliapp": "node build/app.js" - }, - "build": { - "asar": true, - "files": [ - "build/**/*", - "node_modules/**/*", - "package.json" - ], - "extraFiles": [ - { - "from": "dist/", - "to": "dist/", - "filter": [ - "**/*", - "!**/*.map" - ] - }, - "index.html" - ], - "appId": "com.prelude.ionian.app", - "productName": "Ionian", - "win": { - "target": [ - "zip" - ] - }, - "linux": { - "target": [ - "zip" - ] - }, - "directories": { - "output": "app/", - "app": "." - } + "test": "echo \"Error: no test specified\" && exit 1", + "format": "biome format --write", + "lint": "biome lint" }, + "keywords": [], + "workspaces": [ + "packages/*" + ], "author": "", - "license": "ISC", - "dependencies": { - "@louislam/sqlite3": "^6.0.1", - "@types/koa-compose": "^3.2.5", - "chokidar": "^3.5.3", - "dprint": "^0.36.1", - "jsonschema": "^1.4.1", - "jsonwebtoken": "^8.5.1", - "knex": "^0.95.15", - "koa": "^2.13.4", - "koa-bodyparser": "^4.3.0", - "koa-compose": "^4.1.0", - "koa-router": "^10.1.1", - "natural-orderby": "^2.0.3", - "node-stream-zip": "^1.15.0", - "sqlite3": "^5.0.8", - "tiny-async-pool": "^1.3.0" - }, + "license": "MIT", "devDependencies": { - "@types/jsonwebtoken": "^8.5.8", - "@types/koa": "^2.13.4", - "@types/koa-bodyparser": "^4.3.7", - "@types/koa-router": "^7.4.4", - "@types/node": "^14.18.21", - "@types/tiny-async-pool": "^1.0.1", - "electron": "^11.5.0", - "electron-builder": "^22.14.13", - "ts-json-schema-generator": "^0.82.0", - "ts-node": "^9.1.1", - "typescript": "^4.7.4" + "@biomejs/biome": "1.6.3" } -} +} \ No newline at end of file diff --git a/packages/client/.eslintrc.cjs b/packages/client/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/packages/client/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/packages/client/.gitignore b/packages/client/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/packages/client/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 0000000..0d6babe --- /dev/null +++ b/packages/client/README.md @@ -0,0 +1,30 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default { + // other rules... + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +} +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/packages/client/components.json b/packages/client/components.json new file mode 100644 index 0000000..1cc471f --- /dev/null +++ b/packages/client/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/packages/client/index.html b/packages/client/index.html new file mode 100644 index 0000000..ad1ff78 --- /dev/null +++ b/packages/client/index.html @@ -0,0 +1,13 @@ + + + + + + + Ionian + + +
+ + + diff --git a/packages/client/package.json b/packages/client/package.json new file mode 100644 index 0000000..4f70d99 --- /dev/null +++ b/packages/client/package.json @@ -0,0 +1,52 @@ +{ + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "shadcn": "shadcn-ui" + }, + "dependencies": { + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/react-virtual": "^3.2.1", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "dbtype": "workspace:*", + "jotai": "^2.7.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-resizable-panels": "^2.0.16", + "swr": "^2.2.5", + "tailwind-merge": "^2.2.2", + "tailwindcss-animate": "^1.0.7", + "usehooks-ts": "^3.1.0", + "wouter": "^3.1.0" + }, + "devDependencies": { + "@types/node": ">=20.0.0", + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react-swc": "^3.5.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.38", + "shadcn-ui": "^0.8.0", + "tailwindcss": "^3.4.3", + "typescript": "^5.2.2", + "vite": "^5.2.0" + } +} diff --git a/packages/client/postcss.config.js b/packages/client/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/packages/client/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/client/public/vite.svg b/packages/client/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/packages/client/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/client/src/App.css b/packages/client/src/App.css new file mode 100644 index 0000000..1ff976b --- /dev/null +++ b/packages/client/src/App.css @@ -0,0 +1,13 @@ +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&display=swap'); + +body { + margin: 0; + font-family: "Noto Sans KR", sans-serif; + font-optical-sizing: auto; + min-height: 100vh; +} + +#root { + margin: 0; + min-height: 100vh; +} \ No newline at end of file diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx new file mode 100644 index 0000000..45a6fe5 --- /dev/null +++ b/packages/client/src/App.tsx @@ -0,0 +1,49 @@ +import { Route, Switch, Redirect } from "wouter"; +import { useTernaryDarkMode } from "usehooks-ts"; +import { useEffect } from "react"; + +import './App.css' + +import { TooltipProvider } from "./components/ui/tooltip.tsx"; +import Layout from "./components/layout/layout.tsx"; + +import Gallery from "@/page/galleryPage.tsx"; +import NotFoundPage from "@/page/404.tsx"; +import LoginPage from "@/page/loginPage.tsx"; +import ProfilePage from "@/page/profilesPage.tsx"; +import ContentInfoPage from "@/page/contentInfoPage.tsx"; +import SettingPage from "@/page/settingPage.tsx"; +import ComicPage from "@/page/reader/comicPage.tsx"; +import DifferencePage from "./page/differencePage.tsx"; + +const App = () => { + const { isDarkMode } = useTernaryDarkMode(); + + useEffect(() => { + if (isDarkMode) { + document.body.classList.add("dark"); + } + else { + document.body.classList.remove("dark"); + } + }, [isDarkMode]); + + return ( + + + + } /> + + + + + + + + + + + ); +}; + +export default App diff --git a/packages/client/src/assets/react.svg b/packages/client/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/packages/client/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/client/src/components/Spinner.tsx b/packages/client/src/components/Spinner.tsx new file mode 100644 index 0000000..4c3d960 --- /dev/null +++ b/packages/client/src/components/Spinner.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +export function Spinner(props: { className?: string; }) { + const chars = ["⠋", + "⠙", + "⠹", + "⠸", + "⠼", + "⠴", + "⠦", + "⠧", + "⠇", + "⠏" + ]; + const [index, setIndex] = React.useState(0); + React.useEffect(() => { + const interval = setInterval(() => { + setIndex((index + 1) % chars.length); + }, 80); + return () => clearInterval(interval); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [index]); + + return {chars[index]}; +} diff --git a/packages/client/src/components/gallery/DescItem.tsx b/packages/client/src/components/gallery/DescItem.tsx new file mode 100644 index 0000000..566e889 --- /dev/null +++ b/packages/client/src/components/gallery/DescItem.tsx @@ -0,0 +1,26 @@ +import StyledLink from "@/components/gallery/StyledLink"; +import { cn } from "@/lib/utils"; + +export function DescItem({ name, children, className }: { + name: string; + className?: string; + children?: React.ReactNode; +}) { + return
+ {name} + {children} +
; +} +export function DescTagItem({ + items, name, className, +}: { + name: string; + items: string[]; + className?: string; +}) { + return + {items.length === 0 ? "N/A" : items.map( + (x) => {x} + )} + ; +} diff --git a/packages/client/src/components/gallery/GalleryCard.tsx b/packages/client/src/components/gallery/GalleryCard.tsx new file mode 100644 index 0000000..9381f02 --- /dev/null +++ b/packages/client/src/components/gallery/GalleryCard.tsx @@ -0,0 +1,90 @@ +import type { Document } from "dbtype/api"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card.tsx"; +import TagBadge from "@/components/gallery/TagBadge.tsx"; +import { Fragment, useLayoutEffect, useRef, useState } from "react"; +import { LazyImage } from "./LazyImage.tsx"; +import StyledLink from "./StyledLink.tsx"; +import React from "react"; + +function clipTagsWhenOverflow(tags: string[], limit: number) { + let l = 0; + for (let i = 0; i < tags.length; i++) { + l += tags[i].length; + if (l > limit) { + return tags.slice(0, i); + } + l += 1; // for space + } + return tags; +} + +function GalleryCardImpl({ + doc: x +}: { doc: Document; }) { + const ref = useRef(null); + const [clipCharCount, setClipCharCount] = useState(200); + const isDeleted = x.deleted_at !== null; + + const artists = x.tags.filter(x => x.startsWith("artist:")).map(x => x.replace("artist:", "")); + const groups = x.tags.filter(x => x.startsWith("group:")).map(x => x.replace("group:", "")); + + const originalTags = x.tags.filter(x => !x.startsWith("artist:") && !x.startsWith("group:")); + const clippedTags = clipTagsWhenOverflow(originalTags, clipCharCount); + + useLayoutEffect(() => { + const listener = () => { + if (ref.current) { + const { width } = ref.current.getBoundingClientRect(); + const charWidth = 7; // rough estimate + const newClipCharCount = Math.floor(width / charWidth) * 3; + setClipCharCount(newClipCharCount); + } + }; + listener(); + window.addEventListener("resize", listener); + return () => { + window.removeEventListener("resize", listener); + }; + }, []); + + return + {isDeleted ?
+ Deleted +
:
+ +
+ } +
+ + + + {x.title} + + + + {artists.map((x, i) => + {x} + {i + 1 < artists.length && , } + )} + {groups.length > 0 && {" | "}} + {groups.map((x, i) => + {x} + {i + 1 < groups.length && , } + + )} + + + +
    + {clippedTags.map(tag => )} + {clippedTags.length < originalTags.length && } +
+
+
+
; +} + +export const GalleryCard = React.memo(GalleryCardImpl); \ No newline at end of file diff --git a/packages/client/src/components/gallery/LazyImage.tsx b/packages/client/src/components/gallery/LazyImage.tsx new file mode 100644 index 0000000..fd6ea7c --- /dev/null +++ b/packages/client/src/components/gallery/LazyImage.tsx @@ -0,0 +1,38 @@ +import { useEffect, useRef, useState } from "react"; + +export function LazyImage({ src, alt, className }: { src: string; alt: string; className?: string; }) { + const ref = useRef(null); + const [loaded, setLoaded] = useState(false); + + useEffect(() => { + if (ref.current) { + const observer = new IntersectionObserver((entries) => { + if (entries.some(x => x.isIntersecting)) { + setLoaded(true); + ref.current?.animate([ + { opacity: 0 }, + { opacity: 1 } + ], { + duration: 300, + easing: "ease-in-out" + }); + observer.disconnect(); + } + }, { + rootMargin: "200px", + threshold: 0 + }); + observer.observe(ref.current); + return () => { + observer.disconnect(); + }; + } + }, []); + + return {alt}; +} diff --git a/packages/client/src/components/gallery/StyledLink.tsx b/packages/client/src/components/gallery/StyledLink.tsx new file mode 100644 index 0000000..b2e4458 --- /dev/null +++ b/packages/client/src/components/gallery/StyledLink.tsx @@ -0,0 +1,14 @@ +import { cn } from "@/lib/utils"; +import { Link } from "wouter"; + +type StyledLinkProps = { + children?: React.ReactNode; + className?: string; + to: string; +}; + +export default function StyledLink({ children, className, ...rest }: StyledLinkProps) { + return {children} +} \ No newline at end of file diff --git a/packages/client/src/components/gallery/TagBadge.tsx b/packages/client/src/components/gallery/TagBadge.tsx new file mode 100644 index 0000000..a1ff5d1 --- /dev/null +++ b/packages/client/src/components/gallery/TagBadge.tsx @@ -0,0 +1,88 @@ +import { badgeVariants } from "@/components/ui/badge.tsx"; +import { Link } from "wouter"; +import { cn } from "@/lib/utils.ts"; +import { cva } from "class-variance-authority" + +enum TagKind { + Default = "default", + Type = "type", + Character = "character", + Series = "series", + Group = "group", + Artist = "artist", + Male = "male", + Female = "female", +} + +type TagKindType = `${TagKind}`; + +export function getTagKind(tagname: string): TagKindType { + if (tagname.match(":") === null) { + return "default"; + } + const prefix = tagname.split(":")[0]; + return prefix as TagKindType; +} + +export function toPrettyTagname(tagname: string): string { + const kind = getTagKind(tagname); + const name = tagname.slice(kind.length + 1); + + switch (kind) { + case "male": + return `♂ ${name}`; + case "female": + return `♀ ${name}`; + case "artist": + return `🎨 ${name}`; + case "group": + return `🖿 ${name}`; + case "series": + return `📚 ${name}` + case "character": + return `👤 ${name}`; + case "default": + return tagname; + default: + return name; + } +} + +interface TagBadgeProps { + tagname: string; + className?: string; + disabled?: boolean; +} + + +export const tagBadgeVariants = cva( + cn(badgeVariants({ variant: "default"}), "px-1"), + { + variants: { + variant: { + default: "bg-[#4a5568] hover:bg-[#718096]", + type: "bg-[#d53f8c] hover:bg-[#e24996]", + character: "bg-[#52952c] hover:bg-[#6cc24a]", + series: "bg-[#dc8f09] hover:bg-[#e69d17]", + group: "bg-[#805ad5] hover:bg-[#8b5cd6]", + artist: "bg-[#319795] hover:bg-[#38a89d]", + female: "bg-[#c21f58] hover:bg-[#db2d67]", + male: "bg-[#2a7bbf] hover:bg-[#3091e7]", + }, + }, + defaultVariants: { + variant: "default", + }, + } +); + +export default function TagBadge(props: TagBadgeProps) { + const { tagname } = props; + const kind = getTagKind(tagname); + return
  • {toPrettyTagname(tagname)}
  • ; +} \ No newline at end of file diff --git a/packages/client/src/components/gallery/TagInput.tsx b/packages/client/src/components/gallery/TagInput.tsx new file mode 100644 index 0000000..23774fb --- /dev/null +++ b/packages/client/src/components/gallery/TagInput.tsx @@ -0,0 +1,182 @@ +import { cn } from "@/lib/utils"; +import { getTagKind, tagBadgeVariants } from "./TagBadge"; +import { useEffect, useRef, useState } from "react"; +import { Button } from "../ui/button"; +import { useOnClickOutside } from "usehooks-ts"; +import { useTags } from "@/hook/useTags"; +import { Skeleton } from "../ui/skeleton"; + +interface TagsSelectListProps { + className?: string; + search?: string; + onSelect?: (tag: string) => void; + onFirstArrowUp?: () => void; +} + +function TagsSelectList({ + search = "", + onSelect, + onFirstArrowUp = () => { }, +}: TagsSelectListProps) { + const { data, isLoading } = useTags(); + const candidates = data?.filter(s => s.name.startsWith(search)); + + return +} + +interface TagInputProps { + className?: string; + tags: string[]; + onTagsChange: (tags: string[]) => void; + input: string; + onInputChange: (input: string) => void; +} + +export default function TagInput({ + className, + tags = [], + onTagsChange = () => { }, + input = "", + onInputChange = () => { }, +}: TagInputProps) { + const inputRef = useRef(null); + const setTags = onTagsChange; + const setInput = onInputChange; + const [isFocused, setIsFocused] = useState(false); + const [openInfo, setOpenInfo] = useState<{ + top: number; + left: number; + } | null>(null); + const autocompleteRef = useRef(null); + useOnClickOutside(autocompleteRef, () => { + setOpenInfo(null); + }); + useEffect(() => { + const listener = (e: KeyboardEvent) => { + if (e.key === "Escape") { + setOpenInfo(null); + } + } + document.addEventListener("keyup", listener); + return () => { + document.removeEventListener("keyup", listener); + } + }, []); + + return <> + {/* biome-ignore lint/a11y/useKeyWithClickEvents: input exist */} +
    inputRef.current?.focus()} + > +
      + {tags.map((tag) =>
    • { + setTags(tags.filter(x=>x!==tag)); + }}>{tag}
    • )} +
    + setIsFocused(true)} onBlur={() => setIsFocused(false)} + value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => { + if (e.key === "Enter") { + if (input.trim() === "") return; + setTags([...tags, input]); + setInput(""); + setOpenInfo(null); + } + if (e.key === "Backspace" && input === "") { + setTags(tags.slice(0, -1)); + setOpenInfo(null); + } + if (e.key === ":" || (e.ctrlKey && e.key === " ")) { + if (inputRef.current) { + const rect = inputRef.current.getBoundingClientRect(); + setOpenInfo({ + top: rect.bottom, + left: rect.left, + }); + } + } + if (e.key === "Down" || e.key === "ArrowDown") { + if (openInfo && autocompleteRef.current) { + const firstChild = autocompleteRef.current.firstElementChild?.firstElementChild as HTMLElement; + firstChild?.focus(); + e.preventDefault(); + } + } + }} + /> + { + openInfo &&
    + { + setTags([...tags, tag]); + setInput(""); + setOpenInfo(null); + }} + onFirstArrowUp={() => { + inputRef.current?.focus(); + }} + /> +
    + } + { + tags.length > 0 && + } +
    + +} \ No newline at end of file diff --git a/packages/client/src/components/layout/layout.tsx b/packages/client/src/components/layout/layout.tsx new file mode 100644 index 0000000..1216fc8 --- /dev/null +++ b/packages/client/src/components/layout/layout.tsx @@ -0,0 +1,49 @@ +import { useLayoutEffect, useState } from "react"; +import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "../ui/resizable"; +import { NavList } from "./nav"; + + +interface LayoutProps { + children?: React.ReactNode; +} + +export default function Layout({ children }: LayoutProps) { + const MIN_SIZE_IN_PIXELS = 70; + const [minSize, setMinSize] = useState(MIN_SIZE_IN_PIXELS); + + useLayoutEffect(() => { + const panelGroup = document.querySelector('[data-panel-group-id="main"]'); + const resizeHandles = document.querySelectorAll( + "[data-panel-resize-handle-id]" + ); + if (!panelGroup || !resizeHandles) return; + const observer = new ResizeObserver(() => { + let width = panelGroup?.clientWidth; + if (!width) return; + width -= [...resizeHandles].reduce((acc, resizeHandle) => acc + resizeHandle.clientWidth, 0); + // Minimum size in pixels is a percentage of the PanelGroup's height, + // less the (fixed) height of the resize handles. + setMinSize((MIN_SIZE_IN_PIXELS / width) * 100); + }); + observer.observe(panelGroup); + for (const resizeHandle of resizeHandles) { + observer.observe(resizeHandle); + } + + return () => { + observer.disconnect(); + }; + }, []); + + return ( + + + + + + + {children} + + + ); +} \ No newline at end of file diff --git a/packages/client/src/components/layout/nav.tsx b/packages/client/src/components/layout/nav.tsx new file mode 100644 index 0000000..88f1c37 --- /dev/null +++ b/packages/client/src/components/layout/nav.tsx @@ -0,0 +1,78 @@ +import { Link } from "wouter" +import { MagnifyingGlassIcon, GearIcon, ActivityLogIcon, ArchiveIcon, PersonIcon } from "@radix-ui/react-icons" +import { Button, buttonVariants } from "@/components/ui/button.tsx" +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip.tsx" +import { useLogin } from "@/state/user.ts"; +import { useNavItems } from "./navAtom"; +import { Separator } from "../ui/separator"; + +interface NavItemProps { + icon: React.ReactNode; + to: string; + name: string; +} + +export function NavItem({ + icon, + to, + name +}: NavItemProps) { + return + + + {icon} + {name} + + + {name} + +} + +interface NavItemButtonProps { + icon: React.ReactNode; + onClick: () => void; + name: string; + className?: string; +} + +export function NavItemButton({ + icon, + onClick, + name, + className +}: NavItemButtonProps) { + return + + + + {name} + +} + +export function NavList() { + const loginInfo = useLogin(); + const navItems = useNavItems(); + + return +} \ No newline at end of file diff --git a/packages/client/src/components/layout/navAtom.tsx b/packages/client/src/components/layout/navAtom.tsx new file mode 100644 index 0000000..f0b23a6 --- /dev/null +++ b/packages/client/src/components/layout/navAtom.tsx @@ -0,0 +1,23 @@ +import { atom, useAtomValue, setAtomValue, getAtomState } from "@/lib/atom"; +import { useLayoutEffect } from "react"; + +const NavItems = atom("NavItems", null); + +// eslint-disable-next-line react-refresh/only-export-components +export function useNavItems() { + return useAtomValue(NavItems); +} + +export function PageNavItem({items, children}:{items: React.ReactNode, children: React.ReactNode}) { + useLayoutEffect(() => { + const prev = getAtomState(NavItems).value; + const setter = setAtomValue(NavItems); + setter(items); + return () => { + setter(prev); + }; + }, [items]); + + return children; +} + diff --git a/packages/client/src/components/ui/badge.tsx b/packages/client/src/components/ui/badge.tsx new file mode 100644 index 0000000..3eb4c0d --- /dev/null +++ b/packages/client/src/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils.ts" + +const badgeVariants = cva( + "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
    + ) +} + +export { Badge, badgeVariants } diff --git a/packages/client/src/components/ui/button.tsx b/packages/client/src/components/ui/button.tsx new file mode 100644 index 0000000..5b05535 --- /dev/null +++ b/packages/client/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils.ts" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/packages/client/src/components/ui/card.tsx b/packages/client/src/components/ui/card.tsx new file mode 100644 index 0000000..b751f76 --- /dev/null +++ b/packages/client/src/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as React from "react" + +import { cn } from "@/lib/utils.ts" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
    +)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
    +)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

    +)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

    +)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

    +)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
    +)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/packages/client/src/components/ui/input.tsx b/packages/client/src/components/ui/input.tsx new file mode 100644 index 0000000..0174a6b --- /dev/null +++ b/packages/client/src/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react" + +import { cn } from "@/lib/utils.ts" + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/packages/client/src/components/ui/label.tsx b/packages/client/src/components/ui/label.tsx new file mode 100644 index 0000000..ef29585 --- /dev/null +++ b/packages/client/src/components/ui/label.tsx @@ -0,0 +1,24 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils.ts" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/packages/client/src/components/ui/popover.tsx b/packages/client/src/components/ui/popover.tsx new file mode 100644 index 0000000..d82e714 --- /dev/null +++ b/packages/client/src/components/ui/popover.tsx @@ -0,0 +1,31 @@ +import * as React from "react" +import * as PopoverPrimitive from "@radix-ui/react-popover" + +import { cn } from "@/lib/utils" + +const Popover = PopoverPrimitive.Root + +const PopoverTrigger = PopoverPrimitive.Trigger + +const PopoverAnchor = PopoverPrimitive.Anchor + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)) +PopoverContent.displayName = PopoverPrimitive.Content.displayName + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } diff --git a/packages/client/src/components/ui/radio-group.tsx b/packages/client/src/components/ui/radio-group.tsx new file mode 100644 index 0000000..c939b06 --- /dev/null +++ b/packages/client/src/components/ui/radio-group.tsx @@ -0,0 +1,42 @@ +import * as React from "react" +import { CheckIcon } from "@radix-ui/react-icons" +import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" + +import { cn } from "@/lib/utils" + +const RadioGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + ) +}) +RadioGroup.displayName = RadioGroupPrimitive.Root.displayName + +const RadioGroupItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + + + + + ) +}) +RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName + +export { RadioGroup, RadioGroupItem } diff --git a/packages/client/src/components/ui/resizable.tsx b/packages/client/src/components/ui/resizable.tsx new file mode 100644 index 0000000..1cdd646 --- /dev/null +++ b/packages/client/src/components/ui/resizable.tsx @@ -0,0 +1,43 @@ +import { DragHandleDots2Icon } from "@radix-ui/react-icons" +import * as ResizablePrimitive from "react-resizable-panels" + +import { cn } from "@/lib/utils.ts" + +const ResizablePanelGroup = ({ + className, + ...props +}: React.ComponentProps) => ( + +) + +const ResizablePanel = ResizablePrimitive.Panel + +const ResizableHandle = ({ + withHandle, + className, + ...props +}: React.ComponentProps & { + withHandle?: boolean +}) => ( + div]:rotate-90", + className + )} + {...props} + > + {withHandle && ( +
    + +
    + )} +
    +) + +export { ResizablePanelGroup, ResizablePanel, ResizableHandle } diff --git a/packages/client/src/components/ui/separator.tsx b/packages/client/src/components/ui/separator.tsx new file mode 100644 index 0000000..6d7f122 --- /dev/null +++ b/packages/client/src/components/ui/separator.tsx @@ -0,0 +1,29 @@ +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "@/lib/utils" + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( + + ) +) +Separator.displayName = SeparatorPrimitive.Root.displayName + +export { Separator } diff --git a/packages/client/src/components/ui/skeleton.tsx b/packages/client/src/components/ui/skeleton.tsx new file mode 100644 index 0000000..7851593 --- /dev/null +++ b/packages/client/src/components/ui/skeleton.tsx @@ -0,0 +1,15 @@ +import { cn } from "@/lib/utils.ts" + +function Skeleton({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
    + ) +} + +export { Skeleton } diff --git a/packages/client/src/components/ui/tooltip.tsx b/packages/client/src/components/ui/tooltip.tsx new file mode 100644 index 0000000..4737dd6 --- /dev/null +++ b/packages/client/src/components/ui/tooltip.tsx @@ -0,0 +1,28 @@ +import * as React from "react" +import * as TooltipPrimitive from "@radix-ui/react-tooltip" + +import { cn } from "@/lib/utils.ts" + +const TooltipProvider = TooltipPrimitive.Provider + +const Tooltip = TooltipPrimitive.Root + +const TooltipTrigger = TooltipPrimitive.Trigger + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)) +TooltipContent.displayName = TooltipPrimitive.Content.displayName + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } diff --git a/packages/client/src/hook/fetcher.ts b/packages/client/src/hook/fetcher.ts new file mode 100644 index 0000000..d35738f --- /dev/null +++ b/packages/client/src/hook/fetcher.ts @@ -0,0 +1,20 @@ +export const BASE_API_URL = import.meta.env.VITE_API_URL ?? window.location.origin; + +export function makeApiUrl(pathnameAndQueryparam: string) { + return new URL(pathnameAndQueryparam, BASE_API_URL).toString(); +} + +export class ApiError extends Error { + constructor(public readonly status: number, message: string) { + super(message); + } +} + +export async function fetcher(url: string, init?: RequestInit) { + const u = makeApiUrl(url); + const res = await fetch(u, init); + if (!res.ok) { + throw new ApiError(res.status, await res.text()); + } + return res.json(); +} diff --git a/packages/client/src/hook/useDifference.ts b/packages/client/src/hook/useDifference.ts new file mode 100644 index 0000000..627f628 --- /dev/null +++ b/packages/client/src/hook/useDifference.ts @@ -0,0 +1,38 @@ +import useSWR, { mutate } from "swr"; +import { fetcher } from "./fetcher"; + +type FileDifference = { + type: string; + value: { + type: string; + path: string; + }[]; +}; + +export function useDifferenceDoc() { + return useSWR("/api/diff/list", fetcher); +} + +export async function commit(path: string, type: string) { + const data = await fetcher("/api/diff/commit", { + method: "POST", + body: JSON.stringify([{ path, type }]), + headers: { + "Content-Type": "application/json", + }, + }); + mutate("/api/diff/list"); + return data; +} + +export async function commitAll(type: string) { + const data = await fetcher("/api/diff/commitall", { + method: "POST", + body: JSON.stringify({ type }), + headers: { + "Content-Type": "application/json", + }, + }); + mutate("/api/diff/list"); + return data; +} \ No newline at end of file diff --git a/packages/client/src/hook/useGalleryDoc.ts b/packages/client/src/hook/useGalleryDoc.ts new file mode 100644 index 0000000..54a7dce --- /dev/null +++ b/packages/client/src/hook/useGalleryDoc.ts @@ -0,0 +1,7 @@ +import useSWR from "swr"; +import type { Document } from "dbtype/api"; +import { fetcher } from "./fetcher"; + +export function useGalleryDoc(id: string) { + return useSWR(`/api/doc/${id}`, fetcher); +} \ No newline at end of file diff --git a/packages/client/src/hook/useSearchGallery.ts b/packages/client/src/hook/useSearchGallery.ts new file mode 100644 index 0000000..b8c1a7f --- /dev/null +++ b/packages/client/src/hook/useSearchGallery.ts @@ -0,0 +1,62 @@ +import useSWRInifinite from "swr/infinite"; +import type { Document } from "dbtype/api"; +import { fetcher } from "./fetcher"; +import useSWR from "swr"; + +interface SearchParams { + word?: string; + tags?: string[]; + limit?: number; + cursor?: number; +} + +function makeSearchParams({ + word, tags, limit, cursor, +}: SearchParams){ + const search = new URLSearchParams(); + if (word) search.set("word", word); + if (tags) { + for (const tag of tags){ + search.append("allow_tag", tag); + } + } + if (limit) search.set("limit", limit.toString()); + if (cursor) search.set("cursor", cursor.toString()); + return search; +} + +export function useSearchGallery(searchParams: SearchParams = {}) { + return useSWR(`/api/doc/search?${makeSearchParams(searchParams).toString()}`, fetcher); +} + +export function useSearchGalleryInfinite(searchParams: SearchParams = {}) { + return useSWRInifinite< + { + data: Document[]; + nextCursor: number | null; + startCursor: number | null; + hasMore: boolean; + } + >((index, previous) => { + if (!previous && index > 0) return null; + if (previous && !previous.hasMore) return null; + const search = makeSearchParams(searchParams) + if (index === 0) { + return `/api/doc/search?${search.toString()}`; + } + if (!previous || !previous.data) return null; + const last = previous.data[previous.data.length - 1]; + search.set("cursor", last.id.toString()); + return `/api/doc/search?${search.toString()}`; + }, async (url) => { + const limit = searchParams.limit; + const res = await fetcher(url); + return { + data: res, + startCursor: res.length === 0 ? null : res[0].id, + nextCursor: res.length === 0 ? null : res[res.length - 1].id, + hasMore: limit ? res.length === limit : (res.length === 20), + }; + }); +} + diff --git a/packages/client/src/hook/useTags.ts b/packages/client/src/hook/useTags.ts new file mode 100644 index 0000000..3141274 --- /dev/null +++ b/packages/client/src/hook/useTags.ts @@ -0,0 +1,9 @@ +import useSWR from "swr"; +import { fetcher } from "./fetcher"; + +export function useTags() { + return useSWR<{ + name: string; + description: string; + }[]>("/api/tags", fetcher); +} \ No newline at end of file diff --git a/packages/client/src/index.css b/packages/client/src/index.css new file mode 100644 index 0000000..e5b7c67 --- /dev/null +++ b/packages/client/src/index.css @@ -0,0 +1,76 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/packages/client/src/lib/atom.ts b/packages/client/src/lib/atom.ts new file mode 100644 index 0000000..526e725 --- /dev/null +++ b/packages/client/src/lib/atom.ts @@ -0,0 +1,70 @@ +import { useEffect, useReducer, useState } from "react"; + +interface AtomState { + value: T; + listeners: Set<() => void>; +} +interface Atom { + key: string; + default: T; +} + +const atomStateMap = new WeakMap, AtomState>(); + +export function atom(key: string, defaultVal: T): Atom { + return { key, default: defaultVal }; +} + +export function getAtomState(atom: Atom): AtomState { + let atomState = atomStateMap.get(atom); + if (!atomState) { + atomState = { + value: atom.default, + listeners: new Set(), + }; + atomStateMap.set(atom, atomState); + } + return atomState as AtomState; +} + +export function useAtom(atom: Atom): [T, (val: T) => void] { + const state = getAtomState(atom); + const [, setState] = useState(state.value); + useEffect(() => { + const listener = () => setState(state.value); + state.listeners.add(listener); + return () => { + state.listeners.delete(listener); + }; + }, [state]); + return [ + state.value as T, + (val: T) => { + state.value = val; + // biome-ignore lint/complexity/noForEach: forEach is used to call each listener + state.listeners.forEach((listener) => listener()); + }, + ]; +} + +export function useAtomValue(atom: Atom): T { + const state = getAtomState(atom); + const update = useReducer((x) => x + 1, 0)[1]; + useEffect(() => { + const listener = () => update(); + state.listeners.add(listener); + return () => { + state.listeners.delete(listener); + }; + }, [state, update]); + return state.value; +} + +export function setAtomValue(atom: Atom): (val: T) => void { + const state = getAtomState(atom); + return (val: T) => { + state.value = val; + // biome-ignore lint/complexity/noForEach: forEach is used to call each listener + state.listeners.forEach((listener) => listener()); + }; +} \ No newline at end of file diff --git a/packages/client/src/lib/classifyTags.tsx b/packages/client/src/lib/classifyTags.tsx new file mode 100644 index 0000000..d72bea9 --- /dev/null +++ b/packages/client/src/lib/classifyTags.tsx @@ -0,0 +1,33 @@ +interface TagClassifyResult { + artist: string[]; + group: string[]; + series: string[]; + type: string[]; + character: string[]; + rest: string[]; +} + +export function classifyTags(tags: string[]): TagClassifyResult { + const result = { + artist: [], + group: [], + series: [], + type: [], + character: [], + rest: [], + } as TagClassifyResult; + const tagKind = new Set(["artist", "group", "series", "type", "character"]); + for (const tag of tags) { + const split = tag.split(":"); + if (split.length !== 2) { + continue; + } + const [prefix, name] = split; + if (tagKind.has(prefix)) { + result[prefix as keyof TagClassifyResult].push(name); + } else { + result.rest.push(tag); + } + } + return result; +} diff --git a/packages/client/src/lib/utils.ts b/packages/client/src/lib/utils.ts new file mode 100644 index 0000000..d084cca --- /dev/null +++ b/packages/client/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/packages/client/src/main.tsx b/packages/client/src/main.tsx new file mode 100644 index 0000000..e534213 --- /dev/null +++ b/packages/client/src/main.tsx @@ -0,0 +1,11 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +// biome-ignore lint/style/noNonNullAssertion: +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/packages/client/src/page/404.tsx b/packages/client/src/page/404.tsx new file mode 100644 index 0000000..af25627 --- /dev/null +++ b/packages/client/src/page/404.tsx @@ -0,0 +1,9 @@ +export const NotFoundPage = () => { + return (
    +

    404 Not Found

    +

    찾을 수 없음

    +
    + ); +}; + +export default NotFoundPage; \ No newline at end of file diff --git a/packages/client/src/page/contentInfoPage.tsx b/packages/client/src/page/contentInfoPage.tsx new file mode 100644 index 0000000..5bdb394 --- /dev/null +++ b/packages/client/src/page/contentInfoPage.tsx @@ -0,0 +1,82 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { useGalleryDoc } from "../hook/useGalleryDoc.ts"; +import TagBadge from "@/components/gallery/TagBadge"; +import StyledLink from "@/components/gallery/StyledLink"; +import { Link } from "wouter"; +import { classifyTags } from "../lib/classifyTags.tsx"; +import { DescTagItem, DescItem } from "../components/gallery/DescItem.tsx"; + +export interface ContentInfoPageProps { + params: { + id: string; + }; +} + +export function ContentInfoPage({ params }: ContentInfoPageProps) { + const { data, error, isLoading } = useGalleryDoc(params.id); + + if (isLoading) { + return
    Loading...
    + } + + if (error) { + return
    Error: {String(error)}
    + } + + if (!data) { + return
    Not found
    + } + + const tags = data?.tags ?? []; + const classifiedTags = classifyTags(tags); + + const contentLocation = `/doc/${params.id}/reader`; + + return ( +
    + +
    + {data.title} +
    + + + + + + {data.title} + + + + + {classifiedTags.type[0] ?? "N/A"} + + + + +
    + + + + + {new Date(data.created_at).toLocaleString()} + {new Date(data.modified_at).toLocaleString()} + {`${data.basepath}/${data.filename}`} + {JSON.stringify(data.additional)} +
    +
    + Tags +
      + {classifiedTags.rest.map((tag) => )} +
    +
    +
    +
    +
    + ); +} + +export default ContentInfoPage; \ No newline at end of file diff --git a/packages/client/src/page/differencePage.tsx b/packages/client/src/page/differencePage.tsx new file mode 100644 index 0000000..0d49d9b --- /dev/null +++ b/packages/client/src/page/differencePage.tsx @@ -0,0 +1,62 @@ +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; +import { useDifferenceDoc, commit, commitAll } from "@/hook/useDifference"; +import { useLogin } from "@/state/user"; +import { Fragment } from "react/jsx-runtime"; + +export function DifferencePage() { + const { data, isLoading, error } = useDifferenceDoc(); + const userInfo = useLogin(); + + if (!userInfo) { + return
    +

    + Not logged in +

    +
    + } + + if (error) { + return
    Error: {String(error)}
    + } + + return ( +
    + + + + Difference + Scanned Files List + + + + {isLoading &&
    Loading...
    } + {data?.map((c) => { + const x = c.value; + return ( + + {x.map((y) => ( +
    +

    {y.path}

    + +
    + ))} +
    + ) + })} +
    +
    +
    + ) +} + +export default DifferencePage; \ No newline at end of file diff --git a/packages/client/src/page/galleryPage.tsx b/packages/client/src/page/galleryPage.tsx new file mode 100644 index 0000000..ceb52d2 --- /dev/null +++ b/packages/client/src/page/galleryPage.tsx @@ -0,0 +1,141 @@ +import { useLocation, useSearch } from "wouter"; +import { Button } from "@/components/ui/button.tsx"; +import { GalleryCard } from "@/components/gallery/GalleryCard.tsx"; +import TagBadge from "@/components/gallery/TagBadge.tsx"; +import { useSearchGalleryInfinite } from "../hook/useSearchGallery.ts"; +import { Spinner } from "../components/Spinner.tsx"; +import TagInput from "@/components/gallery/TagInput.tsx"; +import { useEffect, useRef, useState } from "react"; +import { Separator } from "@/components/ui/separator.tsx"; +import { useVirtualizer } from "@tanstack/react-virtual"; + +export default function Gallery() { + const search = useSearch(); + const searchParams = new URLSearchParams(search); + const word = searchParams.get("word") ?? undefined; + const tags = searchParams.getAll("allow_tag") ?? undefined; + const limit = searchParams.get("limit"); + const cursor = searchParams.get("cursor"); + const { data, error, isLoading, size, setSize } = useSearchGalleryInfinite({ + word, tags, + limit: limit ? Number.parseInt(limit) : undefined, + cursor: cursor ? Number.parseInt(cursor) : undefined + }); + const parentRef = useRef(null); + const virtualizer = useVirtualizer({ + count: size, + // biome-ignore lint/style/noNonNullAssertion: + getScrollElement: () => parentRef.current!, + estimateSize: (index) => { + if (!data) return 8; + const docs = data?.[index]; + if (!docs) return 8; + return docs.data.length * (200 + 8) + 37 + 8; + }, + overscan: 1, + }); + + + + const virtualItems = virtualizer.getVirtualItems(); + useEffect(() => { + const lastItems = virtualItems.slice(-1); + if (lastItems.some(x => x.index >= size - 1)) { + const last = lastItems[0]; + const docs = data?.[last.index]; + if (docs?.hasMore) { + setSize(size + 1); + } + } + }, [virtualItems, setSize, size, data]); + + useEffect(() => { + virtualizer.measure(); + }, [virtualizer, data]); + + if (isLoading) { + return
    Loading...
    + } + if (error) { + return
    Error: {String(error)}
    + } + if (!data) { + return
    No data
    + } + + + const isLoadingMore = data && size > 0 && (data[size - 1] === undefined); + return (
    + + {(word || tags) && +
    + {word && Search: {word}} + {tags && Tags:
      { + tags.map(x => )} +
    } +
    + } + {data?.length === 0 &&
    No results
    } +
    + {// TODO: date based grouping + virtualItems.map((item) => { + const isLoaderRow = item.index === size - 1 && isLoadingMore; + if (isLoaderRow) { + return
    + +
    ; + } + const docs = data[item.index]; + if (!docs) return null; + return
    + {docs.startCursor &&
    +

    Start with {docs.startCursor}

    + +
    } + {docs?.data?.map((x) => { + return ( + + ); + })} +
    + }) + } +
    +
    + ); +} + +function Search() { + const search = useSearch(); + const [, navigate] = useLocation(); + const searchParams = new URLSearchParams(search); + const [tags, setTags] = useState(searchParams.get("allow_tag")?.split(",") ?? []); + const [word, setWord] = useState(searchParams.get("word") ?? ""); + return
    + + +
    ; +} + diff --git a/packages/client/src/page/loginPage.tsx b/packages/client/src/page/loginPage.tsx new file mode 100644 index 0000000..882f16b --- /dev/null +++ b/packages/client/src/page/loginPage.tsx @@ -0,0 +1,58 @@ +import { Button } from "@/components/ui/button.tsx"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card.tsx"; +import { Input } from "@/components/ui/input.tsx"; +import { Label } from "@/components/ui/label.tsx"; +import { doLogin } from "@/state/user.ts"; +import { useState } from "react"; +import { useLocation } from "wouter"; + +export function LoginForm() { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [, setLocation] = useLocation(); + + return ( + + + Login + + Enter your email below to login to your account. + + + +
    + + setUsername(e.target.value)}/> +
    +
    + + setPassword(e.target.value)}/> +
    +
    + + + +
    + ) +} + +export function LoginPage() { + return ( +
    + +
    + ) +} + +export default LoginPage; \ No newline at end of file diff --git a/packages/client/src/page/profilesPage.tsx b/packages/client/src/page/profilesPage.tsx new file mode 100644 index 0000000..8039a06 --- /dev/null +++ b/packages/client/src/page/profilesPage.tsx @@ -0,0 +1,34 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { useLogin } from "@/state/user"; +import { Redirect } from "wouter"; + +export function ProfilePage() { + const userInfo = useLogin(); + if (!userInfo) { + console.error("User session expired. Redirecting to login page."); + return ; + } + // TODO: Add a logout button + // TODO: Add a change password button + return ( +
    + + + Profile + + +
    + Username + {userInfo.username} +
    +
    + Permission + {userInfo.permission.length > 1 ? userInfo.permission.join(",") : "N/A"} +
    +
    +
    +
    + ) +} + +export default ProfilePage; \ No newline at end of file diff --git a/packages/client/src/page/reader/comicPage.tsx b/packages/client/src/page/reader/comicPage.tsx new file mode 100644 index 0000000..97c849c --- /dev/null +++ b/packages/client/src/page/reader/comicPage.tsx @@ -0,0 +1,166 @@ +import { NavItem, NavItemButton } from "@/components/layout/nav"; +import { PageNavItem } from "@/components/layout/navAtom"; +import { Input } from "@/components/ui/input"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { useGalleryDoc } from "@/hook/useGalleryDoc.ts"; +import { cn } from "@/lib/utils"; +import { EnterFullScreenIcon, ExitFullScreenIcon, ExitIcon } from "@radix-ui/react-icons"; +import { useEventListener } from "usehooks-ts"; +import type { Document } from "dbtype/api"; +import { useCallback, useEffect, useRef, useState } from "react"; + +interface ComicPageProps { + params: { + id: string; + }; +} + +function ComicViewer({ + doc, + totalPage, + curPage, + onChangePage: setCurPage, +}: { + doc: Document; + totalPage: number; + curPage: number; + onChangePage: (page: number) => void; +}) { + const [fade, setFade] = useState(false); + const PageDown = useCallback((step: number) => setCurPage(Math.max(curPage - step, 0)), [curPage, setCurPage]); + const PageUp = useCallback((step: number) => setCurPage(Math.min(curPage + step, totalPage - 1)), [curPage, setCurPage, totalPage]); + const currentImageRef = useRef(null); + + useEffect(() => { + const onKeyUp = (e: KeyboardEvent) => { + const step = e.shiftKey ? 10 : 1; + if (e.code === "ArrowLeft") { + PageDown(step); + } else if (e.code === "ArrowRight") { + PageUp(step); + } + }; + window.addEventListener("keyup", onKeyUp); + return () => { + window.removeEventListener("keyup", onKeyUp); + }; + }, [PageDown, PageUp]); + + useEffect(() => { + if(currentImageRef.current){ + if (curPage < 0 || curPage >= totalPage) { + return; + } + const img = new Image(); + img.src = `/api/doc/${doc.id}/comic/${curPage}`; + if (img.complete) { + currentImageRef.current.src = img.src; + setFade(false); + return; + } + setFade(true); + const listener = () => { + // biome-ignore lint/style/noNonNullAssertion: + const currentImage = currentImageRef.current!; + currentImage.src = img.src; + setFade(false); + }; + img.addEventListener("load", listener); + return () => { + img.removeEventListener("load", listener); + // abort loading + img.src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAI=;'; + // TODO: use web worker to abort loading image in the future + }; + } + }, [curPage, doc.id, totalPage]); + + return ( +
    +
    PageDown(1)} /> + main content +
    PageUp(1)} /> +
    + ); +} + +function clip(val: number, min: number, max: number): number { + return Math.max(min, Math.min(max, val)); +} + +function useFullScreen() { + const ref = useRef(document.documentElement); + const [isFullScreen, setIsFullScreen] = useState(false); + + const toggleFullScreen = useCallback(() => { + if (isFullScreen) { + document.exitFullscreen(); + } else { + document.documentElement.requestFullscreen(); + } + }, [isFullScreen]); + + useEventListener("fullscreenchange", () => { + setIsFullScreen(!!document.fullscreenElement); + }, ref); + return { isFullScreen, toggleFullScreen }; +} + +export default function ComicPage({ + params +}: ComicPageProps) { + const { data, error, isLoading } = useGalleryDoc(params.id); + const [curPage, setCurPage] = useState(0); + const { isFullScreen, toggleFullScreen } = useFullScreen(); + if (isLoading) { + // TODO: Add a loading spinner + return
    + Loading... +
    + } + if (error) { + return
    Error: {String(error)}
    + } + if (!data) { + return
    Not found
    + } + + if (data.content_type !== "comic") { + return
    Not a comic
    + } + if (!("page" in data.additional)) { + console.error(`invalid content : page read fail : ${JSON.stringify(data.additional)}`); + return
    Error. DB error. page restriction
    + } + + return ( + + }/> + : } onClick={()=>{ + toggleFullScreen(); + }} /> + + + {curPage + 1}/{data.additional.page as number} + + + + setCurPage(clip(Number.parseInt(e.target.value) - 1, + 0, + (data.additional.page as number) - 1))} /> + + + }> + + + ) +} \ No newline at end of file diff --git a/src/client/page/reader/thumbnail.css b/packages/client/src/page/reader/thumbnail.css similarity index 100% rename from src/client/page/reader/thumbnail.css rename to packages/client/src/page/reader/thumbnail.css diff --git a/packages/client/src/page/settingPage.tsx b/packages/client/src/page/settingPage.tsx new file mode 100644 index 0000000..ef5deac --- /dev/null +++ b/packages/client/src/page/settingPage.tsx @@ -0,0 +1,92 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { type TernaryDarkMode, useTernaryDarkMode, useMediaQuery } from "usehooks-ts"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { Label } from "@/components/ui/label"; + +function LightModeView() { + return
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    ; +} + +function DarkModeView() { + return
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +} + +export function SettingPage() { + const { setTernaryDarkMode, ternaryDarkMode } = useTernaryDarkMode(); + const isSystemDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); + + return ( +
    + + + Settings + + +
    +
    +

    Appearance

    + Dark mode +
    + setTernaryDarkMode(v as TernaryDarkMode)} + className="flex space-x-2 items-center" + > + + + + + + + +
    +
    +
    +
    + ) +} + +export default SettingPage; \ No newline at end of file diff --git a/packages/client/src/state/user.ts b/packages/client/src/state/user.ts new file mode 100644 index 0000000..8c9241c --- /dev/null +++ b/packages/client/src/state/user.ts @@ -0,0 +1,116 @@ +import { atom, useAtomValue, setAtomValue } from "../lib/atom.ts"; +import { makeApiUrl } from "../hook/fetcher.ts"; + +type LoginLocalStorage = { + username: string; + permission: string[]; + accessExpired: number; +}; + +let localObj: LoginLocalStorage | null = null; +function getUserSessions() { + if (localObj === null) { + const storagestr = localStorage.getItem("UserLoginContext") as string | null; + const storage = storagestr !== null ? (JSON.parse(storagestr) as LoginLocalStorage | null) : null; + localObj = storage; + } + if (localObj !== null && localObj.accessExpired > Math.floor(Date.now() / 1000)) { + return { + username: localObj.username, + permission: localObj.permission, + }; + } + return null; +} + +export async function refresh() { + const u = makeApiUrl("/api/user/refresh"); + const res = await fetch(u, { + method: "POST", + credentials: "include", + }); + if (res.status !== 200) throw new Error("Maybe Network Error"); + const r = (await res.json()) as LoginLocalStorage & { refresh: boolean }; + if (r.refresh) { + localObj = { + ...r + }; + } else { + localObj = { + accessExpired: 0, + username: "", + permission: r.permission, + }; + } + localStorage.setItem("UserLoginContext", JSON.stringify(localObj)); + return { + username: r.username, + permission: r.permission, + }; +} + +export const doLogout = async () => { + const u = makeApiUrl("/api/user/logout"); + const req = await fetch(u, { + method: "POST", + credentials: "include", + }); + const setVal = setAtomValue(userLoginStateAtom); + try { + const res = await req.json(); + localObj = { + accessExpired: 0, + username: "", + permission: res.permission, + }; + window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj)); + setVal(localObj); + return { + username: localObj.username, + permission: localObj.permission, + }; + } catch (error) { + console.error(`Server Error ${error}`); + return { + username: "", + permission: [], + }; + } +}; +export const doLogin = async (userLoginInfo: { + username: string; + password: string; +}): Promise => { + const u = makeApiUrl("/api/user/login"); + const res = await fetch(u, { + method: "POST", + body: JSON.stringify(userLoginInfo), + headers: { "content-type": "application/json" }, + credentials: "include", + }); + const b = await res.json(); + if (res.status !== 200) { + return b.detail as string; + } + const setVal = setAtomValue(userLoginStateAtom); + localObj = b; + setVal(b); + window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj)); + return b; +}; + + +export async function getInitialValue() { + const user = getUserSessions(); + if (user) { + return user; + } + return refresh(); +} + +export const userLoginStateAtom = atom("userLoginState", getUserSessions()); + +export function useLogin() { + const val = useAtomValue(userLoginStateAtom); + return val; +} \ No newline at end of file diff --git a/packages/client/src/vite-env.d.ts b/packages/client/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/packages/client/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/client/tailwind.config.js b/packages/client/tailwind.config.js new file mode 100644 index 0000000..7cb7e37 --- /dev/null +++ b/packages/client/tailwind.config.js @@ -0,0 +1,77 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: [ + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} \ No newline at end of file diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json new file mode 100644 index 0000000..0d28e1d --- /dev/null +++ b/packages/client/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/packages/client/tsconfig.node.json b/packages/client/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/packages/client/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts new file mode 100644 index 0000000..327a9e7 --- /dev/null +++ b/packages/client/vite.config.ts @@ -0,0 +1,20 @@ +import { defineConfig, loadEnv } from 'vite' +import path from 'node:path' +import react from '@vitejs/plugin-react-swc' + +// https://vitejs.dev/config/ +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + return { + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + server: { + proxy: { + '/api': env.API_BASE_URL ?? 'http://localhost:8000', + } + } +}}) diff --git a/packages/dbtype/api.ts b/packages/dbtype/api.ts new file mode 100644 index 0000000..bc5e33b --- /dev/null +++ b/packages/dbtype/api.ts @@ -0,0 +1,53 @@ +import type { JSONMap } from './jsonmap'; + +export interface DocumentBody { + title: string; + content_type: string; + basepath: string; + filename: string; + modified_at: number; + content_hash: string | null; + additional: JSONMap; + tags: string[]; // eager loading +} + +export interface Document extends DocumentBody { + readonly id: number; + readonly created_at: number; + readonly deleted_at: number | null; +} + +export type QueryListOption = { + /** + * search word + */ + word?: string; + allow_tag?: string[]; + /** + * limit of list + * @default 20 + */ + limit?: number; + /** + * use offset if true, otherwise + * @default false + */ + use_offset?: boolean; + /** + * cursor of documents + */ + cursor?: number; + /** + * offset of documents + */ + offset?: number; + /** + * tag eager loading + * @default true + */ + eager_loading?: boolean; + /** + * content type + */ + content_type?: string; +}; \ No newline at end of file diff --git a/src/types/json.ts b/packages/dbtype/jsonmap.ts similarity index 100% rename from src/types/json.ts rename to packages/dbtype/jsonmap.ts diff --git a/packages/dbtype/package.json b/packages/dbtype/package.json new file mode 100644 index 0000000..47f24b7 --- /dev/null +++ b/packages/dbtype/package.json @@ -0,0 +1,18 @@ +{ + "name": "dbtype", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/better-sqlite3": "^7.6.9", + "better-sqlite3": "^9.4.3", + "kysely": "^0.27.3", + "kysely-codegen": "^0.14.1" + } +} diff --git a/packages/dbtype/types.ts b/packages/dbtype/types.ts new file mode 100644 index 0000000..3ea5ac5 --- /dev/null +++ b/packages/dbtype/types.ts @@ -0,0 +1,53 @@ +import type { ColumnType } from "kysely"; + +export type Generated = T extends ColumnType + ? ColumnType + : ColumnType; + +export interface DocTagRelation { + doc_id: number; + tag_name: string; +} + +export interface Document { + additional: string | null; + basepath: string; + content_hash: string | null; + content_type: string; + created_at: number; + deleted_at: number | null; + filename: string; + id: Generated; + modified_at: number; + title: string; +} + +export interface Permissions { + name: string; + username: string; +} + +export interface SchemaMigration { + dirty: number | null; + version: string | null; +} + +export interface Tags { + description: string | null; + name: string; +} + +export interface Users { + password_hash: string; + password_salt: string; + username: string | null; +} + +export interface DB { + doc_tag_relation: DocTagRelation; + document: Document; + permissions: Permissions; + schema_migration: SchemaMigration; + tags: Tags; + users: Users; +} diff --git a/packages/server/app.ts b/packages/server/app.ts new file mode 100644 index 0000000..baf6dea --- /dev/null +++ b/packages/server/app.ts @@ -0,0 +1,7 @@ +import { create_server } from "./src/server"; + +create_server().then((server) => { + server.start_server(); +}).catch((err) => { + console.error(err); +}); \ No newline at end of file diff --git a/packages/server/gen_conf_schema.ts b/packages/server/gen_conf_schema.ts new file mode 100644 index 0000000..f5a02b6 --- /dev/null +++ b/packages/server/gen_conf_schema.ts @@ -0,0 +1,50 @@ +// import { promises } from "fs"; +// const { readdir, writeFile } = promises; +// import { dirname, join } from "path"; +// import { createGenerator } from "ts-json-schema-generator"; + +// async function genSchema(path: string, typename: string) { +// const gen = createGenerator({ +// path: path, +// type: typename, +// tsconfig: "tsconfig.json", +// }); +// const schema = gen.createSchema(typename); +// if (schema.definitions != undefined) { +// const definitions = schema.definitions; +// const definition = definitions[typename]; +// if (typeof definition == "object") { +// let property = definition.properties; +// if (property) { +// property["$schema"] = { +// type: "string", +// }; +// } +// } +// } +// const text = JSON.stringify(schema); +// await writeFile(join(dirname(path), `${typename}.schema.json`), text); +// } +// function capitalize(s: string) { +// return s.charAt(0).toUpperCase() + s.slice(1); +// } +// async function setToALL(path: string) { +// console.log(`scan ${path}`); +// const direntry = await readdir(path, { withFileTypes: true }); +// const works = direntry +// .filter((x) => x.isFile() && x.name.endsWith("Config.ts")) +// .map((x) => { +// const name = x.name; +// const m = /(.+)\.ts/.exec(name); +// if (m !== null) { +// const typename = m[1]; +// return genSchema(join(path, typename), capitalize(typename)); +// } +// }); +// await Promise.all(works); +// const subdir = direntry.filter((x) => x.isDirectory()).map((x) => x.name); +// for (const x of subdir) { +// await setToALL(join(path, x)); +// } +// } +// setToALL("src"); diff --git a/packages/server/migrations/initial.ts b/packages/server/migrations/initial.ts new file mode 100644 index 0000000..803da57 --- /dev/null +++ b/packages/server/migrations/initial.ts @@ -0,0 +1,56 @@ +import { Knex } from "knex"; + +export async function up(knex: Knex) { + await knex.schema.createTable("schema_migration", (b) => { + b.string("version"); + b.boolean("dirty"); + }); + + await knex.schema.createTable("users", (b) => { + b.string("username").primary().comment("user's login id"); + b.string("password_hash", 64).notNullable(); + b.string("password_salt", 64).notNullable(); + }); + await knex.schema.createTable("document", (b) => { + b.increments("id").primary(); + b.string("title").notNullable(); + b.string("content_type", 16).notNullable(); + b.string("basepath", 256).notNullable().comment("directory path for resource"); + b.string("filename", 256).notNullable().comment("filename"); + b.string("content_hash").nullable(); + b.json("additional").nullable(); + b.integer("created_at").notNullable(); + b.integer("modified_at").notNullable(); + b.integer("deleted_at"); + b.index("content_type", "content_type_index"); + }); + await knex.schema.createTable("tags", (b) => { + b.string("name").primary(); + b.text("description"); + }); + await knex.schema.createTable("doc_tag_relation", (b) => { + b.integer("doc_id").unsigned().notNullable(); + b.string("tag_name").notNullable(); + b.foreign("doc_id").references("document.id"); + b.foreign("tag_name").references("tags.name"); + b.primary(["doc_id", "tag_name"]); + }); + await knex.schema.createTable("permissions", (b) => { + b.string("username").notNullable(); + b.string("name").notNullable(); + b.primary(["username", "name"]); + b.foreign("username").references("users.username"); + }); + // create admin account. + await knex + .insert({ + username: "admin", + password_hash: "unchecked", + password_salt: "unchecked", + }) + .into("users"); +} + +export async function down(knex: Knex) { + throw new Error("Downward migrations are not supported. Restore from backup."); +} diff --git a/packages/server/package.json b/packages/server/package.json new file mode 100644 index 0000000..bf3919d --- /dev/null +++ b/packages/server/package.json @@ -0,0 +1,42 @@ +{ + "name": "followed", + "version": "1.0.0", + "description": "", + "main": "build/app.js", + "scripts": { + "compile": "swc src --out-dir compile", + "dev": "nodemon -r @swc-node/register --enable-source-maps --exec node app.ts", + "start": "node compile/app.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@zip.js/zip.js": "^2.7.40", + "better-sqlite3": "^9.4.3", + "chokidar": "^3.6.0", + "dotenv": "^16.4.5", + "jsonwebtoken": "^8.5.1", + "koa": "^2.15.2", + "koa-bodyparser": "^4.4.1", + "koa-compose": "^4.1.0", + "koa-router": "^12.0.1", + "kysely": "^0.27.3", + "natural-orderby": "^2.0.3", + "tiny-async-pool": "^1.3.0" + }, + "devDependencies": { + "@swc-node/register": "^1.9.0", + "@swc/cli": "^0.3.10", + "@swc/core": "^1.4.11", + "@types/better-sqlite3": "^7.6.9", + "@types/jsonwebtoken": "^8.5.9", + "@types/koa": "^2.15.0", + "@types/koa-bodyparser": "^4.3.12", + "@types/koa-compose": "^3.2.8", + "@types/koa-router": "^7.4.8", + "@types/node": ">=20.0.0", + "@types/tiny-async-pool": "^1.0.5", + "dbtype": "workspace:^", + "nodemon": "^3.1.0" + } +} diff --git a/packages/server/preload.ts b/packages/server/preload.ts new file mode 100644 index 0000000..396815a --- /dev/null +++ b/packages/server/preload.ts @@ -0,0 +1,7 @@ +// import { contextBridge, ipcRenderer } from "electron"; + +// contextBridge.exposeInMainWorld("electron", { +// passwordReset: async (username: string, toPw: string) => { +// return await ipcRenderer.invoke("reset_password", username, toPw); +// }, +// }); diff --git a/packages/server/src/SettingConfig.schema.json b/packages/server/src/SettingConfig.schema.json new file mode 100644 index 0000000..c4c7d48 --- /dev/null +++ b/packages/server/src/SettingConfig.schema.json @@ -0,0 +1,51 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/SettingConfig", + "definitions": { + "SettingConfig": { + "type": "object", + "properties": { + "localmode": { + "type": "boolean", + "description": "if true, server will bind on '127.0.0.1' rather than '0.0.0.0'" + }, + "guest": { + "type": "array", + "items": { + "$ref": "#/definitions/Permission" + }, + "description": "guest permission" + }, + "jwt_secretkey": { + "type": "string", + "description": "JWT secret key. if you change its value, all access tokens are invalidated." + }, + "port": { + "type": "number", + "description": "the port which running server is binding on." + }, + "mode": { + "type": "string", + "enum": ["development", "production"] + }, + "cli": { + "type": "boolean", + "description": "if true, do not show 'electron' window and show terminal only." + }, + "forbid_remote_admin_login": { + "type": "boolean", + "description": "forbid to login admin from remote client. but, it do not invalidate access token. \r if you want to invalidate access token, change 'jwt_secretkey'." + }, + "$schema": { + "type": "string" + } + }, + "required": ["localmode", "guest", "jwt_secretkey", "port", "mode", "cli", "forbid_remote_admin_login"], + "additionalProperties": false + }, + "Permission": { + "type": "string", + "enum": ["ModifyTag", "QueryContent", "ModifyTagDesc"] + } + } +} diff --git a/packages/server/src/SettingConfig.ts b/packages/server/src/SettingConfig.ts new file mode 100644 index 0000000..a59944e --- /dev/null +++ b/packages/server/src/SettingConfig.ts @@ -0,0 +1,80 @@ +import { randomBytes } from "node:crypto"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import type { Permission } from "./permission/permission"; + +export interface SettingConfig { + /** + * if true, server will bind on '127.0.0.1' rather than '0.0.0.0' + */ + localmode: boolean; + /** + * secure only + */ + secure: boolean; + + /** + * guest permission + */ + guest: Permission[]; + /** + * JWT secret key. if you change its value, all access tokens are invalidated. + */ + jwt_secretkey: string; + /** + * the port which running server is binding on. + */ + port: number; + + mode: "development" | "production"; + /** + * if true, do not show 'electron' window and show terminal only. + */ + cli: boolean; + /** forbid to login admin from remote client. but, it do not invalidate access token. + * if you want to invalidate access token, change 'jwt_secretkey'. */ + forbid_remote_admin_login: boolean; +} +const default_setting: SettingConfig = { + localmode: true, + secure: true, + guest: [], + jwt_secretkey: "itsRandom", + port: 8080, + mode: "production", + cli: false, + forbid_remote_admin_login: true, +}; +let setting: null | SettingConfig = null; + +// biome-ignore lint/suspicious/noExplicitAny: +const setEmptyToDefault = (target: any, default_table: SettingConfig) => { + let diff_occur = false; + for (const key in default_table) { + if (key === undefined || key in target) { + continue; + } + target[key] = default_table[key as keyof SettingConfig]; + diff_occur = true; + } + return diff_occur; +}; + +export const read_setting_from_file = () => { + const ret = existsSync("settings.json") ? JSON.parse(readFileSync("settings.json", { encoding: "utf8" })) : {}; + const partial_occur = setEmptyToDefault(ret, default_setting); + if (partial_occur) { + writeFileSync("settings.json", JSON.stringify(ret)); + } + return ret as SettingConfig; +}; +export function get_setting(): SettingConfig { + if (setting === null) { + setting = read_setting_from_file(); + const env = process.env.NODE_ENV; + if (env !== undefined && env !== "production" && env !== "development") { + throw new Error('process unknown value in NODE_ENV: must be either "development" or "production"'); + } + setting.mode = env ?? setting.mode; + } + return setting; +} diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts new file mode 100644 index 0000000..6807e05 --- /dev/null +++ b/packages/server/src/config.ts @@ -0,0 +1,22 @@ +import type { Knex as k } from "knex"; + +export namespace Knex { + export const config: { + development: k.Config; + production: k.Config; + } = { + development: { + client: "sqlite3", + connection: { + filename: "./devdb.sqlite3", + }, + debug: true, + }, + production: { + client: "sqlite3", + connection: { + filename: "./db.sqlite3", + }, + }, + }; +} diff --git a/packages/server/src/content/comic.ts b/packages/server/src/content/comic.ts new file mode 100644 index 0000000..f42338e --- /dev/null +++ b/packages/server/src/content/comic.ts @@ -0,0 +1,70 @@ +import { extname } from "node:path"; +import type { DocumentBody } from "dbtype/api"; +import { readZip } from "../util/zipwrap"; +import { type ContentConstructOption, createDefaultClass, registerContentReferrer } from "./file"; +import { TextWriter } from "@zip.js/zip.js"; + +type ComicType = "doujinshi" | "artist cg" | "manga" | "western"; +interface ComicDesc { + title: string; + artist?: string[]; + group?: string[]; + series?: string[]; + type: ComicType | [ComicType]; + character?: string[]; + tags?: string[]; +} +const ImageExt = [".gif", ".png", ".jpeg", ".bmp", ".webp", ".jpg"]; +export class ComicReferrer extends createDefaultClass("comic") { + desc: ComicDesc | undefined; + pagenum: number; + additional: ContentConstructOption | undefined; + constructor(path: string, option?: ContentConstructOption) { + super(path); + this.additional = option; + this.pagenum = 0; + } + async initDesc(): Promise { + if (this.desc !== undefined) return; + const zip = await readZip(this.path); + const entries = await zip.reader.getEntries(); + this.pagenum = entries.filter((x) => ImageExt.includes(extname(x.filename))).length; + const descEntry = entries.find(x=> x.filename === "desc.json"); + if (descEntry === undefined) { + return; + } + if (descEntry.getData === undefined) { + throw new Error("entry.getData is undefined"); + } + const textWriter = new TextWriter(); + const data = (await descEntry.getData(textWriter)); + this.desc = JSON.parse(data); + zip.reader.close() + .then(() => zip.handle.close()); + } + + async createDocumentBody(): Promise { + await this.initDesc(); + const basebody = await super.createDocumentBody(); + this.desc?.title; + if (this.desc === undefined) { + return basebody; + } + let tags: string[] = this.desc.tags ?? []; + tags = tags.concat(this.desc.artist?.map((x) => `artist:${x}`) ?? []); + tags = tags.concat(this.desc.character?.map((x) => `character:${x}`) ?? []); + tags = tags.concat(this.desc.group?.map((x) => `group:${x}`) ?? []); + tags = tags.concat(this.desc.series?.map((x) => `series:${x}`) ?? []); + const type = Array.isArray(this.desc.type) ? this.desc.type[0] : this.desc.type; + tags.push(`type:${type}`); + return { + ...basebody, + title: this.desc.title, + additional: { + page: this.pagenum, + }, + tags: tags, + }; + } +} +registerContentReferrer(ComicReferrer); diff --git a/packages/server/src/content/file.ts b/packages/server/src/content/file.ts new file mode 100644 index 0000000..21f4397 --- /dev/null +++ b/packages/server/src/content/file.ts @@ -0,0 +1,98 @@ +import { createHash } from "node:crypto"; +import { promises, type Stats } from "node:fs"; +import path, { extname } from "node:path"; +import type { DocumentBody } from "dbtype/api"; +/** + * content file or directory referrer + */ +export interface ContentFile { + getHash(): Promise; + createDocumentBody(): Promise; + readonly path: string; + readonly type: string; +} +export type ContentConstructOption = { + hash: string; +}; +type ContentFileConstructor = (new ( + path: string, + option?: ContentConstructOption, +) => ContentFile) & { + content_type: string; +}; +export const createDefaultClass = (type: string): ContentFileConstructor => { + const cons = class implements ContentFile { + readonly path: string; + // type = type; + static content_type = type; + protected hash: string | undefined; + protected stat: Stats | undefined; + + protected getStat(){ + return this.stat; + } + + constructor(path: string, option?: ContentConstructOption) { + this.path = path; + this.hash = option?.hash; + this.stat = undefined; + } + async createDocumentBody(): Promise { + console.log(`createDocumentBody: ${this.path}`); + const { base, dir, name } = path.parse(this.path); + + const ret = { + title: name, + basepath: dir, + additional: {}, + content_type: cons.content_type, + filename: base, + tags: [], + content_hash: await this.getHash(), + modified_at: await this.getMtime(), + } as DocumentBody; + return ret; + } + get type(): string { + return cons.content_type; + } + async getHash(): Promise { + if (this.hash !== undefined) return this.hash; + this.stat = await promises.stat(this.path); + const hash = createHash("sha512"); + hash.update(extname(this.path)); + hash.update(this.stat.mode.toString()); + // if(this.desc !== undefined) + // hash.update(JSON.stringify(this.desc)); + hash.update(this.stat.size.toString()); + this.hash = hash.digest("base64"); + return this.hash; + } + async getMtime(): Promise { + const oldStat = this.getStat(); + if (oldStat !== undefined) return oldStat.mtimeMs; + await this.getHash(); + const newStat = this.getStat(); + if (newStat === undefined) throw new Error("stat is undefined"); + return newStat.mtimeMs; + } + }; + return cons; +}; +const ContstructorTable: { [k: string]: ContentFileConstructor } = {}; +export function registerContentReferrer(s: ContentFileConstructor) { + console.log(`registered content type: ${s.content_type}`); + ContstructorTable[s.content_type] = s; +} +export function createContentFile(type: string, path: string, option?: ContentConstructOption) { + const constructorMethod = ContstructorTable[type]; + if (constructorMethod === undefined) { + console.log(`${type} are not in ${JSON.stringify(ContstructorTable)}`); + throw new Error("construction method of the content type is undefined"); + } + return new constructorMethod(path, option); +} +export function getContentFileConstructor(type: string): ContentFileConstructor | undefined { + const ret = ContstructorTable[type]; + return ret; +} diff --git a/src/content/mod.ts b/packages/server/src/content/mod.ts similarity index 100% rename from src/content/mod.ts rename to packages/server/src/content/mod.ts diff --git a/packages/server/src/content/video.ts b/packages/server/src/content/video.ts new file mode 100644 index 0000000..c26cfd5 --- /dev/null +++ b/packages/server/src/content/video.ts @@ -0,0 +1,6 @@ +import { registerContentReferrer } from "./file"; +import { createDefaultClass } from "./file"; + +export class VideoReferrer extends createDefaultClass("video") { +} +registerContentReferrer(VideoReferrer); diff --git a/packages/server/src/database.ts b/packages/server/src/database.ts new file mode 100644 index 0000000..997fe9f --- /dev/null +++ b/packages/server/src/database.ts @@ -0,0 +1,26 @@ +import { existsSync } from "node:fs"; +import { get_setting } from "./SettingConfig"; +import { getKysely } from "./db/kysely"; + +export async function connectDB() { + const kysely = getKysely(); + + let tries = 0; + for (;;) { + try { + console.log("try to connect db"); + await kysely.selectNoFrom(eb=> eb.val(1).as("dummy")).execute(); + console.log("connect success"); + } catch (err) { + if (tries < 3) { + tries++; + console.error(`connection fail ${err} retry...`); + await new Promise((resolve) => setTimeout(resolve, 1000)); + continue; + } + throw err; + } + break; + } + return kysely; +} diff --git a/packages/server/src/db/doc.ts b/packages/server/src/db/doc.ts new file mode 100644 index 0000000..0afa8d2 --- /dev/null +++ b/packages/server/src/db/doc.ts @@ -0,0 +1,234 @@ +import { getKysely } from "./kysely"; +import { jsonArrayFrom } from "kysely/helpers/sqlite"; +import type { DocumentAccessor } from "../model/doc"; +import type { + Document, + QueryListOption, + DocumentBody +} from "dbtype/api"; +import type { NotNull } from "kysely"; +import { MyParseJSONResultsPlugin } from "./plugin"; + +export type DBTagContentRelation = { + doc_id: number; + tag_name: string; +}; + +class SqliteDocumentAccessor implements DocumentAccessor { + constructor(private kysely = getKysely()) { + } + async search(search_word: string): Promise { + throw new Error("Method not implemented."); + } + async addList(content_list: DocumentBody[]): Promise { + return await this.kysely.transaction().execute(async (trx) => { + // add tags + const tagCollected = new Set(); + for (const content of content_list) { + for (const tag of content.tags) { + tagCollected.add(tag); + } + } + await trx.insertInto("tags") + .values(Array.from(tagCollected).map((x) => ({ name: x }))) + .onConflict((oc) => oc.doNothing()) + .execute(); + + const ids = await trx.insertInto("document") + .values(content_list.map((content) => { + const { tags, additional, ...rest } = content; + return { + additional: JSON.stringify(additional), + created_at: Date.now(), + ...rest, + }; + })) + .returning("id") + .execute(); + const id_lst = ids.map((x) => x.id); + + const doc_tags = content_list.flatMap((content, index) => { + const { tags, ...rest } = content; + return tags.map((tag) => ({ doc_id: id_lst[index], tag_name: tag })); + }); + await trx.insertInto("doc_tag_relation") + .values(doc_tags) + .execute(); + return id_lst; + }); + } + async add(c: DocumentBody) { + return await this.kysely.transaction().execute(async (trx) => { + const { tags, additional, ...rest } = c; + const id_lst = await trx.insertInto("document").values({ + additional: JSON.stringify(additional), + created_at: Date.now(), + ...rest, + }) + .returning("id") + .executeTakeFirst() as { id: number }; + const id = id_lst.id; + + // add tags + await trx.insertInto("tags") + .values(tags.map((x) => ({ name: x }))) + // on conflict is supported in sqlite and postgresql. + .onConflict((oc) => oc.doNothing()) + .execute(); + + if (tags.length > 0) { + await trx.insertInto("doc_tag_relation") + .values(tags.map((x) => ({ doc_id: id, tag_name: x }))) + .execute(); + } + return id; + }); + } + async del(id: number) { + // delete tags + await this.kysely + .deleteFrom("doc_tag_relation") + .where("doc_id", "=", id) + .execute(); + // delete document + const result = await this.kysely + .deleteFrom("document") + .where("id", "=", id) + .executeTakeFirst(); + return result.numDeletedRows > 0; + } + async findById(id: number, tagload?: boolean): Promise { + const doc = await this.kysely.selectFrom("document") + .selectAll() + .where("id", "=", id) + .$if(tagload ?? false, (qb) => + qb.select(eb => jsonArrayFrom( + eb.selectFrom("doc_tag_relation") + .select(["doc_tag_relation.tag_name"]) + .whereRef("document.id", "=", "doc_tag_relation.doc_id") + .select("tag_name") + ).as("tags")).withPlugin(new MyParseJSONResultsPlugin("tags")) + ) + .executeTakeFirst(); + if (!doc) return undefined; + + return { + ...doc, + content_hash: doc.content_hash ?? "", + additional: doc.additional !== null ? JSON.parse(doc.additional) : {}, + tags: doc.tags?.map((x: { tag_name: string }) => x.tag_name) ?? [], + }; + } + async findDeleted(content_type: string) { + const docs = await this.kysely + .selectFrom("document") + .selectAll() + .where("content_type", "=", content_type) + .where("deleted_at", "is not", null) + .$narrowType<{ deleted_at: NotNull }>() + .execute(); + return docs.map((x) => ({ + ...x, + tags: [], + content_hash: x.content_hash ?? "", + additional: {}, + })); + } + async findList(option?: QueryListOption) { + const { + allow_tag = [], + eager_loading = true, + limit = 20, + use_offset = false, + offset = 0, + word, + content_type, + cursor, + } = option ?? {}; + + const result = await this.kysely + .selectFrom("document") + .selectAll() + .$if(allow_tag.length > 0, (qb) => { + return allow_tag.reduce((prevQb, tag, index) => { + return prevQb.innerJoin(`doc_tag_relation as tags_${index}`, `tags_${index}.doc_id`, "document.id") + .where(`tags_${index}.tag_name`, "=", tag); + }, qb) as unknown as typeof qb; + }) + .$if(word !== undefined, (qb) => qb.where("title", "like", `%${word}%`)) + .$if(content_type !== undefined, (qb) => qb.where("content_type", "=", content_type as string)) + .$if(use_offset, (qb) => qb.offset(offset)) + .$if(!use_offset && cursor !== undefined, (qb) => qb.where("id", "<", cursor as number)) + .limit(limit) + .$if(eager_loading, (qb) => { + return qb.select(eb => + eb.selectFrom(e => + e.selectFrom("doc_tag_relation") + .select(["doc_tag_relation.tag_name"]) + .whereRef("document.id", "=", "doc_tag_relation.doc_id") + .as("agg") + ).select(e => e.fn.agg("json_group_array", ["agg.tag_name"]) + .as("tags_list") + ).as("tags") + ) + }) + .orderBy("id", "desc") + .execute(); + return result.map((x) => ({ + ...x, + content_hash: x.content_hash ?? "", + additional: x.additional !== null ? (JSON.parse(x.additional)) : {}, + tags: JSON.parse(x.tags ?? "[]"), //?.map((x: { tag_name: string }) => x.tag_name) ?? [], + })); + } + async findByPath(path: string, filename?: string): Promise { + const results = await this.kysely + .selectFrom("document") + .selectAll() + .where("basepath", "=", path) + .$if(filename !== undefined, (qb) => qb.where("filename", "=", filename as string)) + .execute(); + return results.map((x) => ({ + ...x, + content_hash: x.content_hash ?? "", + tags: [], + additional: {}, + })); + } + async update(c: Partial & { id: number }) { + const { id, tags, additional, ...rest } = c; + const r = await this.kysely.updateTable("document") + .set({ + ...rest, + modified_at: Date.now(), + additional: additional !== undefined ? JSON.stringify(additional) : undefined, + }) + .where("id", "=", id) + .executeTakeFirst(); + return r.numUpdatedRows > 0; + } + async addTag(c: Document, tag_name: string) { + if (c.tags.includes(tag_name)) return false; + await this.kysely.insertInto("tags") + .values({ name: tag_name }) + .onConflict((oc) => oc.doNothing()) + .execute(); + await this.kysely.insertInto("doc_tag_relation") + .values({ tag_name: tag_name, doc_id: c.id }) + .execute(); + c.tags.push(tag_name); + return true; + } + async delTag(c: Document, tag_name: string) { + if (c.tags.includes(tag_name)) return false; + await this.kysely.deleteFrom("doc_tag_relation") + .where("tag_name", "=", tag_name) + .where("doc_id", "=", c.id) + .execute(); + c.tags.splice(c.tags.indexOf(tag_name), 1); + return true; + } +} +export const createSqliteDocumentAccessor = (kysely = getKysely()): DocumentAccessor => { + return new SqliteDocumentAccessor(kysely); +}; diff --git a/packages/server/src/db/kysely.ts b/packages/server/src/db/kysely.ts new file mode 100644 index 0000000..31e1ba2 --- /dev/null +++ b/packages/server/src/db/kysely.ts @@ -0,0 +1,26 @@ +import { Kysely, ParseJSONResultsPlugin, SqliteDialect } from "kysely"; +import SqliteDatabase from "better-sqlite3"; +import type { DB } from "dbtype/types"; + +export function createSqliteDialect() { + const url = process.env.DATABASE_URL; + if (!url) { + throw new Error("DATABASE_URL is not set"); + } + const db = new SqliteDatabase(url); + return new SqliteDialect({ + database: db, + }); +} + +// Create a new Kysely instance with a new SqliteDatabase instance +let kysely: Kysely | null = null; +export function getKysely() { + if (!kysely) { + kysely = new Kysely({ + dialect: createSqliteDialect(), + // plugins: [new ParseJSONResultsPlugin()], + }); + } + return kysely; +} \ No newline at end of file diff --git a/src/db/mod.ts b/packages/server/src/db/mod.ts similarity index 100% rename from src/db/mod.ts rename to packages/server/src/db/mod.ts diff --git a/packages/server/src/db/plugin.ts b/packages/server/src/db/plugin.ts new file mode 100644 index 0000000..5574973 --- /dev/null +++ b/packages/server/src/db/plugin.ts @@ -0,0 +1,24 @@ +import type { KyselyPlugin, PluginTransformQueryArgs, PluginTransformResultArgs, QueryResult, RootOperationNode, UnknownRow } from "kysely"; + +export class MyParseJSONResultsPlugin implements KyselyPlugin { + + constructor(private readonly itemPath: string) { } + + transformQuery(args: PluginTransformQueryArgs): RootOperationNode { + // do nothing + return args.node; + } + async transformResult(args: PluginTransformResultArgs): Promise> { + return { + ...args.result, + rows: args.result.rows.map((row) => { + const newRow = { ...row }; + const item = newRow[this.itemPath]; + if (typeof item === "string") { + newRow[this.itemPath] = JSON.parse(item); + } + return newRow; + }) + } + } +} \ No newline at end of file diff --git a/packages/server/src/db/tag.ts b/packages/server/src/db/tag.ts new file mode 100644 index 0000000..35ff2cf --- /dev/null +++ b/packages/server/src/db/tag.ts @@ -0,0 +1,65 @@ +import { getKysely } from "./kysely"; +import { jsonArrayFrom } from "kysely/helpers/sqlite"; +import type { Tag, TagAccessor, TagCount } from "../model/tag"; +import type { DBTagContentRelation } from "./doc"; + +class SqliteTagAccessor implements TagAccessor { + + constructor(private kysely = getKysely()) { + } + async getAllTagCount(): Promise { + const result = await this.kysely + .selectFrom("doc_tag_relation") + .select("tag_name") + .select(qb => qb.fn.count("doc_id").as("occurs")) + .groupBy("tag_name") + .execute(); + return result; + } + async getAllTagList(): Promise { + return (await this.kysely.selectFrom("tags") + .selectAll() + .execute() + ).map((x) => ({ + name: x.name, + description: x.description ?? undefined, + })); + } + async getTagByName(name: string) { + const result = await this.kysely + .selectFrom("tags") + .selectAll() + .where("name", "=", name) + .executeTakeFirst(); + if (result === undefined) { + return undefined; + } + return { + name: result.name, + description: result.description ?? undefined, + }; + } + async addTag(tag: Tag) { + const result = await this.kysely.insertInto("tags") + .values([tag]) + .onConflict((oc) => oc.doNothing()) + .executeTakeFirst(); + return (result.numInsertedOrUpdatedRows ?? 0n) > 0; + } + async delTag(name: string) { + const result = await this.kysely.deleteFrom("tags") + .where("name", "=", name) + .executeTakeFirst(); + return (result.numDeletedRows ?? 0n) > 0; + } + async updateTag(name: string, desc: string) { + const result = await this.kysely.updateTable("tags") + .set({ description: desc }) + .where("name", "=", name) + .executeTakeFirst(); + return (result.numUpdatedRows ?? 0n) > 0; + } +} +export const createSqliteTagController = (kysely = getKysely()): TagAccessor => { + return new SqliteTagAccessor(kysely); +}; diff --git a/packages/server/src/db/user.ts b/packages/server/src/db/user.ts new file mode 100644 index 0000000..ba2c899 --- /dev/null +++ b/packages/server/src/db/user.ts @@ -0,0 +1,87 @@ +import { getKysely } from "./kysely"; +import { type IUser, Password, type UserAccessor, type UserCreateInput } from "../model/user"; + +class SqliteUser implements IUser { + readonly username: string; + readonly password: Password; + + constructor(username: string, pw: Password, private kysely = getKysely()) { + this.username = username; + this.password = pw; + } + async reset_password(password: string) { + this.password.set_password(password); + await this.kysely + .updateTable("users") + .where("username", "=", this.username) + .set({ password_hash: this.password.hash, password_salt: this.password.salt }) + .execute(); + } + async get_permissions() { + const permissions = await this.kysely + .selectFrom("permissions") + .selectAll() + .where("username", "=", this.username) + .execute(); + return permissions.map((x) => x.name); + } + async add(name: string) { + const result = await this.kysely + .insertInto("permissions") + .values({ username: this.username, name }) + .onConflict((oc) => oc.doNothing()) + .executeTakeFirst(); + return (result.numInsertedOrUpdatedRows ?? 0n) > 0; + } + async remove(name: string) { + const result = await this.kysely + .deleteFrom("permissions") + .where("username", "=", this.username) + .where("name", "=", name) + .executeTakeFirst(); + return (result.numDeletedRows ?? 0n) > 0; + } +} + +export const createSqliteUserController = (kysely = getKysely()): UserAccessor => { + const createUser = async (input: UserCreateInput) => { + if (undefined !== (await findUser(input.username))) { + return undefined; + } + const user = new SqliteUser(input.username, new Password(input.password), kysely); + await kysely + .insertInto("users") + .values({ username: user.username, password_hash: user.password.hash, password_salt: user.password.salt }) + .execute(); + return user; + }; + const findUser = async (id: string) => { + const user = await kysely + .selectFrom("users") + .selectAll() + .where("username", "=", id) + .executeTakeFirst(); + if (!user) return undefined; + if (!user.password_hash || !user.password_salt) { + throw new Error("password hash or salt is missing"); + } + if (user.username === null) { + throw new Error("username is null"); + } + return new SqliteUser(user.username, new Password({ + hash: user.password_hash, + salt: user.password_salt + }), kysely); + }; + const delUser = async (id: string) => { + const result = await kysely.deleteFrom("users") + .where("username", "=", id) + .executeTakeFirst(); + return (result.numDeletedRows ?? 0n) > 0; + }; + return { + createUser: createUser, + findUser: findUser, + delUser: delUser, + }; +}; diff --git a/packages/server/src/diff/content_handler.ts b/packages/server/src/diff/content_handler.ts new file mode 100644 index 0000000..d0e0cc6 --- /dev/null +++ b/packages/server/src/diff/content_handler.ts @@ -0,0 +1,121 @@ +import { basename, dirname, join as pathjoin } from "node:path"; +import { ContentFile, createContentFile } from "../content/mod"; +import type { Document, DocumentAccessor } from "../model/mod"; +import { ContentList } from "./content_list"; +import type { IDiffWatcher } from "./watcher"; + +// refactoring needed. +export class ContentDiffHandler { + /** content file list waiting to add */ + waiting_list: ContentList; + /** deleted contents */ + tombstone: Map; // hash, contentfile + doc_cntr: DocumentAccessor; + /** content type of handle */ + content_type: string; + constructor(cntr: DocumentAccessor, content_type: string) { + this.waiting_list = new ContentList(); + this.tombstone = new Map(); + this.doc_cntr = cntr; + this.content_type = content_type; + } + async setup() { + const deleted = await this.doc_cntr.findDeleted(this.content_type); + for (const it of deleted) { + this.tombstone.set(it.content_hash, it); + } + } + register(diff: IDiffWatcher) { + diff + .on("create", (path) => this.OnCreated(path)) + .on("delete", (path) => this.OnDeleted(path)) + .on("change", (prev, cur) => this.OnChanged(prev, cur)); + } + private async OnDeleted(cpath: string) { + const basepath = dirname(cpath); + const filename = basename(cpath); + console.log("deleted ", cpath); + // if it wait to add, delete it from waiting list. + if (this.waiting_list.hasByPath(cpath)) { + this.waiting_list.deleteByPath(cpath); + return; + } + const dbc = await this.doc_cntr.findByPath(basepath, filename); + // when there is no related content in db, ignore. + if (dbc.length === 0) { + console.log("its not in waiting_list and db!!!: ", cpath); + return; + } + const content_hash = dbc[0].content_hash; + // When a path is changed, it takes into account when the + // creation event occurs first and the deletion occurs, not + // the change event. + const cf = this.waiting_list.getByHash(content_hash); + if (cf) { + // if a path is changed, update the changed path. + console.log("update path from", cpath, "to", cf.path); + const newFilename = basename(cf.path); + const newBasepath = dirname(cf.path); + this.waiting_list.deleteByHash(content_hash); + await this.doc_cntr.update({ + id: dbc[0].id, + deleted_at: null, + filename: newFilename, + basepath: newBasepath, + }); + return; + } + // invalidate db and add it to tombstone. + await this.doc_cntr.update({ + id: dbc[0].id, + deleted_at: Date.now(), + }); + this.tombstone.set(dbc[0].content_hash, dbc[0]); + } + private async OnCreated(cpath: string) { + const basepath = dirname(cpath); + const filename = basename(cpath); + console.log("createContentFile", cpath); + const content = createContentFile(this.content_type, cpath); + const hash = await content.getHash(); + const c = this.tombstone.get(hash); + if (c !== undefined) { + await this.doc_cntr.update({ + id: c.id, + deleted_at: null, + filename: filename, + basepath: basepath, + }); + } + if (this.waiting_list.hasByHash(hash)) { + console.log("Hash Conflict!!!"); + } + this.waiting_list.set(content); + } + private async OnChanged(prev_path: string, cur_path: string) { + const prev_basepath = dirname(prev_path); + const prev_filename = basename(prev_path); + const cur_basepath = dirname(cur_path); + const cur_filename = basename(cur_path); + console.log("modify", cur_path, "from", prev_path); + const c = this.waiting_list.getByPath(prev_path); + if (c !== undefined) { + await this.waiting_list.delete(c); + const content = createContentFile(this.content_type, cur_path); + await this.waiting_list.set(content); + return; + } + const doc = await this.doc_cntr.findByPath(prev_basepath, prev_filename); + + if (doc.length === 0) { + await this.OnCreated(cur_path); + return; + } + + await this.doc_cntr.update({ + ...doc[0], + basepath: cur_basepath, + filename: cur_filename, + }); + } +} diff --git a/packages/server/src/diff/content_list.ts b/packages/server/src/diff/content_list.ts new file mode 100644 index 0000000..9e3f7f8 --- /dev/null +++ b/packages/server/src/diff/content_list.ts @@ -0,0 +1,59 @@ +import type { ContentFile } from "../content/mod"; + +export class ContentList { + /** path map */ + private cl: Map; + /** hash map */ + private hl: Map; + + constructor() { + this.cl = new Map(); + this.hl = new Map(); + } + hasByHash(s: string) { + return this.hl.has(s); + } + hasByPath(p: string) { + return this.cl.has(p); + } + getByHash(s: string) { + return this.hl.get(s); + } + getByPath(p: string) { + return this.cl.get(p); + } + async set(c: ContentFile) { + const path = c.path; + const hash = await c.getHash(); + this.cl.set(path, c); + this.hl.set(hash, c); + } + /** delete content file */ + async delete(c: ContentFile) { + const hash = await c.getHash(); + let r = true; + r = this.cl.delete(c.path) && r; + r = this.hl.delete(hash) && r; + return r; + } + async deleteByPath(p: string) { + const o = this.getByPath(p); + if (o === undefined) return false; + return await this.delete(o); + } + deleteByHash(s: string) { + const o = this.getByHash(s); + if (o === undefined) return false; + let r = true; + r = this.cl.delete(o.path) && r; + r = this.hl.delete(s) && r; + return r; + } + clear() { + this.cl.clear(); + this.hl.clear(); + } + getAll() { + return [...this.cl.values()]; + } +} diff --git a/packages/server/src/diff/diff.ts b/packages/server/src/diff/diff.ts new file mode 100644 index 0000000..3f69f50 --- /dev/null +++ b/packages/server/src/diff/diff.ts @@ -0,0 +1,46 @@ +import asyncPool from "tiny-async-pool"; +import type { DocumentAccessor } from "../model/doc"; +import { ContentDiffHandler } from "./content_handler"; +import type { IDiffWatcher } from "./watcher"; + +export class DiffManager { + watching: { [content_type: string]: ContentDiffHandler }; + doc_cntr: DocumentAccessor; + constructor(contorller: DocumentAccessor) { + this.watching = {}; + this.doc_cntr = contorller; + } + async register(content_type: string, watcher: IDiffWatcher) { + if (this.watching[content_type] === undefined) { + this.watching[content_type] = new ContentDiffHandler(this.doc_cntr, content_type); + } + this.watching[content_type].register(watcher); + await watcher.setup(this.doc_cntr); + } + async commit(type: string, path: string) { + const list = this.watching[type].waiting_list; + const c = list.getByPath(path); + if (c === undefined) { + throw new Error("path is not exist"); + } + await list.delete(c); + console.log(`commit: ${c.path} ${c.type}`); + const body = await c.createDocumentBody(); + const id = await this.doc_cntr.add(body); + return id; + } + async commitAll(type: string) { + const list = this.watching[type].waiting_list; + const contentFiles = list.getAll(); + list.clear(); + const bodies = await asyncPool(30, contentFiles, async (x) => await x.createDocumentBody()); + const ids = await this.doc_cntr.addList(bodies); + return ids; + } + getAdded() { + return Object.keys(this.watching).map((x) => ({ + type: x, + value: this.watching[x].waiting_list.getAll(), + })); + } +} diff --git a/src/diff/mod.ts b/packages/server/src/diff/mod.ts similarity index 100% rename from src/diff/mod.ts rename to packages/server/src/diff/mod.ts diff --git a/packages/server/src/diff/router.ts b/packages/server/src/diff/router.ts new file mode 100644 index 0000000..41ab912 --- /dev/null +++ b/packages/server/src/diff/router.ts @@ -0,0 +1,85 @@ +import type Koa from "koa"; +import Router from "koa-router"; +import type { ContentFile } from "../content/mod"; +import { AdminOnlyMiddleware } from "../permission/permission"; +import { sendError } from "../route/error_handler"; +import type { DiffManager } from "./diff"; + +function content_file_to_return(x: ContentFile) { + return { path: x.path, type: x.type }; +} + +export const getAdded = (diffmgr: DiffManager) => (ctx: Koa.Context, next: Koa.Next) => { + const ret = diffmgr.getAdded(); + ctx.body = ret.map((x) => ({ + type: x.type, + value: x.value.map((x) => ({ path: x.path, type: x.type })), + })); + ctx.type = "json"; +}; + +type PostAddedBody = { + type: string; + path: string; +}[]; + +function checkPostAddedBody(body: unknown): body is PostAddedBody { + if (Array.isArray(body)) { + return body.map((x) => "type" in x && "path" in x).every((x) => x); + } + return false; +} + +export const postAdded = (diffmgr: DiffManager) => async (ctx: Router.IRouterContext, next: Koa.Next) => { + const reqbody = ctx.request.body; + if (!checkPostAddedBody(reqbody)) { + sendError(400, "format exception"); + return; + } + const allWork = reqbody.map((op) => diffmgr.commit(op.type, op.path)); + const results = await Promise.all(allWork); + ctx.body = { + ok: true, + docs: results, + }; + ctx.type = "json"; + await next(); +}; +export const postAddedAll = (diffmgr: DiffManager) => async (ctx: Router.IRouterContext, next: Koa.Next) => { + if (!ctx.is("json")) { + sendError(400, "format exception"); + return; + } + const reqbody = ctx.request.body as Record; + if (!("type" in reqbody)) { + sendError(400, 'format exception: there is no "type"'); + return; + } + const t = reqbody.type; + if (typeof t !== "string") { + sendError(400, 'format exception: invalid type of "type"'); + return; + } + await diffmgr.commitAll(t); + ctx.body = { + ok: true, + }; + ctx.type = "json"; + await next(); +}; +/* +export const getNotWatched = (diffmgr : DiffManager)=> (ctx:Router.IRouterContext,next:Koa.Next)=>{ + ctx.body = { + added: diffmgr.added.map(content_file_to_return), + deleted: diffmgr.deleted.map(content_file_to_return), + }; + ctx.type = 'json'; +}*/ + +export function createDiffRouter(diffmgr: DiffManager) { + const ret = new Router(); + ret.get("/list", AdminOnlyMiddleware, getAdded(diffmgr)); + ret.post("/commit", AdminOnlyMiddleware, postAdded(diffmgr)); + ret.post("/commitall", AdminOnlyMiddleware, postAddedAll(diffmgr)); + return ret; +} diff --git a/packages/server/src/diff/watcher.ts b/packages/server/src/diff/watcher.ts new file mode 100644 index 0000000..70b01c8 --- /dev/null +++ b/packages/server/src/diff/watcher.ts @@ -0,0 +1,25 @@ +import type event from "node:events"; +import { FSWatcher, watch } from "node:fs"; +import { promises } from "node:fs"; +import { join } from "node:path"; +import type { DocumentAccessor } from "../model/doc"; + +const readdir = promises.readdir; + +export interface DiffWatcherEvent { + create: (path: string) => void; + delete: (path: string) => void; + change: (prev_path: string, cur_path: string) => void; +} + +export interface IDiffWatcher extends event.EventEmitter { + on(event: U, listener: DiffWatcherEvent[U]): this; + emit(event: U, ...arg: Parameters): boolean; + setup(cntr: DocumentAccessor): Promise; +} + +export function linkWatcher(fromWatcher: IDiffWatcher, toWatcher: IDiffWatcher) { + fromWatcher.on("create", (p) => toWatcher.emit("create", p)); + fromWatcher.on("delete", (p) => toWatcher.emit("delete", p)); + fromWatcher.on("change", (p, c) => toWatcher.emit("change", p, c)); +} diff --git a/packages/server/src/diff/watcher/ComicConfig.schema.json b/packages/server/src/diff/watcher/ComicConfig.schema.json new file mode 100644 index 0000000..bd7b2ec --- /dev/null +++ b/packages/server/src/diff/watcher/ComicConfig.schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/ComicConfig", + "definitions": { + "ComicConfig": { + "type": "object", + "properties": { "watch": { "type": "array", "items": { "type": "string" } }, "$schema": { "type": "string" } }, + "required": ["watch"], + "additionalProperties": false + } + } +} diff --git a/src/diff/watcher/ComicConfig.ts b/packages/server/src/diff/watcher/ComicConfig.ts similarity index 92% rename from src/diff/watcher/ComicConfig.ts rename to packages/server/src/diff/watcher/ComicConfig.ts index 0ff864c..3cc93ea 100644 --- a/src/diff/watcher/ComicConfig.ts +++ b/packages/server/src/diff/watcher/ComicConfig.ts @@ -1,7 +1,7 @@ import { ConfigManager } from "../../util/configRW"; import ComicSchema from "./ComicConfig.schema.json"; export interface ComicConfig { - watch: string[]; + watch: string[]; } export const ComicConfig = new ConfigManager("comic_config.json", { watch: [] }, ComicSchema); diff --git a/packages/server/src/diff/watcher/comic_watcher.ts b/packages/server/src/diff/watcher/comic_watcher.ts new file mode 100644 index 0000000..96f5273 --- /dev/null +++ b/packages/server/src/diff/watcher/comic_watcher.ts @@ -0,0 +1,13 @@ +import { ComicConfig } from "./ComicConfig"; +import { WatcherCompositer } from "./compositer"; +import { RecursiveWatcher } from "./recursive_watcher"; +import { WatcherFilter } from "./watcher_filter"; + +const createComicWatcherBase = (path: string) => { + return new WatcherFilter(new RecursiveWatcher(path), (x) => x.endsWith(".zip")); +}; +export const createComicWatcher = () => { + const file = ComicConfig.get_config_file(); + console.log(`register comic ${file.watch.join(",")}`); + return new WatcherCompositer(file.watch.map((path) => createComicWatcherBase(path))); +}; diff --git a/packages/server/src/diff/watcher/common_watcher.ts b/packages/server/src/diff/watcher/common_watcher.ts new file mode 100644 index 0000000..2782660 --- /dev/null +++ b/packages/server/src/diff/watcher/common_watcher.ts @@ -0,0 +1,44 @@ +import event from "node:events"; +import { type FSWatcher, promises, watch } from "node:fs"; +import { join } from "node:path"; +import type { DocumentAccessor } from "../../model/doc"; +import type { DiffWatcherEvent, IDiffWatcher } from "../watcher"; +import { setupHelp } from "./util"; + +const { readdir } = promises; + +export class CommonDiffWatcher extends event.EventEmitter implements IDiffWatcher { + on(event: U, listener: DiffWatcherEvent[U]): this { + return super.on(event, listener); + } + emit(event: U, ...arg: Parameters): boolean { + return super.emit(event, ...arg); + } + private _path: string; + private _watcher: FSWatcher; + + constructor(path: string) { + super(); + this._path = path; + this._watcher = watch(this._path, { persistent: true, recursive: false }, async (eventType, filename) => { + if (eventType === "rename") { + const cur = await readdir(this._path); + // add + if (cur.includes(filename)) { + this.emit("create", join(this.path, filename)); + } else { + this.emit("delete", join(this.path, filename)); + } + } + }); + } + async setup(cntr: DocumentAccessor): Promise { + await setupHelp(this, this.path, cntr); + } + public get path() { + return this._path; + } + watchClose() { + this._watcher.close(); + } +} diff --git a/packages/server/src/diff/watcher/compositer.ts b/packages/server/src/diff/watcher/compositer.ts new file mode 100644 index 0000000..b356f58 --- /dev/null +++ b/packages/server/src/diff/watcher/compositer.ts @@ -0,0 +1,23 @@ +import { EventEmitter } from "node:events"; +import type { DocumentAccessor } from "../../model/doc"; +import { type DiffWatcherEvent, type IDiffWatcher, linkWatcher } from "../watcher"; + +export class WatcherCompositer extends EventEmitter implements IDiffWatcher { + refWatchers: IDiffWatcher[]; + on(event: U, listener: DiffWatcherEvent[U]): this { + return super.on(event, listener); + } + emit(event: U, ...arg: Parameters): boolean { + return super.emit(event, ...arg); + } + constructor(refWatchers: IDiffWatcher[]) { + super(); + this.refWatchers = refWatchers; + for (const refWatcher of this.refWatchers) { + linkWatcher(refWatcher, this); + } + } + async setup(cntr: DocumentAccessor): Promise { + await Promise.all(this.refWatchers.map((x) => x.setup(cntr))); + } +} diff --git a/packages/server/src/diff/watcher/recursive_watcher.ts b/packages/server/src/diff/watcher/recursive_watcher.ts new file mode 100644 index 0000000..14fe70d --- /dev/null +++ b/packages/server/src/diff/watcher/recursive_watcher.ts @@ -0,0 +1,67 @@ +import { type FSWatcher, watch } from "chokidar"; +import { EventEmitter } from "node:events"; +import type { DocumentAccessor } from "../../model/doc"; +import type { DiffWatcherEvent, IDiffWatcher } from "../watcher"; +import { setupRecursive } from "./util"; + +type RecursiveWatcherOption = { + /** @default true */ + watchFile?: boolean; + /** @default false */ + watchDir?: boolean; +}; + +export class RecursiveWatcher extends EventEmitter implements IDiffWatcher { + on(event: U, listener: DiffWatcherEvent[U]): this { + return super.on(event, listener); + } + emit(event: U, ...arg: Parameters): boolean { + return super.emit(event, ...arg); + } + readonly path: string; + private watcher: FSWatcher; + + constructor( + path: string, + option: RecursiveWatcherOption = { + watchDir: false, + watchFile: true, + }, + ) { + super(); + this.path = path; + this.watcher = watch(path, { + persistent: true, + ignoreInitial: true, + depth: 100, + }); + option.watchFile ??= true; + if (option.watchFile) { + this.watcher + .on("add", (path) => { + const cpath = path; + // console.log("add ", cpath); + this.emit("create", cpath); + }) + .on("unlink", (path) => { + const cpath = path; + // console.log("unlink ", cpath); + this.emit("delete", cpath); + }); + } + if (option.watchDir) { + this.watcher + .on("addDir", (path) => { + const cpath = path; + this.emit("create", cpath); + }) + .on("unlinkDir", (path) => { + const cpath = path; + this.emit("delete", cpath); + }); + } + } + async setup(cntr: DocumentAccessor): Promise { + await setupRecursive(this, this.path, cntr); + } +} diff --git a/packages/server/src/diff/watcher/util.ts b/packages/server/src/diff/watcher/util.ts new file mode 100644 index 0000000..7e136f0 --- /dev/null +++ b/packages/server/src/diff/watcher/util.ts @@ -0,0 +1,37 @@ +import { promises } from "node:fs"; +import { join } from "node:path"; +const { readdir } = promises; +import type { DocumentAccessor } from "../../model/doc"; +import type { IDiffWatcher } from "../watcher"; + +function setupCommon(watcher: IDiffWatcher, basepath: string, initial_filenames: string[], cur: string[]) { + // Todo : reduce O(nm) to O(n+m) using hash map. + const added = cur.filter((x) => !initial_filenames.includes(x)); + const deleted = initial_filenames.filter((x) => !cur.includes(x)); + for (const it of added) { + const cpath = join(basepath, it); + watcher.emit("create", cpath); + } + for (const it of deleted) { + const cpath = join(basepath, it); + watcher.emit("delete", cpath); + } +} +export async function setupHelp(watcher: IDiffWatcher, basepath: string, cntr: DocumentAccessor) { + const initial_document = await cntr.findByPath(basepath); + const initial_filenames = initial_document.map((x) => x.filename); + const cur = await readdir(basepath); + setupCommon(watcher, basepath, initial_filenames, cur); +} +export async function setupRecursive(watcher: IDiffWatcher, basepath: string, cntr: DocumentAccessor) { + const initial_document = await cntr.findByPath(basepath); + const initial_filenames = initial_document.map((x) => x.filename); + const cur = await readdir(basepath, { withFileTypes: true }); + setupCommon( + watcher, + basepath, + initial_filenames, + cur.map((x) => x.name), + ); + await Promise.all([cur.filter((x) => x.isDirectory()).map((x) => setupHelp(watcher, join(basepath, x.name), cntr))]); +} diff --git a/packages/server/src/diff/watcher/watcher_filter.ts b/packages/server/src/diff/watcher/watcher_filter.ts new file mode 100644 index 0000000..67d0fca --- /dev/null +++ b/packages/server/src/diff/watcher/watcher_filter.ts @@ -0,0 +1,44 @@ +import { EventEmitter } from "node:events"; +import type { DocumentAccessor } from "../../model/doc"; +import { type DiffWatcherEvent, type IDiffWatcher, linkWatcher } from "../watcher"; + +export class WatcherFilter extends EventEmitter implements IDiffWatcher { + refWatcher: IDiffWatcher; + filter: (filename: string) => boolean; + on(event: U, listener: DiffWatcherEvent[U]): this { + return super.on(event, listener); + } + /** + * emit event + * @param event + * @param arg + * @returns `true` if the event had listeners, `false` otherwise. + */ + emit(event: U, ...arg: Parameters): boolean { + if (event === "change") { + const prev = arg[0]; + const cur = arg[1] as string; + if (this.filter(prev)) { + if (this.filter(cur)) { + return super.emit("change", prev, cur); + } + return super.emit("delete", cur); + } + if (this.filter(cur)) { + return super.emit("create", cur); + } + return false; + }if (!this.filter(arg[0])) { + return false; + }return super.emit(event, ...arg); + } + constructor(refWatcher: IDiffWatcher, filter: (filename: string) => boolean) { + super(); + this.refWatcher = refWatcher; + this.filter = filter; + linkWatcher(refWatcher, this); + } + setup(cntr: DocumentAccessor): Promise { + return this.refWatcher.setup(cntr); + } +} diff --git a/packages/server/src/login.ts b/packages/server/src/login.ts new file mode 100644 index 0000000..1046d6f --- /dev/null +++ b/packages/server/src/login.ts @@ -0,0 +1,240 @@ +import { sign, TokenExpiredError, verify } from "jsonwebtoken"; +import type Koa from "koa"; +import Router from "koa-router"; +import type { IUser, UserAccessor } from "./model/mod"; +import { sendError } from "./route/error_handler"; +import { get_setting } from "./SettingConfig"; + +type PayloadInfo = { + username: string; + permission: string[]; +}; + +export type UserState = { + user: PayloadInfo; +}; + +const isUserState = (obj: object | string): obj is PayloadInfo => { + if (typeof obj === "string") return false; + return "username" in obj && "permission" in obj && Array.isArray((obj as { permission: unknown }).permission); +}; +type RefreshPayloadInfo = { username: string }; +const isRefreshToken = (obj: object | string): obj is RefreshPayloadInfo => { + if (typeof obj === "string") return false; + return "username" in obj && typeof (obj as { username: unknown }).username === "string"; +}; + +export const accessTokenName = "access_token"; +export const refreshTokenName = "refresh_token"; +const accessExpiredTime = 60 * 60; // 1 hour +const refreshExpiredTime = 60 * 60 * 24 * 14; // 14 day; + +export const getAdminAccessTokenValue = () => { + const { jwt_secretkey } = get_setting(); + return publishAccessToken(jwt_secretkey, "admin", [], accessExpiredTime); +}; +export const getAdminRefreshTokenValue = () => { + const { jwt_secretkey } = get_setting(); + return publishRefreshToken(jwt_secretkey, "admin", refreshExpiredTime); +}; +const publishAccessToken = (secretKey: string, username: string, permission: string[], expiredtime: number) => { + const payload = sign( + { + username: username, + permission: permission, + }, + secretKey, + { expiresIn: expiredtime }, + ); + return payload; +}; +const publishRefreshToken = (secretKey: string, username: string, expiredtime: number) => { + const payload = sign({ username: username }, secretKey, { expiresIn: expiredtime }); + return payload; +}; +function setToken(ctx: Koa.Context, token_name: string, token_payload: string | null, expiredtime: number) { + const setting = get_setting(); + if (token_payload === null && !ctx.cookies.get(token_name)) { + return; + } + ctx.cookies.set(token_name, token_payload, { + httpOnly: true, + secure: setting.secure, + sameSite: "strict", + expires: new Date(Date.now() + expiredtime * 1000), + }); +} +export const createLoginMiddleware = (userController: UserAccessor) => async (ctx: Koa.Context, _next: Koa.Next) => { + const setting = get_setting(); + const secretKey = setting.jwt_secretkey; + const body = ctx.request.body; + // check format + if (typeof body === "string" || !("username" in body) || !("password" in body)) { + return sendError(400, "invalid form : username or password is not found in query."); + } + const username = body.username; + const password = body.password; + // check type + if (typeof username !== "string" || typeof password !== "string") { + return sendError(400, "invalid form : username or password is not string"); + } + // if admin login is forbidden? + if (username === "admin" && setting.forbid_remote_admin_login) { + return sendError(403, "forbidden remote admin login"); + } + const user = await userController.findUser(username); + // username not exist + if (user === undefined) return sendError(401, "not authorized"); + // password not matched + if (!user.password.check_password(password)) { + return sendError(401, "not authorized"); + } + // create token + const userPermission = await user.get_permissions(); + const payload = publishAccessToken(secretKey, user.username, userPermission, accessExpiredTime); + const payload2 = publishRefreshToken(secretKey, user.username, refreshExpiredTime); + setToken(ctx, accessTokenName, payload, accessExpiredTime); + setToken(ctx, refreshTokenName, payload2, refreshExpiredTime); + ctx.body = { + username: user.username, + permission: userPermission, + accessExpired: Math.floor(Date.now() / 1000) + accessExpiredTime, + }; + console.log(`${username} logined`); + return; +}; + +export const LogoutMiddleware = (ctx: Koa.Context, next: Koa.Next) => { + const setting = get_setting(); + ctx.cookies.set(accessTokenName, null); + ctx.cookies.set(refreshTokenName, null); + ctx.body = { + ok: true, + username: "", + permission: setting.guest, + }; + return; +}; +export const createUserMiddleWare = + (userController: UserAccessor) => async (ctx: Koa.ParameterizedContext, next: Koa.Next) => { + const refreshToken = refreshTokenHandler(userController); + const setting = get_setting(); + const setGuest = async () => { + setToken(ctx, accessTokenName, null, 0); + setToken(ctx, refreshTokenName, null, 0); + ctx.state.user = { username: "", permission: setting.guest }; + return await next(); + }; + return await refreshToken(ctx, setGuest, next); + }; +const refreshTokenHandler = (cntr: UserAccessor) => async (ctx: Koa.Context, fail: Koa.Next, next: Koa.Next) => { + const accessPayload = ctx.cookies.get(accessTokenName); + const setting = get_setting(); + const secretKey = setting.jwt_secretkey; + if (accessPayload === undefined) { + return await checkRefreshAndUpdate(); + } + try { + const o = verify(accessPayload, secretKey); + if (isUserState(o)) { + ctx.state.user = o; + return await next(); + } + console.error("invalid token detected"); + throw new Error("token form invalid"); + } catch (e) { + if (e instanceof TokenExpiredError) { + return await checkRefreshAndUpdate(); + }throw e; + } + async function checkRefreshAndUpdate() { + const refreshPayload = ctx.cookies.get(refreshTokenName); + if (refreshPayload === undefined) { + return await fail(); // refresh token doesn't exist + } + try { + const o = verify(refreshPayload, secretKey); + if (isRefreshToken(o)) { + const user = await cntr.findUser(o.username); + if (user === undefined) return await fail(); // already non-existence user + const perm = await user.get_permissions(); + const payload = publishAccessToken(secretKey, user.username, perm, accessExpiredTime); + setToken(ctx, accessTokenName, payload, accessExpiredTime); + ctx.state.user = { username: o.username, permission: perm }; + } else { + console.error("invalid token detected"); + throw new Error("token form invalid"); + } + } catch (e) { + if (e instanceof TokenExpiredError) { + // refresh token is expired. + return await fail(); + }throw e; + } + return await next(); + } +}; +export const createRefreshTokenMiddleware = (cntr: UserAccessor) => async (ctx: Koa.Context, next: Koa.Next) => { + const handler = refreshTokenHandler(cntr); + await handler(ctx, fail, success); + async function fail() { + const user = ctx.state.user as PayloadInfo; + ctx.body = { + refresh: false, + ...user, + }; + ctx.type = "json"; + } + async function success() { + const user = ctx.state.user as PayloadInfo; + ctx.body = { + ...user, + refresh: true, + refreshExpired: Math.floor(Date.now() / 1000 + accessExpiredTime), + }; + ctx.type = "json"; + } +}; +export const resetPasswordMiddleware = (cntr: UserAccessor) => async (ctx: Koa.Context, next: Koa.Next) => { + const body = ctx.request.body; + if (typeof body !== "object" || !("username" in body) || !("oldpassword" in body) || !("newpassword" in body)) { + return sendError(400, "request body is invalid format"); + } + const username = body.username; + const oldpw = body.oldpassword; + const newpw = body.newpassword; + if (typeof username !== "string" || typeof oldpw !== "string" || typeof newpw !== "string") { + return sendError(400, "request body is invalid format"); + } + const user = await cntr.findUser(username); + if (user === undefined) { + return sendError(403, "not authorized"); + } + if (!user.password.check_password(oldpw)) { + return sendError(403, "not authorized"); + } + user.reset_password(newpw); + ctx.body = { ok: true }; + ctx.type = "json"; +}; + +export function createLoginRouter(userController: UserAccessor) { + const router = new Router(); + router.post("/login", createLoginMiddleware(userController)); + router.post("/logout", LogoutMiddleware); + router.post("/refresh", createRefreshTokenMiddleware(userController)); + router.post("/reset", resetPasswordMiddleware(userController)); + return router; +} + +export const getAdmin = async (cntr: UserAccessor) => { + const admin = await cntr.findUser("admin"); + if (admin === undefined) { + throw new Error("initial process failed!"); // ??? + } + return admin; +}; + +export const isAdminFirst = (admin: IUser) => { + return admin.password.hash === "unchecked" && admin.password.salt === "unchecked"; +}; diff --git a/packages/server/src/model/doc.ts b/packages/server/src/model/doc.ts new file mode 100644 index 0000000..dab7cb0 --- /dev/null +++ b/packages/server/src/model/doc.ts @@ -0,0 +1,81 @@ +import { check_type } from "../util/type_check"; +import type { + DocumentBody, + Document, + QueryListOption +} from "dbtype/api"; + +export const MetaContentBody = { + title: "string", + content_type: "string", + basepath: "string", + filename: "string", + content_hash: "string", + additional: "object", + tags: "string[]", +}; + +export const isDocBody = (c: unknown): c is DocumentBody => { + return check_type(c, MetaContentBody); +}; + +export const isDoc = (c: unknown): c is Document => { + if (typeof c !== "object" || c === null) return false; + if ("id" in c && typeof c.id === "number") { + const { id, ...rest } = c; + return isDocBody(rest); + } + return false; +}; + +export interface DocumentAccessor { + /** + * find list by option + * @returns documents list + */ + findList: (option?: QueryListOption) => Promise; + /** + * @returns document if exist, otherwise undefined + */ + findById: (id: number, tagload?: boolean) => Promise; + /** + * find by base path and filename. + * if you call this function with filename, its return array length is 0 or 1. + */ + findByPath: (basepath: string, filename?: string) => Promise; + /** + * find deleted content + */ + findDeleted: (content_type: string) => Promise; + /** + * search by in document + */ + search: (search_word: string) => Promise; + /** + * update document except tag. + */ + update: (c: Partial & { id: number }) => Promise; + /** + * add document + */ + add: (c: DocumentBody) => Promise; + /** + * add document list + */ + addList: (content_list: DocumentBody[]) => Promise; + /** + * delete document + * @returns if it exists, return true. + */ + del: (id: number) => Promise; + /** + * @param c Valid Document + * @param tagname tag name to add + * @returns if success, return true + */ + addTag: (c: Document, tag_name: string) => Promise; + /** + * @returns if success, return true + */ + delTag: (c: Document, tag_name: string) => Promise; +} diff --git a/src/model/mod.ts b/packages/server/src/model/mod.ts similarity index 100% rename from src/model/mod.ts rename to packages/server/src/model/mod.ts diff --git a/packages/server/src/model/tag.ts b/packages/server/src/model/tag.ts new file mode 100644 index 0000000..ab3f985 --- /dev/null +++ b/packages/server/src/model/tag.ts @@ -0,0 +1,18 @@ +export interface Tag { + readonly name: string; + description?: string; +} + +export interface TagCount { + tag_name: string; + occurs: number; +} + +export interface TagAccessor { + getAllTagList: () => Promise; + getAllTagCount(): Promise; + getTagByName: (name: string) => Promise; + addTag: (tag: Tag) => Promise; + delTag: (name: string) => Promise; + updateTag: (name: string, tag: string) => Promise; +} diff --git a/packages/server/src/model/user.ts b/packages/server/src/model/user.ts new file mode 100644 index 0000000..f4b8005 --- /dev/null +++ b/packages/server/src/model/user.ts @@ -0,0 +1,84 @@ +import { createHmac, randomBytes } from "node:crypto"; + +function hashForPassword(salt: string, password: string) { + return createHmac("sha256", salt).update(password).digest("hex"); +} +function createPasswordHashAndSalt(password: string): { salt: string; hash: string } { + const secret = randomBytes(32).toString("hex"); + return { + salt: secret, + hash: hashForPassword(secret, password), + }; +} + +export class Password { + private _salt: string; + private _hash: string; + constructor(pw: string | { salt: string; hash: string }) { + const { salt, hash } = typeof pw === "string" ? createPasswordHashAndSalt(pw) : pw; + this._hash = hash; + this._salt = salt; + } + set_password(password: string) { + const { salt, hash } = createPasswordHashAndSalt(password); + this._hash = hash; + this._salt = salt; + } + check_password(password: string): boolean { + return this._hash === hashForPassword(this._salt, password); + } + get salt() { + return this._salt; + } + get hash() { + return this._hash; + } +} + +export interface UserCreateInput { + username: string; + password: string; +} + +export interface IUser { + readonly username: string; + readonly password: Password; + /** + * return user's permission list. + */ + get_permissions(): Promise; + /** + * add permission + * @param name permission name to add + * @returns if `name` doesn't exist, return true + */ + add(name: string): Promise; + /** + * remove permission + * @param name permission name to remove + * @returns if `name` exist, return true + */ + remove(name: string): Promise; + /** + * reset password. + * @param password password to set + */ + reset_password(password: string): Promise; +} + +export interface UserAccessor { + /** + * create user + * @returns if user exist, return undefined + */ + createUser: (input: UserCreateInput) => Promise; + /** + * find user + */ + findUser: (username: string) => Promise; + /** + * remove user + * @returns if user exist, true + */ + delUser: (username: string) => Promise; +} diff --git a/packages/server/src/permission/permission.ts b/packages/server/src/permission/permission.ts new file mode 100644 index 0000000..8229316 --- /dev/null +++ b/packages/server/src/permission/permission.ts @@ -0,0 +1,59 @@ +import type Koa from "koa"; +import type { UserState } from "../login"; +import { sendError } from "../route/error_handler"; + +export enum Permission { + // ======== + // not implemented + // admin only + /** remove document */ + // removeContent = 'removeContent', + + /** upload document */ + // uploadContent = 'uploadContent', + + /** modify document except base path, filename, content_hash. but admin can modify all. */ + // modifyContent = 'modifyContent', + + /** add tag into document */ + // addTagContent = 'addTagContent', + /** remove tag from document */ + // removeTagContent = 'removeTagContent', + /** ModifyTagInDoc */ + ModifyTag = "ModifyTag", + + /** find documents with query */ + // findAllContent = 'findAllContent', + /** find one document. */ + // findOneContent = 'findOneContent', + /** view content*/ + // viewContent = 'viewContent', + QueryContent = "QueryContent", + + /** modify description about the one tag. */ + modifyTagDesc = "ModifyTagDesc", +} + +export const createPermissionCheckMiddleware = + (...permissions: string[]) => + async (ctx: Koa.ParameterizedContext, next: Koa.Next) => { + const user = ctx.state.user; + if (user.username === "admin") { + return await next(); + } + const user_permission = user.permission; + // if permissions is not subset of user permission + if (!permissions.map((p) => user_permission.includes(p)).every((x) => x)) { + if (user.username === "") { + return sendError(401, "you are guest. login needed."); + }return sendError(403, "do not have permission"); + } + await next(); + }; +export const AdminOnlyMiddleware = async (ctx: Koa.ParameterizedContext, next: Koa.Next) => { + const user = ctx.state.user; + if (user.username !== "admin") { + return sendError(403, "admin only"); + } + await next(); +}; diff --git a/packages/server/src/route/all.ts b/packages/server/src/route/all.ts new file mode 100644 index 0000000..49724ef --- /dev/null +++ b/packages/server/src/route/all.ts @@ -0,0 +1,58 @@ +import type { DefaultContext, Middleware, Next, ParameterizedContext } from "koa"; +import compose from "koa-compose"; +import Router from "koa-router"; +import ComicRouter from "./comic"; +import type { ContentContext } from "./context"; +import VideoRouter from "./video"; + +const table: { [s: string]: Router | undefined } = { + comic: new ComicRouter(), + video: new VideoRouter(), +}; +const all_middleware = + (cont: string | undefined, restarg: string | undefined) => + async (ctx: ParameterizedContext, next: Next) => { + if (cont === undefined) { + ctx.status = 404; + return; + } + if (ctx.state.location.type !== cont) { + console.error("not matched"); + ctx.status = 404; + return; + } + const router = table[cont]; + if (router === undefined) { + ctx.status = 404; + return; + } + const rest = `/${restarg ?? ""}`; + const result = router.match(rest, "GET"); + if (!result.route) { + return await next(); + } + // biome-ignore lint/suspicious/noExplicitAny: + const chain = result.pathAndMethod.reduce((combination: Middleware[], cur) => { + combination.push(async (ctx, next) => { + const captures = cur.captures(rest); + ctx.params = cur.params(rest, captures); + ctx.request.params = ctx.params; + ctx.routerPath = cur.path; + return await next(); + }); + return combination.concat(cur.stack); + }, []); + return await compose(chain)(ctx, next); + }; +export class AllContentRouter extends Router { + constructor() { + super(); + this.get("/:content_type", async (ctx, next) => { + return await all_middleware(ctx.params.content_type, undefined)(ctx, next); + }); + this.get("/:content_type/:rest(.*)", async (ctx, next) => { + const cont = ctx.params.content_type as string; + return await all_middleware(cont, ctx.params.rest)(ctx, next); + }); + } +} diff --git a/packages/server/src/route/comic.ts b/packages/server/src/route/comic.ts new file mode 100644 index 0000000..cc05aaf --- /dev/null +++ b/packages/server/src/route/comic.ts @@ -0,0 +1,135 @@ +import type { Context } from "koa"; +import Router from "koa-router"; +import { createReadableStreamFromZip, entriesByNaturalOrder, readZip } from "../util/zipwrap"; +import type { ContentContext } from "./context"; +import { since_last_modified } from "./util"; +import type { ZipReader } from "@zip.js/zip.js"; +import type { FileHandle } from "node:fs/promises"; +import { Readable } from "node:stream"; + +/** + * zip stream cache. + */ +const ZipStreamCache = new Map, + handle: FileHandle, + refCount: number, +}>(); + + +function markUseZip(path: string) { + const ret = ZipStreamCache.get(path); + if (ret) { + ret.refCount++; + } + return ret !== undefined; +} + +async function acquireZip(path: string, marked = false) { + const ret = ZipStreamCache.get(path); + if (!ret) { + const obj = await readZip(path); + const check = ZipStreamCache.get(path); + if (check) { + check.refCount++; + // if the cache is updated, release the previous one. + releaseZip(path); + return check.reader; + } + // if the cache is not updated, set the new one. + ZipStreamCache.set(path, { + reader: obj.reader, + handle: obj.handle, + refCount: 1, + }); + return obj.reader; + } + if (!marked) { + ret.refCount++; + } + return ret.reader; +} + +function releaseZip(path: string) { + const obj = ZipStreamCache.get(path); + if (obj === undefined) { + console.warn(`warning! duplicate release at ${path}`); + return; + } + if (obj.refCount === 1) { + const { reader, handle } = obj; + reader.close().then(() => { + handle.close(); + }); + ZipStreamCache.delete(path); + } else { + obj.refCount--; + } +} + +async function renderZipImage(ctx: Context, path: string, page: number) { + const image_ext = ["gif", "png", "jpeg", "bmp", "webp", "jpg"]; + const marked = markUseZip(path); + const zip = await acquireZip(path, marked); + const entries = (await entriesByNaturalOrder(zip)).filter((x) => { + const ext = x.filename.split(".").pop(); + return ext !== undefined && image_ext.includes(ext); + }); + if (0 <= page && page < entries.length) { + const entry = entries[page]; + const last_modified = entry.lastModDate; + if (since_last_modified(ctx, last_modified)) { + return; + } + const read_stream = await createReadableStreamFromZip(zip, entry); + const nodeReadableStream = new Readable(); + nodeReadableStream._read = () => { }; + + read_stream.pipeTo(new WritableStream({ + write(chunk) { + nodeReadableStream.push(chunk); + }, + close() { + nodeReadableStream.push(null); + }, + })); + nodeReadableStream.on("error", (err) => { + console.error("readalbe stream error",err); + setTimeout(()=>{ + releaseZip(path) + },100); + }); + nodeReadableStream.on("close", () => { + setTimeout(()=>{ + releaseZip(path); + },100); + }); + + ctx.body = nodeReadableStream; + ctx.response.length = entry.uncompressedSize; + ctx.response.type = entry.filename.split(".").pop() as string; + ctx.status = 200; + ctx.set("Date", new Date().toUTCString()); + ctx.set("Last-Modified", last_modified.toUTCString()); + } else { + ctx.status = 404; + } +} + +export class ComicRouter extends Router { + constructor() { + super(); + this.get("/", async (ctx, next) => { + await renderZipImage(ctx, ctx.state.location.path, 0); + }); + this.get("/:page(\\d+)", async (ctx, next) => { + const page = Number.parseInt(ctx.params.page); + await renderZipImage(ctx, ctx.state.location.path, page); + }); + this.get("/thumbnail", async (ctx, next) => { + await renderZipImage(ctx, ctx.state.location.path, 0); + }); + } +} + +export default ComicRouter; diff --git a/packages/server/src/route/contents.ts b/packages/server/src/route/contents.ts new file mode 100644 index 0000000..9a2068f --- /dev/null +++ b/packages/server/src/route/contents.ts @@ -0,0 +1,163 @@ +import type { Context, Next } from "koa"; +import Router from "koa-router"; +import { join } from "node:path"; +import type { + Document, + QueryListOption, +} from "dbtype/api"; +import type { DocumentAccessor } from "../model/doc"; +import { + AdminOnlyMiddleware as AdminOnly, + createPermissionCheckMiddleware as PerCheck, + Permission as Per, +} from "../permission/permission"; +import { AllContentRouter } from "./all"; +import type { ContentLocation } from "./context"; +import { sendError } from "./error_handler"; +import { ParseQueryArgString, ParseQueryArray, ParseQueryBoolean, ParseQueryNumber } from "./util"; + +const ContentIDHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + const num = Number.parseInt(ctx.params.num); + const document = await controller.findById(num, true); + if (document === undefined) { + return sendError(404, "document does not exist."); + } + ctx.body = document; + ctx.type = "json"; +}; +const ContentTagIDHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + const num = Number.parseInt(ctx.params.num); + const document = await controller.findById(num, true); + if (document === undefined) { + return sendError(404, "document does not exist."); + } + ctx.body = document.tags; + ctx.type = "json"; +}; +const ContentQueryHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + const query_limit = ctx.query.limit; + const query_cursor = ctx.query.cursor; + const query_word = ctx.query.word; + const query_content_type = ctx.query.content_type; + const query_offset = ctx.query.offset; + const query_use_offset = ctx.query.use_offset; + if (Array.isArray(query_limit) ||Array.isArray(query_cursor) ||Array.isArray(query_word) ||Array.isArray(query_content_type) ||Array.isArray(query_offset) ||Array.isArray(query_use_offset) + ) { + return sendError(400, "paramter can not be array"); + } + const limit = Math.min(ParseQueryNumber(query_limit) ?? 20, 100); + const cursor = ParseQueryNumber(query_cursor); + const word = ParseQueryArgString(query_word); + const content_type = ParseQueryArgString(query_content_type); + const offset = ParseQueryNumber(query_offset); + if (Number.isNaN(limit) || Number.isNaN(cursor) || Number.isNaN(offset)) { + return sendError(400, "parameter limit, cursor or offset is not a number"); + } + const allow_tag = ParseQueryArray(ctx.query.allow_tag); + const [ok, use_offset] = ParseQueryBoolean(query_use_offset); + if (!ok) { + return sendError(400, "use_offset must be true or false."); + } + const option: QueryListOption = { + limit: limit, + allow_tag: allow_tag, + word: word, + cursor: cursor, + eager_loading: true, + offset: offset, + use_offset: use_offset, + content_type: content_type, + }; + const document = await controller.findList(option); + ctx.body = document; + ctx.type = "json"; +}; +const UpdateContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + const num = Number.parseInt(ctx.params.num); + + if (ctx.request.type !== "json") { + return sendError(400, "update fail. invalid document type: it is not json."); + } + if (typeof ctx.request.body !== "object") { + return sendError(400, "update fail. invalid argument: not"); + } + const content_desc: Partial & { id: number } = { + id: num, + ...ctx.request.body, + }; + const success = await controller.update(content_desc); + ctx.body = JSON.stringify(success); + ctx.type = "json"; +}; + +const AddTagHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + let tag_name = ctx.params.tag; + const num = Number.parseInt(ctx.params.num); + if (typeof tag_name === "undefined") { + return sendError(400, "??? Unreachable"); + } + tag_name = String(tag_name); + const c = await controller.findById(num); + if (c === undefined) { + return sendError(404); + } + const r = await controller.addTag(c, tag_name); + ctx.body = JSON.stringify(r); + ctx.type = "json"; +}; +const DelTagHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + let tag_name = ctx.params.tag; + const num = Number.parseInt(ctx.params.num); + if (typeof tag_name === "undefined") { + return sendError(400, "?? Unreachable"); + } + tag_name = String(tag_name); + const c = await controller.findById(num); + if (c === undefined) { + return sendError(404); + } + const r = await controller.delTag(c, tag_name); + ctx.body = JSON.stringify(r); + ctx.type = "json"; +}; +const DeleteContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + const num = Number.parseInt(ctx.params.num); + const r = await controller.del(num); + ctx.body = JSON.stringify(r); + ctx.type = "json"; +}; +const ContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { + const num = Number.parseInt(ctx.params.num); + const document = await controller.findById(num, true); + if (document === undefined) { + return sendError(404, "document does not exist."); + } + if (document.deleted_at !== null) { + return sendError(404, "document has been removed."); + } + const path = join(document.basepath, document.filename); + ctx.state.location = { + path: path, + type: document.content_type, + additional: document.additional, + } as ContentLocation; + await next(); +}; + +export const getContentRouter = (controller: DocumentAccessor) => { + const ret = new Router(); + ret.get("/search", PerCheck(Per.QueryContent), ContentQueryHandler(controller)); + ret.get("/:num(\\d+)", PerCheck(Per.QueryContent), ContentIDHandler(controller)); + ret.post("/:num(\\d+)", AdminOnly, UpdateContentHandler(controller)); + // ret.use("/:num(\\d+)/:content_type"); + // ret.post("/",AdminOnly,CreateContentHandler(controller)); + ret.get("/:num(\\d+)/tags", PerCheck(Per.QueryContent), ContentTagIDHandler(controller)); + ret.post("/:num(\\d+)/tags/:tag", PerCheck(Per.ModifyTag), AddTagHandler(controller)); + ret.del("/:num(\\d+)/tags/:tag", PerCheck(Per.ModifyTag), DelTagHandler(controller)); + ret.del("/:num(\\d+)", AdminOnly, DeleteContentHandler(controller)); + ret.all("/:num(\\d+)/(.*)", PerCheck(Per.QueryContent), ContentHandler(controller)); + ret.use("/:num(\\d+)", PerCheck(Per.QueryContent), new AllContentRouter().routes()); + return ret; +}; + +export default getContentRouter; diff --git a/packages/server/src/route/context.ts b/packages/server/src/route/context.ts new file mode 100644 index 0000000..afd4874 --- /dev/null +++ b/packages/server/src/route/context.ts @@ -0,0 +1,8 @@ +export type ContentLocation = { + path: string; + type: string; + additional: object | undefined; +}; +export interface ContentContext { + location: ContentLocation; +} diff --git a/packages/server/src/route/error_handler.ts b/packages/server/src/route/error_handler.ts new file mode 100644 index 0000000..a329645 --- /dev/null +++ b/packages/server/src/route/error_handler.ts @@ -0,0 +1,49 @@ +import type { Context, Next } from "koa"; + +export interface ErrorFormat { + code: number; + message: string; + detail?: string; +} + +class ClientRequestError implements Error { + name: string; + message: string; + stack?: string | undefined; + code: number; + + constructor(code: number, message: string) { + this.name = "client request error"; + this.message = message; + this.code = code; + } +} + +const code_to_message_table: { [key: number]: string | undefined } = { + 400: "BadRequest", + 404: "NotFound", +}; + +export const error_handler = async (ctx: Context, next: Next) => { + try { + await next(); + } catch (err) { + if (err instanceof ClientRequestError) { + const body: ErrorFormat = { + code: err.code, + message: code_to_message_table[err.code] ?? "", + detail: err.message, + }; + ctx.status = err.code; + ctx.body = body; + } else { + throw err; + } + } +}; + +export const sendError = (code: number, message?: string) => { + throw new ClientRequestError(code, message ?? ""); +}; + +export default error_handler; diff --git a/packages/server/src/route/tags.ts b/packages/server/src/route/tags.ts new file mode 100644 index 0000000..da0f535 --- /dev/null +++ b/packages/server/src/route/tags.ts @@ -0,0 +1,29 @@ +import { type Context, Next } from "koa"; +import Router, { type RouterContext } from "koa-router"; +import type { TagAccessor } from "../model/tag"; +import { createPermissionCheckMiddleware as PerCheck, Permission } from "../permission/permission"; +import { sendError } from "./error_handler"; + +export function getTagRounter(tagController: TagAccessor) { + const router = new Router(); + router.get("/", PerCheck(Permission.QueryContent), async (ctx: Context) => { + if (ctx.query.withCount) { + const c = await tagController.getAllTagCount(); + ctx.body = c; + } else { + const c = await tagController.getAllTagList(); + ctx.body = c; + } + ctx.type = "json"; + }); + router.get("/:tag_name", PerCheck(Permission.QueryContent), async (ctx: RouterContext) => { + const tag_name = ctx.params.tag_name; + const c = await tagController.getTagByName(tag_name); + if (!c) { + sendError(404, "tags not found"); + } + ctx.body = c; + ctx.type = "json"; + }); + return router; +} diff --git a/packages/server/src/route/util.ts b/packages/server/src/route/util.ts new file mode 100644 index 0000000..bdc39f0 --- /dev/null +++ b/packages/server/src/route/util.ts @@ -0,0 +1,37 @@ +import type { Context } from "koa"; + +export function ParseQueryNumber(s: string[] | string | undefined): number | undefined { + if (s === undefined) return undefined; + if (typeof s === "object") return undefined; + return Number.parseInt(s); +} +export function ParseQueryArray(s: string[] | string | undefined) { + const input = s ?? []; + const r = Array.isArray(input) ? input : [input]; + return r.map((x) => decodeURIComponent(x)); +} +export function ParseQueryArgString(s: string[] | string | undefined) { + if (typeof s === "object") return undefined; + return s === undefined ? s : decodeURIComponent(s); +} +export function ParseQueryBoolean(s: string[] | string | undefined): [boolean, boolean | undefined] { + let value: boolean | undefined; + + if (s === "true") { + value = true; + } else if (s === "false") { + value = false; + } else if (s === undefined) { + value = undefined; + } else return [false, undefined]; + return [true, value]; +} + +export function since_last_modified(ctx: Context, last_modified: Date): boolean { + const con = ctx.get("If-Modified-Since"); + if (con === "") return false; + const mdate = new Date(con); + if (last_modified > mdate) return false; + ctx.status = 304; + return true; +} diff --git a/packages/server/src/route/video.ts b/packages/server/src/route/video.ts new file mode 100644 index 0000000..198adc2 --- /dev/null +++ b/packages/server/src/route/video.ts @@ -0,0 +1,67 @@ +import { createReadStream, promises } from "node:fs"; +import type { Context } from "koa"; +import Router from "koa-router"; +import type { ContentContext } from "./context"; + +export async function renderVideo(ctx: Context, path: string) { + const ext = path.trim().split(".").pop(); + if (ext === undefined) { + // ctx.status = 404; + console.error(`${path}:${ext}`); + return; + } + ctx.response.type = ext; + const range_text = ctx.request.get("range"); + const stat = await promises.stat(path); + let start = 0; + let end = 0; + ctx.set("Last-Modified", new Date(stat.mtime).toUTCString()); + ctx.set("Date", new Date().toUTCString()); + ctx.set("Accept-Ranges", "bytes"); + if (range_text === "") { + end = 1024 * 512; + end = Math.min(end, stat.size - 1); + if (start > end) { + ctx.status = 416; + return; + } + ctx.status = 200; + ctx.length = stat.size; + const stream = createReadStream(path); + ctx.body = stream; + } else { + const m = range_text.match(/^bytes=(\d+)-(\d*)/); + if (m === null) { + ctx.status = 416; + return; + } + start = Number.parseInt(m[1]); + end = m[2].length > 0 ? Number.parseInt(m[2]) : start + 1024 * 1024; + end = Math.min(end, stat.size - 1); + if (start > end) { + ctx.status = 416; + return; + } + ctx.status = 206; + ctx.length = end - start + 1; + ctx.response.set("Content-Range", `bytes ${start}-${end}/${stat.size}`); + ctx.body = createReadStream(path, { + start: start, + end: end, + }); // inclusive range. + } +} + +export class VideoRouter extends Router { + constructor() { + super(); + this.get("/", async (ctx, next) => { + await renderVideo(ctx, ctx.state.location.path); + }); + this.get("/thumbnail", async (ctx, next) => { + await renderVideo(ctx, ctx.state.location.path); + }); + } +} + +export default VideoRouter; diff --git a/packages/server/src/search/indexer.ts b/packages/server/src/search/indexer.ts new file mode 100644 index 0000000..3879b70 --- /dev/null +++ b/packages/server/src/search/indexer.ts @@ -0,0 +1,12 @@ +export interface PaginationOption { + cursor: number; + limit: number; +} + +export interface IIndexer { + indexDoc(word: string, doc_id: number): boolean; + indexDoc(word: string[], doc_id: number): boolean; + + getDoc(word: string, option?: PaginationOption): number[]; + getDoc(word: string[], option?: PaginationOption): number[]; +} diff --git a/packages/server/src/search/tokenizer.ts b/packages/server/src/search/tokenizer.ts new file mode 100644 index 0000000..87d180e --- /dev/null +++ b/packages/server/src/search/tokenizer.ts @@ -0,0 +1,9 @@ +export interface ITokenizer { + tokenize(s: string): string[]; +} + +export class DefaultTokenizer implements ITokenizer { + tokenize(s: string): string[] { + return s.split(" "); + } +} diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts new file mode 100644 index 0000000..96b4a7a --- /dev/null +++ b/packages/server/src/server.ts @@ -0,0 +1,256 @@ +import Koa from "koa"; +import Router from "koa-router"; + +import { connectDB } from "./database"; +import { createDiffRouter, DiffManager } from "./diff/mod"; +import { get_setting, SettingConfig } from "./SettingConfig"; + +import { createReadStream, readFileSync } from "node:fs"; +import bodyparser from "koa-bodyparser"; +import { createSqliteDocumentAccessor, createSqliteTagController, createSqliteUserController } from "./db/mod"; +import { createLoginRouter, createUserMiddleWare, getAdmin, isAdminFirst } from "./login"; +import getContentRouter from "./route/contents"; +import { error_handler } from "./route/error_handler"; + +import { createInterface as createReadlineInterface } from "node:readline"; +import { createComicWatcher } from "./diff/watcher/comic_watcher"; +import type { DocumentAccessor, TagAccessor, UserAccessor } from "./model/mod"; +import { getTagRounter } from "./route/tags"; + +import { config } from "dotenv"; +import { extname, join } from "node:path"; +config(); + +class ServerApplication { + readonly userController: UserAccessor; + readonly documentController: DocumentAccessor; + readonly tagController: TagAccessor; + readonly diffManger: DiffManager; + readonly app: Koa; + private index_html: string; + private constructor(controller: { + userController: UserAccessor; + documentController: DocumentAccessor; + tagController: TagAccessor; + }) { + this.userController = controller.userController; + this.documentController = controller.documentController; + this.tagController = controller.tagController; + + this.diffManger = new DiffManager(this.documentController); + this.app = new Koa(); + this.index_html = readFileSync("dist/index.html", "utf-8"); + } + private async setup() { + const setting = get_setting(); + const app = this.app; + + if (setting.cli) { + const userAdmin = await getAdmin(this.userController); + if (await isAdminFirst(userAdmin)) { + const rl = createReadlineInterface({ + input: process.stdin, + output: process.stdout, + }); + const pw = await new Promise((res: (data: string) => void, err) => { + rl.question("put admin password :", (data) => { + res(data); + }); + }); + rl.close(); + userAdmin.reset_password(pw); + } + } + app.use(bodyparser()); + app.use(error_handler); + app.use(createUserMiddleWare(this.userController)); + + const diff_router = createDiffRouter(this.diffManger); + this.diffManger.register("comic", createComicWatcher()); + + console.log("setup router"); + + const router = new Router(); + router.use("/api/(.*)", async (ctx, next) => { + // For CORS + ctx.res.setHeader("access-control-allow-origin", "*"); + await next(); + }); + + router.use("/api/diff", diff_router.routes()); + router.use("/api/diff", diff_router.allowedMethods()); + + const content_router = getContentRouter(this.documentController); + router.use("/api/doc", content_router.routes()); + router.use("/api/doc", content_router.allowedMethods()); + + const tags_router = getTagRounter(this.tagController); + router.use("/api/tags", tags_router.allowedMethods()); + router.use("/api/tags", tags_router.routes()); + + this.serve_with_meta_index(router); + this.serve_index(router); + this.serve_static_file(router); + + const login_router = createLoginRouter(this.userController); + router.use("/api/user", login_router.routes()); + router.use("/api/user", login_router.allowedMethods()); + + if (setting.mode === "development") { + let mm_count = 0; + app.use(async (ctx, next) => { + console.log(`=== Request No ${mm_count++} \t===`); + const ip = ctx.get("X-Real-IP").length > 0 ? ctx.get("X-Real-IP") : ctx.ip; + const fromClient = ctx.state.user.username === "" ? ip : ctx.state.user.username; + console.log(`${mm_count} ${fromClient} : ${ctx.method} ${ctx.url}`); + const start = Date.now(); + await next(); + const end = Date.now(); + console.log(`${mm_count} ${fromClient} : ${ctx.method} ${ctx.url} ${ctx.status} ${end - start}ms`); + }); + } + app.use(router.routes()); + app.use(router.allowedMethods()); + console.log("setup done"); + } + private serve_index(router: Router) { + const serveindex = (url: string) => { + router.get(url, (ctx) => { + ctx.type = "html"; + ctx.body = this.index_html; + const setting = get_setting(); + ctx.set("x-content-type-options", "no-sniff"); + if (setting.mode === "development") { + ctx.set("cache-control", "no-cache"); + } else { + ctx.set("cache-control", "public, max-age=3600"); + } + }); + }; + serveindex("/"); + serveindex("/doc/:rest(.*)"); + serveindex("/search"); + serveindex("/login"); + serveindex("/profile"); + serveindex("/difference"); + serveindex("/setting"); + serveindex("/tags"); + } + private serve_with_meta_index(router: Router) { + const DocMiddleware = async (ctx: Koa.ParameterizedContext) => { + const docId = Number.parseInt(ctx.params.id); + const doc = await this.documentController.findById(docId, true); + // biome-ignore lint/suspicious/noImplicitAnyLet: + let meta; + if (doc === undefined) { + ctx.status = 404; + meta = NotFoundContent(); + } else { + ctx.status = 200; + meta = createOgTagContent( + doc.title, + doc.tags.join(", "), + `https://aeolian.prelude.duckdns.org/api/doc/${docId}/comic/thumbnail`, + ); + } + const html = makeMetaTagInjectedHTML(this.index_html, meta); + serveHTML(ctx, html); + }; + router.get("/doc/:id(\\d+)", DocMiddleware); + + function NotFoundContent() { + return createOgTagContent("Not Found Doc", "Not Found", ""); + } + function makeMetaTagInjectedHTML(html: string, tagContent: string) { + return html.replace("", tagContent); + } + function serveHTML(ctx: Koa.Context, file: string) { + ctx.type = "html"; + ctx.body = file; + const setting = get_setting(); + ctx.set("x-content-type-options", "no-sniff"); + if (setting.mode === "development") { + ctx.set("cache-control", "no-cache"); + } else { + ctx.set("cache-control", "public, max-age=3600"); + } + } + + function createMetaTagContent(key: string, value: string) { + return ``; + } + function createOgTagContent(title: string, description: string, image: string) { + return [ + createMetaTagContent("og:title", title), + createMetaTagContent("og:type", "website"), + createMetaTagContent("og:description", description), + createMetaTagContent("og:image", image), + // createMetaTagContent("og:image:width","480"), + // createMetaTagContent("og:image","480"), + // createMetaTagContent("og:image:type","image/png"), + createMetaTagContent("twitter:card", "summary_large_image"), + createMetaTagContent("twitter:title", title), + createMetaTagContent("twitter:description", description), + createMetaTagContent("twitter:image", image), + ].join("\n"); + } + } + private serve_static_file(router: Router) { + router.get("/assets/(.*)", async (ctx, next) => { + const setting = get_setting(); + const ext = extname(ctx.path); + ctx.type = ext; + ctx.body = createReadStream(join("dist",`.${ctx.path}`)); + ctx.set("x-content-type-options", "no-sniff"); + if (setting.mode === "development") { + ctx.set("cache-control", "no-cache"); + } else { + ctx.set("cache-control", "public, max-age=3600"); + } + }); + // const static_file_server = (path: string, type: string) => { + // router.get(`/${path}`, async (ctx, next) => { + // const setting = get_setting(); + // ctx.type = type; + // ctx.body = createReadStream(path); + // ctx.set("x-content-type-options", "no-sniff"); + // if (setting.mode === "development") { + // ctx.set("cache-control", "no-cache"); + // } else { + // ctx.set("cache-control", "public, max-age=3600"); + // } + // }); + // }; + // const setting = get_setting(); + // static_file_server("dist/bundle.css", "css"); + // static_file_server("dist/bundle.js", "js"); + // if (setting.mode === "development") { + // static_file_server("dist/bundle.js.map", "text"); + // static_file_server("dist/bundle.css.map", "text"); + // } + } + start_server() { + const setting = get_setting(); + // todo : support https + console.log(`listen on http://${setting.localmode ? "localhost" : "0.0.0.0"}:${setting.port}`); + return this.app.listen(setting.port, setting.localmode ? "127.0.0.1" : "0.0.0.0"); + } + static async createServer() { + const setting = get_setting(); + const db = await connectDB(); + + const app = new ServerApplication({ + userController: createSqliteUserController(db), + documentController: createSqliteDocumentAccessor(db), + tagController: createSqliteTagController(db), + }); + await app.setup(); + return app; + } +} + +export async function create_server() { + return await ServerApplication.createServer(); +} + +export default { create_server }; diff --git a/packages/server/src/types/json.ts b/packages/server/src/types/json.ts new file mode 100644 index 0000000..b774877 --- /dev/null +++ b/packages/server/src/types/json.ts @@ -0,0 +1,4 @@ +export type JSONPrimitive = null | boolean | number | string; +export interface JSONMap extends Record {} +export interface JSONArray extends Array {} +export type JSONType = JSONMap | JSONPrimitive | JSONArray; diff --git a/packages/server/src/util/configRW.ts b/packages/server/src/util/configRW.ts new file mode 100644 index 0000000..dd873ad --- /dev/null +++ b/packages/server/src/util/configRW.ts @@ -0,0 +1,46 @@ +import { existsSync, promises as fs, readFileSync, writeFileSync } from "node:fs"; + +export class ConfigManager { + path: string; + default_config: T; + config: T | null; + schema: object; + constructor(path: string, default_config: T, schema: object) { + this.path = path; + this.default_config = default_config; + this.config = null; + this.schema = schema; + } + get_config_file(): T { + if (this.config !== null) return this.config; + this.config = { ...this.read_config_file() }; + return this.config; + } + private emptyToDefault(target: T) { + let occur = false; + for (const key in this.default_config) { + if (key === undefined || key in target) { + continue; + } + target[key] = this.default_config[key]; + occur = true; + } + return occur; + } + read_config_file(): T { + if (!existsSync(this.path)) { + writeFileSync(this.path, JSON.stringify(this.default_config)); + return this.default_config; + } + const ret = JSON.parse(readFileSync(this.path, { encoding: "utf8" })); + if (this.emptyToDefault(ret)) { + writeFileSync(this.path, JSON.stringify(ret)); + } + return ret; + } + async write_config_file(new_config: T) { + this.config = new_config; + await fs.writeFile(`${this.path}.temp`, JSON.stringify(new_config)); + await fs.rename(`${this.path}.temp`, this.path); + } +} diff --git a/packages/server/src/util/type_check.ts b/packages/server/src/util/type_check.ts new file mode 100644 index 0000000..6ee6d50 --- /dev/null +++ b/packages/server/src/util/type_check.ts @@ -0,0 +1,17 @@ +export function check_type(obj: unknown, check_proto: Record): obj is T { + if (typeof obj !== "object" || obj === null) return false; + for (const it in check_proto) { + let defined = check_proto[it]; + if (defined === undefined) return false; + defined = defined.trim(); + if (defined.endsWith("[]")) { + if (!Array.isArray((obj as Record)[it])) { + return false; + } + // biome-ignore lint/suspicious/useValidTypeof: + } else if (defined !== typeof (obj as Record)[it]) { + return false; + } + } + return true; +} diff --git a/packages/server/src/util/zipwrap.ts b/packages/server/src/util/zipwrap.ts new file mode 100644 index 0000000..f32ab4f --- /dev/null +++ b/packages/server/src/util/zipwrap.ts @@ -0,0 +1,54 @@ +import { type FileHandle, open } from "node:fs/promises"; +import { orderBy } from "natural-orderby"; +import { ZipReader, Reader, type Entry } from "@zip.js/zip.js"; + +class FileReader extends Reader { + private fd: FileHandle; + constructor(fd: FileHandle) { + super(fd); + this.fd = fd; + } + + async init(): Promise { + this.size = (await this.fd.stat()).size; + } + close(): void { + this.fd.close(); + } + + async readUint8Array(index: number, length: number): Promise { + const buffer = new Uint8Array(length); + const buf = await this.fd.read(buffer, 0, length, index); + if (buf.bytesRead !== length) { + console.error(`read error: ${buf.bytesRead} !== ${length}`); + throw new Error("read error"); + } + return buffer; + } +} + +export async function readZip(path: string): Promise<{ + reader: ZipReader + handle: FileHandle +}> { + const fd = await open(path, "r"); + const reader = new ZipReader(new FileReader(fd), { + useCompressionStream: true, + preventClose: false, + }); + return { reader, handle: fd }; +} +export async function entriesByNaturalOrder(zip: ZipReader) { + const entries = await zip.getEntries(); + const ret = orderBy(entries, (v) => v.filename); + return ret; +} + +export async function createReadableStreamFromZip(_zip: ZipReader, entry: Entry): Promise { + if (entry.getData === undefined) { + throw new Error("entry.getData is undefined"); + } + const stream = new TransformStream(); + entry.getData(stream.writable); + return stream.readable; +} diff --git a/tsconfig.json b/packages/server/tsconfig.json similarity index 98% rename from tsconfig.json rename to packages/server/tsconfig.json index 4b13120..e824e11 100644 --- a/tsconfig.json +++ b/packages/server/tsconfig.json @@ -6,7 +6,7 @@ // "incremental": true, /* Enable incremental compilation */ "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - "lib": ["DOM", "ES6"], /* Specify library files to be included in the compilation. */ + "lib": ["DOM", "ESNext"], // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a70546..af296d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,382 +1,2255 @@ -lockfileVersion: 5.4 +lockfileVersion: '6.0' -specifiers: - '@louislam/sqlite3': ^6.0.1 - '@types/jsonwebtoken': ^8.5.8 - '@types/koa': ^2.13.4 - '@types/koa-bodyparser': ^4.3.7 - '@types/koa-compose': ^3.2.5 - '@types/koa-router': ^7.4.4 - '@types/node': ^14.18.21 - '@types/tiny-async-pool': ^1.0.1 - chokidar: ^3.5.3 - dprint: ^0.36.1 - electron: ^11.5.0 - electron-builder: ^22.14.13 - jsonschema: ^1.4.1 - jsonwebtoken: ^8.5.1 - knex: ^0.95.15 - koa: ^2.13.4 - koa-bodyparser: ^4.3.0 - koa-compose: ^4.1.0 - koa-router: ^10.1.1 - natural-orderby: ^2.0.3 - node-stream-zip: ^1.15.0 - sqlite3: ^5.0.8 - tiny-async-pool: ^1.3.0 - ts-json-schema-generator: ^0.82.0 - ts-node: ^9.1.1 - typescript: ^4.7.4 +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false -dependencies: - '@louislam/sqlite3': 6.0.1 - '@types/koa-compose': 3.2.5 - chokidar: 3.5.3 - dprint: 0.36.1 - jsonschema: 1.4.1 - jsonwebtoken: 8.5.1 - knex: 0.95.15_sqlite3@5.0.8 - koa: 2.13.4 - koa-bodyparser: 4.3.0 - koa-compose: 4.1.0 - koa-router: 10.1.1 - natural-orderby: 2.0.3 - node-stream-zip: 1.15.0 - sqlite3: 5.0.8 - tiny-async-pool: 1.3.0 +importers: -devDependencies: - '@types/jsonwebtoken': 8.5.8 - '@types/koa': 2.13.4 - '@types/koa-bodyparser': 4.3.7 - '@types/koa-router': 7.4.4 - '@types/node': 14.18.21 - '@types/tiny-async-pool': 1.0.1 - electron: 11.5.0 - electron-builder: 22.14.13 - ts-json-schema-generator: 0.82.0 - ts-node: 9.1.1_typescript@4.7.4 - typescript: 4.7.4 + .: + devDependencies: + '@biomejs/biome': + specifier: 1.6.3 + version: 1.6.3 + + packages/client: + dependencies: + '@radix-ui/react-icons': + specifier: ^1.3.0 + version: 1.3.0(react@18.2.0) + '@radix-ui/react-label': + specifier: ^2.0.2 + version: 2.0.2(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-popover': + specifier: ^1.0.7 + version: 1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-radio-group': + specifier: ^1.1.3 + version: 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-separator': + specifier: ^1.0.3 + version: 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': + specifier: ^1.0.2 + version: 1.0.2(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-tooltip': + specifier: ^1.0.7 + version: 1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-virtual': + specifier: ^3.2.1 + version: 3.2.1(react-dom@18.2.0)(react@18.2.0) + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 + clsx: + specifier: ^2.1.0 + version: 2.1.0 + dbtype: + specifier: workspace:* + version: link:../dbtype + jotai: + specifier: ^2.7.2 + version: 2.7.2(@types/react@18.2.71)(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-dom: + specifier: ^18.2.0 + version: 18.2.0(react@18.2.0) + react-resizable-panels: + specifier: ^2.0.16 + version: 2.0.16(react-dom@18.2.0)(react@18.2.0) + swr: + specifier: ^2.2.5 + version: 2.2.5(react@18.2.0) + tailwind-merge: + specifier: ^2.2.2 + version: 2.2.2 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.3) + usehooks-ts: + specifier: ^3.1.0 + version: 3.1.0(react@18.2.0) + wouter: + specifier: ^3.1.0 + version: 3.1.0(react@18.2.0) + devDependencies: + '@types/node': + specifier: '>=20.0.0' + version: 20.12.3 + '@types/react': + specifier: ^18.2.66 + version: 18.2.71 + '@types/react-dom': + specifier: ^18.2.22 + version: 18.2.22 + '@typescript-eslint/eslint-plugin': + specifier: ^7.2.0 + version: 7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/parser': + specifier: ^7.2.0 + version: 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@vitejs/plugin-react-swc': + specifier: ^3.5.0 + version: 3.6.0(vite@5.2.6) + autoprefixer: + specifier: ^10.4.19 + version: 10.4.19(postcss@8.4.38) + eslint: + specifier: ^8.57.0 + version: 8.57.0 + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.0(eslint@8.57.0) + eslint-plugin-react-refresh: + specifier: ^0.4.6 + version: 0.4.6(eslint@8.57.0) + postcss: + specifier: ^8.4.38 + version: 8.4.38 + shadcn-ui: + specifier: ^0.8.0 + version: 0.8.0(typescript@5.4.3) + tailwindcss: + specifier: ^3.4.3 + version: 3.4.3 + typescript: + specifier: ^5.2.2 + version: 5.4.3 + vite: + specifier: ^5.2.0 + version: 5.2.6(@types/node@20.12.3) + + packages/dbtype: + devDependencies: + '@types/better-sqlite3': + specifier: ^7.6.9 + version: 7.6.9 + better-sqlite3: + specifier: ^9.4.3 + version: 9.4.3 + kysely: + specifier: ^0.27.3 + version: 0.27.3 + kysely-codegen: + specifier: ^0.14.1 + version: 0.14.1(better-sqlite3@9.4.3)(kysely@0.27.3) + + packages/server: + dependencies: + '@zip.js/zip.js': + specifier: ^2.7.40 + version: 2.7.40 + better-sqlite3: + specifier: ^9.4.3 + version: 9.4.3 + chokidar: + specifier: ^3.6.0 + version: 3.6.0 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + jsonwebtoken: + specifier: ^8.5.1 + version: 8.5.1 + koa: + specifier: ^2.15.2 + version: 2.15.2 + koa-bodyparser: + specifier: ^4.4.1 + version: 4.4.1 + koa-compose: + specifier: ^4.1.0 + version: 4.1.0 + koa-router: + specifier: ^12.0.1 + version: 12.0.1 + kysely: + specifier: ^0.27.3 + version: 0.27.3 + natural-orderby: + specifier: ^2.0.3 + version: 2.0.3 + tiny-async-pool: + specifier: ^1.3.0 + version: 1.3.0 + devDependencies: + '@swc-node/register': + specifier: ^1.9.0 + version: 1.9.0(@swc/core@1.4.11)(@swc/types@0.1.6)(typescript@5.4.3) + '@swc/cli': + specifier: ^0.3.10 + version: 0.3.10(@swc/core@1.4.11)(chokidar@3.6.0) + '@swc/core': + specifier: ^1.4.11 + version: 1.4.11 + '@types/better-sqlite3': + specifier: ^7.6.9 + version: 7.6.9 + '@types/jsonwebtoken': + specifier: ^8.5.9 + version: 8.5.9 + '@types/koa': + specifier: ^2.15.0 + version: 2.15.0 + '@types/koa-bodyparser': + specifier: ^4.3.12 + version: 4.3.12 + '@types/koa-compose': + specifier: ^3.2.8 + version: 3.2.8 + '@types/koa-router': + specifier: ^7.4.8 + version: 7.4.8 + '@types/node': + specifier: '>=20.0.0' + version: 20.12.3 + '@types/tiny-async-pool': + specifier: ^1.0.5 + version: 1.0.5 + dbtype: + specifier: workspace:^ + version: link:../dbtype + nodemon: + specifier: ^3.1.0 + version: 3.1.0 packages: - /7zip-bin/5.1.1: - resolution: {integrity: sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==} + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} dev: true - /@develar/schema-utils/2.6.5: - resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} - engines: {node: '>= 8.9.0'} + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + /@ampproject/remapping@2.3.0: + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} dependencies: - ajv: 6.12.6 - ajv-keywords: 3.5.2_ajv@6.12.6 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 dev: true - /@electron/get/1.14.1: - resolution: {integrity: sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw==} - engines: {node: '>=8.6'} + /@antfu/ni@0.21.12: + resolution: {integrity: sha512-2aDL3WUv8hMJb2L3r/PIQWsTLyq7RQr3v9xD16fiz6O8ys1xEyLhhTOv8gxtZvJiTzjTF5pHoArvRdesGL1DMQ==} + hasBin: true + dev: true + + /@babel/code-frame@7.24.2: + resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} + engines: {node: '>=6.9.0'} dependencies: - debug: 4.3.4 - env-paths: 2.2.1 - fs-extra: 8.1.0 - got: 9.6.0 - progress: 2.0.3 - semver: 6.3.0 - sumchecker: 3.0.1 - optionalDependencies: - global-agent: 3.0.0 - global-tunnel-ng: 2.7.1 + '@babel/highlight': 7.24.2 + picocolors: 1.0.0 + dev: true + + /@babel/compat-data@7.24.1: + resolution: {integrity: sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.24.3: + resolution: {integrity: sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.1 + '@babel/helper-compilation-targets': 7.23.6 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) + '@babel/helpers': 7.24.1 + '@babel/parser': 7.24.1 + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 + convert-source-map: 2.0.0 + debug: 4.3.4(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@electron/universal/1.0.5: - resolution: {integrity: sha512-zX9O6+jr2NMyAdSkwEUlyltiI4/EBLu2Ls/VD3pUQdi3cAYeYfdQnT2AJJ38HE4QxLccbU13LSpccw1IWlkyag==} - engines: {node: '>=8.6'} + /@babel/generator@7.24.1: + resolution: {integrity: sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==} + engines: {node: '>=6.9.0'} dependencies: - '@malept/cross-spawn-promise': 1.1.1 - asar: 3.1.0 - debug: 4.3.4 - dir-compare: 2.4.0 - fs-extra: 9.1.0 + '@babel/types': 7.24.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + dev: true + + /@babel/helper-annotate-as-pure@7.22.5: + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-compilation-targets@7.23.6: + resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.24.1 + '@babel/helper-validator-option': 7.23.5 + browserslist: 4.23.0 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + + /@babel/helper-create-class-features-plugin@7.24.1(@babel/core@7.24.3): + resolution: {integrity: sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.3) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + semver: 6.3.1 + dev: true + + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.24.0 + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-hoist-variables@7.22.5: + resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-module-imports@7.24.3: + resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.3): + resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.24.3 + '@babel/helper-simple-access': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-validator-identifier': 7.22.20 + dev: true + + /@babel/helper-optimise-call-expression@7.22.5: + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-plugin-utils@7.24.0: + resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-replace-supers@7.24.1(@babel/core@7.24.3): + resolution: {integrity: sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-optimise-call-expression': 7.22.5 + dev: true + + /@babel/helper-simple-access@7.22.5: + resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-skip-transparent-expression-wrappers@7.22.5: + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-split-export-declaration@7.22.6: + resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.24.0 + dev: true + + /@babel/helper-string-parser@7.24.1: + resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.23.5: + resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.24.1: + resolution: {integrity: sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.24.0 + '@babel/traverse': 7.24.1 + '@babel/types': 7.24.0 transitivePeerDependencies: - supports-color dev: true - /@gar/promisify/1.1.3: - resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - dev: false - optional: true - - /@louislam/sqlite3/6.0.1: - resolution: {integrity: sha512-QGLj5bjQ+O4YSPj/qxtEAArbIqW9wNzBUamlIcRbvFjFiNokItwdubqL2Gl5iX0q1mUn3Z6NoFO1rrAZ/qqlsA==} - requiresBuild: true - peerDependenciesMeta: - node-gyp: - optional: true + /@babel/highlight@7.24.2: + resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==} + engines: {node: '>=6.9.0'} dependencies: - '@mapbox/node-pre-gyp': 1.0.9 - node-addon-api: 3.2.1 - optionalDependencies: - node-gyp: 8.4.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /@malept/cross-spawn-promise/1.1.1: - resolution: {integrity: sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==} - engines: {node: '>= 10'} - dependencies: - cross-spawn: 7.0.3 + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.0 dev: true - /@malept/flatpak-bundler/0.4.0: - resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==} - engines: {node: '>= 10.0.0'} - dependencies: - debug: 4.3.4 - fs-extra: 9.1.0 - lodash: 4.17.21 - tmp-promise: 3.0.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@mapbox/node-pre-gyp/1.0.9: - resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==} + /@babel/parser@7.24.1: + resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} + engines: {node: '>=6.0.0'} hasBin: true dependencies: - detect-libc: 2.0.1 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.6.7 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.3.7 - tar: 6.1.11 - transitivePeerDependencies: - - encoding - - supports-color + '@babel/types': 7.24.0 + dev: true + + /@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.3): + resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-plugin-utils': 7.24.0 + dev: true + + /@babel/plugin-transform-typescript@7.24.1(@babel/core@7.24.3): + resolution: {integrity: sha512-liYSESjX2fZ7JyBFkYG78nfvHlMKE6IpNdTVnxmlYUR+j5ZLsitFbaAE+eJSK2zPPkNWNw4mXL51rQ8WrvdK0w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.3 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.24.1(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.0 + '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.3) + dev: true + + /@babel/runtime@7.24.1: + resolution: {integrity: sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 dev: false - /@npmcli/fs/1.1.1: - resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} + /@babel/template@7.24.0: + resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} + engines: {node: '>=6.9.0'} dependencies: - '@gar/promisify': 1.1.3 - semver: 7.3.7 - dev: false + '@babel/code-frame': 7.24.2 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 + dev: true + + /@babel/traverse@7.24.1: + resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.1 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-hoist-variables': 7.22.5 + '@babel/helper-split-export-declaration': 7.22.6 + '@babel/parser': 7.24.1 + '@babel/types': 7.24.0 + debug: 4.3.4(supports-color@5.5.0) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.24.0: + resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.24.1 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: true + + /@biomejs/biome@1.6.3: + resolution: {integrity: sha512-Xnp/TIpIcTnRA4LwerJuoGYQJEqwXtn5AL0U0OPXll/QGbAKmcUAfizU880xTwZRD4f53iceqODLDaD3wxYlIw==} + engines: {node: '>=14.*'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@biomejs/cli-darwin-arm64': 1.6.3 + '@biomejs/cli-darwin-x64': 1.6.3 + '@biomejs/cli-linux-arm64': 1.6.3 + '@biomejs/cli-linux-arm64-musl': 1.6.3 + '@biomejs/cli-linux-x64': 1.6.3 + '@biomejs/cli-linux-x64-musl': 1.6.3 + '@biomejs/cli-win32-arm64': 1.6.3 + '@biomejs/cli-win32-x64': 1.6.3 + dev: true + + /@biomejs/cli-darwin-arm64@1.6.3: + resolution: {integrity: sha512-0E8PGu3/8HSkBJdtjno+niJE1ANS/12D7sPK65vw5lTBYmmaYwJdfclDp6XO0IAX7uVd3/YtXlsEua0SVrNt3Q==} + engines: {node: '>=14.*'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true optional: true - /@npmcli/move-file/1.1.2: - resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} + /@biomejs/cli-darwin-x64@1.6.3: + resolution: {integrity: sha512-UWu0We/aIRtWXgJKe6ygWt2xR0yXs64BwWqtZbfxBojRn3jgW8UdFAkV5yiUOX3TQlsV6BZH1EQaUAVsccUeeA==} + engines: {node: '>=14.*'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@biomejs/cli-linux-arm64-musl@1.6.3: + resolution: {integrity: sha512-AntGCSfLN1nPcQj4VOk3X2JgnDw07DaPC8BuBmRcsRmn+7GPSWLllVN5awIKlRPZEbGJtSnLkTiDc5Bxw8OiuA==} + engines: {node: '>=14.*'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@biomejs/cli-linux-arm64@1.6.3: + resolution: {integrity: sha512-wFVkQw38kOssfnkbpSh6ums5TaElw3RAt5i/VZwHmgR2nQgE0fHXLO7HwIE9VBkOEdbiIFq+2PxvFIHuJF3z3Q==} + engines: {node: '>=14.*'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@biomejs/cli-linux-x64-musl@1.6.3: + resolution: {integrity: sha512-GelAvGsUwbxfFpKLG+7+dvDmbrfkGqn08sL8CMQrGnhjE1krAqHWiXQsjfmi0UMFdMsk7hbc4oSAP+1+mrXcHQ==} + engines: {node: '>=14.*'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@biomejs/cli-linux-x64@1.6.3: + resolution: {integrity: sha512-vyn8TQaTZg617hjqFitwGmb1St5XXvq6I3vmxU/QFalM74BryMSvYCrYWb2Yw/TkykdEwZTMGYp+SWHRb04fTg==} + engines: {node: '>=14.*'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@biomejs/cli-win32-arm64@1.6.3: + resolution: {integrity: sha512-Gx8N2Tixke6pAI1BniteCVZgUUmaFEDYosdWxoaCus15BZI/7RcBxhsRM0ZL/lC66StSQ8vHl8JBrrld1k570Q==} + engines: {node: '>=14.*'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@biomejs/cli-win32-x64@1.6.3: + resolution: {integrity: sha512-meungPJw64SqoR7LXY1wG7GC4+4wgpyThdFUMGXa6PCe0BLFOIOcZ9VMj9PstuczMPdgmt/BUMPsj25dK1VO8A==} + engines: {node: '>=14.*'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4(supports-color@5.5.0) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.57.0: + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@floating-ui/core@1.6.0: + resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} + dependencies: + '@floating-ui/utils': 0.2.1 + dev: false + + /@floating-ui/dom@1.6.3: + resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==} + dependencies: + '@floating-ui/core': 1.6.0 + '@floating-ui/utils': 0.2.1 + dev: false + + /@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 1.6.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@floating-ui/utils@0.2.1: + resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} + dev: false + + /@humanwhocodes/config-array@0.11.14: + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.2 + debug: 4.3.4(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.2: + resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + dev: true + + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + + /@jridgewell/gen-mapping@0.3.5: + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 + + /@jridgewell/resolve-uri@3.1.2: + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + /@jridgewell/set-array@1.2.1: + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + /@jridgewell/trace-mapping@0.3.25: + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + + /@mole-inc/bin-wrapper@8.0.1: + resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bin-check: 4.1.0 + bin-version-check: 5.1.0 + content-disposition: 0.5.4 + ext-name: 5.0.0 + file-type: 17.1.6 + filenamify: 5.1.1 + got: 11.8.6 + os-filter-obj: 2.0.0 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + optional: true + + /@radix-ui/primitive@1.0.1: + resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + dependencies: + '@babel/runtime': 7.24.1 + dev: false + + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-context@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-direction@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-icons@1.3.0(react@18.2.0): + resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x + dependencies: + react: 18.2.0 + dev: false + + /@radix-ui/react-id@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-label@2.0.2(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.5.5(@types/react@18.2.71)(react@18.2.0) + dev: false + + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-radio-group@1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-x+yELayyefNeKeTx4fjK6j99Fs6c4qKm3aY38G3swQVTN6xMpsrbigC0uHs2L//g8q4qR7qOcww8430jJmi2ag==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-slot@1.0.2(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/rect': 1.0.1 + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-size@1.0.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0) + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.1 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.71 + '@types/react-dom': 18.2.22 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /@radix-ui/rect@1.0.1: + resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} + dependencies: + '@babel/runtime': 7.24.1 + dev: false + + /@rollup/rollup-android-arm-eabi@4.13.0: + resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.13.0: + resolution: {integrity: sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.13.0: + resolution: {integrity: sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.13.0: + resolution: {integrity: sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.13.0: + resolution: {integrity: sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.13.0: + resolution: {integrity: sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.13.0: + resolution: {integrity: sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.13.0: + resolution: {integrity: sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.13.0: + resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.13.0: + resolution: {integrity: sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.13.0: + resolution: {integrity: sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.13.0: + resolution: {integrity: sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.13.0: + resolution: {integrity: sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@sindresorhus/is@4.6.0: + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + dev: true + + /@swc-node/core@1.13.0(@swc/core@1.4.11)(@swc/types@0.1.6): + resolution: {integrity: sha512-lFPD4nmy4ifAOVMChFjwlpXN5KQXvegqeyuzz1KQz42q1lf+cL3Qux1/GteGuZjh8HC+Rj1RdNrHpE/MCfJSTw==} + engines: {node: '>= 10'} + peerDependencies: + '@swc/core': '>= 1.3' + '@swc/types': '>= 0.1' + dependencies: + '@swc/core': 1.4.11 + '@swc/types': 0.1.6 + dev: true + + /@swc-node/register@1.9.0(@swc/core@1.4.11)(@swc/types@0.1.6)(typescript@5.4.3): + resolution: {integrity: sha512-i0iYInD4q5v3xQC6bKvs0QtfUxu197CU5qKALmpxEqTYs7sIhQ7KFLe3kP+eAR4gRkJTvAgjQgrokXLN2jZrOw==} + peerDependencies: + '@swc/core': '>= 1.3' + typescript: '>= 4.3' + dependencies: + '@swc-node/core': 1.13.0(@swc/core@1.4.11)(@swc/types@0.1.6) + '@swc-node/sourcemap-support': 0.5.0 + '@swc/core': 1.4.11 + colorette: 2.0.20 + debug: 4.3.4(supports-color@5.5.0) + pirates: 4.0.6 + tslib: 2.6.2 + typescript: 5.4.3 + transitivePeerDependencies: + - '@swc/types' + - supports-color + dev: true + + /@swc-node/sourcemap-support@0.5.0: + resolution: {integrity: sha512-fbhjL5G0YvFoWwNhWleuBUfotiX+USiA9oJqu9STFw+Hb0Cgnddn+HVS/K5fI45mn92e8V+cHD2jgFjk4w2T9Q==} + dependencies: + source-map-support: 0.5.21 + tslib: 2.6.2 + dev: true + + /@swc/cli@0.3.10(@swc/core@1.4.11)(chokidar@3.6.0): + resolution: {integrity: sha512-YWfYo9kXdbmIuGwIPth9geKgb0KssCMTdZa44zAN5KoqcuCP2rTW9s60heQDSRNpbtCmUr7BKF1VivsoHXrvrQ==} + engines: {node: '>= 16.14.0'} + hasBin: true + peerDependencies: + '@swc/core': ^1.2.66 + chokidar: ^3.5.1 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + '@mole-inc/bin-wrapper': 8.0.1 + '@swc/core': 1.4.11 + '@swc/counter': 0.1.3 + chokidar: 3.6.0 + commander: 8.3.0 + fast-glob: 3.3.2 + minimatch: 9.0.3 + piscina: 4.4.0 + semver: 7.6.0 + slash: 3.0.0 + source-map: 0.7.4 + dev: true + + /@swc/core-darwin-arm64@1.4.11: + resolution: {integrity: sha512-C1j1Qp/IHSelVWdEnT7f0iONWxQz6FAqzjCF2iaL+0vFg4V5f2nlgrueY8vj5pNNzSGhrAlxsMxEIp4dj1MXkg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.4.11: + resolution: {integrity: sha512-0TTy3Ni8ncgaMCchSQ7FK8ZXQLlamy0FXmGWbR58c+pVZWYZltYPTmheJUvVcR0H2+gPAymRKyfC0iLszDALjg==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.4.11: + resolution: {integrity: sha512-XJLB71uw0rog4DjYAPxFGAuGCBQpgJDlPZZK6MTmZOvI/1t0+DelJ24IjHIxk500YYM26Yv47xPabqFPD7I2zQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.4.11: + resolution: {integrity: sha512-vYQwzJvm/iu052d5Iw27UFALIN5xSrGkPZXxLNMHPySVko2QMNNBv35HLatkEQHbQ3X+VKSW9J9SkdtAvAVRAQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.4.11: + resolution: {integrity: sha512-eV+KduiRYUFjPsvbZuJ9aknQH9Tj0U2/G9oIZSzLx/18WsYi+upzHbgxmIIHJ2VJgfd7nN40RI/hMtxNsUzR/g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.4.11: + resolution: {integrity: sha512-WA1iGXZ2HpqM1OR9VCQZJ8sQ1KP2or9O4bO8vWZo6HZJIeoQSo7aa9waaCLRpkZvkng1ct/TF/l6ymqSNFXIzQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.4.11: + resolution: {integrity: sha512-UkVJToKf0owwQYRnGvjHAeYVDfeimCEcx0VQSbJoN7Iy0ckRZi7YPlmWJU31xtKvikE2bQWCOVe0qbSDqqcWXA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.4.11: + resolution: {integrity: sha512-35khwkyly7lF5NDSyvIrukBMzxPorgc5iTSDfVO/LvnmN5+fm4lTlrDr4tUfTdOhv3Emy7CsKlsNAeFRJ+Pm+w==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.4.11: + resolution: {integrity: sha512-Wx8/6f0ufgQF2pbVPsJ2dAmFLwIOW+xBE5fxnb7VnEbGkTgP1qMDWiiAtD9rtvDSuODG3i1AEmAak/2HAc6i6A==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.4.11: + resolution: {integrity: sha512-0xRFW6K9UZQH2NVC/0pVB0GJXS45lY24f+6XaPBF1YnMHd8A8GoHl7ugyM5yNUTe2AKhSgk5fJV00EJt/XBtdQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.4.11: + resolution: {integrity: sha512-WKEakMZxkVwRdgMN4AMJ9K5nysY8g8npgQPczmjBeNK5In7QEAZAJwnyccrWwJZU0XjVeHn2uj+XbOKdDW17rg==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.6 + optionalDependencies: + '@swc/core-darwin-arm64': 1.4.11 + '@swc/core-darwin-x64': 1.4.11 + '@swc/core-linux-arm-gnueabihf': 1.4.11 + '@swc/core-linux-arm64-gnu': 1.4.11 + '@swc/core-linux-arm64-musl': 1.4.11 + '@swc/core-linux-x64-gnu': 1.4.11 + '@swc/core-linux-x64-musl': 1.4.11 + '@swc/core-win32-arm64-msvc': 1.4.11 + '@swc/core-win32-ia32-msvc': 1.4.11 + '@swc/core-win32-x64-msvc': 1.4.11 + dev: true + + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + + /@swc/types@0.1.6: + resolution: {integrity: sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==} + dependencies: + '@swc/counter': 0.1.3 + dev: true + + /@szmarczak/http-timer@4.0.6: + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 + defer-to-connect: 2.0.1 + dev: true + + /@tanstack/react-virtual@3.2.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-i9Nt0ssIh2bSjomJZlr6Iq5usT/9+ewo2/fKHRNk6kjVKS8jrhXbnO8NEawarCuBx/efv0xpoUUKKGxa0cQb4Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/virtual-core': 3.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) dev: false - optional: true - /@sindresorhus/is/0.14.0: - resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} - engines: {node: '>=6'} - dev: true - - /@szmarczak/http-timer/1.1.2: - resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} - engines: {node: '>=6'} - dependencies: - defer-to-connect: 1.1.3 - dev: true - - /@tootallnate/once/1.1.2: - resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} - engines: {node: '>= 6'} + /@tanstack/virtual-core@3.2.1: + resolution: {integrity: sha512-nO0d4vRzsmpBQCJYyClNHPPoUMI4nXNfrm6IcCRL33ncWMoNVpURh9YebEHPw8KrtsP2VSJIHE4gf4XFGk1OGg==} dev: false - optional: true - /@tootallnate/once/2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} + /@tokenizer/token@0.3.0: + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} dev: true - /@types/accepts/1.3.5: - resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==} + /@ts-morph/common@0.19.0: + resolution: {integrity: sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==} dependencies: - '@types/node': 14.18.21 - - /@types/body-parser/1.19.2: - resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} - dependencies: - '@types/connect': 3.4.35 - '@types/node': 14.18.21 - - /@types/connect/3.4.35: - resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} - dependencies: - '@types/node': 14.18.21 - - /@types/content-disposition/0.5.5: - resolution: {integrity: sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==} - - /@types/cookies/0.7.7: - resolution: {integrity: sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==} - dependencies: - '@types/connect': 3.4.35 - '@types/express': 4.17.13 - '@types/keygrip': 1.0.2 - '@types/node': 14.18.21 - - /@types/debug/4.1.7: - resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} - dependencies: - '@types/ms': 0.7.31 + fast-glob: 3.3.2 + minimatch: 7.4.6 + mkdirp: 2.1.6 + path-browserify: 1.0.1 dev: true - /@types/express-serve-static-core/4.17.29: - resolution: {integrity: sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==} + /@types/accepts@1.3.7: + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} dependencies: - '@types/node': 14.18.21 - '@types/qs': 6.9.7 - '@types/range-parser': 1.2.4 + '@types/node': 20.12.3 + dev: true - /@types/express/4.17.13: - resolution: {integrity: sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==} + /@types/better-sqlite3@7.6.9: + resolution: {integrity: sha512-FvktcujPDj9XKMJQWFcl2vVl7OdRIqsSRX9b0acWwTmwLK9CF2eqo/FRcmMLNpugKoX/avA6pb7TorDLmpgTnQ==} dependencies: - '@types/body-parser': 1.19.2 - '@types/express-serve-static-core': 4.17.29 - '@types/qs': 6.9.7 - '@types/serve-static': 1.13.10 + '@types/node': 20.12.3 + dev: true - /@types/fs-extra/9.0.13: - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: - '@types/node': 14.18.21 + '@types/connect': 3.4.38 + '@types/node': 20.12.3 dev: true - /@types/glob/7.2.0: - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - requiresBuild: true + /@types/cacheable-request@6.0.3: + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} dependencies: - '@types/minimatch': 3.0.5 - '@types/node': 14.18.21 - dev: true - optional: true - - /@types/http-assert/1.5.3: - resolution: {integrity: sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==} - - /@types/http-errors/1.8.2: - resolution: {integrity: sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==} - - /@types/json-schema/7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + '@types/http-cache-semantics': 4.0.4 + '@types/keyv': 3.1.4 + '@types/node': 20.12.3 + '@types/responselike': 1.0.3 dev: true - /@types/jsonwebtoken/8.5.8: - resolution: {integrity: sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==} + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 14.18.21 + '@types/node': 20.12.3 dev: true - /@types/keygrip/1.0.2: - resolution: {integrity: sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==} + /@types/content-disposition@0.5.8: + resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} + dev: true - /@types/koa-bodyparser/4.3.7: - resolution: {integrity: sha512-21NhEp7LjZm4zbNV5alHHmrNY4J+S7B8lYTO6CzRL8ShTMnl20Gd14dRgVhAxraLaW5iZMofox+BycbuiDvj2Q==} + /@types/cookies@0.9.0: + resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} dependencies: - '@types/koa': 2.13.4 + '@types/connect': 3.4.38 + '@types/express': 4.17.21 + '@types/keygrip': 1.0.6 + '@types/node': 20.12.3 dev: true - /@types/koa-compose/3.2.5: - resolution: {integrity: sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==} + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true + + /@types/express-serve-static-core@4.17.43: + resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} dependencies: - '@types/koa': 2.13.4 + '@types/node': 20.12.3 + '@types/qs': 6.9.14 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true - /@types/koa-router/7.4.4: - resolution: {integrity: sha512-3dHlZ6CkhgcWeF6wafEUvyyqjWYfKmev3vy1PtOmr0mBc3wpXPU5E8fBBd4YQo5bRpHPfmwC5yDaX7s4jhIN6A==} + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} dependencies: - '@types/koa': 2.13.4 + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.17.43 + '@types/qs': 6.9.14 + '@types/serve-static': 1.15.5 dev: true - /@types/koa/2.13.4: - resolution: {integrity: sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw==} + /@types/http-assert@1.5.5: + resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} + dev: true + + /@types/http-cache-semantics@4.0.4: + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + + /@types/json-schema@7.0.15: + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + dev: true + + /@types/jsonwebtoken@8.5.9: + resolution: {integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==} dependencies: - '@types/accepts': 1.3.5 - '@types/content-disposition': 0.5.5 - '@types/cookies': 0.7.7 - '@types/http-assert': 1.5.3 - '@types/http-errors': 1.8.2 - '@types/keygrip': 1.0.2 - '@types/koa-compose': 3.2.5 - '@types/node': 14.18.21 - - /@types/mime/1.3.2: - resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} - - /@types/minimatch/3.0.5: - resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - dev: true - optional: true - - /@types/ms/0.7.31: - resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + '@types/node': 20.12.3 dev: true - /@types/node/12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + /@types/keygrip@1.0.6: + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} dev: true - /@types/node/14.18.21: - resolution: {integrity: sha512-x5W9s+8P4XteaxT/jKF0PSb7XEvo5VmqEWgsMlyeY4ZlLK8I6aH6g5TPPyDlLAep+GYf4kefb7HFyc7PAO3m+Q==} - - /@types/plist/3.0.2: - resolution: {integrity: sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==} - requiresBuild: true + /@types/keyv@3.1.4: + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 14.18.21 - xmlbuilder: 15.1.1 + '@types/node': 20.12.3 dev: true - optional: true - /@types/qs/6.9.7: - resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} - - /@types/range-parser/1.2.4: - resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} - - /@types/serve-static/1.13.10: - resolution: {integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==} + /@types/koa-bodyparser@4.3.12: + resolution: {integrity: sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==} dependencies: - '@types/mime': 1.3.2 - '@types/node': 14.18.21 - - /@types/tiny-async-pool/1.0.1: - resolution: {integrity: sha512-7msTTzXOAI9zS/u4DJLMAfAT/sz1laaZTFYVB83h0L78q4yB8RzRyxcqUlfkg12/hMC6G/cG//vbDwmdTpZ+gQ==} + '@types/koa': 2.15.0 dev: true - /@types/verror/1.10.5: - resolution: {integrity: sha512-9UjMCHK5GPgQRoNbqdLIAvAy0EInuiqbW0PBMtVP6B5B2HQJlvoJHM+KodPZMEjOa5VkSc+5LH7xy+cUzQdmHw==} - requiresBuild: true - dev: true - optional: true - - /@types/yargs-parser/21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - dev: true - - /@types/yargs/17.0.10: - resolution: {integrity: sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==} + /@types/koa-compose@3.2.8: + resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} dependencies: - '@types/yargs-parser': 21.0.0 + '@types/koa': 2.15.0 dev: true - /abbrev/1.1.1: + /@types/koa-router@7.4.8: + resolution: {integrity: sha512-SkWlv4F9f+l3WqYNQHnWjYnyTxYthqt8W9az2RTdQW7Ay8bc00iRZcrb8MC75iEfPqnGcg2csEl8tTG1NQPD4A==} + dependencies: + '@types/koa': 2.15.0 + dev: true + + /@types/koa@2.15.0: + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.8 + '@types/cookies': 0.9.0 + '@types/http-assert': 1.5.5 + '@types/http-errors': 2.0.4 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 20.12.3 + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + + /@types/mime@3.0.4: + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + dev: true + + /@types/node@20.12.3: + resolution: {integrity: sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/prop-types@15.7.12: + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + /@types/qs@6.9.14: + resolution: {integrity: sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/react-dom@18.2.22: + resolution: {integrity: sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==} + dependencies: + '@types/react': 18.2.71 + + /@types/react@18.2.71: + resolution: {integrity: sha512-PxEsB9OjmQeYGffoWnYAd/r5FiJuUw2niFQHPc2v2idwh8wGPkkYzOHuinNJJY6NZqfoTCiOIizDOz38gYNsyw==} + dependencies: + '@types/prop-types': 15.7.12 + '@types/scheduler': 0.23.0 + csstype: 3.1.3 + + /@types/responselike@1.0.3: + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + dependencies: + '@types/node': 20.12.3 + dev: true + + /@types/scheduler@0.23.0: + resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==} + + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + dev: true + + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.12.3 + dev: true + + /@types/serve-static@1.15.5: + resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/mime': 3.0.4 + '@types/node': 20.12.3 + dev: true + + /@types/tiny-async-pool@1.0.5: + resolution: {integrity: sha512-8hqr+s4rBthBtb+k02NXejl7BGVbj7CD/ZB2rMSvoSjXO52aXbtm9q/JEey5uDjzADs/zXEo7bU9iX+M6glAUA==} + dev: true + + /@typescript-eslint/eslint-plugin@7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.3): + resolution: {integrity: sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/scope-manager': 7.4.0 + '@typescript-eslint/type-utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/visitor-keys': 7.4.0 + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.3) + typescript: 5.4.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@7.4.0(eslint@8.57.0)(typescript@5.4.3): + resolution: {integrity: sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 7.4.0 + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3) + '@typescript-eslint/visitor-keys': 7.4.0 + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.57.0 + typescript: 5.4.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@7.4.0: + resolution: {integrity: sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==} + engines: {node: ^18.18.0 || >=20.0.0} + dependencies: + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/visitor-keys': 7.4.0 + dev: true + + /@typescript-eslint/type-utils@7.4.0(eslint@8.57.0)(typescript@5.4.3): + resolution: {integrity: sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3) + '@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.57.0 + ts-api-utils: 1.3.0(typescript@5.4.3) + typescript: 5.4.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@7.4.0: + resolution: {integrity: sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==} + engines: {node: ^18.18.0 || >=20.0.0} + dev: true + + /@typescript-eslint/typescript-estree@7.4.0(typescript@5.4.3): + resolution: {integrity: sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/visitor-keys': 7.4.0 + debug: 4.3.4(supports-color@5.5.0) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.0 + ts-api-utils: 1.3.0(typescript@5.4.3) + typescript: 5.4.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@7.4.0(eslint@8.57.0)(typescript@5.4.3): + resolution: {integrity: sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 7.4.0 + '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3) + eslint: 8.57.0 + semver: 7.6.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@7.4.0: + resolution: {integrity: sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==} + engines: {node: ^18.18.0 || >=20.0.0} + dependencies: + '@typescript-eslint/types': 7.4.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vitejs/plugin-react-swc@3.6.0(vite@5.2.6): + resolution: {integrity: sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==} + peerDependencies: + vite: ^4 || ^5 + dependencies: + '@swc/core': 1.4.11 + vite: 5.2.6(@types/node@20.12.3) + transitivePeerDependencies: + - '@swc/helpers' + dev: true + + /@zip.js/zip.js@2.7.40: + resolution: {integrity: sha512-kSYwO0Wth6G66QM4CejZqG0nRhBsVVTaR18M/Ta8EcqcvaV0dYrnDDyKAstfy0V5+ejK4b9w5xc1W0ECATJTvA==} + engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'} + dev: false + + /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: false + dev: true - /accepts/1.3.8: + /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} dependencies: @@ -384,44 +2257,30 @@ packages: negotiator: 0.6.3 dev: false - /agent-base/6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - - /agentkeepalive/4.2.1: - resolution: {integrity: sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==} - engines: {node: '>= 8.0.0'} - dependencies: - debug: 4.3.4 - depd: 1.1.2 - humanize-ms: 1.2.1 - transitivePeerDependencies: - - supports-color - dev: false - optional: true - - /aggregate-error/3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - dev: false - optional: true - - /ajv-keywords/3.5.2_ajv@6.12.6: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + /acorn-jsx@5.3.2(acorn@8.11.3): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: - ajv: ^6.9.1 + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - ajv: 6.12.6 + acorn: 8.11.3 dev: true - /ajv/6.12.6: + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + dependencies: + debug: 4.3.4(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + dev: true + + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: fast-deep-equal: 3.1.3 @@ -430,308 +2289,205 @@ packages: uri-js: 4.4.1 dev: true - /ansi-align/3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - dependencies: - string-width: 4.2.3 - dev: true - - /ansi-regex/5.0.1: + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - /ansi-styles/4.3.0: + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true - /anymatch/3.1.2: - resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: false - /app-builder-bin/3.7.1: - resolution: {integrity: sha512-ql93vEUq6WsstGXD+SBLSIQw6SNnhbDEM0swzgugytMxLp3rT24Ag/jcC80ZHxiPRTdew1niuR7P3/FCrDqIjw==} + /arch@2.2.0: + resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} dev: true - /app-builder-lib/22.14.13: - resolution: {integrity: sha512-SufmrtxU+D0Tn948fjEwAOlCN9757UXLkzzTWXMwZKR/5hisvgqeeBepWfphMIE6OkDGz0fbzEhL1P2Pty4XMg==} - engines: {node: '>=14.0.0'} - dependencies: - '@develar/schema-utils': 2.6.5 - '@electron/universal': 1.0.5 - '@malept/flatpak-bundler': 0.4.0 - 7zip-bin: 5.1.1 - async-exit-hook: 2.0.1 - bluebird-lst: 1.0.9 - builder-util: 22.14.13 - builder-util-runtime: 8.9.2 - chromium-pickle-js: 0.2.0 - debug: 4.3.4 - ejs: 3.1.8 - electron-osx-sign: 0.5.0 - electron-publish: 22.14.13 - form-data: 4.0.0 - fs-extra: 10.1.0 - hosted-git-info: 4.1.0 - is-ci: 3.0.1 - isbinaryfile: 4.0.10 - js-yaml: 4.1.0 - lazy-val: 1.0.5 - minimatch: 3.1.2 - read-config-file: 6.2.0 - sanitize-filename: 1.6.3 - semver: 7.3.7 - temp-file: 3.4.0 - transitivePeerDependencies: - - supports-color - dev: true + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - /aproba/2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: false - - /are-we-there-yet/2.0.0: - resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} - engines: {node: '>=10'} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.0 - dev: false - - /are-we-there-yet/3.0.0: - resolution: {integrity: sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.0 - dev: false - optional: true - - /arg/4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true - - /argparse/2.0.1: + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true - /asar/3.1.0: - resolution: {integrity: sha512-vyxPxP5arcAqN4F/ebHd/HhwnAiZtwhglvdmc7BR2f0ywbVNTOpSeyhLDbGXtE/y58hv1oC75TaNIXutnsOZsQ==} - engines: {node: '>=10.12.0'} - hasBin: true - dependencies: - chromium-pickle-js: 0.2.0 - commander: 5.1.0 - glob: 7.2.3 - minimatch: 3.1.2 - optionalDependencies: - '@types/glob': 7.2.0 - dev: true - - /assert-plus/1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} - requiresBuild: true - dev: true - optional: true - - /astral-regex/2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - requiresBuild: true - dev: true - optional: true - - /async-exit-hook/2.0.1: - resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} - engines: {node: '>=0.12.0'} - dev: true - - /async/3.2.4: - resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - dev: true - - /asynckit/0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: true - - /at-least-node/1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} - dev: true - - /balanced-match/1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - /base64-js/1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - requiresBuild: true - dev: true - - /binary-extensions/2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - dev: false - - /bluebird-lst/1.0.9: - resolution: {integrity: sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==} - dependencies: - bluebird: 3.7.2 - dev: true - - /bluebird/3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - dev: true - - /boolean/3.2.0: - resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} - dev: true - optional: true - - /boxen/5.1.2: - resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} + /aria-hidden@1.2.4: + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} engines: {node: '>=10'} dependencies: - ansi-align: 3.0.1 - camelcase: 6.3.0 - chalk: 4.1.2 - cli-boxes: 2.2.1 - string-width: 4.2.3 - type-fest: 0.20.2 - widest-line: 3.1.0 - wrap-ansi: 7.0.0 + tslib: 2.6.2 + dev: false + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} dev: true - /brace-expansion/1.1.11: + /ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + dependencies: + tslib: 2.6.2 + dev: true + + /autoprefixer@10.4.19(postcss@8.4.38): + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.23.0 + caniuse-lite: 1.0.30001605 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + /better-sqlite3@9.4.3: + resolution: {integrity: sha512-ud0bTmD9O3uWJGuXDltyj3R47Nz0OHX8iqPOT5PMspGqlu/qQFn+5S2eFBUCrySpavTjFXbi4EgrfVvPAHlImw==} + requiresBuild: true + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.2 + + /bin-check@4.1.0: + resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==} + engines: {node: '>=4'} + dependencies: + execa: 0.7.0 + executable: 4.1.1 + dev: true + + /bin-version-check@5.1.0: + resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==} + engines: {node: '>=12'} + dependencies: + bin-version: 6.0.0 + semver: 7.6.0 + semver-truncate: 3.0.0 + dev: true + + /bin-version@6.0.0: + resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==} + engines: {node: '>=12'} + dependencies: + execa: 5.1.1 + find-versions: 5.1.0 + dev: true + + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + /bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + dependencies: + file-uri-to-path: 1.0.0 + + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + /bl@5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 + dev: true - /brace-expansion/2.0.1: + /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 - dev: true - /braces/3.0.2: + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: false - /buffer-alloc-unsafe/1.1.0: - resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==} - dev: true - - /buffer-alloc/1.2.0: - resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} + /browserslist@4.23.0: + resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true dependencies: - buffer-alloc-unsafe: 1.1.0 - buffer-fill: 1.0.0 + caniuse-lite: 1.0.30001605 + electron-to-chromium: 1.4.724 + node-releases: 2.0.14 + update-browserslist-db: 1.0.13(browserslist@4.23.0) dev: true - /buffer-crc32/0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - /buffer-equal-constant-time/1.0.1: + /buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} dev: false - /buffer-equal/1.0.0: - resolution: {integrity: sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==} - engines: {node: '>=0.4.0'} - dev: true - - /buffer-fill/1.0.0: - resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} - dev: true - - /buffer-from/1.1.2: + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true - /buffer/5.7.1: + /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - requiresBuild: true + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 dev: true - optional: true - /builder-util-runtime/8.9.2: - resolution: {integrity: sha512-rhuKm5vh7E0aAmT6i8aoSfEjxzdYEFX7zDApK+eNgOhjofnWb74d9SRJv0H/8nsgOkos0TZ4zxW0P8J4N7xQ2A==} - engines: {node: '>=12.0.0'} - dependencies: - debug: 4.3.4 - sax: 1.2.4 - transitivePeerDependencies: - - supports-color - dev: true - - /builder-util/22.14.13: - resolution: {integrity: sha512-oePC/qrrUuerhmH5iaCJzPRAKlSBylrhzuAJmRQClTyWnZUv6jbaHh+VoHMbEiE661wrj2S2aV7/bQh12cj1OA==} - dependencies: - '@types/debug': 4.1.7 - '@types/fs-extra': 9.0.13 - 7zip-bin: 5.1.1 - app-builder-bin: 3.7.1 - bluebird-lst: 1.0.9 - builder-util-runtime: 8.9.2 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - fs-extra: 10.1.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-ci: 3.0.1 - js-yaml: 4.1.0 - source-map-support: 0.5.21 - stat-mode: 1.0.0 - temp-file: 3.4.0 - transitivePeerDependencies: - - supports-color - dev: true - - /bytes/3.1.2: + /bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} dev: false - /cacache/15.3.0: - resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} - engines: {node: '>= 10'} - dependencies: - '@npmcli/fs': 1.1.1 - '@npmcli/move-file': 1.1.2 - chownr: 2.0.0 - fs-minipass: 2.1.0 - glob: 7.2.3 - infer-owner: 1.0.4 - lru-cache: 6.0.0 - minipass: 3.3.3 - minipass-collect: 1.0.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - mkdirp: 1.0.4 - p-map: 4.0.0 - promise-inflight: 1.0.1 - rimraf: 3.0.2 - ssri: 8.0.1 - tar: 6.1.11 - unique-filename: 1.1.1 - dev: false - optional: true - - /cache-content-type/1.0.1: + /cache-content-type@1.0.1: resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} engines: {node: '>= 6.0.0'} dependencies: @@ -739,32 +2495,58 @@ packages: ylru: 1.3.2 dev: false - /cacheable-request/6.1.0: - resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==} + /cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + dev: true + + /cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} engines: {node: '>=8'} dependencies: - clone-response: 1.0.2 + clone-response: 1.0.3 get-stream: 5.2.0 - http-cache-semantics: 4.1.0 - keyv: 3.1.0 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 lowercase-keys: 2.0.0 - normalize-url: 4.5.1 - responselike: 1.0.2 + normalize-url: 6.1.0 + responselike: 2.0.1 dev: true - /call-bind/1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.1.2 + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 dev: false - /camelcase/6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} dev: true - /chalk/4.1.2: + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + /caniuse-lite@1.0.30001605: + resolution: {integrity: sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} dependencies: @@ -772,11 +2554,16 @@ packages: supports-color: 7.2.0 dev: true - /chokidar/3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + /chalk@5.2.0: + resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} dependencies: - anymatch: 3.1.2 + anymatch: 3.1.3 braces: 3.0.2 glob-parent: 5.1.2 is-binary-path: 2.1.0 @@ -784,250 +2571,186 @@ packages: normalize-path: 3.0.0 readdirp: 3.6.0 optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 + + /chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + /class-variance-authority@0.7.0: + resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + dependencies: + clsx: 2.0.0 dev: false - /chownr/2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - dev: false - - /chromium-pickle-js/0.2.0: - resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} + /cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 dev: true - /ci-info/2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} - dev: true - - /ci-info/3.3.2: - resolution: {integrity: sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==} - dev: true - - /clean-stack/2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: false - optional: true - - /cli-boxes/2.2.1: - resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} dev: true - /cli-truncate/2.1.0: - resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} - engines: {node: '>=8'} - requiresBuild: true - dependencies: - slice-ansi: 3.0.0 - string-width: 4.2.3 - dev: true - optional: true + /client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: false - /cliui/7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - - /clone-response/1.0.2: - resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==} + /clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: mimic-response: 1.0.1 dev: true - /co-body/6.1.0: + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true + + /clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + dev: false + + /clsx@2.1.0: + resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==} + engines: {node: '>=6'} + dev: false + + /co-body@6.1.0: resolution: {integrity: sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==} dependencies: - inflation: 2.0.0 - qs: 6.10.5 - raw-body: 2.5.1 + inflation: 2.1.0 + qs: 6.12.0 + raw-body: 2.5.2 type-is: 1.6.18 dev: false - /co/4.6.0: + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: false - /color-convert/2.0.1: + /code-block-writer@12.0.0: + resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} dev: true - /color-name/1.1.4: + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true - /color-support/1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - dev: false - - /colorette/2.0.16: - resolution: {integrity: sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==} - dev: false - - /colors/1.0.3: - resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} - engines: {node: '>=0.1.90'} + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} dev: true - /combined-stream/1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: true - - /commander/2.9.0: - resolution: {integrity: sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==} - engines: {node: '>= 0.6.x'} - dependencies: - graceful-readlink: 1.0.1 - dev: true - - /commander/5.1.0: - resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + + /commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} dev: true - /commander/6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - dev: true - - /commander/7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - dev: false - - /compare-version/0.1.2: - resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==} - engines: {node: '>=0.10.0'} - dev: true - - /concat-map/0.0.1: + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - /concat-stream/1.6.2: - resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} - engines: {'0': node >= 0.8} - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 2.3.7 - typedarray: 0.0.6 dev: true - /config-chain/1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - dependencies: - ini: 1.3.8 - proto-list: 1.2.4 - dev: true - optional: true - - /configstore/5.0.1: - resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} - engines: {node: '>=8'} - dependencies: - dot-prop: 5.3.0 - graceful-fs: 4.2.10 - make-dir: 3.1.0 - unique-string: 2.0.0 - write-file-atomic: 3.0.3 - xdg-basedir: 4.0.0 - dev: true - - /console-control-strings/1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: false - - /content-disposition/0.5.4: + /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} dependencies: safe-buffer: 5.2.1 - dev: false - /content-type/1.0.4: - resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} dev: false - /cookies/0.8.0: - resolution: {integrity: sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==} + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /cookies@0.9.1: + resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} engines: {node: '>= 0.8'} dependencies: depd: 2.0.0 keygrip: 1.1.0 dev: false - /copy-to/2.0.1: + /copy-to@2.0.1: resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==} dev: false - /core-util-is/1.0.2: - resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - requiresBuild: true - dev: true - optional: true - - /core-util-is/1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true - - /crc/3.8.0: - resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} - requiresBuild: true + /cosmiconfig@8.3.6(typescript@5.4.3): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true dependencies: - buffer: 5.7.1 - dev: true - optional: true - - /create-require/1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.4.3 dev: true - /cross-spawn/7.0.3: + /cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + dependencies: + lru-cache: 4.1.5 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + /data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} dev: true - /crypto-random-string/2.0.0: - resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} - engines: {node: '>=8'} - dev: true - - /debug/2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - dependencies: - ms: 2.0.0 - dev: true - - /debug/4.3.2: - resolution: {integrity: sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: false - - /debug/4.3.4: + /debug@4.3.4(supports-color@5.5.0): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -1037,463 +2760,632 @@ packages: optional: true dependencies: ms: 2.1.2 + supports-color: 5.5.0 - /decompress-response/3.3.0: - resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==} - engines: {node: '>=4'} + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} dependencies: - mimic-response: 1.0.1 - dev: true + mimic-response: 3.1.0 - /deep-equal/1.0.1: + /deep-equal@1.0.1: resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} dev: false - /deep-extend/0.6.0: + /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /defer-to-connect/1.1.3: - resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + dependencies: + clone: 1.0.4 dev: true - /define-properties/1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true + + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - dev: true - optional: true + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dev: false - /delayed-stream/1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: true - - /delegates/1.0.0: + /delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} dev: false - /depd/1.1.2: + /depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} dev: false - /depd/2.0.0: + /depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} dev: false - /destroy/1.2.0: + /destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} dev: false - /detect-libc/2.0.1: - resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + /detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} + + /detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false - /detect-node/2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - dev: true - optional: true + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - /diff/4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + /diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} engines: {node: '>=0.3.1'} dev: true - /dir-compare/2.4.0: - resolution: {integrity: sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==} - hasBin: true - dependencies: - buffer-equal: 1.0.0 - colors: 1.0.3 - commander: 2.9.0 - minimatch: 3.0.4 + /diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} dev: true - /dmg-builder/22.14.13: - resolution: {integrity: sha512-xNOugB6AbIRETeU2uID15sUfjdZZcKdxK8xkFnwIggsM00PJ12JxpLNPTjcRoUnfwj3WrPjilrO64vRMwNItQg==} - dependencies: - app-builder-lib: 22.14.13 - builder-util: 22.14.13 - builder-util-runtime: 8.9.2 - fs-extra: 10.1.0 - iconv-lite: 0.6.3 - js-yaml: 4.1.0 - optionalDependencies: - dmg-license: 1.0.11 - transitivePeerDependencies: - - supports-color - dev: true - - /dmg-license/1.0.11: - resolution: {integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==} - engines: {node: '>=8'} - os: [darwin] - deprecated: 'Disk image license agreements are deprecated by Apple and will probably be removed in a future macOS release. Discussion at: https://github.com/argv-minus-one/dmg-license/issues/11' - hasBin: true - requiresBuild: true - dependencies: - '@types/plist': 3.0.2 - '@types/verror': 1.10.5 - ajv: 6.12.6 - crc: 3.8.0 - iconv-corefoundation: 1.1.7 - plist: 3.0.5 - smart-buffer: 4.2.0 - verror: 1.10.1 - dev: true - optional: true - - /dot-prop/5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} dependencies: - is-obj: 2.0.0 + path-type: 4.0.0 dev: true - /dotenv-expand/5.1.0: - resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} - dev: true + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - /dotenv/9.0.2: - resolution: {integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==} - engines: {node: '>=10'} - dev: true - - /dprint/0.36.1: - resolution: {integrity: sha512-F8TaWlieYtJc4ktqr0tjNwg/HN4wNPTU6rTq29MdoIKXi9gQmh7Ibqr0oyW1hsLpqBuAXjjwEYIos9plYkwENQ==} - hasBin: true - requiresBuild: true + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} dependencies: - https-proxy-agent: 5.0.1 - yauzl: 2.10.0 - transitivePeerDependencies: - - supports-color - dev: false - - /duplexer3/0.1.4: - resolution: {integrity: sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA==} + esutils: 2.0.3 dev: true - /ecdsa-sig-formatter/1.0.11: + /dotenv-expand@11.0.6: + resolution: {integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==} + engines: {node: '>=12'} + dependencies: + dotenv: 16.4.5 + dev: true + + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + /ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} dependencies: safe-buffer: 5.2.1 dev: false - /ee-first/1.1.1: + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false - /ejs/3.1.8: - resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==} - engines: {node: '>=0.10.0'} - hasBin: true - dependencies: - jake: 10.8.5 + /electron-to-chromium@1.4.724: + resolution: {integrity: sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==} dev: true - /electron-builder/22.14.13: - resolution: {integrity: sha512-3fgLxqF2TXVKiUPeg74O4V3l0l3j7ERLazo8sUbRkApw0+4iVAf2BJkHsHMaXiigsgCoEzK/F4/rB5rne/VAnw==} - engines: {node: '>=14.0.0'} - hasBin: true - dependencies: - '@types/yargs': 17.0.10 - app-builder-lib: 22.14.13 - builder-util: 22.14.13 - builder-util-runtime: 8.9.2 - chalk: 4.1.2 - dmg-builder: 22.14.13 - fs-extra: 10.1.0 - is-ci: 3.0.1 - lazy-val: 1.0.5 - read-config-file: 6.2.0 - update-notifier: 5.1.0 - yargs: 17.5.1 - transitivePeerDependencies: - - supports-color - dev: true - - /electron-osx-sign/0.5.0: - resolution: {integrity: sha512-icoRLHzFz/qxzDh/N4Pi2z4yVHurlsCAYQvsCSG7fCedJ4UJXBS6PoQyGH71IfcqKupcKeK7HX/NkyfG+v6vlQ==} - engines: {node: '>=4.0.0'} - hasBin: true - dependencies: - bluebird: 3.7.2 - compare-version: 0.1.2 - debug: 2.6.9 - isbinaryfile: 3.0.3 - minimist: 1.2.6 - plist: 3.0.5 - dev: true - - /electron-publish/22.14.13: - resolution: {integrity: sha512-0oP3QiNj3e8ewOaEpEJV/o6Zrmy2VarVvZ/bH7kyO/S/aJf9x8vQsKVWpsdmSiZ5DJEHgarFIXrnO0ZQf0P9iQ==} - dependencies: - '@types/fs-extra': 9.0.13 - builder-util: 22.14.13 - builder-util-runtime: 8.9.2 - chalk: 4.1.2 - fs-extra: 10.1.0 - lazy-val: 1.0.5 - mime: 2.6.0 - transitivePeerDependencies: - - supports-color - dev: true - - /electron/11.5.0: - resolution: {integrity: sha512-WjNDd6lGpxyiNjE3LhnFCAk/D9GIj1rU3GSDealVShhkkkPR3Vh4q8ErXGDl1OAO/faomVa10KoFPUN/pLbNxg==} - engines: {node: '>= 8.6'} - hasBin: true - requiresBuild: true - dependencies: - '@electron/get': 1.14.1 - '@types/node': 12.20.55 - extract-zip: 1.7.0 - transitivePeerDependencies: - - supports-color - dev: true - - /emoji-regex/8.0.0: + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - /encodeurl/1.0.2: + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} - - /encoding/0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true - dependencies: - iconv-lite: 0.6.3 dev: false - optional: true - /end-of-stream/1.4.4: + /end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 dev: true - /env-paths/2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - /err-code/2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 dev: false - optional: true - /es6-error/4.1.1: - resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: false + + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 dev: true - optional: true - /escalade/3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} - - /escape-goat/2.1.1: - resolution: {integrity: sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==} - engines: {node: '>=8'} dev: true - /escape-html/1.0.3: + /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} dev: false - /escape-string-regexp/4.0.0: + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: true - optional: true - /esm/3.2.25: - resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} - engines: {node: '>=6'} - dev: false + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: true - /extract-zip/1.7.0: - resolution: {integrity: sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==} + /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.57.0 + dev: true + + /eslint-plugin-react-refresh@0.4.6(eslint@8.57.0): + resolution: {integrity: sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.57.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - concat-stream: 1.6.2 - debug: 2.6.9 - mkdirp: 0.5.6 - yauzl: 2.10.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4(supports-color@5.5.0) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color dev: true - /extsprintf/1.4.1: - resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} - engines: {'0': node >=0.6.0} - requiresBuild: true + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.3 + acorn-jsx: 5.3.2(acorn@8.11.3) + eslint-visitor-keys: 3.4.3 dev: true - optional: true - /fast-deep-equal/3.1.3: + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /execa@0.7.0: + resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==} + engines: {node: '>=4'} + dependencies: + cross-spawn: 5.1.0 + get-stream: 3.0.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + dev: true + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: true + + /executable@4.1.1: + resolution: {integrity: sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==} + engines: {node: '>=4'} + dependencies: + pify: 2.3.0 + dev: true + + /expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + /ext-list@2.2.2: + resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} + engines: {node: '>=0.10.0'} + dependencies: + mime-db: 1.52.0 + dev: true + + /ext-name@5.0.0: + resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} + engines: {node: '>=4'} + dependencies: + ext-list: 2.2.2 + sort-keys-length: 1.0.1 + dev: true + + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - requiresBuild: true dev: true - /fast-json-stable-stringify/2.1.0: + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + + /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: true - /fd-slicer/1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - dependencies: - pend: 1.2.0 - - /filelist/1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - dependencies: - minimatch: 5.1.0 + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fill-range/7.0.1: + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + dependencies: + reusify: 1.0.4 + + /fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.2.0 + dev: true + + /file-type@17.1.6: + resolution: {integrity: sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + readable-web-to-node-stream: 3.0.2 + strtok3: 7.0.0 + token-types: 5.0.1 + dev: true + + /file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + /filename-reserved-regex@3.0.0: + resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /filenamify@5.1.1: + resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==} + engines: {node: '>=12.20'} + dependencies: + filename-reserved-regex: 3.0.0 + strip-outer: 2.0.0 + trim-repeated: 2.0.0 + dev: true + + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: false - /form-data/4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 + locate-path: 6.0.0 + path-exists: 4.0.0 dev: true - /fresh/0.5.2: + /find-versions@5.1.0: + resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==} + engines: {node: '>=12'} + dependencies: + semver-regex: 4.0.5 + dev: true + + /flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + /formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + dependencies: + fetch-blob: 3.2.0 + dev: true + + /fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + dev: true + + /fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} dev: false - /fs-extra/10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + /fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + /fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 - universalify: 2.0.0 + universalify: 2.0.1 dev: true - /fs-extra/8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.10 - jsonfile: 4.0.0 - universalify: 0.1.2 - dev: true - - /fs-extra/9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.10 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: true - - /fs-minipass/2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.3 - dev: false - - /fs.realpath/1.0.0: + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true - /fsevents/2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: false optional: true - /function-bind/1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - /gauge/3.0.2: - resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} - engines: {node: '>=10'} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: false - - /gauge/4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - dev: false - optional: true - - /get-caller-file/2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} dev: true - /get-intrinsic/1.1.2: - resolution: {integrity: sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==} + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 - has: 1.0.3 + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 has-symbols: 1.0.3 + hasown: 2.0.2 + dev: false - /get-stream/4.1.0: - resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + /get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} - dependencies: - pump: 3.0.0 + dev: false + + /get-stream@3.0.0: + resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} + engines: {node: '>=4'} dev: true - /get-stream/5.2.0: + /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} dependencies: pump: 3.0.0 dev: true - /getopts/2.2.5: - resolution: {integrity: sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==} - dev: false + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true - /glob-parent/5.1.2: + /git-diff@2.0.6: + resolution: {integrity: sha512-/Iu4prUrydE3Pb3lCBMbcSNIf81tgGt0W1ZwknnyF62t3tHmtiJTRj0f+1ZIhp3+Rh0ktz1pJVoa7ZXUCskivA==} + engines: {node: '>= 4.8.0'} + dependencies: + chalk: 2.4.2 + diff: 3.5.0 + loglevel: 1.9.1 + shelljs: 0.8.5 + shelljs.exec: 1.1.8 + dev: true + + /github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: false - /glob/7.2.3: + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + + /glob@10.3.12: + resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.2 + + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: fs.realpath: 1.0.0 @@ -1502,118 +3394,102 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - - /global-agent/3.0.0: - resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} - engines: {node: '>=10.0'} - requiresBuild: true - dependencies: - boolean: 3.2.0 - es6-error: 4.1.1 - matcher: 3.0.0 - roarr: 2.15.4 - semver: 7.3.7 - serialize-error: 7.0.1 dev: true - optional: true - /global-dirs/3.0.0: - resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==} + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} dependencies: - ini: 2.0.0 + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 dev: true - /global-tunnel-ng/2.7.1: - resolution: {integrity: sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==} - engines: {node: '>=0.10'} - requiresBuild: true + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: - encodeurl: 1.0.2 - lodash: 4.17.21 - npm-conf: 1.1.3 - tunnel: 0.0.6 - dev: true - optional: true + get-intrinsic: 1.2.4 + dev: false - /globalthis/1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} + /got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} dependencies: - define-properties: 1.1.4 - dev: true - optional: true - - /got/9.6.0: - resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} - engines: {node: '>=8.6'} - dependencies: - '@sindresorhus/is': 0.14.0 - '@szmarczak/http-timer': 1.1.2 - cacheable-request: 6.1.0 - decompress-response: 3.3.0 - duplexer3: 0.1.4 - get-stream: 4.1.0 - lowercase-keys: 1.0.1 - mimic-response: 1.0.1 - p-cancelable: 1.1.0 - to-readable-stream: 1.0.0 - url-parse-lax: 3.0.0 + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 dev: true - /graceful-fs/4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - - /graceful-readlink/1.0.1: - resolution: {integrity: sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==} + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true - /has-flag/4.0.0: + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} dev: true - /has-property-descriptors/1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} dependencies: - get-intrinsic: 1.1.2 - dev: true - optional: true + es-define-property: 1.0.0 + dev: false - /has-symbols/1.0.3: + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + dev: false - /has-tostringtag/1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} dependencies: has-symbols: 1.0.3 dev: false - /has-unicode/2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: false - - /has-yarn/2.1.0: - resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} - engines: {node: '>=8'} - dev: true - - /has/1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 - /hosted-git-info/4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} - dependencies: - lru-cache: 6.0.0 - dev: true - - /http-assert/1.5.0: + /http-assert@1.5.0: resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} engines: {node: '>= 0.8'} dependencies: @@ -1621,10 +3497,11 @@ packages: http-errors: 1.8.1 dev: false - /http-cache-semantics/4.1.0: - resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true - /http-errors/1.8.1: + /http-errors@1.8.1: resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} engines: {node: '>= 0.6'} dependencies: @@ -1635,7 +3512,7 @@ packages: toidentifier: 1.0.1 dev: false - /http-errors/2.0.0: + /http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} dependencies: @@ -1646,305 +3523,248 @@ packages: toidentifier: 1.0.1 dev: false - /http-proxy-agent/4.0.1: - resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} - engines: {node: '>= 6'} + /http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: false - optional: true + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + dev: true - /http-proxy-agent/5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} + /https-proxy-agent@6.2.1: + resolution: {integrity: sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA==} + engines: {node: '>= 14'} dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.3.4 + agent-base: 7.1.1 + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: true - /https-proxy-agent/5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - - /humanize-ms/1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - dependencies: - ms: 2.1.3 - dev: false - optional: true - - /iconv-corefoundation/1.1.7: - resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} - engines: {node: ^8.11.2 || >=10} - os: [darwin] - requiresBuild: true - dependencies: - cli-truncate: 2.1.0 - node-addon-api: 1.7.2 + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} dev: true - optional: true - /iconv-lite/0.4.24: + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: true + + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 dev: false - /iconv-lite/0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - - /ieee754/1.2.1: + /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - requiresBuild: true - dev: true - optional: true - /import-lazy/2.1.0: - resolution: {integrity: sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==} - engines: {node: '>=4'} + /ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} dev: true - /imurmurhash/0.1.4: + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + dev: true - /indent-string/4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - dev: false - optional: true - - /infer-owner/1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - dev: false - optional: true - - /inflation/2.0.0: - resolution: {integrity: sha512-m3xv4hJYR2oXw4o4Y5l6P5P16WYmazYof+el6Al3f+YlggGj6qT9kImBAnzDelRALnP5d3h4jGBPKzYCizjZZw==} + /inflation@2.1.0: + resolution: {integrity: sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==} engines: {node: '>= 0.8.0'} dev: false - /inflight/1.0.6: + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 + dev: true - /inherits/2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /ini/1.3.8: + /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true - /ini/2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - dev: true - - /interpret/2.2.0: - resolution: {integrity: sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==} + /interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} + dev: true + + /invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 dev: false - /ip/1.1.8: - resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} - dev: false - optional: true + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true - /is-binary-path/2.1.0: + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: - binary-extensions: 2.2.0 - dev: false + binary-extensions: 2.3.0 - /is-ci/2.0.0: - resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} - hasBin: true + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: - ci-info: 2.0.0 - dev: true + hasown: 2.0.2 - /is-ci/3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - dependencies: - ci-info: 3.3.2 - dev: true - - /is-core-module/2.9.0: - resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} - dependencies: - has: 1.0.3 - dev: false - - /is-extglob/2.1.1: + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: false - /is-fullwidth-code-point/3.0.0: + /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - /is-generator-function/1.0.10: + /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 dev: false - /is-glob/4.0.3: + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: false - /is-installed-globally/0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} - dependencies: - global-dirs: 3.0.0 - is-path-inside: 3.0.3 + /is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} dev: true - /is-lambda/1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: false - optional: true - - /is-npm/5.0.0: - resolution: {integrity: sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==} - engines: {node: '>=10'} - dev: true - - /is-number/7.0.0: + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: false - /is-obj/2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - dev: true - - /is-path-inside/3.0.3: + /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} dev: true - /is-typedarray/1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + /is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} dev: true - /is-yarn-global/0.3.0: - resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} + /is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} dev: true - /isarray/1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} dev: true - /isbinaryfile/3.0.3: - resolution: {integrity: sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==} - engines: {node: '>=0.6.0'} - dependencies: - buffer-alloc: 1.2.0 + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /isbinaryfile/4.0.10: - resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} - engines: {node: '>= 8.0.0'} + /is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} dev: true - /isexe/2.0.0: + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - /jake/10.8.5: - resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} - engines: {node: '>=10'} - hasBin: true + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} dependencies: - async: 3.2.4 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 - dev: true + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 - /js-yaml/4.1.0: + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + + /jotai@2.7.2(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-6Ft5kpNu8p93Ssf1Faoza3hYQZRIYp7rioK8MwTTFnbQKwUyZElwquPwl1h6U0uo9hC0jr+ghO3gcSjc6P35/Q==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + dependencies: + '@types/react': 18.2.71 + react: 18.2.0 + dev: false + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 dev: true - /json-buffer/3.0.0: - resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true dev: true - /json-schema-traverse/0.4.1: + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - requiresBuild: true dev: true - /json-stable-stringify/1.0.1: - resolution: {integrity: sha512-i/J297TW6xyj7sDFa7AmBPkQvLIxWr2kKPWI26tXydnZrzVAocNqn5DMNT1Mzk0vit1V5UkRM7C1KdVNp7Lmcg==} - dependencies: - jsonify: 0.0.0 + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /json-stringify-safe/5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: true - optional: true - - /json5/2.2.1: - resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true dev: true - /jsonfile/4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - optionalDependencies: - graceful-fs: 4.2.10 - dev: true - - /jsonfile/6.1.0: + /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: - universalify: 2.0.0 + universalify: 2.0.1 optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true - /jsonify/0.0.0: - resolution: {integrity: sha512-trvBk1ki43VZptdBI5rIlG4YOzyeH/WefQt5rj1grasPn4iiZWKet8nkgc4GlsAylaztn0qZfUYOiTsASJFdNA==} - dev: true - - /jsonschema/1.4.1: - resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} - dev: false - - /jsonwebtoken/8.5.1: + /jsonwebtoken@8.5.1: resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} engines: {node: '>=4', npm: '>=1.4.28'} dependencies: @@ -1957,10 +3777,10 @@ packages: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 5.7.1 + semver: 5.7.2 dev: false - /jwa/1.4.1: + /jwa@1.4.1: resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} dependencies: buffer-equal-constant-time: 1.0.1 @@ -1968,82 +3788,45 @@ packages: safe-buffer: 5.2.1 dev: false - /jws/3.2.2: + /jws@3.2.2: resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} dependencies: jwa: 1.4.1 safe-buffer: 5.2.1 dev: false - /keygrip/1.1.0: + /keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} dependencies: tsscmp: 1.0.6 dev: false - /keyv/3.1.0: - resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: - json-buffer: 3.0.0 + json-buffer: 3.0.1 dev: true - /knex/0.95.15_sqlite3@5.0.8: - resolution: {integrity: sha512-Loq6WgHaWlmL2bfZGWPsy4l8xw4pOE+tmLGkPG0auBppxpI0UcK+GYCycJcqz9W54f2LiGewkCVLBm3Wq4ur/w==} - engines: {node: '>=10'} - hasBin: true - peerDependencies: - mysql: '*' - mysql2: '*' - pg: '*' - pg-native: '*' - sqlite3: '*' - tedious: '*' - peerDependenciesMeta: - mysql: - optional: true - mysql2: - optional: true - pg: - optional: true - pg-native: - optional: true - sqlite3: - optional: true - tedious: - optional: true - dependencies: - colorette: 2.0.16 - commander: 7.2.0 - debug: 4.3.2 - escalade: 3.1.1 - esm: 3.2.25 - getopts: 2.2.5 - interpret: 2.2.0 - lodash: 4.17.21 - pg-connection-string: 2.5.0 - rechoir: 0.7.0 - resolve-from: 5.0.0 - sqlite3: 5.0.8 - tarn: 3.0.2 - tildify: 2.0.0 - transitivePeerDependencies: - - supports-color - dev: false + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true - /koa-bodyparser/4.3.0: - resolution: {integrity: sha512-uyV8G29KAGwZc4q/0WUAjH+Tsmuv9ImfBUF2oZVyZtaeo0husInagyn/JH85xMSxM0hEk/mbCII5ubLDuqW/Rw==} + /koa-bodyparser@4.4.1: + resolution: {integrity: sha512-kBH3IYPMb+iAXnrxIhXnW+gXV8OTzCu8VPDqvcDHW9SQrbkHmqPQtiZwrltNmSq6/lpipHnT7k7PsjlVD7kK0w==} engines: {node: '>=8.0.0'} dependencies: co-body: 6.1.0 copy-to: 2.0.1 + type-is: 1.6.18 dev: false - /koa-compose/4.1.0: + /koa-compose@4.1.0: resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} dev: false - /koa-convert/2.0.0: + /koa-convert@2.0.0: resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} engines: {node: '>= 10'} dependencies: @@ -2051,12 +3834,12 @@ packages: koa-compose: 4.1.0 dev: false - /koa-router/10.1.1: - resolution: {integrity: sha512-z/OzxVjf5NyuNO3t9nJpx7e1oR3FSBAauiwXtMQu4ppcnuNZzTaQ4p21P8A6r2Es8uJJM339oc4oVW+qX7SqnQ==} - engines: {node: '>= 8.0.0'} + /koa-router@12.0.1: + resolution: {integrity: sha512-gaDdj3GtzoLoeosacd50kBBTnnh3B9AYxDThQUo4sfUyXdOhY6ku1qyZKW88tQCRgc3Sw6ChXYXWZwwgjOxE0w==} + engines: {node: '>= 12'} dependencies: - debug: 4.3.4 - http-errors: 1.8.1 + debug: 4.3.4(supports-color@5.5.0) + http-errors: 2.0.0 koa-compose: 4.1.0 methods: 1.1.2 path-to-regexp: 6.2.1 @@ -2064,16 +3847,16 @@ packages: - supports-color dev: false - /koa/2.13.4: - resolution: {integrity: sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==} + /koa@2.15.2: + resolution: {integrity: sha512-MXTeZH3M6AJ8ukW2QZ8wqO3Dcdfh2WRRmjCBkEP+NhKNCiqlO5RDqHmSnsyNrbRJrdjyvIGSJho4vQiWgQJSVA==} engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} dependencies: accepts: 1.3.8 cache-content-type: 1.0.1 content-disposition: 0.5.4 - content-type: 1.0.4 - cookies: 0.8.0 - debug: 4.3.4 + content-type: 1.0.5 + cookies: 0.9.1 + debug: 4.3.4(supports-color@5.5.0) delegates: 1.0.0 depd: 2.0.0 destroy: 1.2.0 @@ -2095,515 +3878,738 @@ packages: - supports-color dev: false - /latest-version/5.1.0: - resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} - engines: {node: '>=8'} + /kysely-codegen@0.14.1(better-sqlite3@9.4.3)(kysely@0.27.3): + resolution: {integrity: sha512-W6ULVWYnlBcGalHlKqw3ls0QjPl6n6z1xJ9ixPCh1lxDSbe98s4gjEbKC3hh8ZTCUTZSLSasi+QCii4AL/j7pw==} + hasBin: true + peerDependencies: + '@libsql/kysely-libsql': ^0.3.0 + '@tediousjs/connection-string': ^0.5.0 + better-sqlite3: '>=7.6.2' + kysely: ^0.27.0 + kysely-bun-worker: ^0.5.3 + mysql2: ^2.3.3 || ^3.0.0 + pg: ^8.8.0 + tarn: ^3.0.0 + tedious: ^16.6.0 || ^17.0.0 + peerDependenciesMeta: + '@libsql/kysely-libsql': + optional: true + '@tediousjs/connection-string': + optional: true + better-sqlite3: + optional: true + kysely-bun-worker: + optional: true + mysql2: + optional: true + pg: + optional: true + tarn: + optional: true + tedious: + optional: true dependencies: - package-json: 6.5.0 + better-sqlite3: 9.4.3 + chalk: 4.1.2 + dotenv: 16.4.5 + dotenv-expand: 11.0.6 + git-diff: 2.0.6 + kysely: 0.27.3 + micromatch: 4.0.5 + minimist: 1.2.8 dev: true - /lazy-val/1.0.5: - resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} + /kysely@0.27.3: + resolution: {integrity: sha512-lG03Ru+XyOJFsjH3OMY6R/9U38IjDPfnOfDgO3ynhbDr+Dz8fak+X6L62vqu3iybQnj+lG84OttBuU9KY3L9kA==} + engines: {node: '>=14.0.0'} + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 dev: true - /lodash.includes/4.3.0: + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + /lilconfig@3.1.1: + resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} + engines: {node: '>=14'} + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash._reinterpolate@3.0.0: + resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==} + dev: true + + /lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: false + + /lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} dev: false - /lodash.isboolean/3.0.3: + /lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} dev: false - /lodash.isinteger/4.0.4: + /lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} dev: false - /lodash.isnumber/3.0.3: + /lodash.isnumber@3.0.3: resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} dev: false - /lodash.isplainobject/4.0.6: + /lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} dev: false - /lodash.isstring/4.0.1: + /lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} dev: false - /lodash.once/4.1.1: + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} dev: false - /lodash/4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - /lowercase-keys/1.0.1: - resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} - engines: {node: '>=0.10.0'} + /lodash.template@4.5.0: + resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==} + dependencies: + lodash._reinterpolate: 3.0.0 + lodash.templatesettings: 4.2.0 dev: true - /lowercase-keys/2.0.0: + /lodash.templatesettings@4.2.0: + resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==} + dependencies: + lodash._reinterpolate: 3.0.0 + dev: true + + /log-symbols@5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + dependencies: + chalk: 5.2.0 + is-unicode-supported: 1.3.0 + dev: true + + /loglevel@1.9.1: + resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + engines: {node: '>= 0.6.0'} + dev: true + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} dev: true - /lru-cache/6.0.0: + /lru-cache@10.2.0: + resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + engines: {node: 14 || >=16.14} + + /lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} dependencies: yallist: 4.0.0 - /make-dir/3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - - /make-error/1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true - - /make-fetch-happen/9.1.0: - resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} - engines: {node: '>= 10'} - dependencies: - agentkeepalive: 4.2.1 - cacache: 15.3.0 - http-cache-semantics: 4.1.0 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - is-lambda: 1.0.1 - lru-cache: 6.0.0 - minipass: 3.3.3 - minipass-collect: 1.0.2 - minipass-fetch: 1.4.1 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.3 - promise-retry: 2.0.1 - socks-proxy-agent: 6.2.1 - ssri: 8.0.1 - transitivePeerDependencies: - - supports-color - dev: false - optional: true - - /matcher/3.0.0: - resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 4.0.0 - dev: true - optional: true - - /media-typer/0.3.0: + /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} dev: false - /methods/1.1.2: + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + /methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} dev: false - /mime-db/1.52.0: + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + + /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - /mime-types/2.1.35: + /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 + dev: false - /mime/2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} dev: true - /mimic-response/1.0.1: + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} dev: true - /minimatch/3.0.4: - resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true - /minimatch/3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - - /minimatch/5.1.0: - resolution: {integrity: sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==} + /minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 dev: true - /minimist/1.2.6: - resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} - dev: true - - /minipass-collect/1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} + /minimatch@9.0.3: + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} dependencies: - minipass: 3.3.3 - dev: false - optional: true + brace-expansion: 2.0.1 - /minipass-fetch/1.4.1: - resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.3 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 - dev: false - optional: true + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - /minipass-flush/1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.3 - dev: false - optional: true + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} - /minipass-pipeline/1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.3 - dev: false - optional: true - - /minipass-sized/1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - dependencies: - minipass: 3.3.3 - dev: false - optional: true - - /minipass/3.3.3: - resolution: {integrity: sha512-N0BOsdFAlNRfmwMhjAsLVWOk7Ljmeb39iqFlsV1At+jqRhSUP9yeof8FyJu4imaJiSUp8vQebWD/guZwGQC8iA==} - engines: {node: '>=8'} - dependencies: - yallist: 4.0.0 + /mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} dev: false - /minizlib/2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - dependencies: - minipass: 3.3.3 - yallist: 4.0.0 - dev: false + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - /mkdirp/0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - dependencies: - minimist: 1.2.6 - dev: true - - /mkdirp/1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + /mkdirp@2.1.6: + resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} engines: {node: '>=10'} hasBin: true - dev: false - - /ms/2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: true - /ms/2.1.2: + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - /ms/2.1.3: + /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: false - /natural-orderby/2.0.3: + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /natural-orderby@2.0.3: resolution: {integrity: sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==} dev: false - /negotiator/0.6.3: + /negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} dev: false - /node-addon-api/1.7.2: - resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + /nice-napi@1.0.2: + resolution: {integrity: sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==} + os: ['!win32'] + requiresBuild: true + dependencies: + node-addon-api: 3.2.1 + node-gyp-build: 4.8.0 + dev: true + optional: true + + /node-abi@3.56.0: + resolution: {integrity: sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==} + engines: {node: '>=10'} + dependencies: + semver: 7.6.0 + + /node-addon-api@3.2.1: + resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} requiresBuild: true dev: true optional: true - /node-addon-api/3.2.1: - resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} - dev: false + /node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + dev: true - /node-addon-api/4.3.0: - resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} - dev: false - - /node-fetch/2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + /node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - whatwg-url: 5.0.0 - dev: false + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + dev: true - /node-gyp/8.4.1: - resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} - engines: {node: '>= 10.12.0'} + /node-gyp-build@4.8.0: + resolution: {integrity: sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==} hasBin: true requiresBuild: true - dependencies: - env-paths: 2.2.1 - glob: 7.2.3 - graceful-fs: 4.2.10 - make-fetch-happen: 9.1.0 - nopt: 5.0.0 - npmlog: 6.0.2 - rimraf: 3.0.2 - semver: 7.3.7 - tar: 6.1.11 - which: 2.0.2 - transitivePeerDependencies: - - supports-color - dev: false + dev: true optional: true - /node-stream-zip/1.15.0: - resolution: {integrity: sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==} - engines: {node: '>=0.12.0'} - dev: false + /node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + dev: true - /nopt/5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} + /nodemon@3.1.0: + resolution: {integrity: sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chokidar: 3.6.0 + debug: 4.3.4(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.6.0 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.0 + undefsafe: 2.0.5 + dev: true + + /nopt@1.0.10: + resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} hasBin: true dependencies: abbrev: 1.1.1 - dev: false + dev: true - /normalize-path/3.0.0: + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: false - /normalize-url/4.5.1: - resolution: {integrity: sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==} - engines: {node: '>=8'} + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} dev: true - /npm-conf/1.1.3: - resolution: {integrity: sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==} + /normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + dev: true + + /npm-run-path@2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} engines: {node: '>=4'} dependencies: - config-chain: 1.1.13 - pify: 3.0.0 + path-key: 2.0.1 dev: true - optional: true - /npmlog/5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 - dev: false + path-key: 3.1.1 + dev: true - /npmlog/6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + /npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - are-we-there-yet: 3.0.0 - console-control-strings: 1.1.0 - gauge: 4.0.4 - set-blocking: 2.0.0 - dev: false - optional: true + path-key: 4.0.0 + dev: true - /object-assign/4.1.1: + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} dev: false - /object-inspect/1.12.2: - resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} - dev: false - - /object-keys/1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: true - optional: true - - /on-finished/2.4.1: + /on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} dependencies: ee-first: 1.1.1 dev: false - /once/1.4.0: + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - /only/0.0.2: + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /only@0.0.2: resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} dev: false - /p-cancelable/1.1.0: - resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==} - engines: {node: '>=6'} + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 dev: true - /p-map/4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + /ora@6.3.1: + resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + chalk: 5.2.0 + cli-cursor: 4.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + stdin-discarder: 0.1.0 + strip-ansi: 7.1.0 + wcwidth: 1.0.1 + dev: true + + /os-filter-obj@2.0.0: + resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==} + engines: {node: '>=4'} + dependencies: + arch: 2.2.0 + dev: true + + /p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + dev: true + + /p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: - aggregate-error: 3.1.0 - dev: false - optional: true - - /package-json/6.5.0: - resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==} - engines: {node: '>=8'} - dependencies: - got: 9.6.0 - registry-auth-token: 4.2.2 - registry-url: 5.1.0 - semver: 6.3.0 + yocto-queue: 0.1.0 dev: true - /parseurl/1.3.3: + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.24.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} dev: false - /path-is-absolute/1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true - /path-key/3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} dev: true - /path-parse/1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: false + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true - /path-to-regexp/6.2.1: + /path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + /path-scurry@1.10.2: + resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.2.0 + minipass: 7.0.4 + + /path-to-regexp@6.2.1: resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} dev: false - /pend/1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true - /pg-connection-string/2.5.0: - resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} - dev: false + /peek-readable@5.0.0: + resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} + engines: {node: '>=14.16'} + dev: true - /picomatch/2.3.1: + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: false - /pify/3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + /piscina@4.4.0: + resolution: {integrity: sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg==} + optionalDependencies: + nice-napi: 1.0.2 dev: true - optional: true - /plist/3.0.5: - resolution: {integrity: sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==} - engines: {node: '>=6'} + /postcss-import@15.1.0(postcss@8.4.38): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 dependencies: - base64-js: 1.5.1 - xmlbuilder: 9.0.7 - dev: true + postcss: 8.4.38 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 - /prepend-http/2.0.0: - resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==} + /postcss-js@4.0.1(postcss@8.4.38): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.38 + + /postcss-load-config@4.0.2(postcss@8.4.38): + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 3.1.1 + postcss: 8.4.38 + yaml: 2.4.1 + + /postcss-nested@6.0.1(postcss@8.4.38): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.38 + postcss-selector-parser: 6.0.16 + + /postcss-selector-parser@6.0.16: + resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} engines: {node: '>=4'} - dev: true + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 - /process-nextick-args/2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - /progress/2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 - /promise-inflight/1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - dev: false - optional: true - - /promise-retry/2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + /prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} + hasBin: true dependencies: - err-code: 2.0.3 - retry: 0.12.0 - dev: false - optional: true + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.56.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 - /proto-list/1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} dev: true - optional: true - /pump/3.0.0: + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true + + /pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + dev: true + + /pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: true - /punycode/2.1.1: - resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - requiresBuild: true dev: true - /pupa/2.1.1: - resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} - engines: {node: '>=8'} - dependencies: - escape-goat: 2.1.1 - dev: true - - /qs/6.10.5: - resolution: {integrity: sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ==} + /qs@6.12.0: + resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==} engines: {node: '>=0.6'} dependencies: - side-channel: 1.0.4 + side-channel: 1.0.6 dev: false - /raw-body/2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} dependencies: bytes: 3.1.2 @@ -2612,309 +4618,438 @@ packages: unpipe: 1.0.0 dev: false - /rc/1.2.8: + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true dependencies: deep-extend: 0.6.0 ini: 1.3.8 - minimist: 1.2.6 + minimist: 1.2.8 strip-json-comments: 2.0.1 - dev: true - /read-config-file/6.2.0: - resolution: {integrity: sha512-gx7Pgr5I56JtYz+WuqEbQHj/xWo+5Vwua2jhb1VwM4Wid5PqYmZ4i00ZB0YEGIfkVBsCv9UrjgyqCiQfS/Oosg==} - engines: {node: '>=12.0.0'} + /react-dom@18.2.0(react@18.2.0): + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 dependencies: - dotenv: 9.0.2 - dotenv-expand: 5.1.0 - js-yaml: 4.1.0 - json5: 2.2.1 - lazy-val: 1.0.5 - dev: true + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.0 + dev: false - /readable-stream/2.3.7: - resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + /react-remove-scroll-bar@2.3.6(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - dev: true + '@types/react': 18.2.71 + react: 18.2.0 + react-style-singleton: 2.2.1(@types/react@18.2.71)(react@18.2.0) + tslib: 2.6.2 + dev: false - /readable-stream/3.6.0: - resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} + /react-remove-scroll@2.5.5(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.71 + react: 18.2.0 + react-remove-scroll-bar: 2.3.6(@types/react@18.2.71)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.71)(react@18.2.0) + tslib: 2.6.2 + use-callback-ref: 1.3.2(@types/react@18.2.71)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.71)(react@18.2.0) + dev: false + + /react-resizable-panels@2.0.16(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UrnxmTZaTnbCl/xIOX38ig35RicqGfLuqt2x5fytpNlQvCRuxyXZwIBEhmF+pmrEGxfajyXFBoCplNxLvhF0CQ==} + peerDependencies: + react: ^16.14.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + + /react-style-singleton@2.2.1(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.71 + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 18.2.0 + tslib: 2.6.2 + dev: false + + /react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + dependencies: + loose-envify: 1.4.0 + dev: false + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - dev: false - /readdirp/3.6.0: + /readable-web-to-node-stream@3.0.2: + resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} + engines: {node: '>=8'} + dependencies: + readable-stream: 3.6.2 + dev: true + + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 - dev: false - /rechoir/0.7.0: - resolution: {integrity: sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==} + /recast@0.23.6: + resolution: {integrity: sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==} + engines: {node: '>= 4'} + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.6.2 + dev: true + + /rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} dependencies: - resolve: 1.22.1 + resolve: 1.22.8 + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} dev: false - /registry-auth-token/4.2.2: - resolution: {integrity: sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==} - engines: {node: '>=6.0.0'} - dependencies: - rc: 1.2.8 - dev: true - - /registry-url/5.1.0: - resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} - engines: {node: '>=8'} - dependencies: - rc: 1.2.8 - dev: true - - /require-directory/2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true - - /resolve-from/5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + /regexparam@3.0.0: + resolution: {integrity: sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==} engines: {node: '>=8'} dev: false - /resolve/1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.9.0 + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: false - /responselike/1.0.2: - resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} + /responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} dependencies: - lowercase-keys: 1.0.1 + lowercase-keys: 2.0.0 dev: true - /retry/0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: false - optional: true + /restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true - /rimraf/3.0.2: + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 + dev: true - /roarr/2.15.4: - resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} - engines: {node: '>=8.0'} + /rollup@4.13.0: + resolution: {integrity: sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true dependencies: - boolean: 3.2.0 - detect-node: 2.1.0 - globalthis: 1.0.3 - json-stringify-safe: 5.0.1 - semver-compare: 1.0.0 - sprintf-js: 1.1.2 - dev: true - optional: true - - /safe-buffer/5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.13.0 + '@rollup/rollup-android-arm64': 4.13.0 + '@rollup/rollup-darwin-arm64': 4.13.0 + '@rollup/rollup-darwin-x64': 4.13.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.13.0 + '@rollup/rollup-linux-arm64-gnu': 4.13.0 + '@rollup/rollup-linux-arm64-musl': 4.13.0 + '@rollup/rollup-linux-riscv64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-gnu': 4.13.0 + '@rollup/rollup-linux-x64-musl': 4.13.0 + '@rollup/rollup-win32-arm64-msvc': 4.13.0 + '@rollup/rollup-win32-ia32-msvc': 4.13.0 + '@rollup/rollup-win32-x64-msvc': 4.13.0 + fsevents: 2.3.3 dev: true - /safe-buffer/5.2.1: + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + + /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false - /safer-buffer/2.1.2: + /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false - /sanitize-filename/1.6.3: - resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} + /scheduler@0.23.0: + resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} dependencies: - truncate-utf8-bytes: 1.0.2 + loose-envify: 1.4.0 + dev: false + + /semver-regex@4.0.5: + resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} + engines: {node: '>=12'} dev: true - /sax/1.2.4: - resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - dev: true - - /semver-compare/1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - dev: true - optional: true - - /semver-diff/3.1.1: - resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} - engines: {node: '>=8'} + /semver-truncate@3.0.0: + resolution: {integrity: sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==} + engines: {node: '>=12'} dependencies: - semver: 6.3.0 + semver: 7.6.0 dev: true - /semver/5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true dev: false - /semver/6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + dev: true - /semver/7.3.7: - resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + /semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} engines: {node: '>=10'} hasBin: true dependencies: lru-cache: 6.0.0 - /serialize-error/7.0.1: - resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} - engines: {node: '>=10'} + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} dependencies: - type-fest: 0.13.1 - dev: true - optional: true - - /set-blocking/2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 dev: false - /setprototypeof/1.2.0: + /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} dev: false - /shebang-command/2.0.0: + /shadcn-ui@0.8.0(typescript@5.4.3): + resolution: {integrity: sha512-avqRgjJ6PIQQXdfvoCAWQpyLTLk6oHhtU5DQKmLeYcgu1ZIsgMqA9MKWAkr0HpEdCAenCCZvFbvJ2C2m5ZXRiA==} + hasBin: true + dependencies: + '@antfu/ni': 0.21.12 + '@babel/core': 7.24.3 + '@babel/parser': 7.24.1 + '@babel/plugin-transform-typescript': 7.24.1(@babel/core@7.24.3) + chalk: 5.2.0 + commander: 10.0.1 + cosmiconfig: 8.3.6(typescript@5.4.3) + diff: 5.2.0 + execa: 7.2.0 + fast-glob: 3.3.2 + fs-extra: 11.2.0 + https-proxy-agent: 6.2.1 + lodash.template: 4.5.0 + node-fetch: 3.3.2 + ora: 6.3.1 + prompts: 2.4.2 + recast: 0.23.6 + ts-morph: 18.0.0 + tsconfig-paths: 4.2.0 + zod: 3.22.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + + /shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} dev: true - /shebang-regex/3.0.0: + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + + /shelljs.exec@1.1.8: + resolution: {integrity: sha512-vFILCw+lzUtiwBAHV8/Ex8JsFjelFMdhONIsgKNLgTzeRckp2AOYRQtHJE/9LhNvdMmE27AGtzWx0+DHpwIwSw==} + engines: {node: '>= 4.0.0'} dev: true - /side-channel/1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + /shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.1.2 - object-inspect: 1.12.2 + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + dev: true + + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 dev: false - /signal-exit/3.0.7: + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - /slice-ansi/3.0.0: - resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} - engines: {node: '>=8'} - requiresBuild: true - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 dev: true - optional: true - /smart-buffer/4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - optional: true + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} - /socks-proxy-agent/6.2.1: - resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} - engines: {node: '>= 10'} + /simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + /simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - socks: 2.6.2 - transitivePeerDependencies: - - supports-color - dev: false - optional: true + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 - /socks/2.6.2: - resolution: {integrity: sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==} - engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + /simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} dependencies: - ip: 1.1.8 - smart-buffer: 4.2.0 - dev: false - optional: true + semver: 7.6.0 + dev: true - /source-map-support/0.5.21: + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /sort-keys-length@1.0.1: + resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} + engines: {node: '>=0.10.0'} + dependencies: + sort-keys: 1.1.2 + dev: true + + /sort-keys@1.1.2: + resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} + engines: {node: '>=0.10.0'} + dependencies: + is-plain-obj: 1.1.0 + dev: true + + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 dev: true - /source-map/0.6.1: + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} dev: true - /sprintf-js/1.1.2: - resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==} - dev: true - optional: true - - /sqlite3/5.0.8: - resolution: {integrity: sha512-f2ACsbSyb2D1qFFcqIXPfFscLtPVOWJr5GmUzYxf4W+0qelu5MWrR+FAQE1d5IUArEltBrzSDxDORG8P/IkqyQ==} - requiresBuild: true - peerDependenciesMeta: - node-gyp: - optional: true - dependencies: - '@mapbox/node-pre-gyp': 1.0.9 - node-addon-api: 4.3.0 - tar: 6.1.11 - optionalDependencies: - node-gyp: 8.4.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /ssri/8.0.1: - resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + /source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} - dependencies: - minipass: 3.3.3 - dev: false - optional: true - - /stat-mode/1.0.0: - resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} - engines: {node: '>= 6'} dev: true - /statuses/1.5.0: + /statuses@1.5.0: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} dev: false - /statuses/2.0.1: + /statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} dev: false - /string-width/4.2.3: + /stdin-discarder@0.1.0: + resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bl: 5.1.0 + dev: true + + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} dependencies: @@ -2922,177 +5057,292 @@ packages: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string_decoder/1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} dependencies: - safe-buffer: 5.1.2 - dev: true + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 - /string_decoder/1.3.0: + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 - dev: false - /strip-ansi/6.0.1: + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - /strip-json-comments/2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + dependencies: + ansi-regex: 6.0.1 + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} engines: {node: '>=0.10.0'} dev: true - /sumchecker/3.0.1: - resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} - engines: {node: '>= 8.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} dev: true - /supports-color/7.2.0: + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /strip-outer@2.0.0: + resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /strtok3@7.0.0: + resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} + engines: {node: '>=14.16'} + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 5.0.0 + dev: true + + /sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.3.12 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - dev: false - /tar/6.1.11: - resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==} - engines: {node: '>= 10'} + /swr@2.2.5(react@18.2.0): + resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 3.3.3 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 + client-only: 0.0.1 + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) dev: false - /tarn/3.0.2: - resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==} - engines: {node: '>=8.0.0'} - dev: false - - /temp-file/3.4.0: - resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} + /tailwind-merge@2.2.2: + resolution: {integrity: sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==} dependencies: - async-exit-hook: 2.0.1 - fs-extra: 10.1.0 + '@babel/runtime': 7.24.1 + dev: false + + /tailwindcss-animate@1.0.7(tailwindcss@3.4.3): + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + tailwindcss: 3.4.3 + dev: false + + /tailwindcss@3.4.3: + resolution: {integrity: sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.38 + postcss-import: 15.1.0(postcss@8.4.38) + postcss-js: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.2(postcss@8.4.38) + postcss-nested: 6.0.1(postcss@8.4.38) + postcss-selector-parser: 6.0.16 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + /tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + + /tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true - /tildify/2.0.0: - resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} - engines: {node: '>=8'} - dev: false + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 - /tiny-async-pool/1.3.0: + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + + /tiny-async-pool@1.3.0: resolution: {integrity: sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==} dependencies: - semver: 5.7.1 + semver: 5.7.2 dev: false - /tmp-promise/3.0.3: - resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} - dependencies: - tmp: 0.2.1 + /tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} dev: true - /tmp/0.2.1: - resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} - engines: {node: '>=8.17.0'} - dependencies: - rimraf: 3.0.2 + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} dev: true - /to-readable-stream/1.0.0: - resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==} - engines: {node: '>=6'} - dev: true - - /to-regex-range/5.0.1: + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: false - /toidentifier/1.0.1: + /toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} dev: false - /tr46/0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: false - - /truncate-utf8-bytes/1.0.2: - resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} + /token-types@5.0.1: + resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==} + engines: {node: '>=14.16'} dependencies: - utf8-byte-length: 1.0.4 + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 dev: true - /ts-json-schema-generator/0.82.0: - resolution: {integrity: sha512-g5/h3jmN7DqYTOx7OhTvWS638CJWzyoqlsKwe2eksvK8mnFHTnOZGvZITmPkRDPFUFwSUMo0J+Vm3A+Y8EDsEg==} - engines: {node: '>=10.0.0'} + /touch@3.1.0: + resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} hasBin: true dependencies: - '@types/json-schema': 7.0.11 - commander: 6.2.1 - fast-json-stable-stringify: 2.1.0 - glob: 7.2.3 - json-stable-stringify: 1.0.1 - typescript: 4.1.6 + nopt: 1.0.10 dev: true - /ts-node/9.1.1_typescript@4.7.4: - resolution: {integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==} - engines: {node: '>=10.0.0'} - hasBin: true + /trim-repeated@2.0.0: + resolution: {integrity: sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==} + engines: {node: '>=12'} + dependencies: + escape-string-regexp: 5.0.0 + dev: true + + /ts-api-utils@1.3.0(typescript@5.4.3): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} peerDependencies: - typescript: '>=2.7' + typescript: '>=4.2.0' dependencies: - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - source-map-support: 0.5.21 - typescript: 4.7.4 - yn: 3.1.1 + typescript: 5.4.3 dev: true - /tsscmp/1.0.6: + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + /ts-morph@18.0.0: + resolution: {integrity: sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==} + dependencies: + '@ts-morph/common': 0.19.0 + code-block-writer: 12.0.0 + dev: true + + /tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + /tsscmp@1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} engines: {node: '>=0.6.x'} dev: false - /tunnel/0.0.6: - resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} - engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - dev: true - optional: true + /tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + dependencies: + safe-buffer: 5.2.1 - /type-fest/0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 dev: true - optional: true - /type-fest/0.20.2: + /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} dev: true - /type-is/1.6.18: + /type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} dependencies: @@ -3100,229 +5350,221 @@ packages: mime-types: 2.1.35 dev: false - /typedarray-to-buffer/3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - dependencies: - is-typedarray: 1.0.0 - dev: true - - /typedarray/0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - dev: true - - /typescript/4.1.6: - resolution: {integrity: sha512-pxnwLxeb/Z5SP80JDRzVjh58KsM6jZHRAOtTpS7sXLS4ogXNKC9ANxHHZqLLeVHZN35jCtI4JdmLLbLiC1kBow==} - engines: {node: '>=4.2.0'} + /typescript@5.4.3: + resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} + engines: {node: '>=14.17'} hasBin: true dev: true - /typescript/4.7.4: - resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} - engines: {node: '>=4.2.0'} - hasBin: true + /undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} dev: true - /unique-filename/1.1.1: - resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} - dependencies: - unique-slug: 2.0.2 - dev: false - optional: true - - /unique-slug/2.0.2: - resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} - dependencies: - imurmurhash: 0.1.4 - dev: false - optional: true - - /unique-string/2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - dependencies: - crypto-random-string: 2.0.0 + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true - /universalify/0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true - - /universalify/2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} dev: true - /unpipe/1.0.0: + /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} dev: false - /update-notifier/5.1.0: - resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} - engines: {node: '>=10'} + /update-browserslist-db@1.0.13(browserslist@4.23.0): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' dependencies: - boxen: 5.1.2 - chalk: 4.1.2 - configstore: 5.0.1 - has-yarn: 2.1.0 - import-lazy: 2.1.0 - is-ci: 2.0.0 - is-installed-globally: 0.4.0 - is-npm: 5.0.0 - is-yarn-global: 0.3.0 - latest-version: 5.1.0 - pupa: 2.1.1 - semver: 7.3.7 - semver-diff: 3.1.1 - xdg-basedir: 4.0.0 + browserslist: 4.23.0 + escalade: 3.1.2 + picocolors: 1.0.0 dev: true - /uri-js/4.4.1: + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - requiresBuild: true dependencies: - punycode: 2.1.1 + punycode: 2.3.1 dev: true - /url-parse-lax/3.0.0: - resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} - engines: {node: '>=4'} + /use-callback-ref@1.3.2(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true dependencies: - prepend-http: 2.0.0 - dev: true + '@types/react': 18.2.71 + react: 18.2.0 + tslib: 2.6.2 + dev: false - /utf8-byte-length/1.0.4: - resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==} - dev: true + /use-sidecar@1.1.2(@types/react@18.2.71)(react@18.2.0): + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.2.71 + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.6.2 + dev: false - /util-deprecate/1.0.2: + /use-sync-external-store@1.2.0(react@18.2.0): + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + + /usehooks-ts@3.1.0(react@18.2.0): + resolution: {integrity: sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw==} + engines: {node: '>=16.15.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + dependencies: + lodash.debounce: 4.0.8 + react: 18.2.0 + dev: false + + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - /vary/1.1.2: + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} dev: false - /verror/1.10.1: - resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} - engines: {node: '>=0.6.0'} - requiresBuild: true + /vite@5.2.6(@types/node@20.12.3): + resolution: {integrity: sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.4.1 + '@types/node': 20.12.3 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 dev: true - optional: true - /webidl-conversions/3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: false - - /whatwg-url/5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: false + defaults: 1.0.4 + dev: true - /which/2.0.2: + /web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + dev: true + + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true dependencies: isexe: 2.0.0 - /wide-align/1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + /wouter@3.1.0(react@18.2.0): + resolution: {integrity: sha512-hou3w+12BMTBckdWdyJp/z7+kKcbdLDWfz6omSyrO6bbx4irNuQQyLDQkfSGXXJCxmglea3c8On9XFUkBSU8+Q==} + peerDependencies: + react: '>=16.8.0' dependencies: - string-width: 4.2.3 + mitt: 3.0.1 + react: 18.2.0 + regexparam: 3.0.0 + use-sync-external-store: 1.2.0(react@18.2.0) dev: false - /widest-line/3.1.0: - resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} - engines: {node: '>=8'} - dependencies: - string-width: 4.2.3 - dev: true - - /wrap-ansi/7.0.0: + /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrappy/1.0.2: + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - /write-file-atomic/3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - dependencies: - imurmurhash: 0.1.4 - is-typedarray: 1.0.0 - signal-exit: 3.0.7 - typedarray-to-buffer: 3.1.5 + /yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} dev: true - /xdg-basedir/4.0.0: - resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} - engines: {node: '>=8'} + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true - /xmlbuilder/15.1.1: - resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} - engines: {node: '>=8.0'} - requiresBuild: true - dev: true - optional: true - - /xmlbuilder/9.0.7: - resolution: {integrity: sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==} - engines: {node: '>=4.0'} - requiresBuild: true - dev: true - - /y18n/5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true - - /yallist/4.0.0: + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - /yargs-parser/21.0.1: - resolution: {integrity: sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==} - engines: {node: '>=12'} - dev: true + /yaml@2.4.1: + resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} + engines: {node: '>= 14'} + hasBin: true - /yargs/17.5.1: - resolution: {integrity: sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==} - engines: {node: '>=12'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.0.1 - dev: true - - /yauzl/2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - - /ylru/1.3.2: + /ylru@1.3.2: resolution: {integrity: sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==} engines: {node: '>= 4.0.0'} dev: false - /yn/3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /zod@3.22.4: + resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..4340350 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - 'packages/*' \ No newline at end of file diff --git a/preload.ts b/preload.ts deleted file mode 100644 index 05b82f8..0000000 --- a/preload.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { contextBridge, ipcRenderer } from "electron"; - -contextBridge.exposeInMainWorld("electron", { - passwordReset: async (username: string, toPw: string) => { - return await ipcRenderer.invoke("reset_password", username, toPw); - }, -}); diff --git a/src/SettingConfig.schema.json b/src/SettingConfig.schema.json deleted file mode 100644 index 0ff349b..0000000 --- a/src/SettingConfig.schema.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/SettingConfig", - "definitions": { - "SettingConfig": { - "type": "object", - "properties": { - "localmode": { - "type": "boolean", - "description": "if true, server will bind on '127.0.0.1' rather than '0.0.0.0'" - }, - "guest": { - "type": "array", - "items": { - "$ref": "#/definitions/Permission" - }, - "description": "guest permission" - }, - "jwt_secretkey": { - "type": "string", - "description": "JWT secret key. if you change its value, all access tokens are invalidated." - }, - "port": { - "type": "number", - "description": "the port which running server is binding on." - }, - "mode": { - "type": "string", - "enum": [ - "development", - "production" - ] - }, - "cli": { - "type": "boolean", - "description": "if true, do not show 'electron' window and show terminal only." - }, - "forbid_remote_admin_login": { - "type": "boolean", - "description": "forbid to login admin from remote client. but, it do not invalidate access token. \r if you want to invalidate access token, change 'jwt_secretkey'." - }, - "$schema": { - "type": "string" - } - }, - "required": [ - "localmode", - "guest", - "jwt_secretkey", - "port", - "mode", - "cli", - "forbid_remote_admin_login" - ], - "additionalProperties": false - }, - "Permission": { - "type": "string", - "enum": [ - "ModifyTag", - "QueryContent", - "ModifyTagDesc" - ] - } - } -} diff --git a/src/SettingConfig.ts b/src/SettingConfig.ts deleted file mode 100644 index 0f8b080..0000000 --- a/src/SettingConfig.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { randomBytes } from "crypto"; -import { existsSync, readFileSync, writeFileSync } from "fs"; -import { Permission } from "./permission/permission"; - -export interface SettingConfig { - /** - * if true, server will bind on '127.0.0.1' rather than '0.0.0.0' - */ - localmode: boolean; - /** - * secure only - */ - secure: boolean; - - /** - * guest permission - */ - guest: (Permission)[]; - /** - * JWT secret key. if you change its value, all access tokens are invalidated. - */ - jwt_secretkey: string; - /** - * the port which running server is binding on. - */ - port: number; - - mode: "development" | "production"; - /** - * if true, do not show 'electron' window and show terminal only. - */ - cli: boolean; - /** forbid to login admin from remote client. but, it do not invalidate access token. - * if you want to invalidate access token, change 'jwt_secretkey'. */ - forbid_remote_admin_login: boolean; -} -const default_setting: SettingConfig = { - localmode: true, - secure: true, - guest: [], - jwt_secretkey: "itsRandom", - port: 8080, - mode: "production", - cli: false, - forbid_remote_admin_login: true, -}; -let setting: null | SettingConfig = null; - -const setEmptyToDefault = (target: any, default_table: SettingConfig) => { - let diff_occur = false; - for (const key in default_table) { - if (key === undefined || key in target) { - continue; - } - target[key] = default_table[key as keyof SettingConfig]; - diff_occur = true; - } - return diff_occur; -}; - -export const read_setting_from_file = () => { - let ret = existsSync("settings.json") ? JSON.parse(readFileSync("settings.json", { encoding: "utf8" })) : {}; - const partial_occur = setEmptyToDefault(ret, default_setting); - if (partial_occur) { - writeFileSync("settings.json", JSON.stringify(ret)); - } - return ret as SettingConfig; -}; -export function get_setting(): SettingConfig { - if (setting === null) { - setting = read_setting_from_file(); - const env = process.env.NODE_ENV; - if (env !== undefined && (env != "production" && env != "development")) { - throw new Error("process unknown value in NODE_ENV: must be either \"development\" or \"production\""); - } - setting.mode = env ?? setting.mode; - } - return setting; -} diff --git a/src/client/accessor/document.ts b/src/client/accessor/document.ts deleted file mode 100644 index 21046b0..0000000 --- a/src/client/accessor/document.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Document, DocumentAccessor, DocumentBody, QueryListOption } from "../../model/doc"; -import { toQueryString } from "./util"; -const baseurl = "/api/doc"; - -export * from "../../model/doc"; - -export class FetchFailError extends Error {} - -export class ClientDocumentAccessor implements DocumentAccessor { - search: (search_word: string) => Promise; - addList: (content_list: DocumentBody[]) => Promise; - async findByPath(basepath: string, filename?: string): Promise { - throw new Error("not allowed"); - } - async findDeleted(content_type: string): Promise { - throw new Error("not allowed"); - } - async findList(option?: QueryListOption | undefined): Promise { - let res = await fetch(`${baseurl}/search?${option !== undefined ? toQueryString(option) : ""}`); - if (res.status == 401) throw new FetchFailError("Unauthorized"); - if (res.status !== 200) throw new FetchFailError("findList Failed"); - let ret = await res.json(); - return ret; - } - async findById(id: number, tagload?: boolean | undefined): Promise { - let res = await fetch(`${baseurl}/${id}`); - if (res.status !== 200) throw new FetchFailError("findById Failed"); - let ret = await res.json(); - return ret; - } - /** - * not implement - */ - async findListByBasePath(basepath: string): Promise { - throw new Error("not implement"); - return []; - } - async update(c: Partial & { id: number }): Promise { - const { id, ...rest } = c; - const res = await fetch(`${baseurl}/${id}`, { - method: "POST", - body: JSON.stringify(rest), - headers: { - "content-type": "application/json", - }, - }); - const ret = await res.json(); - return ret; - } - async add(c: DocumentBody): Promise { - throw new Error("not allow"); - const res = await fetch(`${baseurl}`, { - method: "POST", - body: JSON.stringify(c), - headers: { - "content-type": "application/json", - }, - }); - const ret = await res.json(); - return ret; - } - async del(id: number): Promise { - const res = await fetch(`${baseurl}/${id}`, { - method: "DELETE", - }); - const ret = await res.json(); - return ret; - } - async addTag(c: Document, tag_name: string): Promise { - const { id, ...rest } = c; - const res = await fetch(`${baseurl}/${id}/tags/${tag_name}`, { - method: "POST", - body: JSON.stringify(rest), - headers: { - "content-type": "application/json", - }, - }); - const ret = await res.json(); - return ret; - } - async delTag(c: Document, tag_name: string): Promise { - const { id, ...rest } = c; - const res = await fetch(`${baseurl}/${id}/tags/${tag_name}`, { - method: "DELETE", - body: JSON.stringify(rest), - headers: { - "content-type": "application/json", - }, - }); - const ret = await res.json(); - return ret; - } -} -export const CDocumentAccessor = new ClientDocumentAccessor(); -export const makeThumbnailUrl = (x: Document) => { - return `${baseurl}/${x.id}/${x.content_type}/thumbnail`; -}; - -export default CDocumentAccessor; diff --git a/src/client/accessor/util.ts b/src/client/accessor/util.ts deleted file mode 100644 index d2fa9ce..0000000 --- a/src/client/accessor/util.ts +++ /dev/null @@ -1,32 +0,0 @@ -type Representable = string | number | boolean; - -type ToQueryStringA = { - [name: string]: Representable | Representable[] | undefined; -}; - -export const toQueryString = (obj: ToQueryStringA) => { - return Object.entries(obj) - .filter((e): e is [string, Representable | Representable[]] => e[1] !== undefined) - .map(e => - e[1] instanceof Array - ? e[1].map(f => `${e[0]}=${(f)}`).join("&") - : `${e[0]}=${(e[1])}` - ) - .join("&"); -}; -export const QueryStringToMap = (query: string) => { - const keyValue = query.slice(query.indexOf("?") + 1).split("&"); - const param: { [k: string]: string | string[] } = {}; - keyValue.forEach((p) => { - const [k, v] = p.split("="); - const pv = param[k]; - if (pv === undefined) { - param[k] = v; - } else if (typeof pv === "string") { - param[k] = [pv, v]; - } else { - pv.push(v); - } - }); - return param; -}; diff --git a/src/client/app.tsx b/src/client/app.tsx deleted file mode 100644 index e69a9ed..0000000 --- a/src/client/app.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { createTheme, ThemeProvider } from "@mui/material"; -import React, { createContext, useEffect, useRef, useState } from "react"; -import ReactDom from "react-dom"; -import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom"; -import { - DifferencePage, - DocumentAbout, - Gallery, - LoginPage, - NotFoundPage, - ProfilePage, - ReaderPage, - SettingPage, - TagsPage, -} from "./page/mod"; -import { getInitialValue, UserContext } from "./state"; - -import "./css/style.css"; - -const theme = createTheme(); - -const App = () => { - const [user, setUser] = useState(""); - const [userPermission, setUserPermission] = useState([]); - (async () => { - const { username, permission } = await getInitialValue(); - if (username !== user) { - setUser(username); - setUserPermission(permission); - } - })(); - // useEffect(()=>{}); - return ( - - - - - } /> - } /> - }> - }> - } /> - }> - }> - }> - }> - } /> - - - - - ); -}; - -ReactDom.render( - , - document.getElementById("root"), -); diff --git a/src/client/build.ts b/src/client/build.ts deleted file mode 100644 index de54a07..0000000 --- a/src/client/build.ts +++ /dev/null @@ -1,32 +0,0 @@ -import esbuild from "esbuild"; - -async function main() { - try { - const result = await esbuild.build({ - entryPoints: ["app.tsx"], - bundle: true, - outfile: "../../dist/bundle.js", - platform: "browser", - sourcemap: true, - minify: true, - target: ["chrome100", "firefox100"], - watch: { - onRebuild: async (err, _result) => { - if (err) { - console.error("watch build failed: ", err); - } else { - console.log("watch build success"); - } - }, - }, - }); - console.log("watching..."); - return result; - } catch (error) { - console.error(error); - process.exit(1); - } -} - -main().then((res) => { -}); diff --git a/src/client/component/contentinfo.tsx b/src/client/component/contentinfo.tsx deleted file mode 100644 index 8f699b9..0000000 --- a/src/client/component/contentinfo.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import React, {} from "react"; -import { Link as RouterLink } from "react-router-dom"; -import { Document } from "../accessor/document"; - -import { Box, Button, Grid, Link, Paper, Theme, Typography, useTheme } from "@mui/material"; -import { TagChip } from "../component/tagchip"; -import { ThumbnailContainer } from "../page/reader/reader"; - -import DocumentAccessor from "../accessor/document"; - -export const makeContentInfoUrl = (id: number) => `/doc/${id}`; -export const makeContentReaderUrl = (id: number) => `/doc/${id}/reader`; - -const useStyles = (theme: Theme) => ({ - thumbnail_content: { - maxHeight: "400px", - maxWidth: "min(400px, 100vw)", - }, - tag_list: { - display: "flex", - justifyContent: "flex-start", - flexWrap: "wrap", - overflowY: "hidden", - "& > *": { - margin: theme.spacing(0.5), - }, - }, - title: { - marginLeft: theme.spacing(1), - }, - infoContainer: { - padding: theme.spacing(2), - }, - subinfoContainer: { - display: "grid", - gridTemplateColumns: "100px auto", - overflowY: "hidden", - alignItems: "baseline", - }, - short_subinfoContainer: { - [theme.breakpoints.down("md")]: { - display: "none", - }, - }, - short_root: { - overflowY: "hidden", - display: "flex", - flexDirection: "column", - [theme.breakpoints.up("sm")]: { - height: 200, - flexDirection: "row", - }, - }, - short_thumbnail_anchor: { - background: "#272733", - display: "flex", - alignItems: "center", - justifyContent: "center", - [theme.breakpoints.up("sm")]: { - width: theme.spacing(25), - height: theme.spacing(25), - flexShrink: 0, - }, - }, - short_thumbnail_content: { - maxWidth: "100%", - maxHeight: "100%", - }, -}); - -export const ContentInfo = (props: { - document: Document; - children?: React.ReactNode; - classes?: { - root?: string; - thumbnail_anchor?: string; - thumbnail_content?: string; - tag_list?: string; - title?: string; - infoContainer?: string; - subinfoContainer?: string; - }; - gallery?: string; - short?: boolean; -}) => { - const theme = useTheme(); - const document = props.document; - const url = props.gallery === undefined ? makeContentReaderUrl(document.id) : makeContentInfoUrl(document.id); - return ( - - - {document.deleted_at === null - ? - : Deleted} - - - - {document.title} - - - {props.short - ? ( - - {document.tags.map(x => ( - - ))} - - ) - : ( - - )} - - {document.deleted_at != null - && ( - - )} - - - ); -}; -async function documentDelete(id: number) { - const t = await DocumentAccessor.del(id); - if (t) { - alert("document deleted!"); - } else { - alert("document already deleted."); - } -} - -function ComicDetailTag(prop: { - tags: string[]; /*classes:{ - tag_list:string -}*/ - path?: string; - createdAt?: number; - deletedAt?: number; -}) { - let allTag = prop.tags; - const tagKind = ["artist", "group", "series", "type", "character"]; - let tagTable: { [kind: string]: string[] } = {}; - for (const kind of tagKind) { - const tags = allTag.filter(x => x.startsWith(kind + ":")).map(x => x.slice(kind.length + 1)); - tagTable[kind] = tags; - allTag = allTag.filter(x => !x.startsWith(kind + ":")); - } - return ( - - {tagKind.map(key => ( - - - {key} - - - {tagTable[key].length !== 0 ? tagTable[key].map((elem, i)=>{ - return <> - {elem} - {(i < tagTable[key].length - 1) ? ',' : ''} - }) : "N/A"} - - - ))} - {prop.path != undefined && ( - <> - - Path - - - {prop.path} - - - )} - {prop.createdAt != undefined && ( - <> - - CreatedAt - - - {new Date(prop.createdAt).toUTCString()} - - - )} - {prop.deletedAt != undefined && ( - <> - - DeletedAt - - - {new Date(prop.deletedAt).toUTCString()} - - - )} - - Tags - - - {allTag.map(x => )} - - - ); -} diff --git a/src/client/component/headline.tsx b/src/client/component/headline.tsx deleted file mode 100644 index 248ada0..0000000 --- a/src/client/component/headline.tsx +++ /dev/null @@ -1,273 +0,0 @@ -import { AccountCircle, ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon } from "@mui/icons-material"; -import { - AppBar, - Button, - CssBaseline, - Divider, - Drawer, - Hidden, - IconButton, - InputBase, - Link, - List, - ListItem, - ListItemIcon, - ListItemText, - Menu, - MenuItem, - styled, - Toolbar, - Tooltip, - Typography, -} from "@mui/material"; -import { alpha, Theme, useTheme } from "@mui/material/styles"; -import React, { useContext, useState } from "react"; - -import { Link as RouterLink, useNavigate } from "react-router-dom"; -import { doLogout, UserContext } from "../state"; - -const drawerWidth = 270; - -const DrawerHeader = styled("div")(({ theme }) => ({ - ...theme.mixins.toolbar, -})); - -const StyledDrawer = styled(Drawer)(({ theme }) => ({ - flexShrink: 0, - whiteSpace: "nowrap", - [theme.breakpoints.up("sm")]: { - width: drawerWidth, - }, -})); -const StyledSearchBar = styled("div")(({ theme }) => ({ - position: "relative", - borderRadius: theme.shape.borderRadius, - backgroundColor: alpha(theme.palette.common.white, 0.15), - "&:hover": { - backgroundColor: alpha(theme.palette.common.white, 0.25), - }, - marginLeft: 0, - width: "100%", - [theme.breakpoints.up("sm")]: { - marginLeft: theme.spacing(1), - width: "auto", - }, -})); -const StyledInputBase = styled(InputBase)(({ theme }) => ({ - color: "inherit", - "& .MuiInputBase-input": { - padding: theme.spacing(1, 1, 1, 0), - // vertical padding + font size from searchIcon - paddingLeft: `calc(1em + ${theme.spacing(4)})`, - transition: theme.transitions.create("width"), - width: "100%", - [theme.breakpoints.up("sm")]: { - width: "12ch", - "&:focus": { - width: "20ch", - }, - }, - }, -})); - -const StyledNav = styled("nav")(({ theme }) => ({ - [theme.breakpoints.up("sm")]: { - width: theme.spacing(7), - }, -})); - -const closedMixin = (theme: Theme) => ({ - overflowX: "hidden", - width: `calc(${theme.spacing(7)} + 1px)`, -}); - -export const Headline = (prop: { - children?: React.ReactNode; - classes?: { - content?: string; - toolbar?: string; - }; - rightAppbar?: React.ReactNode; - menu: React.ReactNode; -}) => { - const [v, setv] = useState(false); - const [anchorEl, setAnchorEl] = React.useState(null); - const theme = useTheme(); - const toggleV = () => setv(!v); - const handleProfileMenuOpen = (e: React.MouseEvent) => setAnchorEl(e.currentTarget); - const handleProfileMenuClose = () => setAnchorEl(null); - const isProfileMenuOpened = Boolean(anchorEl); - const menuId = "primary-search-account-menu"; - const user_ctx = useContext(UserContext); - const isLogin = user_ctx.username !== ""; - const navigate = useNavigate(); - const [search, setSearch] = useState(""); - - const renderProfileMenu = ( - - Profile - { - handleProfileMenuClose(); - await doLogout(); - user_ctx.setUsername(""); - }} - > - Logout - - - ); - const drawer_contents = ( - <> - - - {theme.direction === "ltr" ? : } - - - - {prop.menu} - - ); - - return ( -
    - - - - - - - - Ionian - -
    - {prop.rightAppbar} - -
    - navSearch(search)} /> -
    - setSearch(e.target.value)} - onKeyUp={(e) => { - if (e.key === "Enter") { - navSearch(search); - } - }} - value={search} - /> -
    - {isLogin - ? ( - - - - ) - : } -
    -
    - {renderProfileMenu} - - - - {drawer_contents} - - - - - {drawer_contents} - - - -
    {prop.children} -
    -
    - ); - function navSearch(search: string) { - let words = search.includes("&") ? search.split("&") : [search]; - words = words.map(w => w.trim()) - .map(w => - w.includes(":") - ? `allow_tag=${w}` - : `word=${encodeURIComponent(w)}` - ); - navigate(`/search?${words.join("&")}`); - } -}; - -export default Headline; diff --git a/src/client/component/loading.tsx b/src/client/component/loading.tsx deleted file mode 100644 index 6aa9172..0000000 --- a/src/client/component/loading.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Box, CircularProgress } from "@mui/material"; -import React from "react"; - -export const LoadingCircle = () => { - return ( - - - - ); -}; diff --git a/src/client/component/mod.ts b/src/client/component/mod.ts deleted file mode 100644 index a92d73a..0000000 --- a/src/client/component/mod.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./contentinfo"; -export * from "./headline"; -export * from "./loading"; -export * from "./navlist"; -export * from "./tagchip"; diff --git a/src/client/component/navlist.tsx b/src/client/component/navlist.tsx deleted file mode 100644 index c7af904..0000000 --- a/src/client/component/navlist.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - ArrowBack as ArrowBackIcon, - Collections as CollectionIcon, - Folder as FolderIcon, - Home as HomeIcon, - List as ListIcon, - Settings as SettingIcon, - VideoLibrary as VideoIcon, -} from "@mui/icons-material"; -import { Divider, List, ListItem, ListItemIcon, ListItemText, Tooltip } from "@mui/material"; -import React from "react"; -import { Link as RouterLink } from "react-router-dom"; - -export const NavItem = (props: { name: string; to: string; icon: React.ReactElement }) => { - return ( - - - - {props.icon} - - - - - ); -}; - -export const NavList = (props: { children?: React.ReactNode }) => { - return ( - - {props.children} - - ); -}; - -export const BackItem = (props: { to?: string }) => { - return } />; -}; - -export function CommonMenuList(props?: { url?: string }) { - let url = props?.url ?? ""; - return ( - - {url !== "" && ( - <> - - - )} - } /> - }> - } /> - - } /> - - }> - } /> - - ); -} diff --git a/src/client/component/pagepad.tsx b/src/client/component/pagepad.tsx deleted file mode 100644 index 93cf098..0000000 --- a/src/client/component/pagepad.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { styled } from "@mui/material"; - -export const PagePad = styled("div")(({theme})=>({ - padding: theme.spacing(3) -})) \ No newline at end of file diff --git a/src/client/component/tagchip.tsx b/src/client/component/tagchip.tsx deleted file mode 100644 index 3e84519..0000000 --- a/src/client/component/tagchip.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import * as colors from "@mui/material/colors"; -import Chip, { ChipTypeMap } from "@mui/material/Chip"; -import { emphasize, styled, Theme, useTheme } from "@mui/material/styles"; -import React from "react"; -import { Link as RouterLink } from "react-router-dom"; - -type TagChipStyleProp = { - color: `rgba(${number},${number},${number},${number})` | `#${string}` | 'default'; -}; - -const { blue, pink } = colors; -const getTagColorName = (tagname: string): TagChipStyleProp['color'] => { - if (tagname.startsWith("female")) { - return pink[600]; - } else if (tagname.startsWith("male")) { - return blue[600]; - } else return "default"; -}; - -type ColorChipProp = Omit & TagChipStyleProp & { - component?: React.ElementType; - to?: string; -}; - -export const ColorChip = (props: ColorChipProp) => { - const { color, ...rest } = props; - const theme = useTheme(); - - let newcolor = color; - if (color === "default"){ - newcolor = "#ebebeb"; - } - return ; -}; - -type TagChipProp = Omit & { - tagname: string; -}; - -export const TagChip = (props: TagChipProp) => { - const { tagname, label, clickable, ...rest } = props; - const colorName = getTagColorName(tagname); - - let newlabel: React.ReactNode = label; - if (typeof label === "string") { - const female = "female:"; - const male = "male:"; - if (label.startsWith(female)) { - newlabel = "♀ " + label.slice(female.length); - } else if (label.startsWith(male)) { - newlabel = "♂ " + label.slice(male.length); - } - } - - const inner = clickable - ? ( - - ) - : ( - - ); - return inner; -}; diff --git a/src/client/css/style.css b/src/client/css/style.css deleted file mode 100644 index 257cfc8..0000000 --- a/src/client/css/style.css +++ /dev/null @@ -1,9 +0,0 @@ -body { - margin: 0; - padding: 0; - font-family: sans-serif; -} - -h1 { - text-decoration: underline; -} \ No newline at end of file diff --git a/src/client/package.json b/src/client/package.json deleted file mode 100644 index 3831fab..0000000 --- a/src/client/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "ionian_client", - "version": "0.0.1", - "description": "client of ionian", - "scripts": { - "build:watch": "ts-node build.ts" - }, - "dependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/icons-material": "^5.6.2", - "@mui/material": "^5.6.2", - "@mui/x-data-grid": "^5.12.3", - "@types/react": "^18.0.5", - "@types/react-dom": "^18.0.1", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "react-router-dom": "^6.3.0" - }, - "devDependencies": { - "esbuild": "^0.14.36", - "ts-node": "^10.7.0" - } -} diff --git a/src/client/page/404.tsx b/src/client/page/404.tsx deleted file mode 100644 index 98e7d84..0000000 --- a/src/client/page/404.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Typography } from "@mui/material"; -import React from "react"; -import { CommonMenuList, Headline } from "../component/mod"; -import { PagePad } from "../component/pagepad"; - -export const NotFoundPage = () => { - const menu = CommonMenuList(); - return ( - - - 404 Not Found - - - ); -}; diff --git a/src/client/page/contentinfo.tsx b/src/client/page/contentinfo.tsx deleted file mode 100644 index 48756ee..0000000 --- a/src/client/page/contentinfo.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { IconButton, Theme, Typography } from "@mui/material"; -import FullscreenIcon from '@mui/icons-material/Fullscreen'; -import React, { useEffect, useRef, useState } from "react"; -import { Route, Routes, useLocation, useParams } from "react-router-dom"; -import DocumentAccessor, { Document } from "../accessor/document"; -import { LoadingCircle } from "../component/loading"; -import { CommonMenuList, ContentInfo, Headline } from "../component/mod"; -import { NotFoundPage } from "./404"; -import { getPresenter } from "./reader/reader"; -import { PagePad } from "../component/pagepad"; - -export const makeContentInfoUrl = (id: number) => `/doc/${id}`; -export const makeComicReaderUrl = (id: number) => `/doc/${id}/reader`; - -type DocumentState = { - doc: Document | undefined; - notfound: boolean; -}; - -export function ReaderPage(props?: {}) { - const location = useLocation(); - const match = useParams<{ id: string }>(); - if (match == null) { - throw new Error("unreachable"); - } - const id = Number.parseInt(match.id ?? "NaN"); - const [info, setInfo] = useState({ doc: undefined, notfound: false }); - const menu_list = (link?: string) => ; - const fullScreenTargetRef = useRef(null); - - useEffect(() => { - (async () => { - if (!isNaN(id)) { - const c = await DocumentAccessor.findById(id); - setInfo({ doc: c, notfound: c === undefined }); - } - })(); - }, []); - - if (isNaN(id)) { - return ( - - Oops. Invalid ID - - ); - } else if (info.notfound) { - return ( - - Content has been removed. - - ); - } else if (info.doc === undefined) { - return ( - - - - ); - } else { - const ReaderPage = getPresenter(info.doc); - return ( - { - if (fullScreenTargetRef.current != null && document.fullscreenEnabled) { - fullScreenTargetRef.current.requestFullscreen(); - } - }} - color="inherit"> - - }> - - - ); - } -} - -export const DocumentAbout = (prop?: {}) => { - const match = useParams<{ id: string }>(); - if (match == null) { - throw new Error("unreachable"); - } - const id = Number.parseInt(match.id ?? "NaN"); - const [info, setInfo] = useState({ doc: undefined, notfound: false }); - const menu_list = (link?: string) => ; - - useEffect(() => { - (async () => { - if (!isNaN(id)) { - const c = await DocumentAccessor.findById(id); - setInfo({ doc: c, notfound: c === undefined }); - } - })(); - }, []); - - if (isNaN(id)) { - return ( - - - Oops. Invalid ID - - - ); - } else if (info.notfound) { - return ( - - - Content has been removed. - - - ); - } else if (info.doc === undefined) { - return ( - - - - - - ); - } else { - return ( - - - - - - ); - } -}; diff --git a/src/client/page/difference.tsx b/src/client/page/difference.tsx deleted file mode 100644 index 1cf82de..0000000 --- a/src/client/page/difference.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { Box, Button, Paper, Typography } from "@mui/material"; -import React, { useContext, useEffect, useState } from "react"; -import { CommonMenuList, Headline } from "../component/mod"; -import { UserContext } from "../state"; -import { PagePad } from "../component/pagepad"; - -type FileDifference = { - type: string; - value: { - type: string; - path: string; - }[]; -}; - -function TypeDifference(prop: { - content: FileDifference; - onCommit: (v: { type: string; path: string }) => void; - onCommitAll: (type: string) => void; -}) { - // const classes = useStyles(); - const x = prop.content; - const [button_disable, set_disable] = useState(false); - - return ( - - - {x.type} - - - {x.value.map(y => ( - - - {y.path} - - ))} - - ); -} - -export function DifferencePage() { - const ctx = useContext(UserContext); - // const classes = useStyles(); - const [diffList, setDiffList] = useState< - FileDifference[] - >([]); - const doLoad = async () => { - const list = await fetch("/api/diff/list"); - if (list.ok) { - const inner = await list.json(); - setDiffList(inner); - } else { - // setDiffList([]); - } - }; - const Commit = async (x: { type: string; path: string }) => { - const res = await fetch("/api/diff/commit", { - method: "POST", - body: JSON.stringify([{ ...x }]), - headers: { - "content-type": "application/json", - }, - }); - const bb = await res.json(); - if (bb.ok) { - doLoad(); - } else { - console.error("fail to add document"); - } - }; - const CommitAll = async (type: string) => { - const res = await fetch("/api/diff/commitall", { - method: "POST", - body: JSON.stringify({ type: type }), - headers: { - "content-type": "application/json", - }, - }); - const bb = await res.json(); - if (bb.ok) { - doLoad(); - } else { - console.error("fail to add document"); - } - }; - useEffect( - () => { - doLoad(); - const i = setInterval(doLoad, 5000); - return () => { - clearInterval(i); - }; - }, - [], - ); - const menu = CommonMenuList(); - return ( - - - {(ctx.username == "admin") - ? ( -
    - {diffList.map(x => ( - - ))} -
    - ) - : Not Allowed : please login as an admin} -
    -
    - ); -} diff --git a/src/client/page/gallery.tsx b/src/client/page/gallery.tsx deleted file mode 100644 index 5a7e126..0000000 --- a/src/client/page/gallery.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, { useContext, useEffect, useState } from "react"; -import { CommonMenuList, ContentInfo, Headline, LoadingCircle, NavItem, NavList, TagChip } from "../component/mod"; - -import { Box, Button, Chip, Pagination, Typography } from "@mui/material"; -import ContentAccessor, { Document, QueryListOption } from "../accessor/document"; -import { toQueryString } from "../accessor/util"; - -import { useLocation } from "react-router-dom"; -import { QueryStringToMap } from "../accessor/util"; -import { useIsElementInViewport } from "./reader/reader"; -import { PagePad } from "../component/pagepad"; - -export type GalleryProp = { - option?: QueryListOption; - diff: string; -}; -type GalleryState = { - documents: Document[] | undefined; -}; - -export const GalleryInfo = (props: GalleryProp) => { - const [state, setState] = useState({ documents: undefined }); - const [error, setError] = useState(null); - const [loadAll, setLoadAll] = useState(false); - const { elementRef, isVisible: isLoadVisible } = useIsElementInViewport({}); - - useEffect(() => { - if (isLoadVisible && (!loadAll) && (state.documents != undefined)) { - loadMore(); - } - }, [isLoadVisible]); - - useEffect(() => { - const abortController = new AbortController(); - console.log("load first", props.option); - const load = async () => { - try { - const c = await ContentAccessor.findList(props.option); - // todo : if c is undefined, retry to fetch 3 times. and show error message. - setState({ documents: c }); - setLoadAll(c.length == 0); - } catch (e) { - if (e instanceof Error) { - setError(e.message); - } else { - setError("unknown error"); - } - } - }; - load(); - }, [props.diff]); - const queryString = toQueryString(props.option ?? {}); - if (state.documents === undefined && error == null) { - return ; - } else { - return ( - - {props.option !== undefined && props.diff !== "" && ( - - search for - {props.option.word !== undefined && ( - - )} - {props.option.content_type !== undefined && ( - - )} - {props.option.allow_tag !== undefined - && props.option.allow_tag.map(x => ( - - ))} - - )} - {state.documents && state.documents.map(x => { - return ; - })} - {error && Error : {error}} - - {state.documents ? state.documents.length : "null"} loaded... - - - - ); - } - function loadMore() { - let option = { ...props.option }; - console.log(elementRef); - if (state.documents === undefined || state.documents.length === 0) { - console.log("loadall"); - setLoadAll(true); - return; - } - const prev_documents = state.documents; - option.cursor = prev_documents[prev_documents.length - 1].id; - console.log("load more", option); - const load = async () => { - const c = await ContentAccessor.findList(option); - if (c.length === 0) { - setLoadAll(true); - } else { - setState({ documents: [...prev_documents, ...c] }); - } - }; - load(); - } -}; - -export const Gallery = () => { - const location = useLocation(); - const query = QueryStringToMap(location.search); - const menu_list = CommonMenuList({ url: location.search }); - let option: QueryListOption = query; - option.allow_tag = typeof option.allow_tag === "string" ? [option.allow_tag] : option.allow_tag; - option.limit = typeof query["limit"] === "string" ? parseInt(query["limit"]) : undefined; - return ( - - - - - - ); -}; diff --git a/src/client/page/login.tsx b/src/client/page/login.tsx deleted file mode 100644 index 82d0720..0000000 --- a/src/client/page/login.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - MenuList, - Paper, - TextField, - Typography, - useTheme, -} from "@mui/material"; -import React, { useContext, useState } from "react"; -import { useNavigate } from "react-router-dom"; -import { CommonMenuList, Headline } from "../component/mod"; -import { UserContext } from "../state"; -import { doLogin as doSessionLogin } from "../state"; -import { PagePad } from "../component/pagepad"; - -export const LoginPage = () => { - const theme = useTheme(); - const [userLoginInfo, setUserLoginInfo] = useState({ username: "", password: "" }); - const [openDialog, setOpenDialog] = useState({ open: false, message: "" }); - const { setUsername, setPermission } = useContext(UserContext); - const navigate = useNavigate(); - const handleDialogClose = () => { - setOpenDialog({ ...openDialog, open: false }); - }; - const doLogin = async () => { - try { - const b = await doSessionLogin(userLoginInfo); - if (typeof b === "string") { - setOpenDialog({ open: true, message: b }); - return; - } - console.log(`login as ${b.username}`); - setUsername(b.username); - setPermission(b.permission); - } catch (e) { - if (e instanceof Error) { - console.error(e); - setOpenDialog({ open: true, message: e.message }); - } else console.error(e); - return; - } - navigate("/"); - }; - const menu = CommonMenuList(); - return ( - - - - Login -
    -
    - setUserLoginInfo({ ...userLoginInfo, username: e.target.value ?? "" })} - > - - { - if (e.key === "Enter") doLogin(); - }} - onChange={(e) => setUserLoginInfo({ ...userLoginInfo, password: e.target.value ?? "" })} - /> -
    -
    - - -
    - -
    - - Login Failed - - detail : {openDialog.message} - - - - - -
    -
    - ); -}; diff --git a/src/client/page/mod.ts b/src/client/page/mod.ts deleted file mode 100644 index 47167f2..0000000 --- a/src/client/page/mod.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from "./404"; -export * from "./contentinfo"; -export * from "./difference"; -export * from "./gallery"; -export * from "./login"; -export * from "./profile"; -export * from "./setting"; -export * from "./tags"; diff --git a/src/client/page/profile.tsx b/src/client/page/profile.tsx deleted file mode 100644 index 8df722e..0000000 --- a/src/client/page/profile.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { - Button, - Chip, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - Divider, - Grid, - Paper, - TextField, - Theme, - Typography, -} from "@mui/material"; -import React, { useContext, useState } from "react"; -import { CommonMenuList, Headline } from "../component/mod"; -import { UserContext } from "../state"; -import { PagePad } from "../component/pagepad"; - -const useStyles = (theme: Theme) => ({ - paper: { - alignSelf: "center", - padding: theme.spacing(2), - }, - formfield: { - display: "flex", - flexFlow: "column", - }, -}); - -export function ProfilePage() { - const userctx = useContext(UserContext); - // const classes = useStyles(); - const menu = CommonMenuList(); - const [pw_open, set_pw_open] = useState(false); - const [oldpw, setOldpw] = useState(""); - const [newpw, setNewpw] = useState(""); - const [newpwch, setNewpwch] = useState(""); - const [msg_dialog, set_msg_dialog] = useState({ opened: false, msg: "" }); - const permission_list = userctx.permission.map(p => ); - const isElectronContent = ((window["electron"] as any) !== undefined) as boolean; - const handle_open = () => set_pw_open(true); - const handle_close = () => { - set_pw_open(false); - setNewpw(""); - setNewpwch(""); - }; - const handle_ok = async () => { - if (newpw != newpwch) { - set_msg_dialog({ opened: true, msg: "password and password check is not equal." }); - handle_close(); - return; - } - if (isElectronContent) { - const elec = window["electron"] as any; - const success = elec.passwordReset(userctx.username, newpw); - if (!success) { - set_msg_dialog({ opened: true, msg: "user not exist." }); - } - } else { - const res = await fetch("/user/reset", { - method: "POST", - body: JSON.stringify({ - username: userctx.username, - oldpassword: oldpw, - newpassword: newpw, - }), - headers: { - "content-type": "application/json", - }, - }); - if (res.status != 200) { - set_msg_dialog({ opened: true, msg: "failed to change password." }); - } - } - handle_close(); - }; - return ( - - - - - - - {userctx.username} - - - - Permission - - - {permission_list.length == 0 ? "-" : permission_list} - - - - - - - - Password Reset - - type the old and new password -
    - {(!isElectronContent) && ( - setOldpw(e.target.value)} - > - - )} - setNewpw(e.target.value)} - > - - setNewpwch(e.target.value)} - > - -
    -
    - - - - -
    - set_msg_dialog({ opened: false, msg: "" })}> - Alert! - - {msg_dialog.msg} - - - - - -
    -
    - ); -} diff --git a/src/client/page/reader/comic.tsx b/src/client/page/reader/comic.tsx deleted file mode 100644 index 70d7baf..0000000 --- a/src/client/page/reader/comic.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { Typography, styled } from "@mui/material"; -import React, { RefObject, useEffect, useState } from "react"; -import { useSearchParams } from "react-router-dom"; -import { Document } from "../../accessor/document"; - -type ComicType = "comic" | "artist cg" | "donjinshi" | "western"; - -export type PresentableTag = { - artist: string[]; - group: string[]; - series: string[]; - type: ComicType; - character: string[]; - tags: string[]; -}; - -const ViewMain = styled("div")(({ theme }) => ({ - overflow: "hidden", - width: "100%", - height: "calc(100vh - 64px)", - position: "relative", -})); -const CurrentView = styled("img")(({theme})=>({ - maxWidth: "100%", - maxHeight: "100%", - top:"50%", - left:"50%", - transform: "translate(-50%,-50%)", - position: "absolute" -})); - -export const ComicReader = (props: { doc: Document, - fullScreenTarget?: RefObject}) => { - const additional = props.doc.additional; - const [searchParams, setSearchParams] = useSearchParams(); - - const curPage = (parseInt(searchParams.get("page") ?? "0")); - const setCurPage = (n: number) => { - setSearchParams([ - ["page", n.toString()] - ]); - } - if (isNaN(curPage)){ - return Error. Page number is not a number. - } - if (!("page" in additional)) { - console.error("invalid content : page read fail : " + JSON.stringify(additional)); - return Error. DB error. page restriction; - } - - const maxPage: number = additional["page"] as number; - const PageDown = () => setCurPage(Math.max(curPage - 1, 0)); - const PageUp = () => setCurPage(Math.min(curPage + 1, maxPage - 1)); - - const onKeyUp = (e: KeyboardEvent) => { - console.log(`currently: ${curPage}/${maxPage}`) - if (e.code === "ArrowLeft") { - PageDown(); - } else if (e.code === "ArrowRight") { - PageUp(); - } - }; - - useEffect(() => { - document.addEventListener("keydown", onKeyUp); - return () => { - document.removeEventListener("keydown", onKeyUp); - }; - }, [curPage]); - // theme.mixins.toolbar.minHeight; - return ( - -
    - -
    -
    - ); -}; - -export default ComicReader; diff --git a/src/client/page/reader/reader.tsx b/src/client/page/reader/reader.tsx deleted file mode 100644 index 16877d6..0000000 --- a/src/client/page/reader/reader.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { styled, Typography } from "@mui/material"; -import React from "react"; -import { Document, makeThumbnailUrl } from "../../accessor/document"; -import { ComicReader } from "./comic"; -import { VideoReader } from "./video"; - -export interface PagePresenterProp { - doc: Document; - className?: string; - fullScreenTarget?: React.RefObject; -} -interface PagePresenter { - (prop: PagePresenterProp): JSX.Element; -} - -export const getPresenter = (content: Document): PagePresenter => { - switch (content.content_type) { - case "comic": - return ComicReader; - case "video": - return VideoReader; - } - return () => Not implemented reader; -}; -const BackgroundDiv = styled("div")({ - height: "400px", - width: "300px", - backgroundColor: "#272733", - display: "flex", - alignItems: "center", - justifyContent: "center", -}); - -import { useEffect, useRef, useState } from "react"; -import "./thumbnail.css"; - -export function useIsElementInViewport(options?: IntersectionObserverInit) { - const elementRef = useRef(null); - const [isVisible, setIsVisible] = useState(false); - - const callback = (entries: IntersectionObserverEntry[]) => { - const [entry] = entries; - setIsVisible(entry.isIntersecting); - }; - - useEffect(() => { - const observer = new IntersectionObserver(callback, options); - elementRef.current && observer.observe(elementRef.current); - return () => observer.disconnect(); - }, [elementRef, options]); - - return { elementRef, isVisible }; -} - -export function ThumbnailContainer(props: { - content: Document; - className?: string; -}) { - const { elementRef, isVisible } = useIsElementInViewport({}); - const [loaded, setLoaded] = useState(false); - useEffect(() => { - if (isVisible) { - setLoaded(true); - } - }, [isVisible]); - const style = { - maxHeight: "400px", - maxWidth: "min(400px, 100vw)", - }; - const thumbnailurl = makeThumbnailUrl(props.content); - if (props.content.content_type === "video") { - return ; - } else {return ( - - {loaded && } - - );} -} diff --git a/src/client/page/reader/video.tsx b/src/client/page/reader/video.tsx deleted file mode 100644 index af6d137..0000000 --- a/src/client/page/reader/video.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; -import { Document } from "../../accessor/document"; - -export const VideoReader = (props: { doc: Document }) => { - const id = props.doc.id; - return ( - - ); -}; diff --git a/src/client/page/setting.tsx b/src/client/page/setting.tsx deleted file mode 100644 index a2d102b..0000000 --- a/src/client/page/setting.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Paper, Typography } from "@mui/material"; -import React from "react"; -import { CommonMenuList, Headline } from "../component/mod"; -import { PagePad } from "../component/pagepad"; - -export const SettingPage = () => { - const menu = CommonMenuList(); - return ( - - - - Setting - - - - ); -}; diff --git a/src/client/page/tags.tsx b/src/client/page/tags.tsx deleted file mode 100644 index 9c65cea..0000000 --- a/src/client/page/tags.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { Box, Paper, Typography } from "@mui/material"; -import { DataGrid, GridColDef } from "@mui/x-data-grid"; -import React, { useEffect, useState } from "react"; -import { LoadingCircle } from "../component/loading"; -import { CommonMenuList, Headline } from "../component/mod"; -import { PagePad } from "../component/pagepad"; - -type TagCount = { - tag_name: string; - occurs: number; -}; - -const tagTableColumn: GridColDef[] = [ - { - field: "tag_name", - headerName: "Tag Name", - width: 200, - }, - { - field: "occurs", - headerName: "Occurs", - width: 100, - type: "number", - }, -]; - -function TagTable() { - const [data, setData] = useState(); - const [error, setErrorMsg] = useState(undefined); - const isLoading = data === undefined; - - useEffect(() => { - loadData(); - }, []); - - if (isLoading) { - return ; - } - if (error !== undefined) { - return {error}; - } - return ( - - - t.tag_name}> - - - ); - - async function loadData() { - try { - const res = await fetch("/api/tags?withCount=true"); - const data = await res.json(); - setData(data); - } catch (e) { - setData([]); - if (e instanceof Error) { - setErrorMsg(e.message); - } else { - console.log(e); - setErrorMsg(""); - } - } - } -} - -export const TagsPage = () => { - const menu = CommonMenuList(); - return ( - - - - - - ); -}; diff --git a/src/client/pnpm-lock.yaml b/src/client/pnpm-lock.yaml deleted file mode 100644 index f8f0e79..0000000 --- a/src/client/pnpm-lock.yaml +++ /dev/null @@ -1,1421 +0,0 @@ -lockfileVersion: '6.0' - -dependencies: - '@emotion/react': - specifier: ^11.9.0 - version: 11.9.3(@babel/core@7.22.1)(@types/react@18.0.12)(react@18.1.0) - '@emotion/styled': - specifier: ^11.8.1 - version: 11.9.3(@babel/core@7.22.1)(@emotion/react@11.9.3)(@types/react@18.0.12)(react@18.1.0) - '@mui/icons-material': - specifier: ^5.6.2 - version: 5.8.3(@mui/material@5.8.3)(@types/react@18.0.12)(react@18.1.0) - '@mui/material': - specifier: ^5.6.2 - version: 5.8.3(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(@types/react@18.0.12)(react-dom@18.1.0)(react@18.1.0) - '@mui/x-data-grid': - specifier: ^5.12.3 - version: 5.12.3(@mui/material@5.8.3)(@mui/system@5.8.3)(react-dom@18.1.0)(react@18.1.0) - '@types/react': - specifier: ^18.0.5 - version: 18.0.12 - '@types/react-dom': - specifier: ^18.0.1 - version: 18.0.5 - react: - specifier: ^18.0.0 - version: 18.1.0 - react-dom: - specifier: ^18.0.0 - version: 18.1.0(react@18.1.0) - react-router-dom: - specifier: ^6.3.0 - version: 6.3.0(react-dom@18.1.0)(react@18.1.0) - -devDependencies: - esbuild: - specifier: ^0.14.36 - version: 0.14.43 - ts-node: - specifier: ^10.7.0 - version: 10.8.1(@types/node@20.2.5)(typescript@5.0.4) - -packages: - - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.9 - dev: false - - /@babel/code-frame@7.16.7: - resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.17.12 - dev: false - - /@babel/code-frame@7.21.4: - resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - dev: false - - /@babel/compat-data@7.22.3: - resolution: {integrity: sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==} - engines: {node: '>=6.9.0'} - dev: false - - /@babel/core@7.22.1: - resolution: {integrity: sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.22.3 - '@babel/helper-compilation-targets': 7.22.1(@babel/core@7.22.1) - '@babel/helper-module-transforms': 7.22.1 - '@babel/helpers': 7.22.3 - '@babel/parser': 7.22.4 - '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4 - '@babel/types': 7.22.4 - convert-source-map: 1.8.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: false - - /@babel/generator@7.22.3: - resolution: {integrity: sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.4 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - jsesc: 2.5.2 - dev: false - - /@babel/helper-compilation-targets@7.22.1(@babel/core@7.22.1): - resolution: {integrity: sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.22.3 - '@babel/core': 7.22.1 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.21.7 - lru-cache: 5.1.1 - semver: 6.3.0 - dev: false - - /@babel/helper-environment-visitor@7.22.1: - resolution: {integrity: sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==} - engines: {node: '>=6.9.0'} - dev: false - - /@babel/helper-function-name@7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.21.9 - '@babel/types': 7.22.4 - dev: false - - /@babel/helper-hoist-variables@7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.4 - dev: false - - /@babel/helper-module-imports@7.16.7: - resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.4 - dev: false - - /@babel/helper-module-imports@7.21.4: - resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.4 - dev: false - - /@babel/helper-module-transforms@7.22.1: - resolution: {integrity: sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.22.1 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-simple-access': 7.21.5 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4 - '@babel/types': 7.22.4 - transitivePeerDependencies: - - supports-color - dev: false - - /@babel/helper-plugin-utils@7.17.12: - resolution: {integrity: sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==} - engines: {node: '>=6.9.0'} - dev: false - - /@babel/helper-simple-access@7.21.5: - resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.4 - dev: false - - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.22.4 - dev: false - - /@babel/helper-string-parser@7.21.5: - resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==} - engines: {node: '>=6.9.0'} - dev: false - - /@babel/helper-validator-identifier@7.16.7: - resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==} - engines: {node: '>=6.9.0'} - dev: false - - /@babel/helper-validator-identifier@7.19.1: - resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} - engines: {node: '>=6.9.0'} - dev: false - - /@babel/helper-validator-option@7.21.0: - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} - engines: {node: '>=6.9.0'} - dev: false - - /@babel/helpers@7.22.3: - resolution: {integrity: sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.21.9 - '@babel/traverse': 7.22.4 - '@babel/types': 7.22.4 - transitivePeerDependencies: - - supports-color - dev: false - - /@babel/highlight@7.17.12: - resolution: {integrity: sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.16.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: false - - /@babel/highlight@7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: false - - /@babel/parser@7.22.4: - resolution: {integrity: sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.22.4 - dev: false - - /@babel/plugin-syntax-jsx@7.17.12(@babel/core@7.22.1): - resolution: {integrity: sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.22.1 - '@babel/helper-plugin-utils': 7.17.12 - dev: false - - /@babel/runtime@7.18.3: - resolution: {integrity: sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.13.9 - dev: false - - /@babel/template@7.21.9: - resolution: {integrity: sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/parser': 7.22.4 - '@babel/types': 7.22.4 - dev: false - - /@babel/traverse@7.22.4: - resolution: {integrity: sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.22.3 - '@babel/helper-environment-visitor': 7.22.1 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.22.4 - '@babel/types': 7.22.4 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: false - - /@babel/types@7.18.4: - resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.16.7 - to-fast-properties: 2.0.0 - dev: false - - /@babel/types@7.22.4: - resolution: {integrity: sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.21.5 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - dev: false - - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - dev: true - - /@emotion/babel-plugin@11.9.2(@babel/core@7.22.1): - resolution: {integrity: sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.22.1 - '@babel/helper-module-imports': 7.16.7 - '@babel/plugin-syntax-jsx': 7.17.12(@babel/core@7.22.1) - '@babel/runtime': 7.18.3 - '@emotion/hash': 0.8.0 - '@emotion/memoize': 0.7.5 - '@emotion/serialize': 1.0.4 - babel-plugin-macros: 2.8.0 - convert-source-map: 1.8.0 - escape-string-regexp: 4.0.0 - find-root: 1.1.0 - source-map: 0.5.7 - stylis: 4.0.13 - dev: false - - /@emotion/cache@11.9.3: - resolution: {integrity: sha512-0dgkI/JKlCXa+lEXviaMtGBL0ynpx4osh7rjOXE71q9bIF8G+XhJgvi+wDu0B0IdCVx37BffiwXlN9I3UuzFvg==} - dependencies: - '@emotion/memoize': 0.7.5 - '@emotion/sheet': 1.1.1 - '@emotion/utils': 1.1.0 - '@emotion/weak-memoize': 0.2.5 - stylis: 4.0.13 - dev: false - - /@emotion/hash@0.8.0: - resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} - dev: false - - /@emotion/is-prop-valid@1.1.3: - resolution: {integrity: sha512-RFg04p6C+1uO19uG8N+vqanzKqiM9eeV1LDOG3bmkYmuOj7NbKNlFC/4EZq5gnwAIlcC/jOT24f8Td0iax2SXA==} - dependencies: - '@emotion/memoize': 0.7.5 - dev: false - - /@emotion/memoize@0.7.5: - resolution: {integrity: sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==} - dev: false - - /@emotion/react@11.9.3(@babel/core@7.22.1)(@types/react@18.0.12)(react@18.1.0): - resolution: {integrity: sha512-g9Q1GcTOlzOEjqwuLF/Zd9LC+4FljjPjDfxSM7KmEakm+hsHXk+bYZ2q+/hTJzr0OUNkujo72pXLQvXj6H+GJQ==} - peerDependencies: - '@babel/core': ^7.0.0 - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@babel/core': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/core': 7.22.1 - '@babel/runtime': 7.18.3 - '@emotion/babel-plugin': 11.9.2(@babel/core@7.22.1) - '@emotion/cache': 11.9.3 - '@emotion/serialize': 1.0.4 - '@emotion/utils': 1.1.0 - '@emotion/weak-memoize': 0.2.5 - '@types/react': 18.0.12 - hoist-non-react-statics: 3.3.2 - react: 18.1.0 - dev: false - - /@emotion/serialize@1.0.4: - resolution: {integrity: sha512-1JHamSpH8PIfFwAMryO2bNka+y8+KA5yga5Ocf2d7ZEiJjb7xlLW7aknBGZqJLajuLOvJ+72vN+IBSwPlXD1Pg==} - dependencies: - '@emotion/hash': 0.8.0 - '@emotion/memoize': 0.7.5 - '@emotion/unitless': 0.7.5 - '@emotion/utils': 1.1.0 - csstype: 3.1.0 - dev: false - - /@emotion/sheet@1.1.1: - resolution: {integrity: sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA==} - dev: false - - /@emotion/styled@11.9.3(@babel/core@7.22.1)(@emotion/react@11.9.3)(@types/react@18.0.12)(react@18.1.0): - resolution: {integrity: sha512-o3sBNwbtoVz9v7WB1/Y/AmXl69YHmei2mrVnK7JgyBJ//Rst5yqPZCecEJlMlJrFeWHp+ki/54uN265V2pEcXA==} - peerDependencies: - '@babel/core': ^7.0.0 - '@emotion/react': ^11.0.0-rc.0 - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@babel/core': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/core': 7.22.1 - '@babel/runtime': 7.18.3 - '@emotion/babel-plugin': 11.9.2(@babel/core@7.22.1) - '@emotion/is-prop-valid': 1.1.3 - '@emotion/react': 11.9.3(@babel/core@7.22.1)(@types/react@18.0.12)(react@18.1.0) - '@emotion/serialize': 1.0.4 - '@emotion/utils': 1.1.0 - '@types/react': 18.0.12 - react: 18.1.0 - dev: false - - /@emotion/unitless@0.7.5: - resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} - dev: false - - /@emotion/utils@1.1.0: - resolution: {integrity: sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==} - dev: false - - /@emotion/weak-memoize@0.2.5: - resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==} - dev: false - - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.13 - '@jridgewell/trace-mapping': 0.3.18 - dev: false - - /@jridgewell/resolve-uri@3.0.7: - resolution: {integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==} - engines: {node: '>=6.0.0'} - - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} - dev: false - - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - dev: false - - /@jridgewell/sourcemap-codec@1.4.13: - resolution: {integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==} - - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - dev: false - - /@jridgewell/trace-mapping@0.3.18: - resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: false - - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.0.7 - '@jridgewell/sourcemap-codec': 1.4.13 - - /@mui/base@5.0.0-alpha.84(@types/react@18.0.12)(react-dom@18.1.0)(react@18.1.0): - resolution: {integrity: sha512-uDx+wGVytS+ZHiWHyzUyijY83GSIXJpzSJ0PGc/8/s+8nBzeHvaPKrAyJz15ASLr52hYRA6PQGqn0eRAsB7syQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.18.3 - '@emotion/is-prop-valid': 1.1.3 - '@mui/types': 7.1.3(@types/react@18.0.12) - '@mui/utils': 5.8.0(react@18.1.0) - '@popperjs/core': 2.11.5 - '@types/react': 18.0.12 - clsx: 1.1.1 - prop-types: 15.8.1 - react: 18.1.0 - react-dom: 18.1.0(react@18.1.0) - react-is: 17.0.2 - dev: false - - /@mui/icons-material@5.8.3(@mui/material@5.8.3)(@types/react@18.0.12)(react@18.1.0): - resolution: {integrity: sha512-dAdhimSLKOV0Q8FR7AYGEaCrTUh9OV7zU4Ueo5REoUt4cC3Vy+UBKDjZk66x5ezaYb63AFgQIFwtnZj3B/QDbQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@mui/material': ^5.0.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.18.3 - '@mui/material': 5.8.3(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(@types/react@18.0.12)(react-dom@18.1.0)(react@18.1.0) - '@types/react': 18.0.12 - react: 18.1.0 - dev: false - - /@mui/material@5.8.3(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(@types/react@18.0.12)(react-dom@18.1.0)(react@18.1.0): - resolution: {integrity: sha512-8UecY/W9SMtEZm5PMCUcMbujajVP6fobu0BgBPiIWwwWRblZVEzqprY6v1P2me7qCyrve4L4V/rqAKPKhVHOSg==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.18.3 - '@emotion/react': 11.9.3(@babel/core@7.22.1)(@types/react@18.0.12)(react@18.1.0) - '@emotion/styled': 11.9.3(@babel/core@7.22.1)(@emotion/react@11.9.3)(@types/react@18.0.12)(react@18.1.0) - '@mui/base': 5.0.0-alpha.84(@types/react@18.0.12)(react-dom@18.1.0)(react@18.1.0) - '@mui/system': 5.8.3(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(@types/react@18.0.12)(react@18.1.0) - '@mui/types': 7.1.3(@types/react@18.0.12) - '@mui/utils': 5.8.0(react@18.1.0) - '@types/react': 18.0.12 - '@types/react-transition-group': 4.4.4 - clsx: 1.1.1 - csstype: 3.1.0 - hoist-non-react-statics: 3.3.2 - prop-types: 15.8.1 - react: 18.1.0 - react-dom: 18.1.0(react@18.1.0) - react-is: 17.0.2 - react-transition-group: 4.4.2(react-dom@18.1.0)(react@18.1.0) - dev: false - - /@mui/private-theming@5.8.0(@types/react@18.0.12)(react@18.1.0): - resolution: {integrity: sha512-MjRAneTmCKLR9u2S4jtjLUe6gpHxlbb4g2bqpDJ2PdwlvwsWIUzbc/gVB4dvccljXeWxr5G2M/Co2blXisvFIw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.18.3 - '@mui/utils': 5.8.0(react@18.1.0) - '@types/react': 18.0.12 - prop-types: 15.8.1 - react: 18.1.0 - dev: false - - /@mui/styled-engine@5.8.0(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(react@18.1.0): - resolution: {integrity: sha512-Q3spibB8/EgeMYHc+/o3RRTnAYkSl7ROCLhXJ830W8HZ2/iDiyYp16UcxKPurkXvLhUaILyofPVrP3Su2uKsAw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.4.1 - '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - dependencies: - '@babel/runtime': 7.18.3 - '@emotion/cache': 11.9.3 - '@emotion/react': 11.9.3(@babel/core@7.22.1)(@types/react@18.0.12)(react@18.1.0) - '@emotion/styled': 11.9.3(@babel/core@7.22.1)(@emotion/react@11.9.3)(@types/react@18.0.12)(react@18.1.0) - prop-types: 15.8.1 - react: 18.1.0 - dev: false - - /@mui/system@5.8.3(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(@types/react@18.0.12)(react@18.1.0): - resolution: {integrity: sha512-/tyGQcYqZT0nl98qV9XnGiedTO+V7VHc28k4POfhMJNedB1CRrwWRm767DeEdc5f/8CU2See3WD16ikP6pYiOA==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.18.3 - '@emotion/react': 11.9.3(@babel/core@7.22.1)(@types/react@18.0.12)(react@18.1.0) - '@emotion/styled': 11.9.3(@babel/core@7.22.1)(@emotion/react@11.9.3)(@types/react@18.0.12)(react@18.1.0) - '@mui/private-theming': 5.8.0(@types/react@18.0.12)(react@18.1.0) - '@mui/styled-engine': 5.8.0(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(react@18.1.0) - '@mui/types': 7.1.3(@types/react@18.0.12) - '@mui/utils': 5.8.0(react@18.1.0) - '@types/react': 18.0.12 - clsx: 1.1.1 - csstype: 3.1.0 - prop-types: 15.8.1 - react: 18.1.0 - dev: false - - /@mui/types@7.1.3(@types/react@18.0.12): - resolution: {integrity: sha512-DDF0UhMBo4Uezlk+6QxrlDbchF79XG6Zs0zIewlR4c0Dt6GKVFfUtzPtHCH1tTbcSlq/L2bGEdiaoHBJ9Y1gSA==} - peerDependencies: - '@types/react': '*' - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 18.0.12 - dev: false - - /@mui/utils@5.8.0(react@18.1.0): - resolution: {integrity: sha512-7LgUtCvz78676iC0wpTH7HizMdCrTphhBmRWimIMFrp5Ph6JbDFVuKS1CwYnWWxRyYKL0QzXrDL0lptAU90EXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - react: ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.18.3 - '@types/prop-types': 15.7.5 - '@types/react-is': 17.0.3 - prop-types: 15.8.1 - react: 18.1.0 - react-is: 17.0.2 - dev: false - - /@mui/x-data-grid@5.12.3(@mui/material@5.8.3)(@mui/system@5.8.3)(react-dom@18.1.0)(react@18.1.0): - resolution: {integrity: sha512-57A2MkRR/uUNC/dECFV0YDJvi1Q+gQgmgw1OHmZ1uSnKh29PcHpswkdapO0LueLpxAy8tfH+fTtnnPDmYgJeUg==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@mui/material': ^5.4.1 - '@mui/system': ^5.4.1 - react: ^17.0.2 || ^18.0.0 - react-dom: ^17.0.2 || ^18.0.0 - dependencies: - '@babel/runtime': 7.18.3 - '@mui/material': 5.8.3(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(@types/react@18.0.12)(react-dom@18.1.0)(react@18.1.0) - '@mui/system': 5.8.3(@emotion/react@11.9.3)(@emotion/styled@11.9.3)(@types/react@18.0.12)(react@18.1.0) - '@mui/utils': 5.8.0(react@18.1.0) - clsx: 1.1.1 - prop-types: 15.8.1 - react: 18.1.0 - react-dom: 18.1.0(react@18.1.0) - reselect: 4.1.6 - dev: false - - /@popperjs/core@2.11.5: - resolution: {integrity: sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==} - dev: false - - /@tsconfig/node10@1.0.9: - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} - dev: true - - /@tsconfig/node12@1.0.10: - resolution: {integrity: sha512-N+srakvPaYMGkwjNDx3ASx65Zl3QG8dJgVtIB+YMOkucU+zctlv/hdP5250VKdDHSDoW9PFZoCqbqNcAPjCjXA==} - dev: true - - /@tsconfig/node14@1.0.2: - resolution: {integrity: sha512-YwrUA5ysDXHFYfL0Xed9x3sNS4P+aKlCOnnbqUa2E5HdQshHFleCJVrj1PlGTb4GgFUCDyte1v3JWLy2sz8Oqg==} - dev: true - - /@tsconfig/node16@1.0.3: - resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} - dev: true - - /@types/node@20.2.5: - resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==} - dev: true - - /@types/parse-json@4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} - dev: false - - /@types/prop-types@15.7.5: - resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} - dev: false - - /@types/react-dom@18.0.5: - resolution: {integrity: sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA==} - dependencies: - '@types/react': 18.0.12 - dev: false - - /@types/react-is@17.0.3: - resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} - dependencies: - '@types/react': 18.0.12 - dev: false - - /@types/react-transition-group@4.4.4: - resolution: {integrity: sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==} - dependencies: - '@types/react': 18.0.12 - dev: false - - /@types/react@18.0.12: - resolution: {integrity: sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==} - dependencies: - '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.2 - csstype: 3.1.0 - dev: false - - /@types/scheduler@0.16.2: - resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} - dev: false - - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - dev: true - - /acorn@8.7.1: - resolution: {integrity: sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - dev: false - - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true - - /babel-plugin-macros@2.8.0: - resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} - dependencies: - '@babel/runtime': 7.18.3 - cosmiconfig: 6.0.0 - resolve: 1.22.0 - dev: false - - /browserslist@4.21.7: - resolution: {integrity: sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001492 - electron-to-chromium: 1.4.415 - node-releases: 2.0.12 - update-browserslist-db: 1.0.11(browserslist@4.21.7) - dev: false - - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - dev: false - - /caniuse-lite@1.0.30001492: - resolution: {integrity: sha512-2efF8SAZwgAX1FJr87KWhvuJxnGJKOnctQa8xLOskAXNXq8oiuqgl6u1kk3fFpsp3GgvzlRjiK1sl63hNtFADw==} - dev: false - - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - dev: false - - /clsx@1.1.1: - resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==} - engines: {node: '>=6'} - dev: false - - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - dev: false - - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: false - - /convert-source-map@1.8.0: - resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==} - dependencies: - safe-buffer: 5.1.2 - dev: false - - /cosmiconfig@6.0.0: - resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} - engines: {node: '>=8'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - dev: false - - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true - - /csstype@3.1.0: - resolution: {integrity: sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==} - dev: false - - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: false - - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true - - /dom-helpers@5.2.1: - resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dependencies: - '@babel/runtime': 7.18.3 - csstype: 3.1.0 - dev: false - - /electron-to-chromium@1.4.415: - resolution: {integrity: sha512-3meOxxvyUOJVwa7cem6O2/MRPZ+FTzblSPSpG7biZoF9yOVrhCaS2l9C4jjW6YTm8uuEpmApuP0xQJSmUglfdg==} - dev: false - - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - dev: false - - /esbuild-android-64@0.14.43: - resolution: {integrity: sha512-kqFXAS72K6cNrB6RiM7YJ5lNvmWRDSlpi7ZuRZ1hu1S3w0zlwcoCxWAyM23LQUyZSs1PbjHgdbbfYAN8IGh6xg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-android-arm64@0.14.43: - resolution: {integrity: sha512-bKS2BBFh+7XZY9rpjiHGRNA7LvWYbZWP87pLehggTG7tTaCDvj8qQGOU/OZSjCSKDYbgY7Q+oDw8RlYQ2Jt2BA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-64@0.14.43: - resolution: {integrity: sha512-/3PSilx011ttoieRGkSZ0XV8zjBf2C9enV4ScMMbCT4dpx0mFhMOpFnCHkOK0pWGB8LklykFyHrWk2z6DENVUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-darwin-arm64@0.14.43: - resolution: {integrity: sha512-1HyFUKs8DMCBOvw1Qxpr5Vv/ThNcVIFb5xgXWK3pyT40WPvgYIiRTwJCvNs4l8i5qWF8/CK5bQxJVDjQvtv0Yw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-64@0.14.43: - resolution: {integrity: sha512-FNWc05TPHYgaXjbPZO5/rJKSBslfG6BeMSs8GhwnqAKP56eEhvmzwnIz1QcC9cRVyO+IKqWNfmHFkCa1WJTULA==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-freebsd-arm64@0.14.43: - resolution: {integrity: sha512-amrYopclz3VohqisOPR6hA3GOWA3LZC1WDLnp21RhNmoERmJ/vLnOpnrG2P/Zao+/erKTCUqmrCIPVtj58DRoA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-32@0.14.43: - resolution: {integrity: sha512-KoxoEra+9O3AKVvgDFvDkiuddCds6q71owSQEYwjtqRV7RwbPzKxJa6+uyzUulHcyGVq0g15K0oKG5CFBcvYDw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-64@0.14.43: - resolution: {integrity: sha512-EwINwGMyiJMgBby5/SbMqKcUhS5AYAZ2CpEBzSowsJPNBJEdhkCTtEjk757TN/wxgbu3QklqDM6KghY660QCUw==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm64@0.14.43: - resolution: {integrity: sha512-UlSpjMWllAc70zYbHxWuDS3FJytyuR/gHJYBr8BICcTNb/TSOYVBg6U7b3jZ3mILTrgzwJUHwhEwK18FZDouUQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-arm@0.14.43: - resolution: {integrity: sha512-e6YzQUoDxxtyamuF12eVzzRC7bbEFSZohJ6igQB9tBqnNmIQY3fI6Cns3z2wxtbZ3f2o6idkD2fQnlvs2902Dg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-mips64le@0.14.43: - resolution: {integrity: sha512-f+v8cInPEL1/SDP//CfSYzcDNgE4CY3xgDV81DWm3KAPWzhvxARrKxB1Pstf5mB56yAslJDxu7ryBUPX207EZA==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-ppc64le@0.14.43: - resolution: {integrity: sha512-5wZYMDGAL/K2pqkdIsW+I4IR41kyfHr/QshJcNpUfK3RjB3VQcPWOaZmc+74rm4ZjVirYrtz+jWw0SgxtxRanA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-riscv64@0.14.43: - resolution: {integrity: sha512-lYcAOUxp85hC7lSjycJUVSmj4/9oEfSyXjb/ua9bNl8afonaduuqtw7hvKMoKuYnVwOCDw4RSfKpcnIRDWq+Bw==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-s390x@0.14.43: - resolution: {integrity: sha512-27e43ZhHvhFE4nM7HqtUbMRu37I/4eNSUbb8FGZWszV+uLzMIsHDwLoBiJmw7G9N+hrehNPeQ4F5Ujad0DrUKQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-netbsd-64@0.14.43: - resolution: {integrity: sha512-2mH4QF6hHBn5zzAfxEI/2eBC0mspVsZ6UVo821LpAJKMvLJPBk3XJO5xwg7paDqSqpl7p6IRrAenW999AEfJhQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-openbsd-64@0.14.43: - resolution: {integrity: sha512-ZhQpiZjvqCqO8jKdGp9+8k9E/EHSA+zIWOg+grwZasI9RoblqJ1QiZqqi7jfd6ZrrG1UFBNGe4m0NFxCFbMVbg==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-sunos-64@0.14.43: - resolution: {integrity: sha512-DgxSi9DaHReL9gYuul2rrQCAapgnCJkh3LSHPKsY26zytYppG0HgkgVF80zjIlvEsUbGBP/GHQzBtrezj/Zq1Q==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-32@0.14.43: - resolution: {integrity: sha512-Ih3+2O5oExiqm0mY6YYE5dR0o8+AspccQ3vIAtRodwFvhuyGLjb0Hbmzun/F3Lw19nuhPMu3sW2fqIJ5xBxByw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-64@0.14.43: - resolution: {integrity: sha512-8NsuNfI8xwFuJbrCuI+aBqNTYkrWErejFO5aYM+yHqyHuL8mmepLS9EPzAzk8rvfaJrhN0+RvKWAcymViHOKEw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-arm64@0.14.43: - resolution: {integrity: sha512-7ZlD7bo++kVRblJEoG+cepljkfP8bfuTPz5fIXzptwnPaFwGS6ahvfoYzY7WCf5v/1nX2X02HDraVItTgbHnKw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild@0.14.43: - resolution: {integrity: sha512-Uf94+kQmy/5jsFwKWiQB4hfo/RkM9Dh7b79p8yqd1tshULdr25G2szLz631NoH3s2ujnKEKVD16RmOxvCNKRFA==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - esbuild-android-64: 0.14.43 - esbuild-android-arm64: 0.14.43 - esbuild-darwin-64: 0.14.43 - esbuild-darwin-arm64: 0.14.43 - esbuild-freebsd-64: 0.14.43 - esbuild-freebsd-arm64: 0.14.43 - esbuild-linux-32: 0.14.43 - esbuild-linux-64: 0.14.43 - esbuild-linux-arm: 0.14.43 - esbuild-linux-arm64: 0.14.43 - esbuild-linux-mips64le: 0.14.43 - esbuild-linux-ppc64le: 0.14.43 - esbuild-linux-riscv64: 0.14.43 - esbuild-linux-s390x: 0.14.43 - esbuild-netbsd-64: 0.14.43 - esbuild-openbsd-64: 0.14.43 - esbuild-sunos-64: 0.14.43 - esbuild-windows-32: 0.14.43 - esbuild-windows-64: 0.14.43 - esbuild-windows-arm64: 0.14.43 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - dev: false - - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - dev: false - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: false - - /find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - dev: false - - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - dev: false - - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: false - - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - dev: false - - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - dev: false - - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - dev: false - - /history@5.3.0: - resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==} - dependencies: - '@babel/runtime': 7.18.3 - dev: false - - /hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - dependencies: - react-is: 16.13.1 - dev: false - - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - dev: false - - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - dev: false - - /is-core-module@2.9.0: - resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} - dependencies: - has: 1.0.3 - dev: false - - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false - - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - dev: false - - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - dev: false - - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: false - - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - dev: false - - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - dev: false - - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - dev: false - - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true - - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: false - - /node-releases@2.0.12: - resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==} - dev: false - - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - dev: false - - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - dev: false - - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.16.7 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - dev: false - - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: false - - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: false - - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: false - - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - dev: false - - /react-dom@18.1.0(react@18.1.0): - resolution: {integrity: sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w==} - peerDependencies: - react: ^18.1.0 - dependencies: - loose-envify: 1.4.0 - react: 18.1.0 - scheduler: 0.22.0 - dev: false - - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: false - - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - dev: false - - /react-router-dom@6.3.0(react-dom@18.1.0)(react@18.1.0): - resolution: {integrity: sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' - dependencies: - history: 5.3.0 - react: 18.1.0 - react-dom: 18.1.0(react@18.1.0) - react-router: 6.3.0(react@18.1.0) - dev: false - - /react-router@6.3.0(react@18.1.0): - resolution: {integrity: sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==} - peerDependencies: - react: '>=16.8' - dependencies: - history: 5.3.0 - react: 18.1.0 - dev: false - - /react-transition-group@4.4.2(react-dom@18.1.0)(react@18.1.0): - resolution: {integrity: sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==} - peerDependencies: - react: '>=16.6.0' - react-dom: '>=16.6.0' - dependencies: - '@babel/runtime': 7.18.3 - dom-helpers: 5.2.1 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.1.0 - react-dom: 18.1.0(react@18.1.0) - dev: false - - /react@18.1.0: - resolution: {integrity: sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ==} - engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 - dev: false - - /regenerator-runtime@0.13.9: - resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==} - dev: false - - /reselect@4.1.6: - resolution: {integrity: sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ==} - dev: false - - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - dev: false - - /resolve@1.22.0: - resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} - hasBin: true - dependencies: - is-core-module: 2.9.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: false - - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: false - - /scheduler@0.22.0: - resolution: {integrity: sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ==} - dependencies: - loose-envify: 1.4.0 - dev: false - - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true - dev: false - - /source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - dev: false - - /stylis@4.0.13: - resolution: {integrity: sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==} - dev: false - - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 - dev: false - - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: false - - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - dev: false - - /ts-node@10.8.1(@types/node@20.2.5)(typescript@5.0.4): - resolution: {integrity: sha512-Wwsnao4DQoJsN034wePSg5nZiw4YKXf56mPIAeD6wVmiv+RytNSWqc2f3fKvcUoV+Yn2+yocD71VOfQHbmVX4g==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.10 - '@tsconfig/node14': 1.0.2 - '@tsconfig/node16': 1.0.3 - '@types/node': 20.2.5 - acorn: 8.7.1 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.0.4 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - - /typescript@5.0.4: - resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} - engines: {node: '>=12.20'} - hasBin: true - dev: true - - /update-browserslist-db@1.0.11(browserslist@4.21.7): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.7 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: false - - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true - - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: false - - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - dev: false - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true diff --git a/src/client/state.tsx b/src/client/state.tsx deleted file mode 100644 index ad9a74c..0000000 --- a/src/client/state.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { createContext, useRef, useState } from "react"; -export const BackLinkContext = createContext({ backLink: "", setBackLink: (s: string) => {} }); -export const UserContext = createContext({ - username: "", - permission: [] as string[], - setUsername: (s: string) => {}, - setPermission: (permission: string[]) => {}, -}); - -type LoginLocalStorage = { - username: string; - permission: string[]; - accessExpired: number; -}; - -let localObj: LoginLocalStorage | null = null; - -export const getInitialValue = async () => { - if (localObj === null) { - const storagestr = window.localStorage.getItem("UserLoginContext") as string | null; - const storage = storagestr !== null ? JSON.parse(storagestr) as LoginLocalStorage | null : null; - localObj = storage; - } - if (localObj !== null && localObj.accessExpired > Math.floor(Date.now() / 1000)) { - return { - username: localObj.username, - permission: localObj.permission, - }; - } - const res = await fetch("/user/refresh", { - method: "POST", - }); - if (res.status !== 200) throw new Error("Maybe Network Error"); - const r = await res.json() as LoginLocalStorage & { refresh: boolean }; - if (r.refresh) { - localObj = { - username: r.username, - permission: r.permission, - accessExpired: r.accessExpired, - }; - } else { - localObj = { - accessExpired: 0, - username: "", - permission: r.permission, - }; - } - window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj)); - return { - username: r.username, - permission: r.permission, - }; -}; -export const doLogout = async () => { - const req = await fetch("/user/logout", { - method: "POST", - }); - try { - const res = await req.json(); - localObj = { - accessExpired: 0, - username: "", - permission: res["permission"], - }; - window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj)); - return { - username: localObj.username, - permission: localObj.permission, - }; - } catch (error) { - console.error(`Server Error ${error}`); - return { - username: "", - permission: [], - }; - } -}; -export const doLogin = async (userLoginInfo: { - username: string; - password: string; -}): Promise => { - const res = await fetch("/user/login", { - method: "POST", - body: JSON.stringify(userLoginInfo), - headers: { "content-type": "application/json" }, - }); - const b = await res.json(); - if (res.status !== 200) { - return b.detail as string; - } - localObj = b; - window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj)); - return b; -}; diff --git a/src/config.ts b/src/config.ts deleted file mode 100644 index 583c834..0000000 --- a/src/config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Knex as k } from "knex"; - -export namespace Knex { - export const config: { - development: k.Config; - production: k.Config; - } = { - development: { - client: "sqlite3", - connection: { - filename: "./devdb.sqlite3", - }, - debug: true, - }, - production: { - client: "sqlite3", - connection: { - filename: "./db.sqlite3", - }, - }, - }; -} diff --git a/src/content/comic.ts b/src/content/comic.ts deleted file mode 100644 index 0f0feb0..0000000 --- a/src/content/comic.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { extname } from "path"; -import { DocumentBody } from "../model/doc"; -import { readAllFromZip, readZip } from "../util/zipwrap"; -import { ContentConstructOption, ContentFile, createDefaultClass, registerContentReferrer } from "./file"; - -type ComicType = "doujinshi" | "artist cg" | "manga" | "western"; -interface ComicDesc { - title: string; - artist?: string[]; - group?: string[]; - series?: string[]; - type: ComicType | [ComicType]; - character?: string[]; - tags?: string[]; -} -const ImageExt = [".gif", ".png", ".jpeg", ".bmp", ".webp", ".jpg"]; -export class ComicReferrer extends createDefaultClass("comic") { - desc: ComicDesc | undefined; - pagenum: number; - additional: ContentConstructOption | undefined; - constructor(path: string, option?: ContentConstructOption) { - super(path); - this.additional = option; - this.pagenum = 0; - } - async initDesc(): Promise { - if (this.desc !== undefined) return; - const zip = await readZip(this.path); - const entries = await zip.entries(); - this.pagenum = Object.keys(entries).filter(x => ImageExt.includes(extname(x))).length; - const entry = entries["desc.json"]; - if (entry === undefined) { - return; - } - const data = (await readAllFromZip(zip, entry)).toString("utf-8"); - this.desc = JSON.parse(data); - if (this.desc === undefined) { - throw new Error(`JSON.parse is returning undefined. ${this.path} desc.json format error`); - } - } - - async createDocumentBody(): Promise { - await this.initDesc(); - const basebody = await super.createDocumentBody(); - this.desc?.title; - if (this.desc === undefined) { - return basebody; - } - let tags: string[] = this.desc.tags ?? []; - tags = tags.concat(this.desc.artist?.map(x => `artist:${x}`) ?? []); - tags = tags.concat(this.desc.character?.map(x => `character:${x}`) ?? []); - tags = tags.concat(this.desc.group?.map(x => `group:${x}`) ?? []); - tags = tags.concat(this.desc.series?.map(x => `series:${x}`) ?? []); - const type = this.desc.type instanceof Array ? this.desc.type[0] : this.desc.type; - tags.push(`type:${type}`); - return { - ...basebody, - title: this.desc.title, - additional: { - page: this.pagenum, - }, - tags: tags, - }; - } -} -registerContentReferrer(ComicReferrer); diff --git a/src/content/file.ts b/src/content/file.ts deleted file mode 100644 index e7b85e0..0000000 --- a/src/content/file.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { createHash } from "crypto"; -import { promises, Stats } from "fs"; -import { Context, DefaultContext, DefaultState, Middleware, Next } from "koa"; -import Router from "koa-router"; -import { extname } from "path"; -import path from "path"; -import { DocumentBody } from "../model/mod"; -/** - * content file or directory referrer - */ -export interface ContentFile { - getHash(): Promise; - createDocumentBody(): Promise; - readonly path: string; - readonly type: string; -} -export type ContentConstructOption = { - hash: string; -}; -type ContentFileConstructor = (new(path: string, option?: ContentConstructOption) => ContentFile) & { - content_type: string; -}; -export const createDefaultClass = (type: string): ContentFileConstructor => { - let cons = class implements ContentFile { - readonly path: string; - // type = type; - static content_type = type; - protected hash: string | undefined; - protected stat: Stats | undefined; - - constructor(path: string, option?: ContentConstructOption) { - this.path = path; - this.hash = option?.hash; - this.stat = undefined; - } - async createDocumentBody(): Promise { - const { base, dir, name } = path.parse(this.path); - - const ret = { - title: name, - basepath: dir, - additional: {}, - content_type: cons.content_type, - filename: base, - tags: [], - content_hash: await this.getHash(), - modified_at: await this.getMtime(), - } as DocumentBody; - return ret; - } - get type(): string { - return cons.content_type; - } - async getHash(): Promise { - if (this.hash !== undefined) return this.hash; - this.stat = await promises.stat(this.path); - const hash = createHash("sha512"); - hash.update(extname(this.path)); - hash.update(this.stat.mode.toString()); - // if(this.desc !== undefined) - // hash.update(JSON.stringify(this.desc)); - hash.update(this.stat.size.toString()); - this.hash = hash.digest("base64"); - return this.hash; - } - async getMtime(): Promise { - if (this.stat !== undefined) return this.stat.mtimeMs; - await this.getHash(); - return this.stat!.mtimeMs; - } - }; - return cons; -}; -let ContstructorTable: { [k: string]: ContentFileConstructor } = {}; -export function registerContentReferrer(s: ContentFileConstructor) { - console.log(`registered content type: ${s.content_type}`); - ContstructorTable[s.content_type] = s; -} -export function createContentFile(type: string, path: string, option?: ContentConstructOption) { - const constructorMethod = ContstructorTable[type]; - if (constructorMethod === undefined) { - console.log(`${type} are not in ${JSON.stringify(ContstructorTable)}`); - throw new Error("construction method of the content type is undefined"); - } - return new constructorMethod(path, option); -} -export function getContentFileConstructor(type: string): ContentFileConstructor | undefined { - const ret = ContstructorTable[type]; - return ret; -} diff --git a/src/content/video.ts b/src/content/video.ts deleted file mode 100644 index d24e19f..0000000 --- a/src/content/video.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ContentConstructOption, ContentFile, registerContentReferrer } from "./file"; -import { createDefaultClass } from "./file"; - -export class VideoReferrer extends createDefaultClass("video") { - constructor(path: string, desc?: ContentConstructOption) { - super(path, desc); - } -} -registerContentReferrer(VideoReferrer); diff --git a/src/database.ts b/src/database.ts deleted file mode 100644 index 285e726..0000000 --- a/src/database.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { existsSync } from "fs"; -import Knex from "knex"; -import { Knex as KnexConfig } from "./config"; -import { get_setting } from "./SettingConfig"; - -export async function connectDB() { - const env = get_setting().mode; - const config = KnexConfig.config[env]; - if (!config.connection) { - throw new Error("connection options required."); - } - const connection = config.connection; - if (typeof connection === "string") { - throw new Error("unknown connection options"); - } - if (typeof connection === "function") { - throw new Error("connection provider not supported..."); - } - if (!("filename" in connection)) { - throw new Error("sqlite3 config need"); - } - const init_need = !existsSync(connection.filename); - const knex = Knex(config); - let tries = 0; - for (;;) { - try { - console.log("try to connect db"); - await knex.raw("select 1 + 1;"); - console.log("connect success"); - } catch (err) { - if (tries < 3) { - tries++; - console.error(`connection fail ${err} retry...`); - continue; - } else { - throw err; - } - } - break; - } - if (init_need) { - console.log("first execute: initialize database..."); - const migrate = await import("../migrations/initial"); - await migrate.up(knex); - } - return knex; -} diff --git a/src/db/doc.ts b/src/db/doc.ts deleted file mode 100644 index ef48898..0000000 --- a/src/db/doc.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { Knex } from "knex"; -import { Document, DocumentAccessor, DocumentBody, QueryListOption } from "../model/doc"; -import { TagAccessor } from "../model/tag"; -import { createKnexTagController } from "./tag"; - -export type DBTagContentRelation = { - doc_id: number; - tag_name: string; -}; - -class KnexDocumentAccessor implements DocumentAccessor { - knex: Knex; - tagController: TagAccessor; - constructor(knex: Knex) { - this.knex = knex; - this.tagController = createKnexTagController(knex); - } - async search(search_word: string): Promise { - throw new Error("Method not implemented."); - const sw = `%${search_word}%`; - const docs = await this.knex.select("*").from("document") - .where("title", "like", sw); - return docs; - } - async addList(content_list: DocumentBody[]): Promise { - return await this.knex.transaction(async (trx) => { - // add tags - const tagCollected = new Set(); - content_list.map(x => x.tags).forEach((x) => { - x.forEach(x => { - tagCollected.add(x); - }); - }); - const tagCollectPromiseList = []; - const tagController = createKnexTagController(trx); - for (const it of tagCollected) { - const p = tagController.addTag({ name: it }); - tagCollectPromiseList.push(p); - } - await Promise.all(tagCollectPromiseList); - // add for each contents - const ret = []; - for (const content of content_list) { - const { tags, additional, ...rest } = content; - const id_lst = await trx.insert({ - additional: JSON.stringify(additional), - created_at: Date.now(), - ...rest, - }).into("document"); - const id = id_lst[0]; - if (tags.length > 0) { - await trx.insert(tags.map(y => ({ - doc_id: id, - tag_name: y, - }))).into("doc_tag_relation"); - } - ret.push(id); - } - return ret; - }); - } - async add(c: DocumentBody) { - const { tags, additional, ...rest } = c; - const id_lst = await this.knex.insert({ - additional: JSON.stringify(additional), - created_at: Date.now(), - ...rest, - }).into("document"); - const id = id_lst[0]; - for (const it of tags) { - this.tagController.addTag({ name: it }); - } - if (tags.length > 0) { - await this.knex.insert( - tags.map(x => ({ doc_id: id, tag_name: x })), - ).into("doc_tag_relation"); - } - return id; - } - async del(id: number) { - if (await this.findById(id) !== undefined) { - await this.knex.delete().from("doc_tag_relation").where({ doc_id: id }); - await this.knex.delete().from("document").where({ id: id }); - return true; - } - return false; - } - async findById(id: number, tagload?: boolean): Promise { - const s = await this.knex.select("*").from("document").where({ id: id }); - if (s.length === 0) return undefined; - const first = s[0]; - let ret_tags: string[] = []; - if (tagload === true) { - const tags: DBTagContentRelation[] = await this.knex.select("*") - .from("doc_tag_relation").where({ doc_id: first.id }); - ret_tags = tags.map(x => x.tag_name); - } - return { - ...first, - tags: ret_tags, - additional: first.additional !== null ? JSON.parse(first.additional) : {}, - }; - } - async findDeleted(content_type: string) { - const s = await this.knex.select("*") - .where({ content_type: content_type }) - .whereNotNull("update_at") - .from("document"); - return s.map(x => ({ - ...x, - tags: [], - additional: {}, - })); - } - async findList(option?: QueryListOption) { - option = option ?? {}; - const allow_tag = option.allow_tag ?? []; - const eager_loading = option.eager_loading ?? true; - const limit = option.limit ?? 20; - const use_offset = option.use_offset ?? false; - const offset = option.offset ?? 0; - const word = option.word; - const content_type = option.content_type; - const cursor = option.cursor; - - const buildquery = () => { - let query = this.knex.select("document.*"); - if (allow_tag.length > 0) { - query = query.from("doc_tag_relation as tags_0"); - query = query.where("tags_0.tag_name", "=", allow_tag[0]); - for (let index = 1; index < allow_tag.length; index++) { - const element = allow_tag[index]; - query = query.innerJoin( - `doc_tag_relation as tags_${index}`, - `tags_${index}.doc_id`, - "tags_0.doc_id", - ); - query = query.where(`tags_${index}.tag_name`, "=", element); - } - query = query.innerJoin("document", "tags_0.doc_id", "document.id"); - } else { - query = query.from("document"); - } - if (word !== undefined) { - // don't worry about sql injection. - query = query.where("title", "like", `%${word}%`); - } - if (content_type !== undefined) { - query = query.where("content_type", "=", content_type); - } - if (use_offset) { - query = query.offset(offset); - } else { - if (cursor !== undefined) { - query = query.where("id", "<", cursor); - } - } - query = query.limit(limit); - query = query.orderBy("id", "desc"); - return query; - }; - let query = buildquery(); - // console.log(query.toSQL()); - let result: Document[] = await query; - for (let i of result) { - i.additional = JSON.parse((i.additional as unknown) as string); - } - if (eager_loading) { - let idmap: { [index: number]: Document } = {}; - for (const r of result) { - idmap[r.id] = r; - r.tags = []; - } - let subquery = buildquery(); - let tagquery = this.knex.select("id", "doc_tag_relation.tag_name").from(subquery) - .innerJoin("doc_tag_relation", "doc_tag_relation.doc_id", "id"); - // console.log(tagquery.toSQL()); - let tagresult: { id: number; tag_name: string }[] = await tagquery; - for (const { id, tag_name } of tagresult) { - idmap[id].tags.push(tag_name); - } - } else { - result.forEach(v => { - v.tags = []; - }); - } - return result; - } - async findByPath(path: string, filename?: string): Promise { - const e = filename == undefined ? {} : { filename: filename }; - const results = await this.knex.select("*").from("document").where({ basepath: path, ...e }); - return results.map(x => ({ - ...x, - tags: [], - additional: {}, - })); - } - async update(c: Partial & { id: number }) { - const { id, tags, ...rest } = c; - if (await this.findById(id) !== undefined) { - await this.knex.update(rest).where({ id: id }).from("document"); - return true; - } - return false; - } - async addTag(c: Document, tag_name: string) { - if (c.tags.includes(tag_name)) return false; - this.tagController.addTag({ name: tag_name }); - await this.knex.insert({ tag_name: tag_name, doc_id: c.id }) - .into("doc_tag_relation"); - c.tags.push(tag_name); - return true; - } - async delTag(c: Document, tag_name: string) { - if (c.tags.includes(tag_name)) return false; - await this.knex.delete().where({ tag_name: tag_name, doc_id: c.id }).from("doc_tag_relation"); - c.tags.push(tag_name); - return true; - } -} -export const createKnexDocumentAccessor = (knex: Knex): DocumentAccessor => { - return new KnexDocumentAccessor(knex); -}; diff --git a/src/db/tag.ts b/src/db/tag.ts deleted file mode 100644 index dae8909..0000000 --- a/src/db/tag.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Knex } from "knex"; -import { Tag, TagAccessor, TagCount } from "../model/tag"; -import { DBTagContentRelation } from "./doc"; - -type DBTags = { - name: string; - description?: string; -}; - -class KnexTagAccessor implements TagAccessor { - knex: Knex; - constructor(knex: Knex) { - this.knex = knex; - } - async getAllTagCount(): Promise { - const result = await this.knex("doc_tag_relation").select("tag_name") - .count("*", { as: "occurs" }).groupBy("tag_name"); - return result; - } - async getAllTagList(onlyname?: boolean) { - onlyname = onlyname ?? false; - const t: DBTags[] = await this.knex.select(onlyname ? "*" : "name").from("tags"); - return t; - } - async getTagByName(name: string) { - const t: DBTags[] = await this.knex.select("*").from("tags").where({ name: name }); - if (t.length === 0) return undefined; - return t[0]; - } - async addTag(tag: Tag) { - if (await this.getTagByName(tag.name) === undefined) { - await this.knex.insert({ - name: tag.name, - description: tag.description === undefined ? "" : tag.description, - }).into("tags"); - return true; - } - return false; - } - async delTag(name: string) { - if (await this.getTagByName(name) !== undefined) { - await this.knex.delete().where({ name: name }).from("tags"); - return true; - } - return false; - } - async updateTag(name: string, desc: string) { - if (await this.getTagByName(name) !== undefined) { - await this.knex.update({ description: desc }).where({ name: name }).from("tags"); - return true; - } - return false; - } -} -export const createKnexTagController = (knex: Knex): TagAccessor => { - return new KnexTagAccessor(knex); -}; diff --git a/src/db/user.ts b/src/db/user.ts deleted file mode 100644 index 1ff2f15..0000000 --- a/src/db/user.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Knex } from "knex"; -import { IUser, Password, UserAccessor, UserCreateInput } from "../model/user"; - -type PermissionTable = { - username: string; - name: string; -}; -type DBUser = { - username: string; - password_hash: string; - password_salt: string; -}; -class KnexUser implements IUser { - private knex: Knex; - readonly username: string; - readonly password: Password; - - constructor(username: string, pw: Password, knex: Knex) { - this.username = username; - this.password = pw; - this.knex = knex; - } - async reset_password(password: string) { - this.password.set_password(password); - await this.knex.from("users") - .where({ username: this.username }) - .update({ password_hash: this.password.hash, password_salt: this.password.salt }); - } - async get_permissions() { - let b = (await this.knex.select("*").from("permissions") - .where({ username: this.username })) as PermissionTable[]; - return b.map(x => x.name); - } - async add(name: string) { - if (!(await this.get_permissions()).includes(name)) { - const r = await this.knex.insert({ - username: this.username, - name: name, - }).into("permissions"); - return true; - } - return false; - } - async remove(name: string) { - const r = await this.knex - .from("permissions") - .where({ - username: this.username, - name: name, - }).delete(); - return r !== 0; - } -} - -export const createKnexUserController = (knex: Knex): UserAccessor => { - const createUserKnex = async (input: UserCreateInput) => { - if (undefined !== (await findUserKenx(input.username))) { - return undefined; - } - const user = new KnexUser(input.username, new Password(input.password), knex); - await knex.insert({ - username: user.username, - password_hash: user.password.hash, - password_salt: user.password.salt, - }).into("users"); - return user; - }; - const findUserKenx = async (id: string) => { - let user: DBUser[] = await knex.select("*").from("users").where({ username: id }); - if (user.length == 0) return undefined; - const first = user[0]; - return new KnexUser( - first.username, - new Password({ hash: first.password_hash, salt: first.password_salt }), - knex, - ); - }; - const delUserKnex = async (id: string) => { - let r = await knex.delete().from("users").where({ username: id }); - return r === 0; - }; - return { - createUser: createUserKnex, - findUser: findUserKenx, - delUser: delUserKnex, - }; -}; diff --git a/src/diff/content_handler.ts b/src/diff/content_handler.ts deleted file mode 100644 index 11ab44c..0000000 --- a/src/diff/content_handler.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { basename, dirname, join as pathjoin } from "path"; -import { ContentFile, createContentFile } from "../content/mod"; -import { Document, DocumentAccessor } from "../model/mod"; -import { ContentList } from "./content_list"; -import { IDiffWatcher } from "./watcher"; - -// refactoring needed. -export class ContentDiffHandler { - /** content file list waiting to add */ - waiting_list: ContentList; - /** deleted contents */ - tombstone: Map; // hash, contentfile - doc_cntr: DocumentAccessor; - /** content type of handle */ - content_type: string; - constructor(cntr: DocumentAccessor, content_type: string) { - this.waiting_list = new ContentList(); - this.tombstone = new Map(); - this.doc_cntr = cntr; - this.content_type = content_type; - } - async setup() { - const deleted = await this.doc_cntr.findDeleted(this.content_type); - for (const it of deleted) { - this.tombstone.set(it.content_hash, it); - } - } - register(diff: IDiffWatcher) { - diff.on("create", (path) => this.OnCreated(path)) - .on("delete", (path) => this.OnDeleted(path)) - .on("change", (prev, cur) => this.OnChanged(prev, cur)); - } - private async OnDeleted(cpath: string) { - const basepath = dirname(cpath); - const filename = basename(cpath); - console.log("deleted ", cpath); - // if it wait to add, delete it from waiting list. - if (this.waiting_list.hasByPath(cpath)) { - this.waiting_list.deleteByPath(cpath); - return; - } - const dbc = await this.doc_cntr.findByPath(basepath, filename); - // when there is no related content in db, ignore. - if (dbc.length === 0) { - console.log("its not in waiting_list and db!!!: ", cpath); - return; - } - const content_hash = dbc[0].content_hash; - // When a path is changed, it takes into account when the - // creation event occurs first and the deletion occurs, not - // the change event. - const cf = this.waiting_list.getByHash(content_hash); - if (cf) { - // if a path is changed, update the changed path. - console.log("update path from", cpath, "to", cf.path); - const newFilename = basename(cf.path); - const newBasepath = dirname(cf.path); - this.waiting_list.deleteByHash(content_hash); - await this.doc_cntr.update({ - id: dbc[0].id, - deleted_at: null, - filename: newFilename, - basepath: newBasepath, - }); - return; - } - // invalidate db and add it to tombstone. - await this.doc_cntr.update({ - id: dbc[0].id, - deleted_at: Date.now(), - }); - this.tombstone.set(dbc[0].content_hash, dbc[0]); - } - private async OnCreated(cpath: string) { - const basepath = dirname(cpath); - const filename = basename(cpath); - console.log("createContentFile", cpath); - const content = createContentFile(this.content_type, cpath); - const hash = await content.getHash(); - const c = this.tombstone.get(hash); - if (c !== undefined) { - await this.doc_cntr.update({ - id: c.id, - deleted_at: null, - filename: filename, - basepath: basepath, - }); - } - if (this.waiting_list.hasByHash(hash)) { - console.log("Hash Conflict!!!"); - } - this.waiting_list.set(content); - } - private async OnChanged(prev_path: string, cur_path: string) { - const prev_basepath = dirname(prev_path); - const prev_filename = basename(prev_path); - const cur_basepath = dirname(cur_path); - const cur_filename = basename(cur_path); - console.log("modify", cur_path, "from", prev_path); - const c = this.waiting_list.getByPath(prev_path); - if (c !== undefined) { - await this.waiting_list.delete(c); - const content = createContentFile(this.content_type, cur_path); - await this.waiting_list.set(content); - return; - } - const doc = await this.doc_cntr.findByPath(prev_basepath, prev_filename); - - if (doc.length === 0) { - await this.OnCreated(cur_path); - return; - } - - await this.doc_cntr.update({ - ...doc[0], - basepath: cur_basepath, - filename: cur_filename, - }); - } -} diff --git a/src/diff/content_list.ts b/src/diff/content_list.ts deleted file mode 100644 index 7a69cbd..0000000 --- a/src/diff/content_list.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ContentFile } from "../content/mod"; - -export class ContentList { - /** path map */ - private cl: Map; - /** hash map */ - private hl: Map; - - constructor() { - this.cl = new Map(); - this.hl = new Map(); - } - hasByHash(s: string) { - return this.hl.has(s); - } - hasByPath(p: string) { - return this.cl.has(p); - } - getByHash(s: string) { - return this.hl.get(s); - } - getByPath(p: string) { - return this.cl.get(p); - } - async set(c: ContentFile) { - const path = c.path; - const hash = await c.getHash(); - this.cl.set(path, c); - this.hl.set(hash, c); - } - /** delete content file */ - async delete(c: ContentFile) { - const hash = await c.getHash(); - let r = true; - r = this.cl.delete(c.path) && r; - r = this.hl.delete(hash) && r; - return r; - } - async deleteByPath(p: string) { - const o = this.getByPath(p); - if (o === undefined) return false; - return await this.delete(o); - } - deleteByHash(s: string) { - const o = this.getByHash(s); - if (o === undefined) return false; - let r = true; - r = this.cl.delete(o.path) && r; - r = this.hl.delete(s) && r; - return r; - } - clear() { - this.cl.clear(); - this.hl.clear(); - } - getAll() { - return [...this.cl.values()]; - } -} diff --git a/src/diff/diff.ts b/src/diff/diff.ts deleted file mode 100644 index c290ee3..0000000 --- a/src/diff/diff.ts +++ /dev/null @@ -1,45 +0,0 @@ -import asyncPool from "tiny-async-pool"; -import { DocumentAccessor } from "../model/doc"; -import { ContentDiffHandler } from "./content_handler"; -import { IDiffWatcher } from "./watcher"; - -export class DiffManager { - watching: { [content_type: string]: ContentDiffHandler }; - doc_cntr: DocumentAccessor; - constructor(contorller: DocumentAccessor) { - this.watching = {}; - this.doc_cntr = contorller; - } - async register(content_type: string, watcher: IDiffWatcher) { - if (this.watching[content_type] === undefined) { - this.watching[content_type] = new ContentDiffHandler(this.doc_cntr, content_type); - } - this.watching[content_type].register(watcher); - await watcher.setup(this.doc_cntr); - } - async commit(type: string, path: string) { - const list = this.watching[type].waiting_list; - const c = list.getByPath(path); - if (c === undefined) { - throw new Error("path is not exist"); - } - await list.delete(c); - const body = await c.createDocumentBody(); - const id = await this.doc_cntr.add(body); - return id; - } - async commitAll(type: string) { - const list = this.watching[type].waiting_list; - const contentFiles = list.getAll(); - list.clear(); - const bodies = await asyncPool(30, contentFiles, async (x) => await x.createDocumentBody()); - const ids = await this.doc_cntr.addList(bodies); - return ids; - } - getAdded() { - return Object.keys(this.watching).map(x => ({ - type: x, - value: this.watching[x].waiting_list.getAll(), - })); - } -} diff --git a/src/diff/router.ts b/src/diff/router.ts deleted file mode 100644 index 63dc3ee..0000000 --- a/src/diff/router.ts +++ /dev/null @@ -1,83 +0,0 @@ -import Koa from "koa"; -import Router from "koa-router"; -import { ContentFile } from "../content/mod"; -import { AdminOnlyMiddleware } from "../permission/permission"; -import { sendError } from "../route/error_handler"; -import { DiffManager } from "./diff"; - -function content_file_to_return(x: ContentFile) { - return { path: x.path, type: x.type }; -} - -export const getAdded = (diffmgr: DiffManager) => (ctx: Koa.Context, next: Koa.Next) => { - const ret = diffmgr.getAdded(); - ctx.body = ret.map(x => ({ - type: x.type, - value: x.value.map(x => ({ path: x.path, type: x.type })), - })); - ctx.type = "json"; -}; - -type PostAddedBody = { - type: string; - path: string; -}[]; - -function checkPostAddedBody(body: any): body is PostAddedBody { - if (body instanceof Array) { - return body.map(x => "type" in x && "path" in x).every(x => x); - } - return false; -} - -export const postAdded = (diffmgr: DiffManager) => async (ctx: Router.IRouterContext, next: Koa.Next) => { - const reqbody = ctx.request.body; - if (!checkPostAddedBody(reqbody)) { - sendError(400, "format exception"); - return; - } - const allWork = reqbody.map(op => diffmgr.commit(op.type, op.path)); - const results = await Promise.all(allWork); - ctx.body = { - ok: true, - docs: results, - }; - ctx.type = "json"; -}; -export const postAddedAll = (diffmgr: DiffManager) => async (ctx: Router.IRouterContext, next: Koa.Next) => { - if (!ctx.is("json")) { - sendError(400, "format exception"); - return; - } - const reqbody = ctx.request.body as Record; - if (!("type" in reqbody)) { - sendError(400, "format exception: there is no \"type\""); - return; - } - const t = reqbody["type"]; - if (typeof t !== "string") { - sendError(400, "format exception: invalid type of \"type\""); - return; - } - await diffmgr.commitAll(t); - ctx.body = { - ok: true, - }; - ctx.type = "json"; -}; -/* -export const getNotWatched = (diffmgr : DiffManager)=> (ctx:Router.IRouterContext,next:Koa.Next)=>{ - ctx.body = { - added: diffmgr.added.map(content_file_to_return), - deleted: diffmgr.deleted.map(content_file_to_return), - }; - ctx.type = 'json'; -}*/ - -export function createDiffRouter(diffmgr: DiffManager) { - const ret = new Router(); - ret.get("/list", AdminOnlyMiddleware, getAdded(diffmgr)); - ret.post("/commit", AdminOnlyMiddleware, postAdded(diffmgr)); - ret.post("/commitall", AdminOnlyMiddleware, postAddedAll(diffmgr)); - return ret; -} diff --git a/src/diff/watcher.ts b/src/diff/watcher.ts deleted file mode 100644 index f64604e..0000000 --- a/src/diff/watcher.ts +++ /dev/null @@ -1,25 +0,0 @@ -import event from "events"; -import { FSWatcher, watch } from "fs"; -import { promises } from "fs"; -import { join } from "path"; -import { DocumentAccessor } from "../model/doc"; - -const readdir = promises.readdir; - -export interface DiffWatcherEvent { - "create": (path: string) => void; - "delete": (path: string) => void; - "change": (prev_path: string, cur_path: string) => void; -} - -export interface IDiffWatcher extends event.EventEmitter { - on(event: U, listener: DiffWatcherEvent[U]): this; - emit(event: U, ...arg: Parameters): boolean; - setup(cntr: DocumentAccessor): Promise; -} - -export function linkWatcher(fromWatcher: IDiffWatcher, toWatcher: IDiffWatcher) { - fromWatcher.on("create", p => toWatcher.emit("create", p)); - fromWatcher.on("delete", p => toWatcher.emit("delete", p)); - fromWatcher.on("change", (p, c) => toWatcher.emit("change", p, c)); -} diff --git a/src/diff/watcher/ComicConfig.schema.json b/src/diff/watcher/ComicConfig.schema.json deleted file mode 100644 index 50196de..0000000 --- a/src/diff/watcher/ComicConfig.schema.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/ComicConfig", - "definitions": { - "ComicConfig": { - "type": "object", - "properties": { "watch": { "type": "array", "items": { "type": "string" } }, "$schema": { "type": "string" } }, - "required": ["watch"], - "additionalProperties": false - } - } -} diff --git a/src/diff/watcher/comic_watcher.ts b/src/diff/watcher/comic_watcher.ts deleted file mode 100644 index ebad62b..0000000 --- a/src/diff/watcher/comic_watcher.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { EventEmitter } from "events"; -import { DocumentAccessor } from "../../model/doc"; -import { DiffWatcherEvent, IDiffWatcher } from "../watcher"; -import { ComicConfig } from "./ComicConfig"; -import { WatcherCompositer } from "./compositer"; -import { RecursiveWatcher } from "./recursive_watcher"; -import { WatcherFilter } from "./watcher_filter"; - -const createComicWatcherBase = (path: string) => { - return new WatcherFilter(new RecursiveWatcher(path), (x) => x.endsWith(".zip")); -}; -export const createComicWatcher = () => { - const file = ComicConfig.get_config_file(); - console.log(`register comic ${file.watch.join(",")}`); - return new WatcherCompositer(file.watch.map(path => createComicWatcherBase(path))); -}; diff --git a/src/diff/watcher/common_watcher.ts b/src/diff/watcher/common_watcher.ts deleted file mode 100644 index 808645e..0000000 --- a/src/diff/watcher/common_watcher.ts +++ /dev/null @@ -1,44 +0,0 @@ -import event from "events"; -import { FSWatcher, promises, watch } from "fs"; -import { join } from "path"; -import { DocumentAccessor } from "../../model/doc"; -import { DiffWatcherEvent, IDiffWatcher } from "../watcher"; -import { setupHelp } from "./util"; - -const { readdir } = promises; - -export class CommonDiffWatcher extends event.EventEmitter implements IDiffWatcher { - on(event: U, listener: DiffWatcherEvent[U]): this { - return super.on(event, listener); - } - emit(event: U, ...arg: Parameters): boolean { - return super.emit(event, ...arg); - } - private _path: string; - private _watcher: FSWatcher; - - constructor(path: string) { - super(); - this._path = path; - this._watcher = watch(this._path, { persistent: true, recursive: false }, async (eventType, filename) => { - if (eventType === "rename") { - const cur = await readdir(this._path); - // add - if (cur.includes(filename)) { - this.emit("create", join(this.path, filename)); - } else { - this.emit("delete", join(this.path, filename)); - } - } - }); - } - async setup(cntr: DocumentAccessor): Promise { - await setupHelp(this, this.path, cntr); - } - public get path() { - return this._path; - } - watchClose() { - this._watcher.close(); - } -} diff --git a/src/diff/watcher/compositer.ts b/src/diff/watcher/compositer.ts deleted file mode 100644 index e621ab8..0000000 --- a/src/diff/watcher/compositer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { EventEmitter } from "events"; -import { DocumentAccessor } from "../../model/doc"; -import { DiffWatcherEvent, IDiffWatcher, linkWatcher } from "../watcher"; - -export class WatcherCompositer extends EventEmitter implements IDiffWatcher { - refWatchers: IDiffWatcher[]; - on(event: U, listener: DiffWatcherEvent[U]): this { - return super.on(event, listener); - } - emit(event: U, ...arg: Parameters): boolean { - return super.emit(event, ...arg); - } - constructor(refWatchers: IDiffWatcher[]) { - super(); - this.refWatchers = refWatchers; - for (const refWatcher of this.refWatchers) { - linkWatcher(refWatcher, this); - } - } - async setup(cntr: DocumentAccessor): Promise { - await Promise.all(this.refWatchers.map(x => x.setup(cntr))); - } -} diff --git a/src/diff/watcher/recursive_watcher.ts b/src/diff/watcher/recursive_watcher.ts deleted file mode 100644 index 6afadce..0000000 --- a/src/diff/watcher/recursive_watcher.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { FSWatcher, watch } from "chokidar"; -import { EventEmitter } from "events"; -import { join } from "path"; -import { DocumentAccessor } from "../../model/doc"; -import { DiffWatcherEvent, IDiffWatcher } from "../watcher"; -import { setupHelp, setupRecursive } from "./util"; - -type RecursiveWatcherOption = { - /** @default true */ - watchFile?: boolean; - /** @default false */ - watchDir?: boolean; -}; - -export class RecursiveWatcher extends EventEmitter implements IDiffWatcher { - on(event: U, listener: DiffWatcherEvent[U]): this { - return super.on(event, listener); - } - emit(event: U, ...arg: Parameters): boolean { - return super.emit(event, ...arg); - } - readonly path: string; - private watcher: FSWatcher; - - constructor(path: string, option: RecursiveWatcherOption = { - watchDir: false, - watchFile: true, - }) { - super(); - this.path = path; - this.watcher = watch(path, { - persistent: true, - ignoreInitial: true, - depth: 100, - }); - option.watchFile ??= true; - if (option.watchFile) { - this.watcher.on("add", path => { - const cpath = path; - // console.log("add ", cpath); - this.emit("create", cpath); - }).on("unlink", path => { - const cpath = path; - // console.log("unlink ", cpath); - this.emit("delete", cpath); - }); - } - if (option.watchDir) { - this.watcher.on("addDir", path => { - const cpath = path; - this.emit("create", cpath); - }).on("unlinkDir", path => { - const cpath = path; - this.emit("delete", cpath); - }); - } - } - async setup(cntr: DocumentAccessor): Promise { - await setupRecursive(this, this.path, cntr); - } -} diff --git a/src/diff/watcher/util.ts b/src/diff/watcher/util.ts deleted file mode 100644 index 8e51d7f..0000000 --- a/src/diff/watcher/util.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { EventEmitter } from "events"; -import { promises } from "fs"; -import { join } from "path"; -const { readdir } = promises; -import { DocumentAccessor } from "../../model/doc"; -import { IDiffWatcher } from "../watcher"; - -function setupCommon(watcher: IDiffWatcher, basepath: string, initial_filenames: string[], cur: string[]) { - // Todo : reduce O(nm) to O(n+m) using hash map. - let added = cur.filter(x => !initial_filenames.includes(x)); - let deleted = initial_filenames.filter(x => !cur.includes(x)); - for (const it of added) { - const cpath = join(basepath, it); - watcher.emit("create", cpath); - } - for (const it of deleted) { - const cpath = join(basepath, it); - watcher.emit("delete", cpath); - } -} -export async function setupHelp(watcher: IDiffWatcher, basepath: string, cntr: DocumentAccessor) { - const initial_document = await cntr.findByPath(basepath); - const initial_filenames = initial_document.map(x => x.filename); - const cur = await readdir(basepath); - setupCommon(watcher, basepath, initial_filenames, cur); -} -export async function setupRecursive(watcher: IDiffWatcher, basepath: string, cntr: DocumentAccessor) { - const initial_document = await cntr.findByPath(basepath); - const initial_filenames = initial_document.map(x => x.filename); - const cur = await readdir(basepath, { withFileTypes: true }); - setupCommon(watcher, basepath, initial_filenames, cur.map(x => x.name)); - await Promise.all([ - cur.filter(x => x.isDirectory()) - .map(x => setupHelp(watcher, join(basepath, x.name), cntr)), - ]); -} diff --git a/src/diff/watcher/watcher_filter.ts b/src/diff/watcher/watcher_filter.ts deleted file mode 100644 index 381b807..0000000 --- a/src/diff/watcher/watcher_filter.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { EventEmitter } from "events"; -import { DocumentAccessor } from "../../model/doc"; -import { DiffWatcherEvent, IDiffWatcher, linkWatcher } from "../watcher"; - -export class WatcherFilter extends EventEmitter implements IDiffWatcher { - refWatcher: IDiffWatcher; - filter: (filename: string) => boolean; - on(event: U, listener: DiffWatcherEvent[U]): this { - return super.on(event, listener); - } - /** - * emit event - * @param event - * @param arg - * @returns `true` if the event had listeners, `false` otherwise. - */ - emit(event: U, ...arg: Parameters): boolean { - if (event === "change") { - const prev = arg[0]; - const cur = arg[1] as string; - if (this.filter(prev)) { - if (this.filter(cur)) { - return super.emit("change", prev, cur); - } else { - return super.emit("delete", cur); - } - } else { - if (this.filter(cur)) { - return super.emit("create", cur); - } - } - return false; - } else if (!this.filter(arg[0])) { - return false; - } else return super.emit(event, ...arg); - } - constructor(refWatcher: IDiffWatcher, filter: (filename: string) => boolean) { - super(); - this.refWatcher = refWatcher; - this.filter = filter; - linkWatcher(refWatcher, this); - } - setup(cntr: DocumentAccessor): Promise { - return this.refWatcher.setup(cntr); - } -} diff --git a/src/login.ts b/src/login.ts deleted file mode 100644 index a3a1cbf..0000000 --- a/src/login.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { request } from "http"; -import { decode, sign, TokenExpiredError, verify } from "jsonwebtoken"; -import Knex from "knex"; -import Koa from "koa"; -import Router from "koa-router"; -import { createKnexUserController } from "./db/mod"; -import { IUser, UserAccessor } from "./model/mod"; -import { sendError } from "./route/error_handler"; -import { get_setting } from "./SettingConfig"; - -type PayloadInfo = { - username: string; - permission: string[]; -}; - -export type UserState = { - user: PayloadInfo; -}; - -const isUserState = (obj: object | string): obj is PayloadInfo => { - if (typeof obj === "string") return false; - return "username" in obj && "permission" in obj - && (obj as { permission: unknown }).permission instanceof Array; -}; -type RefreshPayloadInfo = { username: string }; -const isRefreshToken = (obj: object | string): obj is RefreshPayloadInfo => { - if (typeof obj === "string") return false; - return "username" in obj - && typeof (obj as { username: unknown }).username === "string"; -}; - -export const accessTokenName = "access_token"; -export const refreshTokenName = "refresh_token"; -const accessExpiredTime = 60 * 60; // 1 hour -const refreshExpiredTime = 60 * 60 * 24 * 14; // 14 day; - -export const getAdminAccessTokenValue = () => { - const { jwt_secretkey } = get_setting(); - return publishAccessToken(jwt_secretkey, "admin", [], accessExpiredTime); -}; -export const getAdminRefreshTokenValue = () => { - const { jwt_secretkey } = get_setting(); - return publishRefreshToken(jwt_secretkey, "admin", refreshExpiredTime); -}; -const publishAccessToken = ( - secretKey: string, - username: string, - permission: string[], - expiredtime: number, -) => { - const payload = sign( - { - username: username, - permission: permission, - }, - secretKey, - { expiresIn: expiredtime }, - ); - return payload; -}; -const publishRefreshToken = ( - secretKey: string, - username: string, - expiredtime: number, -) => { - const payload = sign( - { username: username }, - secretKey, - { expiresIn: expiredtime }, - ); - return payload; -}; -function setToken( - ctx: Koa.Context, - token_name: string, - token_payload: string | null, - expiredtime: number, -) { - const setting = get_setting(); - if (token_payload === null && !!!ctx.cookies.get(token_name)) { - return; - } - ctx.cookies.set(token_name, token_payload, { - httpOnly: true, - secure: setting.secure, - sameSite: "strict", - expires: new Date(Date.now() + expiredtime * 1000), - }); -} -export const createLoginMiddleware = (userController: UserAccessor) => async (ctx: Koa.Context, _next: Koa.Next) => { - const setting = get_setting(); - const secretKey = setting.jwt_secretkey; - const body = ctx.request.body; - // check format - if (typeof body == "string" || !("username" in body) || !("password" in body)) { - return sendError( - 400, - "invalid form : username or password is not found in query.", - ); - } - const username = body["username"]; - const password = body["password"]; - // check type - if (typeof username !== "string" || typeof password !== "string") { - return sendError( - 400, - "invalid form : username or password is not string", - ); - } - // if admin login is forbidden? - if (username === "admin" && setting.forbid_remote_admin_login) { - return sendError(403, "forbidden remote admin login"); - } - const user = await userController.findUser(username); - // username not exist - if (user === undefined) return sendError(401, "not authorized"); - // password not matched - if (!user.password.check_password(password)) { - return sendError(401, "not authorized"); - } - // create token - const userPermission = await user.get_permissions(); - const payload = publishAccessToken( - secretKey, - user.username, - userPermission, - accessExpiredTime, - ); - const payload2 = publishRefreshToken( - secretKey, - user.username, - refreshExpiredTime, - ); - setToken(ctx, accessTokenName, payload, accessExpiredTime); - setToken(ctx, refreshTokenName, payload2, refreshExpiredTime); - ctx.body = { - username: user.username, - permission: userPermission, - accessExpired: (Math.floor(Date.now() / 1000) + accessExpiredTime), - }; - console.log(`${username} logined`); - return; -}; - -export const LogoutMiddleware = (ctx: Koa.Context, next: Koa.Next) => { - const setting = get_setting(); - ctx.cookies.set(accessTokenName, null); - ctx.cookies.set(refreshTokenName, null); - ctx.body = { - ok: true, - username: "", - permission: setting.guest, - }; - return; -}; -export const createUserMiddleWare = - (userController: UserAccessor) => async (ctx: Koa.ParameterizedContext, next: Koa.Next) => { - const refreshToken = refreshTokenHandler(userController); - const setting = get_setting(); - const setGuest = async () => { - setToken(ctx, accessTokenName, null, 0); - setToken(ctx, refreshTokenName, null, 0); - ctx.state["user"] = { username: "", permission: setting.guest }; - return await next(); - }; - return await refreshToken(ctx, setGuest, next); - }; -const refreshTokenHandler = (cntr: UserAccessor) => async (ctx: Koa.Context, fail: Koa.Next, next: Koa.Next) => { - const accessPayload = ctx.cookies.get(accessTokenName); - const setting = get_setting(); - const secretKey = setting.jwt_secretkey; - if (accessPayload == undefined) { - return await checkRefreshAndUpdate(); - } - try { - const o = verify(accessPayload, secretKey); - if (isUserState(o)) { - ctx.state.user = o; - return await next(); - } else { - console.error("invalid token detected"); - throw new Error("token form invalid"); - } - } catch (e) { - if (e instanceof TokenExpiredError) { - return await checkRefreshAndUpdate(); - } else throw e; - } - async function checkRefreshAndUpdate() { - const refreshPayload = ctx.cookies.get(refreshTokenName); - if (refreshPayload === undefined) { - return await fail(); // refresh token doesn't exist - } else { - try { - const o = verify(refreshPayload, secretKey); - if (isRefreshToken(o)) { - const user = await cntr.findUser(o.username); - if (user === undefined) return await fail(); // already non-existence user - const perm = await user.get_permissions(); - const payload = publishAccessToken( - secretKey, - user.username, - perm, - accessExpiredTime, - ); - setToken(ctx, accessTokenName, payload, accessExpiredTime); - ctx.state.user = { username: o.username, permission: perm }; - } else { - console.error("invalid token detected"); - throw new Error("token form invalid"); - } - } catch (e) { - if (e instanceof TokenExpiredError) { // refresh token is expired. - return await fail(); - } else throw e; - } - } - return await next(); - } -}; -export const createRefreshTokenMiddleware = (cntr: UserAccessor) => async (ctx: Koa.Context, next: Koa.Next) => { - const handler = refreshTokenHandler(cntr); - await handler(ctx, fail, success); - async function fail() { - const user = ctx.state.user as PayloadInfo; - ctx.body = { - refresh: false, - ...user, - }; - ctx.type = "json"; - } - async function success() { - const user = ctx.state.user as PayloadInfo; - ctx.body = { - ...user, - refresh: true, - refreshExpired: Math.floor(Date.now() / 1000 + accessExpiredTime), - }; - ctx.type = "json"; - } -}; -export const resetPasswordMiddleware = (cntr: UserAccessor) => async (ctx: Koa.Context, next: Koa.Next) => { - const body = ctx.request.body; - if (typeof body !== "object" || !("username" in body) || !("oldpassword" in body) || !("newpassword" in body)) { - return sendError(400, "request body is invalid format"); - } - const username = body["username"]; - const oldpw = body["oldpassword"]; - const newpw = body["newpassword"]; - if (typeof username !== "string" || typeof oldpw !== "string" || typeof newpw !== "string") { - return sendError(400, "request body is invalid format"); - } - const user = await cntr.findUser(username); - if (user === undefined) { - return sendError(403, "not authorized"); - } - if (!user.password.check_password(oldpw)) { - return sendError(403, "not authorized"); - } - user.reset_password(newpw); - ctx.body = { ok: true }; - ctx.type = "json"; -}; - -export function createLoginRouter(userController: UserAccessor) { - const router = new Router(); - router.post("/login", createLoginMiddleware(userController)); - router.post("/logout", LogoutMiddleware); - router.post("/refresh", createRefreshTokenMiddleware(userController)); - router.post("/reset", resetPasswordMiddleware(userController)); - return router; -} - -export const getAdmin = async (cntr: UserAccessor) => { - const admin = await cntr.findUser("admin"); - if (admin === undefined) { - throw new Error("initial process failed!"); // ??? - } - return admin; -}; - -export const isAdminFirst = (admin: IUser) => { - return admin.password.hash === "unchecked" - && admin.password.salt === "unchecked"; -}; diff --git a/src/model/doc.ts b/src/model/doc.ts deleted file mode 100644 index 7954de0..0000000 --- a/src/model/doc.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { JSONMap } from "../types/json"; -import { check_type } from "../util/type_check"; -import { TagAccessor } from "./tag"; - -export interface DocumentBody { - title: string; - content_type: string; - basepath: string; - filename: string; - modified_at: number; - content_hash: string; - additional: JSONMap; - tags: string[]; // eager loading -} - -export const MetaContentBody = { - title: "string", - content_type: "string", - basepath: "string", - filename: "string", - content_hash: "string", - additional: "object", - tags: "string[]", -}; - -export const isDocBody = (c: any): c is DocumentBody => { - return check_type(c, MetaContentBody); -}; - -export interface Document extends DocumentBody { - readonly id: number; - readonly created_at: number; - readonly deleted_at: number | null; -} - -export const isDoc = (c: any): c is Document => { - if ("id" in c && typeof c["id"] === "number") { - const { id, ...rest } = c; - return isDocBody(rest); - } - return false; -}; - -export type QueryListOption = { - /** - * search word - */ - word?: string; - allow_tag?: string[]; - /** - * limit of list - * @default 20 - */ - limit?: number; - /** - * use offset if true, otherwise - * @default false - */ - use_offset?: boolean; - /** - * cursor of documents - */ - cursor?: number; - /** - * offset of documents - */ - offset?: number; - /** - * tag eager loading - * @default true - */ - eager_loading?: boolean; - /** - * content type - */ - content_type?: string; -}; - -export interface DocumentAccessor { - /** - * find list by option - * @returns documents list - */ - findList: (option?: QueryListOption) => Promise; - /** - * @returns document if exist, otherwise undefined - */ - findById: (id: number, tagload?: boolean) => Promise; - /** - * find by base path and filename. - * if you call this function with filename, its return array length is 0 or 1. - */ - findByPath: (basepath: string, filename?: string) => Promise; - /** - * find deleted content - */ - findDeleted: (content_type: string) => Promise; - /** - * search by in document - */ - search: (search_word: string) => Promise; - /** - * update document except tag. - */ - update: (c: Partial & { id: number }) => Promise; - /** - * add document - */ - add: (c: DocumentBody) => Promise; - /** - * add document list - */ - addList: (content_list: DocumentBody[]) => Promise; - /** - * delete document - * @returns if it exists, return true. - */ - del: (id: number) => Promise; - /** - * @param c Valid Document - * @param tagname tag name to add - * @returns if success, return true - */ - addTag: (c: Document, tag_name: string) => Promise; - /** - * @returns if success, return true - */ - delTag: (c: Document, tag_name: string) => Promise; -} diff --git a/src/model/tag.ts b/src/model/tag.ts deleted file mode 100644 index 74d945c..0000000 --- a/src/model/tag.ts +++ /dev/null @@ -1,18 +0,0 @@ -export interface Tag { - readonly name: string; - description?: string; -} - -export interface TagCount { - tag_name: string; - occurs: number; -} - -export interface TagAccessor { - getAllTagList: (onlyname?: boolean) => Promise; - getAllTagCount(): Promise; - getTagByName: (name: string) => Promise; - addTag: (tag: Tag) => Promise; - delTag: (name: string) => Promise; - updateTag: (name: string, tag: string) => Promise; -} diff --git a/src/model/user.ts b/src/model/user.ts deleted file mode 100644 index eb7c14b..0000000 --- a/src/model/user.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { createHmac, randomBytes } from "crypto"; - -function hashForPassword(salt: string, password: string) { - return createHmac("sha256", salt).update(password).digest("hex"); -} -function createPasswordHashAndSalt(password: string): { salt: string; hash: string } { - const secret = randomBytes(32).toString("hex"); - return { - salt: secret, - hash: hashForPassword(secret, password), - }; -} - -export class Password { - private _salt: string; - private _hash: string; - constructor(pw: string | { salt: string; hash: string }) { - const { salt, hash } = typeof pw === "string" ? createPasswordHashAndSalt(pw) : pw; - this._hash = hash; - this._salt = salt; - } - set_password(password: string) { - const { salt, hash } = createPasswordHashAndSalt(password); - this._hash = hash; - this._salt = salt; - } - check_password(password: string): boolean { - return this._hash === hashForPassword(this._salt, password); - } - get salt() { - return this._salt; - } - get hash() { - return this._hash; - } -} - -export interface UserCreateInput { - username: string; - password: string; -} - -export interface IUser { - readonly username: string; - readonly password: Password; - /** - * return user's permission list. - */ - get_permissions(): Promise; - /** - * add permission - * @param name permission name to add - * @returns if `name` doesn't exist, return true - */ - add(name: string): Promise; - /** - * remove permission - * @param name permission name to remove - * @returns if `name` exist, return true - */ - remove(name: string): Promise; - /** - * reset password. - * @param password password to set - */ - reset_password(password: string): Promise; -} - -export interface UserAccessor { - /** - * create user - * @returns if user exist, return undefined - */ - createUser: (input: UserCreateInput) => Promise; - /** - * find user - */ - findUser: (username: string) => Promise; - /** - * remove user - * @returns if user exist, true - */ - delUser: (username: string) => Promise; -} diff --git a/src/permission/permission.ts b/src/permission/permission.ts deleted file mode 100644 index 2d06225..0000000 --- a/src/permission/permission.ts +++ /dev/null @@ -1,58 +0,0 @@ -import Koa from "koa"; -import { UserState } from "../login"; -import { sendError } from "../route/error_handler"; - -export enum Permission { - // ======== - // not implemented - // admin only - /** remove document */ - // removeContent = 'removeContent', - - /** upload document */ - // uploadContent = 'uploadContent', - - /** modify document except base path, filename, content_hash. but admin can modify all. */ - // modifyContent = 'modifyContent', - - /** add tag into document */ - // addTagContent = 'addTagContent', - /** remove tag from document */ - // removeTagContent = 'removeTagContent', - /** ModifyTagInDoc */ - ModifyTag = "ModifyTag", - - /** find documents with query */ - // findAllContent = 'findAllContent', - /** find one document. */ - // findOneContent = 'findOneContent', - /** view content*/ - // viewContent = 'viewContent', - QueryContent = "QueryContent", - - /** modify description about the one tag. */ - modifyTagDesc = "ModifyTagDesc", -} - -export const createPermissionCheckMiddleware = - (...permissions: string[]) => async (ctx: Koa.ParameterizedContext, next: Koa.Next) => { - const user = ctx.state["user"]; - if (user.username === "admin") { - return await next(); - } - const user_permission = user.permission; - // if permissions is not subset of user permission - if (!permissions.map(p => user_permission.includes(p)).every(x => x)) { - if (user.username === "") { - return sendError(401, "you are guest. login needed."); - } else return sendError(403, "do not have permission"); - } - await next(); - }; -export const AdminOnlyMiddleware = async (ctx: Koa.ParameterizedContext, next: Koa.Next) => { - const user = ctx.state["user"]; - if (user.username !== "admin") { - return sendError(403, "admin only"); - } - await next(); -}; diff --git a/src/route/all.ts b/src/route/all.ts deleted file mode 100644 index 381226b..0000000 --- a/src/route/all.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { DefaultContext, Middleware, Next, ParameterizedContext } from "koa"; -import compose from "koa-compose"; -import Router, { IParamMiddleware } from "koa-router"; -import ComicRouter from "./comic"; -import { ContentContext } from "./context"; -import VideoRouter from "./video"; - -const table: { [s: string]: Router | undefined } = { - "comic": new ComicRouter(), - "video": new VideoRouter(), -}; -const all_middleware = - (cont: string | undefined, restarg: string | undefined) => - async (ctx: ParameterizedContext, next: Next) => { - if (cont == undefined) { - ctx.status = 404; - return; - } - if (ctx.state.location.type != cont) { - console.error("not matched"); - ctx.status = 404; - return; - } - const router = table[cont]; - if (router == undefined) { - ctx.status = 404; - return; - } - const rest = "/" + (restarg ?? ""); - const result = router.match(rest, "GET"); - if (!result.route) { - return await next(); - } - const chain = result.pathAndMethod.reduce((combination: Middleware[], cur) => { - combination.push(async (ctx, next) => { - const captures = cur.captures(rest); - ctx.params = cur.params(rest, captures); - ctx.request.params = ctx.params; - ctx.routerPath = cur.path; - return await next(); - }); - return combination.concat(cur.stack); - }, []); - return await compose(chain)(ctx, next); - }; -export class AllContentRouter extends Router { - constructor() { - super(); - this.get("/:content_type", async (ctx, next) => { - return await (all_middleware(ctx.params["content_type"], undefined))(ctx, next); - }); - this.get("/:content_type/:rest(.*)", async (ctx, next) => { - const cont = ctx.params["content_type"] as string; - return await (all_middleware(cont, ctx.params["rest"]))(ctx, next); - }); - } -} diff --git a/src/route/comic.ts b/src/route/comic.ts deleted file mode 100644 index 875fd02..0000000 --- a/src/route/comic.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Context, DefaultContext, DefaultState, Next } from "koa"; -import Router from "koa-router"; -import { createReadableStreamFromZip, entriesByNaturalOrder, readZip, ZipAsync } from "../util/zipwrap"; -import { ContentContext } from "./context"; -import { since_last_modified } from "./util"; - -/** - * zip stream cache. - */ - -let ZipStreamCache: { [path: string]: [ZipAsync, number] } = {}; - -async function acquireZip(path: string) { - if (!(path in ZipStreamCache)) { - const ret = await readZip(path); - ZipStreamCache[path] = [ret, 1]; - // console.log(`acquire ${path} 1`); - return ret; - } else { - const [ret, refCount] = ZipStreamCache[path]; - ZipStreamCache[path] = [ret, refCount + 1]; - // console.log(`acquire ${path} ${refCount + 1}`); - return ret; - } -} - -function releaseZip(path: string) { - const obj = ZipStreamCache[path]; - if (obj === undefined) throw new Error("error! key invalid"); - const [ref, refCount] = obj; - // console.log(`release ${path} : ${refCount}`); - if (refCount === 1) { - ref.close(); - delete ZipStreamCache[path]; - } else { - ZipStreamCache[path] = [ref, refCount - 1]; - } -} - -async function renderZipImage(ctx: Context, path: string, page: number) { - const image_ext = ["gif", "png", "jpeg", "bmp", "webp", "jpg"]; - // console.log(`opened ${page}`); - let zip = await acquireZip(path); - const entries = (await entriesByNaturalOrder(zip)).filter((x) => { - const ext = x.name.split(".").pop(); - return ext !== undefined && image_ext.includes(ext); - }); - if (0 <= page && page < entries.length) { - const entry = entries[page]; - const last_modified = new Date(entry.time); - if (since_last_modified(ctx, last_modified)) { - return; - } - const read_stream = await createReadableStreamFromZip(zip, entry); - /** Exceptions (ECONNRESET, ECONNABORTED) may be thrown when processing this request - * for reasons such as when the browser unexpectedly closes the connection. - * Once such an exception is raised, the stream is not properly destroyed, - * so there is a problem with the zlib stream being accessed even after the stream is closed. - * So it waits for 100 ms and releases it. - * Additionaly, there is a risk of memory leak becuase zlib stream is not properly destroyed. - * @todo modify function 'stream' in 'node-stream-zip' library to prevent memory leak */ - read_stream.once("close", () => { - setTimeout(() => { - releaseZip(path); - }, 100); - }); - - ctx.body = read_stream; - ctx.response.length = entry.size; - // console.log(`${entry.name}'s ${page}:${entry.size}`); - ctx.response.type = entry.name.split(".").pop() as string; - ctx.status = 200; - ctx.set("Date", new Date().toUTCString()); - ctx.set("Last-Modified", last_modified.toUTCString()); - } else { - ctx.status = 404; - } -} - -export class ComicRouter extends Router { - constructor() { - super(); - this.get("/", async (ctx, next) => { - await renderZipImage(ctx, ctx.state.location.path, 0); - }); - this.get("/:page(\\d+)", async (ctx, next) => { - const page = Number.parseInt(ctx.params["page"]); - await renderZipImage(ctx, ctx.state.location.path, page); - }); - this.get("/thumbnail", async (ctx, next) => { - await renderZipImage(ctx, ctx.state.location.path, 0); - }); - } -} - -export default ComicRouter; diff --git a/src/route/contents.ts b/src/route/contents.ts deleted file mode 100644 index 1f737a1..0000000 --- a/src/route/contents.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { Context, Next } from "koa"; -import Router from "koa-router"; -import { join } from "path"; -import { Document, DocumentAccessor, isDocBody } from "../model/doc"; -import { QueryListOption } from "../model/doc"; -import { - AdminOnlyMiddleware as AdminOnly, - createPermissionCheckMiddleware as PerCheck, - Permission as Per, -} from "../permission/permission"; -import { AllContentRouter } from "./all"; -import { ContentLocation } from "./context"; -import { sendError } from "./error_handler"; -import { ParseQueryArgString, ParseQueryArray, ParseQueryBoolean, ParseQueryNumber } from "./util"; - -const ContentIDHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - const num = Number.parseInt(ctx.params["num"]); - let document = await controller.findById(num, true); - if (document == undefined) { - return sendError(404, "document does not exist."); - } - ctx.body = document; - ctx.type = "json"; - console.log(document.additional); -}; -const ContentTagIDHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - const num = Number.parseInt(ctx.params["num"]); - let document = await controller.findById(num, true); - if (document == undefined) { - return sendError(404, "document does not exist."); - } - ctx.body = document.tags; - ctx.type = "json"; -}; -const ContentQueryHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - let query_limit = ctx.query["limit"]; - let query_cursor = ctx.query["cursor"]; - let query_word = ctx.query["word"]; - let query_content_type = ctx.query["content_type"]; - let query_offset = ctx.query["offset"]; - let query_use_offset = ctx.query["use_offset"]; - if ( - query_limit instanceof Array - || query_cursor instanceof Array - || query_word instanceof Array - || query_content_type instanceof Array - || query_offset instanceof Array - || query_use_offset instanceof Array - ) { - return sendError(400, "paramter can not be array"); - } - const limit = ParseQueryNumber(query_limit); - const cursor = ParseQueryNumber(query_cursor); - const word = ParseQueryArgString(query_word); - const content_type = ParseQueryArgString(query_content_type); - const offset = ParseQueryNumber(query_offset); - if (limit === NaN || cursor === NaN || offset === NaN) { - return sendError(400, "parameter limit, cursor or offset is not a number"); - } - const allow_tag = ParseQueryArray(ctx.query["allow_tag"]); - const [ok, use_offset] = ParseQueryBoolean(query_use_offset); - if (!ok) { - return sendError(400, "use_offset must be true or false."); - } - const option: QueryListOption = { - limit: limit, - allow_tag: allow_tag, - word: word, - cursor: cursor, - eager_loading: true, - offset: offset, - use_offset: use_offset, - content_type: content_type, - }; - let document = await controller.findList(option); - ctx.body = document; - ctx.type = "json"; -}; -const UpdateContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - const num = Number.parseInt(ctx.params["num"]); - - if (ctx.request.type !== "json") { - return sendError(400, "update fail. invalid document type: it is not json."); - } - if (typeof ctx.request.body !== "object") { - return sendError(400, "update fail. invalid argument: not"); - } - const content_desc: Partial & { id: number } = { - id: num, - ...ctx.request.body, - }; - const success = await controller.update(content_desc); - ctx.body = JSON.stringify(success); - ctx.type = "json"; -}; - -const AddTagHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - let tag_name = ctx.params["tag"]; - const num = Number.parseInt(ctx.params["num"]); - if (typeof tag_name === undefined) { - return sendError(400, "??? Unreachable"); - } - tag_name = String(tag_name); - const c = await controller.findById(num); - if (c === undefined) { - return sendError(404); - } - const r = await controller.addTag(c, tag_name); - ctx.body = JSON.stringify(r); - ctx.type = "json"; -}; -const DelTagHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - let tag_name = ctx.params["tag"]; - const num = Number.parseInt(ctx.params["num"]); - if (typeof tag_name === undefined) { - return sendError(400, "?? Unreachable"); - } - tag_name = String(tag_name); - const c = await controller.findById(num); - if (c === undefined) { - return sendError(404); - } - const r = await controller.delTag(c, tag_name); - ctx.body = JSON.stringify(r); - ctx.type = "json"; -}; -const DeleteContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - const num = Number.parseInt(ctx.params["num"]); - const r = await controller.del(num); - ctx.body = JSON.stringify(r); - ctx.type = "json"; -}; -const ContentHandler = (controller: DocumentAccessor) => async (ctx: Context, next: Next) => { - const num = Number.parseInt(ctx.params["num"]); - let document = await controller.findById(num, true); - if (document == undefined) { - return sendError(404, "document does not exist."); - } - if (document.deleted_at !== null) { - return sendError(404, "document has been removed."); - } - const path = join(document.basepath, document.filename); - ctx.state["location"] = { - path: path, - type: document.content_type, - additional: document.additional, - } as ContentLocation; - await next(); -}; - -export const getContentRouter = (controller: DocumentAccessor) => { - const ret = new Router(); - ret.get("/search", PerCheck(Per.QueryContent), ContentQueryHandler(controller)); - ret.get("/:num(\\d+)", PerCheck(Per.QueryContent), ContentIDHandler(controller)); - ret.post("/:num(\\d+)", AdminOnly, UpdateContentHandler(controller)); - // ret.use("/:num(\\d+)/:content_type"); - // ret.post("/",AdminOnly,CreateContentHandler(controller)); - ret.get("/:num(\\d+)/tags", PerCheck(Per.QueryContent), ContentTagIDHandler(controller)); - ret.post("/:num(\\d+)/tags/:tag", PerCheck(Per.ModifyTag), AddTagHandler(controller)); - ret.del("/:num(\\d+)/tags/:tag", PerCheck(Per.ModifyTag), DelTagHandler(controller)); - ret.del("/:num(\\d+)", AdminOnly, DeleteContentHandler(controller)); - ret.all("/:num(\\d+)/(.*)", PerCheck(Per.QueryContent), ContentHandler(controller)); - ret.use("/:num(\\d+)", PerCheck(Per.QueryContent), (new AllContentRouter()).routes()); - return ret; -}; - -export default getContentRouter; diff --git a/src/route/context.ts b/src/route/context.ts deleted file mode 100644 index 19e44d9..0000000 --- a/src/route/context.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type ContentLocation = { - path: string; - type: string; - additional: object | undefined; -}; -export interface ContentContext { - location: ContentLocation; -} diff --git a/src/route/error_handler.ts b/src/route/error_handler.ts deleted file mode 100644 index 5d3e84f..0000000 --- a/src/route/error_handler.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Context, Next } from "koa"; - -export interface ErrorFormat { - code: number; - message: string; - detail?: string; -} - -class ClientRequestError implements Error { - name: string; - message: string; - stack?: string | undefined; - code: number; - - constructor(code: number, message: string) { - this.name = "client request error"; - this.message = message; - this.code = code; - } -} - -const code_to_message_table: { [key: number]: string | undefined } = { - 400: "BadRequest", - 404: "NotFound", -}; - -export const error_handler = async (ctx: Context, next: Next) => { - try { - await next(); - } catch (err) { - if (err instanceof ClientRequestError) { - const body: ErrorFormat = { - code: err.code, - message: code_to_message_table[err.code] ?? "", - detail: err.message, - }; - ctx.status = err.code; - ctx.body = body; - } else { - throw err; - } - } -}; - -export const sendError = (code: number, message?: string) => { - throw new ClientRequestError(code, message ?? ""); -}; - -export default error_handler; diff --git a/src/route/tags.ts b/src/route/tags.ts deleted file mode 100644 index aa86d67..0000000 --- a/src/route/tags.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Context, Next } from "koa"; -import Router, { RouterContext } from "koa-router"; -import { TagAccessor } from "../model/tag"; -import { createPermissionCheckMiddleware as PerCheck, Permission } from "../permission/permission"; -import { sendError } from "./error_handler"; - -export function getTagRounter(tagController: TagAccessor) { - let router = new Router(); - router.get("/", PerCheck(Permission.QueryContent), async (ctx: Context) => { - if (ctx.query["withCount"]) { - const c = await tagController.getAllTagCount(); - ctx.body = c; - } else { - const c = await tagController.getAllTagList(); - ctx.body = c; - } - ctx.type = "json"; - }); - router.get("/:tag_name", PerCheck(Permission.QueryContent), async (ctx: RouterContext) => { - const tag_name = ctx.params["tag_name"]; - const c = await tagController.getTagByName(tag_name); - if (!c) { - sendError(404, "tags not found"); - } - ctx.body = c; - ctx.type = "json"; - }); - return router; -} diff --git a/src/route/util.ts b/src/route/util.ts deleted file mode 100644 index 8a64a41..0000000 --- a/src/route/util.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Context } from "koa"; - -export function ParseQueryNumber(s: string[] | string | undefined): number | undefined { - if (s === undefined) return undefined; - else if (typeof s === "object") return undefined; - else return Number.parseInt(s); -} -export function ParseQueryArray(s: string[] | string | undefined) { - s = s ?? []; - const r = s instanceof Array ? s : [s]; - return r.map(x => decodeURIComponent(x)); -} -export function ParseQueryArgString(s: string[] | string | undefined) { - if (typeof s === "object") return undefined; - return s === undefined ? s : decodeURIComponent(s); -} -export function ParseQueryBoolean(s: string[] | string | undefined): [boolean, boolean | undefined] { - let value: boolean | undefined; - - if (s === "true") { - value = true; - } else if (s === "false") { - value = false; - } else if (s === undefined) { - value = undefined; - } else return [false, undefined]; - return [true, value]; -} - -export function since_last_modified(ctx: Context, last_modified: Date): boolean { - const con = ctx.get("If-Modified-Since"); - if (con === "") return false; - const mdate = new Date(con); - if (last_modified > mdate) return false; - ctx.status = 304; - return true; -} diff --git a/src/route/video.ts b/src/route/video.ts deleted file mode 100644 index f52499d..0000000 --- a/src/route/video.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { createReadStream, promises } from "fs"; -import { Context } from "koa"; -import Router from "koa-router"; -import { ContentContext } from "./context"; - -export async function renderVideo(ctx: Context, path: string) { - const ext = path.trim().split(".").pop(); - if (ext === undefined) { - // ctx.status = 404; - console.error(`${path}:${ext}`); - return; - } - ctx.response.type = ext; - const range_text = ctx.request.get("range"); - const stat = await promises.stat(path); - let start = 0; - let end = 0; - ctx.set("Last-Modified", new Date(stat.mtime).toUTCString()); - ctx.set("Date", new Date().toUTCString()); - ctx.set("Accept-Ranges", "bytes"); - if (range_text === "") { - end = 1024 * 512; - end = Math.min(end, stat.size - 1); - if (start > end) { - ctx.status = 416; - return; - } - ctx.status = 200; - ctx.length = stat.size; - let stream = createReadStream(path); - ctx.body = stream; - } else { - const m = range_text.match(/^bytes=(\d+)-(\d*)/); - if (m === null) { - ctx.status = 416; - return; - } - start = parseInt(m[1]); - end = m[2].length > 0 ? parseInt(m[2]) : start + 1024 * 1024; - end = Math.min(end, stat.size - 1); - if (start > end) { - ctx.status = 416; - return; - } - ctx.status = 206; - ctx.length = end - start + 1; - ctx.response.set("Content-Range", `bytes ${start}-${end}/${stat.size}`); - ctx.body = createReadStream(path, { - start: start, - end: end, - }); // inclusive range. - } -} - -export class VideoRouter extends Router { - constructor() { - super(); - this.get("/", async (ctx, next) => { - await renderVideo(ctx, ctx.state.location.path); - }); - this.get("/thumbnail", async (ctx, next) => { - await renderVideo(ctx, ctx.state.location.path); - }); - } -} - -export default VideoRouter; diff --git a/src/search/indexer.ts b/src/search/indexer.ts deleted file mode 100644 index f3c129b..0000000 --- a/src/search/indexer.ts +++ /dev/null @@ -1,12 +0,0 @@ -export interface PaginationOption { - cursor: number; - limit: number; -} - -export interface IIndexer { - indexDoc(word: string, doc_id: number): boolean; - indexDoc(word: string[], doc_id: number): boolean; - - getDoc(word: string, option?: PaginationOption): number[]; - getDoc(word: string[], option?: PaginationOption): number[]; -} diff --git a/src/search/tokenizer.ts b/src/search/tokenizer.ts deleted file mode 100644 index cb7f8ad..0000000 --- a/src/search/tokenizer.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface ITokenizer { - tokenize(s: string): string[]; -} - -export class DefaultTokenizer implements ITokenizer { - tokenize(s: string): string[] { - return s.split(" "); - } -} diff --git a/src/server.ts b/src/server.ts deleted file mode 100644 index 23590b0..0000000 --- a/src/server.ts +++ /dev/null @@ -1,237 +0,0 @@ -import Koa from "koa"; -import Router from "koa-router"; - -import { connectDB } from "./database"; -import { createDiffRouter, DiffManager } from "./diff/mod"; -import { get_setting, SettingConfig } from "./SettingConfig"; - -import { createReadStream, readFileSync } from "fs"; -import bodyparser from "koa-bodyparser"; -import { createKnexDocumentAccessor, createKnexTagController, createKnexUserController } from "./db/mod"; -import { createLoginRouter, createUserMiddleWare, getAdmin, isAdminFirst } from "./login"; -import getContentRouter from "./route/contents"; -import { error_handler } from "./route/error_handler"; - -import { createInterface as createReadlineInterface } from "readline"; -import { createComicWatcher } from "./diff/watcher/comic_watcher"; -import { DocumentAccessor, TagAccessor, UserAccessor } from "./model/mod"; -import { getTagRounter } from "./route/tags"; - -class ServerApplication { - readonly userController: UserAccessor; - readonly documentController: DocumentAccessor; - readonly tagController: TagAccessor; - readonly diffManger: DiffManager; - readonly app: Koa; - private index_html: string; - private constructor(controller: { - userController: UserAccessor; - documentController: DocumentAccessor; - tagController: TagAccessor; - }) { - this.userController = controller.userController; - this.documentController = controller.documentController; - this.tagController = controller.tagController; - - this.diffManger = new DiffManager(this.documentController); - this.app = new Koa(); - this.index_html = readFileSync("index.html", "utf-8"); - } - private async setup() { - const setting = get_setting(); - const app = this.app; - - if (setting.cli) { - const userAdmin = await getAdmin(this.userController); - if (await isAdminFirst(userAdmin)) { - const rl = createReadlineInterface({ - input: process.stdin, - output: process.stdout, - }); - const pw = await new Promise((res: (data: string) => void, err) => { - rl.question("put admin password :", (data) => { - res(data); - }); - }); - rl.close(); - userAdmin.reset_password(pw); - } - } - app.use(bodyparser()); - app.use(error_handler); - app.use(createUserMiddleWare(this.userController)); - - let diff_router = createDiffRouter(this.diffManger); - this.diffManger.register("comic", createComicWatcher()); - - console.log("setup router"); - - let router = new Router(); - router.use("/api/(.*)", async (ctx, next) => { - // For CORS - ctx.res.setHeader("access-control-allow-origin", "*"); - await next(); - }); - - router.use("/api/diff", diff_router.routes()); - router.use("/api/diff", diff_router.allowedMethods()); - - const content_router = getContentRouter(this.documentController); - router.use("/api/doc", content_router.routes()); - router.use("/api/doc", content_router.allowedMethods()); - - const tags_router = getTagRounter(this.tagController); - router.use("/api/tags", tags_router.allowedMethods()); - router.use("/api/tags", tags_router.routes()); - - this.serve_with_meta_index(router); - this.serve_index(router); - this.serve_static_file(router); - - const login_router = createLoginRouter(this.userController); - router.use("/user", login_router.routes()); - router.use("/user", login_router.allowedMethods()); - - if (setting.mode == "development") { - let mm_count = 0; - app.use(async (ctx, next) => { - console.log(`==========================${mm_count++}`); - const ip = (ctx.get("X-Real-IP")) ?? ctx.ip; - const fromClient = ctx.state["user"].username === "" ? ip : ctx.state["user"].username; - console.log(`${fromClient} : ${ctx.method} ${ctx.url}`); - await next(); - // console.log(`404`); - }); - } - app.use(router.routes()); - app.use(router.allowedMethods()); - console.log("setup done"); - } - private serve_index(router: Router) { - const serveindex = (url: string) => { - router.get(url, (ctx) => { - ctx.type = "html"; - ctx.body = this.index_html; - const setting = get_setting(); - ctx.set("x-content-type-options", "no-sniff"); - if (setting.mode === "development") { - ctx.set("cache-control", "no-cache"); - } else { - ctx.set("cache-control", "public, max-age=3600"); - } - }); - }; - serveindex("/"); - serveindex("/doc/:rest(.*)"); - serveindex("/search"); - serveindex("/login"); - serveindex("/profile"); - serveindex("/difference"); - serveindex("/setting"); - serveindex("/tags"); - } - private serve_with_meta_index(router: Router) { - const DocMiddleware = async (ctx: Koa.ParameterizedContext) => { - const docId = Number.parseInt(ctx.params["id"]); - const doc = await this.documentController.findById(docId, true); - let meta; - if (doc === undefined) { - ctx.status = 404; - meta = NotFoundContent(); - } else { - ctx.status = 200; - meta = createOgTagContent( - doc.title, - doc.tags.join(", "), - `https://aeolian.prelude.duckdns.org/api/doc/${docId}/comic/thumbnail`, - ); - } - const html = makeMetaTagInjectedHTML(this.index_html, meta); - serveHTML(ctx, html); - }; - router.get("/doc/:id(\\d+)", DocMiddleware); - - function NotFoundContent() { - return createOgTagContent("Not Found Doc", "Not Found", ""); - } - function makeMetaTagInjectedHTML(html: string, tagContent: string) { - return html.replace("", tagContent); - } - function serveHTML(ctx: Koa.Context, file: string) { - ctx.type = "html"; - ctx.body = file; - const setting = get_setting(); - ctx.set("x-content-type-options", "no-sniff"); - if (setting.mode === "development") { - ctx.set("cache-control", "no-cache"); - } else { - ctx.set("cache-control", "public, max-age=3600"); - } - } - - function createMetaTagContent(key: string, value: string) { - return ``; - } - function createOgTagContent(title: string, description: string, image: string) { - return [ - createMetaTagContent("og:title", title), - createMetaTagContent("og:type", "website"), - createMetaTagContent("og:description", description), - createMetaTagContent("og:image", image), - // createMetaTagContent("og:image:width","480"), - // createMetaTagContent("og:image","480"), - // createMetaTagContent("og:image:type","image/png"), - createMetaTagContent("twitter:card", "summary_large_image"), - createMetaTagContent("twitter:title", title), - createMetaTagContent("twitter:description", description), - createMetaTagContent("twitter:image", image), - ].join("\n"); - } - } - private serve_static_file(router: Router) { - const static_file_server = (path: string, type: string) => { - router.get("/" + path, async (ctx, next) => { - const setting = get_setting(); - ctx.type = type; - ctx.body = createReadStream(path); - ctx.set("x-content-type-options", "no-sniff"); - if (setting.mode === "development") { - ctx.set("cache-control", "no-cache"); - } else { - ctx.set("cache-control", "public, max-age=3600"); - } - }); - }; - const setting = get_setting(); - static_file_server("dist/bundle.css", "css"); - static_file_server("dist/bundle.js", "js"); - if (setting.mode === "development") { - static_file_server("dist/bundle.js.map", "text"); - static_file_server("dist/bundle.css.map", "text"); - } - } - start_server() { - let setting = get_setting(); - // todo : support https - console.log(`listen on http://${setting.localmode ? "localhost" : "0.0.0.0"}:${setting.port}`); - return this.app.listen(setting.port, setting.localmode ? "127.0.0.1" : "0.0.0.0"); - } - static async createServer() { - const setting = get_setting(); - let db = await connectDB(); - - const app = new ServerApplication({ - userController: createKnexUserController(db), - documentController: createKnexDocumentAccessor(db), - tagController: createKnexTagController(db), - }); - await app.setup(); - return app; - } -} - -export async function create_server() { - return await ServerApplication.createServer(); -} - -export default { create_server }; diff --git a/src/types/db.d.ts b/src/types/db.d.ts deleted file mode 100644 index 4e2fdda..0000000 --- a/src/types/db.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Knex } from "knex"; - -declare module "knex" { - interface Tables { - tags: { - name: string; - description?: string; - }; - users: { - username: string; - password_hash: string; - password_salt: string; - }; - document: { - id: number; - title: string; - content_type: string; - basepath: string; - filename: string; - created_at: number; - deleted_at: number | null; - content_hash: string; - additional: string | null; - }; - doc_tag_relation: { - doc_id: number; - tag_name: string; - }; - permissions: { - username: string; - name: string; - }; - } -} diff --git a/src/util/configRW.ts b/src/util/configRW.ts deleted file mode 100644 index f0700f9..0000000 --- a/src/util/configRW.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { existsSync, promises as fs, readFileSync, writeFileSync } from "fs"; -import { validate } from "jsonschema"; - -export class ConfigManager { - path: string; - default_config: T; - config: T | null; - schema: object; - constructor(path: string, default_config: T, schema: object) { - this.path = path; - this.default_config = default_config; - this.config = null; - this.schema = schema; - } - get_config_file(): T { - if (this.config !== null) return this.config; - this.config = { ...this.read_config_file() }; - return this.config; - } - private emptyToDefault(target: T) { - let occur = false; - for (const key in this.default_config) { - if (key === undefined || key in target) { - continue; - } - target[key] = this.default_config[key]; - occur = true; - } - return occur; - } - read_config_file(): T { - if (!existsSync(this.path)) { - writeFileSync(this.path, JSON.stringify(this.default_config)); - return this.default_config; - } - const ret = JSON.parse(readFileSync(this.path, { encoding: "utf8" })); - if (this.emptyToDefault(ret)) { - writeFileSync(this.path, JSON.stringify(ret)); - } - const result = validate(ret, this.schema); - if (!result.valid) { - throw new Error(result.toString()); - } - return ret; - } - async write_config_file(new_config: T) { - this.config = new_config; - await fs.writeFile(`${this.path}.temp`, JSON.stringify(new_config)); - await fs.rename(`${this.path}.temp`, this.path); - } -} diff --git a/src/util/type_check.ts b/src/util/type_check.ts deleted file mode 100644 index c45a2b1..0000000 --- a/src/util/type_check.ts +++ /dev/null @@ -1,15 +0,0 @@ -export function check_type(obj: any, check_proto: Record): obj is T { - for (const it in check_proto) { - let defined = check_proto[it]; - if (defined === undefined) return false; - defined = defined.trim(); - if (defined.endsWith("[]")) { - if (!(obj[it] instanceof Array)) { - return false; - } - } else if (defined !== typeof obj[it]) { - return false; - } - } - return true; -} diff --git a/src/util/zipwrap.ts b/src/util/zipwrap.ts deleted file mode 100644 index 2de4529..0000000 --- a/src/util/zipwrap.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ZipEntry } from "node-stream-zip"; - -import { ReadStream } from "fs"; -import { orderBy } from "natural-orderby"; -import StreamZip from "node-stream-zip"; - -export type ZipAsync = InstanceType; -export async function readZip(path: string): Promise { - return new StreamZip.async({ - file: path, - storeEntries: true, - }); -} -export async function entriesByNaturalOrder(zip: ZipAsync) { - const entries = await zip.entries(); - const ret = orderBy(Object.values(entries), v => v.name); - return ret; -} - -export async function createReadableStreamFromZip(zip: ZipAsync, entry: ZipEntry): Promise { - return await zip.stream(entry); -} -export async function readAllFromZip(zip: ZipAsync, entry: ZipEntry): Promise { - const stream = await createReadableStreamFromZip(zip, entry); - const chunks: Uint8Array[] = []; - return new Promise((resolve, reject) => { - stream.on("data", (data) => { - chunks.push(data); - }); - stream.on("error", (err) => reject(err)); - stream.on("end", () => resolve(Buffer.concat(chunks))); - }); -}