diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml index 7f29909..afe6836 100644 --- a/.github/workflows/build-packages.yml +++ b/.github/workflows/build-packages.yml @@ -8,15 +8,18 @@ on: - 'v*.*.*' jobs: main: - runs-on: self-hosted + runs-on: deployinatorv1 permissions: contents: write packages: write steps: - name: Checkout uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 1 - name: Package Binaries - run: build-packages ${{github.repository}} ${{github.workspace}} --env NPM_AUTH_TOKEN ${{secrets.NPM_AUTH_PAT}} + run: build-packages ${{github.repository}} ${{github.workspace}} --env NPM_AUTH_TOKEN ${{secrets.NPM_AUTH_PAT}} --env YARN_IGNORE_NODE 1 - name: Set Variables if: startsWith(github.ref, 'refs/tags/') run: | diff --git a/.gitignore b/.gitignore index 1333ed7..4e5eaf1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ TODO +node_modules/ +.yarn/ +.yarnrc.yml +*_generic.tar.gz +*_generic.zip diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0748344 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "houston-common"] + path = houston-common + url = git@github.com:45Drives/houston-common.git diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1c4c144 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "tailwindCSS.classAttributes": [ + "class", + "className", + "ngClass", + "enter-active-class", + "enter-from-class", + "enter-to-class", + "leave-active-class", + "leave-from-class", + "leave-to-class" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index fee0489..0cd9818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ -## Cockpit File Sharing 3.3.7-1 +## Cockpit File Sharing 4.2.5-1 -* Samba - fix saving global advanced settings with '=' in their value -* NFS - better whitespace handling while parsing exports file \ No newline at end of file +* iSCSI Release \ No newline at end of file diff --git a/Makefile b/Makefile index 5937c6e..236ed4d 100644 --- a/Makefile +++ b/Makefile @@ -13,11 +13,10 @@ # If not, see . # PLUGIN_SRCS is space-delimited list of subdirectories containg a plugin project. -# You can leave it empty for automatic detection based on directories containing a package.json file. -PLUGIN_SRCS= +PLUGIN_SRCS=file-sharing # For installing to a remote machine for testing with `make install-remote` -REMOTE_TEST_HOST=192.168.207.27 +REMOTE_TEST_HOST=192.168.45.23 REMOTE_TEST_USER=root # Restarts cockpit after install @@ -53,7 +52,8 @@ BUILD_FLAGS=-- --minify false endif ifndef PLUGIN_SRCS -PLUGIN_SRCS:=$(patsubst %/package.json,%,$(wildcard */package.json)) +# PLUGIN_SRCS:=$(filter-out %-old houston-common, $(patsubst %/package.json,%,$(wildcard */package.json))) +$(error PLUGIN_SRCS not set - please edit Makefile) endif OUTPUTS:=$(addsuffix /dist/index.html, $(PLUGIN_SRCS)) @@ -61,27 +61,35 @@ OUTPUTS:=$(addsuffix /dist/index.html, $(PLUGIN_SRCS)) NPM_PREFIX:=$(shell command -v yarn > /dev/null 2>&1 && echo 'yarn --cwd' || echo 'npm --prefix') NPM_UPDATE:=$(shell command -v yarn > /dev/null 2>&1 && echo 'yarn upgrade --cwd' || echo 'npm update --prefix') -VERSION_FILES:=$(addsuffix /src/version.js, $(PLUGIN_SRCS)) -OS_PACKAGE_RELEASE?=built_from_source - -default: $(VERSION_FILES) $(OUTPUTS) +default: $(OUTPUTS) all: default -.PHONY: default all install clean help install-local install-remote install +.PHONY: default all install clean help install-local install-remote install houston-common bootstrap-yarn + +bootstrap-yarn: .yarnrc.yml + +.yarnrc.yml: + ./bootstrap.sh + +houston-common/Makefile: + git submodule update --init -$(VERSION_FILES): ./manifest.json - echo 'export const pluginVersion = "$(shell jq -r '.version' ./manifest.json)-$(shell jq -r '.buildVersion' ./manifest.json)$(OS_PACKAGE_RELEASE)";' > $@ +houston-common: houston-common/Makefile bootstrap-yarn + $(MAKE) -C houston-common + +houston-common-%: + $(MAKE) -C houston-common $* # build outputs .SECONDEXPANSION: -$(OUTPUTS): %/dist/index.html: $$(shell find '$$*' -type d \( -name node_modules -o -path '$$*/dist' -o -path '*node_modules*' \) -prune -o -type f -not \( -name .gitignore \) -print) +$(OUTPUTS): %/dist/index.html: bootstrap-yarn houston-common $$(shell find '$$*' -type d \( -name node_modules -o -path '$$*/dist' -o -path '*node_modules*' \) -prune -o -type f -not \( -name .gitignore \) -print) @echo -e $(call cyantext,Building $*) - $(NPM_PREFIX) $* install + yarn --cwd $* install ifeq ($(AUTO_UPGRADE_DEPS),1) - $(NPM_UPDATE) $* + yarn upgrade --cwd $* endif - $(NPM_PREFIX) $* run build $(BUILD_FLAGS) + yarn --cwd $* run build $(BUILD_FLAGS) @echo -e $(call greentext,Done building $*) @echo @@ -141,6 +149,10 @@ package-generic: default clean: FORCE rm $(dir $(OUTPUTS)) -rf +clean-all: clean FORCE + rm .yarnrc.yml .yarn/ -rf + find . -name node_modules -type d -exec rm -rf {} \; -prune + help: @echo 'make usage' @echo @@ -158,4 +170,9 @@ help: @echo 'build cleanup:' @echo ' make clean' +test-%: + yarn --cwd $* run test + +test: houston-common-test $(addprefix test-, $(PLUGIN_SRCS)) + FORCE: diff --git a/README.md b/README.md index f97e67b..776067f 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,8 @@ sudo apt install cockpit-file-sharing ### Direct from .deb Installing this way may work for other versions of Ubuntu and Debian, but it is unsupported. You won't get automatic updates this way. ```bash -curl -LO https://github.com/45Drives/cockpit-file-sharing/releases/download/v3.3.7/cockpit-file-sharing_3.3.7-1focal_all.deb -sudo apt install ./cockpit-file-sharing_3.3.7-1focal_all.deb +curl -LO https://github.com/45Drives/cockpit-file-sharing/releases/download/v4.1.0/cockpit-file-sharing_4.1.0-1focal_all.deb +sudo apt install ./cockpit-file-sharing_4.1.0-1focal_all.deb ``` ## Rocky 8 ### From 45Drives Repo (Recommended, Rocky 8 only) @@ -51,38 +51,39 @@ sudo dnf install cockpit-file-sharing Installing this way may work for other versions of Rocky/Centos/RHEL/Fedora/etc, but it is unsupported. You won't get automatic updates this way. ```bash # dnf or yum -sudo dnf install https://github.com/45Drives/cockpit-file-sharing/releases/download/v3.3.7/cockpit-file-sharing-3.3.7-1.el8.noarch.rpm +sudo dnf install https://github.com/45Drives/cockpit-file-sharing/releases/download/v4.1.0/cockpit-file-sharing-4.1.0-1.el8.noarch.rpm ``` ## Generic Installation 1. Install Dependencies ```bash # debian-like -cockpit -attr +cockpit-bridge coreutils +attr +findutils +hostname +iproute2 libc-bin +systemd nfs-kernel-server -samba samba-common-bin -systemd -winbind -gawk # RHEL-like -cockpit -attr +cockpit-bridge coreutils +attr +findutils +hostname +iproute glibc-common +systemd nfs-utils samba-common-tools -samba-winbind-clients -system -gawk ``` 2. Download pre-built archive and install ```bash -curl -LO https://github.com/45Drives/cockpit-file-sharing/releases/download/v3.3.7/cockpit-file-sharing_3.3.7_generic.zip -unzip cockpit-file-sharing_3.3.7_generic.zip -cd cockpit-file-sharing_3.3.7_generic +curl -LO https://github.com/45Drives/cockpit-file-sharing/releases/download/v4.1.0/cockpit-file-sharing_4.1.0_generic.zip +unzip cockpit-file-sharing_4.1.0_generic.zip +cd cockpit-file-sharing_4.1.0_generic # no need to run `make` first, the plugin is pre-built sudo make install ``` @@ -95,12 +96,13 @@ Simply click the `+` in the top right of the shares list, fill out the required * Share Name - Unique name for the share * Share Description - Optional description for the share * Path - The path to share out from the server -* Valid Users/Groups - * Allow-lists for users and groups - * By default, any user or group is allowed +* ~~Valid Users/Groups~~ removed in v4 - valid users property still available through advanced settings. + * ~~Allow-lists for users and groups~~ + * ~~By default, any user or group is allowed~~ * Guest Ok - Allow accessing share with no password, privileges mapped to `guest account` (default=`nobody`) * Read Only - Disallow creation/modification of files/directories * Browsable - Controls whether this share is seen in the list of available shares in a net view and in the browse list +* Inherit Permissions - New directories inherit the mode of the parent directory, including bits such as setgid. New files inherit their read/write bits from the parent directory. * Windows ACLs - Administer share permissions from Windows, sets the following advanced settings ```ini map acl inherit = yes diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..79e7bcf --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# bootstrap.sh + +set -e +set -o pipefail +set -x + +command -v sponge >/dev/null || { echo "Missing 'sponge'. Please install moreutils." >&2 ; exit 1 ; } + +jq 'del(.packageManager)' ./package.json | sponge ./package.json + +rm .yarnrc.yml .yarn -rf + +yarn set version stable + +yarn config set nodeLinker node-modules diff --git a/docs/refactor.puml b/docs/refactor.puml new file mode 100644 index 0000000..ef18e24 --- /dev/null +++ b/docs/refactor.puml @@ -0,0 +1,191 @@ +@startuml +namespace Fundamental { + class Server { + getLocalUsers() : User[] + getDomainUsers() : User[] + getLocalGroups() : Group[] + getDomainGroups() : Group[] + execute(command: Command) : Process + makeFilesystemNode(Path): FilesystemNode + makeFile(Path): File + makeDirectory(Path): Directory + } + + + class User { + login : string + uid : number + name : string + primaryGroup : Group + home : Path + getGroups(): Group[] + } + + class Group { + name : string + gid : number + members : User[] + isDomain : boolean + } + + class Path { + tokens: string[] + basename(): string + parent(): Directory + isAbsolute() : boolean + isRelative() : boolean + relativeTo(Path): Path + } + class FileSystemNode { + server : Server + exists() : boolean + getFilesystemMount() : FilesystemMount + create() + unlink() + stat() + chmod(mode) + chown(User) + chown(Group) + chown(User, Group) + rename(Path) + } + Path <|-- FileSystemNode + FileSystemNode <|-- BinaryFile + BinaryFile <|-- File + FileSystemNode <|-- Directory + BinaryFile : read() : Uint8Array + BinaryFile : replace(Uint8Array) + File : read() : string + File : replace(string) + Directory : list() : List + + class SambaShare { + name : string + description : string + path : Path + validUsers : List + guestOk : boolean + readOnly : boolean + browsable : boolean + advancedOptions : Map + } + + class SambaGlobalSettings { + logLevel : number + workgroup : string + serverString : string + advancedOptions : Map + } + + class NFSExport { + path : Path + name : string + clients : NFSExportClient[] + } + + class NFSExportClient { + host : string + settings : string[] + } + + class Filesystem { + source : string + getStats() + } + + class FilesystemMount { + filesystem : Filesystem + mountpoint : Path + mountOptions : string[] + } + + class SystemdUnit { + file : Path + settings : {unit: {}, mount?: {}, service?: {}} + } + + SystemdUnit <|-- SystemdMount + FilesystemMount <|-- SystemdMount + + SystemdUnit <|-- SystemdService +} + +newpage + +namespace Interactive { + class Command { + argv : string[] + envp : string[] + options : cockpit.SpawnOptions + } + + class ProcessBase { + command : Command + } + + class Process { + wait() : ExitedProcess + input(data : string|Uint8Array) + getOutputStream() : ReadableStream + getInputStream() : WritableStream + streamInputFrom(ReadableStream) + streamOutputInto(WritableStream) + close() + terminate() + } + ProcessBase <|-- Process + + class ExitedProcess { + exitStatus : number + stdout : Uint8Array + stderr : string + killedBy: string | undefined + getStdout(binary : boolean) : string | Uint8Array + succeeded() : boolean + failed() : boolean + } + ProcessBase <|-- ExitedProcess + + class "SyntaxParser" as SyntaxParser_T { + parse(text : string) : T + unparse(repr : T) : string + } + + SyntaxParser_T <.. NFSExportSyntax : <>T::NFSExport[] + + + SyntaxParser_T <.. SystemdUnitSyntax : "<>T::{unit: {}, service?: {}, mount?: {}}" +} + +namespace Management { + class SambaManager { + loadGlobalSettings(server: Server) : SambaGlobalSettings + loadShares(Server) : SambaShare[] + addShare(Server, SambaShare) + removeShare(Server, SambaShare) + editShare(Server, SambaShare) + } + class NFSManager { + loadExports(Server) : NFSExport[] + addExport(Server, NFSExport) + removeExport(Server, NFSExport) + editExport(Server, NFSExport) + } + class SystemdManager { + loadMounts(Server) : SystemdMount[] + addMount(Server, SystemdMount) + removeMount(Server, SystemdMount) + editMount(Server, SystemdMount) + loadServices(Server) : SystemdService[] + addService(Server, SystemdService) + removeService(Server, SystemdService) + editService(Server, SystemdService) + startService(Server, SystemdService) + stopService(Server, SystemdService) + enableService(Server, SystemdService) + disableService(Server, SystemdService) + } + + +} +@enduml diff --git a/file-sharing-old/.gitignore b/file-sharing-old/.gitignore new file mode 100644 index 0000000..27ae478 --- /dev/null +++ b/file-sharing-old/.gitignore @@ -0,0 +1,27 @@ +# 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? + +# Generated files +src/version.js diff --git a/file-sharing-old/index.html b/file-sharing-old/index.html new file mode 100644 index 0000000..c1d9c6a --- /dev/null +++ b/file-sharing-old/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + + File Sharing + + + +
+ + + + diff --git a/file-sharing/package-lock.json b/file-sharing-old/package-lock.json similarity index 100% rename from file-sharing/package-lock.json rename to file-sharing-old/package-lock.json diff --git a/file-sharing-old/package.json b/file-sharing-old/package.json new file mode 100644 index 0000000..128b6bb --- /dev/null +++ b/file-sharing-old/package.json @@ -0,0 +1,29 @@ +{ + "name": "file-sharing", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.2.37" + }, + "devDependencies": { + "@45drives/cockpit-css": "^0.1.12", + "@45drives/cockpit-helpers": "^0.1.19", + "@45drives/cockpit-syntaxes": "^0.1.6", + "@45drives/regex": "^0.1.0", + "@fontsource/red-hat-text": "^4.5.9", + "@headlessui/vue": "^1.6.5", + "@heroicons/vue": "^1.0.6", + "@tailwindcss/forms": "^0.5.2", + "@vitejs/plugin-vue": "^2.3.3", + "autoprefixer": "^10.4.7", + "postcss": "^8.4.14", + "source-sans-pro": "^3.6.0", + "tailwindcss": "^3.1.4", + "vite": "^2.9.13" + } +} diff --git a/file-sharing/postcss.config.js b/file-sharing-old/postcss.config.js similarity index 100% rename from file-sharing/postcss.config.js rename to file-sharing-old/postcss.config.js diff --git a/file-sharing/public/assets/images/45d-fan-dark.svg b/file-sharing-old/public/assets/images/45d-fan-dark.svg similarity index 100% rename from file-sharing/public/assets/images/45d-fan-dark.svg rename to file-sharing-old/public/assets/images/45d-fan-dark.svg diff --git a/file-sharing/public/assets/images/45d-fan-light.svg b/file-sharing-old/public/assets/images/45d-fan-light.svg similarity index 100% rename from file-sharing/public/assets/images/45d-fan-light.svg rename to file-sharing-old/public/assets/images/45d-fan-light.svg diff --git a/file-sharing-old/public/manifest.json b/file-sharing-old/public/manifest.json new file mode 100644 index 0000000..92ee158 --- /dev/null +++ b/file-sharing-old/public/manifest.json @@ -0,0 +1,21 @@ +{ + "version": 2.0, + + "menu": { + "file-sharing": { + "label": "File Sharing", + "path": "index.html", + "order": 116, + "docs": [ + { + "label": "Managing SMB/CIFS", + "url": "http://knowledgebase.45drives.com/kb/kb045281-managing-smb-cifs-in-houston-ui/" + }, + { + "label": "Managing NFS", + "url": "http://knowledgebase.45drives.com/kb/kb045282-managing-nfs-in-houston-ui/" + } + ] + } + } +} diff --git a/file-sharing-old/src/App.vue b/file-sharing-old/src/App.vue new file mode 100644 index 0000000..e4e5936 --- /dev/null +++ b/file-sharing-old/src/App.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/file-sharing/src/components/CephShareOptions.vue b/file-sharing-old/src/components/CephShareOptions.vue similarity index 100% rename from file-sharing/src/components/CephShareOptions.vue rename to file-sharing-old/src/components/CephShareOptions.vue diff --git a/file-sharing/src/components/Config.vue b/file-sharing-old/src/components/Config.vue similarity index 100% rename from file-sharing/src/components/Config.vue rename to file-sharing-old/src/components/Config.vue diff --git a/file-sharing/src/components/DropdownSelector.vue b/file-sharing-old/src/components/DropdownSelector.vue similarity index 100% rename from file-sharing/src/components/DropdownSelector.vue rename to file-sharing-old/src/components/DropdownSelector.vue diff --git a/file-sharing/src/components/FileModeMatrix.vue b/file-sharing-old/src/components/FileModeMatrix.vue similarity index 100% rename from file-sharing/src/components/FileModeMatrix.vue rename to file-sharing-old/src/components/FileModeMatrix.vue diff --git a/file-sharing/src/components/FilePermissions.vue b/file-sharing-old/src/components/FilePermissions.vue similarity index 100% rename from file-sharing/src/components/FilePermissions.vue rename to file-sharing-old/src/components/FilePermissions.vue diff --git a/file-sharing/src/components/HoustonHeader.vue b/file-sharing-old/src/components/HoustonHeader.vue similarity index 99% rename from file-sharing/src/components/HoustonHeader.vue rename to file-sharing-old/src/components/HoustonHeader.vue index 322d9c9..1228712 100644 --- a/file-sharing/src/components/HoustonHeader.vue +++ b/file-sharing-old/src/components/HoustonHeader.vue @@ -105,7 +105,7 @@ If not, see . import "@fontsource/red-hat-text/700.css"; import "@fontsource/red-hat-text/400.css"; import "source-sans-pro/source-sans-pro.css"; -import { SunIcon, MoonIcon, QuestionMarkCircleIcon } from "@heroicons/vue/solid"; +import { SunIcon, MoonIcon, QuestionMarkCircleIcon } from "@heroicons/vue/20/solid"; import { ref, watch, inject } from "vue"; import LoadingSpinner from "./LoadingSpinner.vue"; import ModalPopup from './ModalPopup.vue'; diff --git a/file-sharing/src/components/InfoTip.vue b/file-sharing-old/src/components/InfoTip.vue similarity index 95% rename from file-sharing/src/components/InfoTip.vue rename to file-sharing-old/src/components/InfoTip.vue index d8ca89c..e2b5ece 100644 --- a/file-sharing/src/components/InfoTip.vue +++ b/file-sharing-old/src/components/InfoTip.vue @@ -26,7 +26,7 @@ If not, see . - - diff --git a/file-sharing/src/components/SambaManager.vue b/file-sharing-old/src/components/SambaManager.vue similarity index 99% rename from file-sharing/src/components/SambaManager.vue rename to file-sharing-old/src/components/SambaManager.vue index 1ca73f8..7466d60 100644 --- a/file-sharing/src/components/SambaManager.vue +++ b/file-sharing-old/src/components/SambaManager.vue @@ -98,7 +98,7 @@ If not, see . - - + + + File Sharing
- + diff --git a/file-sharing/package.json b/file-sharing/package.json index 128b6bb..773e258 100644 --- a/file-sharing/package.json +++ b/file-sharing/package.json @@ -1,29 +1,59 @@ { "name": "file-sharing", - "private": true, "version": "0.0.0", + "private": true, + "type": "module", "scripts": { "dev": "vite", - "build": "vite build", - "preview": "vite preview" + "build:app": "run-p type-check \"build-only {@}\" --", + "build": "run-p build:app", + "preview": "vite preview", + "test:app": "vitest run", + "test": "vitest run", + "build-only": "vite build", + "type-check": "vue-tsc --build --force", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "format": "prettier --write src/" }, "dependencies": { - "vue": "^3.2.37" + "@45drives/houston-common-css": "workspace:^", + "@45drives/houston-common-lib": "workspace:^", + "@45drives/houston-common-ui": "workspace:^", + "@headlessui/vue": "^1.7.22", + "monet": "^0.9.3", + "neverthrow": "^6.2.1", + "vue": "^3.4.27", + "vue-router": "^4.3.2", + "zod": "^3.23.8" }, "devDependencies": { - "@45drives/cockpit-css": "^0.1.12", - "@45drives/cockpit-helpers": "^0.1.19", - "@45drives/cockpit-syntaxes": "^0.1.6", - "@45drives/regex": "^0.1.0", - "@fontsource/red-hat-text": "^4.5.9", - "@headlessui/vue": "^1.6.5", - "@heroicons/vue": "^1.0.6", - "@tailwindcss/forms": "^0.5.2", - "@vitejs/plugin-vue": "^2.3.3", - "autoprefixer": "^10.4.7", - "postcss": "^8.4.14", - "source-sans-pro": "^3.6.0", - "tailwindcss": "^3.1.4", - "vite": "^2.9.13" + "@fontsource/red-hat-text": "^5.0.18", + "@rushstack/eslint-patch": "^1.10.2", + "@tailwindcss/forms": "^0.5.7", + "@tsconfig/node20": "^20.1.4", + "@types/deep-equal": "^1.0.4", + "@types/jsdom": "^21.1.6", + "@types/node": "^20.12.12", + "@vitejs/plugin-vue": "^5.0.4", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "@vue/eslint-config-prettier": "^9.0.0", + "@vue/eslint-config-typescript": "^13.0.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.5.1", + "autoprefixer": "^10.4.19", + "deep-equal": "^2.2.3", + "eslint": "^9.2.0", + "eslint-plugin-vue": "^9.26.0", + "jsdom": "^24.0.0", + "npm-run-all2": "^6.1.2", + "postcss": "^8.4.38", + "postcss-modules": "^6.0.0", + "tailwindcss": "^3.4.3", + "ts-node": "^10.9.2", + "typescript": "~5.4.5", + "vite": "^5.2.11", + "vite-plugin-vue-devtools": "^7.2.0", + "vitest": "^1.6.0", + "vue-tsc": "^2.0.19" } } diff --git a/file-sharing/postcss.config.cjs b/file-sharing/postcss.config.cjs new file mode 100644 index 0000000..8a82839 --- /dev/null +++ b/file-sharing/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + "plugins": { + "tailwindcss/nesting": {}, + "tailwindcss": {}, + } +} diff --git a/file-sharing/public/favicon.ico b/file-sharing/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/file-sharing/public/favicon.ico differ diff --git a/file-sharing/src/App.vue b/file-sharing/src/App.vue index e4e5936..d3f2f0d 100644 --- a/file-sharing/src/App.vue +++ b/file-sharing/src/App.vue @@ -1,82 +1,45 @@ - + - - diff --git a/file-sharing/src/common/ceph-option-manager.ts b/file-sharing/src/common/ceph-option-manager.ts new file mode 100644 index 0000000..b58d4e9 --- /dev/null +++ b/file-sharing/src/common/ceph-option-manager.ts @@ -0,0 +1,330 @@ +import { SystemdManagerSingleServer, type SystemdMountSettings } from "@/common/systemd-manager"; +import { + Command, + FileSystemNode, + ParsingError, + ProcessError, + StringToIntCaster, + safeJsonParse, + type CommandOptions, + type Server, +} from "@45drives/houston-common-lib"; +import { ResultAsync, err, ok } from "neverthrow"; +import { Maybe } from "monet"; + +export interface ICephOptionManager { + pathIsMountpoint(path: string): ResultAsync; + pathMountpointManagedByFileSharing( + path: string + ): ResultAsync; + remountPath(path: string): ResultAsync; + removeRemount(path: string): ResultAsync; + getQuotaMaxBytes(path: string): ResultAsync, ProcessError | ParsingError>; + setQuotaMaxBytes( + path: string, + quotaMaxBytes: number + ): ResultAsync; + removeQuotaMaxBytes(path: string): ResultAsync; + getLayoutPools(): ResultAsync; + getLayoutPool(path: string): ResultAsync, ProcessError | ParsingError>; + setLayoutPool(path: string, layoutPool: string): ResultAsync; + removeLayoutPool(path: string): ResultAsync; +} + +const SYSTEMD_MOUNT_DESCRIPTION = "share remount created by cockpit-file-sharing"; +const CEPH_QUOTA_MAX_BYTES_ATTRIBUTE_NAME = "ceph.quota.max_bytes"; +const CEPH_DIR_LAYOUT_ATTRIBUTE_NAME = "ceph.dir.layout"; +const CEPH_DIR_LAYOUT_POOL_ATTRIBUTE_NAME = CEPH_DIR_LAYOUT_ATTRIBUTE_NAME + ".pool"; + +export class CephOptionManagerSingleServer implements ICephOptionManager { + private systemdManager: SystemdManagerSingleServer; + private commandOptions: CommandOptions; + private stringToIntCaster = StringToIntCaster(10); + constructor( + private server: Server, + private clientName: `client.${string}` + ) { + this.systemdManager = new SystemdManagerSingleServer(this.server, "system"); + this.commandOptions = { superuser: "try" }; + } + + private getResolvedNode(path: string) { + return new FileSystemNode(this.server, path).resolve(true); + } + + private getMountUnit(pathNode: FileSystemNode) { + return this.systemdManager.pathToMountUnitName(pathNode.path).map((name) => ({ name })); + } + + pathIsMountpoint(path: string) { + return this.getResolvedNode(path) + .andThen((node) => node.getFilesystemMount().map((mount) => ({ node, mount }))) + .map(({ node, mount }) => node.path === mount.mountpoint); + } + + pathMountpointManagedByFileSharing(path: string) { + return this.getResolvedNode(path) + .andThen((node) => this.getMountUnit(node)) + .andThen((unit) => this.systemdManager.getSettings(unit)) + .map((mountSettings) => mountSettings.Unit?.Description === SYSTEMD_MOUNT_DESCRIPTION); + } + + private enableRemount(pathNode: FileSystemNode) { + return this.getMountUnit(pathNode).andThen((mountUnit) => + this.systemdManager.enable(mountUnit, "now") + ); + } + + private createRemount(pathNode: FileSystemNode) { + const ancestorMountpoint = pathNode + .getFilesystemMount() + .andThen(({ filesystem: { type }, mountpoint }) => + type !== "ceph" + ? err(new ProcessError(`Not a ceph filesystem: ${mountpoint}`)) + : ok(mountpoint) + ); + const ancestorMountSettings = ancestorMountpoint + .andThen((ancestorMountpoint) => this.getResolvedNode(ancestorMountpoint)) + .andThen((ancestorNode) => this.getMountUnit(ancestorNode)) + .andThen((ancestorMountUnit) => this.systemdManager.getSettings(ancestorMountUnit)); + const newMountpointSource = ancestorMountSettings.andThen((ancestorMountSettings) => { + const ancestorMountpoint = ancestorMountSettings.Mount.Where; + if (!pathNode.path.startsWith(ancestorMountpoint)) { + return err( + new ProcessError( + `Share path is not within ancestor mount path:\n${pathNode.path}\n${ancestorMountpoint}` + ) + ); + } + const pathRelativeToAncestorMountpoint = pathNode.path.replace(ancestorMountpoint, ""); + if (!pathRelativeToAncestorMountpoint.startsWith("/")) { + return err( + new ProcessError( + `Path relative to ancestor mountpoint doesn't start with '/': ${pathRelativeToAncestorMountpoint}` + ) + ); + } + return ok( + ancestorMountSettings.Mount.What + + (ancestorMountSettings.Mount.What.endsWith("/") + ? pathRelativeToAncestorMountpoint.slice(1) + : pathRelativeToAncestorMountpoint) + ); + }); + const newMountSettings = ResultAsync.combine([ancestorMountSettings, newMountpointSource]).map( + ([ancestorMountSettings, newMountpointSource]) => { + return { + Unit: { + DefaultDependencies: "no", + After: ["remote-fs-pre.target", "network.target", "network-online.target"], + Wants: ["network.target", "network-online.target"], + Conflicts: ["umount.target"], + Before: ["umount.target", "ctdb.service"], + Description: SYSTEMD_MOUNT_DESCRIPTION, + }, + Mount: { + What: newMountpointSource, + Where: pathNode.path, + Options: ancestorMountSettings.Mount.Options, + Type: "ceph", + LazyUnmount: "true", + }, + Install: { + WantedBy: "remote-fs.target", + }, + } as SystemdMountSettings; + } + ); + return ResultAsync.combine([this.getMountUnit(pathNode), newMountSettings]).andThen( + ([mountUnit, mountSettings]) => + this.systemdManager + .createUnit(mountUnit, mountSettings) + .andThen((unit) => this.systemdManager.enable(unit, "now")) + ); + } + + remountPath(path: string): ResultAsync { + return this.getResolvedNode(path) + .andThen((pathNode) => + this.pathMountpointManagedByFileSharing(path) + .andThen((managedbyFileSharing) => + managedbyFileSharing + ? this.enableRemount(pathNode) + : err(new Error("Mount not managed by File Sharing!")) + ) + .orElse((e) => (e instanceof ProcessError ? this.createRemount(pathNode) : err(e))) + ) + .map(() => this); + } + + removeRemount(path: string): ResultAsync { + return this.getResolvedNode(path) + .andThen((node) => this.getMountUnit(node)) + .andThen((unit) => this.systemdManager.disable(unit, "now")) + .andThen((unit) => this.systemdManager.removeUnit(unit)) + .map(() => this); + } + + getQuotaMaxBytes(path: string): ResultAsync, ProcessError | ParsingError> { + return new FileSystemNode(this.server, path) + .getExtendedAttribute(CEPH_QUOTA_MAX_BYTES_ATTRIBUTE_NAME, this.commandOptions) + .map((attr) => attr.flatMap(this.stringToIntCaster)); + } + + setQuotaMaxBytes( + path: string, + quotaMaxBytes: number + ): ResultAsync { + return new FileSystemNode(this.server, path) + .setExtendedAttribute( + CEPH_QUOTA_MAX_BYTES_ATTRIBUTE_NAME, + quotaMaxBytes.toString(10), + this.commandOptions + ) + .map(() => this); + } + + removeQuotaMaxBytes(path: string): ResultAsync { + return new FileSystemNode(this.server, path) + .setExtendedAttribute(CEPH_QUOTA_MAX_BYTES_ATTRIBUTE_NAME, "0", this.commandOptions) + .map(() => this); + } + + getLayoutPools(): ResultAsync { + return this.server + .execute( + new Command( + [ + "ceph", + "fs", + "status", + `--keyring=/etc/ceph/ceph.${this.clientName}.keyring`, + "--name", + this.clientName, + "--format=json", + ], + this.commandOptions + ) + ) + .orElse(() => + // fall back to default keyring + this.server.execute( + new Command(["ceph", "fs", "status", "--format=json"], this.commandOptions) + ) + ) + .map((proc) => proc.getStdout()) + .andThen((jsonText) => + safeJsonParse<{ pools: { name: string; type: string }[] }>(jsonText).andThen(({ pools }) => + pools && pools.every((pool) => pool.name !== undefined && pool.type !== undefined) + ? ok(pools) + : err(new ParsingError(`Malformed output from ceph fs status:\n${jsonText}`)) + ) + ) + .map((pools) => pools.filter((pool) => pool.type === "data").map((pool) => pool.name)); + } + + getLayoutPool(path: string): ResultAsync, ProcessError | ParsingError> { + return new FileSystemNode(this.server, path).getExtendedAttribute( + CEPH_DIR_LAYOUT_POOL_ATTRIBUTE_NAME, + this.commandOptions + ); + } + + setLayoutPool(path: string, layoutPool: string): ResultAsync { + return new FileSystemNode(this.server, path) + .setExtendedAttribute(CEPH_DIR_LAYOUT_POOL_ATTRIBUTE_NAME, layoutPool, this.commandOptions) + .map(() => this); + } + + removeLayoutPool(path: string): ResultAsync { + return new FileSystemNode(this.server, path) + .removeExtendedAttribute(CEPH_DIR_LAYOUT_ATTRIBUTE_NAME, this.commandOptions) + .map(() => this); + } +} + +export class CephOptionManagerClustered implements ICephOptionManager { + private managers: [CephOptionManagerSingleServer, ...CephOptionManagerSingleServer[]]; + private getterManager: CephOptionManagerSingleServer; + + constructor(servers: [Server, ...Server[]], clientName: `client.${string}`) { + this.managers = servers.map((s) => new CephOptionManagerSingleServer(s, clientName)) as [ + CephOptionManagerSingleServer, + ...CephOptionManagerSingleServer[], + ]; + this.getterManager = this.managers[0]; + } + + pathIsMountpoint(path: string): ResultAsync { + return ResultAsync.combine(this.managers.map((m) => m.pathIsMountpoint(path))).map((rs) => + rs.every((r) => r) + ); + } + + pathMountpointManagedByFileSharing( + path: string + ): ResultAsync { + return ResultAsync.combine( + this.managers.map((m) => m.pathMountpointManagedByFileSharing(path)) + ).map((rs) => rs.every((r) => r)); + } + + remountPath(path: string): ResultAsync { + return ResultAsync.combine(this.managers.map((m) => m.remountPath(path))).map(() => this); + } + + removeRemount(path: string): ResultAsync { + return ResultAsync.combine(this.managers.map((m) => m.removeRemount(path))).map(() => this); + } + + getQuotaMaxBytes(path: string): ResultAsync, ProcessError | ParsingError> { + return this.getterManager.getQuotaMaxBytes(path); + } + + setQuotaMaxBytes( + path: string, + quotaMaxBytes: number + ): ResultAsync { + return this.getterManager.setQuotaMaxBytes(path, quotaMaxBytes).map(() => this); + } + + removeQuotaMaxBytes(path: string): ResultAsync { + return this.getterManager.removeQuotaMaxBytes(path).map(() => this); + } + + getLayoutPools(): ResultAsync { + return this.getterManager.getLayoutPools(); + } + + getLayoutPool(path: string): ResultAsync, ProcessError | ParsingError> { + return this.getterManager.getLayoutPool(path); + } + + setLayoutPool(path: string, layoutPool: string): ResultAsync { + return this.getterManager.setLayoutPool(path, layoutPool).map(() => this); + } + + removeLayoutPool(path: string): ResultAsync { + return this.getterManager.removeLayoutPool(path).map(() => this); + } +} + +export function getCephOptionManager( + server: Server | [Server], + clientName: `client.${string}` +): CephOptionManagerSingleServer; +export function getCephOptionManager( + servers: [Server, ...Server[]], + clientName: `client.${string}` +): CephOptionManagerClustered; +export function getCephOptionManager( + servers: Server | [Server, ...Server[]], + clientName: `client.${string}` +): CephOptionManagerSingleServer | CephOptionManagerClustered { + if (Array.isArray(servers)) { + return servers.length === 1 + ? new CephOptionManagerSingleServer(servers[0], clientName) + : new CephOptionManagerClustered(servers, clientName); + } + return new CephOptionManagerSingleServer(servers, clientName); +} diff --git a/file-sharing/src/common/hooks.ts b/file-sharing/src/common/hooks.ts new file mode 100644 index 0000000..7ede70a --- /dev/null +++ b/file-sharing/src/common/hooks.ts @@ -0,0 +1,55 @@ +import { onMounted, onUnmounted } from "vue"; +import { ResultAsync, ok, err } from "neverthrow"; +import { type SambaShareConfig } from "@/tabs/samba/data-types"; +import { type NFSExport } from "@/tabs/nfs/data-types"; +import { type Server } from "@45drives/houston-common-lib"; + +type Common = { + [P in keyof A & keyof B]: A[P] | B[P]; +}; + +export type Share = Common; + +export type HookCallback = (server: Server, share: Share) => ResultAsync | void; + +export const Hooks = { + BeforeAddShare: Symbol("BeforeAddShare"), + AfterAddShare: Symbol("AfterAddShare"), + BeforeEditShare: Symbol("BeforeEditShare"), + AfterEditShare: Symbol("AfterEditShare"), + BeforeRemoveShare: Symbol("BeforeRemoveShare"), + AfterRemoveShare: Symbol("AfterRemoveShare"), +}; + +export type Hook = (typeof Hooks)[keyof typeof Hooks]; + +const hookCallbacks = new Map>( + Object.entries(Hooks).map(([_, hook]) => [hook, new Set()]) +); + +const getHookCallbacks = (hook: Hook) => { + const callbacks = hookCallbacks.get(hook); + return callbacks ? ok(callbacks) : err(new Error(`Hooks not in Map for ${String(hook)}`)); +}; + +export const executeHookCallbacks = (hook: Hook, server: Server, share: Share) => + getHookCallbacks(hook).asyncAndThen((hooks) => + ResultAsync.combine( + [...hooks.values()] + .map((callback) => callback(server, share)) + .filter((result): result is ResultAsync => result instanceof ResultAsync) + ) + ); + +export const registerHookCallback = (hook: Hook, callback: HookCallback) => + getHookCallbacks(hook).map((callbacks) => callbacks.add(callback)); +export const unregisterHookCallback = (hook: Hook, callback: HookCallback) => + getHookCallbacks(hook).map((callbacks) => callbacks.delete(callback)); + +export const useHookCallback = (hook: Hook | Hook[], callback: HookCallback) => { + const hooks = [hook].flat(); + for (const hook of hooks) { + onMounted(() => registerHookCallback(hook, callback)); + onUnmounted(() => unregisterHookCallback(hook, callback)); + } +}; diff --git a/file-sharing/src/common/injectionKeys.ts b/file-sharing/src/common/injectionKeys.ts new file mode 100644 index 0000000..00c8d63 --- /dev/null +++ b/file-sharing/src/common/injectionKeys.ts @@ -0,0 +1,7 @@ +import { type InjectionKey, type Ref } from "vue"; +import { getServerCluster } from "@45drives/houston-common-lib"; + +export const serverClusterInjectionKey = Symbol() as InjectionKey< + ReturnType +>; +export const cephClientNameInjectionKey = Symbol() as InjectionKey>; diff --git a/file-sharing/src/common/systemd-manager.ts b/file-sharing/src/common/systemd-manager.ts new file mode 100644 index 0000000..dfa4050 --- /dev/null +++ b/file-sharing/src/common/systemd-manager.ts @@ -0,0 +1,437 @@ +import { + type IniConfigData, + ProcessError, + ParsingError, + type Server, + Command, + IniSyntax, + File, + type SyntaxParser, +} from "@45drives/houston-common-lib"; +import { ResultAsync, errAsync, okAsync, err, ok } from "neverthrow"; + +export type SystemdUnitType = + | "service" + | "mount" + | "swap" + | "socket" + | "target" + | "device" + | "automount" + | "timer" + | "path" + | "slice" + | "scope"; + +export type SystemdUnit = { + name: `${string}.${T}`; +}; + +export function isMount(unit: SystemdUnit): unit is SystemdUnit<"mount"> { + return unit.name.endsWith(".mount"); +} + +export type SystemdUnitSettingsBase = IniConfigData & { + Unit?: { + DefaultDependencies?: "yes" | "no"; + After?: string | [string, ...string[]]; + Wants?: string | [string, ...string[]]; + Conflicts?: string | [string, ...string[]]; + Before?: string | [string, ...string[]]; + Description?: string; + }; + + Install?: { + Alias?: string | [string, ...string[]]; + WantedBy?: string | [string, ...string[]]; + RequiredBy?: string | [string, ...string[]]; + UpheldBy?: string | [string, ...string[]]; + Also?: string | [string, ...string[]]; + }; +}; + +export type SystemdMountSettings = SystemdUnitSettingsBase & { + Mount: { + What: string; + Where: string; + Type?: string; + Options?: string; + LazyUnmount?: string; + }; +}; + +export type SystemdUnitSettings = T extends "mount" + ? SystemdMountSettings + : SystemdUnitSettingsBase; + +export function isMountSettings( + settings: SystemdUnitSettingsBase +): settings is SystemdMountSettings { + return ( + "Mount" in settings && + "What" in settings.Mount && + "Where" in settings.Mount && + typeof settings.Mount.What === "string" && + typeof settings.Mount.Where === "string" + ); +} + +export interface ISystemdManager { + setServiceManager(serviceManager: "system" | "user"): this; + getServiceManager(): "system" | "user"; + listUnits(): ResultAsync; + listUnits( + type: T + ): ResultAsync[], ProcessError | ParsingError>; + createUnit( + unit: SystemdUnit, + settings: SystemdUnitSettings + ): ResultAsync; + removeUnit(unit: SystemdUnit): ResultAsync; + checkEnabled(unit: SystemdUnit): ResultAsync; + enable(unit: SystemdUnit, now?: "now"): ResultAsync; + disable(unit: SystemdUnit, now?: "now"): ResultAsync; + checkActive(unit: SystemdUnit): ResultAsync; + start(unit: SystemdUnit): ResultAsync; + restart(unit: SystemdUnit): ResultAsync; + stop(unit: SystemdUnit): ResultAsync; + getSettings( + unit: SystemdUnit + ): ResultAsync, ProcessError | ParsingError>; + setSettings( + unit: SystemdUnit, + settings: SystemdUnitSettings + ): ResultAsync; + escape( + text: string, + opts?: { path?: boolean; suffix?: SystemdUnitType } + ): ResultAsync; + unescape(text: string, opts?: { path?: boolean }): ResultAsync; + pathToMountUnitName(path: string): ResultAsync["name"], ProcessError>; + mountUnitNameToPath(unitName: SystemdUnit<"mount">["name"]): ResultAsync; +} + +export class SystemdManagerSingleServer implements ISystemdManager { + private unitFileDirectory: string; + private iniParser: SyntaxParser>; + + constructor( + private server: Server, + private serviceManager: "system" | "user" = "system" + ) { + this.unitFileDirectory = `/etc/systemd/${serviceManager}`; + this.iniParser = IniSyntax({ duplicateKey: "append", paramIndent: "" }); + } + + private systemctlCommand(command: string, ...args: string[]) { + return new Command(["systemctl", `--${this.serviceManager}`, command, ...args], { + superuser: this.serviceManager === "system" ? "try" : undefined, + }); + } + + private getUnitProperty(unit: SystemdUnit, property: string) { + return this.server + .execute(this.systemctlCommand("show", unit.name, `--property=${property}`, "--value")) + .map((proc) => proc.getStdout().trim()); + } + + private getUnitFilePath(unit: SystemdUnit) { + return this.getUnitProperty(unit, "FragmentPath").andThen((filePath) => + filePath === "" + ? err(new ParsingError(`Failed to get unit file path for ${unit.name}`)) + : ok(filePath) + ); + } + + setServiceManager(serviceManager: "system" | "user") { + this.serviceManager = serviceManager; + this.unitFileDirectory = `/etc/systemd/${serviceManager}`; + return this; + } + + getServiceManager() { + return this.serviceManager; + } + + listUnits( + type?: T + ): ResultAsync[], ProcessError | ParsingError> { + return this.server + .execute( + this.systemctlCommand( + "list-unit-files", + ...(type ? [`--type=${type}`] : []), + "--output=json" + ) + ) + .map((proc) => proc.getStdout()) + .andThen((unitFilesJson) => { + try { + return ok(JSON.parse(unitFilesJson) as { unit_file: SystemdUnit["name"] }[]); + } catch (e) { + if (e instanceof Error) { + return err(new ParsingError(e.message)); + } + return err(new ParsingError(`unknown error: ${e}`)); + } + }) + .map((unitFiles) => + unitFiles.map((unitFile) => ({ + name: unitFile.unit_file, + })) + ); + } + + createUnit( + unit: SystemdUnit, + settings: SystemdUnitSettings + ): ResultAsync { + return new File(this.server, `${this.unitFileDirectory}/${unit.name}`) + .assertExists(false) + .andThen((unitFile) => unitFile.create(false, { superuser: "try" })) + .andThen(() => this.setSettings(unit, settings)) + .map(() => unit); + } + + removeUnit(unit: SystemdUnit): ResultAsync { + return this.checkActive(unit) + .andThen((isActive) => + isActive ? err(new ProcessError(`Cannot remove active unit: ${unit.name}`)) : ok(unit) + ) + .andThen((unit) => + this.checkEnabled(unit).andThen((isEnabled) => + isEnabled ? err(new ProcessError(`Cannot remove enabled unit: ${unit.name}`)) : ok(unit) + ) + ) + .andThen((unit) => this.getUnitFilePath(unit).map((path) => new File(this.server, path))) + .andThen((unitFile) => unitFile.assertIsFile()) + .andThen((unitFile) => unitFile.remove({ superuser: "try" })) + .andThen(() => this.server.execute(this.systemctlCommand("daemon-reload"))) + .map(() => unit); + } + + checkEnabled(unit: SystemdUnit): ResultAsync { + return this.server + .execute(this.systemctlCommand("is-enabled", unit.name), false) + .map((proc) => proc.exitStatus === 0); + } + + enable(unit: SystemdUnit, now?: "now"): ResultAsync { + return this.server + .execute(this.systemctlCommand("enable", ...(now ? ["--now"] : []), unit.name), true) + .map(() => unit); + } + + disable(unit: SystemdUnit, now?: "now"): ResultAsync { + return this.server + .execute(this.systemctlCommand("disable", ...(now ? ["--now"] : []), unit.name), true) + .map(() => unit); + } + + checkActive(unit: SystemdUnit): ResultAsync { + return this.server + .execute(this.systemctlCommand("is-active", unit.name), false) + .map((proc) => proc.exitStatus === 0); + } + + start(unit: SystemdUnit): ResultAsync { + return this.server.execute(this.systemctlCommand("start", unit.name), true).map(() => unit); + } + + restart(unit: SystemdUnit): ResultAsync { + return this.server.execute(this.systemctlCommand("restart", unit.name), true).map(() => unit); + } + + stop(unit: SystemdUnit): ResultAsync { + return this.server.execute(this.systemctlCommand("stop", unit.name), true).map(() => unit); + } + + getSettings( + unit: SystemdUnit + ): ResultAsync, ProcessError | ParsingError> { + return this.server + .execute(this.systemctlCommand("cat", unit.name)) + .map((proc) => proc.getStdout()) + .andThen((unitSettingsText) => + this.iniParser.apply(unitSettingsText).map((settings) => settings as SystemdUnitSettings) + ) + .andThen((settings) => { + if (isMount(unit) && !isMountSettings(settings)) { + return errAsync(new ParsingError("Systemd mount unit settings malformed")); + } + return okAsync(settings); + }); + } + + setSettings( + unit: SystemdUnit, + settings: SystemdUnitSettings + ): ResultAsync { + return isMount(unit) && !isMountSettings(settings) + ? errAsync(new ParsingError("Systemd mount unit settings malformed")) + : this.iniParser + .unapply(settings) + .asyncAndThen((settingsText) => + this.getUnitFilePath(unit) + .andThen((filePath) => + filePath.startsWith("/run") + ? err(new ProcessError(`Tried to set settings on volatile unit: ${filePath}`)) + : ok(filePath) + ) + .andThen((filePath) => + new File(this.server, filePath).write(settingsText, { superuser: "try" }) + ) + ) + .andThen(() => this.server.execute(this.systemctlCommand("daemon-reload"))) + .map(() => unit); + } + + escape( + text: string, + opts?: { path?: boolean; suffix?: SystemdUnitType } + ): ResultAsync { + return this.server + .execute( + new Command([ + "systemd-escape", + ...(opts?.path ? ["--path"] : []), + ...(opts?.suffix ? [`--suffix=${opts.suffix}`] : []), + text, + ]) + ) + .map((proc) => proc.getStdout().trim()); + } + + unescape(text: string, opts?: { path?: boolean }): ResultAsync { + return this.server + .execute( + new Command(["systemd-escape", "--unescape", ...(opts?.path ? ["--path"] : []), text]) + ) + .map((proc) => proc.getStdout().trim()); + } + + pathToMountUnitName(path: string): ResultAsync["name"], ProcessError> { + return this.escape(path, { path: true }).map<`${string}.mount`>( + (mountUnitName) => `${mountUnitName}.mount` + ); + } + mountUnitNameToPath(unitName: SystemdUnit<"mount">["name"]): ResultAsync { + return this.unescape(unitName.replace(/\.mount$/, ""), { path: true }); + } +} + +export class SystemdManagerClustered implements ISystemdManager { + private managers: [SystemdManagerSingleServer, ...SystemdManagerSingleServer[]]; + private getterManager: SystemdManagerSingleServer; + + constructor(servers: [Server, ...Server[]], serviceManager: "system" | "user" = "system") { + this.managers = servers.map((s) => new SystemdManagerSingleServer(s, serviceManager)) as [ + SystemdManagerSingleServer, + ...SystemdManagerSingleServer[], + ]; + this.getterManager = this.managers[0]; + } + + setServiceManager(serviceManager: "system" | "user") { + this.managers.forEach((m) => m.setServiceManager(serviceManager)); + return this; + } + + getServiceManager() { + return this.getterManager.getServiceManager(); + } + + listUnits(type?: T) { + return this.getterManager.listUnits(type); + } + + createUnit(unit: SystemdUnit, settings: SystemdUnitSettings) { + return ResultAsync.combine(this.managers.map((m) => m.createUnit(unit, settings))).map( + ([unit]) => unit! + ); + } + + removeUnit(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.removeUnit(...args))).map( + ([unit]) => unit! + ); + } + + checkEnabled(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.checkEnabled(...args))).map((results) => + results.every((r) => r) + ); + } + + enable(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.enable(...args))).map(([unit]) => unit!); + } + + disable(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.disable(...args))).map(([unit]) => unit!); + } + + checkActive(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.checkActive(...args))).map((results) => + results.every((r) => r) + ); + } + + start(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.start(...args))).map(([unit]) => unit!); + } + + restart(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.restart(...args))).map(([unit]) => unit!); + } + + stop(...args: Parameters) { + return ResultAsync.combine(this.managers.map((m) => m.stop(...args))).map(([unit]) => unit!); + } + + getSettings(unit: SystemdUnit) { + return this.getterManager.getSettings(unit); + } + + setSettings(unit: SystemdUnit, settings: SystemdUnitSettings) { + return ResultAsync.combine(this.managers.map((m) => m.setSettings(unit, settings))).map( + ([unit]) => unit! + ); + } + + escape(...args: Parameters) { + return this.getterManager.escape(...args); + } + + unescape(...args: Parameters) { + return this.getterManager.unescape(...args); + } + + pathToMountUnitName(...args: Parameters) { + return this.getterManager.pathToMountUnitName(...args); + } + + mountUnitNameToPath(...args: Parameters) { + return this.getterManager.mountUnitNameToPath(...args); + } +} + +export function getSystemdManager( + server: Server | [Server], + serviceManager?: "system" | "user" +): SystemdManagerSingleServer; +export function getSystemdManager( + servers: [Server, ...Server[]], + serviceManager?: "system" | "user" +): SystemdManagerClustered; +export function getSystemdManager( + servers: Server | [Server, ...Server[]], + serviceManager: "system" | "user" = "system" +): SystemdManagerSingleServer | SystemdManagerClustered { + if (Array.isArray(servers)) { + return servers.length === 1 + ? new SystemdManagerSingleServer(servers[0], serviceManager) + : new SystemdManagerClustered(servers, serviceManager); + } + return new SystemdManagerSingleServer(servers, serviceManager); +} diff --git a/file-sharing/src/common/ui/CephOptions.vue b/file-sharing/src/common/ui/CephOptions.vue new file mode 100644 index 0000000..14527cc --- /dev/null +++ b/file-sharing/src/common/ui/CephOptions.vue @@ -0,0 +1,233 @@ + + + diff --git a/file-sharing/src/common/ui/ShareDirectoryInputAndOptions.vue b/file-sharing/src/common/ui/ShareDirectoryInputAndOptions.vue new file mode 100644 index 0000000..7ec485d --- /dev/null +++ b/file-sharing/src/common/ui/ShareDirectoryInputAndOptions.vue @@ -0,0 +1,327 @@ + + + diff --git a/file-sharing/src/common/ui/UserSettingsView.vue b/file-sharing/src/common/ui/UserSettingsView.vue new file mode 100644 index 0000000..a6e2795 --- /dev/null +++ b/file-sharing/src/common/ui/UserSettingsView.vue @@ -0,0 +1,116 @@ + + + diff --git a/file-sharing/src/common/useMountpointInfo.ts b/file-sharing/src/common/useMountpointInfo.ts new file mode 100644 index 0000000..0e4f893 --- /dev/null +++ b/file-sharing/src/common/useMountpointInfo.ts @@ -0,0 +1,35 @@ +import { ref, watchEffect, type Ref } from "vue"; + +import { + Server, + type CommandOptions, + FileSystemNode, + type FilesystemMount, +} from "@45drives/houston-common-lib"; +import { ResultAsync, okAsync } from "neverthrow"; + +export const useMountpointInfo = ( + path: Ref, + server: Server | ResultAsync, + commandOptions: CommandOptions = { superuser: "try" } +) => { + if (server instanceof Server) { + server = okAsync(server); + } + + const fsNode = (path: string) => server.map((server) => new FileSystemNode(server, path)); + + const mountpointInfo = ref(); + const updateMountpointInfo = () => { + fsNode(path.value) + .andThen((node) => node.findLongestExistingStem(commandOptions)) + .andThen((node) => node.getFilesystemMount(commandOptions)) + .map((mi) => (mountpointInfo.value = mi)); + }; + watchEffect(updateMountpointInfo); + + return { + mountpointInfo, + updateMountpointInfo, + }; +}; diff --git a/file-sharing/src/common/user-settings.ts b/file-sharing/src/common/user-settings.ts new file mode 100644 index 0000000..d735181 --- /dev/null +++ b/file-sharing/src/common/user-settings.ts @@ -0,0 +1,105 @@ +import { computed, ref, type Ref, type WritableComputedRef } from "vue"; + +export type UserSettings = { + /** + * Samba-specific settings + */ + samba: { + /** + * Path to smb.conf + */ + confPath: string; + }; + /** + * NFS-specific settings + */ + nfs: { + /** + * Path to cockpit-file-sharing.exports or equivalent + */ + confPath: string; + }; + /** + * iSCSI-specific settings + */ + iscsi: { + /** + * Path to iSCSI configuration path. + */ + confPath: string; + clusteredServerChecked: boolean; + clusteredServer: boolean; + subnetMask: number; + }; + /** + * Include users and groups with uid and gid from 1 to 999 + */ + includeSystemAccounts: boolean; +}; + +const defaultSettings = (): UserSettings => ({ + samba: { + confPath: "/etc/samba/smb.conf", + }, + nfs: { + confPath: "/etc/exports.d/cockpit-file-sharing.exports", + }, + iscsi: { + confPath: "/etc/scst/cockpit-iscsi.conf", + clusteredServerChecked: false, + clusteredServer: false, + subnetMask: 16, + }, + includeSystemAccounts: false, +}); + +const configPath = "/etc/cockpit-file-sharing.conf.json"; + +const configFile = cockpit.file(configPath, { + superuser: "try", + syntax: JSON, +}); + +const config = ref(defaultSettings()); + +const configFileReadPromise = new Promise>((resolve) => { + configFile.watch( + (contents: Partial | null) => { + if (contents !== null) { + config.value = { + samba: { + confPath: contents.samba?.confPath || defaultSettings().samba.confPath, + }, + nfs: { + confPath: contents.nfs?.confPath || defaultSettings().nfs.confPath, + }, + iscsi: { + confPath: contents.iscsi?.confPath || defaultSettings().iscsi.confPath, + clusteredServer: contents.iscsi?.clusteredServer || defaultSettings().iscsi.clusteredServer, + clusteredServerChecked: contents.iscsi?.clusteredServerChecked ?? defaultSettings().iscsi.clusteredServerChecked, + subnetMask: contents.iscsi?.subnetMask || defaultSettings().iscsi.subnetMask, + }, + includeSystemAccounts: + contents.includeSystemAccounts ?? defaultSettings().includeSystemAccounts, + }; + } + resolve(config); + }, + { read: true } + ); +}); + +const computedSettingsRef = computed({ + get: () => config.value, + set: (newConfig) => configFile.replace(newConfig), +}); + +export function useUserSettings(waitUntilRead?: false): WritableComputedRef; +export function useUserSettings( + waitUntilRead: true +): Promise>; +export function useUserSettings(waitUntilRead: boolean = false) { + return waitUntilRead + ? configFileReadPromise.then(() => computedSettingsRef) + : computedSettingsRef; +} diff --git a/file-sharing/src/main.ts b/file-sharing/src/main.ts new file mode 100644 index 0000000..d70acce --- /dev/null +++ b/file-sharing/src/main.ts @@ -0,0 +1,10 @@ +import { createApp } from "vue"; + +import App from "./App.vue"; + +import "@45drives/houston-common-css/src/index.css"; +import "@45drives/houston-common-ui/style.css"; + +const app = createApp(App); + +app.mount("#app"); diff --git a/file-sharing/src/tabs/iSCSI/types/CHAPConfiguration.ts b/file-sharing/src/tabs/iSCSI/types/CHAPConfiguration.ts new file mode 100644 index 0000000..30e634c --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/CHAPConfiguration.ts @@ -0,0 +1,26 @@ +export class CHAPConfiguration { + username: string; + password: string; + chapType: CHAPType; + + constructor(username: string, password: string, chapType: CHAPType) { + this.username = username; + this.password = password; + this.chapType = chapType; + } +} + +export namespace CHAPConfiguration { + export function empty() : CHAPConfiguration { + return { + username: "", + password: "", + chapType: CHAPType.IncomingUser, + } + } +} + +export enum CHAPType { + IncomingUser = "IncomingUser", + OutgoingUser = "OutgoingUser" +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/ConfigurationManager.ts b/file-sharing/src/tabs/iSCSI/types/ConfigurationManager.ts new file mode 100644 index 0000000..f2dfcfa --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/ConfigurationManager.ts @@ -0,0 +1,34 @@ +import { useUserSettings } from "@/common/user-settings"; +import { BashCommand, ProcessError, type Server, File } from "@45drives/houston-common-lib"; +import type { ResultAsync } from "neverthrow"; + +export class ConfigurationManager { + server: Server; + + constructor(server: Server) { + this.server = server; + } + + exportConfiguration(): ResultAsync { + return this.server.execute(new BashCommand(`scstadmin -write_config ${useUserSettings().value.iscsi.confPath}`)).andThen(() => + this.server.execute(new BashCommand(`cat ${useUserSettings().value.iscsi.confPath}`)).map((proc) => + proc.getStdout() + ) + ) + } + + importConfiguration(newConfig: string) { + return new File(this.server, useUserSettings().value.iscsi.confPath) + .create() + .andThen((file) => file.write(newConfig)) + .andThen(() => this.server.execute(new BashCommand(`scstadmin -check_config ${useUserSettings().value.iscsi.confPath}`))) + .map(() => this.server.execute(new BashCommand(`scstadmin -config ${useUserSettings().value.iscsi.confPath} -force -noprompt`))) + .mapErr(() => new ProcessError("Config file syntax validation failed.")) + } + + saveCurrentConfiguration(): ResultAsync { + return new File(this.server, useUserSettings().value.iscsi.confPath) + .create(true) + .andThen((file) => this.exportConfiguration().map((config) => file.write(config)).map(() => file)) + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/Connection.ts b/file-sharing/src/tabs/iSCSI/types/Connection.ts new file mode 100644 index 0000000..4b47044 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/Connection.ts @@ -0,0 +1,6 @@ +export interface Connection { + connectionID: string; + ipAddress: string; + + devicePath: string; +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/Initiator.ts b/file-sharing/src/tabs/iSCSI/types/Initiator.ts new file mode 100644 index 0000000..fe6e492 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/Initiator.ts @@ -0,0 +1,15 @@ +export class Initiator { + name: string; + + constructor(name: string) { + this.name = name; + } +} + +export namespace Initiator { + export function empty() : Initiator { + return { + name: "", + } + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/InitiatorGroup.ts b/file-sharing/src/tabs/iSCSI/types/InitiatorGroup.ts new file mode 100644 index 0000000..068e7b1 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/InitiatorGroup.ts @@ -0,0 +1,29 @@ +import type { Initiator } from "./Initiator"; +import type { LogicalUnitNumber } from "./LogicalUnitNumber"; + +export class InitiatorGroup { + name: string; + logicalUnitNumbers: LogicalUnitNumber[]; + initiators: Initiator[]; + + devicePath: string; + + constructor(name: string, logicalUnitNumber: LogicalUnitNumber[], initiators: Initiator[], devicePath: string) { + this.name = name; + this.logicalUnitNumbers = logicalUnitNumber; + this.initiators = initiators; + this.devicePath = devicePath; + + } +} + +export namespace InitiatorGroup { + export function empty() : InitiatorGroup { + return { + name: "", + devicePath: "", + logicalUnitNumbers: [], + initiators: [], + } + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/LogicalUnitNumber.ts b/file-sharing/src/tabs/iSCSI/types/LogicalUnitNumber.ts new file mode 100644 index 0000000..5227153 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/LogicalUnitNumber.ts @@ -0,0 +1,23 @@ +import type { VirtualDevice } from "./VirtualDevice"; + +export class LogicalUnitNumber { + name: string; + unitNumber: number; + blockDevice?: VirtualDevice; + + constructor(name: string, unitNumber: number, blockDevice?: VirtualDevice) { + this.name = name; + this.unitNumber = unitNumber; + this.blockDevice = blockDevice; + } +} + +export namespace LogicalUnitNumber { + export function empty() : LogicalUnitNumber { + return { + name: "", + unitNumber: 0, + blockDevice: undefined, + } + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/Portal.ts b/file-sharing/src/tabs/iSCSI/types/Portal.ts new file mode 100644 index 0000000..c3cb15a --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/Portal.ts @@ -0,0 +1,7 @@ +export class Portal { + address: string; + + constructor(address: string) { + this.address = address; + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/Session.ts b/file-sharing/src/tabs/iSCSI/types/Session.ts new file mode 100644 index 0000000..6d8e108 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/Session.ts @@ -0,0 +1,11 @@ +import type { Connection } from "./Connection"; + +export interface Session { + initiatorName: string; + connections: Connection[]; + + writeAmountKB: number; + readAmountKB: number; + + devicePath: string; +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/Target.ts b/file-sharing/src/tabs/iSCSI/types/Target.ts new file mode 100644 index 0000000..cbe18dd --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/Target.ts @@ -0,0 +1,27 @@ +import { CHAPConfiguration } from "./CHAPConfiguration"; +import type { InitiatorGroup } from "./InitiatorGroup"; +import type { Portal } from "./Portal"; +import type { Session } from "./Session"; + +export interface Target { + name: string; + chapConfigurations: CHAPConfiguration[]; + initiatorGroups: InitiatorGroup[]; + portals: Portal[]; + sessions: Session[]; + + devicePath: string; +} + +export namespace Target { + export function empty() : Target { + return { + name: "", + chapConfigurations: [], + portals: [], + sessions: [], + initiatorGroups: [], + devicePath: "", + } + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/VirtualDevice.ts b/file-sharing/src/tabs/iSCSI/types/VirtualDevice.ts new file mode 100644 index 0000000..e36a5ae --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/VirtualDevice.ts @@ -0,0 +1,18 @@ +export class VirtualDevice { + deviceName: string; + filePath: string; + blockSize: number; + deviceType: DeviceType; + + constructor(deviceName: string, filePath: string, blockSize: number, deviceType: DeviceType) { + this.deviceName = deviceName; + this.filePath = filePath; + this.blockSize = blockSize; + this.deviceType = deviceType; + } +} + +export enum DeviceType { + FileIO = "FileIO", + BlockIO = "BlockIO" +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/LogicalVolume.ts b/file-sharing/src/tabs/iSCSI/types/cluster/LogicalVolume.ts new file mode 100644 index 0000000..5d155c9 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/LogicalVolume.ts @@ -0,0 +1,15 @@ +import type { RadosBlockDevice } from '@/tabs/iSCSI/types/cluster/RadosBlockDevice'; +import type { VolumeGroup } from './VolumeGroup'; +import { DeviceType, VirtualDevice } from '@/tabs/iSCSI/types/VirtualDevice'; + +export class LogicalVolume extends VirtualDevice{ + volumeGroup: VolumeGroup; + maximumSize: number; + + constructor(deviceName: string, blockSize: number, volumeGroup: VolumeGroup, maximumSize: number) { + super(deviceName, `/dev/${volumeGroup.name}/${deviceName}`, blockSize, DeviceType.BlockIO); + + this.volumeGroup = volumeGroup; + this.maximumSize = maximumSize; + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/PCSResource.ts b/file-sharing/src/tabs/iSCSI/types/cluster/PCSResource.ts new file mode 100644 index 0000000..6b959a4 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/PCSResource.ts @@ -0,0 +1,71 @@ +import type { PCSResourceGroup } from "@/tabs/iSCSI/types/cluster/PCSResourceGroup"; +import type { Command } from "@45drives/houston-common-lib"; + +export class PCSResource { + name: string; + generationCommand: Command; + resourceType: PCSResourceType; + resourceGroup: PCSResourceGroup | undefined; + + constructor(name: string, generationCommand: Command, type: PCSResourceType, group?: PCSResourceGroup) { + this.name = name; + this.generationCommand = generationCommand; + this.resourceType = type + this.resourceGroup = group; + } +} + +export enum PCSResourceType { + PORTBLOCK_ON, + VIP, + LVM, + TARGET, + LUN , + PORTBLOCK_OFF, + RBD, +} + +interface PCSResourceInfo { + namePrefix: string, + internalTypeName: string, + orderInGroup: number; +} + +export const PCSResourceTypeInfo: { [key in PCSResourceType]: PCSResourceInfo } = { + [PCSResourceType.PORTBLOCK_ON]: { + namePrefix: "iscsi_portblock_on", + internalTypeName: "portblock", + orderInGroup: 0, + }, + [PCSResourceType.VIP]: { + namePrefix: "iscsi_vip", + internalTypeName: "IPaddr2", + orderInGroup: 1, + }, + [PCSResourceType.LVM]: { + namePrefix: "iscsi_lvm", + internalTypeName: "LVM_activate", + orderInGroup: 2, + }, + [PCSResourceType.TARGET]: { + namePrefix: "iscsi_target", + internalTypeName: "iSCSITarget", + orderInGroup: 3, + }, + [PCSResourceType.LUN]: { + namePrefix: "iscsi_lun", + internalTypeName: "iSCSILogicalUnit", + orderInGroup: 4, + }, + [PCSResourceType.PORTBLOCK_OFF]: { + namePrefix: "iscsi_portblock_off", + internalTypeName: "portblock", + orderInGroup: 5, + }, + [PCSResourceType.RBD]: { + namePrefix: "rbd", + internalTypeName: "rbd", + orderInGroup: -1, + }, +} + diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/PCSResourceGroup.ts b/file-sharing/src/tabs/iSCSI/types/cluster/PCSResourceGroup.ts new file mode 100644 index 0000000..74a923f --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/PCSResourceGroup.ts @@ -0,0 +1,9 @@ +import type { PCSResource } from "@/tabs/iSCSI/types/cluster/PCSResource"; + +export class PCSResourceGroup { + name: string; + + constructor(name: string) { + this.name = name; + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/PCSResourceManager.ts b/file-sharing/src/tabs/iSCSI/types/cluster/PCSResourceManager.ts new file mode 100644 index 0000000..170101b --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/PCSResourceManager.ts @@ -0,0 +1,206 @@ +import { safeTry, okAsync, ok } from 'neverthrow'; +import { ResultAsync } from 'neverthrow'; +import { PCSResource, PCSResourceType, PCSResourceTypeInfo } from "@/tabs/iSCSI/types/cluster/PCSResource"; +import { PCSResourceGroup } from '@/tabs/iSCSI/types/cluster/PCSResourceGroup'; +import { BashCommand, ProcessError, safeJsonParse, type Server } from "@45drives/houston-common-lib"; + +export class PCSResourceManager { + + server: Server; + + currentResources: PCSResource[] | undefined; + + constructor(server: Server) { + this.server = server; + + this.currentResources = undefined; + } + + createResource(name: string, creationArugments: string, type: PCSResourceType) { + const resourceName = name.replace(':', '_'); + + const creationCommand = new BashCommand(`pcs resource create '${resourceName}' ${creationArugments}`); + + return this.server.execute(creationCommand) + .map(() => { + const resource = new PCSResource(resourceName, creationCommand, type); + this.currentResources = [...this.currentResources!, resource]; + return resource; + }) + } + + deleteResource(resource: Pick) { + return this.server.execute(new BashCommand(`pcs resource delete '${resource.name}'`)) + .map(() => { + this.currentResources = this.currentResources!.filter((existingResource) => existingResource.name !== resource.name); + return undefined; + }) + } + + disableResource(resource: Pick) { + return this.server.execute(new BashCommand(`pcs resource disable '${resource.name}'`)) + .map(() => undefined); + } + + deleteResourceGroup(resourceGroup: Pick) { + return this.server.execute(new BashCommand(`pcs resource delete '${resourceGroup.name}'`)) + .map(() => { + this.currentResources = this.currentResources!.filter((existingResource) => existingResource.resourceGroup?.name !== resourceGroup.name); + return undefined; + }) + } + + updateResource(resource: PCSResource, parameters: String) { + return this.server.execute(new BashCommand(`pcs resource update '${resource.name}' ${parameters}`)) + .map(() => undefined) + } + + addResourceToGroup(resource: PCSResource, resourceGroup: PCSResourceGroup): ResultAsync { + const self = this; + + return new ResultAsync(safeTry(async function * () { + const currentResourceIndex = PCSResourceTypeInfo[resource.resourceType].orderInGroup; + + const resources = yield * self.fetchResources().safeUnwrap(); + + const nextResource = resources + .filter((resource) => resource.resourceGroup?.name === resourceGroup.name) + .sort((r1, r2) => PCSResourceTypeInfo[r1.resourceType].orderInGroup - PCSResourceTypeInfo[r2.resourceType].orderInGroup) + .find((groupResource) => currentResourceIndex <= PCSResourceTypeInfo[groupResource.resourceType].orderInGroup); + + let positionArgument: string[] = []; + + if (nextResource !== undefined) { + positionArgument = [`--before`, nextResource.name]; + } + + resource.resourceGroup = resourceGroup; + + return self.server.execute(new BashCommand(`pcs resource group add '${resourceGroup.name}' ${positionArgument.join(" ")} '${resource.name}'`)).map(() => undefined); + })) + } + + constrainResourceToGroup(resource: PCSResource, resourceGroup: PCSResourceGroup) { + return this.server.execute(new BashCommand(`pcs constraint colocation add '${resource.name}' with '${resourceGroup.name}'`)) + .map(() => undefined) + } + + orderResourceBeforeGroup(resource: PCSResource, resourceGroup: PCSResourceGroup) { + return this.server.execute(new BashCommand(`pcs constraint order start '${resource.name}' then '${resourceGroup.name}'`)) + .map(() => undefined); + } + + fetchResourceConfig(resource: Pick) { + return this.server.execute(new BashCommand(`pcs resource config --output-format json '${resource.name}'`)) + .map((process) => process.getStdout()) + .andThen(safeJsonParse); + } + + fetchResourceInstanceAttributeValue(resource: Pick, nvPairName: string) { + return this.fetchResourceConfig(resource).map((config) => config.primitives![0]!.instance_attributes![0]!.nvpairs.find((pair) => pair.name === nvPairName)?.value); + } + + fetchResourceInstanceAttributeValues(resource: Pick, nvPairName: string[]) { + return this.fetchResourceConfig(resource).map((config) => { + let pairResults = new Map(); + + for (var pairName of nvPairName) { + pairResults.set(pairName, config.primitives![0]!.instance_attributes![0]!.nvpairs.find((pair) => pair.name === pairName)?.value); + } + + return pairResults; + }); + } + + fetchResourceByName(resourceName: string) { + return this.fetchResources().map((resources) => resources.find((resource) => resource.name === resourceName)); + } + + fetchResources() { + if (this.currentResources === undefined) { + return this.server.execute(new BashCommand(`pcs resource config --output-format json`)) + .map((process) => process.getStdout()) + .andThen(safeJsonParse) + .mapErr((err) => new ProcessError(`Unable to get current PCS configuration: ${err}`)) + .map((partialObject) => { + return partialObject.primitives!.map((resource) => { + return this.getGenerationCommandForPCSResource(resource) + .map((command) => { + const resourceType = this.getResourceTypeOfPCSResource(resource); + + if (resourceType !== undefined) { + let groupObject = undefined; + const group = partialObject.groups!.find((value) => value.member_ids.includes(resource.id)); + + if (group !== undefined) { + groupObject = new PCSResourceGroup(group.id); + } + + // Target Resources require a resource group. + if (resourceType !== PCSResourceType.TARGET || (resourceType === PCSResourceType.TARGET && groupObject !== undefined)) + return new PCSResource(resource.id, command, resourceType, groupObject); + } + + return undefined; + }) + }) + }) + .andThen((resourceMap) => ResultAsync.combine(resourceMap).map((resourceList) => resourceList.filter((resource) => resource !== undefined)) as ResultAsync) + .map((resources) => { + this.currentResources = resources; + return resources; + }); + } + + return okAsync(this.currentResources!); + } + + getResourceTypeOfPCSResource(resource: PCSResourceConfigJson) { + for (const type of Object.values(PCSResourceType).filter((value) => typeof value === 'number')){ + const info = PCSResourceTypeInfo[type as PCSResourceType]; + + if (info.internalTypeName === resource.agent_name.type) { + if (info.internalTypeName === "portblock") { + for (const attribute of resource.instance_attributes) { + for (const nvpair of attribute.nvpairs) { + if (nvpair.name === "action") { + return nvpair.value === "block" ? PCSResourceType.PORTBLOCK_ON : PCSResourceType.PORTBLOCK_OFF; + } + } + } + } + else + return type as PCSResourceType; + } + } + + return undefined; + } + + getGenerationCommandForPCSResource(resource: PCSResourceConfigJson) { + return this.server.execute(new BashCommand(`pcs resource config '${resource.id}' --output-format cmd`)) + .map((proc) => new BashCommand(proc.getStdout().replace(/\\\n/g, "").replace(/\n/g, ""))); + } +} + +type PCSResourceConfigJson = { + id: string, + agent_name: { + type: string + }, + instance_attributes: { + nvpairs: { + id: string, + name: string, + value: string, + }[] + }[] +} + +type PCSConfigJson = { + primitives: PCSResourceConfigJson[], + groups: { + id: string, + member_ids: string[] + }[] +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/PhysicalVolume.ts b/file-sharing/src/tabs/iSCSI/types/cluster/PhysicalVolume.ts new file mode 100644 index 0000000..38b41cc --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/PhysicalVolume.ts @@ -0,0 +1,9 @@ +import type { RadosBlockDevice } from './RadosBlockDevice'; + +export class PhysicalVolume { + rbd: RadosBlockDevice; + + constructor(rbd: RadosBlockDevice) { + this.rbd = rbd; + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/Pool.ts b/file-sharing/src/tabs/iSCSI/types/cluster/Pool.ts new file mode 100644 index 0000000..a205c1c --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/Pool.ts @@ -0,0 +1,14 @@ +export class Pool { + name: string; + poolType: PoolType; + + constructor(name: string, poolType: PoolType) { + this.name = name; + this.poolType = poolType; + } +} + +export enum PoolType { + Replication = "Replication", + ErasureCoded = "Erasure Coded" +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/RBDManager.ts b/file-sharing/src/tabs/iSCSI/types/cluster/RBDManager.ts new file mode 100644 index 0000000..ab0d9b5 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/RBDManager.ts @@ -0,0 +1,237 @@ +import { VirtualDevice } from './../VirtualDevice'; +import { VolumeGroup } from './VolumeGroup'; +import { PhysicalVolume } from './PhysicalVolume'; +import { LogicalVolume } from '@/tabs/iSCSI/types/cluster/LogicalVolume'; +import { RadosBlockDevice } from './RadosBlockDevice'; +import { Pool, PoolType } from "@/tabs/iSCSI/types/cluster/Pool"; +import { BashCommand, ProcessError, safeJsonParse, StringToIntCaster, type Server } from '@45drives/houston-common-lib'; +import { err, errAsync, ok, okAsync, ResultAsync, safeTry } from 'neverthrow'; + +export class RBDManager { + + server: Server; + + constructor(server: Server) { + this.server = server; + } + + createRadosBlockDevice(name: string, size: number, parentPool: Pool, dataPool?: Pool) { + const dataPoolArgument = dataPool === undefined ? "" : `--data-pool ${dataPool.name}` + + return this.server.execute(new BashCommand(`rbd create ${parentPool.name}/${name} --size ${size}B ${dataPoolArgument}`)) + .andThen(() => + this.server.execute(new BashCommand(`rbd map ${parentPool.name}/${name}`)) + .andThen((mapProc) => this.server.execute(new BashCommand(`blockdev --getbsz ${mapProc.getStdout()}`)) + .andThen((blockSizeProc) => { + const blockSize = StringToIntCaster()(blockSizeProc.getStdout()) + + if (!blockSize.isNone()) + return okAsync(new RadosBlockDevice(name, mapProc.getStdout().trim(), blockSize.some(), size, parentPool, dataPool)); + + return errAsync(new ProcessError("Unable to determine block size of RBD")); + }) + ) + ) + } + + createLogicalVolumeFromRadosBlockDevices(logicalVolumeName: string, volumeGroupName: string, rbds: RadosBlockDevice[]) { + const rbdPaths = rbds.map((rbd) => rbd.filePath).join(' '); + let createdLogicalVolume: LogicalVolume | null = null; + + return ResultAsync.combine(rbds.map((rbd) => this.server.execute(new BashCommand(`pvcreate ${rbd.filePath}`)).map(() => new PhysicalVolume(rbd)))) + .andThen((physicalVolumes) => this.server.execute(new BashCommand(`vgcreate ${volumeGroupName} ${rbdPaths}`)).map(() => new VolumeGroup(volumeGroupName, physicalVolumes))) + .andThen((volumeGroup) => this.server.execute(new BashCommand(`lvcreate -i ${rbds.length} -I 64 -l 100%FREE -n ${logicalVolumeName} ${volumeGroupName} ${rbdPaths}`)) + .andThen(() => this.server.execute(new BashCommand(`lvdisplay /dev/${volumeGroupName}/${logicalVolumeName} --units B | grep 'LV Size' | awk '{print $3, $4}'`)) + .map((proc) => proc.getStdout()) + .map((maximumSize) => { + createdLogicalVolume = new LogicalVolume(logicalVolumeName, 0, volumeGroup, StringToIntCaster()(maximumSize!).some()) + }) + ) + ) + .map(() => createdLogicalVolume!); + } + + expandRadosBlockDevice(device: RadosBlockDevice, newSizeBytes: number) { + return this.server.execute(new BashCommand(`rbd resize --size ${newSizeBytes}B ${device.deviceName}`)); + } + + expandLogicalVolume(volume: LogicalVolume, newSizeBytes: number) { + const newSizePerRBD = Math.round(newSizeBytes/volume.volumeGroup.volumes.length); + + return ResultAsync.combine(volume.volumeGroup.volumes.map((volume) => + this.expandRadosBlockDevice(volume.rbd, newSizePerRBD) + .andThen(() => this.server.execute(new BashCommand(`pvresize ${volume.rbd.filePath}`))) + )) + .andThen(() => this.server.execute(new BashCommand(`lvextend -l +100%FREE ${volume.filePath}`))); + } + + fetchAvaliablePools() { + return this.server.execute(new BashCommand(`ceph osd pool ls detail --format json`)) + .map((proc) => proc.getStdout()) + .andThen(safeJsonParse) + .map((allPoolInfo) => allPoolInfo.filter((poolInfo) => poolInfo !== undefined)) + .andThen((filteredPoolInfo) => ResultAsync.combine( + filteredPoolInfo.map((poolData) => { + if (poolData !== undefined) { + if (poolData.application_metadata.rbd !== undefined) { + let poolType = undefined; + + switch (poolData.type) { + case 1: + poolType = PoolType.Replication; + break; + case 3: + poolType = PoolType.ErasureCoded; + break; + } + + if (poolType !== undefined && poolData.pool_name !== undefined) { + return okAsync(new Pool(poolData.pool_name, poolType)); + } + } + } + + return okAsync(undefined); + }) + )) + .map((results) => results.filter((result): result is Pool => result !== undefined)); + } + + fetchAvaliableRadosBlockDevices() { + const self = this; + return this.server.execute(new BashCommand(`rbd showmapped --format json`)) + .map((proc) => proc.getStdout()) + .andThen(safeJsonParse) + .mapErr((err) => new ProcessError(`Unable to get current mapped Rados Block Devices: ${err}`)) + .andThen((rbdEntry) => ResultAsync.combine( + rbdEntry.filter((entry) => entry !== undefined) + .map((entry) => { + return new ResultAsync(safeTry(async function * () { + const blockSize = yield * self.getBlockSizeFromDevicePath(entry!.device).safeUnwrap(); + + const maximumSize = yield * self.getMaximumSizeFromRBDName(entry!.name).safeUnwrap(); + + const parentPool = yield * self.fetchAvaliablePools() + .map((pools) => pools.filter((pool) => pool!.name === entry!.pool)[0]) + .safeUnwrap(); + + if (parentPool !== undefined) { + if (parentPool.poolType === PoolType.Replication) { + return ok(new RadosBlockDevice(entry!.name, entry!.device, blockSize, maximumSize, parentPool)); + } + else if (parentPool.poolType === PoolType.ErasureCoded) { + const dataPool = yield * self.getDataPoolForRBDName(entry!.name, parentPool).safeUnwrap(); + + if (dataPool !== undefined) + return ok(new RadosBlockDevice(entry!.name, entry!.device, blockSize, maximumSize, parentPool, dataPool)); + } + } + + return err(new ProcessError("Unable to get Block Device information.")) + })) + }) + )) + } + + fetchAvaliableLogicalVolumes() { + const self = this; + + return this.server.execute(new BashCommand(`lvs --reportformat json --units B`)) + .map((proc) => proc.getStdout()) + .andThen(safeJsonParse) + .map((logicalVolumeInfo) => logicalVolumeInfo?.report?.flatMap((report) => report.lv)) + .andThen((lvList) => ResultAsync.combine(lvList!.flatMap((lvInfo) => + this.server.execute(new BashCommand(`pvs -S vgname=${lvInfo.vg_name} --reportformat json --units B`)) + .map((proc) => proc.getStdout()) + .andThen(safeJsonParse) + .map((volumeGroupEntries) => volumeGroupEntries!.report!.flatMap((report) => report.pv)) + .andThen((pvList) => new ResultAsync(safeTry(async function * () { + const mappedBlockDevices = yield * self.fetchAvaliableRadosBlockDevices().safeUnwrap(); + + const physicalVolumes = pvList.flatMap((rbdItem) => mappedBlockDevices.find((rbd) => rbd.filePath === rbdItem.pv_name)) + .filter((item) => item !== undefined) + .map((item) => new PhysicalVolume(item!)); + + return okAsync(physicalVolumes); + }))) + .map((volumes) => new VolumeGroup(lvInfo.vg_name, volumes)) + .map((volumeGroup) => new LogicalVolume(lvInfo.lv_name, 0, volumeGroup, StringToIntCaster()(lvInfo.lv_size).some()))) + )).map((LogicalVolumes) => LogicalVolumes.filter((volume) => volume.volumeGroup.volumes.length !== 0)); + } + + fetchExistingImageNames() { + return this.server.execute(new BashCommand(`rbd list`)) + .map((proc) => proc.getStdout()) + .map((output) => output.trim().split('\n')); + } + + getBlockSizeFromDevicePath(path: Pick | string) { + return this.server.execute(new BashCommand(`blockdev --getbsz ${path}`)) + .map((proc) => StringToIntCaster()(proc.getStdout())) + .andThen((maybeNumber) => maybeNumber.isSome() ? okAsync(maybeNumber.some()) : errAsync(new ProcessError(`Unable to determine block size for device: ${path}`))) + } + + getMaximumSizeFromRBDName(rbdName: Pick | string) { + return this.server.execute(new BashCommand(`rbd info ${rbdName} --format json`)) + .map((proc) => proc.getStdout()) + .andThen(safeJsonParse) + .map((rbdInfoEntry) => StringToIntCaster()(rbdInfoEntry.size!)) + .map((number) => number.some()) + .mapErr(() => new ProcessError(`Unable to determine maximum size of RBD: ${rbdName}`)) + } + + getDataPoolForRBDName(rbdName: Pick | string, parentPool: Pool) { + return this.server.execute(new BashCommand(`rbd info ${parentPool.name}/${rbdName}`)) + .map((proc) => proc.getStdout()) + .andThen(safeJsonParse) + .map((rbdInfoEntry) => { + if (rbdInfoEntry.data_pool !== undefined) { + return new Pool(rbdInfoEntry.data_pool, PoolType.ErasureCoded) + } + + return undefined + }) + } +} + +type MappedRBDJson = { + id: string, + pool: string, + namespace: string, + name: string, + snap: string, + device: string, +}[] + +type RBDInfoJson = { + name: string, + size: string, + data_pool: string, +} + +type PoolInfoJson = { + pool_name: string, + type: number, + application_metadata: { + rbd: {} + } +}[] + +type LogicalVolumeInfoJson = { + report: { + lv: { + lv_name: string, + vg_name: string, + lv_size: string, + }[] + }[] +} + +type VolumeGroupInfoJson = { + report: { + pv: { + pv_name: string, + vg_name: string, + }[] + }[] +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/RadosBlockDevice.ts b/file-sharing/src/tabs/iSCSI/types/cluster/RadosBlockDevice.ts new file mode 100644 index 0000000..505b944 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/RadosBlockDevice.ts @@ -0,0 +1,16 @@ +import type { Pool } from "@/tabs/iSCSI/types/cluster/Pool"; +import { DeviceType, VirtualDevice } from "@/tabs/iSCSI/types/VirtualDevice"; + +export class RadosBlockDevice extends VirtualDevice { + parentPool: Pool; + dataPool: Pool | undefined; + maximumSize: number; + + constructor(deviceName: string, filePath: string, blockSize: number, maximumSize: number, parentPool: Pool, dataPool?: Pool) { + super(deviceName, filePath, blockSize, DeviceType.BlockIO); + + this.parentPool = parentPool; + this.dataPool = dataPool; + this.maximumSize = maximumSize; + } +} diff --git a/file-sharing/src/tabs/iSCSI/types/cluster/VolumeGroup.ts b/file-sharing/src/tabs/iSCSI/types/cluster/VolumeGroup.ts new file mode 100644 index 0000000..bc967f0 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/cluster/VolumeGroup.ts @@ -0,0 +1,11 @@ +import type { PhysicalVolume } from "@/tabs/iSCSI/types/cluster/PhysicalVolume"; + +export class VolumeGroup { + name: string; + volumes: PhysicalVolume[]; + + constructor(name: string, volumes: PhysicalVolume[]) { + this.name = name; + this.volumes = volumes; + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriver.ts b/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriver.ts new file mode 100644 index 0000000..c07ea71 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriver.ts @@ -0,0 +1,51 @@ +import type { ResultAsync } from "neverthrow"; +import type { DeviceType, VirtualDevice } from "@/tabs/iSCSI/types/VirtualDevice"; +import type { CHAPConfiguration } from "@/tabs/iSCSI/types/CHAPConfiguration"; +import type { Connection } from "@/tabs/iSCSI/types/Connection"; +import type { Initiator } from "@/tabs/iSCSI/types/Initiator"; +import type { InitiatorGroup } from "@/tabs/iSCSI/types/InitiatorGroup"; +import type { LogicalUnitNumber } from "@/tabs/iSCSI/types/LogicalUnitNumber"; +import type { Portal } from "@/tabs/iSCSI/types/Portal"; +import type { Session } from "@/tabs/iSCSI/types/Session"; +import type { Target } from "@/tabs/iSCSI/types/Target"; +import type { ProcessError } from "@45drives/houston-common-lib"; + +export abstract class ISCSIDriver { + abstract initialize(): ResultAsync; + + abstract getHandledDeviceTypes(): DeviceType[]; + + abstract addVirtualDevice(virtualDevice: VirtualDevice): ResultAsync; + abstract removeVirtualDevice(virtualDevice: VirtualDevice): ResultAsync; + + abstract createTarget(target: Target): ResultAsync; + abstract removeTarget(target: Target): ResultAsync; + + abstract addPortalToTarget(target: Target, portal: Portal): ResultAsync; + abstract deletePortalFromTarget(target: Target, portal: Portal): ResultAsync; + + abstract addInitiatorGroupToTarget(target: Target, initiatorGroup: InitiatorGroup): ResultAsync; + abstract deleteInitiatorGroupFromTarget(target: Target, initiatorGroup: InitiatorGroup): ResultAsync; + + abstract addInitiatorToGroup(initiatorGroup: InitiatorGroup, initiator: Initiator): ResultAsync; + abstract removeInitiatorFromGroup(initiatorGroup: InitiatorGroup, initiator: Initiator): ResultAsync; + + abstract addLogicalUnitNumberToGroup(initiatorGroup: InitiatorGroup, logicalUnitNumber: LogicalUnitNumber): ResultAsync; + abstract removeLogicalUnitNumberFromGroup(initiatorGroup: InitiatorGroup, logicalUnitNumber: LogicalUnitNumber): ResultAsync; + + abstract addCHAPConfigurationToTarget(target: Target, chapConfiguration: CHAPConfiguration): ResultAsync; + abstract removeCHAPConfigurationFromTarget(target: Target, chapConfiguration: CHAPConfiguration): ResultAsync; + + abstract getVirtualDevices(): ResultAsync; + abstract getTargets(): ResultAsync; + + abstract getPortalsOfTarget(target: Target): ResultAsync; + abstract getInitatorGroupsOfTarget(target: Target): ResultAsync; + abstract getSessionsOfTarget(target: Target): ResultAsync; + abstract getCHAPConfigurationsOfTarget(target: Target): ResultAsync; + + abstract getConnectionsOfSession(session: Session): ResultAsync; + + abstract getLogicalUnitNumbersOfInitiatorGroup(initiatorGroup: InitiatorGroup): ResultAsync; + abstract getInitiatorsOfInitiatorGroup(initiatorGroup: InitiatorGroup): ResultAsync; +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriverClusteredServer.ts b/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriverClusteredServer.ts new file mode 100644 index 0000000..bb345d6 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriverClusteredServer.ts @@ -0,0 +1,663 @@ +import { PCSResourceType, PCSResourceTypeInfo } from '@/tabs/iSCSI/types/cluster/PCSResource'; +import { PCSResourceManager } from './../cluster/PCSResourceManager'; +import { RBDManager } from './../cluster/RBDManager'; +import { ConfigurationManager } from "@/tabs/iSCSI/types/ConfigurationManager"; +import { VirtualDevice, DeviceType } from "@/tabs/iSCSI/types/VirtualDevice"; +import { CHAPConfiguration, CHAPType } from "@/tabs/iSCSI/types/CHAPConfiguration"; +import { type Connection } from "@/tabs/iSCSI/types/Connection"; +import { Initiator } from "@/tabs/iSCSI/types/Initiator"; +import { InitiatorGroup } from "@/tabs/iSCSI/types/InitiatorGroup"; +import { LogicalUnitNumber } from "@/tabs/iSCSI/types/LogicalUnitNumber"; +import { Portal } from "@/tabs/iSCSI/types/Portal"; +import { type Session } from "@/tabs/iSCSI/types/Session"; +import { type Target } from "@/tabs/iSCSI/types/Target"; +import { ISCSIDriver } from "@/tabs/iSCSI/types/drivers/ISCSIDriver"; +import { + BashCommand, + Command, + Directory, + ProcessError, + Server, + StringToIntCaster, +} from "@45drives/houston-common-lib"; +import { ResultAsync, err, errAsync, ok, okAsync, safeTry } from "neverthrow"; +import { useUserSettings } from "@/common/user-settings"; +import { ISCSIDriverSingleServer } from "@/tabs/iSCSI/types/drivers/ISCSIDriverSingleServer"; +import { PCSResource } from '@/tabs/iSCSI/types/cluster/PCSResource'; +import { PCSResourceGroup } from '@/tabs/iSCSI/types/cluster/PCSResourceGroup'; +import { RadosBlockDevice } from '@/tabs/iSCSI/types/cluster/RadosBlockDevice'; +import { LogicalVolume } from '@/tabs/iSCSI/types/cluster/LogicalVolume'; +import { Result } from 'postcss'; + +export class ISCSIDriverClusteredServer implements ISCSIDriver { + server: Server; + configurationManager: ConfigurationManager; + rbdManager: RBDManager; + pcsResourceManager:PCSResourceManager; + + singleServerDriver: ISCSIDriverSingleServer | undefined; + + targets: Target[]; + virtualDevices: VirtualDevice[]; + + deviceTypeToHandlerDirectory = { + [DeviceType.BlockIO]: "/sys/kernel/scst_tgt/handlers/vdisk_blockio", + }; + + targetManagementDirectory = "/sys/kernel/scst_tgt/targets/iscsi"; + + resourceNamePrefix = "iscsi" + resourceGroupPrefix = "iscsi_group"; + + constructor(server: Server) { + this.server = server; + this.configurationManager = new ConfigurationManager(server); + this.rbdManager = new RBDManager(server); + this.pcsResourceManager = new PCSResourceManager(server); + this.virtualDevices = []; + this.targets = []; + } + + initialize() { + return new Directory(this.server, "/sys/kernel/scst_tgt").exists() + .andThen((exists) => { + if (exists) { + return this.getActiveNode() + .map((activeNode) => { + this.server = activeNode; + this.singleServerDriver = new ISCSIDriverSingleServer(activeNode); + this.configurationManager = new ConfigurationManager(activeNode); + this.rbdManager = new RBDManager(activeNode); + this.pcsResourceManager = new PCSResourceManager(activeNode); + }) + .andThen(() => this.getExistingVirtualDevices()) + .map((devices) => this.virtualDevices.push(...devices)) + .map(() => this); + } + + return err(new ProcessError("/sys/kernel/scst_tgt was not found. Is SCST installed?")) + }) + } + + getHandledDeviceTypes(): DeviceType[] { + return Object.keys(this.deviceTypeToHandlerDirectory) as DeviceType[]; + } + + getActiveNode(): ResultAsync { + return this.server + .execute(new BashCommand(`pcs status xml`)) + .map((proc) => { + const output = proc.getStdout(); + const parser = new DOMParser(); + + const doc = parser.parseFromString(output, "text/xml"); + return doc + .getElementsByTagName("resource")[0] + ?.getElementsByTagName("node")[0] + ?.attributes.getNamedItem("name")?.value; + }) + .map((nodeIP) => new Server(nodeIP)); + } + + addVirtualDevice(virtualDevice: VirtualDevice): ResultAsync { + this.virtualDevices = [...this.virtualDevices, virtualDevice]; + return okAsync(undefined); + } + + removeVirtualDevice(virtualDevice: VirtualDevice): ResultAsync { + this.virtualDevices = this.virtualDevices.filter((existingDevice) => existingDevice.deviceName !== virtualDevice.deviceName); + return okAsync(undefined); + } + + createTarget(target: Target): ResultAsync { + const targetResourceName = `${this.resourceNamePrefix}_TARGET_${target.name}`; + const creationArugments = `ocf:heartbeat:iSCSITarget iqn=${target.name} op start timeout=20 op stop timeout=20 op monitor interval=20 timeout=40`; + return this.pcsResourceManager.createResource(targetResourceName, creationArugments, PCSResourceType.TARGET) + .andThen((resource) => { + target.devicePath = resource.name; + return this.pcsResourceManager.addResourceToGroup(resource, new PCSResourceGroup(`${this.resourceGroupPrefix}_${resource.name}`)) + }) + .map(() => target.initiatorGroups.push(new InitiatorGroup("allowed", [], [], targetResourceName))) + .map(() => undefined) + } + + removeTarget(target: Target): ResultAsync { + const self = this; + + return new ResultAsync(safeTry(async function * () { + for (let group of target.initiatorGroups) { + for (let lun of group.logicalUnitNumbers) { + yield * self.removeLogicalUnitNumberFromGroup(group, lun).safeUnwrap(); + } + } + + const targetResource = yield * self.findTargetPCSResource(target).safeUnwrap(); + + yield * self.pcsResourceManager.deleteResourceGroup(targetResource.resourceGroup!).safeUnwrap(); + + return okAsync(undefined); + })) + } + + addPortalToTarget(target: Target, portal: Portal){ + const self = this; + + const createdResources: PCSResource[] = []; + + const updatedPortalList = [...target.portals.map((targetPortal) => targetPortal.address), portal.address + ":3260"].join(", "); + + const vipCreationArugments = `ocf:heartbeat:IPaddr2 ip=${portal.address} cidr_netmask=${useUserSettings().value.iscsi.subnetMask.toString()} op start timeout=20 op stop timeout=20 op monitor interval=10`; + const portblockOnCreationArugments = `ocf:heartbeat:portblock ip=${portal.address} portno=3260 protocol=tcp action=block op start timeout=20 op stop timeout=20 op monitor timeout=20 interval=20`; + const portblockOffCreationArugments = `ocf:heartbeat:portblock ip=${portal.address} portno=3260 protocol=tcp action=unblock op start timeout=20 op stop timeout=20 op monitor timeout=20 interval=20`; + + return this.findTargetPCSResource(target) + .andThen((targetResource) => this.pcsResourceManager.createResource(`${this.resourceNamePrefix}_VIP_${portal.address}`, vipCreationArugments, PCSResourceType.VIP) + .andThen((vipResource) => { + createdResources.push(vipResource); + return this.pcsResourceManager.addResourceToGroup(vipResource, targetResource.resourceGroup!); + }) + .andThen(() => this.pcsResourceManager.createResource(`${this.resourceNamePrefix}_PORTBLOCKON_${portal.address}`, portblockOnCreationArugments, PCSResourceType.PORTBLOCK_ON)) + .andThen((portBlockResource) => { + createdResources.push(portBlockResource); + return this.pcsResourceManager.addResourceToGroup(portBlockResource, targetResource.resourceGroup!); + }) + .andThen(() => this.pcsResourceManager.createResource(`${this.resourceNamePrefix}_PORTBLOCKOFF_${portal.address}`, portblockOffCreationArugments, PCSResourceType.PORTBLOCK_OFF)) + .andThen((portBlockResource) => { + createdResources.push(portBlockResource); + return this.pcsResourceManager.addResourceToGroup(portBlockResource, targetResource.resourceGroup!); + }) + .andThen(() => this.pcsResourceManager.updateResource(targetResource, `portals='${updatedPortalList}'`)) + ) + .mapErr((err) => { + createdResources.forEach((resource) => this.pcsResourceManager.deleteResource(resource)) + return err; + }); + } + + deletePortalFromTarget(target: Target, portal: Portal): ResultAsync { + return this.findTargetPCSResource(target) + .andThen((targetResource) => { + const updatedPortalList = target.portals.filter((existingPortal) => existingPortal !== portal).map((existingPortal) => existingPortal.address).join(", "); + + return this.pcsResourceManager.updateResource(targetResource, `portals='${updatedPortalList}'`) + .andThen(() => this.findPortblockPCSResource(target, portal, PCSResourceType.PORTBLOCK_OFF)) + .andThen((resource) => { + if (resource !== undefined) { + return this.pcsResourceManager.deleteResource(resource); + } + + return okAsync(undefined); + }) + .andThen(() => this.findPortblockPCSResource(target, portal, PCSResourceType.PORTBLOCK_ON)) + .andThen((resource) => { + if (resource !== undefined) { + return this.pcsResourceManager.deleteResource(resource); + } + + return okAsync(undefined); + }) + .andThen(() => this.findPortblockVIPResource(target, portal)) + .andThen((resource) => { + if (resource !== undefined) { + return this.pcsResourceManager.deleteResource(resource); + } + + return okAsync(undefined); + }) + }) + } + + // Only group available is created by default by the Target resource, called 'allowed' + addInitiatorGroupToTarget( + _target: Target, + _initiatorGroup: InitiatorGroup + ): ResultAsync { + throw new Error("Adding initiator groups is not supported by this driver."); + } + + // Only group available is created by default by the Target resource, called 'allowed' + deleteInitiatorGroupFromTarget( + _target: Target, + _initiatorGroup: InitiatorGroup + ): ResultAsync { + throw new Error("Removing initiator groups is not supported by this driver."); + } + + addInitiatorToGroup( + initiatorGroup: InitiatorGroup, + initiator: Initiator + ): ResultAsync { + const updatedInitiatorList = [...initiatorGroup.initiators, initiator].map((initiator) => initiator.name).join(" "); + + return this.pcsResourceManager.fetchResourceByName(initiatorGroup.devicePath) + .andThen((targetResource) => { + if (targetResource !== undefined) + return this.pcsResourceManager.updateResource(targetResource, `allowed_initiators='${updatedInitiatorList}'`) + + return errAsync(new ProcessError("Could not find Target resource.")) + }) + } + + removeInitiatorFromGroup( + initiatorGroup: InitiatorGroup, + initiator: Initiator + ): ResultAsync { + const updatedInitiatorList = initiatorGroup.initiators.filter((existinginitiator) => existinginitiator !== initiator).map((existinginitiator) => existinginitiator.name).join(" "); + + return this.pcsResourceManager.fetchResourceByName(initiatorGroup.devicePath) + .andThen((targetResource) => { + if (targetResource !== undefined) + return this.pcsResourceManager.updateResource(targetResource, `allowed_initiators='${updatedInitiatorList}'`) + + return errAsync(new ProcessError("Could not find Target resource.")) + }) + } + + + addLogicalUnitNumberToGroup( + initiatorGroup: InitiatorGroup, + logicalUnitNumber: LogicalUnitNumber + ): ResultAsync { + const self = this; + + return this.pcsResourceManager.fetchResourceByName(initiatorGroup.devicePath) + .andThen((targetResource) => { + if (targetResource !== undefined) { + return new ResultAsync(safeTry(async function * () { + const targetIQN = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValue(targetResource, "iqn").safeUnwrap(); + + if (logicalUnitNumber.blockDevice! instanceof RadosBlockDevice) { + yield * self.createAndConfigureRBDResource(logicalUnitNumber, targetIQN!, targetResource.resourceGroup!).safeUnwrap(); + } + else if (logicalUnitNumber.blockDevice! instanceof LogicalVolume) { + yield * self.createAndConfigureLVResources(logicalUnitNumber, targetIQN!, targetResource.resourceGroup!).safeUnwrap(); + } + else { + yield * self.pcsResourceManager.createResource(`${targetResource.resourceGroup!.name}_LUN_${logicalUnitNumber.unitNumber}`, `ocf:heartbeat:iSCSILogicalUnit target_iqn=${targetIQN} lun=${logicalUnitNumber.unitNumber} path=${logicalUnitNumber.blockDevice!.filePath} op start timeout=100 op stop timeout=100 op monitor interval=10 timeout=100`, PCSResourceType.LUN) + .andThen((resource) => self.pcsResourceManager.addResourceToGroup(resource, targetResource.resourceGroup!)).safeUnwrap(); + } + + return okAsync(undefined); + })) + } + + return errAsync(new ProcessError("Could not find Target resource.")) + }) + } + + removeLogicalUnitNumberFromGroup( + initiatorGroup: InitiatorGroup, + logicalUnitNumber: LogicalUnitNumber + ): ResultAsync { + const self = this; + + return this.pcsResourceManager.fetchResourceByName(initiatorGroup.devicePath) + .andThen((targetResource) => new ResultAsync(safeTry(async function * () { + const targetIQN = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValue(targetResource!, "iqn").safeUnwrap(); + + if (logicalUnitNumber.blockDevice! instanceof RadosBlockDevice) { + yield * self.removeRBDAndRelatedResource(logicalUnitNumber, targetIQN!).safeUnwrap(); + } + else if (logicalUnitNumber.blockDevice! instanceof LogicalVolume) { + yield * self.removeLVAndRelatedResources(logicalUnitNumber, targetIQN!).safeUnwrap(); + } + else { + yield * self.removeLUNResource(logicalUnitNumber, targetIQN!).safeUnwrap(); + } + + return okAsync(undefined) + }))); + } + + addCHAPConfigurationToTarget( + target: Target, + chapConfiguration: CHAPConfiguration + ): ResultAsync { + return this.findTargetPCSResource(target) + .andThen((targetResource) => this.pcsResourceManager.updateResource(targetResource, `incoming_username='${chapConfiguration.username}' incoming_password='${chapConfiguration.password}'`)) + } + + removeCHAPConfigurationFromTarget( + target: Target, + _chapConfiguration: CHAPConfiguration + ): ResultAsync { + return this.findTargetPCSResource(target) + .andThen((targetResource) => this.pcsResourceManager.updateResource(targetResource, `incoming_username='' incoming_password=''`)) + } + + getVirtualDevices(): ResultAsync { + return okAsync(this.virtualDevices); + } + + getExistingVirtualDevices(): ResultAsync { + const self = this; + + return new ResultAsync(safeTry(async function * () { + let foundDevices: VirtualDevice[] = []; + + const avaliableLogicalVolumes = yield * self.rbdManager.fetchAvaliableLogicalVolumes().safeUnwrap(); + const avaliableRadosBlockDevices = yield * self.rbdManager.fetchAvaliableRadosBlockDevices().safeUnwrap(); + + const resources = yield * self.pcsResourceManager.fetchResources().safeUnwrap(); + + for (let resource of resources.filter((resource) => resource.resourceType === PCSResourceType.LUN)) { + const attributes = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValues(resource, ["path"]).safeUnwrap(); + + const foundLogicalVolume = avaliableLogicalVolumes.find((volume) => volume.filePath === attributes.get("path")); + if (foundLogicalVolume !== undefined) { + foundDevices.push(foundLogicalVolume); + continue; + } + + const foundRBD = avaliableRadosBlockDevices.find((rbd) => rbd.filePath === attributes.get("path")); + if (foundRBD !== undefined) { + foundDevices.push(foundRBD); + continue; + } + + const blockSizeResult = await self.rbdManager.getBlockSizeFromDevicePath(attributes.get("path")!); + let blockSize = 0; + + if (blockSizeResult.isOk()) + blockSize = blockSizeResult.value; + + foundDevices.push(new VirtualDevice(attributes.get("path")!, attributes.get("path")!, blockSize, DeviceType.BlockIO)); + } + + return ok(foundDevices); + })); + } + + getTargets(): ResultAsync { + const self = this; + + return this.pcsResourceManager.fetchResources() + .map((resources) => resources.filter((resource) => resource.resourceType === PCSResourceType.TARGET)) + .andThen((filteredResources) => ResultAsync.combine( + filteredResources.map((resource) => + new ResultAsync(safeTry(async function * () { + const targetIQN = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValue(resource, "iqn").safeUnwrap(); + + const partialTarget = { + name: targetIQN!, + devicePath: resource.name + }; + + return ok({ + ...partialTarget, + portals: yield * self.getPortalsOfTarget(partialTarget).safeUnwrap(), + chapConfigurations: yield * self.getCHAPConfigurationsOfTarget(partialTarget).safeUnwrap(), + initiatorGroups: yield * self.getInitatorGroupsOfTarget(partialTarget).safeUnwrap(), + sessions: yield * self.getSessionsOfTarget(partialTarget).safeUnwrap() + }); + })) + ) + )) + } + + getPortalsOfTarget(target: Pick): ResultAsync { + return this.findTargetPCSResource(target) + .andThen((targetResource) => this.pcsResourceManager.fetchResourceInstanceAttributeValue(targetResource, "portals")) + .map((portalsString) => { + if (portalsString !== undefined) + return portalsString!.split(", ").map((portalAddress) => new Portal(portalAddress)); + + return []; + }) + } + + // iSCSI through PCS only seems to support one ini_group 'allowed', that is created automatically. + getInitatorGroupsOfTarget(target: Pick): ResultAsync { + const self = this; + + return this.findTargetPCSResource(target) + .andThen((resource) => new ResultAsync(safeTry(async function * () { + const partialInitiatorGroup = { + name: "allowed", + devicePath: resource.name, + } + + return ok([{ + ...partialInitiatorGroup, + logicalUnitNumbers: yield * self.getLogicalUnitNumbersOfInitiatorGroup(partialInitiatorGroup).safeUnwrap(), + initiators: yield * self.getInitiatorsOfInitiatorGroup(partialInitiatorGroup).safeUnwrap() + }]) + }))) + } + + getCHAPConfigurationsOfTarget(target: Pick): ResultAsync { + return this.findTargetPCSResource(target) + .andThen((resource) => this.pcsResourceManager.fetchResourceInstanceAttributeValues(resource, ["incoming_username", "incoming_password"])) + .map((value) => { + if (value.get("incoming_username") !== undefined && value.get("incoming_password") !== undefined) { + return [new CHAPConfiguration(value.get("incoming_username")!, value.get("incoming_password")!, CHAPType.IncomingUser)] + } + + return []; + }) + } + + getLogicalUnitNumbersOfInitiatorGroup(initiatorGroup: Pick): ResultAsync { + return this.pcsResourceManager.fetchResourceByName(initiatorGroup.devicePath) + .andThen((targetResource) => this.pcsResourceManager.fetchResources().map((resources) => resources.filter((resource) => resource.resourceType === PCSResourceType.LUN && resource.resourceGroup?.name === targetResource?.resourceGroup?.name))) + .andThen((filteredResources) => { + return ResultAsync.combine(filteredResources.map((resource) => this.pcsResourceManager.fetchResourceInstanceAttributeValues(resource, ["lun", "path"]) + .andThen((values) => { + const lunAttribute = StringToIntCaster()(values.get("lun")!); + const virtualDevice = this.virtualDevices.find((device) => device.filePath === values.get("path")); + + if (virtualDevice !== undefined) + return okAsync(new LogicalUnitNumber(virtualDevice.deviceName, lunAttribute.some(), virtualDevice)); + + return okAsync(undefined); + }) + )) + .map((foundLuns) => foundLuns.filter((lun) => lun !== undefined)) as ResultAsync; + }); + } + + getInitiatorsOfInitiatorGroup(initiatorGroup: Pick): ResultAsync { + return this.pcsResourceManager.fetchResourceByName(initiatorGroup.devicePath) + .andThen((targetResource) => this.pcsResourceManager.fetchResourceInstanceAttributeValue(targetResource!, "allowed_initiators")) + .map((value) => { + if (value !== undefined) { + return value.split(" ").map((initiatorName) => new Initiator(initiatorName)) + } + + return []; + }) + } + + getSessionsOfTarget(target: Pick): ResultAsync { + return this.singleServerDriver!.getSessionsOfTarget(target); + } + + getConnectionsOfSession(session: Session): ResultAsync { + return this.singleServerDriver!.getConnectionsOfSession(session); + } + + findTargetPCSResource(target: Pick) { + const self = this; + + return this.pcsResourceManager.fetchResourceByName(target.devicePath) + .andThen((resource) => { + return resource !== undefined ? okAsync(resource) : errAsync(new ProcessError(`Unable to find resource for Target IQN ${target.name}.`)) + }); + } + + findPortblockPCSResource(target: Target, portal: Portal, type: PCSResourceType.PORTBLOCK_ON | PCSResourceType.PORTBLOCK_OFF) { + const self = this; + + const actionFromType = type === PCSResourceType.PORTBLOCK_ON ? "block" : "unblock"; + + return this.findTargetPCSResource(target) + .andThen((targetResource) => new ResultAsync(safeTry(async function * () { + if (targetResource.resourceGroup != undefined) { + let resources = yield * self.pcsResourceManager.fetchResources().safeUnwrap(); + + for (var groupResource of resources.filter((resource) => resource.resourceGroup?.name === targetResource.resourceGroup?.name)) { + let attributes = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValues({name: groupResource.name}, ["action", "ip"]).safeUnwrap(); + + if (attributes.get("action") === actionFromType && attributes.get("ip") === portal.address.split(":")[0]!) { + const resource = yield * self.pcsResourceManager.fetchResourceByName(groupResource.name).safeUnwrap(); + return ok(resource!); + } + } + } + + return ok(undefined); + }))) + } + + findPortblockVIPResource(target: Target, portal: Portal) { + const self = this; + + return this.findTargetPCSResource(target) + .andThen((targetResource) => new ResultAsync(safeTry(async function * () { + if (targetResource.resourceGroup != undefined) { + let resources = yield * self.pcsResourceManager.fetchResources().safeUnwrap(); + + for (var groupResource of resources.filter((resource) => resource.resourceGroup?.name === targetResource.resourceGroup?.name)) { + const foundResource = yield * self.pcsResourceManager.fetchResourceByName(groupResource.name).safeUnwrap(); + + if (foundResource!.resourceType === PCSResourceType.VIP) { + const vipAddress = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValue(foundResource!, "ip").safeUnwrap(); + + if (vipAddress === portal.address.split(":")[0]!) { + return ok(foundResource!); + } + } + } + } + + return ok(undefined); + }))) + } + + createAndConfigureRBDResource(lun: LogicalUnitNumber, targetIQN: string, group: PCSResourceGroup) { + const blockDevice = (lun.blockDevice! as RadosBlockDevice); + + return this.server.execute(new BashCommand(`rbd unmap ${blockDevice.parentPool.name}/${blockDevice.deviceName}`)) + .andThen(() => this.pcsResourceManager.createResource(`RBD_${blockDevice.deviceName}`, `ocf:ceph:rbd name=${blockDevice.deviceName} pool=${blockDevice.parentPool.name} user=admin cephconf=/etc/ceph/ceph.conf op start timeout=60s interval=0 op stop timeout=60s interval=0 op monitor timeout=30s interval=15s`, PCSResourceType.RBD) + .andThen((resource) => this.pcsResourceManager.constrainResourceToGroup(resource, group) + .andThen(() => this.pcsResourceManager.orderResourceBeforeGroup(resource, group))) + ) + .andThen(() => this.pcsResourceManager.createResource(`${this.resourceNamePrefix}_LUN_${blockDevice.deviceName}`, `ocf:heartbeat:iSCSILogicalUnit target_iqn=${targetIQN} lun=${lun.unitNumber} path=${blockDevice.filePath} op start timeout=100 op stop timeout=100 op monitor interval=10 timeout=100`, PCSResourceType.LUN)) + .andThen((resource) => this.pcsResourceManager.addResourceToGroup(resource, group)) + } + + removeRBDAndRelatedResource(lun: LogicalUnitNumber, targetIQN: string) { + const self = this; + + const blockDevice = lun.blockDevice! as RadosBlockDevice; + + return new ResultAsync(safeTry(async function * () { + yield * self.removeRBDResources([blockDevice]).safeUnwrap(); + yield * self.removeLUNResource(lun, targetIQN).safeUnwrap(); + + return ok(undefined); + })) + } + + createAndConfigureLVResources(lun: LogicalUnitNumber, targetIQN: string, group: PCSResourceGroup) { + const self = this; + + const blockDevice = (lun.blockDevice! as LogicalVolume); + + return this.server.execute(new BashCommand(`lvchange -an ${blockDevice!.volumeGroup.name}/${blockDevice!.deviceName}`)) + .andThen(() => new ResultAsync(safeTry(async function * () { + for (let physicalVolume of blockDevice.volumeGroup.volumes) { + yield * self.server.execute(new BashCommand(`rbd unmap ${physicalVolume.rbd.parentPool.name}/${physicalVolume.rbd.deviceName}`)).safeUnwrap(); + + const resource = yield * self.pcsResourceManager.createResource(`RBD_${physicalVolume.rbd.deviceName}`, `ocf:ceph:rbd name=${physicalVolume.rbd.deviceName} pool=${physicalVolume.rbd.parentPool.name} user=admin cephconf=/etc/ceph/ceph.conf op start timeout=60s interval=0 op stop timeout=60s interval=0 op monitor timeout=30s interval=15s`, PCSResourceType.RBD).safeUnwrap(); + + yield * self.pcsResourceManager.constrainResourceToGroup(resource, group).safeUnwrap(); + yield * self.pcsResourceManager.orderResourceBeforeGroup(resource, group).safeUnwrap(); + } + + return ok(undefined); + }))) + .andThen(() => this.pcsResourceManager.createResource(`${this.resourceNamePrefix}_LVM_${blockDevice.deviceName}_${blockDevice.volumeGroup.name}`, `ocf:heartbeat:LVM-activate lvname=${blockDevice.deviceName} vgname=${blockDevice.volumeGroup.name} activation_mode=exclusive vg_access_mode=system_id op start timeout=30 op stop timeout=30 op monitor interval=10 timeout=60`, PCSResourceType.LVM)) + .andThen((resource) => this.pcsResourceManager.addResourceToGroup(resource, group)) + .andThen(() => this.pcsResourceManager.createResource(`${this.resourceNamePrefix}_LUN_${blockDevice.deviceName}`, `ocf:heartbeat:iSCSILogicalUnit target_iqn=${targetIQN} lun=${lun.unitNumber} path=${blockDevice.filePath} op start timeout=100 op stop timeout=100 op monitor interval=10 timeout=100`, PCSResourceType.LUN)) + .andThen((resource) => this.pcsResourceManager.addResourceToGroup(resource, group)) + .andThen(() => this.server.execute(new BashCommand(`pcs resource cleanup`))) + } + + removeLVAndRelatedResources(lun: LogicalUnitNumber, targetIQN: string) { + const self = this; + + const blockDevice = lun.blockDevice! as LogicalVolume; + + return new ResultAsync(safeTry(async function * () { + let rbdsToRemove = blockDevice.volumeGroup.volumes.map((physicalVolume) => physicalVolume.rbd); + + yield * self.removeLUNResource(lun, targetIQN).safeUnwrap(); + + const lvmResources = yield * self.pcsResourceManager.fetchResources().safeUnwrap(); + + for (var resource of lvmResources.filter((resource) => resource.resourceType === PCSResourceType.LVM)) { + const values = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValues(resource, ["lvname", "vgname"]).safeUnwrap(); + + if (values.get("lvname") === blockDevice.deviceName && values.get("vgname") === blockDevice.volumeGroup.name) { + yield * self.pcsResourceManager.deleteResource(resource).safeUnwrap(); + break; + } + } + + yield * self.removeRBDResources(rbdsToRemove).safeUnwrap(); + + return ok(undefined); + })) + } + + removeRBDResources(rbdsToRemove: RadosBlockDevice[]) { + const self = this; + return new ResultAsync(safeTry(async function * () { + const rbdResources = yield * self.pcsResourceManager.fetchResources().safeUnwrap(); + + for (var resource of rbdResources.filter((resource) => resource.resourceType === PCSResourceType.RBD)) { + const values = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValues(resource, ["name", "pool"]).safeUnwrap(); + + for (var rbdToRemove of rbdsToRemove) { + if (values.get("name") === rbdToRemove.deviceName && values.get("pool") === rbdToRemove.parentPool.name) { + yield * self.pcsResourceManager.disableResource(resource).safeUnwrap(); + break; + } + } + } + + for (var resource of rbdResources.filter((resource) => resource.resourceType === PCSResourceType.RBD)) { + const values = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValues(resource, ["name", "pool"]).safeUnwrap(); + + for (var rbdToRemove of rbdsToRemove) { + if (values.get("name") === rbdToRemove.deviceName && values.get("pool") === rbdToRemove.parentPool.name) { + yield * self.pcsResourceManager.deleteResource(resource).safeUnwrap(); + break; + } + } + } + + return ok(undefined); + })); + } + + removeLUNResource(lun: LogicalUnitNumber, targetIQN: string) { + const self = this; + + return new ResultAsync(safeTry(async function * () { + const lunResources = yield * self.pcsResourceManager.fetchResources().safeUnwrap(); + + for(var resource of lunResources.filter((resource) => resource.resourceType === PCSResourceType.LUN)) { + const values = yield * self.pcsResourceManager.fetchResourceInstanceAttributeValues(resource, ["lun", "target_iqn"]).safeUnwrap(); + + if (values.get("lun") === lun.unitNumber.toString() && values.get("target_iqn") === targetIQN) { + yield * self.pcsResourceManager.deleteResource(resource).safeUnwrap(); + break; + } + } + + return ok(undefined); + })); + } +} diff --git a/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriverSingleServer.ts b/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriverSingleServer.ts new file mode 100644 index 0000000..212481c --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/types/drivers/ISCSIDriverSingleServer.ts @@ -0,0 +1,410 @@ +import { ConfigurationManager } from '@/tabs/iSCSI/types/ConfigurationManager'; +import { Directory, File } from "@45drives/houston-common-lib"; +import { VirtualDevice, DeviceType } from "@/tabs/iSCSI/types/VirtualDevice"; +import { CHAPConfiguration, CHAPType } from "@/tabs/iSCSI/types/CHAPConfiguration"; +import { type Connection } from "@/tabs/iSCSI/types/Connection"; +import { type Initiator } from "@/tabs/iSCSI/types/Initiator"; +import { type InitiatorGroup } from "@/tabs/iSCSI/types/InitiatorGroup"; +import { LogicalUnitNumber } from "@/tabs/iSCSI/types/LogicalUnitNumber"; +import { Portal } from "@/tabs/iSCSI/types/Portal"; +import { type Session } from "@/tabs/iSCSI/types/Session"; +import { type Target } from "@/tabs/iSCSI/types/Target"; +import { ISCSIDriver } from "@/tabs/iSCSI/types/drivers/ISCSIDriver"; +import { BashCommand, Command, ParsingError, ProcessError, Server, StringToIntCaster } from "@45drives/houston-common-lib"; +import { ResultAsync, err, ok, okAsync, safeTry } from "neverthrow"; + +export class ISCSIDriverSingleServer implements ISCSIDriver { + + server: Server; + configurationManager: ConfigurationManager; + + deviceTypeToHandlerDirectory = { + [DeviceType.BlockIO]: "/sys/kernel/scst_tgt/handlers/vdisk_blockio", + [DeviceType.FileIO]: "/sys/kernel/scst_tgt/handlers/vdisk_fileio" + }; + + targetManagementDirectory = "/sys/kernel/scst_tgt/targets/iscsi"; + + constructor(server: Server) { + this.server = server; + this.configurationManager = new ConfigurationManager(server); + } + + initialize(): ResultAsync { + return new Directory(this.server, "/sys/kernel/scst_tgt").exists() + .andThen((exists) => { + return exists ? ok(this) : err(new ProcessError("/sys/kernel/scst_tgt was not found. Is SCST installed?")); + }) + } + + getHandledDeviceTypes(): DeviceType[] { + return Object.keys(this.deviceTypeToHandlerDirectory) as DeviceType[]; + } + + addVirtualDevice(virtualDevice: VirtualDevice): ResultAsync { + return this.server.execute(new BashCommand(`echo "add_device $1 $2" > $3`, [virtualDevice.deviceName, "filename=" + virtualDevice.filePath + ";blocksize=" + virtualDevice.blockSize, this.deviceTypeToHandlerDirectory[virtualDevice.deviceType] + "/mgmt"])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + removeVirtualDevice(virtualDevice: VirtualDevice): ResultAsync { + return this.server.execute(new BashCommand(`echo "del_device $1" > $2`, [virtualDevice.deviceName, this.deviceTypeToHandlerDirectory[virtualDevice.deviceType] + "/mgmt"])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + createTarget(target: Target): ResultAsync { + return this.server.execute(new BashCommand(`echo "add_target $1" > $2`, [target.name, this.targetManagementDirectory + "/mgmt"])) + .andThen(() => this.server.execute(new BashCommand(`echo 1 > $1`, [this.targetManagementDirectory + "/enabled"]))) + .andThen(() => this.server.execute(new BashCommand(`echo 1 > $1`, [`${this.targetManagementDirectory}/${target.name}/enabled`]))) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + removeTarget(target: Target): ResultAsync { + return this.server.execute(new BashCommand(`echo "del_target $1" > $2`, [target.name, this.targetManagementDirectory + "/mgmt"])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + addPortalToTarget(target: Target, portal: Portal): ResultAsync { + return this.server.execute(new BashCommand(`echo "add_target_attribute $1 $2" > $3`, [target.name, `allowed_portal=${portal.address}`, `${this.getTargetPath(target)}/../mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + deletePortalFromTarget(target: Target, portal: Portal): ResultAsync { + return this.server.execute(new BashCommand(`echo "del_target_attribute $1 $2" > $3`, [target.name, `allowed_portal=${portal.address}`, `${this.getTargetPath(target)}/../mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + addInitiatorGroupToTarget(target: Target, initiatorGroup: InitiatorGroup): ResultAsync { + return this.server.execute(new BashCommand(`echo "create $1" > $2`, [initiatorGroup.name, `${this.getTargetPath(target)}/ini_groups/mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + deleteInitiatorGroupFromTarget(target: Target, initiatorGroup: InitiatorGroup): ResultAsync { + return this.server.execute(new BashCommand(`echo "del $1" > $2`, [initiatorGroup.name, `${this.getTargetPath(target)}/ini_groups/mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + addInitiatorToGroup(initiatorGroup: InitiatorGroup, initiator: Initiator): ResultAsync { + return this.server.execute(new BashCommand(`echo "add $1" > $2`, [initiator.name, `${initiatorGroup.devicePath}/initiators/mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + removeInitiatorFromGroup(initiatorGroup: InitiatorGroup, initiator: Initiator): ResultAsync { + return this.server.execute(new BashCommand(`echo "del $1" > $2`, [initiator.name, `${initiatorGroup.devicePath}/initiators/mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + addLogicalUnitNumberToGroup(initiatorGroup: InitiatorGroup, logicalUnitNumber: LogicalUnitNumber): ResultAsync { + return this.server.execute(new BashCommand(`echo "add $1 $2" > $3`, [logicalUnitNumber.name, logicalUnitNumber.unitNumber.toString(), `${initiatorGroup.devicePath}/luns/mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + removeLogicalUnitNumberFromGroup(initiatorGroup: InitiatorGroup, logicalUnitNumber: LogicalUnitNumber): ResultAsync { + return this.server.execute(new BashCommand(`echo "del $1" > $2`, [logicalUnitNumber.unitNumber.toString(), `${initiatorGroup.devicePath}/luns/mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + addCHAPConfigurationToTarget(target: Target, chapConfiguration: CHAPConfiguration): ResultAsync { + return this.server.execute(new BashCommand(`echo "add_target_attribute $1 $2" > $3`, [target.name, `${chapConfiguration.chapType}=${chapConfiguration.username} ${chapConfiguration.password}`, `${this.getTargetPath(target)}/../mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + removeCHAPConfigurationFromTarget(target: Target, chapConfiguration: CHAPConfiguration): ResultAsync { + return this.server.execute(new BashCommand(`echo "del_target_attribute $1 $2" > $3`, [target.name, `${chapConfiguration.chapType}=${chapConfiguration.username}`, `${this.getTargetPath(target)}/../mgmt`])) + .andThen(() => this.configurationManager.saveCurrentConfiguration()) + .map(() => undefined); + } + + getVirtualDevices(): ResultAsync { + const deviceTypesToCheck = [DeviceType.BlockIO, DeviceType.FileIO]; + + const results = deviceTypesToCheck.map((deviceType) => this.getVirtualDevicesOfDeviceType(deviceType)); + + return ResultAsync.combine(results).map((devices) => devices.flat()); + } + + getVirtualDevicesOfDeviceType(deviceType: DeviceType): ResultAsync { + return this.server.execute(new Command(["find", this.deviceTypeToHandlerDirectory[deviceType], ..."-mindepth 1 -maxdepth 1 ( -type d -o -type l ) -printf %f\\0".split(" ")])).map( + (proc) => { + const virtualDeviceNames = proc.getStdout().split("\0").slice(0, -1); + return virtualDeviceNames; + } + ).andThen((virtualDeviceNames) => { + return ResultAsync.combine(virtualDeviceNames.map((virtualDeviceName) => { + const virtualDevicePath = this.deviceTypeToHandlerDirectory[deviceType] + "/" + virtualDeviceName; + + const blockSizeResult = this.server.execute(new Command(["cat", virtualDevicePath + "/blocksize"])).andThen((proc) => { + const blockSizeString = proc.getStdout().trim(); + const maybeBlockSize = StringToIntCaster()(blockSizeString); + + if (maybeBlockSize.isNone()) + return err(new ParsingError(`Failed to parse block size: ${blockSizeString}`)) + + return ok(maybeBlockSize.some()); + }); + + const filePathResult = this.server.execute(new Command(["cat", virtualDevicePath + "/filename"])).andThen((proc) => { + const filePathString = proc.getStdout().split('\n')[0]; + + if (filePathString === undefined) + return err(new ParsingError(`Failed to read file path: ${proc.getStdout()}`)); + + return ok(filePathString); + }); + + return ResultAsync.combine([blockSizeResult, filePathResult]).map(([blockSize, filePath]) => { + return new VirtualDevice(virtualDeviceName, filePath, blockSize, deviceType); + }); + })); + }) + } + + getTargets(): ResultAsync { + const self = this; + + const targetDirectory = "/sys/kernel/scst_tgt/targets/iscsi"; + + return this.server.execute(new Command(["find", targetDirectory, ..."-mindepth 1 -maxdepth 1 ( -type d -o -type l ) -printf %f\\0".split(" ")])).map( + (proc) => { + const targetNames = proc.getStdout().split("\0").slice(0, -1); + return targetNames; + } + ).andThen((targetNames) => { + return ResultAsync.combine(targetNames.map((targetName) => { + return new ResultAsync(safeTry(async function * () { + const partialTarget = { + name: targetName, + devicePath: targetDirectory + "/" + targetName, + }; + + return ok({ + ...partialTarget, + portals: yield * self.getPortalsOfTarget(partialTarget).safeUnwrap(), + chapConfigurations: yield * self.getCHAPConfigurationsOfTarget(partialTarget).safeUnwrap(), + initiatorGroups: yield * self.getInitatorGroupsOfTarget(partialTarget).safeUnwrap(), + sessions: yield * self.getSessionsOfTarget(partialTarget).safeUnwrap() + }); + })) + })) + }); + } + + getPortalsOfTarget(target: Pick): ResultAsync { + return this.server.execute(new Command(["find", this.getTargetPath(target), ..."-name allowed_portal* -printf %f\\0".split(" ")])).map( + (proc) => { + const portalAddressFileNames = proc.getStdout().split("\0").slice(0, -1); + return portalAddressFileNames; + } + ).andThen((portalAddressFileNames) => { + const addressResults = portalAddressFileNames.map((portalAddressFileName) => { + const file = new File(this.server, `${this.getTargetPath(target)}/${portalAddressFileName}`) + return file.read().andThen((fileContent) => { + const address = fileContent.split('\n')[0] + + if (address === undefined) + return err(new ProcessError(`Could not parse address from allowed_portal file: ${file.basename}`)); + + return ok(address); + }); + }) + + return ResultAsync.combine(addressResults).map((addresses) => addresses.map((address) => new Portal(address))); + }) + } + + getInitatorGroupsOfTarget(target: Pick): ResultAsync { + const self = this; + const initiatorGroupDirectory = `${this.getTargetPath(target)}/ini_groups`; + + return this.server.execute(new Command(["find", initiatorGroupDirectory, ..."-mindepth 1 -maxdepth 1 ( -type d -o -type l ) -printf %f\\0".split(" ")])).map( + (proc) => { + const groupNames = proc.getStdout().split("\0").slice(0, -1); + return groupNames; + } + ).andThen((groupNames) => { + return ResultAsync.combine(groupNames.map((groupName) => { + + return new ResultAsync(safeTry(async function * () { + const partialInitiatorGroup = { + name: groupName, + devicePath: `${initiatorGroupDirectory}/${groupName}`, + }; + + return ok({ + ...partialInitiatorGroup, + initiators: yield * self.getInitiatorsOfInitiatorGroup(partialInitiatorGroup).safeUnwrap(), + logicalUnitNumbers: yield * self.getLogicalUnitNumbersOfInitiatorGroup(partialInitiatorGroup).safeUnwrap(), + }); + })) + })) + }); + } + + getSessionsOfTarget(target: Pick): ResultAsync { + const self = this; + + const sessionsDirectory = `${this.getTargetPath(target)}/sessions`; + + return new Directory(this.server, sessionsDirectory).exists() + .andThen((exists) => { + if (exists) + { + return this.server.execute(new Command(["find", sessionsDirectory, ..."-mindepth 1 -maxdepth 1 ( -type d -o -type l ) -printf %f\\0".split(" ")])) + .map((proc) => proc.getStdout().split("\0").slice(0, -1)) + .andThen((initiatorNames) => { + return ResultAsync.combine(initiatorNames.map((initiatorName) => { + return new ResultAsync(safeTry(async function * () { + const partialSession = { + initiatorName: initiatorName, + devicePath: `${sessionsDirectory}/${initiatorName}`, + }; + + return ok({ + ...partialSession, + readAmountKB: StringToIntCaster()((yield * self.server.execute(new Command(["cat", `${partialSession.devicePath}/read_io_count_kb`])).safeUnwrap()).getStdout()).some(), + writeAmountKB: StringToIntCaster()((yield * self.server.execute(new Command(["cat", `${partialSession.devicePath}/write_io_count_kb`])).safeUnwrap()).getStdout()).some(), + connections: yield * self.getConnectionsOfSession(partialSession).safeUnwrap(), + }); + })) + })) + }) + } + else { + return ok([]); + } + }) + } + + getCHAPConfigurationsOfTarget(target: Pick): ResultAsync { + return this.server.execute(new Command(["find", this.getTargetPath(target), ..."-type f ( -name IncomingUser* -o -name OutgoingUser* ) -printf %f\\0".split(" ")])).map( + (proc) => proc.getStdout().split("\0").slice(0, -1) + ).andThen((configurationFileNames) => { + return ResultAsync.combine(configurationFileNames.map((configurationFileName) => { + const file = new File(this.server, `${this.getTargetPath(target)}/${configurationFileName}`); + + return file.read().andThen((fileContent) => { + const credentialLine = fileContent.split('\n')[0] + + if (credentialLine === undefined) + return err(new ProcessError(`Could not parse credentials line from CHAP configuration file: ${file.basename}`)); + + const chapType = configurationFileName.includes("IncomingUser") ? CHAPType.IncomingUser : CHAPType.OutgoingUser; + const username = credentialLine.split(' ')[0]; + const password = credentialLine.split(' ')[1]; + + if (username === undefined || password === undefined) + return err(new ProcessError(`Could not parse credentials from configuration file: ${file.basename}`)); + + return ok({ + username: username, + password: password, + chapType: chapType, + }) + }); + })); + }) + } + + getConnectionsOfSession(session: Pick): ResultAsync { + const self = this; + + const ignoredNames = ["latency", "lun", "."]; + + return this.server.execute(new Command(["find", `${session.devicePath}/`, ..."-type d -mindepth 1 -maxdepth 1 -printf %f\\0".split(" ")])).map( + (proc) => { + const connectionFileNames = proc.getStdout().split("\0").slice(0, -1); + return connectionFileNames.filter(directoryName => + !ignoredNames.some(ignoredName => directoryName.startsWith(ignoredName)) + ); + } + ).andThen((connectionFileNames) => { + return ResultAsync.combine(connectionFileNames.map((connectionFileName) => { + + return new ResultAsync(safeTry(async function * () { + const partialConnection = { + devicePath: `${session.devicePath}/${connectionFileName}` + }; + + const connectionIDFile = new File(self.server, `${partialConnection.devicePath}/cid`); + const ipFile = new File(self.server, `${partialConnection.devicePath}/ip`); + + return ok({ + ...partialConnection, + connectionID: yield * connectionIDFile.read().safeUnwrap(), + ipAddress: yield * ipFile.read().safeUnwrap(), + }); + })) + })) + }); + } + + getLogicalUnitNumbersOfInitiatorGroup(initiatorGroup: Pick): ResultAsync { + const self = this; + + const lunsDirectory = `${initiatorGroup.devicePath}/luns`; + + return this.server.execute(new Command(["find", lunsDirectory, ..."-mindepth 1 -maxdepth 1 ( -type d -o -type l ) -printf %f\\0".split(" ")])).map( + (proc) => { + return proc.getStdout().split("\0").slice(0, -1); + } + ).andThen((numbers) => { + return ResultAsync.combine(numbers.map((number) => { + return new ResultAsync(safeTry(async function * () { + const partialLogicalUnitNumber = { + unitNumber: StringToIntCaster()(number).some(), + }; + + const lunDeviceName = (yield * self.server.execute(new Command(["cat", `${lunsDirectory}/${partialLogicalUnitNumber.unitNumber}/device/prod_id`])).safeUnwrap()).getStdout(); + const device = (yield * self.getVirtualDevices().safeUnwrap()).find((device) => device.deviceName === lunDeviceName); + + return ok({ + ...partialLogicalUnitNumber, + name: lunDeviceName, + blockDevice: device, + }); + })) + })) + }); + } + + getInitiatorsOfInitiatorGroup(initiatorGroup: Pick): ResultAsync { + const ignoredNames = ["mgmt"]; + + const initiatorDirectory = `${initiatorGroup.devicePath}/initiators`; + + return this.server.execute(new Command(["find", initiatorDirectory, ..."-mindepth 1 -maxdepth 1 -printf %f\\0".split(" ")])).map( + (proc) => { + const initiatorNames = proc.getStdout().split("\0").slice(0, -1); + return initiatorNames.filter(name => !ignoredNames.includes(name)); + } + ).andThen((initiatorNames) => { + return ResultAsync.combine(initiatorNames.map((initiatorName) => { + return new ResultAsync(safeTry(async function * () { + const partialInitiator = { + name: initiatorName, + }; + + return ok({ + ...partialInitiator, + }); + })) + })) + }); + } + + getTargetPath(target: Pick) { + return `${this.targetManagementDirectory}/${target.name}` + } +} \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/ISCSITabMain.vue b/file-sharing/src/tabs/iSCSI/ui/ISCSITabMain.vue new file mode 100644 index 0000000..b095f37 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/ISCSITabMain.vue @@ -0,0 +1,89 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationEditor.vue new file mode 100644 index 0000000..f21b0e8 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationEditor.vue @@ -0,0 +1,159 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationEntry.vue new file mode 100644 index 0000000..a79f314 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationEntry.vue @@ -0,0 +1,59 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationTable.vue new file mode 100644 index 0000000..9feba70 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/chapConfigurations/ChapConfigurationTable.vue @@ -0,0 +1,94 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/config/ConfigurationEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/config/ConfigurationEditor.vue new file mode 100644 index 0000000..857a589 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/config/ConfigurationEditor.vue @@ -0,0 +1,70 @@ + + + \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionEntry.vue new file mode 100644 index 0000000..e6c7d10 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionEntry.vue @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionTable.vue new file mode 100644 index 0000000..214c551 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/connections/ConnectionTable.vue @@ -0,0 +1,29 @@ + + + \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEditor.vue new file mode 100644 index 0000000..c70de38 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEditor.vue @@ -0,0 +1,86 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEntry.vue new file mode 100644 index 0000000..c9c5ee3 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupEntry.vue @@ -0,0 +1,66 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupTable.vue new file mode 100644 index 0000000..846e1c6 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/initiatorGroups/InitiatorGroupTable.vue @@ -0,0 +1,90 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEditor.vue new file mode 100644 index 0000000..6d770bd --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEditor.vue @@ -0,0 +1,96 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEntry.vue new file mode 100644 index 0000000..e4cdba9 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorEntry.vue @@ -0,0 +1,50 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorTable.vue new file mode 100644 index 0000000..6552c19 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/initiators/InitiatorTable.vue @@ -0,0 +1,84 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberEditor.vue new file mode 100644 index 0000000..8f8f9de --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberEditor.vue @@ -0,0 +1,130 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberEntry.vue new file mode 100644 index 0000000..6deedb7 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberEntry.vue @@ -0,0 +1,56 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberTable.vue new file mode 100644 index 0000000..3910089 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/logicalUnitNumbers/LogicalUnitNumberTable.vue @@ -0,0 +1,96 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEditor.vue new file mode 100644 index 0000000..36aa70c --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEditor.vue @@ -0,0 +1,118 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEntry.vue new file mode 100644 index 0000000..7016e6d --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalEntry.vue @@ -0,0 +1,46 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalTable.vue new file mode 100644 index 0000000..0ee74d9 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/portal/PortalTable.vue @@ -0,0 +1,84 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDDeviceCreationScreen.vue b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDDeviceCreationScreen.vue new file mode 100644 index 0000000..84b607f --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDDeviceCreationScreen.vue @@ -0,0 +1,213 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDExpansionScreen.vue b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDExpansionScreen.vue new file mode 100644 index 0000000..22c6dbb --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDExpansionScreen.vue @@ -0,0 +1,102 @@ + + + + \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDManagementScreen.vue b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDManagementScreen.vue new file mode 100644 index 0000000..5c2ed7e --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDManagementScreen.vue @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTable.vue new file mode 100644 index 0000000..09475cb --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTable.vue @@ -0,0 +1,131 @@ + + + + \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTableEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTableEntry.vue new file mode 100644 index 0000000..50f00fa --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/radosBlockDeviceManagement/RBDTableEntry.vue @@ -0,0 +1,56 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionEntry.vue new file mode 100644 index 0000000..57e36fa --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionEntry.vue @@ -0,0 +1,45 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionTable.vue new file mode 100644 index 0000000..0e199c8 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/sessions/SessionTable.vue @@ -0,0 +1,68 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetEditor.vue new file mode 100644 index 0000000..4bb7b64 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetEditor.vue @@ -0,0 +1,165 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetEntry.vue new file mode 100644 index 0000000..19a1088 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetEntry.vue @@ -0,0 +1,67 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetTable.vue new file mode 100644 index 0000000..92414f6 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/target/TargetTable.vue @@ -0,0 +1,95 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/FileIOCreationPrompt.vue b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/FileIOCreationPrompt.vue new file mode 100644 index 0000000..86d266b --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/FileIOCreationPrompt.vue @@ -0,0 +1,83 @@ + + + \ No newline at end of file diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceEditor.vue b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceEditor.vue new file mode 100644 index 0000000..be63018 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceEditor.vue @@ -0,0 +1,246 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceEntry.vue b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceEntry.vue new file mode 100644 index 0000000..87cd026 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceEntry.vue @@ -0,0 +1,62 @@ + + + diff --git a/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceTable.vue b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceTable.vue new file mode 100644 index 0000000..1f69ba9 --- /dev/null +++ b/file-sharing/src/tabs/iSCSI/ui/screens/virtualDevice/VirtualDeviceTable.vue @@ -0,0 +1,95 @@ + + + diff --git a/file-sharing/src/tabs/nfs/data-types.ts b/file-sharing/src/tabs/nfs/data-types.ts new file mode 100644 index 0000000..b4d5848 --- /dev/null +++ b/file-sharing/src/tabs/nfs/data-types.ts @@ -0,0 +1,183 @@ +import { Maybe, Some, None } from "monet"; +import { RegexSnippets } from "@45drives/houston-common-lib"; +import { v4 as uuidv4 } from "uuid"; + +export interface INFSClientOption { + value: T; + configValue(overrideNoDefault?: boolean): Maybe; +} + +export interface INFSClientOptionCtor { + new (value?: T): INFSClientOption; + fromString(s: string): Maybe>; +} + +export function NFSBooleanOption( + trueString: TrueString | [TrueString, ...TrueString[]], + falseString: FalseString | [FalseString, ...FalseString[]], + defaultString: TrueString | FalseString, + defaultRequiredInConfig: boolean = false +): INFSClientOptionCtor { + const trueStrings = [trueString].flat() as [TrueString, ...TrueString[]]; + const falseStrings = [falseString].flat() as [FalseString, ...FalseString[]]; + trueString = trueStrings[0] as TrueString; + falseString = falseStrings[0] as FalseString; + const defaultValue = defaultString === trueString; + const opt = class implements INFSClientOption { + public value: boolean; + constructor(value: boolean = defaultValue) { + this.value = value; + } + toString() { + return this.value ? trueString : falseString; + } + configValue(overrideNoDefault: boolean = false): Maybe { + const result = this.toString(); + if (defaultRequiredInConfig || (result !== defaultString && !overrideNoDefault)) { + return Some(result); + } + return None(); + } + static fromString(s: string): Maybe> { + if (trueStrings.includes(s as TrueString)) { + return Some(new opt(true)); + } + if (falseStrings.includes(s as FalseString)) { + return Some(new opt(false)); + } + return None(); + } + }; + return opt; +} + +export function NFSOptionWithArgument( + key: string | [string, ...string[]] +): INFSClientOptionCtor { + const keys = [key].flat() as [string, ...string[]]; + key = keys[0]; + const opt = class implements INFSClientOption { + public value: string | undefined; + constructor(value: string | undefined = undefined) { + this.value = value; + } + configValue(_?: boolean): Maybe { + return Maybe.fromEmpty(this.value).map((value) => `${key}=${value}`); + } + static fromString(s: string): Maybe> { + const [keyToken, valueToken] = s.split(RegexSnippets.keyValueSplitter); + return Maybe.fromEmpty(keyToken) + .filter((k) => keys.includes(k)) + .flatMap(() => Maybe.fromEmpty(valueToken).map((value) => new opt(value))); + } + }; + return opt; +} + +export function NFSOptionWithOptionalArgument( + key: string | [string, ...string[]] +): INFSClientOptionCtor { + const keys = [key].flat() as [string, ...string[]]; + key = keys[0]; + const Parent = NFSOptionWithArgument(key); + const opt = class extends Parent { + configValue(_?: boolean): Maybe { + return super.configValue().orElse(Maybe.fromUndefined(this.value).map(() => key)); + } + static fromString(s: string): Maybe> { + if (s.includes("=")) { + return Parent.fromString(s); + } + if (keys.includes(s)) { + return Some(new opt("")); + } + return None(); + } + }; + return opt; +} + +export const NFSClientOptionsCtors = { + secure: NFSBooleanOption("secure", "insecure", "secure"), + rw: NFSBooleanOption("rw", "ro", "ro"), + async: NFSBooleanOption("async", "sync", "sync", true), + no_wdelay: NFSBooleanOption("no_wdelay", "wdelay", "wdelay"), + nohide: NFSBooleanOption("nohide", "hide", "hide"), + crossmnt: NFSBooleanOption("crossmnt", "", ""), + no_subtree_check: NFSBooleanOption("no_subtree_check", "subtree_check", "no_subtree_check", true), + insecure_locks: NFSBooleanOption( + ["insecure_locks", "no_auth_nlm"], + ["auth_nlm", "secure_locks"], + "auth_nlm" + ), + no_acl: NFSBooleanOption("no_acl", "", ""), + mountpoint: NFSOptionWithOptionalArgument(["mountpoint", "mp"]), + fsid: NFSOptionWithArgument("fsid"), + refer: NFSOptionWithArgument("refer"), + replicas: NFSOptionWithArgument("replicas"), + root_squash: NFSBooleanOption("root_squash", "no_root_squash", "root_squash"), + all_squash: NFSBooleanOption("all_squash", "no_all_squash", "no_all_squash"), + anonuid: NFSOptionWithArgument("anonuid"), + anongid: NFSOptionWithArgument("anongid"), +}; + +// export type NFSClientOptions = { +// [Prop in keyof typeof NFSClientOptionsCtors]: InstanceType<(typeof NFSClientOptionsCtors)[Prop]>; +// }; +export type NFSClientOptions = string; + +export function defaultNFSClientOptions(): NFSClientOptions { + // return Object.fromEntries( + // Object.entries(NFSClientOptionsCtors).map(([key, ctor]) => [key, new ctor()]) + // ) as NFSClientOptions; + return ""; +} + +export function newNFSClientOptions(): NFSClientOptions { + return "rw,sync,no_subtree_check"; +} + +export function defaultNFSDefaultClientOptions(): NFSClientOptions { + return ""; +} + +export function newNFSDefaultClientOptions(): NFSClientOptions { + return `fsid=${uuidv4()}`; +} + +export type NFSExportClient = { + host: string; + settings: NFSClientOptions; +}; + +export type NFSExport = { + path: string; + defaultClientSettings: NFSClientOptions; + clients: NFSExportClient[]; + comment: string; +}; + +export function defaultNFSExport(): NFSExport { + return { + path: "", + defaultClientSettings: defaultNFSDefaultClientOptions(), + clients: [], + comment: "", + }; +} + +export function newNFSExport(): NFSExport { + return { + path: "", + defaultClientSettings: newNFSDefaultClientOptions(), + clients: [newNFSExportClient()], + comment: "", + }; +} + +export function newNFSExportClient(): NFSExportClient { + return { + host: "*", + settings: newNFSClientOptions(), + }; +} diff --git a/file-sharing/src/tabs/nfs/exports-parser.test.ts b/file-sharing/src/tabs/nfs/exports-parser.test.ts new file mode 100644 index 0000000..c65ed04 --- /dev/null +++ b/file-sharing/src/tabs/nfs/exports-parser.test.ts @@ -0,0 +1,459 @@ +import { suite, test, expect } from "vitest"; +import { ok, err } from "neverthrow"; +import { + NFSClientOptionsParser, + NFSClientsParser, + NFSExportParser, + NFSExportsParser, +} from "./exports-parser"; +import { + NFSBooleanOption, + NFSOptionWithArgument, + NFSOptionWithOptionalArgument, + defaultNFSClientOptions, +} from "@/tabs/nfs/data-types"; + +function unwrapValues>( + obj: TObj +): { + [Prop in keyof TObj]: TObj[Prop]["value"]; +} { + return Object.fromEntries(Object.entries(obj).map(([key, { value }]) => [key, value])) as { + [Prop in keyof TObj]: TObj[Prop]["value"]; + }; +} + +suite("NFS Exports Parsing", () => { + suite("NFSClientOption", () => { + test("NFSBooleanOption", () => { + const DefaultsNo = NFSBooleanOption("yes", "no", "no"); + expect(new DefaultsNo().value).toEqual(false); + expect(new DefaultsNo(false).value).toEqual(false); + expect(new DefaultsNo(true).value).toEqual(true); + expect(new DefaultsNo().configValue().orUndefined()).toBeUndefined(); + expect(new DefaultsNo(false).configValue().orUndefined()).toBeUndefined(); + expect(new DefaultsNo(true).configValue().orUndefined()).toEqual("yes"); + expect( + DefaultsNo.fromString("no") + .map((o) => o.value) + .orUndefined() + ).toEqual(false); + expect( + DefaultsNo.fromString("yes") + .map((o) => o.value) + .orUndefined() + ).toEqual(true); + expect( + DefaultsNo.fromString("abc123") + .map((o) => o.value) + .orUndefined() + ).toBeUndefined(); + const DefaultsYes = NFSBooleanOption("yes", "no", "yes"); + expect(new DefaultsYes().value).toEqual(true); + expect(new DefaultsYes(false).value).toEqual(false); + expect(new DefaultsYes(true).value).toEqual(true); + expect(new DefaultsYes().configValue().orUndefined()).toBeUndefined(); + expect(new DefaultsYes(true).configValue().orUndefined()).toBeUndefined(); + expect(new DefaultsYes(false).configValue().orUndefined()).toEqual("no"); + expect( + DefaultsYes.fromString("no") + .map((o) => o.value) + .orUndefined() + ).toEqual(false); + expect( + DefaultsYes.fromString("yes") + .map((o) => o.value) + .orUndefined() + ).toEqual(true); + expect( + DefaultsYes.fromString("abc123") + .map((o) => o.value) + .orUndefined() + ).toBeUndefined(); + const DefaultsYesRequired = NFSBooleanOption("yes", "no", "yes", true); + expect(new DefaultsYesRequired().value).toEqual(true); + expect(new DefaultsYesRequired(false).value).toEqual(false); + expect(new DefaultsYesRequired(true).value).toEqual(true); + expect(new DefaultsYesRequired().configValue().orUndefined()).toEqual("yes"); + expect(new DefaultsYesRequired(true).configValue().orUndefined()).toEqual("yes"); + expect(new DefaultsYesRequired(false).configValue().orUndefined()).toEqual("no"); + expect( + DefaultsYesRequired.fromString("no") + .map((o) => o.value) + .orUndefined() + ).toEqual(false); + expect( + DefaultsYesRequired.fromString("yes") + .map((o) => o.value) + .orUndefined() + ).toEqual(true); + expect( + DefaultsYesRequired.fromString("abc123") + .map((o) => o.value) + .orUndefined() + ).toBeUndefined(); + }); + test("NFSOptionWithArgument", () => { + const FSID = NFSOptionWithArgument("fsid"); + expect(new FSID().value).toBeUndefined(); + expect(new FSID("root").value).toEqual("root"); + expect(new FSID().configValue().orUndefined()).toBeUndefined(); + expect(new FSID("root").configValue().orUndefined()).toEqual("fsid=root"); + expect(new FSID("").configValue().orUndefined()).toBeUndefined(); + expect(FSID.fromString("").isNone()).toBe(true); + expect(FSID.fromString("lkjsdf").isNone()).toBe(true); + expect(FSID.fromString("lkjsdf=").isNone()).toBe(true); + expect(FSID.fromString("lkjsdf=kljhdfg").isNone()).toBe(true); + expect(FSID.fromString("fsid").isNone()).toBe(true); + expect(FSID.fromString("fsid=").isNone()).toBe(true); + expect(FSID.fromString("fsid=root").some().value).toEqual("root"); + }); + test("NFSOptionWithOptionalArgument", () => { + const Mountpoint = NFSOptionWithOptionalArgument("mountpoint"); + expect(new Mountpoint().value).toBeUndefined(); + expect(new Mountpoint("/tmp").value).toEqual("/tmp"); + expect(new Mountpoint().configValue().orUndefined()).toBeUndefined(); + expect(new Mountpoint("/tmp").configValue().orUndefined()).toEqual("mountpoint=/tmp"); + expect(new Mountpoint("").configValue().orUndefined()).toEqual("mountpoint"); + expect(Mountpoint.fromString("").isNone()).toBe(true); + expect(Mountpoint.fromString("lkjsdf").isNone()).toBe(true); + expect(Mountpoint.fromString("lkjsdf=").isNone()).toBe(true); + expect(Mountpoint.fromString("lkjsdf=kljhdfg").isNone()).toBe(true); + expect(Mountpoint.fromString("mountpoint").some().value).toBe(""); + expect(Mountpoint.fromString("mountpoint=").isNone()).toBe(true); + expect(Mountpoint.fromString("mountpoint=/tmp").some().value).toEqual("/tmp"); + }); + }); + // suite("NFSClientOptionsParser", () => { + // const parser = new NFSClientOptionsParser(); + // test('default values (parse "")', () => { + // const defaultParsed = parser.apply(""); + // expect(defaultParsed.isOk()).toBe(true); + // defaultParsed.map(unwrapValues).map((defaultParsed) => { + // expect(defaultParsed).toEqual({ + // secure: true, + // rw: false, + // async: false, + // no_wdelay: false, + // nohide: false, + // crossmnt: false, + // no_subtree_check: true, + // insecure_locks: false, + // no_acl: false, + // mountpoint: undefined, + // fsid: undefined, + // refer: undefined, + // replicas: undefined, + // root_squash: true, + // all_squash: false, + // anonuid: undefined, + // anongid: undefined, + // }); + // }); + // const unparsed = defaultParsed.andThen((options) => parser.unapply(options)); + // expect(unparsed.isOk()).toBe(true); + // unparsed.map((unparsed) => { + // expect(unparsed).toEqual("sync,no_subtree_check"); + // }); + // }); + // }); + // test("NFSClientsParser", () => { + // const parser = new NFSClientsParser(); + // const optionsParser = new NFSClientOptionsParser(); + // const input = `master(rw) \ttrusty(rw,no_root_squash) proj*.local.domain(rw) *.local.domain(ro) @trusted(rw) pc001(rw,all_squash,anonuid=150,anongid=100) *(ro,insecure,all_squash) server @trusted @external(ro) 2001:db8:9:e54::/64(rw) 192.0.2.0/24(rw) buildhost[0-9].local.domain(rw)`; + // const parsed = parser.apply(input); + // expect(parsed.isOk()).toBe(true); + // parsed.map((parsed): void => { + // expect( + // parsed.map(({ host, settings }) => ({ host, settings: unwrapValues(settings) })) + // ).toEqual( + // [ + // { host: "master", settings: "rw" }, + // { host: "trusty", settings: "rw,no_root_squash" }, + // { host: "proj*.local.domain", settings: "rw" }, + // { host: "*.local.domain", settings: "ro" }, + // { host: "@trusted", settings: "rw" }, + // { host: "pc001", settings: "rw,all_squash,anonuid=150,anongid=100" }, + // { host: "*", settings: "ro,insecure,all_squash" }, + // { host: "server", settings: "" }, + // { host: "@trusted", settings: "" }, + // { host: "@external", settings: "ro" }, + // { host: "2001:db8:9:e54::/64", settings: "rw" }, + // { host: "192.0.2.0/24", settings: "rw" }, + // { host: "buildhost[0-9].local.domain", settings: "rw" }, + // ].map(({ host, settings }) => ({ + // host, + // settings: unwrapValues(optionsParser.apply(settings).unwrapOr({})), + // })) + // ); + // }); + // const unparsed = parsed.andThen((clients) => parser.unapply(clients)); + // expect(unparsed.isOk()).toBe(true); + // unparsed.map((unparsed) => { + // expect(unparsed).toEqual( + // "master(rw,sync,no_subtree_check) trusty(rw,sync,no_subtree_check,no_root_squash) proj*.local.domain(rw,sync,no_subtree_check) *.local.domain(sync,no_subtree_check) @trusted(rw,sync,no_subtree_check) pc001(rw,sync,no_subtree_check,all_squash,anonuid=150,anongid=100) *(insecure,sync,no_subtree_check,all_squash) server(sync,no_subtree_check) @trusted(sync,no_subtree_check) @external(sync,no_subtree_check) 2001:db8:9:e54::/64(rw,sync,no_subtree_check) 192.0.2.0/24(rw,sync,no_subtree_check) buildhost[0-9].local.domain(rw,sync,no_subtree_check)" + // ); + // }); + // }); + test("NFSExportParser", () => { + const parser = new NFSExportParser(); + const parse = (text: string) => + parser + .apply(text) + .map(({ path, defaultClientSettings, clients, comment }) => ({ + path, + defaultClientSettings, //: unwrapValues(defaultClientSettings), + clients: clients.map(({ host, settings }) => ({ + host, + settings, //: unwrapValues(settings), + })), + comment, + })) + .unwrapOr(undefined); + expect(parse("/ master(rw) trusty(rw,no_root_squash)")).toEqual({ + path: "/", + defaultClientSettings: "", // unwrapValues(defaultNFSClientOptions()), + clients: [ + { + host: "master", + settings: "rw", + // { ...unwrapValues(defaultNFSClientOptions()), rw: true } + }, + { + host: "trusty", + settings: "rw,no_root_squash", + // { ...unwrapValues(defaultNFSClientOptions()), rw: true, root_squash: false }, + }, + ], + comment: "", + }); + expect(parse("/projects proj*.local.domain(rw) # test comment")).toEqual({ + path: "/projects", + defaultClientSettings: "", // unwrapValues(defaultNFSClientOptions()), + clients: [ + { + host: "proj*.local.domain", + settings: "rw", + // { ...unwrapValues(defaultNFSClientOptions()), rw: true }, + }, + ], + comment: "test comment", + }); + expect(parse("/home/joe pc001(rw,all_squash,anonuid=150,anongid=100)")).toEqual({ + path: "/home/joe", + defaultClientSettings: "", // unwrapValues(defaultNFSClientOptions()), + clients: [ + { + host: "pc001", + settings: "rw,all_squash,anonuid=150,anongid=100", + // { + // ...unwrapValues(defaultNFSClientOptions()), + // rw: true, + // all_squash: true, + // anonuid: "150", + // anongid: "100", + // }, + }, + ], + comment: "", + }); + expect(parse("/srv/www -sync,rw server @trusted @external(ro)")).toEqual({ + path: "/srv/www", + defaultClientSettings: "sync,rw", + // { + // ...unwrapValues(defaultNFSClientOptions()), + // async: false, + // rw: true, + // }, + clients: [ + { + host: "server", + settings: "", + // { + // ...unwrapValues(defaultNFSClientOptions()), + // }, + }, + { + host: "@trusted", + settings: "", + // { + // ...unwrapValues(defaultNFSClientOptions()), + // }, + }, + { + host: "@external", + settings: "ro", + // { + // ...unwrapValues(defaultNFSClientOptions()), + // rw: false, + // }, + }, + ], + comment: "", + }); + }); + test("NFSExportsParser", () => { + const parser = new NFSExportsParser(); + const input = `# sample /etc/exports file +/ master(rw) trusty(rw,no_root_squash) +/projects proj*.local.domain(rw) +/usr *.local.domain(ro) @trusted(rw) +/home/joe pc001(rw,all_squash,anonuid=150,anongid=100) +/pub *(ro,insecure,all_squash) # comment test 1 +/srv/www -sync,rw server @trusted @external(ro) +/foo \t 2001:db8:9:e54::/64(rw) 192.0.2.0/24(rw) +/build buildhost[0-9].local.domain(rw) +"/home/John Doe/share" jdoe.local(rw) # lkjsdf +`; + const parsed = parser.apply(input); + parsed.mapErr((e) => { + throw e; + }); + expect(parsed.isOk()).toBe(true); + parsed.map((parsed) => { + expect(parsed).toEqual([ + // / master(rw) trusty(rw,no_root_squash) + { + path: "/", + defaultClientSettings: "", + comment: "", + clients: [ + { + host: "master", + settings: "rw", + }, + { + host: "trusty", + settings: "rw,no_root_squash", + }, + ], + }, + // /projects proj*.local.domain(rw) + { + path: "/projects", + defaultClientSettings: "", + comment: "", + clients: [ + { + host: "proj*.local.domain", + settings: "rw", + }, + ], + }, + // /usr *.local.domain(ro) @trusted(rw) + { + path: "/usr", + defaultClientSettings: "", + comment: "", + clients: [ + { + host: "*.local.domain", + settings: "ro", + }, + { + host: "@trusted", + settings: "rw", + }, + ], + }, + // /home/joe pc001(rw,all_squash,anonuid=150,anongid=100) + { + path: "/home/joe", + defaultClientSettings: "", + comment: "", + clients: [ + { + host: "pc001", + settings: "rw,all_squash,anonuid=150,anongid=100", + }, + ], + }, + // /pub *(ro,insecure,all_squash) # comment test 1 + { + path: "/pub", + defaultClientSettings: "", + comment: "comment test 1", + clients: [ + { + host: "*", + settings: "ro,insecure,all_squash", + }, + ], + }, + // /srv/www -sync,rw server @trusted @external(ro) + { + path: "/srv/www", + defaultClientSettings: "sync,rw", + comment: "", + clients: [ + { + host: "server", + settings: "", + }, + { + host: "@trusted", + settings: "", + }, + { + host: "@external", + settings: "ro", + }, + ], + }, + // /foo 2001:db8:9:e54::/64(rw) 192.0.2.0/24(rw) + { + path: "/foo", + defaultClientSettings: "", + comment: "", + clients: [ + { + host: "2001:db8:9:e54::/64", + settings: "rw", + }, + { + host: "192.0.2.0/24", + settings: "rw", + }, + ], + }, + // /build buildhost[0-9].local.domain(rw) + { + path: "/build", + defaultClientSettings: "", + comment: "", + clients: [ + { + host: "buildhost[0-9].local.domain", + settings: "rw", + }, + ], + }, + // "/home/John Doe/share" jdoe.local(rw) # lkjsdf + { + path: "/home/John Doe/share", + defaultClientSettings: "", + comment: "lkjsdf", + clients: [ + { + host: "jdoe.local", + settings: "rw", + }, + ], + }, + ]); + }); + const unparsed = parsed.andThen((parsed) => parser.unapply(parsed)); + expect(unparsed.isOk()).toBe(true); + unparsed.map((unparsed) => { + expect(unparsed).toEqual(`/ master(rw) trusty(rw,no_root_squash) +/projects proj*.local.domain(rw) +/usr *.local.domain(ro) @trusted(rw) +/home/joe pc001(rw,all_squash,anonuid=150,anongid=100) +/pub *(ro,insecure,all_squash) # comment test 1 +/srv/www -sync,rw server @trusted @external(ro) +/foo 2001:db8:9:e54::/64(rw) 192.0.2.0/24(rw) +/build buildhost[0-9].local.domain(rw) +"/home/John Doe/share" jdoe.local(rw) # lkjsdf`); + }); + }); +}); diff --git a/file-sharing/src/tabs/nfs/exports-parser.ts b/file-sharing/src/tabs/nfs/exports-parser.ts new file mode 100644 index 0000000..9f3b2ed --- /dev/null +++ b/file-sharing/src/tabs/nfs/exports-parser.ts @@ -0,0 +1,270 @@ +import { ParsingError, RegexSnippets, type SyntaxParser } from "@45drives/houston-common-lib"; +import type { + NFSExport, + NFSExportClient, + NFSClientOptions, + INFSClientOptionCtor, + INFSClientOption, +} from "@/tabs/nfs/data-types"; +import { + defaultNFSExport, + defaultNFSClientOptions, + defaultNFSDefaultClientOptions, + NFSClientOptionsCtors, +} from "@/tabs/nfs/data-types"; +import { Result, ok, err, safeTry } from "neverthrow"; +import { Maybe, Some, None } from "monet"; + +// export class NFSClientOptionsParser implements SyntaxParser { +// private static ctors = Object.entries(NFSClientOptionsCtors); +// private hideAllDefaults: boolean; + +// constructor(hideAllDefaults: boolean = false) { +// this.hideAllDefaults = hideAllDefaults; +// } + +// apply(optionsString: string) { +// return optionsString.split(",").reduce>( +// (options, token) => +// options.andThen((options) => { +// for (const [key, Option] of NFSClientOptionsParser.ctors) { +// const nextOptions = Option.fromString(token).map((option) => ({ +// ...options, +// [key]: option, +// })); +// if (nextOptions.isSome()) { +// return ok(nextOptions.some()); +// } +// } +// return err(new ParsingError(`NFSClientOptionsParser: No match for token: ${token}`)); +// }), +// ok(defaultNFSClientOptions()) +// ); +// } +// unapply(options: NFSClientOptions) { +// const hideAllDefaults = this.hideAllDefaults; +// return ok( +// Object.values(options) +// .map((option) => option.configValue(hideAllDefaults).orNull()) +// .filter((o): o is string => o !== null) +// .join(",") +// ); +// } +// } + +export class NFSClientOptionsParser implements SyntaxParser { + constructor(_?: boolean) {} + + apply(unparsed: string): Result { + return ok(unparsed); + } + + unapply(parsed: NFSClientOptions): Result { + return ok(parsed); + } +} + +export class NFSClientsParser implements SyntaxParser { + private clientOptionsParser: SyntaxParser; + constructor() { + this.clientOptionsParser = new NFSClientOptionsParser(); + } + + apply(clientString: string) { + const clientMatches = [...clientString.matchAll(/([^ \t\(]+)(?:\(([^\)]+)\))?/g)]; + return Result.combine( + clientMatches.map(([fullMatch, host, settings]) => { + if (host === undefined) { + return err(new ParsingError(`Failed to parse host from ${fullMatch}`)); + } + return this.clientOptionsParser.apply(settings ?? "").map((settings) => ({ + host, + settings, + })); + }) + ); + } + + unapply(clients: NFSExportClient[]) { + return Result.combine( + clients.map((client) => + this.clientOptionsParser.unapply(client.settings).map((settings) => + Maybe.fromEmpty(settings) + .map((settings) => `${client.host}(${settings})`) + .orSome(client.host) + ) + ) + ).map((clients) => clients.join(" ")); + } +} + +export class NFSExportParser implements SyntaxParser { + private defaultClientOptionsParser: SyntaxParser; + private clientsParser: SyntaxParser; + constructor() { + this.defaultClientOptionsParser = new NFSClientOptionsParser(true); + this.clientsParser = new NFSClientsParser(); + } + + apply(unparsed: string): Result { + return this.grabRawPath(unparsed) + .andThen(([rawPath, optionsClientsAndComment]) => + this.unescapeRawPath(rawPath).map((path) => { + return { path, optionsClientsAndComment }; + }) + ) + .andThen(({ path, optionsClientsAndComment }) => + this.grabDefaultOptions(optionsClientsAndComment).map( + ([maybeDefaultOptions, clientsAndComment]) => { + return { + path, + defaultClientSettings: maybeDefaultOptions.orSome(defaultNFSDefaultClientOptions()), + clientsAndComment, + }; + } + ) + ) + .map(({ path, defaultClientSettings, clientsAndComment }) => { + const [comment, clients] = this.grabComment(clientsAndComment); + return { + path, + defaultClientSettings, + clients, + comment, + }; + }) + .andThen(({ path, defaultClientSettings, clients, comment }) => + this.clientsParser.apply(clients).map((clients) => ({ + path, + defaultClientSettings, + clients, + comment, + })) + ); + } + + unapply(parsed: NFSExport): Result { + return this.defaultClientOptionsParser + .unapply(parsed.defaultClientSettings) + .andThen((defaultClientSettings) => + this.clientsParser + .unapply(parsed.clients) + .map((clients) => ({ defaultClientSettings, clients })) + ) + .map(({ defaultClientSettings, clients }) => { + return [ + /\s/.test(parsed.path) ? `"${parsed.path}"` : parsed.path, + defaultClientSettings ? `-${defaultClientSettings}` : null, + clients, + parsed.comment ? `# ${parsed.comment}` : null, + ] + .filter((t): t is string => t !== null) + .join(" "); + }); + } + + private grabComment(clientsAndComment: string): [comment: string, clients: string] { + const [clients, comment] = clientsAndComment.split(/#(.*)/) as [string, ...string[]]; + return [(comment ?? "").trim(), clients]; + } + + private grabRawPath( + pathOptionsAndClients: string + ): Result<[rawPath: string, optionsAndClients: string], ParsingError> { + if (pathOptionsAndClients.startsWith('"')) { + // quoted path + const closingQuoteIndex = pathOptionsAndClients.indexOf('"', 1); + if (closingQuoteIndex === -1) { + return err(new ParsingError(`Failed to find closing quote: ${pathOptionsAndClients}`)); + } + return ok([ + pathOptionsAndClients.slice(1, closingQuoteIndex), + pathOptionsAndClients.slice(closingQuoteIndex + 1).trim(), + ]); + } + const [path, rest] = pathOptionsAndClients.split(/[ \t](.*)/); + if (path === undefined || rest === undefined) { + return err( + new ParsingError(`Failed to split path from options and clients: ${pathOptionsAndClients}`) + ); + } + return ok([path, rest.trim()]); + } + + private unescapeRawPath(rawPath: string): Result { + try { + return ok( + rawPath.replace(/\\\d{3}/, (escapedChar) => { + const charCode = parseInt(escapedChar.slice(1), 8); + if (isNaN(charCode)) { + throw escapedChar; + } + return String.fromCharCode(charCode); + }) + ); + } catch (e) { + return err(new ParsingError(`Failed to parse escaped path character in ${rawPath}: ${e}`)); + } + } + + private grabDefaultOptions( + optionsAndClients: string + ): Result<[defaultOptions: Maybe, clients: string], ParsingError> { + if (!optionsAndClients.startsWith("-")) { + return ok([None(), optionsAndClients]); + } + const [defaultClientOptions, clients] = optionsAndClients.split(/[ \t](.*)/); + if (defaultClientOptions === undefined || clients === undefined) { + return err( + new ParsingError(`Failed to split default options from clients list: ${optionsAndClients}`) + ); + } + return this.defaultClientOptionsParser + .apply(defaultClientOptions.slice(1)) + .map((defaultOptions) => [Some(defaultOptions), clients]); + } +} + +export class NFSExportsParser implements SyntaxParser { + private nfsExportParser: SyntaxParser; + + constructor() { + this.nfsExportParser = new NFSExportParser(); + } + + apply(raw: string) { + return ok(raw) + .map((raw) => this.splitRecords(raw)) + .andThen((records) => this.parseRecords(records)); + } + + unapply(exports: NFSExport[]) { + return Result.combine(exports.map((nfsExport) => this.nfsExportParser.unapply(nfsExport))).map( + (exports) => exports.join("\n") + ); + } + + private splitRecords(raw: string): string[] { + return ( + raw + // split by newline + .split(RegexSnippets.newlineSplitter) + // join escaped newlines + .reduce((lines, line) => { + const previousLine = lines[lines.length - 1]; + if (previousLine !== undefined && previousLine.endsWith("\\")) { + return [...lines.slice(0, -1), previousLine.slice(0, -1).concat(" ", line)]; + } + return [...lines, line]; + }, []) + // trim whitespace + .map((line) => line.trim()) + // reject empty lines and comment lines + .filter((line) => line && !line.startsWith("#")) + ); + } + + private parseRecords(records: string[]): Result { + return Result.combine(records.map((record) => this.nfsExportParser.apply(record))); + } +} diff --git a/file-sharing/src/tabs/nfs/nfs-manager.ts b/file-sharing/src/tabs/nfs/nfs-manager.ts new file mode 100644 index 0000000..9b1acdf --- /dev/null +++ b/file-sharing/src/tabs/nfs/nfs-manager.ts @@ -0,0 +1,151 @@ +import { type NFSExport } from "@/tabs/nfs/data-types"; +import { + ParsingError, + ProcessError, + Server, + File, + type CommandOptions, + Command, +} from "@45drives/houston-common-lib"; +import { ResultAsync } from "neverthrow"; +import { NFSExportsParser } from "@/tabs/nfs/exports-parser"; +import { Hooks, executeHookCallbacks } from "@/common/hooks"; + +export interface INFSManager { + getExports(): ResultAsync; + addExport(nfsExport: NFSExport): ResultAsync; + editExport(nfsExport: NFSExport): ResultAsync; + removeExport(nfsExport: NFSExport): ResultAsync; + exportConfig(): ResultAsync; + importConfig(config: string): ResultAsync; +} + +export class NFSManagerSingleServer implements INFSManager { + private exportsFile: File; + private commandOptions: CommandOptions = { superuser: "try" }; + private nfsExportsParser = new NFSExportsParser(); + constructor( + private server: Server, + exportsFilePath: string + ) { + this.exportsFile = new File(this.server, exportsFilePath); + } + + private ensureExportsFile(): ResultAsync { + return this.exportsFile + .assertExists(true) + .orElse(() => this.exportsFile.create(true, this.commandOptions)) + .andThen((file) => file.assertIsFile()); + } + + private setExports(exports: NFSExport[]): ResultAsync { + return this.nfsExportsParser + .unapply(exports) + .asyncAndThen((newExportsContent) => + this.ensureExportsFile().andThen((exportsFile) => + exportsFile.replace(newExportsContent, this.commandOptions) + ) + ) + .andThen(() => this.server.execute(new Command(["exportfs", "-ra"], this.commandOptions))) + .map(() => this); + } + + getExports(): ResultAsync { + return this.ensureExportsFile() + .andThen((exportsFile) => exportsFile.read(this.commandOptions)) + .andThen((exportsFileContents) => this.nfsExportsParser.apply(exportsFileContents)); + } + + addExport(nfsExport: NFSExport): ResultAsync { + return executeHookCallbacks(Hooks.BeforeAddShare, this.server, nfsExport) + .andThen(() => this.getExports()) + .map((exports) => [...exports, nfsExport]) + .andThen((exports) => this.setExports(exports)) + .andThen(() => executeHookCallbacks(Hooks.AfterAddShare, this.server, nfsExport)) + .map(() => nfsExport); + } + + editExport(nfsExport: NFSExport): ResultAsync { + return executeHookCallbacks(Hooks.BeforeEditShare, this.server, nfsExport) + .andThen(() => this.getExports()) + .map((exports) => exports.map((e) => (e.path === nfsExport.path ? nfsExport : e))) + .andThen((exports) => this.setExports(exports)) + .andThen(() => executeHookCallbacks(Hooks.AfterEditShare, this.server, nfsExport)) + .map(() => nfsExport); + } + + removeExport(nfsExport: NFSExport): ResultAsync { + return executeHookCallbacks(Hooks.BeforeRemoveShare, this.server, nfsExport) + .andThen(() => this.getExports()) + .map((exports) => exports.filter((e) => e.path !== nfsExport.path)) + .andThen((exports) => this.setExports(exports)) + .andThen(() => executeHookCallbacks(Hooks.AfterRemoveShare, this.server, nfsExport)) + .map(() => nfsExport); + } + + exportConfig(): ResultAsync { + return this.ensureExportsFile().andThen((exportsFile) => exportsFile.read(this.commandOptions)); + } + + importConfig(config: string): ResultAsync { + return this.ensureExportsFile() + .andThen((exportsFile) => + exportsFile.replace(config, { ...this.commandOptions, backup: true }) + ) + .map(() => this); + } +} + +export class NFSManagerClustered implements INFSManager { + private managers: [NFSManagerSingleServer, ...NFSManagerSingleServer[]]; + private getterManager: NFSManagerSingleServer; + + constructor(servers: [Server, ...Server[]], exportsFilePath: string) { + this.managers = servers.map( + (server) => new NFSManagerSingleServer(server, exportsFilePath) + ) as [NFSManagerSingleServer, ...NFSManagerSingleServer[]]; + this.getterManager = this.managers[0]; + } + + getExports(...args: Parameters) { + return this.getterManager.getExports(...args); + } + + addExport(nfsExport: NFSExport): ResultAsync { + return ResultAsync.combine(this.managers.map((m) => m.addExport(nfsExport))).map( + () => nfsExport + ); + } + + editExport(nfsExport: NFSExport): ResultAsync { + return ResultAsync.combine(this.managers.map((m) => m.editExport(nfsExport))).map( + () => nfsExport + ); + } + + removeExport(nfsExport: NFSExport): ResultAsync { + return ResultAsync.combine(this.managers.map((m) => m.removeExport(nfsExport))).map( + () => nfsExport + ); + } + + exportConfig(): ResultAsync { + return this.getterManager.exportConfig(); + } + + importConfig(config: string): ResultAsync { + return ResultAsync.combine(this.managers.map((m) => m.importConfig(config))).map(() => this); + } +} + +export function getNFSManager( + servers: Server | [Server, ...Server[]], + exportsFilePath: string +): INFSManager { + if (Array.isArray(servers)) { + return servers.length === 1 + ? new NFSManagerSingleServer(servers[0], exportsFilePath) + : new NFSManagerClustered(servers, exportsFilePath); + } + return new NFSManagerSingleServer(servers, exportsFilePath); +} diff --git a/file-sharing/src/tabs/nfs/ui/NFSExportEditor.vue b/file-sharing/src/tabs/nfs/ui/NFSExportEditor.vue new file mode 100644 index 0000000..35b1cb4 --- /dev/null +++ b/file-sharing/src/tabs/nfs/ui/NFSExportEditor.vue @@ -0,0 +1,324 @@ + + + diff --git a/file-sharing/src/tabs/nfs/ui/NFSExportListView.vue b/file-sharing/src/tabs/nfs/ui/NFSExportListView.vue new file mode 100644 index 0000000..43729ff --- /dev/null +++ b/file-sharing/src/tabs/nfs/ui/NFSExportListView.vue @@ -0,0 +1,114 @@ + + + diff --git a/file-sharing/src/tabs/nfs/ui/NFSTabMain.vue b/file-sharing/src/tabs/nfs/ui/NFSTabMain.vue new file mode 100644 index 0000000..926e8e7 --- /dev/null +++ b/file-sharing/src/tabs/nfs/ui/NFSTabMain.vue @@ -0,0 +1,128 @@ + + + diff --git a/file-sharing/src/tabs/nfs/ui/index.ts b/file-sharing/src/tabs/nfs/ui/index.ts new file mode 100644 index 0000000..09698fb --- /dev/null +++ b/file-sharing/src/tabs/nfs/ui/index.ts @@ -0,0 +1 @@ +export { default as NFSTabMain } from "./NFSTabMain.vue"; diff --git a/file-sharing/src/tabs/samba/data-types.ts b/file-sharing/src/tabs/samba/data-types.ts new file mode 100644 index 0000000..a281e37 --- /dev/null +++ b/file-sharing/src/tabs/samba/data-types.ts @@ -0,0 +1,47 @@ +import { type KeyValueData } from "@45drives/houston-common-lib"; + +export type SambaGlobalConfig = { + logLevel: number; + workgroup: string; + serverString: string; + advancedOptions: KeyValueData; +}; + +export type SambaShareConfig = { + name: string; + description: string; + path: string; + guestOk: boolean; + readOnly: boolean; + browseable: boolean; + inheritPermissions: boolean; + advancedOptions: KeyValueData; +}; + +export type SambaConfig = { + global: SambaGlobalConfig; + shares: SambaShareConfig[]; +}; + +export const defaultSambaShareConfig = (name: string = ""): SambaShareConfig => ({ + name, + description: "", + path: "", + guestOk: false, + browseable: true, + readOnly: true, + inheritPermissions: false, + advancedOptions: {}, +}); + +export const newSambaShareConfig = (): SambaShareConfig => ({ + ...defaultSambaShareConfig(""), + readOnly: false, +}) + +export const defaultSambaGlobalConfig = () => ({ + serverString: "Samba %v", + logLevel: 0, + workgroup: "WORKGROUP", + advancedOptions: {}, +}); diff --git a/file-sharing/src/tabs/samba/samba-manager.ts b/file-sharing/src/tabs/samba/samba-manager.ts new file mode 100644 index 0000000..2f64421 --- /dev/null +++ b/file-sharing/src/tabs/samba/samba-manager.ts @@ -0,0 +1,273 @@ +import { + Server, + Command, + type CommandOptions, + ProcessError, + ParsingError, + type KeyValueData, + IniSyntax, + keyValueDiff, + File, + RegexSnippets, + StringUtils, +} from "@45drives/houston-common-lib"; +import { type SambaGlobalConfig, type SambaShareConfig } from "@/tabs/samba/data-types"; +import { SmbConfParser, SmbGlobalParser, SmbShareParser } from "@/tabs/samba/smb-conf-parser"; +import { Result, ok, err, ResultAsync, okAsync, errAsync } from "neverthrow"; +import { executeHookCallbacks, Hooks } from "@/common/hooks"; + +export interface ISambaManager { + getGlobalConfig(): ResultAsync; + + editGlobal(globalConfig: SambaGlobalConfig): ResultAsync; + + listShareNames(): ResultAsync; + + getShareProperty(shareName: string, property: string): ResultAsync; + + getShare(shareName: string): ResultAsync; + + getShares(): ResultAsync; + + addShare(share: SambaShareConfig): ResultAsync; + + editShare(share: SambaShareConfig): ResultAsync; + + removeShare(share: SambaShareConfig): ResultAsync; + + exportConfig(): ResultAsync; + + importConfig(config: string): ResultAsync; + + checkIfSmbConfIncludesRegistry(smbConfPath: string): ResultAsync; + + patchSmbConfIncludeRegistry(smbConfPath: string): ResultAsync; + + importFromSmbConf(smbConfPath: string): ResultAsync; +} + +export class SambaManager implements ISambaManager { + private commandOptions: CommandOptions; + + constructor(private server: Server) { + this.commandOptions = { + superuser: "try", + }; + } + + private netConfCommand(...args: string[]) { + return new Command(["net", "conf", ...args], this.commandOptions); + } + + private listShareNamesCommand() { + return this.netConfCommand("listshares"); + } + + private addShareCommand(name: string, path: string) { + return this.netConfCommand("addshare", name, path); + } + + private getParmCommand(section: string, param: string) { + return this.netConfCommand("getparm", section, param); + } + + private setParmCommand(section: string, param: string, value: string) { + return this.netConfCommand("setparm", section, param, value); + } + + private delParmCommand(section: string, param: string) { + return this.netConfCommand("delparm", section, param); + } + + private showShareCommand(section: string) { + return this.netConfCommand("showshare", section); + } + + private showShareParse(commandOutput: string): Result { + return IniSyntax() + .apply(commandOutput) + .andThen((shareIniData) => { + const objKeys = Object.keys(shareIniData); + const [shareName] = objKeys; + if (shareName === undefined) { + return err( + new ParsingError(`net conf showshare returned invalid data:\n${commandOutput}`) + ); + } + return SmbShareParser(shareName).apply(shareIniData[shareName]!); + }); + } + + private delShareCommand(section: string) { + return this.netConfCommand("delshare", section); + } + + private setSectionParams(section: string, params: KeyValueData) { + return ResultAsync.combine( + Object.entries(params).map(([key, value]) => + this.server.execute(this.setParmCommand(section, key, value), true) + ) + ); + } + + private delSectionParms(section: string, params: string[]) { + return ResultAsync.combine( + params.map((param) => this.server.execute(this.delParmCommand(section, param), true)) + ); + } + + getGlobalConfig() { + return this.server + .execute(this.showShareCommand("global")) + .map((p) => p.getStdout()) + .andThen(SmbConfParser().apply) + .map(({ global }) => global); + } + + editGlobal(globalConfig: SambaGlobalConfig) { + const globalParser = SmbGlobalParser(); + return this.getGlobalConfig() + .andThen(globalParser.unapply) + .andThen((originalGlobalKV) => + globalParser + .unapply(globalConfig) + .map((globalKV) => keyValueDiff(originalGlobalKV, globalKV)) + ) + .andThen(({ added, removed, changed }) => + this.setSectionParams("global", { ...added, ...changed }).andThen(() => + this.delSectionParms("global", Object.keys(removed)) + ) + ) + .map(() => this); + } + + listShareNames() { + return this.server + .execute(this.listShareNamesCommand()) + .map((p) => p.getStdout()) + .map(StringUtils.splitBy(RegexSnippets.newlineSplitter)) + .map( + StringUtils.filter( + StringUtils.nonEmptyFilter(), + (shareName) => shareName.toLowerCase() !== "global" + ) + ); + } + + getShareProperty(shareName: string, property: string) { + return this.server + .execute(this.getParmCommand(shareName, property)) + .map((p) => p.getStdout().replace(/[\r\n]+$/, "")); + } + + getShare(shareName: string) { + return this.server + .execute(this.showShareCommand(shareName)) + .map((p) => p.getStdout()) + .andThen(this.showShareParse); + } + + getShares() { + return this.listShareNames().andThen((shareNames) => + ResultAsync.combine(shareNames.map((shareName) => this.getShare(shareName))) + ); + } + + addShare(share: SambaShareConfig) { + return executeHookCallbacks(Hooks.BeforeAddShare, this.server, share) + .andThen(() => SmbShareParser(share.name).unapply(share)) + .andThen((shareParams) => + this.server + .execute(this.addShareCommand(share.name, share.path), true) + .andThen(() => this.setSectionParams(share.name, shareParams)) + ) + .andThen(() => executeHookCallbacks(Hooks.AfterAddShare, this.server, share)) + .map(() => this); + } + + editShare(share: SambaShareConfig) { + const shareParser = SmbShareParser(""); + return executeHookCallbacks(Hooks.BeforeEditShare, this.server, share) + .andThen(() => this.getShare(share.name)) + .andThen(shareParser.unapply) + .andThen((originalShareKV) => + shareParser.unapply(share).map((shareKV) => keyValueDiff(originalShareKV, shareKV)) + ) + .andThen(({ added, removed, changed }) => + this.setSectionParams(share.name, { ...added, ...changed }).andThen(() => + this.delSectionParms(share.name, Object.keys(removed)) + ) + ) + .andThen(() => executeHookCallbacks(Hooks.AfterEditShare, this.server, share)) + .map(() => this); + } + + removeShare(share: SambaShareConfig) { + const shareName = typeof share === "string" ? share : share.name; + return executeHookCallbacks(Hooks.BeforeRemoveShare, this.server, share) + .andThen(() => this.server.execute(this.delShareCommand(shareName))) + .andThen(() => executeHookCallbacks(Hooks.AfterRemoveShare, this.server, share)) + .map(() => this); + } + + exportConfig() { + return this.server.execute(this.netConfCommand("list")).map((proc) => proc.getStdout()); + } + + importConfig(config: string) { + return File.makeTemp(this.server) + .andThen((confFile) => + confFile.write( + // remove include = registry or config backend = registry + config.replace(/^[ \t]*(include|config backend)[ \t]*=[ \t]*registry.*$\n?/im, "") + ) + ) + .andThen((confFile) => this.server.execute(this.netConfCommand("import", confFile.path))) + .map(() => this); + } + + checkIfSmbConfIncludesRegistry(smbConfPath: string) { + return new File(this.server, smbConfPath) + .assertExists() + .andThen((smbConf) => smbConf.read()) + .andThen(IniSyntax({ duplicateKey: "ignore" }).apply) + .map((smbConf) => smbConf.global?.include === "registry"); + } + + patchSmbConfIncludeRegistry(smbConfPath: string) { + return new File(this.server, smbConfPath) + .assertExists() + .andThen((smbConf) => + smbConf.replace( + (currentConfig) => + currentConfig.replace( + // last line of [global] section + /^\s*\[ ?global ?\]\s*$(?:\n^(?!;?\s*\[).*$)*/im, + "$&\n\t# inclusion of net registry, inserted by cockpit-file-sharing:\n\tinclude = registry\n" + ), + this.commandOptions + ) + ) + .map(() => this); + } + + importFromSmbConf(smbConfPath: string) { + return new File(this.server, smbConfPath) + .assertExists() + .andThen((smbConfFile) => + smbConfFile + .read(this.commandOptions) + .andThen((smbConf) => this.importConfig(smbConf)) + .andThen(() => + smbConfFile.replace( + "# this config was generated by cockpit-file-sharing after importing smb.conf\n" + + `# original smb.conf location: ${smbConfPath}.~N~\n` + + "[global]\n" + + " include = registry\n", + { ...this.commandOptions, backup: true } + ) + ) + ) + .map(() => this); + } +} diff --git a/file-sharing/src/tabs/samba/smb-conf-parser.test.ts b/file-sharing/src/tabs/samba/smb-conf-parser.test.ts new file mode 100644 index 0000000..34b9059 --- /dev/null +++ b/file-sharing/src/tabs/samba/smb-conf-parser.test.ts @@ -0,0 +1,182 @@ +import { SmbGlobalParser, SmbShareParser, SmbConfParser } from "@/tabs/samba/smb-conf-parser"; +import { type KeyValueData } from "@45drives/houston-common-lib"; +import type { SambaConfig, SambaGlobalConfig, SambaShareConfig } from "@/tabs/samba/data-types"; +import { ok } from "neverthrow"; +import { suite, test, expect } from "vitest"; + +suite("Samba", () => { + suite("Config Parser", () => { + const globalParser = SmbGlobalParser(); + suite("SmbGlobalParser", () => { + test("default values", () => { + expect(globalParser.apply({})).toEqual( + ok({ + logLevel: 0, + serverString: "Samba %v", + workgroup: "WORKGROUP", + advancedOptions: {}, + } as SambaGlobalConfig) + ); + }); + test("parsed values", () => { + const unparsed = { + "log level": "3", + "server string": "Hello World", + workgroup: "goofy goobers", + "vfs objects": "acl xattr ceph", + "os level": "25", + }; + const parsed = globalParser.apply(unparsed); + expect(parsed).toEqual( + ok({ + logLevel: 3, + serverString: "Hello World", + workgroup: "goofy goobers", + advancedOptions: { + "vfs objects": "acl xattr ceph", + "os level": "25", + }, + } as SambaGlobalConfig) + ); + expect(parsed.andThen(globalParser.unapply)).toEqual(ok(unparsed)); + }); + }); + suite("SmbShareParser", () => { + const parser = SmbShareParser("share name"); + test("default values", () => { + expect(parser.apply({})).toEqual( + ok({ + name: "share name", + description: "", + path: "", + readOnly: true, + guestOk: false, + browseable: true, + inheritPermissions: false, + advancedOptions: {}, + } as SambaShareConfig) + ); + }); + test("parsed values", () => { + const unparsed: KeyValueData = { + comment: "Description", + path: "/tmp/testshare", + "read only": "no", + "guest ok": "yes", + browseable: "no", + "inherit permissions": "yes", + "vfs objects": "acl xattr ceph", + "os level": "25", + }; + const parsed = parser.apply(unparsed); + expect(parsed).toEqual( + ok({ + name: "share name", + description: "Description", + path: "/tmp/testshare", + readOnly: false, + guestOk: true, + browseable: false, + inheritPermissions: true, + advancedOptions: { + "vfs objects": "acl xattr ceph", + "os level": "25", + }, + } as SambaShareConfig) + ); + expect(parsed.andThen(parser.unapply)).toEqual(ok(unparsed)); + }); + }); + test("SmbConfParser", () => { + const parser = SmbConfParser(); + const input = [ + { + unparsed: `[global] +log level = 4 +workgroup = WG +server string = Test server config +some adv option = lksdjflkjgdfdfjlkfdflkj +`, + expected: { + global: { + logLevel: 4, + serverString: "Test server config", + workgroup: "WG", + advancedOptions: { + "some adv option": "lksdjflkjgdfdfjlkfdflkj", + }, + }, + shares: [], + } as SambaConfig, + }, + { + unparsed: `[global] + log level = 4 + workgroup = WG + server string = Test server config + some adv option = lksdjflkjgdfdfjlkfdflkj + +[share1] + comment = hello, world! + path = /lksdf/sdfkjrt/dflk + guest ok = 1 + read only = false + browsable = no + vfs objects = ceph xattr + some adv opt = some value + +[share2] +comment = hello, world! +path = /lksdf/sdfkjrt/dflk +guest ok = 1 +writable = false +browsable = no +inherit permissions = yes +vfs objects = lkjsdlf jfk kejsdf kje +some adv opt = some other value`, + expected: { + global: { + logLevel: 4, + serverString: "Test server config", + workgroup: "WG", + advancedOptions: { + "some adv option": "lksdjflkjgdfdfjlkfdflkj", + }, + }, + shares: [ + { + name: "share1", + description: "hello, world!", + path: "/lksdf/sdfkjrt/dflk", + guestOk: true, + readOnly: false, + browseable: false, + inheritPermissions: false, + advancedOptions: { + "vfs objects": "ceph xattr", + "some adv opt": "some value", + }, + } as SambaShareConfig, + { + name: "share2", + description: "hello, world!", + path: "/lksdf/sdfkjrt/dflk", + guestOk: true, + readOnly: true, + browseable: false, + inheritPermissions: true, + advancedOptions: { + "vfs objects": "lkjsdlf jfk kejsdf kje", + "some adv opt": "some other value", + }, + } as SambaShareConfig, + ], + } as SambaConfig, + }, + ]; + for (const { unparsed, expected } of input) { + expect(parser.apply(unparsed)).toEqual(ok(expected)); + } + }); + }); +}); diff --git a/file-sharing/src/tabs/samba/smb-conf-parser.ts b/file-sharing/src/tabs/samba/smb-conf-parser.ts new file mode 100644 index 0000000..21a344d --- /dev/null +++ b/file-sharing/src/tabs/samba/smb-conf-parser.ts @@ -0,0 +1,153 @@ +import { + type SyntaxParser, + IniSyntax, + type KeyValueData, + KVGrabber, + KVGrabberCollection, + StringToIntCaster, + IdentityCaster, + KVRemainderGrabber, + type Transformer, + IntToStringCaster, + StringToBooleanCaster, + BooleanToStringCaster, + ParsingError, +} from "@45drives/houston-common-lib"; +import { + defaultSambaGlobalConfig, + defaultSambaShareConfig, + type SambaConfig, + type SambaGlobalConfig, + type SambaShareConfig, +} from "./data-types"; +import { Result, ok, err } from "neverthrow"; + +const sambaStringToBooleanCaster = StringToBooleanCaster({ + truthyWords: ["yes", "1", "true"], + falsyWords: ["no", "0", "false"], + ignoreCase: true, +}); +const sambaBooleanToStringCaster = BooleanToStringCaster("yes", "no"); + +export function SmbGlobalParser(): Transformer { + return { + apply: (unparsed) => { + const config = defaultSambaGlobalConfig(); + Object.entries(unparsed).forEach( + KVGrabberCollection([ + KVGrabber(config, "logLevel", ["log level", "debuglevel"], StringToIntCaster()), + KVGrabber(config, "serverString", ["server string"], IdentityCaster()), + KVGrabber(config, "workgroup", ["workgroup"], IdentityCaster()), + KVRemainderGrabber(config, "advancedOptions"), + ]) + ); + return ok(config); + }, + unapply: (parsed) => { + const config: KeyValueData = {}; + Object.entries(parsed).forEach( + KVGrabberCollection([ + KVGrabber(config, "log level", ["logLevel"], IntToStringCaster()), + KVGrabber(config, "server string", ["serverString"], IdentityCaster()), + KVGrabber(config, "workgroup", ["workgroup"], IdentityCaster()), + ]) + ); + return ok({ ...config, ...parsed.advancedOptions }); + }, + }; +} + +export function SmbShareParser(name: string): Transformer { + return { + apply: (unparsed: KeyValueData) => { + const config = defaultSambaShareConfig(name); + const grabbers = KVGrabberCollection([ + KVGrabber(config, "description", ["comment"], IdentityCaster()), + KVGrabber(config, "path", ["path", "directory"], IdentityCaster()), + KVGrabber(config, "guestOk", ["guest ok"], sambaStringToBooleanCaster), + KVGrabber(config, "readOnly", ["read only"], sambaStringToBooleanCaster), + KVGrabber(config, "readOnly", ["write ok", "writeable", "writable"], (s) => + sambaStringToBooleanCaster(s).map((b) => !b) + ), + KVGrabber(config, "browseable", ["browseable", "browsable"], sambaStringToBooleanCaster), + KVGrabber( + config, + "inheritPermissions", + ["inherit permissions"], + sambaStringToBooleanCaster + ), + KVRemainderGrabber(config, "advancedOptions"), + ]); + if ( + !Object.entries(unparsed) + .map(grabbers) + .every((grabbed) => grabbed) + ) { + return err(new ParsingError("KeyValue pair of Samba share INI data not grabbed!")); + } + return ok(config); + }, + unapply: (parsed: SambaShareConfig) => { + const config: KeyValueData = {}; + const grabbers = KVGrabberCollection([ + KVGrabber(config, "comment", ["description"], IdentityCaster()), + KVGrabber(config, "path", ["path"], IdentityCaster()), + KVGrabber(config, "guest ok", ["guestOk"], sambaBooleanToStringCaster), + KVGrabber(config, "read only", ["readOnly"], sambaBooleanToStringCaster), + KVGrabber(config, "browseable", ["browseable"], sambaBooleanToStringCaster), + KVGrabber( + config, + "inherit permissions", + ["inheritPermissions"], + sambaBooleanToStringCaster + ), + ]); + if ( + !Object.entries(parsed) + .filter(([key, _]) => !["advancedOptions", "name"].includes(key)) + .map(grabbers) + .every((grabbed) => grabbed) + ) { + return err(new ParsingError("KeyValue pair of SambaShareConfig not grabbed!")); + } + return ok({ ...config, ...parsed.advancedOptions }); + }, + }; +} + +export function SmbConfParser(): SyntaxParser { + const iniSyntax = IniSyntax({ paramIndent: "\t" }); + const globalParser = SmbGlobalParser(); + return { + apply: (text) => { + return iniSyntax.apply(text).andThen(({ global: globalIni, ...sharesIni }) => { + return globalParser + .apply(globalIni ?? {}) + .andThen((global) => + Result.combine( + Object.entries(sharesIni).map(([name, shareIni]) => + SmbShareParser(name).apply(shareIni) + ) + ).map((shares) => ({ global, shares })) + ); + }); + }, + unapply: (config: SambaConfig) => { + return globalParser + .unapply(config.global) + .andThen((globalIni) => { + return Result.combine( + config.shares.map((share) => + SmbShareParser(share.name) + .unapply(share) + .map((shareIni): [string, KeyValueData] => [share.name, shareIni]) + ) + ).map((shareEntriesIni) => ({ + global: globalIni, + ...Object.fromEntries(shareEntriesIni), + })); + }) + .andThen(iniSyntax.unapply); + }, + }; +} diff --git a/file-sharing/src/tabs/samba/ui/BooleanKeyValueSuite.ts b/file-sharing/src/tabs/samba/ui/BooleanKeyValueSuite.ts new file mode 100644 index 0000000..2ffc402 --- /dev/null +++ b/file-sharing/src/tabs/samba/ui/BooleanKeyValueSuite.ts @@ -0,0 +1,63 @@ +import { computed } from "vue"; + +export const BooleanKeyValueSuite = ( + objGetter: () => Record, + trueContents: { include: Record; exclude: Record } +) => { + return computed({ + get() { + for (const [key, unwantedValuesString] of Object.entries(trueContents.exclude)) { + const unwantedValues = unwantedValuesString.split(/\s+/); + const currentValues = objGetter()[key]?.split(/\s+/); + if ( + currentValues !== undefined && + unwantedValues.some((unwantedValue) => currentValues.includes(unwantedValue)) + ) { + return false; + } + } + for (const [key, wantedValuesString] of Object.entries(trueContents.include)) { + const wantedValues = wantedValuesString.split(/\s+/); + const currentValues = objGetter()[key]?.split(/\s+/); + if (currentValues === undefined) { + return false; + } + if (!wantedValues.every((wantedValue) => currentValues.includes(wantedValue))) { + return false; + } + } + return true; + }, + set(value) { + if (value) { + // remove excluded + for (const [key, unwantedValuesString] of Object.entries(trueContents.exclude)) { + const unwantedValues = unwantedValuesString.split(/\s+/); + const currentValues = objGetter()[key]?.split(/\s+/) ?? []; + const filteredValues = currentValues.filter((v) => !unwantedValues.includes(v)); + if (filteredValues.length) { + objGetter()[key] = filteredValues.join(" "); + } else { + delete objGetter()[key]; + } + } + } + for (const [key, wantedValuesString] of Object.entries(trueContents.include)) { + const wantedValues = wantedValuesString.split(/\s+/); + const currentValues = objGetter()[key]?.split(/\s+/) ?? []; + if (value) { + // add included + objGetter()[key] = [...new Set([...currentValues, ...wantedValues])].join(" "); + } else { + // remove included + const filteredValues = currentValues.filter((v) => !wantedValues.includes(v)); + if (filteredValues.length) { + objGetter()[key] = filteredValues.join(" "); + } else { + delete objGetter()[key]; + } + } + } + }, + }); +}; diff --git a/file-sharing/src/tabs/samba/ui/GlobalConfigEditor.vue b/file-sharing/src/tabs/samba/ui/GlobalConfigEditor.vue new file mode 100644 index 0000000..d0674db --- /dev/null +++ b/file-sharing/src/tabs/samba/ui/GlobalConfigEditor.vue @@ -0,0 +1,132 @@ + + + diff --git a/file-sharing/src/tabs/samba/ui/SambaTabMain.vue b/file-sharing/src/tabs/samba/ui/SambaTabMain.vue new file mode 100644 index 0000000..1cd9417 --- /dev/null +++ b/file-sharing/src/tabs/samba/ui/SambaTabMain.vue @@ -0,0 +1,230 @@ + + + diff --git a/file-sharing/src/tabs/samba/ui/ShareEditor.vue b/file-sharing/src/tabs/samba/ui/ShareEditor.vue new file mode 100644 index 0000000..d229265 --- /dev/null +++ b/file-sharing/src/tabs/samba/ui/ShareEditor.vue @@ -0,0 +1,247 @@ + + + diff --git a/file-sharing/src/tabs/samba/ui/ShareListView.vue b/file-sharing/src/tabs/samba/ui/ShareListView.vue new file mode 100644 index 0000000..c284e31 --- /dev/null +++ b/file-sharing/src/tabs/samba/ui/ShareListView.vue @@ -0,0 +1,118 @@ + + + diff --git a/file-sharing/src/tabs/samba/ui/index.ts b/file-sharing/src/tabs/samba/ui/index.ts new file mode 100644 index 0000000..d53e11f --- /dev/null +++ b/file-sharing/src/tabs/samba/ui/index.ts @@ -0,0 +1 @@ +export { default as SambaTabMain } from "./SambaTabMain.vue"; diff --git a/file-sharing/tailwind.config.cjs b/file-sharing/tailwind.config.cjs new file mode 100644 index 0000000..22784d0 --- /dev/null +++ b/file-sharing/tailwind.config.cjs @@ -0,0 +1,23 @@ +module.exports = { + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + theme: { + extend: { + fontFamily: { + redhat: ['Red Hat Text', 'open-sans', 'sans-serif'], + "source-sans-pro": ["Source Sans Pro", 'open-sans', 'sans-serif'], + }, + colors: { + neutral: { + 850: "#222222", + } + } + }, + }, + plugins: [ + require('@tailwindcss/forms'), + ], + darkMode: "class", +} diff --git a/file-sharing/tsconfig.app.json b/file-sharing/tsconfig.app.json new file mode 100644 index 0000000..1639790 --- /dev/null +++ b/file-sharing/tsconfig.app.json @@ -0,0 +1,36 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + // "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "skipLibCheck": true, + "alwaysStrict": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noFallthroughCasesInSwitch": true, + "experimentalDecorators": true, + "downlevelIteration": true, + "pretty": true, + "allowJs": false, + "checkJs": false, + "noUncheckedIndexedAccess": true, + } +} diff --git a/file-sharing/tsconfig.json b/file-sharing/tsconfig.json new file mode 100644 index 0000000..100cf6a --- /dev/null +++ b/file-sharing/tsconfig.json @@ -0,0 +1,14 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.vitest.json" + } + ] +} diff --git a/file-sharing/tsconfig.node.json b/file-sharing/tsconfig.node.json new file mode 100644 index 0000000..f094063 --- /dev/null +++ b/file-sharing/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*" + ], + "compilerOptions": { + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/file-sharing/tsconfig.vitest.json b/file-sharing/tsconfig.vitest.json new file mode 100644 index 0000000..571995d --- /dev/null +++ b/file-sharing/tsconfig.vitest.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.app.json", + "exclude": [], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo", + + "lib": [], + "types": ["node", "jsdom"] + } +} diff --git a/file-sharing/vite.config.ts b/file-sharing/vite.config.ts new file mode 100644 index 0000000..d97a935 --- /dev/null +++ b/file-sharing/vite.config.ts @@ -0,0 +1,31 @@ +import { fileURLToPath, URL } from "node:url"; + +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; +import vueJsx from "@vitejs/plugin-vue-jsx"; +import VueDevTools from "vite-plugin-vue-devtools"; + +import manifest from "../manifest.json"; + +const getAppVersionDefine = () => { + return `${manifest.version}-${manifest.buildVersion}${process.env.OS_PACKAGE_RELEASE ?? "built_from_source"}`; +}; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue(), vueJsx(), VueDevTools()], + base: "./", + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + dedupe: ["vue"], + }, + build: { + target: ["chrome87", "edge88", "firefox78", "safari14"], + sourcemap: true, + }, + define: { + __APP_VERSION__: JSON.stringify(getAppVersionDefine()), + }, +}); diff --git a/file-sharing/vitest.config.ts b/file-sharing/vitest.config.ts new file mode 100644 index 0000000..4b1c897 --- /dev/null +++ b/file-sharing/vitest.config.ts @@ -0,0 +1,14 @@ +import { fileURLToPath } from 'node:url' +import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' +import viteConfig from './vite.config' + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + environment: 'jsdom', + exclude: [...configDefaults.exclude, 'e2e/**'], + root: fileURLToPath(new URL('./', import.meta.url)) + } + }) +) diff --git a/houston-common b/houston-common new file mode 160000 index 0000000..9bfc292 --- /dev/null +++ b/houston-common @@ -0,0 +1 @@ +Subproject commit 9bfc292ba981b9185a543206012cd29dd73b5bdc diff --git a/manifest.json b/manifest.json index 346b416..af82781 100644 --- a/manifest.json +++ b/manifest.json @@ -1,9 +1,9 @@ { - "__version": "45D-R1", + "__version": "45D-R2", "name": "cockpit-file-sharing", "title": "Cockpit File Sharing", "prerelease": false, - "version": "3.3.7", + "version": "4.2.5", "buildVersion": "1", "author": "Josh Boudreau ", "url": "https://github.com/45Drives/cockpit-file-sharing", @@ -22,29 +22,34 @@ "urgency": "medium" }, "dependencies": { - "deb": [ - "cockpit", - "attr", - "coreutils", - "libc-bin", - "nfs-kernel-server", - "samba", - "samba-common-bin", - "systemd", - "winbind", - "gawk" - ], - "el": [ - "cockpit", - "attr", - "coreutils", - "glibc-common", - "nfs-utils", - "samba-common-tools", - "samba-winbind-clients", - "systemd", - "gawk" - ] + "deb": { + "focal": [ + "cockpit-bridge", + "coreutils", + "attr", + "findutils", + "hostname", + "iproute2", + "libc-bin", + "systemd", + "nfs-kernel-server", + "samba-common-bin" + ] + }, + "el": { + "el8": [ + "cockpit-bridge", + "coreutils", + "attr", + "findutils", + "hostname", + "iproute", + "glibc-common", + "systemd", + "nfs-utils", + "samba-common-tools" + ] + } }, "releases": [ { @@ -60,7 +65,7 @@ ], "changelog": { "urgency": "medium", - "version": "3.3.7", + "version": "4.2.5", "buildVersion": "1", "ignore": [], "date": null, diff --git a/package.json b/package.json new file mode 100644 index 0000000..76b8c4c --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "workspaces": [ + "file-sharing", + "houston-common", + "houston-common/houston-common-*" + ], + "scripts": { + "build": "yarn build:common && yarn build:app", + "build:common": "yarn build:css && yarn build:lib && yarn build:ui" + }, + "packageManager": "yarn@4.3.0" +} diff --git a/packaging/el8/main.spec b/packaging/el8/main.spec index da9d628..667aa4a 100644 --- a/packaging/el8/main.spec +++ b/packaging/el8/main.spec @@ -6,7 +6,7 @@ License: ::package_licence:: URL: ::package_url:: Source0: %{name}-%{version}.tar.gz BuildArch: ::package_architecture_el:: -Requires: ::package_dependencies_el:: +Requires: ::package_dependencies_el_el8:: BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root @@ -27,6 +27,31 @@ make DESTDIR=%{buildroot} install /usr/share/cockpit/file-sharing/* %changelog +* Mon Oct 28 2024 Brandon Kelly 4.2.5-1 +- iSCSI Release +* Mon Sep 23 2024 Brandon Kelly 4.2.4-1 +- Removed standalone RBD creation to use LVs instead +* Thu Sep 19 2024 Brandon Kelly 4.2.3-1 +- Added more validations +* Wed Sep 18 2024 Brandon Kelly 4.2.2-1 +- Refactored iSCSI Clustered integration +* Thu Jul 25 2024 Brandon Kelly 4.2.1-1 +- Fixes to iSCSI Tab +* Thu Jul 25 2024 Brandon Kelly 4.2.0-1 +- Fixes to iSCSI Tab +* Mon Jun 24 2024 Joshua Boudreau 4.1.0-1 +- Add iSCSI tab +* Fri Jun 14 2024 Joshua Boudreau 4.0.3-1 +- New Samba shares now default to read only = no +* Tue Jun 11 2024 Joshua Boudreau 4.0.2-1 +- Fixed error spamming when path does not exist for Ceph path settings +* Tue Jun 11 2024 Joshua Boudreau 4.0.1-1 +- Assign UUID FSID option for new NFS exports +- Add input validation for NFS exports +* Mon Jun 10 2024 Joshua Boudreau 4.0.0-2 +- fix table corners +* Mon Jun 10 2024 Joshua Boudreau 4.0.0-1 +- Complete rewrite for better maintainability and expansion * Tue Apr 23 2024 Joshua Boudreau 3.3.7-1 - Samba - fix saving global advanced settings with '=' in their value - NFS - better whitespace handling while parsing exports file diff --git a/packaging/focal/changelog b/packaging/focal/changelog index a12da89..0517e30 100644 --- a/packaging/focal/changelog +++ b/packaging/focal/changelog @@ -1,3 +1,76 @@ +cockpit-file-sharing (4.2.5-1focal) focal; urgency=medium + + * iSCSI Release + + -- Brandon Kelly Mon, 28 Oct 2024 12:18:32 -0300 + +cockpit-file-sharing (4.2.4-1focal) focal; urgency=medium + + * Removed standalone RBD creation to use LVs instead + + -- Brandon Kelly Mon, 23 Sep 2024 08:08:50 -0300 + +cockpit-file-sharing (4.2.3-1focal) focal; urgency=medium + + * Added more validations + + -- Brandon Kelly Thu, 19 Sep 2024 13:26:43 -0300 + +cockpit-file-sharing (4.2.2-1focal) focal; urgency=medium + + * Refactored iSCSI Clustered integration + + -- Brandon Kelly Wed, 18 Sep 2024 08:26:21 -0300 + +cockpit-file-sharing (4.2.1-1focal) focal; urgency=medium + + * Fixes to iSCSI Tab + + -- Brandon Kelly Thu, 25 Jul 2024 08:13:54 -0300 + +cockpit-file-sharing (4.2.0-1focal) focal; urgency=medium + + * Fixes to iSCSI Tab + + -- Brandon Kelly Thu, 25 Jul 2024 08:07:50 -0300 + +cockpit-file-sharing (4.1.0-1focal) focal; urgency=medium + + * Add iSCSI tab + + -- Joshua Boudreau Mon, 24 Jun 2024 08:36:58 -0300 + +cockpit-file-sharing (4.0.3-1focal) focal; urgency=medium + + * New Samba shares now default to read only = no + + -- Joshua Boudreau Fri, 14 Jun 2024 11:42:30 -0300 + +cockpit-file-sharing (4.0.2-1focal) focal; urgency=medium + + * Fixed error spamming when path does not exist for Ceph path settings + + -- Joshua Boudreau Tue, 11 Jun 2024 09:38:41 -0300 + +cockpit-file-sharing (4.0.1-1focal) focal; urgency=medium + + * Assign UUID FSID option for new NFS exports + * Add input validation for NFS exports + + -- Joshua Boudreau Tue, 11 Jun 2024 09:04:56 -0300 + +cockpit-file-sharing (4.0.0-2focal) focal; urgency=medium + + * fix table corners + + -- Joshua Boudreau Mon, 10 Jun 2024 16:09:01 -0300 + +cockpit-file-sharing (4.0.0-1focal) focal; urgency=medium + + * Complete rewrite for better maintainability and expansion + + -- Joshua Boudreau Mon, 10 Jun 2024 15:19:05 -0300 + cockpit-file-sharing (3.3.7-1focal) focal; urgency=medium * Samba - fix saving global advanced settings with '=' in their value diff --git a/packaging/focal/control b/packaging/focal/control index d9bdb6a..35d9a5b 100644 --- a/packaging/focal/control +++ b/packaging/focal/control @@ -8,5 +8,5 @@ Homepage: ::package_url:: Package: ::package_name:: Architecture: ::package_architecture_deb:: -Depends: ::package_dependencies_deb:: +Depends: ::package_dependencies_deb_focal:: Description: ::package_description_short:: diff --git a/packaging/focal/copyright b/packaging/focal/copyright index 07085e3..dfb8f40 100644 --- a/packaging/focal/copyright +++ b/packaging/focal/copyright @@ -4,14 +4,6 @@ Upstream-Contact: ::package_author:: Source: ::package_url:: Files: * -Copyright: 2021 Sam Silver -License: GPL-3.0+ - -Files: file-sharing/branding/* -Copyright: 2021 Josh Boudreau -License: GPL-3.0+ - -Files: file-sharing/samba-manager/* Copyright: 2021 Josh Boudreau License: GPL-3.0+ diff --git a/system_dependencies.txt b/system_dependencies.txt new file mode 100644 index 0000000..62571d4 --- /dev/null +++ b/system_dependencies.txt @@ -0,0 +1,16 @@ +all of houston-common/system_dependencies.txt, plus: + +commands: +systemctl +exportfs +net + +RHEL 8: +systemd - systemctl +nfs-utils - exportfs +samba-common-tools - net + +Ubuntu Focal: +systemd - systemctl +nfs-kernel-server - exportfs +samba-common-bin - net diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..baa1803 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,7075 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@45drives/houston-common-css@workspace:^, @45drives/houston-common-css@workspace:houston-common/houston-common-css": + version: 0.0.0-use.local + resolution: "@45drives/houston-common-css@workspace:houston-common/houston-common-css" + dependencies: + "@tailwindcss/forms": "npm:^0.5.7" + autoprefixer: "npm:^10.4.19" + postcss: "npm:^8.4.38" + tailwindcss: "npm:^3.4.3" + languageName: unknown + linkType: soft + +"@45drives/houston-common-lib@workspace:^, @45drives/houston-common-lib@workspace:houston-common/houston-common-lib": + version: 0.0.0-use.local + resolution: "@45drives/houston-common-lib@workspace:houston-common/houston-common-lib" + dependencies: + monet: "npm:^0.9.3" + neverthrow: "npm:^6.2.1" + prettier: "npm:^3.3.1" + typedoc: "npm:^0.25.13" + typedoc-plugin-mdn-links: "npm:^3.1.25" + typedoc-umlclass: "npm:^0.9.0" + typescript: "npm:~5.4.5" + vite: "npm:^5.2.11" + vite-plugin-dts: "npm:^3.9.1" + vitest: "npm:^1.6.0" + languageName: unknown + linkType: soft + +"@45drives/houston-common-ui@workspace:^, @45drives/houston-common-ui@workspace:houston-common/houston-common-ui": + version: 0.0.0-use.local + resolution: "@45drives/houston-common-ui@workspace:houston-common/houston-common-ui" + dependencies: + "@45drives/houston-common-css": "workspace:^" + "@45drives/houston-common-lib": "workspace:^" + "@fontsource/red-hat-text": "npm:^5.0.18" + "@headlessui/vue": "npm:^1.7.22" + "@heroicons/vue": "npm:^2.1.3" + "@rushstack/eslint-patch": "npm:^1.10.2" + "@tailwindcss/forms": "npm:^0.5.7" + "@tsconfig/node20": "npm:^20.1.4" + "@types/deep-equal": "npm:^1.0.4" + "@types/jsdom": "npm:^21.1.6" + "@types/node": "npm:^20.12.12" + "@types/uuid": "npm:^9" + "@vitejs/plugin-vue": "npm:^5.0.4" + "@vitejs/plugin-vue-jsx": "npm:^3.1.0" + "@vue/eslint-config-prettier": "npm:^9.0.0" + "@vue/eslint-config-typescript": "npm:^13.0.0" + "@vue/test-utils": "npm:^2.4.6" + "@vue/tsconfig": "npm:^0.5.1" + autoprefixer: "npm:^10.4.19" + deep-equal: "npm:^2.2.3" + eslint: "npm:^9.2.0" + eslint-plugin-vue: "npm:^9.26.0" + jsdom: "npm:^24.0.0" + neverthrow: "npm:^6.2.1" + npm-run-all2: "npm:^6.1.2" + postcss: "npm:^8.4.38" + postcss-modules: "npm:^6.0.0" + prettier: "npm:^3.2.5" + tailwindcss: "npm:^3.4.3" + typescript: "npm:~5.4.5" + uuid: "npm:^9.0.1" + vite: "npm:^5.2.11" + vite-plugin-dts: "npm:^3.9.1" + vite-plugin-vue-devtools: "npm:^7.2.0" + vitest: "npm:^1.6.0" + vue: "npm:^3.4.27" + vue-tsc: "npm:^2.0.19" + zod: "npm:^3.23.8" + zod-validation-error: "npm:^3.3.0" + languageName: unknown + linkType: soft + +"@45drives/houston-common@workspace:houston-common": + version: 0.0.0-use.local + resolution: "@45drives/houston-common@workspace:houston-common" + dependencies: + typedoc: "npm:^0.25.13" + typedoc-plugin-mdn-links: "npm:^3.1.25" + typedoc-plugin-missing-exports: "npm:^2.2.0" + typedoc-plugin-vue: "npm:^1.1.0" + typedoc-umlclass: "npm:^0.9.0" + typescript: "npm:^5.4.5" + languageName: unknown + linkType: soft + +"@aashutoshrathi/word-wrap@npm:^1.2.3": + version: 1.2.6 + resolution: "@aashutoshrathi/word-wrap@npm:1.2.6" + checksum: 10c0/53c2b231a61a46792b39a0d43bc4f4f776bb4542aa57ee04930676802e5501282c2fc8aac14e4cd1f1120ff8b52616b6ff5ab539ad30aa2277d726444b71619f + languageName: node + linkType: hard + +"@alloc/quick-lru@npm:^5.2.0": + version: 5.2.0 + resolution: "@alloc/quick-lru@npm:5.2.0" + checksum: 10c0/7b878c48b9d25277d0e1a9b8b2f2312a314af806b4129dc902f2bc29ab09b58236e53964689feec187b28c80d2203aff03829754773a707a8a5987f1b7682d92 + languageName: node + linkType: hard + +"@ampproject/remapping@npm:^2.2.0": + version: 2.3.0 + resolution: "@ampproject/remapping@npm:2.3.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed + languageName: node + linkType: hard + +"@antfu/utils@npm:^0.7.7": + version: 0.7.7 + resolution: "@antfu/utils@npm:0.7.7" + checksum: 10c0/7ca5db419f3cb6ceabf3ea254ce00ff0eab307585838c074eef40b0f948a8eade0cbad21622a62f3dea83e1cf88accf561be82bc2895e47c62cef01537b92be9 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.24.1, @babel/code-frame@npm:^7.24.2": + version: 7.24.2 + resolution: "@babel/code-frame@npm:7.24.2" + dependencies: + "@babel/highlight": "npm:^7.24.2" + picocolors: "npm:^1.0.0" + checksum: 10c0/d1d4cba89475ab6aab7a88242e1fd73b15ecb9f30c109b69752956434d10a26a52cbd37727c4eca104b6d45227bd1dfce39a6a6f4a14c9b2f07f871e968cf406 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.23.5": + version: 7.24.4 + resolution: "@babel/compat-data@npm:7.24.4" + checksum: 10c0/9cd8a9cd28a5ca6db5d0e27417d609f95a8762b655e8c9c97fd2de08997043ae99f0139007083c5e607601c6122e8432c85fe391731b19bf26ad458fa0c60dd3 + languageName: node + linkType: hard + +"@babel/core@npm:^7.23.0, @babel/core@npm:^7.23.3": + version: 7.24.4 + resolution: "@babel/core@npm:7.24.4" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.24.2" + "@babel/generator": "npm:^7.24.4" + "@babel/helper-compilation-targets": "npm:^7.23.6" + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helpers": "npm:^7.24.4" + "@babel/parser": "npm:^7.24.4" + "@babel/template": "npm:^7.24.0" + "@babel/traverse": "npm:^7.24.1" + "@babel/types": "npm:^7.24.0" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/fc136966583e64d6f84f4a676368de6ab4583aa87f867186068655b30ef67f21f8e65a88c6d446a7efd219ad7ffb9185c82e8a90183ee033f6f47b5026641e16 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.24.1, @babel/generator@npm:^7.24.4": + version: 7.24.4 + resolution: "@babel/generator@npm:7.24.4" + dependencies: + "@babel/types": "npm:^7.24.0" + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" + jsesc: "npm:^2.5.1" + checksum: 10c0/67a1b2f7cc985aaaa11b01e8ddd4fffa4f285837bc7a209738eb8203aa34bdafeb8507ed75fd883ddbabd641a036ca0a8d984e760f28ad4a9d60bff29d0a60bb + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-annotate-as-pure@npm:7.22.5" + dependencies: + "@babel/types": "npm:^7.22.5" + checksum: 10c0/5a80dc364ddda26b334bbbc0f6426cab647381555ef7d0cd32eb284e35b867c012ce6ce7d52a64672ed71383099c99d32765b3d260626527bb0e3470b0f58e45 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helper-compilation-targets@npm:7.23.6" + dependencies: + "@babel/compat-data": "npm:^7.23.5" + "@babel/helper-validator-option": "npm:^7.23.5" + browserslist: "npm:^4.22.2" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/ba38506d11185f48b79abf439462ece271d3eead1673dd8814519c8c903c708523428806f05f2ec5efd0c56e4e278698fac967e5a4b5ee842c32415da54bc6fa + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.24.1, @babel/helper-create-class-features-plugin@npm:^7.24.4": + version: 7.24.4 + resolution: "@babel/helper-create-class-features-plugin@npm:7.24.4" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-member-expression-to-functions": "npm:^7.23.0" + "@babel/helper-optimise-call-expression": "npm:^7.22.5" + "@babel/helper-replace-supers": "npm:^7.24.1" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/6ebb38375dcd44c79f40008c2de4d023376cf436c135439f15c9c54603c2d6a8ada39b2e07be545da684d9e40b602a0cb0d1670f3877d056deb5f0d786c4bf86 + languageName: node + linkType: hard + +"@babel/helper-environment-visitor@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-environment-visitor@npm:7.22.20" + checksum: 10c0/e762c2d8f5d423af89bd7ae9abe35bd4836d2eb401af868a63bbb63220c513c783e25ef001019418560b3fdc6d9a6fb67e6c0b650bcdeb3a2ac44b5c3d2bdd94 + languageName: node + linkType: hard + +"@babel/helper-function-name@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-function-name@npm:7.23.0" + dependencies: + "@babel/template": "npm:^7.22.15" + "@babel/types": "npm:^7.23.0" + checksum: 10c0/d771dd1f3222b120518176733c52b7cadac1c256ff49b1889dbbe5e3fed81db855b8cc4e40d949c9d3eae0e795e8229c1c8c24c0e83f27cfa6ee3766696c6428 + languageName: node + linkType: hard + +"@babel/helper-hoist-variables@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-hoist-variables@npm:7.22.5" + dependencies: + "@babel/types": "npm:^7.22.5" + checksum: 10c0/60a3077f756a1cd9f14eb89f0037f487d81ede2b7cfe652ea6869cd4ec4c782b0fb1de01b8494b9a2d2050e3d154d7d5ad3be24806790acfb8cbe2073bf1e208 + languageName: node + linkType: hard + +"@babel/helper-member-expression-to-functions@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-member-expression-to-functions@npm:7.23.0" + dependencies: + "@babel/types": "npm:^7.23.0" + checksum: 10c0/b810daddf093ffd0802f1429052349ed9ea08ef7d0c56da34ffbcdecbdafac86f95bdea2fe30e0e0e629febc7dd41b56cb5eacc10d1a44336d37b755dac31fa4 + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.22.15": + version: 7.24.3 + resolution: "@babel/helper-module-imports@npm:7.24.3" + dependencies: + "@babel/types": "npm:^7.24.0" + checksum: 10c0/052c188adcd100f5e8b6ff0c9643ddaabc58b6700d3bbbc26804141ad68375a9f97d9d173658d373d31853019e65f62610239e3295cdd58e573bdcb2fded188d + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:~7.22.15": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" + dependencies: + "@babel/types": "npm:^7.22.15" + checksum: 10c0/4e0d7fc36d02c1b8c8b3006dfbfeedf7a367d3334a04934255de5128115ea0bafdeb3e5736a2559917f0653e4e437400d54542da0468e08d3cbc86d3bbfa8f30 + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/helper-module-transforms@npm:7.23.3" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-module-imports": "npm:^7.22.15" + "@babel/helper-simple-access": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/helper-validator-identifier": "npm:^7.22.20" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/211e1399d0c4993671e8e5c2b25383f08bee40004ace5404ed4065f0e9258cc85d99c1b82fd456c030ce5cfd4d8f310355b54ef35de9924eabfc3dff1331d946 + languageName: node + linkType: hard + +"@babel/helper-optimise-call-expression@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-optimise-call-expression@npm:7.22.5" + dependencies: + "@babel/types": "npm:^7.22.5" + checksum: 10c0/31b41a764fc3c585196cf5b776b70cf4705c132e4ce9723f39871f215f2ddbfb2e28a62f9917610f67c8216c1080482b9b05f65dd195dae2a52cef461f2ac7b8 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.0": + version: 7.24.0 + resolution: "@babel/helper-plugin-utils@npm:7.24.0" + checksum: 10c0/90f41bd1b4dfe7226b1d33a4bb745844c5c63e400f9e4e8bf9103a7ceddd7d425d65333b564d9daba3cebd105985764d51b4bd4c95822b97c2e3ac1201a8a5da + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/helper-replace-supers@npm:7.24.1" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-member-expression-to-functions": "npm:^7.23.0" + "@babel/helper-optimise-call-expression": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/d39a3df7892b7c3c0e307fb229646168a9bd35e26a72080c2530729322600e8cff5f738f44a14860a2358faffa741b6a6a0d6749f113387b03ddbfa0ec10e1a0 + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-simple-access@npm:7.22.5" + dependencies: + "@babel/types": "npm:^7.22.5" + checksum: 10c0/f0cf81a30ba3d09a625fd50e5a9069e575c5b6719234e04ee74247057f8104beca89ed03e9217b6e9b0493434cedc18c5ecca4cea6244990836f1f893e140369 + languageName: node + linkType: hard + +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5" + dependencies: + "@babel/types": "npm:^7.22.5" + checksum: 10c0/ab7fa2aa709ab49bb8cd86515a1e715a3108c4bb9a616965ba76b43dc346dee66d1004ccf4d222b596b6224e43e04cbc5c3a34459501b388451f8c589fbc3691 + languageName: node + linkType: hard + +"@babel/helper-split-export-declaration@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helper-split-export-declaration@npm:7.22.6" + dependencies: + "@babel/types": "npm:^7.22.5" + checksum: 10c0/d83e4b623eaa9622c267d3c83583b72f3aac567dc393dda18e559d79187961cb29ae9c57b2664137fc3d19508370b12ec6a81d28af73a50e0846819cb21c6e44 + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.24.1 + resolution: "@babel/helper-string-parser@npm:7.24.1" + checksum: 10c0/2f9bfcf8d2f9f083785df0501dbab92770111ece2f90d120352fda6dd2a7d47db11b807d111e6f32aa1ba6d763fe2dc6603d153068d672a5d0ad33ca802632b2 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 10c0/dcad63db345fb110e032de46c3688384b0008a42a4845180ce7cd62b1a9c0507a1bed727c4d1060ed1a03ae57b4d918570259f81724aaac1a5b776056f37504e + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-validator-option@npm:7.23.5" + checksum: 10c0/af45d5c0defb292ba6fd38979e8f13d7da63f9623d8ab9ededc394f67eb45857d2601278d151ae9affb6e03d5d608485806cd45af08b4468a0515cf506510e94 + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.24.4": + version: 7.24.4 + resolution: "@babel/helpers@npm:7.24.4" + dependencies: + "@babel/template": "npm:^7.24.0" + "@babel/traverse": "npm:^7.24.1" + "@babel/types": "npm:^7.24.0" + checksum: 10c0/747ef62b7fe87de31a2f3c19ff337a86cbb79be2f6c18af63133b614ab5a8f6da5b06ae4b06fb0e71271cb6a27efec6f8b6c9f44c60b8a18777832dc7929e6c5 + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.24.2": + version: 7.24.2 + resolution: "@babel/highlight@npm:7.24.2" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.22.20" + chalk: "npm:^2.4.2" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.0.0" + checksum: 10c0/98ce00321daedeed33a4ed9362dc089a70375ff1b3b91228b9f05e6591d387a81a8cba68886e207861b8871efa0bc997ceabdd9c90f6cce3ee1b2f7f941b42db + languageName: node + linkType: hard + +"@babel/parser@npm:^7.23.9, @babel/parser@npm:^7.24.0, @babel/parser@npm:^7.24.1, @babel/parser@npm:^7.24.4": + version: 7.24.4 + resolution: "@babel/parser@npm:7.24.4" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/8381e1efead5069cb7ed2abc3a583f4a86289b2f376c75cecc69f59a8eb36df18274b1886cecf2f97a6a0dff5334b27330f58535be9b3e4e26102cc50e12eac8 + languageName: node + linkType: hard + +"@babel/plugin-proposal-decorators@npm:^7.23.0": + version: 7.24.1 + resolution: "@babel/plugin-proposal-decorators@npm:7.24.1" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.24.1" + "@babel/helper-plugin-utils": "npm:^7.24.0" + "@babel/plugin-syntax-decorators": "npm:^7.24.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/ffe49522ada6581f1c760b777dbd913afcd204e11e6907c4f2c293ce6d30961449ac19d9960250d8743a1f60e21cb667e51a3af15992dfe7627105e039c46a9b + languageName: node + linkType: hard + +"@babel/plugin-syntax-decorators@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/plugin-syntax-decorators@npm:7.24.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/14028a746f86efbdd47e4961456bb53d656e9e3461890f66b1b01032151d15fda5ba99fcaa60232a229a33aa9e73b11c2597b706d5074c520155757e372cd17b + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-attributes@npm:^7.22.5": + version: 7.24.1 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/309634e3335777aee902552b2cf244c4a8050213cc878b3fb9d70ad8cbbff325dc46ac5e5791836ff477ea373b27832238205f6ceaff81f7ea7c4c7e8fbb13bb + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-meta@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/0b08b5e4c3128523d8e346f8cfc86824f0da2697b1be12d71af50a31aff7a56ceb873ed28779121051475010c28d6146a6bfea8518b150b71eeb4e46190172ee + languageName: node + linkType: hard + +"@babel/plugin-syntax-jsx@npm:^7.23.3": + version: 7.24.1 + resolution: "@babel/plugin-syntax-jsx@npm:7.24.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/6cec76fbfe6ca81c9345c2904d8d9a8a0df222f9269f0962ed6eb2eb8f3f10c2f15e993d1ef09dbaf97726bf1792b5851cf5bd9a769f966a19448df6be95d19a + languageName: node + linkType: hard + +"@babel/plugin-syntax-typescript@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/plugin-syntax-typescript@npm:7.24.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/7a81e277dcfe3138847e8e5944e02a42ff3c2e864aea6f33fd9b70d1556d12b0e70f0d56cc1985d353c91bcbf8fe163e6cc17418da21129b7f7f1d8b9ac00c93 + languageName: node + linkType: hard + +"@babel/plugin-transform-typescript@npm:^7.22.15, @babel/plugin-transform-typescript@npm:^7.23.3": + version: 7.24.4 + resolution: "@babel/plugin-transform-typescript@npm:7.24.4" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.22.5" + "@babel/helper-create-class-features-plugin": "npm:^7.24.4" + "@babel/helper-plugin-utils": "npm:^7.24.0" + "@babel/plugin-syntax-typescript": "npm:^7.24.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/fa6625046f219cdc75061025c8031ada75ef631b137f1442e3d0054ba4e63548eb12cf55e2e1f442c889aa5fdd76d0d0b7904fdf812ce4c38748446227acc798 + languageName: node + linkType: hard + +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.23.9, @babel/template@npm:^7.24.0": + version: 7.24.0 + resolution: "@babel/template@npm:7.24.0" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/parser": "npm:^7.24.0" + "@babel/types": "npm:^7.24.0" + checksum: 10c0/9d3dd8d22fe1c36bc3bdef6118af1f4b030aaf6d7d2619f5da203efa818a2185d717523486c111de8d99a8649ddf4bbf6b2a7a64962d8411cf6a8fa89f010e54 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.23.9, @babel/traverse@npm:^7.24.1": + version: 7.24.1 + resolution: "@babel/traverse@npm:7.24.1" + dependencies: + "@babel/code-frame": "npm:^7.24.1" + "@babel/generator": "npm:^7.24.1" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-hoist-variables": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/parser": "npm:^7.24.1" + "@babel/types": "npm:^7.24.0" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: 10c0/c087b918f6823776537ba246136c70e7ce0719fc05361ebcbfd16f4e6f2f6f1f8f4f9167f1d9b675f27d12074839605189cc9d689de20b89a85e7c140f23daab + languageName: node + linkType: hard + +"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.9, @babel/types@npm:^7.24.0, @babel/types@npm:^7.8.3": + version: 7.24.0 + resolution: "@babel/types@npm:7.24.0" + dependencies: + "@babel/helper-string-parser": "npm:^7.23.4" + "@babel/helper-validator-identifier": "npm:^7.22.20" + to-fast-properties: "npm:^2.0.0" + checksum: 10c0/777a0bb5dbe038ca4c905fdafb1cdb6bdd10fe9d63ce13eca0bd91909363cbad554a53dc1f902004b78c1dcbc742056f877f2c99eeedff647333b1fadf51235d + languageName: node + linkType: hard + +"@cspotcode/source-map-support@npm:^0.8.0": + version: 0.8.1 + resolution: "@cspotcode/source-map-support@npm:0.8.1" + dependencies: + "@jridgewell/trace-mapping": "npm:0.3.9" + checksum: 10c0/05c5368c13b662ee4c122c7bfbe5dc0b613416672a829f3e78bc49a357a197e0218d6e74e7c66cfcd04e15a179acab080bd3c69658c9fbefd0e1ccd950a07fc6 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/aix-ppc64@npm:0.20.2" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-arm64@npm:0.20.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-arm@npm:0.20.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/android-x64@npm:0.20.2" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/darwin-arm64@npm:0.20.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/darwin-x64@npm:0.20.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/freebsd-arm64@npm:0.20.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/freebsd-x64@npm:0.20.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-arm64@npm:0.20.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-arm@npm:0.20.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-ia32@npm:0.20.2" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-loong64@npm:0.20.2" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-mips64el@npm:0.20.2" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-ppc64@npm:0.20.2" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-riscv64@npm:0.20.2" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-s390x@npm:0.20.2" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/linux-x64@npm:0.20.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/netbsd-x64@npm:0.20.2" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/openbsd-x64@npm:0.20.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/sunos-x64@npm:0.20.2" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-arm64@npm:0.20.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-ia32@npm:0.20.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.20.2": + version: 0.20.2 + resolution: "@esbuild/win32-x64@npm:0.20.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": + version: 4.4.0 + resolution: "@eslint-community/eslint-utils@npm:4.4.0" + dependencies: + eslint-visitor-keys: "npm:^3.3.0" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/7e559c4ce59cd3a06b1b5a517b593912e680a7f981ae7affab0d01d709e99cd5647019be8fafa38c350305bc32f1f7d42c7073edde2ab536c745e365f37b607e + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.6.1": + version: 4.10.0 + resolution: "@eslint-community/regexpp@npm:4.10.0" + checksum: 10c0/c5f60ef1f1ea7649fa7af0e80a5a79f64b55a8a8fa5086de4727eb4c86c652aedee407a9c143b8995d2c0b2d75c1222bec9ba5d73dbfc1f314550554f0979ef4 + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^3.0.2": + version: 3.0.2 + resolution: "@eslint/eslintrc@npm:3.0.2" + dependencies: + ajv: "npm:^6.12.4" + debug: "npm:^4.3.2" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.0" + minimatch: "npm:^3.1.2" + strip-json-comments: "npm:^3.1.1" + checksum: 10c0/d8c92f06bdf8e2be9fcc0eeac4a9351745174adfcc72571ef3d179101cb55e19f15f6385c2a4f4945a3ba9245802d3371208e2e1e4f00f6bcf6b8711656af85a + languageName: node + linkType: hard + +"@eslint/js@npm:9.2.0": + version: 9.2.0 + resolution: "@eslint/js@npm:9.2.0" + checksum: 10c0/89632466d329d9dd68c6ec24290e407f0950ca8c4b7f3750b82457daa7f6233799ccbc956cd84231f9544efbefddd69833ee82658883ca673cfca9e4b8e0713a + languageName: node + linkType: hard + +"@fontsource/red-hat-text@npm:^5.0.18": + version: 5.0.18 + resolution: "@fontsource/red-hat-text@npm:5.0.18" + checksum: 10c0/fc74ac4aeaad0477140cd0a88a65f39085b2d970fcaa1be050ad9a9f60e02a79ec68c292a7c5488f0f5f4e482b56f192855a8eb229f331d47a992df97ad5f114 + languageName: node + linkType: hard + +"@headlessui/vue@npm:^1.7.22": + version: 1.7.22 + resolution: "@headlessui/vue@npm:1.7.22" + dependencies: + "@tanstack/vue-virtual": "npm:^3.0.0-beta.60" + peerDependencies: + vue: ^3.2.0 + checksum: 10c0/0e86ab60ac471ae36d60b05f365c7d707feb8884e4aea49875bd2b9d53ccfbd0001b2e18b5e7c2f88e9b535acd4dc58b461a046cde5ee70204cf53ec1e520889 + languageName: node + linkType: hard + +"@heroicons/vue@npm:^2.1.3": + version: 2.1.3 + resolution: "@heroicons/vue@npm:2.1.3" + peerDependencies: + vue: ">= 3" + checksum: 10c0/af0b1336409aa054c22c572f9214009042aec163131c1de58b995fbec1dd4508b768500614100ed750e8911ca8a695e43efc87a8627c86a8c2387422fe2f6dd4 + languageName: node + linkType: hard + +"@humanwhocodes/config-array@npm:^0.13.0": + version: 0.13.0 + resolution: "@humanwhocodes/config-array@npm:0.13.0" + dependencies: + "@humanwhocodes/object-schema": "npm:^2.0.3" + debug: "npm:^4.3.1" + minimatch: "npm:^3.0.5" + checksum: 10c0/205c99e756b759f92e1f44a3dc6292b37db199beacba8f26c2165d4051fe73a4ae52fdcfd08ffa93e7e5cb63da7c88648f0e84e197d154bbbbe137b2e0dd332e + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 10c0/909b69c3b86d482c26b3359db16e46a32e0fb30bd306a3c176b8313b9e7313dba0f37f519de6aa8b0a1921349e505f259d19475e123182416a506d7f87e7f529 + languageName: node + linkType: hard + +"@humanwhocodes/object-schema@npm:^2.0.3": + version: 2.0.3 + resolution: "@humanwhocodes/object-schema@npm:2.0.3" + checksum: 10c0/80520eabbfc2d32fe195a93557cef50dfe8c8905de447f022675aaf66abc33ae54098f5ea78548d925aa671cd4ab7c7daa5ad704fe42358c9b5e7db60f80696c + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.2.3": + version: 0.2.4 + resolution: "@humanwhocodes/retry@npm:0.2.4" + checksum: 10c0/d0e3fe9c353f97fea6a9d0a4022b0f8813d68b646c0fa99718ec703b085fd66dd84154d947670291914bc1ab2d1fe77f0093d99d3a5fe9f56eef65360e7c6c86 + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" + dependencies: + "@sinclair/typebox": "npm:^0.27.8" + checksum: 10c0/b329e89cd5f20b9278ae1233df74016ebf7b385e0d14b9f4c1ad18d096c4c19d1e687aa113a9c976b16ec07f021ae53dea811fb8c1248a50ac34fbe009fdf6be + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" + dependencies: + "@jridgewell/set-array": "npm:^1.2.1" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/1be4fd4a6b0f41337c4f5fdf4afc3bd19e39c3691924817108b82ffcb9c9e609c273f936932b9fba4b3a298ce2eb06d9bff4eb1cc3bd81c4f4ee1b4917e25feb + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 10c0/2a5aa7b4b5c3464c895c802d8ae3f3d2b92fcbe84ad12f8d0bfbb1f5ad006717e7577ee1fd2eac00c088abe486c7adb27976f45d2941ff6b0b92b2c3302c60f4 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: 10c0/0c6b5ae663087558039052a626d2d7ed5208da36cfd707dcc5cea4a07cfc918248403dcb5989a8f7afaf245ce0573b7cc6fd94c4a30453bd10e44d9363940ba5 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:0.3.9": + version: 0.3.9 + resolution: "@jridgewell/trace-mapping@npm:0.3.9" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.0.3" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + checksum: 10c0/fa425b606d7c7ee5bfa6a31a7b050dd5814b4082f318e0e4190f991902181b4330f43f4805db1dd4f2433fd0ed9cc7a7b9c2683f1deeab1df1b0a98b1e24055b + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4 + languageName: node + linkType: hard + +"@microsoft/api-extractor-model@npm:7.28.13": + version: 7.28.13 + resolution: "@microsoft/api-extractor-model@npm:7.28.13" + dependencies: + "@microsoft/tsdoc": "npm:0.14.2" + "@microsoft/tsdoc-config": "npm:~0.16.1" + "@rushstack/node-core-library": "npm:4.0.2" + checksum: 10c0/da83f6ccc01fac3b8274731327a6d35a45b2d98ce8c1d705a974ca34dd48ac0f9b0fe8e98130d2068ec1ee4e2b1f2942b53e21e6e5897f1d3501a3c4e5910645 + languageName: node + linkType: hard + +"@microsoft/api-extractor@npm:7.43.0": + version: 7.43.0 + resolution: "@microsoft/api-extractor@npm:7.43.0" + dependencies: + "@microsoft/api-extractor-model": "npm:7.28.13" + "@microsoft/tsdoc": "npm:0.14.2" + "@microsoft/tsdoc-config": "npm:~0.16.1" + "@rushstack/node-core-library": "npm:4.0.2" + "@rushstack/rig-package": "npm:0.5.2" + "@rushstack/terminal": "npm:0.10.0" + "@rushstack/ts-command-line": "npm:4.19.1" + lodash: "npm:~4.17.15" + minimatch: "npm:~3.0.3" + resolve: "npm:~1.22.1" + semver: "npm:~7.5.4" + source-map: "npm:~0.6.1" + typescript: "npm:5.4.2" + bin: + api-extractor: bin/api-extractor + checksum: 10c0/1bbd1866508db2c5c0ad771e4aeccef95201319879b5cd2b00c5177cfdedb1ad5bc35a452be9d14ac3cfcdf7c9b7c3a737bc2ada9bdcc48eb0e6e11214169b52 + languageName: node + linkType: hard + +"@microsoft/tsdoc-config@npm:~0.16.1": + version: 0.16.2 + resolution: "@microsoft/tsdoc-config@npm:0.16.2" + dependencies: + "@microsoft/tsdoc": "npm:0.14.2" + ajv: "npm:~6.12.6" + jju: "npm:~1.4.0" + resolve: "npm:~1.19.0" + checksum: 10c0/9e8c176b68f01c8bb38e6365d5b543e471bba59fced6070d9bd35b32461fbd650c2e1a6f686e8dca0cf22bc5e7d796e4213e66bce4426c8cb9864c1f6ca6836c + languageName: node + linkType: hard + +"@microsoft/tsdoc@npm:0.14.2": + version: 0.14.2 + resolution: "@microsoft/tsdoc@npm:0.14.2" + checksum: 10c0/c018857ad439144559ce34a397a29ace7cf5b24b999b8e3c1b88d878338088b3a453eaac4435beaf2c7eae13c4c0aac81e42f96f0f1d48e8d4eeb438eb3bb82f + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 + languageName: node + linkType: hard + +"@npmcli/agent@npm:^2.0.0": + version: 2.2.2 + resolution: "@npmcli/agent@npm:2.2.2" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^10.0.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae + languageName: node + linkType: hard + +"@npmcli/fs@npm:^3.1.0": + version: 3.1.0 + resolution: "@npmcli/fs@npm:3.1.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/162b4a0b8705cd6f5c2470b851d1dc6cd228c86d2170e1769d738c1fbb69a87160901411c3c035331e9e99db72f1f1099a8b734bf1637cc32b9a5be1660e4e1e + languageName: node + linkType: hard + +"@one-ini/wasm@npm:0.1.1": + version: 0.1.1 + resolution: "@one-ini/wasm@npm:0.1.1" + checksum: 10c0/54700e055037f1a63bfcc86d24822203b25759598c2c3e295d1435130a449108aebc119c9c2e467744767dbe0b6ab47a182c61aa1071ba7368f5e20ab197ba65 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@pkgr/core@npm:^0.1.0": + version: 0.1.1 + resolution: "@pkgr/core@npm:0.1.1" + checksum: 10c0/3f7536bc7f57320ab2cf96f8973664bef624710c403357429fbf680a5c3b4843c1dbd389bb43daa6b1f6f1f007bb082f5abcb76bb2b5dc9f421647743b71d3d8 + languageName: node + linkType: hard + +"@polka/url@npm:^1.0.0-next.24": + version: 1.0.0-next.25 + resolution: "@polka/url@npm:1.0.0-next.25" + checksum: 10c0/ef61f0a0fe94bb6e1143fc5b9d5a12e6ca9dbd2c57843ebf81db432c21b9f1005c09e8a1ef8b6d5ddfa42146ca65b640feb2d353bd0d3546da46ba59e48a5349 + languageName: node + linkType: hard + +"@rollup/pluginutils@npm:^5.1.0": + version: 5.1.0 + resolution: "@rollup/pluginutils@npm:5.1.0" + dependencies: + "@types/estree": "npm:^1.0.0" + estree-walker: "npm:^2.0.2" + picomatch: "npm:^2.3.1" + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: 10c0/c7bed15711f942d6fdd3470fef4105b73991f99a478605e13d41888963330a6f9e32be37e6ddb13f012bc7673ff5e54f06f59fd47109436c1c513986a8a7612d + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.16.4" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-android-arm64@npm:4.16.4" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.16.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.16.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.16.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.16.4" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.16.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.16.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.16.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.16.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.16.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.16.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.16.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.16.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.16.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.16.4": + version: 4.16.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.16.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rushstack/eslint-patch@npm:^1.10.2": + version: 1.10.2 + resolution: "@rushstack/eslint-patch@npm:1.10.2" + checksum: 10c0/3712b8994bfed0968d4c4f21ff10bf5f2abb47f47d65d4e616de4311d2f372f2528b03b1d4e0dcaa7392f8626ccdf114e753db4f790e92436fc5a4e52195d181 + languageName: node + linkType: hard + +"@rushstack/node-core-library@npm:4.0.2": + version: 4.0.2 + resolution: "@rushstack/node-core-library@npm:4.0.2" + dependencies: + fs-extra: "npm:~7.0.1" + import-lazy: "npm:~4.0.0" + jju: "npm:~1.4.0" + resolve: "npm:~1.22.1" + semver: "npm:~7.5.4" + z-schema: "npm:~5.0.2" + peerDependencies: + "@types/node": "*" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/b60070b5b8f4da0cbda5b37f950ed5d3843f3c68aab13ce1d4383b9fd71ca94065dfdd2e3b10b361ae0ef5fd0182d891da2addfd3f1ca21e7789110f6266d83f + languageName: node + linkType: hard + +"@rushstack/rig-package@npm:0.5.2": + version: 0.5.2 + resolution: "@rushstack/rig-package@npm:0.5.2" + dependencies: + resolve: "npm:~1.22.1" + strip-json-comments: "npm:~3.1.1" + checksum: 10c0/7bff460eb8407a68de20681b6354703c0fdb7a325c58060a2c4591b86dd3b83b95b651ccba3cc833f8d1a94c3a19638091b447c03d89eaa9df57bc9de7abb29d + languageName: node + linkType: hard + +"@rushstack/terminal@npm:0.10.0": + version: 0.10.0 + resolution: "@rushstack/terminal@npm:0.10.0" + dependencies: + "@rushstack/node-core-library": "npm:4.0.2" + supports-color: "npm:~8.1.1" + peerDependencies: + "@types/node": "*" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/128d13d353265bd318fc52a5d2eaf6d352d3abd29fc3500d630b4d114b43392e2dfe8c4df200e855dc2c07e6d4e8f2175c38b5a8b71dff1eee7aa1f5a261e1c7 + languageName: node + linkType: hard + +"@rushstack/ts-command-line@npm:4.19.1": + version: 4.19.1 + resolution: "@rushstack/ts-command-line@npm:4.19.1" + dependencies: + "@rushstack/terminal": "npm:0.10.0" + "@types/argparse": "npm:1.0.38" + argparse: "npm:~1.0.9" + string-argv: "npm:~0.3.1" + checksum: 10c0/329184ae53b3d5dc0218ef63dbbd65efc3a3f423595cf69865cb47ee7ae233cfa8c93d87c31cdb1ef8a00f286d3dd56dc629a16c5903777a9eb1f603dd801c25 + languageName: node + linkType: hard + +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 10c0/ef6351ae073c45c2ac89494dbb3e1f87cc60a93ce4cde797b782812b6f97da0d620ae81973f104b43c9b7eaa789ad20ba4f6a1359f1cc62f63729a55a7d22d4e + languageName: node + linkType: hard + +"@tailwindcss/forms@npm:^0.5.7": + version: 0.5.7 + resolution: "@tailwindcss/forms@npm:0.5.7" + dependencies: + mini-svg-data-uri: "npm:^1.2.3" + peerDependencies: + tailwindcss: ">=3.0.0 || >= 3.0.0-alpha.1" + checksum: 10c0/cd29e0c978402ae87a923ae802dcff43f7b050595666cb067321cac2e37a52f61b9d73385cb0a10455548581ddd0d3886815bd6c64a1da06247c0057fa9f4601 + languageName: node + linkType: hard + +"@tanstack/virtual-core@npm:3.5.0": + version: 3.5.0 + resolution: "@tanstack/virtual-core@npm:3.5.0" + checksum: 10c0/21a12049df81ce282054e8406cb74dd21d867aac077f31568ffdb08acd9aa171ae0666ceb43654895e73907acf5db02c7d651a66960da8acceeb2e034885e605 + languageName: node + linkType: hard + +"@tanstack/vue-virtual@npm:^3.0.0-beta.60": + version: 3.5.0 + resolution: "@tanstack/vue-virtual@npm:3.5.0" + dependencies: + "@tanstack/virtual-core": "npm:3.5.0" + peerDependencies: + vue: ^2.7.0 || ^3.0.0 + checksum: 10c0/4e6f1a5cce660e42c822b34c24325d85c9ab6adb2c0acd7aea75821f25466de9338d84a4258732ede14d9ee538b45d2150a3f21d1c65a38ff3b88333dae67c5f + languageName: node + linkType: hard + +"@tsconfig/node10@npm:^1.0.7": + version: 1.0.11 + resolution: "@tsconfig/node10@npm:1.0.11" + checksum: 10c0/28a0710e5d039e0de484bdf85fee883bfd3f6a8980601f4d44066b0a6bcd821d31c4e231d1117731c4e24268bd4cf2a788a6787c12fc7f8d11014c07d582783c + languageName: node + linkType: hard + +"@tsconfig/node12@npm:^1.0.7": + version: 1.0.11 + resolution: "@tsconfig/node12@npm:1.0.11" + checksum: 10c0/dddca2b553e2bee1308a056705103fc8304e42bb2d2cbd797b84403a223b25c78f2c683ec3e24a095e82cd435387c877239bffcb15a590ba817cd3f6b9a99fd9 + languageName: node + linkType: hard + +"@tsconfig/node14@npm:^1.0.0": + version: 1.0.3 + resolution: "@tsconfig/node14@npm:1.0.3" + checksum: 10c0/67c1316d065fdaa32525bc9449ff82c197c4c19092b9663b23213c8cbbf8d88b6ed6a17898e0cbc2711950fbfaf40388938c1c748a2ee89f7234fc9e7fe2bf44 + languageName: node + linkType: hard + +"@tsconfig/node16@npm:^1.0.2": + version: 1.0.4 + resolution: "@tsconfig/node16@npm:1.0.4" + checksum: 10c0/05f8f2734e266fb1839eb1d57290df1664fe2aa3b0fdd685a9035806daa635f7519bf6d5d9b33f6e69dd545b8c46bd6e2b5c79acb2b1f146e885f7f11a42a5bb + languageName: node + linkType: hard + +"@tsconfig/node20@npm:^20.1.4": + version: 20.1.4 + resolution: "@tsconfig/node20@npm:20.1.4" + checksum: 10c0/239cd6abaceaaffc758e7a9dd3748ec1acfdabd6de28a9a16a3824c21f17ad69a16184870298460345d2fb6f3663d092c21d5b764bef669a179658b4a7eac739 + languageName: node + linkType: hard + +"@types/argparse@npm:1.0.38": + version: 1.0.38 + resolution: "@types/argparse@npm:1.0.38" + checksum: 10c0/4fc892da5df16923f48180da2d1f4562fa8b0507cf636b24780444fa0a1d7321d4dc0c0ecbee6152968823f5a2ae0d321b4f8c705a489bf1ae1245bdeb0868fd + languageName: node + linkType: hard + +"@types/deep-equal@npm:^1.0.4": + version: 1.0.4 + resolution: "@types/deep-equal@npm:1.0.4" + checksum: 10c0/583d41df5d7655b0bd5fdd4b173b045396108fad2191e1bd3b1bfc188f98d24fafff34a8a09c04f9c650c87d82e9f25a8119d269044522da0770a05075fbf74d + languageName: node + linkType: hard + +"@types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": + version: 1.0.5 + resolution: "@types/estree@npm:1.0.5" + checksum: 10c0/b3b0e334288ddb407c7b3357ca67dbee75ee22db242ca7c56fe27db4e1a31989cb8af48a84dd401deb787fe10cc6b2ab1ee82dc4783be87ededbe3d53c79c70d + languageName: node + linkType: hard + +"@types/jsdom@npm:^21.1.6": + version: 21.1.6 + resolution: "@types/jsdom@npm:21.1.6" + dependencies: + "@types/node": "npm:*" + "@types/tough-cookie": "npm:*" + parse5: "npm:^7.0.0" + checksum: 10c0/6ae8f84c6e7b8e1c303b8bc271bc51ad21399bbfec93fdc3b168af8aa9cafb41ebb00eed99753fbb1943e4fc5006aa39e34251dee4d116d55f731cebc0f02e64 + languageName: node + linkType: hard + +"@types/json-schema@npm:^7.0.15": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 20.12.7 + resolution: "@types/node@npm:20.12.7" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/dce80d63a3b91892b321af823d624995c61e39c6a223cc0ac481a44d337640cc46931d33efb3beeed75f5c85c3bda1d97cef4c5cd4ec333caf5dee59cff6eca0 + languageName: node + linkType: hard + +"@types/node@npm:^20.12.12": + version: 20.12.12 + resolution: "@types/node@npm:20.12.12" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/f374b763c744e8f16e4f38cf6e2c0eef31781ec9228c9e43a6f267880fea420fab0a238b59f10a7cb3444e49547c5e3785787e371fc242307310995b21988812 + languageName: node + linkType: hard + +"@types/semver@npm:^7.5.8": + version: 7.5.8 + resolution: "@types/semver@npm:7.5.8" + checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa + languageName: node + linkType: hard + +"@types/tough-cookie@npm:*": + version: 4.0.5 + resolution: "@types/tough-cookie@npm:4.0.5" + checksum: 10c0/68c6921721a3dcb40451543db2174a145ef915bc8bcbe7ad4e59194a0238e776e782b896c7a59f4b93ac6acefca9161fccb31d1ce3b3445cb6faa467297fb473 + languageName: node + linkType: hard + +"@types/uuid@npm:^9": + version: 9.0.8 + resolution: "@types/uuid@npm:9.0.8" + checksum: 10c0/b411b93054cb1d4361919579ef3508a1f12bf15b5fdd97337d3d351bece6c921b52b6daeef89b62340fd73fd60da407878432a1af777f40648cbe53a01723489 + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:^7.1.1": + version: 7.7.1 + resolution: "@typescript-eslint/eslint-plugin@npm:7.7.1" + dependencies: + "@eslint-community/regexpp": "npm:^4.10.0" + "@typescript-eslint/scope-manager": "npm:7.7.1" + "@typescript-eslint/type-utils": "npm:7.7.1" + "@typescript-eslint/utils": "npm:7.7.1" + "@typescript-eslint/visitor-keys": "npm:7.7.1" + debug: "npm:^4.3.4" + graphemer: "npm:^1.4.0" + ignore: "npm:^5.3.1" + natural-compare: "npm:^1.4.0" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^1.3.0" + peerDependencies: + "@typescript-eslint/parser": ^7.0.0 + eslint: ^8.56.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/11a085240e7daf4bdeb011aa53ac7cfeea6263c60d53607823f5c314eb5c9d559b28fce0d6686acb9702ee3d0cb0406534fafae61163e5a903eaf818c48194ad + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:^7.1.1": + version: 7.7.1 + resolution: "@typescript-eslint/parser@npm:7.7.1" + dependencies: + "@typescript-eslint/scope-manager": "npm:7.7.1" + "@typescript-eslint/types": "npm:7.7.1" + "@typescript-eslint/typescript-estree": "npm:7.7.1" + "@typescript-eslint/visitor-keys": "npm:7.7.1" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^8.56.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/ace43eeb8123bbee61e936650e1d57a2cf70f2030870c6dcad8602fce3f7cdf2cce350121dbbc66cffd60bac36652f426a1c5293c45ed28998b90cd95673b5c9 + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:7.7.1": + version: 7.7.1 + resolution: "@typescript-eslint/scope-manager@npm:7.7.1" + dependencies: + "@typescript-eslint/types": "npm:7.7.1" + "@typescript-eslint/visitor-keys": "npm:7.7.1" + checksum: 10c0/4032da8fce8922044a6b659c8435ba203377778d5b7de6a5572c1172f2e3cf8ddd890a0f9e083c5d5315a9c2dba323707528ee4ad3cc1be2bd334de2527ef5cb + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:7.7.1": + version: 7.7.1 + resolution: "@typescript-eslint/type-utils@npm:7.7.1" + dependencies: + "@typescript-eslint/typescript-estree": "npm:7.7.1" + "@typescript-eslint/utils": "npm:7.7.1" + debug: "npm:^4.3.4" + ts-api-utils: "npm:^1.3.0" + peerDependencies: + eslint: ^8.56.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/bd083c4106e207aa8c2a71251eca52d23c7ea905399b8c62004f3bb1e85b9c88d601db9dcecae88beef0f8362d53450bb2721aab353ee731c1665496fea3fbda + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:7.7.1": + version: 7.7.1 + resolution: "@typescript-eslint/types@npm:7.7.1" + checksum: 10c0/7d240503d9d0b12d68c8204167690609f02ededb77dcb035c1c8b932da08cf43553829c29a5f7889824a7337463c300343bc5abe532479726d4c83443a7e2704 + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:7.7.1": + version: 7.7.1 + resolution: "@typescript-eslint/typescript-estree@npm:7.7.1" + dependencies: + "@typescript-eslint/types": "npm:7.7.1" + "@typescript-eslint/visitor-keys": "npm:7.7.1" + debug: "npm:^4.3.4" + globby: "npm:^11.1.0" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^1.3.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/c6b32bd96fd13b9da0a30de01935066f7505f6214f5759e3cd019f7d1852f7bf19358765f62e51de72be47647656aa0e8f07ac0ab316c4149a4e6bd1dd12cbb6 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:7.7.1": + version: 7.7.1 + resolution: "@typescript-eslint/utils@npm:7.7.1" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@types/json-schema": "npm:^7.0.15" + "@types/semver": "npm:^7.5.8" + "@typescript-eslint/scope-manager": "npm:7.7.1" + "@typescript-eslint/types": "npm:7.7.1" + "@typescript-eslint/typescript-estree": "npm:7.7.1" + semver: "npm:^7.6.0" + peerDependencies: + eslint: ^8.56.0 + checksum: 10c0/0986b8c297d6bfdbd2ac8cd3bcf447ef9b934e2dae536771d3368a5c284a0b16c0ea041f82aa100c48d05acc33198e1a3d9d721d3319ae80abba0f5e69c21633 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:7.7.1": + version: 7.7.1 + resolution: "@typescript-eslint/visitor-keys@npm:7.7.1" + dependencies: + "@typescript-eslint/types": "npm:7.7.1" + eslint-visitor-keys: "npm:^3.4.3" + checksum: 10c0/19cbd14ac9a234d847f457cbd880cbd98b83c331a46d2dc2d8c0e6cb54ce6159552f6dd2f7236035be1a71f13f48df4a2aa09e70ad1f1e2ff3da7c3622927bd3 + languageName: node + linkType: hard + +"@vitejs/plugin-vue-jsx@npm:^3.1.0": + version: 3.1.0 + resolution: "@vitejs/plugin-vue-jsx@npm:3.1.0" + dependencies: + "@babel/core": "npm:^7.23.3" + "@babel/plugin-transform-typescript": "npm:^7.23.3" + "@vue/babel-plugin-jsx": "npm:^1.1.5" + peerDependencies: + vite: ^4.0.0 || ^5.0.0 + vue: ^3.0.0 + checksum: 10c0/9d3d10d9ceae9710e819638a4c882d743b04d13fd962edf5b070d57b05b23f77fa1ec4989ed1707f33e64991865bcf062c6036081190f87b72b6610c9ef6a678 + languageName: node + linkType: hard + +"@vitejs/plugin-vue@npm:^5.0.4": + version: 5.0.4 + resolution: "@vitejs/plugin-vue@npm:5.0.4" + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + checksum: 10c0/2e65900ff41037a013ef40351aa2fb68ee1963da461795b4e3d01fc4a0226c65e9ca4bc39941dc163f883773bcd80744ee0f1f96ecc2f46fae1a4cd71c741308 + languageName: node + linkType: hard + +"@vitest/expect@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/expect@npm:1.6.0" + dependencies: + "@vitest/spy": "npm:1.6.0" + "@vitest/utils": "npm:1.6.0" + chai: "npm:^4.3.10" + checksum: 10c0/a4351f912a70543e04960f5694f1f1ac95f71a856a46e87bba27d3eb72a08c5d11d35021cbdc6077452a152e7d93723fc804bba76c2cc53c8896b7789caadae3 + languageName: node + linkType: hard + +"@vitest/runner@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/runner@npm:1.6.0" + dependencies: + "@vitest/utils": "npm:1.6.0" + p-limit: "npm:^5.0.0" + pathe: "npm:^1.1.1" + checksum: 10c0/27d67fa51f40effe0e41ee5f26563c12c0ef9a96161f806036f02ea5eb9980c5cdf305a70673942e7a1e3d472d4d7feb40093ae93024ef1ccc40637fc65b1d2f + languageName: node + linkType: hard + +"@vitest/snapshot@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/snapshot@npm:1.6.0" + dependencies: + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + pretty-format: "npm:^29.7.0" + checksum: 10c0/be027fd268d524589ff50c5fad7b4faa1ac5742b59ac6c1dc6f5a3930aad553560e6d8775e90ac4dfae4be746fc732a6f134ba95606a1519707ce70db3a772a5 + languageName: node + linkType: hard + +"@vitest/spy@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/spy@npm:1.6.0" + dependencies: + tinyspy: "npm:^2.2.0" + checksum: 10c0/df66ea6632b44fb76ef6a65c1abbace13d883703aff37cd6d062add6dcd1b883f19ce733af8e0f7feb185b61600c6eb4042a518e4fb66323d0690ec357f9401c + languageName: node + linkType: hard + +"@vitest/utils@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/utils@npm:1.6.0" + dependencies: + diff-sequences: "npm:^29.6.3" + estree-walker: "npm:^3.0.3" + loupe: "npm:^2.3.7" + pretty-format: "npm:^29.7.0" + checksum: 10c0/8b0d19835866455eb0b02b31c5ca3d8ad45f41a24e4c7e1f064b480f6b2804dc895a70af332f14c11ed89581011b92b179718523f55f5b14787285a0321b1301 + languageName: node + linkType: hard + +"@volar/language-core@npm:1.11.1, @volar/language-core@npm:~1.11.1": + version: 1.11.1 + resolution: "@volar/language-core@npm:1.11.1" + dependencies: + "@volar/source-map": "npm:1.11.1" + checksum: 10c0/92c4439e3a9ccc534c970031388c318740f6fa032283d03e136c6c8c0228f549c68a7c363af1a28252617a0dca6069e14028329ac906d5acf1912931d0cdcb69 + languageName: node + linkType: hard + +"@volar/language-core@npm:2.2.4, @volar/language-core@npm:~2.2.4": + version: 2.2.4 + resolution: "@volar/language-core@npm:2.2.4" + dependencies: + "@volar/source-map": "npm:2.2.4" + checksum: 10c0/2929944dc21e6e9db1942e4903228520cfb90f3cf39bd5c5f49266dfe43527d91f23ca31d94dd0a799eb164ed9856b4355b30f9d2fb26f88eaa873649654fd76 + languageName: node + linkType: hard + +"@volar/source-map@npm:1.11.1, @volar/source-map@npm:~1.11.1": + version: 1.11.1 + resolution: "@volar/source-map@npm:1.11.1" + dependencies: + muggle-string: "npm:^0.3.1" + checksum: 10c0/0bfc639889802705f8036ea8b2052a95a4d691a68bc2b6744ba8b9d312d887393dd3278101180a5ee5304972899d493972a483afafd41e097968746c77d724cb + languageName: node + linkType: hard + +"@volar/source-map@npm:2.2.4": + version: 2.2.4 + resolution: "@volar/source-map@npm:2.2.4" + dependencies: + muggle-string: "npm:^0.4.0" + checksum: 10c0/8d9c880d354bf20b74fdfdd8ee51a20681338b16196c701fbe07191fbd6f737dbe1cd9c2b4d6d4a478b250c99d4cbe1a34073cc099f2e24645d68c9086629bf4 + languageName: node + linkType: hard + +"@volar/typescript@npm:~1.11.1": + version: 1.11.1 + resolution: "@volar/typescript@npm:1.11.1" + dependencies: + "@volar/language-core": "npm:1.11.1" + path-browserify: "npm:^1.0.1" + checksum: 10c0/86fe153db3a14d8eb3632784a1d7fcbfbfb51fa5517c3878bfdd49ee8d15a83b1a09f9c589454b7396454c104d3a8e2db3a987dc99b37c33816772fc3e292bf2 + languageName: node + linkType: hard + +"@volar/typescript@npm:~2.2.4": + version: 2.2.4 + resolution: "@volar/typescript@npm:2.2.4" + dependencies: + "@volar/language-core": "npm:2.2.4" + path-browserify: "npm:^1.0.1" + checksum: 10c0/8b5e3eff64afc614201b4dc4285e52ec37814e1a7d2dee911e65d46d0e79eed376934757d7629490e103653fd4dadcc668bc30392ef1f51a34a273420d473b95 + languageName: node + linkType: hard + +"@vue/babel-helper-vue-transform-on@npm:1.2.2": + version: 1.2.2 + resolution: "@vue/babel-helper-vue-transform-on@npm:1.2.2" + checksum: 10c0/15f9d919b48decebc714ceb89cc402ad78c0ce0b8a18cca3fff5d49f9ba5c9881151c66114bd5a39e629f2ec107d590a7b0201d99ea2448cce9757981ec816fd + languageName: node + linkType: hard + +"@vue/babel-plugin-jsx@npm:^1.1.5": + version: 1.2.2 + resolution: "@vue/babel-plugin-jsx@npm:1.2.2" + dependencies: + "@babel/helper-module-imports": "npm:~7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/plugin-syntax-jsx": "npm:^7.23.3" + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + "@vue/babel-helper-vue-transform-on": "npm:1.2.2" + "@vue/babel-plugin-resolve-type": "npm:1.2.2" + camelcase: "npm:^6.3.0" + html-tags: "npm:^3.3.1" + svg-tags: "npm:^1.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + peerDependenciesMeta: + "@babel/core": + optional: true + checksum: 10c0/a89e62c6e321a06ff98d83255fceef157efe83a33cca78d394125c89303884a9273b101e9a0b1442c4cd09e5ce48ec5d228353625f0f2e6795f1cdd404b5af07 + languageName: node + linkType: hard + +"@vue/babel-plugin-resolve-type@npm:1.2.2": + version: 1.2.2 + resolution: "@vue/babel-plugin-resolve-type@npm:1.2.2" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/helper-module-imports": "npm:~7.22.15" + "@babel/helper-plugin-utils": "npm:^7.22.5" + "@babel/parser": "npm:^7.23.9" + "@vue/compiler-sfc": "npm:^3.4.15" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/577a021f03d2cada0a174bcea75497173dcacd38df4e7fb14de2b90bbaca2b430fc778100df863c20c0364c17933c0fd3b6c758fca528583f68c11723c2d1b66 + languageName: node + linkType: hard + +"@vue/compiler-core@npm:3.4.24": + version: 3.4.24 + resolution: "@vue/compiler-core@npm:3.4.24" + dependencies: + "@babel/parser": "npm:^7.24.4" + "@vue/shared": "npm:3.4.24" + entities: "npm:^4.5.0" + estree-walker: "npm:^2.0.2" + source-map-js: "npm:^1.2.0" + checksum: 10c0/555806bed6e3924a71e623b53193f70b0eb81a2b4637d01fee2bc6f6719f0694a9f611fc71bb60d1d527f308cf6e81a91c3bf9d06dc36ca3d7b96c0af668dc4a + languageName: node + linkType: hard + +"@vue/compiler-core@npm:3.4.26": + version: 3.4.26 + resolution: "@vue/compiler-core@npm:3.4.26" + dependencies: + "@babel/parser": "npm:^7.24.4" + "@vue/shared": "npm:3.4.26" + entities: "npm:^4.5.0" + estree-walker: "npm:^2.0.2" + source-map-js: "npm:^1.2.0" + checksum: 10c0/3591fda1b07e929b2b83eaaee67c21075d0c7676ecb8d65c9d800d0b2db5afcf1c44a98fd7ac3d00c737de5462a74fe6788672f739a998769244dfb319bacaae + languageName: node + linkType: hard + +"@vue/compiler-core@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/compiler-core@npm:3.4.27" + dependencies: + "@babel/parser": "npm:^7.24.4" + "@vue/shared": "npm:3.4.27" + entities: "npm:^4.5.0" + estree-walker: "npm:^2.0.2" + source-map-js: "npm:^1.2.0" + checksum: 10c0/fbc9a4a6c467fa47609df3337c1b2012a55e3b07adbffc45a31435237ec1169d0a4ece22f3538607364427b779ce04154b86a0e8dd40d3bd4aa03358d4db136d + languageName: node + linkType: hard + +"@vue/compiler-dom@npm:3.4.24, @vue/compiler-dom@npm:^3.3.4, @vue/compiler-dom@npm:^3.4.0": + version: 3.4.24 + resolution: "@vue/compiler-dom@npm:3.4.24" + dependencies: + "@vue/compiler-core": "npm:3.4.24" + "@vue/shared": "npm:3.4.24" + checksum: 10c0/1b509534f69a9132698bf8680652e2feb29ce345271f36ba1d86604838242afce760cf1d2ad52e9ff305d74f86b170a80a1f30af07669005ca3f7e60c07fc005 + languageName: node + linkType: hard + +"@vue/compiler-dom@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/compiler-dom@npm:3.4.27" + dependencies: + "@vue/compiler-core": "npm:3.4.27" + "@vue/shared": "npm:3.4.27" + checksum: 10c0/ceb8aef314b6b7df1ab6cd3c7c1290e5b60363a6092bbffc3ee6aca42f6f5247a070b0dcbe71530751e840d01beec00a6268e3663abcf4a6ac297a32bfb90e49 + languageName: node + linkType: hard + +"@vue/compiler-dom@npm:^3.3.0": + version: 3.4.26 + resolution: "@vue/compiler-dom@npm:3.4.26" + dependencies: + "@vue/compiler-core": "npm:3.4.26" + "@vue/shared": "npm:3.4.26" + checksum: 10c0/3b39c5586ba0029ccef8cd6d7f7b98e1522ca8f52edba056bc46a8ec27764a6fdec2619bbe089484586cf440c07249631c9c18efe91633306cf0047f4d76d72f + languageName: node + linkType: hard + +"@vue/compiler-sfc@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/compiler-sfc@npm:3.4.27" + dependencies: + "@babel/parser": "npm:^7.24.4" + "@vue/compiler-core": "npm:3.4.27" + "@vue/compiler-dom": "npm:3.4.27" + "@vue/compiler-ssr": "npm:3.4.27" + "@vue/shared": "npm:3.4.27" + estree-walker: "npm:^2.0.2" + magic-string: "npm:^0.30.10" + postcss: "npm:^8.4.38" + source-map-js: "npm:^1.2.0" + checksum: 10c0/2ccb852c521bf799cf2b118ee8d2aa0eeaaaab1a2e8d3a4a0bd9db5aaccb6224d6673c0c8e39ff8a04e3a99b21128bdaa6ee643e08562af36d75803801cfd641 + languageName: node + linkType: hard + +"@vue/compiler-sfc@npm:^3.4.15": + version: 3.4.24 + resolution: "@vue/compiler-sfc@npm:3.4.24" + dependencies: + "@babel/parser": "npm:^7.24.4" + "@vue/compiler-core": "npm:3.4.24" + "@vue/compiler-dom": "npm:3.4.24" + "@vue/compiler-ssr": "npm:3.4.24" + "@vue/shared": "npm:3.4.24" + estree-walker: "npm:^2.0.2" + magic-string: "npm:^0.30.10" + postcss: "npm:^8.4.38" + source-map-js: "npm:^1.2.0" + checksum: 10c0/385e8499501b2eb30c630794d1cb615a43902efa9bd7749d6ed9a306e3fb37d021be9680aa3fce18e8e36b939b084f03af654bd63aaf549dd8837733f26f2ed4 + languageName: node + linkType: hard + +"@vue/compiler-ssr@npm:3.4.24": + version: 3.4.24 + resolution: "@vue/compiler-ssr@npm:3.4.24" + dependencies: + "@vue/compiler-dom": "npm:3.4.24" + "@vue/shared": "npm:3.4.24" + checksum: 10c0/eeeb1a99eba1e49a1258cc2cfa67979bfb7e5b1b20bbf6684ea6a1449a7367b2c638e0eefc1afa28f982f92469c40ab5e419dd5232f9d735771d204df8f4b667 + languageName: node + linkType: hard + +"@vue/compiler-ssr@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/compiler-ssr@npm:3.4.27" + dependencies: + "@vue/compiler-dom": "npm:3.4.27" + "@vue/shared": "npm:3.4.27" + checksum: 10c0/5c51a43481e5faa3f4e66a01a19a5de8a0c25db5df25183d7f9227853740d8ea75c12b1b89f47198f840de852d2e4c258be114528c0c322aff50c5982a973e1f + languageName: node + linkType: hard + +"@vue/devtools-api@npm:^6.5.1": + version: 6.6.1 + resolution: "@vue/devtools-api@npm:6.6.1" + checksum: 10c0/ab9a1e09baae514b0d3a8bf1d670ecb7724f7e55b82eea30aa2e7255e6200b45c1086c1376560f243cf86e98c0726e94d69cefe0ad23dfd50c7c49dfcb1fbf21 + languageName: node + linkType: hard + +"@vue/devtools-core@npm:^7.2.0": + version: 7.2.0 + resolution: "@vue/devtools-core@npm:7.2.0" + dependencies: + "@vue/devtools-kit": "npm:^7.2.0" + "@vue/devtools-shared": "npm:^7.2.0" + mitt: "npm:^3.0.1" + nanoid: "npm:^3.3.4" + pathe: "npm:^1.1.2" + vite-hot-client: "npm:^0.2.3" + checksum: 10c0/43e9b829ae36c2b7d78a93559e45d49cfa484db143f04f56f8330ed7a4bf3287106cea2b6b1c9b2f6418c394d8a73ae662ac8b7750b464eee6e486c48883218d + languageName: node + linkType: hard + +"@vue/devtools-kit@npm:^7.2.0": + version: 7.2.0 + resolution: "@vue/devtools-kit@npm:7.2.0" + dependencies: + "@vue/devtools-shared": "npm:^7.2.0" + hookable: "npm:^5.5.3" + mitt: "npm:^3.0.1" + perfect-debounce: "npm:^1.0.0" + speakingurl: "npm:^14.0.1" + peerDependencies: + vue: ^3.0.0 + checksum: 10c0/1434eb3329c8c60ed5c9446ce2064ad671df4fe966280ec6a24c9635262a89622f8bd0c9763d0c33e12bcd08dbe0c2663037a69256f0385b2700c3dca972c17b + languageName: node + linkType: hard + +"@vue/devtools-shared@npm:^7.2.0": + version: 7.2.0 + resolution: "@vue/devtools-shared@npm:7.2.0" + dependencies: + rfdc: "npm:^1.3.1" + checksum: 10c0/d5450ed0d1543d181eeb4453f0fd561cea959838f31dd9449a686accf9d659cec3a227c0a5b6728cedcff819aedf7d85a232b06ba3fcaa86a5d36713a084931b + languageName: node + linkType: hard + +"@vue/eslint-config-prettier@npm:^9.0.0": + version: 9.0.0 + resolution: "@vue/eslint-config-prettier@npm:9.0.0" + dependencies: + eslint-config-prettier: "npm:^9.0.0" + eslint-plugin-prettier: "npm:^5.0.0" + peerDependencies: + eslint: ">= 8.0.0" + prettier: ">= 3.0.0" + checksum: 10c0/465fe43e7a4f3181e73298df55cc5ba1de4e4349b6d859ef38e52f82406a0eac938809721b3a0f026b56ae533d51b45c4a2ecb6112d78e8d6e6ee3ca0892bba6 + languageName: node + linkType: hard + +"@vue/eslint-config-typescript@npm:^13.0.0": + version: 13.0.0 + resolution: "@vue/eslint-config-typescript@npm:13.0.0" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:^7.1.1" + "@typescript-eslint/parser": "npm:^7.1.1" + vue-eslint-parser: "npm:^9.3.1" + peerDependencies: + eslint: ^8.56.0 + eslint-plugin-vue: ^9.0.0 + typescript: ">=4.7.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/a960e1157f805e38837107ae8d2a6d280cf8f7cc3fa9ab492e10f52622085076637ee92438ca473c4e3305097448417f66c0d6cbf6130b731dbac9189e4566ca + languageName: node + linkType: hard + +"@vue/language-core@npm:1.8.27, @vue/language-core@npm:^1.8.27": + version: 1.8.27 + resolution: "@vue/language-core@npm:1.8.27" + dependencies: + "@volar/language-core": "npm:~1.11.1" + "@volar/source-map": "npm:~1.11.1" + "@vue/compiler-dom": "npm:^3.3.0" + "@vue/shared": "npm:^3.3.0" + computeds: "npm:^0.0.1" + minimatch: "npm:^9.0.3" + muggle-string: "npm:^0.3.1" + path-browserify: "npm:^1.0.1" + vue-template-compiler: "npm:^2.7.14" + peerDependencies: + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/2018214d8ce2643d19e8e84eddaeacddca28b2980984d7916d97f97556c3716be184cf9f8c4f506d072a11f265401e3bc0391117cf7cfcc1e4a25048f4432dc7 + languageName: node + linkType: hard + +"@vue/language-core@npm:2.0.19": + version: 2.0.19 + resolution: "@vue/language-core@npm:2.0.19" + dependencies: + "@volar/language-core": "npm:~2.2.4" + "@vue/compiler-dom": "npm:^3.4.0" + "@vue/shared": "npm:^3.4.0" + computeds: "npm:^0.0.1" + minimatch: "npm:^9.0.3" + path-browserify: "npm:^1.0.1" + vue-template-compiler: "npm:^2.7.14" + peerDependencies: + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/2922c45ba2da13b00f85bf2ac4deaa50396ffa1737e9574ddddcbf411960f7e8b5225ba68dc056ba5b08030dd30ab057d4f7478103e0f44de0d7df14898d595c + languageName: node + linkType: hard + +"@vue/reactivity@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/reactivity@npm:3.4.27" + dependencies: + "@vue/shared": "npm:3.4.27" + checksum: 10c0/5a30fa92cb1b467f56c467d9851a9d594475c80952a600db444c38a8fe2dfc53e4aa09fed6b0e6074eca667c915c730d02b386be26d5f7a0565e70ae04fe92b7 + languageName: node + linkType: hard + +"@vue/runtime-core@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/runtime-core@npm:3.4.27" + dependencies: + "@vue/reactivity": "npm:3.4.27" + "@vue/shared": "npm:3.4.27" + checksum: 10c0/dc02dfefebeec49c6b8aab9e133551b6cedef3c55e7441732a696aba66b865945549ba0f92a97a0f4ab080b828bca2cc2ce669ad7c6d2ee129d5050948f03817 + languageName: node + linkType: hard + +"@vue/runtime-dom@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/runtime-dom@npm:3.4.27" + dependencies: + "@vue/runtime-core": "npm:3.4.27" + "@vue/shared": "npm:3.4.27" + csstype: "npm:^3.1.3" + checksum: 10c0/2ace60cab29400c4d466b6743552ae3af360f908d7716316c23a641bd5adce7aa05d2b4522ecf3b6b2f912bb525c8e055708db11791e50aea24ff6b2a71e0a8e + languageName: node + linkType: hard + +"@vue/server-renderer@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/server-renderer@npm:3.4.27" + dependencies: + "@vue/compiler-ssr": "npm:3.4.27" + "@vue/shared": "npm:3.4.27" + peerDependencies: + vue: 3.4.27 + checksum: 10c0/5e6761ecd74c0a9ca9fd991f7a980140d2e09427712dbdc74b536bc5a9b97c06825ca4fa006b4a7cd6ba224fdb13c1c6a600e7d039d2a40f036b13ed611aa20f + languageName: node + linkType: hard + +"@vue/shared@npm:3.4.24, @vue/shared@npm:^3.4.0": + version: 3.4.24 + resolution: "@vue/shared@npm:3.4.24" + checksum: 10c0/953373e0a5f7bd518d0804d06be93ec2a0dda2a5294f1c1c2519a223dd760a3215a2012743286b103cd9bea5502f4dadcea01c600951b387066fb036ceeb8633 + languageName: node + linkType: hard + +"@vue/shared@npm:3.4.26, @vue/shared@npm:^3.3.0": + version: 3.4.26 + resolution: "@vue/shared@npm:3.4.26" + checksum: 10c0/8b002badc842d94b47cf78630ca51403a8a8981bc20c35f5b669c299413f81ed5fd97dbd763d0c00fe4614c615991214bc06d709d0b57d1e5836d0262cfbebdb + languageName: node + linkType: hard + +"@vue/shared@npm:3.4.27": + version: 3.4.27 + resolution: "@vue/shared@npm:3.4.27" + checksum: 10c0/4a21918858270bcc654bb94b3429d9acbe95af097ea3063e192b36bd502dc896ca47778fa74a863b01f677ec271b189eb90f8b372943c10e52725a6bdc7f6cd5 + languageName: node + linkType: hard + +"@vue/test-utils@npm:^2.4.6": + version: 2.4.6 + resolution: "@vue/test-utils@npm:2.4.6" + dependencies: + js-beautify: "npm:^1.14.9" + vue-component-type-helpers: "npm:^2.0.0" + checksum: 10c0/37fa46cb6b98f90affb2faf5aa41422617bbd23ff35bc714d08035334e593ae31d18757d5ae688f778dd8b4c28de431601c0b9b7ca17fc1b55f1401a5577375e + languageName: node + linkType: hard + +"@vue/tsconfig@npm:^0.5.1": + version: 0.5.1 + resolution: "@vue/tsconfig@npm:0.5.1" + checksum: 10c0/cf4049c0daf26579e42803eca661809f95f9196323b4a6aec1714ac3c80df37b44d3878468f56e2f3d1fe64640fe9cb42f20db42509ef858ab059fe05e0dd862 + languageName: node + linkType: hard + +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372 + languageName: node + linkType: hard + +"acorn-jsx@npm:^5.3.2": + version: 5.3.2 + resolution: "acorn-jsx@npm:5.3.2" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10c0/4c54868fbef3b8d58927d5e33f0a4de35f59012fe7b12cf9dfbb345fb8f46607709e1c4431be869a23fb63c151033d84c4198fa9f79385cec34fcb1dd53974c1 + languageName: node + linkType: hard + +"acorn-walk@npm:^8.1.1, acorn-walk@npm:^8.3.2": + version: 8.3.2 + resolution: "acorn-walk@npm:8.3.2" + checksum: 10c0/7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52 + languageName: node + linkType: hard + +"acorn@npm:^8.11.3, acorn@npm:^8.4.1, acorn@npm:^8.9.0": + version: 8.11.3 + resolution: "acorn@npm:8.11.3" + bin: + acorn: bin/acorn + checksum: 10c0/3ff155f8812e4a746fee8ecff1f227d527c4c45655bb1fad6347c3cb58e46190598217551b1500f18542d2bbe5c87120cb6927f5a074a59166fbdd9468f0a299 + languageName: node + linkType: hard + +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" + dependencies: + debug: "npm:^4.3.4" + checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50 + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: "npm:^2.0.0" + indent-string: "npm:^4.0.0" + checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039 + languageName: node + linkType: hard + +"ajv@npm:^6.12.4, ajv@npm:~6.12.6": + version: 6.12.6 + resolution: "ajv@npm:6.12.6" + dependencies: + fast-deep-equal: "npm:^3.1.1" + fast-json-stable-stringify: "npm:^2.0.0" + json-schema-traverse: "npm:^0.4.1" + uri-js: "npm:^4.2.2" + checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08 + languageName: node + linkType: hard + +"ansi-sequence-parser@npm:^1.1.0": + version: 1.1.1 + resolution: "ansi-sequence-parser@npm:1.1.1" + checksum: 10c0/ab2259ccf69f145ecf1418d4e71524158828f44afdf37c7536677871f4cebaa8b176fcb95de8f94a68129357dddc59586597da25f9d4ebf9968f6ef022bf0b31 + languageName: node + linkType: hard + +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: "npm:^1.9.0" + checksum: 10c0/ece5a8ef069fcc5298f67e3f4771a663129abd174ea2dfa87923a2be2abf6cd367ef72ac87942da00ce85bd1d651d4cd8595aebdb1b385889b89b205860e977b + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df + languageName: node + linkType: hard + +"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c + languageName: node + linkType: hard + +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + +"anymatch@npm:~3.1.2": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" + dependencies: + normalize-path: "npm:^3.0.0" + picomatch: "npm:^2.0.4" + checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac + languageName: node + linkType: hard + +"arg@npm:^4.1.0": + version: 4.1.3 + resolution: "arg@npm:4.1.3" + checksum: 10c0/070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a + languageName: node + linkType: hard + +"arg@npm:^5.0.2": + version: 5.0.2 + resolution: "arg@npm:5.0.2" + checksum: 10c0/ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + +"argparse@npm:~1.0.9": + version: 1.0.10 + resolution: "argparse@npm:1.0.10" + dependencies: + sprintf-js: "npm:~1.0.2" + checksum: 10c0/b2972c5c23c63df66bca144dbc65d180efa74f25f8fd9b7d9a0a6c88ae839db32df3d54770dcb6460cf840d232b60695d1a6b1053f599d84e73f7437087712de + languageName: node + linkType: hard + +"array-buffer-byte-length@npm:^1.0.0": + version: 1.0.1 + resolution: "array-buffer-byte-length@npm:1.0.1" + dependencies: + call-bind: "npm:^1.0.5" + is-array-buffer: "npm:^3.0.4" + checksum: 10c0/f5cdf54527cd18a3d2852ddf73df79efec03829e7373a8322ef5df2b4ef546fb365c19c71d6b42d641cb6bfe0f1a2f19bc0ece5b533295f86d7c3d522f228917 + languageName: node + linkType: hard + +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 10c0/429897e68110374f39b771ec47a7161fc6a8fc33e196857c0a396dc75df0b5f65e4d046674db764330b6bb66b39ef48dd7c53b6a2ee75cfb0681e0c1a7033962 + languageName: node + linkType: hard + +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: 10c0/25456b2aa333250f01143968e02e4884a34588a8538fbbf65c91a637f1dbfb8069249133cd2f4e530f10f624d206a664e7df30207830b659e9f5298b00a4099b + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d + languageName: node + linkType: hard + +"autoprefixer@npm:^10.4.19": + version: 10.4.19 + resolution: "autoprefixer@npm:10.4.19" + dependencies: + browserslist: "npm:^4.23.0" + caniuse-lite: "npm:^1.0.30001599" + fraction.js: "npm:^4.3.7" + normalize-range: "npm:^0.1.2" + picocolors: "npm:^1.0.0" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.1.0 + bin: + autoprefixer: bin/autoprefixer + checksum: 10c0/fe0178eb8b1da4f15c6535cd329926609b22d1811e047371dccce50563623f8075dd06fb167daff059e4228da651b0bdff6d9b44281541eaf0ce0b79125bfd19 + languageName: node + linkType: hard + +"available-typed-arrays@npm:^1.0.7": + version: 1.0.7 + resolution: "available-typed-arrays@npm:1.0.7" + dependencies: + possible-typed-array-names: "npm:^1.0.0" + checksum: 10c0/d07226ef4f87daa01bd0fe80f8f310982e345f372926da2e5296aecc25c41cab440916bbaa4c5e1034b453af3392f67df5961124e4b586df1e99793a1374bdb2 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"binary-extensions@npm:^2.0.0": + version: 2.3.0 + resolution: "binary-extensions@npm:2.3.0" + checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 + languageName: node + linkType: hard + +"binary-split@npm:^1.0.5": + version: 1.0.5 + resolution: "binary-split@npm:1.0.5" + dependencies: + through2: "npm:^2.0.3" + checksum: 10c0/8529b554f7c3771bda628f1627a52f44f831f3902301983c8901e3f45317416632a9893e766b82b1e53817524c88dcc4713760792e093191850f1c7e0043daf1 + languageName: node + linkType: hard + +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 10c0/e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f + languageName: node + linkType: hard + +"braces@npm:^3.0.2, braces@npm:~3.0.2": + version: 3.0.2 + resolution: "braces@npm:3.0.2" + dependencies: + fill-range: "npm:^7.0.1" + checksum: 10c0/321b4d675791479293264019156ca322163f02dc06e3c4cab33bb15cd43d80b51efef69b0930cfde3acd63d126ebca24cd0544fa6f261e093a0fb41ab9dda381 + languageName: node + linkType: hard + +"browserslist@npm:^4.22.2, browserslist@npm:^4.23.0": + version: 4.23.0 + resolution: "browserslist@npm:4.23.0" + dependencies: + caniuse-lite: "npm:^1.0.30001587" + electron-to-chromium: "npm:^1.4.668" + node-releases: "npm:^2.0.14" + update-browserslist-db: "npm:^1.0.13" + bin: + browserslist: cli.js + checksum: 10c0/8e9cc154529062128d02a7af4d8adeead83ca1df8cd9ee65a88e2161039f3d68a4d40fea7353cab6bae4c16182dec2fdd9a1cf7dc2a2935498cee1af0e998943 + languageName: node + linkType: hard + +"bundle-name@npm:^4.1.0": + version: 4.1.0 + resolution: "bundle-name@npm:4.1.0" + dependencies: + run-applescript: "npm:^7.0.0" + checksum: 10c0/8e575981e79c2bcf14d8b1c027a3775c095d362d1382312f444a7c861b0e21513c0bd8db5bd2b16e50ba0709fa622d4eab6b53192d222120305e68359daece29 + languageName: node + linkType: hard + +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 + languageName: node + linkType: hard + +"cacache@npm:^18.0.0": + version: 18.0.2 + resolution: "cacache@npm:18.0.2" + dependencies: + "@npmcli/fs": "npm:^3.1.0" + fs-minipass: "npm:^3.0.0" + glob: "npm:^10.2.2" + lru-cache: "npm:^10.0.1" + minipass: "npm:^7.0.3" + minipass-collect: "npm:^2.0.1" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + p-map: "npm:^4.0.0" + ssri: "npm:^10.0.0" + tar: "npm:^6.1.11" + unique-filename: "npm:^3.0.0" + checksum: 10c0/7992665305cc251a984f4fdbab1449d50e88c635bc43bf2785530c61d239c61b349e5734461baa461caaee65f040ab14e2d58e694f479c0810cffd181ba5eabc + languageName: node + linkType: hard + +"call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: 10c0/a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 + languageName: node + linkType: hard + +"camelcase-css@npm:^2.0.1": + version: 2.0.1 + resolution: "camelcase-css@npm:2.0.1" + checksum: 10c0/1a1a3137e8a781e6cbeaeab75634c60ffd8e27850de410c162cce222ea331cd1ba5364e8fb21c95e5ca76f52ac34b81a090925ca00a87221355746d049c6e273 + languageName: node + linkType: hard + +"camelcase@npm:^6.3.0": + version: 6.3.0 + resolution: "camelcase@npm:6.3.0" + checksum: 10c0/0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001587": + version: 1.0.30001612 + resolution: "caniuse-lite@npm:1.0.30001612" + checksum: 10c0/d6b405ff06f4e913bc779f9183fa68001c9d6b8526a7dd1b99c60587dd21a01aa8def3d8462cf6214f0181f1c21b9245611ff65241cf9c967fc742e86ece5065 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001599": + version: 1.0.30001614 + resolution: "caniuse-lite@npm:1.0.30001614" + checksum: 10c0/54873ef47f5f04448f1dafe371dece0129f1f2e4644c377956e66ebfa4bfa16dd3529062b7b4d95d9e3a80129907558c19200532669c6dcd5733eff724fdf2b6 + languageName: node + linkType: hard + +"chai@npm:^4.3.10": + version: 4.4.1 + resolution: "chai@npm:4.4.1" + dependencies: + assertion-error: "npm:^1.1.0" + check-error: "npm:^1.0.3" + deep-eql: "npm:^4.1.3" + get-func-name: "npm:^2.0.2" + loupe: "npm:^2.3.6" + pathval: "npm:^1.1.1" + type-detect: "npm:^4.0.8" + checksum: 10c0/91590a8fe18bd6235dece04ccb2d5b4ecec49984b50924499bdcd7a95c02cb1fd2a689407c19bb854497bde534ef57525cfad6c7fdd2507100fd802fbc2aefbd + languageName: node + linkType: hard + +"chalk@npm:^2.4.2": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: "npm:^3.2.1" + escape-string-regexp: "npm:^1.0.5" + supports-color: "npm:^5.3.0" + checksum: 10c0/e6543f02ec877732e3a2d1c3c3323ddb4d39fbab687c23f526e25bd4c6a9bf3b83a696e8c769d078e04e5754921648f7821b2a2acfd16c550435fd630026e073 + languageName: node + linkType: hard + +"chalk@npm:^4.0.0": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 + languageName: node + linkType: hard + +"check-error@npm:^1.0.3": + version: 1.0.3 + resolution: "check-error@npm:1.0.3" + dependencies: + get-func-name: "npm:^2.0.2" + checksum: 10c0/94aa37a7315c0e8a83d0112b5bfb5a8624f7f0f81057c73e4707729cdd8077166c6aefb3d8e2b92c63ee130d4a2ff94bad46d547e12f3238cc1d78342a973841 + languageName: node + linkType: hard + +"chokidar@npm:^3.5.3": + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6 + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: "npm:1.1.3" + checksum: 10c0/5ad3c534949a8c68fca8fbc6f09068f435f0ad290ab8b2f76841b9e6af7e0bb57b98cb05b0e19fe33f5d91e5a8611ad457e5f69e0a484caad1f7487fd0e8253c + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 10c0/566a3d42cca25b9b3cd5528cd7754b8e89c0eb646b7f214e8e2eaddb69994ac5f0557d9c175eb5d8f0ad73531140d9c47525085ee752a91a2ab15ab459caf6d6 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: "npm:~1.0.0" + checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5 + languageName: node + linkType: hard + +"commander@npm:^10.0.0": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 10c0/53f33d8927758a911094adadda4b2cbac111a5b377d8706700587650fd8f45b0bbe336de4b5c3fe47fd61f420a3d9bd452b6e0e6e5600a7e74d7bf0174f6efe3 + languageName: node + linkType: hard + +"commander@npm:^4.0.0": + version: 4.1.1 + resolution: "commander@npm:4.1.1" + checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab + languageName: node + linkType: hard + +"commander@npm:^9.4.1": + version: 9.5.0 + resolution: "commander@npm:9.5.0" + checksum: 10c0/5f7784fbda2aaec39e89eb46f06a999e00224b3763dc65976e05929ec486e174fe9aac2655f03ba6a5e83875bd173be5283dc19309b7c65954701c02025b3c1d + languageName: node + linkType: hard + +"computeds@npm:^0.0.1": + version: 0.0.1 + resolution: "computeds@npm:0.0.1" + checksum: 10c0/8a8736f1f43e4a99286519785d71a10ece8f444a2fa1fc2fe1f03dedf63f3477b45094002c85a2826f7631759c9f5a00b4ace47456997f253073fc525e8983de + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f + languageName: node + linkType: hard + +"confbox@npm:^0.1.7": + version: 0.1.7 + resolution: "confbox@npm:0.1.7" + checksum: 10c0/18b40c2f652196a833f3f1a5db2326a8a579cd14eacabfe637e4fc8cb9b68d7cf296139a38c5e7c688ce5041bf46f9adce05932d43fde44cf7e012840b5da111 + languageName: node + linkType: hard + +"config-chain@npm:^1.1.13": + version: 1.1.13 + resolution: "config-chain@npm:1.1.13" + dependencies: + ini: "npm:^1.3.4" + proto-list: "npm:~1.2.1" + checksum: 10c0/39d1df18739d7088736cc75695e98d7087aea43646351b028dfabd5508d79cf6ef4c5bcd90471f52cd87ae470d1c5490c0a8c1a292fbe6ee9ff688061ea0963e + languageName: node + linkType: hard + +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 10c0/90a0e40abbddfd7618f8ccd63a74d88deea94e77d0e8dbbea059fa7ebebb8fbb4e2909667fe26f3a467073de1a542ebe6ae4c73a73745ac5833786759cd906c9 + languageName: node + linkType: hard + +"create-require@npm:^1.1.0": + version: 1.1.1 + resolution: "create-require@npm:1.1.1" + checksum: 10c0/157cbc59b2430ae9a90034a5f3a1b398b6738bf510f713edc4d4e45e169bc514d3d99dd34d8d01ca7ae7830b5b8b537e46ae8f3c8f932371b0875c0151d7ec91 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-spawn@npm:7.0.3" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750 + languageName: node + linkType: hard + +"cssesc@npm:^3.0.0": + version: 3.0.0 + resolution: "cssesc@npm:3.0.0" + bin: + cssesc: bin/cssesc + checksum: 10c0/6bcfd898662671be15ae7827120472c5667afb3d7429f1f917737f3bf84c4176003228131b643ae74543f17a394446247df090c597bb9a728cce298606ed0aa7 + languageName: node + linkType: hard + +"cssstyle@npm:^4.0.1": + version: 4.0.1 + resolution: "cssstyle@npm:4.0.1" + dependencies: + rrweb-cssom: "npm:^0.6.0" + checksum: 10c0/cadf9a8b23e11f4c6d63f21291096a0b0be868bd4ab9c799daa2c5b18330e39e5281605f01da906e901b42f742df0f3b3645af6465e83377ff7d15a88ee432a0 + languageName: node + linkType: hard + +"csstype@npm:^3.1.3": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 10c0/80c089d6f7e0c5b2bd83cf0539ab41474198579584fa10d86d0cafe0642202343cbc119e076a0b1aece191989477081415d66c9fefbf3c957fc2fc4b7009f248 + languageName: node + linkType: hard + +"data-urls@npm:^5.0.0": + version: 5.0.0 + resolution: "data-urls@npm:5.0.0" + dependencies: + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + checksum: 10c0/1b894d7d41c861f3a4ed2ae9b1c3f0909d4575ada02e36d3d3bc584bdd84278e20709070c79c3b3bff7ac98598cb191eb3e86a89a79ea4ee1ef360e1694f92ad + languageName: node + linkType: hard + +"dbly-linked-list@npm:0.3.4": + version: 0.3.4 + resolution: "dbly-linked-list@npm:0.3.4" + dependencies: + lodash.isequal: "npm:^4.5.0" + checksum: 10c0/092f85b2ca3d9357ec98fbb1d9744419f4f62fb31c6d3952f5bdc986daf236f94a72c8dd71245dda005cf6b2429b6f882327074a95be34e7f0871fe588280c19 + languageName: node + linkType: hard + +"de-indent@npm:^1.0.2": + version: 1.0.2 + resolution: "de-indent@npm:1.0.2" + checksum: 10c0/7058ce58abd6dfc123dd204e36be3797abd419b59482a634605420f47ae97639d0c183ec5d1b904f308a01033f473673897afc2bd59bc620ebf1658763ef4291 + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: "npm:2.1.2" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736 + languageName: node + linkType: hard + +"decimal.js@npm:^10.4.3": + version: 10.4.3 + resolution: "decimal.js@npm:10.4.3" + checksum: 10c0/6d60206689ff0911f0ce968d40f163304a6c1bc739927758e6efc7921cfa630130388966f16bf6ef6b838cb33679fbe8e7a78a2f3c478afce841fd55ac8fb8ee + languageName: node + linkType: hard + +"decorator-cache-getter@npm:^1.0.0": + version: 1.0.0 + resolution: "decorator-cache-getter@npm:1.0.0" + checksum: 10c0/bfffb9ef9a84170c14ce504eff3890b9d4605c057b37f21169c788cba85ef7af34eef4b986b1272da6272419192fd551e9154ca78b2182c70c33d2106b700e6b + languageName: node + linkType: hard + +"deep-eql@npm:^4.1.3": + version: 4.1.3 + resolution: "deep-eql@npm:4.1.3" + dependencies: + type-detect: "npm:^4.0.0" + checksum: 10c0/ff34e8605d8253e1bf9fe48056e02c6f347b81d9b5df1c6650a1b0f6f847b4a86453b16dc226b34f853ef14b626e85d04e081b022e20b00cd7d54f079ce9bbdd + languageName: node + linkType: hard + +"deep-equal@npm:^2.2.3": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.0" + call-bind: "npm:^1.0.5" + es-get-iterator: "npm:^1.1.3" + get-intrinsic: "npm:^1.2.2" + is-arguments: "npm:^1.1.1" + is-array-buffer: "npm:^3.0.2" + is-date-object: "npm:^1.0.5" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.2" + isarray: "npm:^2.0.5" + object-is: "npm:^1.1.5" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.4" + regexp.prototype.flags: "npm:^1.5.1" + side-channel: "npm:^1.0.4" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.1" + which-typed-array: "npm:^1.1.13" + checksum: 10c0/a48244f90fa989f63ff5ef0cc6de1e4916b48ea0220a9c89a378561960814794a5800c600254482a2c8fd2e49d6c2e196131dc983976adb024c94a42dfe4949f + languageName: node + linkType: hard + +"deep-is@npm:^0.1.3": + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c + languageName: node + linkType: hard + +"default-browser-id@npm:^5.0.0": + version: 5.0.0 + resolution: "default-browser-id@npm:5.0.0" + checksum: 10c0/957fb886502594c8e645e812dfe93dba30ed82e8460d20ce39c53c5b0f3e2afb6ceaec2249083b90bdfbb4cb0f34e1f73fde3d68cac00becdbcfd894156b5ead + languageName: node + linkType: hard + +"default-browser@npm:^5.2.1": + version: 5.2.1 + resolution: "default-browser@npm:5.2.1" + dependencies: + bundle-name: "npm:^4.1.0" + default-browser-id: "npm:^5.0.0" + checksum: 10c0/73f17dc3c58026c55bb5538749597db31f9561c0193cd98604144b704a981c95a466f8ecc3c2db63d8bfd04fb0d426904834cfc91ae510c6aeb97e13c5167c4d + languageName: node + linkType: hard + +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + +"define-lazy-prop@npm:^3.0.0": + version: 3.0.0 + resolution: "define-lazy-prop@npm:3.0.0" + checksum: 10c0/5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49 + languageName: node + linkType: hard + +"define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" + dependencies: + define-data-property: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.0" + object-keys: "npm:^1.1.1" + checksum: 10c0/88a152319ffe1396ccc6ded510a3896e77efac7a1bfbaa174a7b00414a1747377e0bb525d303794a47cf30e805c2ec84e575758512c6e44a993076d29fd4e6c3 + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19 + languageName: node + linkType: hard + +"didyoumean@npm:^1.2.2": + version: 1.2.2 + resolution: "didyoumean@npm:1.2.2" + checksum: 10c0/95d0b53d23b851aacff56dfadb7ecfedce49da4232233baecfeecb7710248c4aa03f0aa8995062f0acafaf925adf8536bd7044a2e68316fd7d411477599bc27b + languageName: node + linkType: hard + +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: 10c0/32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 + languageName: node + linkType: hard + +"diff@npm:^4.0.1": + version: 4.0.2 + resolution: "diff@npm:4.0.2" + checksum: 10c0/81b91f9d39c4eaca068eb0c1eb0e4afbdc5bb2941d197f513dd596b820b956fef43485876226d65d497bebc15666aa2aa82c679e84f65d5f2bfbf14ee46e32c1 + languageName: node + linkType: hard + +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: "npm:^4.0.0" + checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c + languageName: node + linkType: hard + +"dlv@npm:^1.1.3": + version: 1.1.3 + resolution: "dlv@npm:1.1.3" + checksum: 10c0/03eb4e769f19a027fd5b43b59e8a05e3fd2100ac239ebb0bf9a745de35d449e2f25cfaf3aa3934664551d72856f4ae8b7822016ce5c42c2d27c18ae79429ec42 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"editorconfig@npm:^1.0.4": + version: 1.0.4 + resolution: "editorconfig@npm:1.0.4" + dependencies: + "@one-ini/wasm": "npm:0.1.1" + commander: "npm:^10.0.0" + minimatch: "npm:9.0.1" + semver: "npm:^7.5.3" + bin: + editorconfig: bin/editorconfig + checksum: 10c0/ed6985959d7b34a56e1c09bef118758c81c969489b768d152c93689fce8403b0452462e934f665febaba3478eebc0fd41c0a36100783eaadf6d926c4abc87a3d + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.4.668": + version: 1.4.747 + resolution: "electron-to-chromium@npm:1.4.747" + checksum: 10c0/2d8bded9bb6b5f204699330979a5675578ed890ee34b631e28af81245c5032ba3bd42064aeded14f0d82239607f65e4c34890dad445b3ceafdbb5e14352faf9a + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: "npm:^0.6.2" + checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 + languageName: node + linkType: hard + +"entities@npm:^4.4.0, entities@npm:^4.5.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 + languageName: node + linkType: hard + +"error-stack-parser-es@npm:^0.1.1": + version: 0.1.1 + resolution: "error-stack-parser-es@npm:0.1.1" + checksum: 10c0/2f94dd2d78f90f148d47a1df38704a3979c8ae2dd16edec7a1e80ff243fd9f6b33c10f0a06cb6d8e53768316984adad8ebfd465088c824692b5ec0d0ab39e51c + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-get-iterator@npm:^1.1.3": + version: 1.1.3 + resolution: "es-get-iterator@npm:1.1.3" + dependencies: + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.1.3" + has-symbols: "npm:^1.0.3" + is-arguments: "npm:^1.1.1" + is-map: "npm:^2.0.2" + is-set: "npm:^2.0.2" + is-string: "npm:^1.0.7" + isarray: "npm:^2.0.5" + stop-iteration-iterator: "npm:^1.0.0" + checksum: 10c0/ebd11effa79851ea75d7f079405f9d0dc185559fd65d986c6afea59a0ff2d46c2ed8675f19f03dce7429d7f6c14ff9aede8d121fbab78d75cfda6a263030bac0 + languageName: node + linkType: hard + +"esbuild@npm:^0.20.1": + version: 0.20.2 + resolution: "esbuild@npm:0.20.2" + dependencies: + "@esbuild/aix-ppc64": "npm:0.20.2" + "@esbuild/android-arm": "npm:0.20.2" + "@esbuild/android-arm64": "npm:0.20.2" + "@esbuild/android-x64": "npm:0.20.2" + "@esbuild/darwin-arm64": "npm:0.20.2" + "@esbuild/darwin-x64": "npm:0.20.2" + "@esbuild/freebsd-arm64": "npm:0.20.2" + "@esbuild/freebsd-x64": "npm:0.20.2" + "@esbuild/linux-arm": "npm:0.20.2" + "@esbuild/linux-arm64": "npm:0.20.2" + "@esbuild/linux-ia32": "npm:0.20.2" + "@esbuild/linux-loong64": "npm:0.20.2" + "@esbuild/linux-mips64el": "npm:0.20.2" + "@esbuild/linux-ppc64": "npm:0.20.2" + "@esbuild/linux-riscv64": "npm:0.20.2" + "@esbuild/linux-s390x": "npm:0.20.2" + "@esbuild/linux-x64": "npm:0.20.2" + "@esbuild/netbsd-x64": "npm:0.20.2" + "@esbuild/openbsd-x64": "npm:0.20.2" + "@esbuild/sunos-x64": "npm:0.20.2" + "@esbuild/win32-arm64": "npm:0.20.2" + "@esbuild/win32-ia32": "npm:0.20.2" + "@esbuild/win32-x64": "npm:0.20.2" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/66398f9fb2c65e456a3e649747b39af8a001e47963b25e86d9c09d2a48d61aa641b27da0ce5cad63df95ad246105e1d83e7fee0e1e22a0663def73b1c5101112 + languageName: node + linkType: hard + +"escalade@npm:^3.1.1": + version: 3.1.2 + resolution: "escalade@npm:3.1.2" + checksum: 10c0/6b4adafecd0682f3aa1cd1106b8fff30e492c7015b178bc81b2d2f75106dabea6c6d6e8508fc491bd58e597c74abb0e8e2368f943ecb9393d4162e3c2f3cf287 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 + languageName: node + linkType: hard + +"eslint-config-prettier@npm:^9.0.0": + version: 9.1.0 + resolution: "eslint-config-prettier@npm:9.1.0" + peerDependencies: + eslint: ">=7.0.0" + bin: + eslint-config-prettier: bin/cli.js + checksum: 10c0/6d332694b36bc9ac6fdb18d3ca2f6ac42afa2ad61f0493e89226950a7091e38981b66bac2b47ba39d15b73fff2cd32c78b850a9cf9eed9ca9a96bfb2f3a2f10d + languageName: node + linkType: hard + +"eslint-plugin-prettier@npm:^5.0.0": + version: 5.1.3 + resolution: "eslint-plugin-prettier@npm:5.1.3" + dependencies: + prettier-linter-helpers: "npm:^1.0.0" + synckit: "npm:^0.8.6" + peerDependencies: + "@types/eslint": ">=8.0.0" + eslint: ">=8.0.0" + eslint-config-prettier: "*" + prettier: ">=3.0.0" + peerDependenciesMeta: + "@types/eslint": + optional: true + eslint-config-prettier: + optional: true + checksum: 10c0/f45d5fc1fcfec6b0cf038a7a65ddd10a25df4fe3f9e1f6b7f0d5100e66f046a26a2492e69ee765dddf461b93c114cf2e1eb18d4970aafa6f385448985c136e09 + languageName: node + linkType: hard + +"eslint-plugin-vue@npm:^9.26.0": + version: 9.26.0 + resolution: "eslint-plugin-vue@npm:9.26.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + globals: "npm:^13.24.0" + natural-compare: "npm:^1.4.0" + nth-check: "npm:^2.1.1" + postcss-selector-parser: "npm:^6.0.15" + semver: "npm:^7.6.0" + vue-eslint-parser: "npm:^9.4.2" + xml-name-validator: "npm:^4.0.0" + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + checksum: 10c0/5236762e9ff0bb4f6ce0f59b7923d0ed46bed7b42141d0a4aa93b4f382f5a5e5c8d940dff2ff1ee30f5d3a16523481580239ec0b8800f5370a5aead2cc577386 + languageName: node + linkType: hard + +"eslint-scope@npm:^7.1.1": + version: 7.2.2 + resolution: "eslint-scope@npm:7.2.2" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10c0/613c267aea34b5a6d6c00514e8545ef1f1433108097e857225fed40d397dd6b1809dffd11c2fde23b37ca53d7bf935fe04d2a18e6fc932b31837b6ad67e1c116 + languageName: node + linkType: hard + +"eslint-scope@npm:^8.0.1": + version: 8.0.1 + resolution: "eslint-scope@npm:8.0.1" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10c0/0ec40ab284e58ac7ef064ecd23c127e03d339fa57173c96852336c73afc70ce5631da21dc1c772415a37a421291845538dd69db83c68d611044c0fde1d1fa269 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^4.0.0": + version: 4.0.0 + resolution: "eslint-visitor-keys@npm:4.0.0" + checksum: 10c0/76619f42cf162705a1515a6868e6fc7567e185c7063a05621a8ac4c3b850d022661262c21d9f1fc1d144ecf0d5d64d70a3f43c15c3fc969a61ace0fb25698cf5 + languageName: node + linkType: hard + +"eslint@npm:^9.2.0": + version: 9.2.0 + resolution: "eslint@npm:9.2.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/regexpp": "npm:^4.6.1" + "@eslint/eslintrc": "npm:^3.0.2" + "@eslint/js": "npm:9.2.0" + "@humanwhocodes/config-array": "npm:^0.13.0" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@humanwhocodes/retry": "npm:^0.2.3" + "@nodelib/fs.walk": "npm:^1.2.8" + ajv: "npm:^6.12.4" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.2" + debug: "npm:^4.3.2" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^8.0.1" + eslint-visitor-keys: "npm:^4.0.0" + espree: "npm:^10.0.1" + esquery: "npm:^1.4.2" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^8.0.0" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + is-path-inside: "npm:^3.0.3" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + levn: "npm:^0.4.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.2" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + strip-ansi: "npm:^6.0.1" + text-table: "npm:^0.2.0" + bin: + eslint: bin/eslint.js + checksum: 10c0/eab3265100a359a486e40e1d9d4d3ecff936d2f4d952f4ab107d404e0684fffbe186ecd0fb41791af5bcb13570a27032ddf9a2e628927ed33473f64255b0037b + languageName: node + linkType: hard + +"espree@npm:^10.0.1": + version: 10.0.1 + resolution: "espree@npm:10.0.1" + dependencies: + acorn: "npm:^8.11.3" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.0.0" + checksum: 10c0/7c0f84afa0f9db7bb899619e6364ed832ef13fe8943691757ddde9a1805ae68b826ed66803323015f707a629a5507d0d290edda2276c25131fe0ad883b8b5636 + languageName: node + linkType: hard + +"espree@npm:^9.3.1": + version: 9.6.1 + resolution: "espree@npm:9.6.1" + dependencies: + acorn: "npm:^8.9.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^3.4.1" + checksum: 10c0/1a2e9b4699b715347f62330bcc76aee224390c28bb02b31a3752e9d07549c473f5f986720483c6469cf3cfb3c9d05df612ffc69eb1ee94b54b739e67de9bb460 + languageName: node + linkType: hard + +"esquery@npm:^1.4.0, esquery@npm:^1.4.2": + version: 1.5.0 + resolution: "esquery@npm:1.5.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 10c0/a084bd049d954cc88ac69df30534043fb2aee5555b56246493f42f27d1e168f00d9e5d4192e46f10290d312dc30dc7d58994d61a609c579c1219d636996f9213 + languageName: node + linkType: hard + +"esrecurse@npm:^4.3.0": + version: 4.3.0 + resolution: "esrecurse@npm:4.3.0" + dependencies: + estraverse: "npm:^5.2.0" + checksum: 10c0/81a37116d1408ded88ada45b9fb16dbd26fba3aadc369ce50fcaf82a0bac12772ebd7b24cd7b91fc66786bf2c1ac7b5f196bc990a473efff972f5cb338877cf5 + languageName: node + linkType: hard + +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 10c0/1ff9447b96263dec95d6d67431c5e0771eb9776427421260a3e2f0fdd5d6bd4f8e37a7338f5ad2880c9f143450c9b1e4fc2069060724570a49cf9cf0312bd107 + languageName: node + linkType: hard + +"estree-walker@npm:^2.0.2": + version: 2.0.2 + resolution: "estree-walker@npm:2.0.2" + checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af + languageName: node + linkType: hard + +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 + languageName: node + linkType: hard + +"execa@npm:^8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^3.0.0" + checksum: 10c0/2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579 + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 + languageName: node + linkType: hard + +"fast-diff@npm:^1.1.2": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: 10c0/5c19af237edb5d5effda008c891a18a585f74bf12953be57923f17a3a4d0979565fc64dbc73b9e20926b9d895f5b690c618cbb969af0cf022e3222471220ad29 + languageName: node + linkType: hard + +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.4" + checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845 + languageName: node + linkType: hard + +"fast-json-stable-stringify@npm:^2.0.0": + version: 2.1.0 + resolution: "fast-json-stable-stringify@npm:2.1.0" + checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b + languageName: node + linkType: hard + +"fast-levenshtein@npm:^2.0.6": + version: 2.0.6 + resolution: "fast-levenshtein@npm:2.0.6" + checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4 + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.17.1 + resolution: "fastq@npm:1.17.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10c0/1095f16cea45fb3beff558bb3afa74ca7a9250f5a670b65db7ed585f92b4b48381445cd328b3d87323da81e43232b5d5978a8201bde84e0cd514310f1ea6da34 + languageName: node + linkType: hard + +"file-entry-cache@npm:^8.0.0": + version: 8.0.0 + resolution: "file-entry-cache@npm:8.0.0" + dependencies: + flat-cache: "npm:^4.0.0" + checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638 + languageName: node + linkType: hard + +"file-sharing@workspace:file-sharing": + version: 0.0.0-use.local + resolution: "file-sharing@workspace:file-sharing" + dependencies: + "@45drives/houston-common-css": "workspace:^" + "@45drives/houston-common-lib": "workspace:^" + "@45drives/houston-common-ui": "workspace:^" + "@fontsource/red-hat-text": "npm:^5.0.18" + "@headlessui/vue": "npm:^1.7.22" + "@rushstack/eslint-patch": "npm:^1.10.2" + "@tailwindcss/forms": "npm:^0.5.7" + "@tsconfig/node20": "npm:^20.1.4" + "@types/deep-equal": "npm:^1.0.4" + "@types/jsdom": "npm:^21.1.6" + "@types/node": "npm:^20.12.12" + "@vitejs/plugin-vue": "npm:^5.0.4" + "@vitejs/plugin-vue-jsx": "npm:^3.1.0" + "@vue/eslint-config-prettier": "npm:^9.0.0" + "@vue/eslint-config-typescript": "npm:^13.0.0" + "@vue/test-utils": "npm:^2.4.6" + "@vue/tsconfig": "npm:^0.5.1" + autoprefixer: "npm:^10.4.19" + deep-equal: "npm:^2.2.3" + eslint: "npm:^9.2.0" + eslint-plugin-vue: "npm:^9.26.0" + jsdom: "npm:^24.0.0" + monet: "npm:^0.9.3" + neverthrow: "npm:^6.2.1" + npm-run-all2: "npm:^6.1.2" + postcss: "npm:^8.4.38" + postcss-modules: "npm:^6.0.0" + tailwindcss: "npm:^3.4.3" + ts-node: "npm:^10.9.2" + typescript: "npm:~5.4.5" + vite: "npm:^5.2.11" + vite-plugin-vue-devtools: "npm:^7.2.0" + vitest: "npm:^1.6.0" + vue: "npm:^3.4.27" + vue-router: "npm:^4.3.2" + vue-tsc: "npm:^2.0.19" + zod: "npm:^3.23.8" + languageName: unknown + linkType: soft + +"fill-range@npm:^7.0.1": + version: 7.0.1 + resolution: "fill-range@npm:7.0.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/7cdad7d426ffbaadf45aeb5d15ec675bbd77f7597ad5399e3d2766987ed20bda24d5fac64b3ee79d93276f5865608bb22344a26b9b1ae6c4d00bd94bf611623f + languageName: node + linkType: hard + +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: "npm:^6.0.0" + path-exists: "npm:^4.0.0" + checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a + languageName: node + linkType: hard + +"flat-cache@npm:^4.0.0": + version: 4.0.1 + resolution: "flat-cache@npm:4.0.1" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.4" + checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc + languageName: node + linkType: hard + +"flatted@npm:^3.2.9": + version: 3.3.1 + resolution: "flatted@npm:3.3.1" + checksum: 10c0/324166b125ee07d4ca9bcf3a5f98d915d5db4f39d711fba640a3178b959919aae1f7cfd8aabcfef5826ed8aa8a2aa14cc85b2d7d18ff638ddf4ae3df39573eaf + languageName: node + linkType: hard + +"for-each@npm:^0.3.3": + version: 0.3.3 + resolution: "for-each@npm:0.3.3" + dependencies: + is-callable: "npm:^1.1.3" + checksum: 10c0/22330d8a2db728dbf003ec9182c2d421fbcd2969b02b4f97ec288721cda63eb28f2c08585ddccd0f77cb2930af8d958005c9e72f47141dc51816127a118f39aa + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.1.1 + resolution: "foreground-child@npm:3.1.1" + dependencies: + cross-spawn: "npm:^7.0.0" + signal-exit: "npm:^4.0.1" + checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0 + languageName: node + linkType: hard + +"form-data@npm:^4.0.0": + version: 4.0.0 + resolution: "form-data@npm:4.0.0" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + mime-types: "npm:^2.1.12" + checksum: 10c0/cb6f3ac49180be03ff07ba3ff125f9eba2ff0b277fb33c7fc47569fc5e616882c5b1c69b9904c4c4187e97dd0419dd03b134174756f296dec62041e6527e2c6e + languageName: node + linkType: hard + +"fraction.js@npm:^4.3.7": + version: 4.3.7 + resolution: "fraction.js@npm:4.3.7" + checksum: 10c0/df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 + languageName: node + linkType: hard + +"fs-extra@npm:^11.2.0": + version: 11.2.0 + resolution: "fs-extra@npm:11.2.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/d77a9a9efe60532d2e790e938c81a02c1b24904ef7a3efb3990b835514465ba720e99a6ea56fd5e2db53b4695319b644d76d5a0e9988a2beef80aa7b1da63398 + languageName: node + linkType: hard + +"fs-extra@npm:~7.0.1": + version: 7.0.1 + resolution: "fs-extra@npm:7.0.1" + dependencies: + graceful-fs: "npm:^4.1.2" + jsonfile: "npm:^4.0.0" + universalify: "npm:^0.1.0" + checksum: 10c0/1943bb2150007e3739921b8d13d4109abdc3cc481e53b97b7ea7f77eda1c3c642e27ae49eac3af074e3496ea02fde30f411ef410c760c70a38b92e656e5da784 + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004 + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"functions-have-names@npm:^1.2.3": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: 10c0/33e77fd29bddc2d9bb78ab3eb854c165909201f88c75faa8272e35899e2d35a8a642a15e7420ef945e1f64a9670d6aa3ec744106b2aa42be68ca5114025954ca + languageName: node + linkType: hard + +"generic-names@npm:^4.0.0": + version: 4.0.0 + resolution: "generic-names@npm:4.0.0" + dependencies: + loader-utils: "npm:^3.2.0" + checksum: 10c0/4e2be864535fadceed4e803fefc1df7f85447d9479d51e611a8a43a2c96533422b62c8fae84d9eb10cc21ee3de569a8c29d5ba68978ae930cccc9cb43b9a36d1 + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 + languageName: node + linkType: hard + +"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": + version: 2.0.2 + resolution: "get-func-name@npm:2.0.2" + checksum: 10c0/89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + has-proto: "npm:^1.0.1" + has-symbols: "npm:^1.0.3" + hasown: "npm:^2.0.0" + checksum: 10c0/0a9b82c16696ed6da5e39b1267104475c47e3a9bdbe8b509dfe1710946e38a87be70d759f4bb3cda042d76a41ef47fe769660f3b7c0d1f68750299344ffb15b7 + languageName: node + linkType: hard + +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 10c0/5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee + languageName: node + linkType: hard + +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: "npm:^4.0.3" + checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 + languageName: node + linkType: hard + +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.3": + version: 10.3.12 + resolution: "glob@npm:10.3.12" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^2.3.6" + minimatch: "npm:^9.0.1" + minipass: "npm:^7.0.4" + path-scurry: "npm:^1.10.2" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/f60cefdc1cf3f958b2bb5823e1b233727f04916d489dc4641d76914f016e6704421e06a83cbb68b0cb1cb9382298b7a88075b844ad2127fc9727ea22b18b0711 + languageName: node + linkType: hard + +"globals@npm:^11.1.0": + version: 11.12.0 + resolution: "globals@npm:11.12.0" + checksum: 10c0/758f9f258e7b19226bd8d4af5d3b0dcf7038780fb23d82e6f98932c44e239f884847f1766e8fa9cc5635ccb3204f7fa7314d4408dd4002a5e8ea827b4018f0a1 + languageName: node + linkType: hard + +"globals@npm:^13.24.0": + version: 13.24.0 + resolution: "globals@npm:13.24.0" + dependencies: + type-fest: "npm:^0.20.2" + checksum: 10c0/d3c11aeea898eb83d5ec7a99508600fbe8f83d2cf00cbb77f873dbf2bcb39428eff1b538e4915c993d8a3b3473fa71eeebfe22c9bb3a3003d1e26b1f2c8a42cd + languageName: node + linkType: hard + +"globals@npm:^14.0.0": + version: 14.0.0 + resolution: "globals@npm:14.0.0" + checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d + languageName: node + linkType: hard + +"globby@npm:^11.1.0": + version: 11.1.0 + resolution: "globby@npm:11.1.0" + dependencies: + array-union: "npm:^2.1.0" + dir-glob: "npm:^3.0.1" + fast-glob: "npm:^3.2.9" + ignore: "npm:^5.2.0" + merge2: "npm:^1.4.1" + slash: "npm:^3.0.0" + checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189 + languageName: node + linkType: hard + +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.1.3" + checksum: 10c0/505c05487f7944c552cee72087bf1567debb470d4355b1335f2c262d218ebbff805cd3715448fe29b4b380bae6912561d0467233e4165830efd28da241418c63 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"graphemer@npm:^1.4.0": + version: 1.4.0 + resolution: "graphemer@npm:1.4.0" + checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 + languageName: node + linkType: hard + +"has-bigints@npm:^1.0.1": + version: 1.0.2 + resolution: "has-bigints@npm:1.0.2" + checksum: 10c0/724eb1485bfa3cdff6f18d95130aa190561f00b3fcf9f19dc640baf8176b5917c143b81ec2123f8cddb6c05164a198c94b13e1377c497705ccc8e1a80306e83b + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 10c0/1c6c83b14b8b1b3c25b0727b8ba3e3b647f99e9e6e13eb7322107261de07a4c1be56fc0d45678fc376e09772a3a1642ccdaf8fc69bdf123b6c086598397ce473 + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1 + languageName: node + linkType: hard + +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-proto@npm:^1.0.1": + version: 1.0.3 + resolution: "has-proto@npm:1.0.3" + checksum: 10c0/35a6989f81e9f8022c2f4027f8b48a552de714938765d019dbea6bb547bd49ce5010a3c7c32ec6ddac6e48fc546166a3583b128f5a7add8b058a6d8b4afec205 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: 10c0/e6922b4345a3f37069cdfe8600febbca791c94988c01af3394d86ca3360b4b93928bbf395859158f88099cb10b19d98e3bbab7c9ff2c1bd09cf665ee90afa2c3 + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"hasown@npm:^2.0.0": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 + languageName: node + linkType: hard + +"he@npm:^1.2.0": + version: 1.2.0 + resolution: "he@npm:1.2.0" + bin: + he: bin/he + checksum: 10c0/a27d478befe3c8192f006cdd0639a66798979dfa6e2125c6ac582a19a5ebfec62ad83e8382e6036170d873f46e4536a7e795bf8b95bf7c247f4cc0825ccc8c17 + languageName: node + linkType: hard + +"hookable@npm:^5.5.3": + version: 5.5.3 + resolution: "hookable@npm:5.5.3" + checksum: 10c0/275f4cc84d27f8d48c5a5cd5685b6c0fea9291be9deea5bff0cfa72856ed566abde1dcd8cb1da0f9a70b4da3d7ec0d60dc3554c4edbba647058cc38816eced3d + languageName: node + linkType: hard + +"html-encoding-sniffer@npm:^4.0.0": + version: 4.0.0 + resolution: "html-encoding-sniffer@npm:4.0.0" + dependencies: + whatwg-encoding: "npm:^3.1.1" + checksum: 10c0/523398055dc61ac9b34718a719cb4aa691e4166f29187e211e1607de63dc25ac7af52ca7c9aead0c4b3c0415ffecb17326396e1202e2e86ff4bca4c0ee4c6140 + languageName: node + linkType: hard + +"html-tags@npm:^3.3.1": + version: 3.3.1 + resolution: "html-tags@npm:3.3.1" + checksum: 10c0/680165e12baa51bad7397452d247dbcc5a5c29dac0e6754b1187eee3bf26f514bc1907a431dd2f7eb56207611ae595ee76a0acc8eaa0d931e72c791dd6463d79 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.1": + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: "npm:^7.1.0" + debug: "npm:^4.3.4" + checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": + version: 7.0.4 + resolution: "https-proxy-agent@npm:7.0.4" + dependencies: + agent-base: "npm:^7.0.2" + debug: "npm:4" + checksum: 10c0/bc4f7c38da32a5fc622450b6cb49a24ff596f9bd48dcedb52d2da3fa1c1a80e100fb506bd59b326c012f21c863c69b275c23de1a01d0b84db396822fdf25e52b + languageName: node + linkType: hard + +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 10c0/5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 + languageName: node + linkType: hard + +"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 + languageName: node + linkType: hard + +"icss-utils@npm:^5.0.0, icss-utils@npm:^5.1.0": + version: 5.1.0 + resolution: "icss-utils@npm:5.1.0" + peerDependencies: + postcss: ^8.1.0 + checksum: 10c0/39c92936fabd23169c8611d2b5cc39e39d10b19b0d223352f20a7579f75b39d5f786114a6b8fc62bee8c5fed59ba9e0d38f7219a4db383e324fb3061664b043d + languageName: node + linkType: hard + +"ignore@npm:^5.2.0, ignore@npm:^5.3.1": + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 10c0/703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd + languageName: node + linkType: hard + +"import-fresh@npm:^3.2.1": + version: 3.3.0 + resolution: "import-fresh@npm:3.3.0" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10c0/7f882953aa6b740d1f0e384d0547158bc86efbf2eea0f1483b8900a6f65c5a5123c2cf09b0d542cc419d0b98a759ecaeb394237e97ea427f2da221dc3cd80cc3 + languageName: node + linkType: hard + +"import-lazy@npm:~4.0.0": + version: 4.0.0 + resolution: "import-lazy@npm:4.0.0" + checksum: 10c0/a3520313e2c31f25c0b06aa66d167f329832b68a4f957d7c9daf6e0fa41822b6e84948191648b9b9d8ca82f94740cdf15eecf2401a5b42cd1c33fd84f2225cca + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f + languageName: node + linkType: hard + +"inherits@npm:~2.0.3": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"ini@npm:^1.3.4": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a + languageName: node + linkType: hard + +"internal-slot@npm:^1.0.4": + version: 1.0.7 + resolution: "internal-slot@npm:1.0.7" + dependencies: + es-errors: "npm:^1.3.0" + hasown: "npm:^2.0.0" + side-channel: "npm:^1.0.4" + checksum: 10c0/f8b294a4e6ea3855fc59551bbf35f2b832cf01fd5e6e2a97f5c201a071cc09b49048f856e484b67a6c721da5e55736c5b6ddafaf19e2dbeb4a3ff1821680de6c + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc + languageName: node + linkType: hard + +"is-arguments@npm:^1.1.1": + version: 1.1.1 + resolution: "is-arguments@npm:1.1.1" + dependencies: + call-bind: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/5ff1f341ee4475350adfc14b2328b38962564b7c2076be2f5bac7bd9b61779efba99b9f844a7b82ba7654adccf8e8eb19d1bb0cc6d1c1a085e498f6793d4328f + languageName: node + linkType: hard + +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4": + version: 3.0.4 + resolution: "is-array-buffer@npm:3.0.4" + dependencies: + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.2.1" + checksum: 10c0/42a49d006cc6130bc5424eae113e948c146f31f9d24460fc0958f855d9d810e6fd2e4519bf19aab75179af9c298ea6092459d8cafdec523cd19e529b26eab860 + languageName: node + linkType: hard + +"is-bigint@npm:^1.0.1": + version: 1.0.4 + resolution: "is-bigint@npm:1.0.4" + dependencies: + has-bigints: "npm:^1.0.1" + checksum: 10c0/eb9c88e418a0d195ca545aff2b715c9903d9b0a5033bc5922fec600eb0c3d7b1ee7f882dbf2e0d5a6e694e42391be3683e4368737bd3c4a77f8ac293e7773696 + languageName: node + linkType: hard + +"is-binary-path@npm:~2.1.0": + version: 2.1.0 + resolution: "is-binary-path@npm:2.1.0" + dependencies: + binary-extensions: "npm:^2.0.0" + checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.1.0": + version: 1.1.2 + resolution: "is-boolean-object@npm:1.1.2" + dependencies: + call-bind: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/6090587f8a8a8534c0f816da868bc94f32810f08807aa72fa7e79f7e11c466d281486ffe7a788178809c2aa71fe3e700b167fe80dd96dad68026bfff8ebf39f7 + languageName: node + linkType: hard + +"is-callable@npm:^1.1.3": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 10c0/ceebaeb9d92e8adee604076971dd6000d38d6afc40bb843ea8e45c5579b57671c3f3b50d7f04869618242c6cee08d1b67806a8cb8edaaaf7c0748b3720d6066f + languageName: node + linkType: hard + +"is-core-module@npm:^2.1.0, is-core-module@npm:^2.13.0": + version: 2.13.1 + resolution: "is-core-module@npm:2.13.1" + dependencies: + hasown: "npm:^2.0.0" + checksum: 10c0/2cba9903aaa52718f11c4896dabc189bab980870aae86a62dc0d5cedb546896770ee946fb14c84b7adf0735f5eaea4277243f1b95f5cefa90054f92fbcac2518 + languageName: node + linkType: hard + +"is-date-object@npm:^1.0.5": + version: 1.0.5 + resolution: "is-date-object@npm:1.0.5" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/eed21e5dcc619c48ccef804dfc83a739dbb2abee6ca202838ee1bd5f760fe8d8a93444f0d49012ad19bb7c006186e2884a1b92f6e1c056da7fd23d0a9ad5992e + languageName: node + linkType: hard + +"is-docker@npm:^3.0.0": + version: 3.0.0 + resolution: "is-docker@npm:3.0.0" + bin: + is-docker: cli.js + checksum: 10c0/d2c4f8e6d3e34df75a5defd44991b6068afad4835bb783b902fa12d13ebdb8f41b2a199dcb0b5ed2cb78bfee9e4c0bbdb69c2d9646f4106464674d3e697a5856 + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-inside-container@npm:^1.0.0": + version: 1.0.0 + resolution: "is-inside-container@npm:1.0.0" + dependencies: + is-docker: "npm:^3.0.0" + bin: + is-inside-container: cli.js + checksum: 10c0/a8efb0e84f6197e6ff5c64c52890fa9acb49b7b74fed4da7c95383965da6f0fa592b4dbd5e38a79f87fc108196937acdbcd758fcefc9b140e479b39ce1fcd1cd + languageName: node + linkType: hard + +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d + languageName: node + linkType: hard + +"is-map@npm:^2.0.2, is-map@npm:^2.0.3": + version: 2.0.3 + resolution: "is-map@npm:2.0.3" + checksum: 10c0/2c4d431b74e00fdda7162cd8e4b763d6f6f217edf97d4f8538b94b8702b150610e2c64961340015fe8df5b1fcee33ccd2e9b62619c4a8a3a155f8de6d6d355fc + languageName: node + linkType: hard + +"is-number-object@npm:^1.0.4": + version: 1.0.7 + resolution: "is-number-object@npm:1.0.7" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/aad266da1e530f1804a2b7bd2e874b4869f71c98590b3964f9d06cc9869b18f8d1f4778f838ecd2a11011bce20aeecb53cb269ba916209b79c24580416b74b1b + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"is-path-inside@npm:^3.0.3": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 + languageName: node + linkType: hard + +"is-potential-custom-element-name@npm:^1.0.1": + version: 1.0.1 + resolution: "is-potential-custom-element-name@npm:1.0.1" + checksum: 10c0/b73e2f22bc863b0939941d369486d308b43d7aef1f9439705e3582bfccaa4516406865e32c968a35f97a99396dac84e2624e67b0a16b0a15086a785e16ce7db9 + languageName: node + linkType: hard + +"is-regex@npm:^1.1.4": + version: 1.1.4 + resolution: "is-regex@npm:1.1.4" + dependencies: + call-bind: "npm:^1.0.2" + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/bb72aae604a69eafd4a82a93002058c416ace8cde95873589a97fc5dac96a6c6c78a9977d487b7b95426a8f5073969124dd228f043f9f604f041f32fcc465fc1 + languageName: node + linkType: hard + +"is-set@npm:^2.0.2, is-set@npm:^2.0.3": + version: 2.0.3 + resolution: "is-set@npm:2.0.3" + checksum: 10c0/f73732e13f099b2dc879c2a12341cfc22ccaca8dd504e6edae26484bd5707a35d503fba5b4daad530a9b088ced1ae6c9d8200fd92e09b428fe14ea79ce8080b7 + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.2": + version: 1.0.3 + resolution: "is-shared-array-buffer@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.7" + checksum: 10c0/adc11ab0acbc934a7b9e5e9d6c588d4ec6682f6fea8cda5180721704fa32927582ede5b123349e32517fdadd07958973d24716c80e7ab198970c47acc09e59c7 + languageName: node + linkType: hard + +"is-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "is-stream@npm:3.0.0" + checksum: 10c0/eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8 + languageName: node + linkType: hard + +"is-string@npm:^1.0.5, is-string@npm:^1.0.7": + version: 1.0.7 + resolution: "is-string@npm:1.0.7" + dependencies: + has-tostringtag: "npm:^1.0.0" + checksum: 10c0/905f805cbc6eedfa678aaa103ab7f626aac9ebbdc8737abb5243acaa61d9820f8edc5819106b8fcd1839e33db21de9f0116ae20de380c8382d16dc2a601921f6 + languageName: node + linkType: hard + +"is-symbol@npm:^1.0.3": + version: 1.0.4 + resolution: "is-symbol@npm:1.0.4" + dependencies: + has-symbols: "npm:^1.0.2" + checksum: 10c0/9381dd015f7c8906154dbcbf93fad769de16b4b961edc94f88d26eb8c555935caa23af88bda0c93a18e65560f6d7cca0fd5a3f8a8e1df6f1abbb9bead4502ef7 + languageName: node + linkType: hard + +"is-weakmap@npm:^2.0.2": + version: 2.0.2 + resolution: "is-weakmap@npm:2.0.2" + checksum: 10c0/443c35bb86d5e6cc5929cd9c75a4024bb0fff9586ed50b092f94e700b89c43a33b186b76dbc6d54f3d3d09ece689ab38dcdc1af6a482cbe79c0f2da0a17f1299 + languageName: node + linkType: hard + +"is-weakset@npm:^2.0.3": + version: 2.0.3 + resolution: "is-weakset@npm:2.0.3" + dependencies: + call-bind: "npm:^1.0.7" + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/8ad6141b6a400e7ce7c7442a13928c676d07b1f315ab77d9912920bf5f4170622f43126f111615788f26c3b1871158a6797c862233124507db0bcc33a9537d1a + languageName: node + linkType: hard + +"is-wsl@npm:^3.1.0": + version: 3.1.0 + resolution: "is-wsl@npm:3.1.0" + dependencies: + is-inside-container: "npm:^1.0.0" + checksum: 10c0/d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947 + languageName: node + linkType: hard + +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: 10c0/4199f14a7a13da2177c66c31080008b7124331956f47bca57dd0b6ea9f11687aa25e565a2c7a2b519bc86988d10398e3049a1f5df13c9f6b7664154690ae79fd + languageName: node + linkType: hard + +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: 10c0/18b5be6669be53425f0b84098732670ed4e727e3af33bc7f948aac01782110eb9a18b3b329c5323bcdd3acdaae547ee077d3951317e7f133bff7105264b3003d + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7 + languageName: node + linkType: hard + +"jackspeak@npm:^2.3.6": + version: 2.3.6 + resolution: "jackspeak@npm:2.3.6" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/f01d8f972d894cd7638bc338e9ef5ddb86f7b208ce177a36d718eac96ec86638a6efa17d0221b10073e64b45edc2ce15340db9380b1f5d5c5d000cbc517dc111 + languageName: node + linkType: hard + +"jiti@npm:^1.21.0": + version: 1.21.0 + resolution: "jiti@npm:1.21.0" + bin: + jiti: bin/jiti.js + checksum: 10c0/7f361219fe6c7a5e440d5f1dba4ab763a5538d2df8708cdc22561cf25ea3e44b837687931fca7cdd8cdd9f567300e90be989dd1321650045012d8f9ed6aab07f + languageName: node + linkType: hard + +"jju@npm:~1.4.0": + version: 1.4.0 + resolution: "jju@npm:1.4.0" + checksum: 10c0/f3f444557e4364cfc06b1abf8331bf3778b26c0c8552ca54429bc0092652172fdea26cbffe33e1017b303d5aa506f7ede8571857400efe459cb7439180e2acad + languageName: node + linkType: hard + +"js-beautify@npm:^1.14.9": + version: 1.15.1 + resolution: "js-beautify@npm:1.15.1" + dependencies: + config-chain: "npm:^1.1.13" + editorconfig: "npm:^1.0.4" + glob: "npm:^10.3.3" + js-cookie: "npm:^3.0.5" + nopt: "npm:^7.2.0" + bin: + css-beautify: js/bin/css-beautify.js + html-beautify: js/bin/html-beautify.js + js-beautify: js/bin/js-beautify.js + checksum: 10c0/4140dd95537143eb429b6c8e47e21310f16c032d97a03163c6c7c0502bc663242a5db08d3ad941b87f24a142ce4f9190c556d2340bcd056545326377dfae5362 + languageName: node + linkType: hard + +"js-cookie@npm:^3.0.5": + version: 3.0.5 + resolution: "js-cookie@npm:3.0.5" + checksum: 10c0/04a0e560407b4489daac3a63e231d35f4e86f78bff9d792011391b49c59f721b513411cd75714c418049c8dc9750b20fcddad1ca5a2ca616c3aca4874cce5b3a + languageName: node + linkType: hard + +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + +"js-tokens@npm:^9.0.0": + version: 9.0.0 + resolution: "js-tokens@npm:9.0.0" + checksum: 10c0/4ad1c12f47b8c8b2a3a99e29ef338c1385c7b7442198a425f3463f3537384dab6032012791bfc2f056ea5ecdb06b1ed4f70e11a3ab3f388d3dcebfe16a52b27d + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f + languageName: node + linkType: hard + +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96 + languageName: node + linkType: hard + +"jsdom@npm:^24.0.0": + version: 24.0.0 + resolution: "jsdom@npm:24.0.0" + dependencies: + cssstyle: "npm:^4.0.1" + data-urls: "npm:^5.0.0" + decimal.js: "npm:^10.4.3" + form-data: "npm:^4.0.0" + html-encoding-sniffer: "npm:^4.0.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.2" + is-potential-custom-element-name: "npm:^1.0.1" + nwsapi: "npm:^2.2.7" + parse5: "npm:^7.1.2" + rrweb-cssom: "npm:^0.6.0" + saxes: "npm:^6.0.0" + symbol-tree: "npm:^3.2.4" + tough-cookie: "npm:^4.1.3" + w3c-xmlserializer: "npm:^5.0.0" + webidl-conversions: "npm:^7.0.0" + whatwg-encoding: "npm:^3.1.1" + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + ws: "npm:^8.16.0" + xml-name-validator: "npm:^5.0.0" + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + checksum: 10c0/7b35043d7af39ad6dcaef0fa5679d8c8a94c6c9b6cc4a79222b7c9987d57ab7150c50856684ae56b473ab28c7d82aec0fb7ca19dcbd4c3f46683c807d717a3af + languageName: node + linkType: hard + +"jsesc@npm:^2.5.1": + version: 2.5.2 + resolution: "jsesc@npm:2.5.2" + bin: + jsesc: bin/jsesc + checksum: 10c0/dbf59312e0ebf2b4405ef413ec2b25abb5f8f4d9bc5fb8d9f90381622ebca5f2af6a6aa9a8578f65903f9e33990a6dc798edd0ce5586894bf0e9e31803a1de88 + languageName: node + linkType: hard + +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 10c0/0d1c91569d9588e7eef2b49b59851f297f3ab93c7b35c7c221e288099322be6b562767d11e4821da500f3219542b9afd2e54c5dc573107c1126ed1080f8e96d7 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^3.0.0": + version: 3.0.1 + resolution: "json-parse-even-better-errors@npm:3.0.1" + checksum: 10c0/bc40600b14231dff1ff911d269c7ed89fbf3dbedf25cad3f47c10ff9cbb998ce03921372a17f27f3c7cfed76e679bc6c02a7b4cb2604b0ba68cd51ed16899492 + languageName: node + linkType: hard + +"json-schema-traverse@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-traverse@npm:0.4.1" + checksum: 10c0/108fa90d4cc6f08243aedc6da16c408daf81793bf903e9fd5ab21983cda433d5d2da49e40711da016289465ec2e62e0324dcdfbc06275a607fe3233fde4942ce + languageName: node + linkType: hard + +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: 10c0/cb168b61fd4de83e58d09aaa6425ef71001bae30d260e2c57e7d09a5fd82223e2f22a042dedaab8db23b7d9ae46854b08bb1f91675a8be11c5cffebef5fb66a5 + languageName: node + linkType: hard + +"json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c + languageName: node + linkType: hard + +"jsonc-parser@npm:^3.2.0": + version: 3.2.1 + resolution: "jsonc-parser@npm:3.2.1" + checksum: 10c0/ada66dec143d7f9cb0e2d0d29c69e9ce40d20f3a4cb96b0c6efb745025ac7f9ba647d7ac0990d0adfc37a2d2ae084a12009a9c833dbdbeadf648879a99b9df89 + languageName: node + linkType: hard + +"jsonfile@npm:^4.0.0": + version: 4.0.0 + resolution: "jsonfile@npm:4.0.0" + dependencies: + graceful-fs: "npm:^4.1.6" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/7dc94b628d57a66b71fb1b79510d460d662eb975b5f876d723f81549c2e9cd316d58a2ddf742b2b93a4fa6b17b2accaf1a738a0e2ea114bdfb13a32e5377e480 + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865 + languageName: node + linkType: hard + +"keyv@npm:^4.5.4": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: "npm:3.0.1" + checksum: 10c0/aa52f3c5e18e16bb6324876bb8b59dd02acf782a4b789c7b2ae21107fab95fab3890ed448d4f8dba80ce05391eeac4bfabb4f02a20221342982f806fa2cf271e + languageName: node + linkType: hard + +"kolorist@npm:^1.8.0": + version: 1.8.0 + resolution: "kolorist@npm:1.8.0" + checksum: 10c0/73075db44a692bf6c34a649f3b4b3aea4993b84f6b754cbf7a8577e7c7db44c0bad87752bd23b0ce533f49de2244ce2ce03b7b1b667a85ae170a94782cc50f9b + languageName: node + linkType: hard + +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: "npm:^1.2.1" + type-check: "npm:~0.4.0" + checksum: 10c0/effb03cad7c89dfa5bd4f6989364bfc79994c2042ec5966cb9b95990e2edee5cd8969ddf42616a0373ac49fac1403437deaf6e9050fbbaa3546093a59b9ac94e + languageName: node + linkType: hard + +"lilconfig@npm:^2.1.0": + version: 2.1.0 + resolution: "lilconfig@npm:2.1.0" + checksum: 10c0/64645641aa8d274c99338e130554abd6a0190533c0d9eb2ce7ebfaf2e05c7d9961f3ffe2bfa39efd3b60c521ba3dd24fa236fe2775fc38501bf82bf49d4678b8 + languageName: node + linkType: hard + +"lilconfig@npm:^3.0.0": + version: 3.1.1 + resolution: "lilconfig@npm:3.1.1" + checksum: 10c0/311b559794546894e3fe176663427326026c1c644145be9e8041c58e268aa9328799b8dfe7e4dd8c6a4ae305feae95a1c9e007db3569f35b42b6e1bc8274754c + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d + languageName: node + linkType: hard + +"loader-utils@npm:^3.2.0": + version: 3.2.1 + resolution: "loader-utils@npm:3.2.1" + checksum: 10c0/d3e1f217d160e8e894a0385a33500d4ce14065e8ffb250f5a81ae65bc2c3baa50625ec34182ba4417b46b4ac6725aed64429e1104d6401e074af2aa1dd018394 + languageName: node + linkType: hard + +"local-pkg@npm:^0.5.0": + version: 0.5.0 + resolution: "local-pkg@npm:0.5.0" + dependencies: + mlly: "npm:^1.4.2" + pkg-types: "npm:^1.0.3" + checksum: 10c0/f61cbd00d7689f275558b1a45c7ff2a3ddf8472654123ed880215677b9adfa729f1081e50c27ffb415cdb9fa706fb755fec5e23cdd965be375c8059e87ff1cc9 + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: "npm:^5.0.0" + checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3 + languageName: node + linkType: hard + +"lodash.camelcase@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.camelcase@npm:4.3.0" + checksum: 10c0/fcba15d21a458076dd309fce6b1b4bf611d84a0ec252cb92447c948c533ac250b95d2e00955801ebc367e5af5ed288b996d75d37d2035260a937008e14eaf432 + languageName: node + linkType: hard + +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: 10c0/48f40d471a1654397ed41685495acb31498d5ed696185ac8973daef424a749ca0c7871bf7b665d5c14f5cc479394479e0307e781f61d5573831769593411be6e + languageName: node + linkType: hard + +"lodash.isequal@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: 10c0/dfdb2356db19631a4b445d5f37868a095e2402292d59539a987f134a8778c62a2810c2452d11ae9e6dcac71fc9de40a6fedcb20e2952a15b431ad8b29e50e28f + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 + languageName: node + linkType: hard + +"lodash@npm:^4.17.21, lodash@npm:~4.17.15": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c + languageName: node + linkType: hard + +"loupe@npm:^2.3.6, loupe@npm:^2.3.7": + version: 2.3.7 + resolution: "loupe@npm:2.3.7" + dependencies: + get-func-name: "npm:^2.0.1" + checksum: 10c0/71a781c8fc21527b99ed1062043f1f2bb30bdaf54fa4cf92463427e1718bc6567af2988300bc243c1f276e4f0876f29e3cbf7b58106fdc186915687456ce5bf4 + languageName: node + linkType: hard + +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.2.0 + resolution: "lru-cache@npm:10.2.0" + checksum: 10c0/c9847612aa2daaef102d30542a8d6d9b2c2bb36581c1bf0dc3ebf5e5f3352c772a749e604afae2e46873b930a9e9523743faac4e5b937c576ab29196774712ee + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: "npm:^3.0.2" + checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 + languageName: node + linkType: hard + +"lru-cache@npm:^6.0.0": + version: 6.0.0 + resolution: "lru-cache@npm:6.0.0" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10c0/cb53e582785c48187d7a188d3379c181b5ca2a9c78d2bce3e7dee36f32761d1c42983da3fe12b55cb74e1779fa94cdc2e5367c028a9b35317184ede0c07a30a9 + languageName: node + linkType: hard + +"lunr@npm:^2.3.9": + version: 2.3.9 + resolution: "lunr@npm:2.3.9" + checksum: 10c0/77d7dbb4fbd602aac161e2b50887d8eda28c0fa3b799159cee380fbb311f1e614219126ecbbd2c3a9c685f1720a8109b3c1ca85cc893c39b6c9cc6a62a1d8a8b + languageName: node + linkType: hard + +"magic-string@npm:^0.30.10, magic-string@npm:^0.30.4, magic-string@npm:^0.30.5, magic-string@npm:^0.30.8": + version: 0.30.10 + resolution: "magic-string@npm:0.30.10" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.4.15" + checksum: 10c0/aa9ca17eae571a19bce92c8221193b6f93ee8511abb10f085e55ffd398db8e4c089a208d9eac559deee96a08b7b24d636ea4ab92f09c6cf42a7d1af51f7fd62b + languageName: node + linkType: hard + +"make-error@npm:^1.1.1": + version: 1.3.6 + resolution: "make-error@npm:1.3.6" + checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f + languageName: node + linkType: hard + +"make-fetch-happen@npm:^13.0.0": + version: 13.0.0 + resolution: "make-fetch-happen@npm:13.0.0" + dependencies: + "@npmcli/agent": "npm:^2.0.0" + cacache: "npm:^18.0.0" + http-cache-semantics: "npm:^4.1.1" + is-lambda: "npm:^1.0.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^3.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^0.6.3" + promise-retry: "npm:^2.0.1" + ssri: "npm:^10.0.0" + checksum: 10c0/43b9f6dcbc6fe8b8604cb6396957c3698857a15ba4dbc38284f7f0e61f248300585ef1eb8cc62df54e9c724af977e45b5cdfd88320ef7f53e45070ed3488da55 + languageName: node + linkType: hard + +"marked@npm:^4.3.0": + version: 4.3.0 + resolution: "marked@npm:4.3.0" + bin: + marked: bin/marked.js + checksum: 10c0/0013463855e31b9c88d8bb2891a611d10ef1dc79f2e3cbff1bf71ba389e04c5971298c886af0be799d7fa9aa4593b086a136062d59f1210b0480b026a8c5dc47 + languageName: node + linkType: hard + +"memorystream@npm:^0.3.1": + version: 0.3.1 + resolution: "memorystream@npm:0.3.1" + checksum: 10c0/4bd164657711d9747ff5edb0508b2944414da3464b7fe21ac5c67cf35bba975c4b446a0124bd0f9a8be54cfc18faf92e92bd77563a20328b1ccf2ff04e9f39b9 + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 + languageName: node + linkType: hard + +"merge2@npm:^1.3.0, merge2@npm:^1.4.1": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb + languageName: node + linkType: hard + +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": + version: 4.0.5 + resolution: "micromatch@npm:4.0.5" + dependencies: + braces: "npm:^3.0.2" + picomatch: "npm:^2.3.1" + checksum: 10c0/3d6505b20f9fa804af5d8c596cb1c5e475b9b0cd05f652c5b56141cf941bd72adaeb7a436fda344235cef93a7f29b7472efc779fcdb83b478eab0867b95cdeff + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 + languageName: node + linkType: hard + +"mimic-fn@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-fn@npm:4.0.0" + checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf + languageName: node + linkType: hard + +"mini-svg-data-uri@npm:^1.2.3": + version: 1.4.4 + resolution: "mini-svg-data-uri@npm:1.4.4" + bin: + mini-svg-data-uri: cli.js + checksum: 10c0/24545fa30b5a45449241bf19c25b8bc37594b63ec06401b3d563bd1c2e8a6abb7c18741f8b354e0064baa63c291be214154bf3a66f201ae71dfab3cc1a5e3191 + languageName: node + linkType: hard + +"minimatch@npm:9.0.1": + version: 9.0.1 + resolution: "minimatch@npm:9.0.1" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/aa043eb8822210b39888a5d0d28df0017b365af5add9bd522f180d2a6962de1cbbf1bdeacdb1b17f410dc3336bc8d76fb1d3e814cdc65d00c2f68e01f0010096 + languageName: node + linkType: hard + +"minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + languageName: node + linkType: hard + +"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4": + version: 9.0.4 + resolution: "minimatch@npm:9.0.4" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/2c16f21f50e64922864e560ff97c587d15fd491f65d92a677a344e970fe62aafdbeafe648965fa96d33c061b4d0eabfe0213466203dd793367e7f28658cf6414 + languageName: node + linkType: hard + +"minimatch@npm:~3.0.3": + version: 3.0.8 + resolution: "minimatch@npm:3.0.8" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/72b226f452dcfb5075255f53534cb83fc25565b909e79b9be4fad463d735cb1084827f7013ff41d050e77ee6e474408c6073473edd2fb72c2fd630cfb0acc6ad + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e + languageName: node + linkType: hard + +"minipass-fetch@npm:^3.0.0": + version: 3.0.4 + resolution: "minipass-fetch@npm:3.0.4" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^2.1.2" + dependenciesMeta: + encoding: + optional: true + checksum: 10c0/1b63c1f3313e88eeac4689f1b71c9f086598db9a189400e3ee960c32ed89e06737fa23976c9305c2d57464fb3fcdc12749d3378805c9d6176f5569b0d0ee8a75 + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4": + version: 7.0.4 + resolution: "minipass@npm:7.0.4" + checksum: 10c0/6c7370a6dfd257bf18222da581ba89a5eaedca10e158781232a8b5542a90547540b4b9b7e7f490e4cda43acfbd12e086f0453728ecf8c19e0ef6921bc5958ac5 + languageName: node + linkType: hard + +"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: "npm:^3.0.0" + yallist: "npm:^4.0.0" + checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78 + languageName: node + linkType: hard + +"mitt@npm:^3.0.1": + version: 3.0.1 + resolution: "mitt@npm:3.0.1" + checksum: 10c0/3ab4fdecf3be8c5255536faa07064d05caa3dd332bd318ff02e04621f7b3069ca1de9106cfe8e7ced675abfc2bec2ce4c4ef321c4a1bb1fb29df8ae090741913 + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf + languageName: node + linkType: hard + +"mlly@npm:^1.4.2, mlly@npm:^1.6.1": + version: 1.6.1 + resolution: "mlly@npm:1.6.1" + dependencies: + acorn: "npm:^8.11.3" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.0.3" + ufo: "npm:^1.3.2" + checksum: 10c0/a7bf26b3d4f83b0f5a5232caa3af44be08b464f562f31c11d885d1bc2d43b7d717137d47b0c06fdc69e1b33ffc09f902b6d2b18de02c577849d40914e8785092 + languageName: node + linkType: hard + +"monet@npm:^0.9.3": + version: 0.9.3 + resolution: "monet@npm:0.9.3" + checksum: 10c0/570e871af691367495a489278e92f69b546a647fdf13455d8a9284d90f7248d756114cd690bd399d403f45ccce2d40238b4c38066fae6a401ad5774b946b31cf + languageName: node + linkType: hard + +"mrmime@npm:^2.0.0": + version: 2.0.0 + resolution: "mrmime@npm:2.0.0" + checksum: 10c0/312b35ed288986aec90955410b21ed7427fd1e4ee318cb5fc18765c8d029eeded9444faa46589e5b1ed6b35fb2054a802ac8dcb917ddf6b3e189cb3bf11a965c + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc + languageName: node + linkType: hard + +"muggle-string@npm:^0.3.1": + version: 0.3.1 + resolution: "muggle-string@npm:0.3.1" + checksum: 10c0/489b0575fa76e30914393915a36638590052409fca2206a6bef0fb0ad7b181c1cbf99761191bfd16fe402c6f5a3164897965422fa32ef20ada1b44024ba46ab6 + languageName: node + linkType: hard + +"muggle-string@npm:^0.4.0": + version: 0.4.1 + resolution: "muggle-string@npm:0.4.1" + checksum: 10c0/e914b63e24cd23f97e18376ec47e4ba3aa24365e4776212b666add2e47bb158003212980d732c49abf3719568900af7861873844a6e2d3a7ca7e86952c0e99e9 + languageName: node + linkType: hard + +"mz@npm:^2.7.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: "npm:^1.0.0" + object-assign: "npm:^4.0.1" + thenify-all: "npm:^1.0.0" + checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.4, nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + languageName: node + linkType: hard + +"natural-compare@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare@npm:1.4.0" + checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447 + languageName: node + linkType: hard + +"negotiator@npm:^0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 + languageName: node + linkType: hard + +"neverthrow@npm:^6.2.1": + version: 6.2.1 + resolution: "neverthrow@npm:6.2.1" + checksum: 10c0/1f816edc4c52c63ed5143d10cae2f5a95373b510a2638f48074ea0d30e76416f707f3edd0455f59eea3496fe310c213897dd84a05acc504a746baea4095567e5 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 10.1.0 + resolution: "node-gyp@npm:10.1.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + glob: "npm:^10.3.10" + graceful-fs: "npm:^4.2.6" + make-fetch-happen: "npm:^13.0.0" + nopt: "npm:^7.0.0" + proc-log: "npm:^3.0.0" + semver: "npm:^7.3.5" + tar: "npm:^6.1.2" + which: "npm:^4.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/9cc821111ca244a01fb7f054db7523ab0a0cd837f665267eb962eb87695d71fb1e681f9e21464cc2fd7c05530dc4c81b810bca1a88f7d7186909b74477491a3c + languageName: node + linkType: hard + +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 10c0/199fc93773ae70ec9969bc6d5ac5b2bbd6eb986ed1907d751f411fef3ede0e4bfdb45ceb43711f8078bea237b6036db8b1bf208f6ff2b70c7d615afd157f3ab9 + languageName: node + linkType: hard + +"nopt@npm:^7.0.0, nopt@npm:^7.2.0": + version: 7.2.0 + resolution: "nopt@npm:7.2.0" + dependencies: + abbrev: "npm:^2.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/9bd7198df6f16eb29ff16892c77bcf7f0cc41f9fb5c26280ac0def2cf8cf319f3b821b3af83eba0e74c85807cc430a16efe0db58fe6ae1f41e69519f585b6aff + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 + languageName: node + linkType: hard + +"normalize-range@npm:^0.1.2": + version: 0.1.2 + resolution: "normalize-range@npm:0.1.2" + checksum: 10c0/bf39b73a63e0a42ad1a48c2bd1bda5a07ede64a7e2567307a407674e595bcff0fa0d57e8e5f1e7fa5e91000797c7615e13613227aaaa4d6d6e87f5bd5cc95de6 + languageName: node + linkType: hard + +"npm-normalize-package-bin@npm:^3.0.0": + version: 3.0.1 + resolution: "npm-normalize-package-bin@npm:3.0.1" + checksum: 10c0/f1831a7f12622840e1375c785c3dab7b1d82dd521211c17ee5e9610cd1a34d8b232d3fdeebf50c170eddcb321d2c644bf73dbe35545da7d588c6b3fa488db0a5 + languageName: node + linkType: hard + +"npm-run-all2@npm:^6.1.2": + version: 6.1.2 + resolution: "npm-run-all2@npm:6.1.2" + dependencies: + ansi-styles: "npm:^6.2.1" + cross-spawn: "npm:^7.0.3" + memorystream: "npm:^0.3.1" + minimatch: "npm:^9.0.0" + pidtree: "npm:^0.6.0" + read-package-json-fast: "npm:^3.0.2" + shell-quote: "npm:^1.7.3" + bin: + npm-run-all: bin/npm-run-all/index.js + npm-run-all2: bin/npm-run-all/index.js + run-p: bin/run-p/index.js + run-s: bin/run-s/index.js + checksum: 10c0/3cab02d23757f23e391037ea9a78732037c9c5d05d29f344d6fac330523aea45f84d472c2702dd5c048f23560e9954ed1f16c6090bb817a73db13842c67c17c0 + languageName: node + linkType: hard + +"npm-run-path@npm:^5.1.0": + version: 5.3.0 + resolution: "npm-run-path@npm:5.3.0" + dependencies: + path-key: "npm:^4.0.0" + checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba + languageName: node + linkType: hard + +"nth-check@npm:^2.1.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: "npm:^1.0.0" + checksum: 10c0/5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479 + languageName: node + linkType: hard + +"nwsapi@npm:^2.2.7": + version: 2.2.9 + resolution: "nwsapi@npm:2.2.9" + checksum: 10c0/e6ebbaedf44d1c1e13f7193e5129c8da1b2e8064862b70458ab9bd9e9640b8ad035a0e48d509e787527ecdddea74d5a02798420cd971264a4e03c2b173fadac8 + languageName: node + linkType: hard + +"object-assign@npm:^4.0.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + +"object-hash@npm:^3.0.0": + version: 3.0.0 + resolution: "object-hash@npm:3.0.0" + checksum: 10c0/a06844537107b960c1c8b96cd2ac8592a265186bfa0f6ccafe0d34eabdb526f6fa81da1f37c43df7ed13b12a4ae3457a16071603bcd39d8beddb5f08c37b0f47 + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.1": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: 10c0/fad603f408e345c82e946abdf4bfd774260a5ed3e5997a0b057c44153ac32c7271ff19e3a5ae39c858da683ba045ccac2f65245c12763ce4e8594f818f4a648d + languageName: node + linkType: hard + +"object-is@npm:^1.1.5": + version: 1.1.6 + resolution: "object-is@npm:1.1.6" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + checksum: 10c0/506af444c4dce7f8e31f34fc549e2fb8152d6b9c4a30c6e62852badd7f520b579c679af433e7a072f9d78eb7808d230dc12e1cf58da9154dfbf8813099ea0fe0 + languageName: node + linkType: hard + +"object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: 10c0/b11f7ccdbc6d406d1f186cdadb9d54738e347b2692a14439ca5ac70c225fa6db46db809711b78589866d47b25fc3e8dee0b4c722ac751e11180f9380e3d8601d + languageName: node + linkType: hard + +"object.assign@npm:^4.1.4": + version: 4.1.5 + resolution: "object.assign@npm:4.1.5" + dependencies: + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + has-symbols: "npm:^1.0.3" + object-keys: "npm:^1.1.1" + checksum: 10c0/60108e1fa2706f22554a4648299b0955236c62b3685c52abf4988d14fffb0e7731e00aa8c6448397e3eb63d087dcc124a9f21e1980f36d0b2667f3c18bacd469 + languageName: node + linkType: hard + +"onetime@npm:^6.0.0": + version: 6.0.0 + resolution: "onetime@npm:6.0.0" + dependencies: + mimic-fn: "npm:^4.0.0" + checksum: 10c0/4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c + languageName: node + linkType: hard + +"open@npm:^10.1.0": + version: 10.1.0 + resolution: "open@npm:10.1.0" + dependencies: + default-browser: "npm:^5.2.1" + define-lazy-prop: "npm:^3.0.0" + is-inside-container: "npm:^1.0.0" + is-wsl: "npm:^3.1.0" + checksum: 10c0/c86d0b94503d5f735f674158d5c5d339c25ec2927562f00ee74590727292ed23e1b8d9336cb41ffa7e1fa4d3641d29b199b4ea37c78cb557d72b511743e90ebb + languageName: node + linkType: hard + +"optionator@npm:^0.9.3": + version: 0.9.3 + resolution: "optionator@npm:0.9.3" + dependencies: + "@aashutoshrathi/word-wrap": "npm:^1.2.3" + deep-is: "npm:^0.1.3" + fast-levenshtein: "npm:^2.0.6" + levn: "npm:^0.4.1" + prelude-ls: "npm:^1.2.1" + type-check: "npm:^0.4.0" + checksum: 10c0/66fba794d425b5be51353035cf3167ce6cfa049059cbb93229b819167687e0f48d2bc4603fcb21b091c99acb516aae1083624675b15c4765b2e4693a085e959c + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + +"p-limit@npm:^5.0.0": + version: 5.0.0 + resolution: "p-limit@npm:5.0.0" + dependencies: + yocto-queue: "npm:^1.0.0" + checksum: 10c0/574e93b8895a26e8485eb1df7c4b58a1a6e8d8ae41b1750cc2cc440922b3d306044fc6e9a7f74578a883d46802d9db72b30f2e612690fcef838c173261b1ed83 + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: "npm:^3.0.2" + checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a + languageName: node + linkType: hard + +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: "npm:^3.0.0" + checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75 + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"parse5@npm:^7.0.0, parse5@npm:^7.1.2": + version: 7.1.2 + resolution: "parse5@npm:7.1.2" + dependencies: + entities: "npm:^4.4.0" + checksum: 10c0/297d7af8224f4b5cb7f6617ecdae98eeaed7f8cbd78956c42785e230505d5a4f07cef352af10d3006fa5c1544b76b57784d3a22d861ae071bbc460c649482bf4 + languageName: node + linkType: hard + +"path-browserify@npm:^1.0.1": + version: 1.0.1 + resolution: "path-browserify@npm:1.0.1" + checksum: 10c0/8b8c3fd5c66bd340272180590ae4ff139769e9ab79522e2eb82e3d571a89b8117c04147f65ad066dccfb42fcad902e5b7d794b3d35e0fd840491a8ddbedf8c66 + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"path-key@npm:^4.0.0": + version: 4.0.0 + resolution: "path-key@npm:4.0.0" + checksum: 10c0/794efeef32863a65ac312f3c0b0a99f921f3e827ff63afa5cb09a377e202c262b671f7b3832a4e64731003fa94af0263713962d317b9887bd1e0c48a342efba3 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.6, path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 + languageName: node + linkType: hard + +"path-scurry@npm:^1.10.2": + version: 1.10.2 + resolution: "path-scurry@npm:1.10.2" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/d723777fbf9627f201e64656680f66ebd940957eebacf780e6cce1c2919c29c116678b2d7dbf8821b3a2caa758d125f4444005ccec886a25c8f324504e48e601 + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c + languageName: node + linkType: hard + +"pathe@npm:^1.1.1, pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: 10c0/64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 + languageName: node + linkType: hard + +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: 10c0/f63e1bc1b33593cdf094ed6ff5c49c1c0dc5dc20a646ca9725cc7fe7cd9995002d51d5685b9b2ec6814342935748b711bafa840f84c0bb04e38ff40a335c94dc + languageName: node + linkType: hard + +"perfect-debounce@npm:^1.0.0": + version: 1.0.0 + resolution: "perfect-debounce@npm:1.0.0" + checksum: 10c0/e2baac416cae046ef1b270812cf9ccfb0f91c04ea36ac7f5b00bc84cb7f41bdbba087c0ab21b4e02a7ef3a1f1f6db399f137cecec46868bd7d8d88c2a9ee431f + languageName: node + linkType: hard + +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: 10c0/20a5b249e331c14479d94ec6817a182fd7a5680debae82705747b2db7ec50009a5f6648d0621c561b0572703f84dbef0858abcbd5856d3c5511426afcb1961f7 + languageName: node + linkType: hard + +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be + languageName: node + linkType: hard + +"pidtree@npm:^0.6.0": + version: 0.6.0 + resolution: "pidtree@npm:0.6.0" + bin: + pidtree: bin/pidtree.js + checksum: 10c0/0829ec4e9209e230f74ebf4265f5ccc9ebfb488334b525cb13f86ff801dca44b362c41252cd43ae4d7653a10a5c6ab3be39d2c79064d6895e0d78dc50a5ed6e9 + languageName: node + linkType: hard + +"pify@npm:^2.3.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 10c0/551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc + languageName: node + linkType: hard + +"pirates@npm:^4.0.1": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 10c0/00d5fa51f8dded94d7429700fb91a0c1ead00ae2c7fd27089f0c5b63e6eca36197fe46384631872690a66f390c5e27198e99006ab77ae472692ab9c2ca903f36 + languageName: node + linkType: hard + +"pkg-types@npm:^1.0.3": + version: 1.1.0 + resolution: "pkg-types@npm:1.1.0" + dependencies: + confbox: "npm:^0.1.7" + mlly: "npm:^1.6.1" + pathe: "npm:^1.1.2" + checksum: 10c0/b350da13d2dab7dc2fa9d65a08a2038d841d8d8c94bf3878dd911a522e20da50d6662bab510fa329e363e403892374b3b847ebf7b3e10011805cdefb00f228fd + languageName: node + linkType: hard + +"plantuml-encoder@npm:^1.4.0": + version: 1.4.0 + resolution: "plantuml-encoder@npm:1.4.0" + checksum: 10c0/f1301b02524ba1bcd70048437dd00127c7543f6e149a426c1bab0b729bf8df798121210c2655a4fd92cfcd82684f04788d696074183164ccccaef5e5380b024b + languageName: node + linkType: hard + +"plantuml-pipe@npm:^1.6.0": + version: 1.6.0 + resolution: "plantuml-pipe@npm:1.6.0" + dependencies: + binary-split: "npm:^1.0.5" + split2: "npm:^4.2.0" + checksum: 10c0/8d60ae0b60cdb93a866154d46612977eef9406311294234f911c58181075988c5035010b1639bdeabbe28015eeeac8bb532bd9016418fb888e82e1829bd5de52 + languageName: node + linkType: hard + +"possible-typed-array-names@npm:^1.0.0": + version: 1.0.0 + resolution: "possible-typed-array-names@npm:1.0.0" + checksum: 10c0/d9aa22d31f4f7680e20269db76791b41c3a32c01a373e25f8a4813b4d45f7456bfc2b6d68f752dc4aab0e0bb0721cb3d76fb678c9101cb7a16316664bc2c73fd + languageName: node + linkType: hard + +"postcss-import@npm:^15.1.0": + version: 15.1.0 + resolution: "postcss-import@npm:15.1.0" + dependencies: + postcss-value-parser: "npm:^4.0.0" + read-cache: "npm:^1.0.0" + resolve: "npm:^1.1.7" + peerDependencies: + postcss: ^8.0.0 + checksum: 10c0/518aee5c83ea6940e890b0be675a2588db68b2582319f48c3b4e06535a50ea6ee45f7e63e4309f8754473245c47a0372632378d1d73d901310f295a92f26f17b + languageName: node + linkType: hard + +"postcss-js@npm:^4.0.1": + version: 4.0.1 + resolution: "postcss-js@npm:4.0.1" + dependencies: + camelcase-css: "npm:^2.0.1" + peerDependencies: + postcss: ^8.4.21 + checksum: 10c0/af35d55cb873b0797d3b42529514f5318f447b134541844285c9ac31a17497297eb72296902967911bb737a75163441695737300ce2794e3bd8c70c13a3b106e + languageName: node + linkType: hard + +"postcss-load-config@npm:^4.0.1": + version: 4.0.2 + resolution: "postcss-load-config@npm:4.0.2" + dependencies: + lilconfig: "npm:^3.0.0" + yaml: "npm:^2.3.4" + peerDependencies: + postcss: ">=8.0.9" + ts-node: ">=9.0.0" + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + checksum: 10c0/3d7939acb3570b0e4b4740e483d6e555a3e2de815219cb8a3c8fc03f575a6bde667443aa93369c0be390af845cb84471bf623e24af833260de3a105b78d42519 + languageName: node + linkType: hard + +"postcss-modules-extract-imports@npm:^3.0.0": + version: 3.1.0 + resolution: "postcss-modules-extract-imports@npm:3.1.0" + peerDependencies: + postcss: ^8.1.0 + checksum: 10c0/402084bcab376083c4b1b5111b48ec92974ef86066f366f0b2d5b2ac2b647d561066705ade4db89875a13cb175b33dd6af40d16d32b2ea5eaf8bac63bd2bf219 + languageName: node + linkType: hard + +"postcss-modules-local-by-default@npm:^4.0.0": + version: 4.0.5 + resolution: "postcss-modules-local-by-default@npm:4.0.5" + dependencies: + icss-utils: "npm:^5.0.0" + postcss-selector-parser: "npm:^6.0.2" + postcss-value-parser: "npm:^4.1.0" + peerDependencies: + postcss: ^8.1.0 + checksum: 10c0/f4ad35abeb685ecb25f80c93d9fe23c8b89ee45ac4185f3560e701b4d7372f9b798577e79c5ed03b6d9c80bc923b001210c127c04ced781f43cda9e32b202a5b + languageName: node + linkType: hard + +"postcss-modules-scope@npm:^3.0.0": + version: 3.2.0 + resolution: "postcss-modules-scope@npm:3.2.0" + dependencies: + postcss-selector-parser: "npm:^6.0.4" + peerDependencies: + postcss: ^8.1.0 + checksum: 10c0/a2f5ffe372169b3feb8628cd785eb748bf12e344cfa57bce9e5cdc4fa5adcdb40d36daa86bb35dad53427703b185772aad08825b5783f745fcb1b6039454a84b + languageName: node + linkType: hard + +"postcss-modules-values@npm:^4.0.0": + version: 4.0.0 + resolution: "postcss-modules-values@npm:4.0.0" + dependencies: + icss-utils: "npm:^5.0.0" + peerDependencies: + postcss: ^8.1.0 + checksum: 10c0/dd18d7631b5619fb9921b198c86847a2a075f32e0c162e0428d2647685e318c487a2566cc8cc669fc2077ef38115cde7a068e321f46fb38be3ad49646b639dbc + languageName: node + linkType: hard + +"postcss-modules@npm:^6.0.0": + version: 6.0.0 + resolution: "postcss-modules@npm:6.0.0" + dependencies: + generic-names: "npm:^4.0.0" + icss-utils: "npm:^5.1.0" + lodash.camelcase: "npm:^4.3.0" + postcss-modules-extract-imports: "npm:^3.0.0" + postcss-modules-local-by-default: "npm:^4.0.0" + postcss-modules-scope: "npm:^3.0.0" + postcss-modules-values: "npm:^4.0.0" + string-hash: "npm:^1.1.1" + peerDependencies: + postcss: ^8.0.0 + checksum: 10c0/24b5240597472e75d115a6eb242cf6f94c284ee443c4572383092ac12246593887e0cec408f47cd57292485f1482b0936b77c25756bc8324dbd1035336a89ff4 + languageName: node + linkType: hard + +"postcss-nested@npm:^6.0.1": + version: 6.0.1 + resolution: "postcss-nested@npm:6.0.1" + dependencies: + postcss-selector-parser: "npm:^6.0.11" + peerDependencies: + postcss: ^8.2.14 + checksum: 10c0/2a50aa36d5d103c2e471954830489f4c024deed94fa066169101db55171368d5f80b32446b584029e0471feee409293d0b6b1d8ede361f6675ba097e477b3cbd + languageName: node + linkType: hard + +"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.0.15, postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4": + version: 6.0.16 + resolution: "postcss-selector-parser@npm:6.0.16" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/0e11657cb3181aaf9ff67c2e59427c4df496b4a1b6a17063fae579813f80af79d444bf38f82eeb8b15b4679653fd3089e66ef0283f9aab01874d885e6cf1d2cf + languageName: node + linkType: hard + +"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.1.0, postcss-value-parser@npm:^4.2.0": + version: 4.2.0 + resolution: "postcss-value-parser@npm:4.2.0" + checksum: 10c0/f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161 + languageName: node + linkType: hard + +"postcss@npm:^8.4.23, postcss@npm:^8.4.38": + version: 8.4.38 + resolution: "postcss@npm:8.4.38" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.2.0" + checksum: 10c0/955407b8f70cf0c14acf35dab3615899a2a60a26718a63c848cf3c29f2467b0533991b985a2b994430d890bd7ec2b1963e36352b0774a19143b5f591540f7c06 + languageName: node + linkType: hard + +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: 10c0/b00d617431e7886c520a6f498a2e14c75ec58f6d93ba48c3b639cf241b54232d90daa05d83a9e9b9fef6baa63cb7e1e4602c2372fea5bc169668401eb127d0cd + languageName: node + linkType: hard + +"prettier-linter-helpers@npm:^1.0.0": + version: 1.0.0 + resolution: "prettier-linter-helpers@npm:1.0.0" + dependencies: + fast-diff: "npm:^1.1.2" + checksum: 10c0/81e0027d731b7b3697ccd2129470ed9913ecb111e4ec175a12f0fcfab0096516373bf0af2fef132af50cafb0a905b74ff57996d615f59512bb9ac7378fcc64ab + languageName: node + linkType: hard + +"prettier@npm:^3.2.5": + version: 3.2.5 + resolution: "prettier@npm:3.2.5" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/ea327f37a7d46f2324a34ad35292af2ad4c4c3c3355da07313339d7e554320f66f65f91e856add8530157a733c6c4a897dc41b577056be5c24c40f739f5ee8c6 + languageName: node + linkType: hard + +"prettier@npm:^3.3.1": + version: 3.3.1 + resolution: "prettier@npm:3.3.1" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/c25a709c9f0be670dc6bcb190b622347e1dbeb6c3e7df8b0711724cb64d8647c60b839937a4df4df18e9cfb556c2b08ca9d24d9645eb5488a7fc032a2c4d5cb3 + languageName: node + linkType: hard + +"pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": "npm:^29.6.3" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^18.0.0" + checksum: 10c0/edc5ff89f51916f036c62ed433506b55446ff739358de77207e63e88a28ca2894caac6e73dcb68166a606e51c8087d32d400473e6a9fdd2dbe743f46c9c0276f + languageName: node + linkType: hard + +"proc-log@npm:^3.0.0": + version: 3.0.0 + resolution: "proc-log@npm:3.0.0" + checksum: 10c0/f66430e4ff947dbb996058f6fd22de2c66612ae1a89b097744e17fb18a4e8e7a86db99eda52ccf15e53f00b63f4ec0b0911581ff2aac0355b625c8eac509b0dc + languageName: node + linkType: hard + +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 10c0/bec089239487833d46b59d80327a1605e1c5287eaad770a291add7f45fda1bb5e28b38e0e061add0a1d0ee0984788ce74fa394d345eed1c420cacf392c554367 + languageName: node + linkType: hard + +"progress@npm:^2.0.3": + version: 2.0.3 + resolution: "progress@npm:2.0.3" + checksum: 10c0/1697e07cb1068055dbe9fe858d242368ff5d2073639e652b75a7eb1f2a1a8d4afd404d719de23c7b48481a6aa0040686310e2dac2f53d776daa2176d3f96369c + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: "npm:^2.0.2" + retry: "npm:^0.12.0" + checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 + languageName: node + linkType: hard + +"proto-list@npm:~1.2.1": + version: 1.2.4 + resolution: "proto-list@npm:1.2.4" + checksum: 10c0/b9179f99394ec8a68b8afc817690185f3b03933f7b46ce2e22c1930dc84b60d09f5ad222beab4e59e58c6c039c7f7fcf620397235ef441a356f31f9744010e12 + languageName: node + linkType: hard + +"psl@npm:^1.1.33": + version: 1.9.0 + resolution: "psl@npm:1.9.0" + checksum: 10c0/6a3f805fdab9442f44de4ba23880c4eba26b20c8e8e0830eff1cb31007f6825dace61d17203c58bfe36946842140c97a1ba7f67bc63ca2d88a7ee052b65d97ab + languageName: node + linkType: hard + +"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.1": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 + languageName: node + linkType: hard + +"querystringify@npm:^2.1.1": + version: 2.2.0 + resolution: "querystringify@npm:2.2.0" + checksum: 10c0/3258bc3dbdf322ff2663619afe5947c7926a6ef5fb78ad7d384602974c467fadfc8272af44f5eb8cddd0d011aae8fabf3a929a8eee4b86edcc0a21e6bd10f9aa + languageName: node + linkType: hard + +"queue-fifo@npm:^0.2.6": + version: 0.2.6 + resolution: "queue-fifo@npm:0.2.6" + dependencies: + dbly-linked-list: "npm:0.3.4" + checksum: 10c0/f6cd84ddf9bbbf16cefb38ab95639117ed0379b2c7f0d0316702d17423dd80421c740c027eb73e7a80d84787f7cb0344719c3fbce970bcafcf543867c6a8dc6f + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 + languageName: node + linkType: hard + +"react-is@npm:^18.0.0": + version: 18.2.0 + resolution: "react-is@npm:18.2.0" + checksum: 10c0/6eb5e4b28028c23e2bfcf73371e72cd4162e4ac7ab445ddae2afe24e347a37d6dc22fae6e1748632cd43c6d4f9b8f86dcf26bf9275e1874f436d129952528ae0 + languageName: node + linkType: hard + +"read-cache@npm:^1.0.0": + version: 1.0.0 + resolution: "read-cache@npm:1.0.0" + dependencies: + pify: "npm:^2.3.0" + checksum: 10c0/90cb2750213c7dd7c80cb420654344a311fdec12944e81eb912cd82f1bc92aea21885fa6ce442e3336d9fccd663b8a7a19c46d9698e6ca55620848ab932da814 + languageName: node + linkType: hard + +"read-package-json-fast@npm:^3.0.2": + version: 3.0.2 + resolution: "read-package-json-fast@npm:3.0.2" + dependencies: + json-parse-even-better-errors: "npm:^3.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + checksum: 10c0/37787e075f0260a92be0428687d9020eecad7ece3bda37461c2219e50d1ec183ab6ba1d9ada193691435dfe119a42c8a5b5b5463f08c8ddbc3d330800b265318 + languageName: node + linkType: hard + +"readable-stream@npm:~2.3.6": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.3" + isarray: "npm:~1.0.0" + process-nextick-args: "npm:~2.0.0" + safe-buffer: "npm:~5.1.1" + string_decoder: "npm:~1.1.1" + util-deprecate: "npm:~1.0.1" + checksum: 10c0/7efdb01f3853bc35ac62ea25493567bf588773213f5f4a79f9c365e1ad13bab845ac0dae7bc946270dc40c3929483228415e92a3fc600cc7e4548992f41ee3fa + languageName: node + linkType: hard + +"readdirp@npm:~3.6.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: "npm:^2.2.1" + checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b + languageName: node + linkType: hard + +"regexp.prototype.flags@npm:^1.5.1": + version: 1.5.2 + resolution: "regexp.prototype.flags@npm:1.5.2" + dependencies: + call-bind: "npm:^1.0.6" + define-properties: "npm:^1.2.1" + es-errors: "npm:^1.3.0" + set-function-name: "npm:^2.0.1" + checksum: 10c0/0f3fc4f580d9c349f8b560b012725eb9c002f36daa0041b3fbf6f4238cb05932191a4d7d5db3b5e2caa336d5150ad0402ed2be81f711f9308fe7e1a9bf9bd552 + languageName: node + linkType: hard + +"requires-port@npm:^1.0.0": + version: 1.0.0 + resolution: "requires-port@npm:1.0.0" + checksum: 10c0/b2bfdd09db16c082c4326e573a82c0771daaf7b53b9ce8ad60ea46aa6e30aaf475fe9b164800b89f93b748d2c234d8abff945d2551ba47bf5698e04cd7713267 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 + languageName: node + linkType: hard + +"resolve@npm:^1.1.7, resolve@npm:^1.22.2, resolve@npm:~1.22.1": + version: 1.22.8 + resolution: "resolve@npm:1.22.8" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/07e179f4375e1fd072cfb72ad66d78547f86e6196c4014b31cb0b8bb1db5f7ca871f922d08da0fbc05b94e9fd42206f819648fa3b5b873ebbc8e1dc68fec433a + languageName: node + linkType: hard + +"resolve@npm:~1.19.0": + version: 1.19.0 + resolution: "resolve@npm:1.19.0" + dependencies: + is-core-module: "npm:^2.1.0" + path-parse: "npm:^1.0.6" + checksum: 10c0/1c8afdfb88c9adab0a19b6f16756d47f5917f64047bf5a38c17aa543aae5ccca2a0631671b19ce8460a7a3e65ead98ee70e046d3056ec173d3377a27487848a8 + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.22.2#optional!builtin, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin": + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/0446f024439cd2e50c6c8fa8ba77eaa8370b4180f401a96abf3d1ebc770ac51c1955e12764cde449fde3fff480a61f84388e3505ecdbab778f4bef5f8212c729 + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A~1.19.0#optional!builtin": + version: 1.19.0 + resolution: "resolve@patch:resolve@npm%3A1.19.0#optional!builtin::version=1.19.0&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.1.0" + path-parse: "npm:^1.0.6" + checksum: 10c0/254980f60dd9fdb28b34a511e70df6e3027d9627efce86a40757eea9b87252d172829c84517554560c4541ebfe207868270c19a0f086997b41209367aa8ef74f + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.0.4 + resolution: "reusify@npm:1.0.4" + checksum: 10c0/c19ef26e4e188f408922c46f7ff480d38e8dfc55d448310dfb518736b23ed2c4f547fb64a6ed5bdba92cd7e7ddc889d36ff78f794816d5e71498d645ef476107 + languageName: node + linkType: hard + +"rfdc@npm:^1.3.1": + version: 1.3.1 + resolution: "rfdc@npm:1.3.1" + checksum: 10c0/69f65e3ed30970f8055fac9fbbef9ce578800ca19554eab1dcbffe73a4b8aef536bc4248313889cf25e3b4e38b212c721eabe30856575bf2b2bc3d90f8ba93ef + languageName: node + linkType: hard + +"rollup@npm:^4.13.0": + version: 4.16.4 + resolution: "rollup@npm:4.16.4" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.16.4" + "@rollup/rollup-android-arm64": "npm:4.16.4" + "@rollup/rollup-darwin-arm64": "npm:4.16.4" + "@rollup/rollup-darwin-x64": "npm:4.16.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.16.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.16.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.16.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.16.4" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.16.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.16.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.16.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.16.4" + "@rollup/rollup-linux-x64-musl": "npm:4.16.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.16.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.16.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.16.4" + "@types/estree": "npm:1.0.5" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/f88017e8a599b055c555fe9b9dc2eee3def3067701600492a2dc2ed3ba78c3f0b1d7927f9ed934afef936167a73447121e8f7fbc4804b73f6c181e2d7f52e853 + languageName: node + linkType: hard + +"root-workspace-0b6124@workspace:.": + version: 0.0.0-use.local + resolution: "root-workspace-0b6124@workspace:." + languageName: unknown + linkType: soft + +"rrweb-cssom@npm:^0.6.0": + version: 0.6.0 + resolution: "rrweb-cssom@npm:0.6.0" + checksum: 10c0/3d9d90d53c2349ea9c8509c2690df5a4ef930c9cf8242aeb9425d4046f09d712bb01047e00da0e1c1dab5db35740b3d78fd45c3e7272f75d3724a563f27c30a3 + languageName: node + linkType: hard + +"run-applescript@npm:^7.0.0": + version: 7.0.0 + resolution: "run-applescript@npm:7.0.0" + checksum: 10c0/bd821bbf154b8e6c8ecffeaf0c33cebbb78eb2987476c3f6b420d67ab4c5301faa905dec99ded76ebb3a7042b4e440189ae6d85bbbd3fc6e8d493347ecda8bfe + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 + languageName: node + linkType: hard + +"safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: 10c0/780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + +"saxes@npm:^6.0.0": + version: 6.0.0 + resolution: "saxes@npm:6.0.0" + dependencies: + xmlchars: "npm:^2.2.0" + checksum: 10c0/3847b839f060ef3476eb8623d099aa502ad658f5c40fd60c105ebce86d244389b0d76fcae30f4d0c728d7705ceb2f7e9b34bb54717b6a7dbedaf5dad2d9a4b74 + languageName: node + linkType: hard + +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.3.6, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": + version: 7.6.0 + resolution: "semver@npm:7.6.0" + dependencies: + lru-cache: "npm:^6.0.0" + bin: + semver: bin/semver.js + checksum: 10c0/fbfe717094ace0aa8d6332d7ef5ce727259815bd8d8815700853f4faf23aacbd7192522f0dc5af6df52ef4fa85a355ebd2f5d39f554bd028200d6cf481ab9b53 + languageName: node + linkType: hard + +"semver@npm:~7.5.4": + version: 7.5.4 + resolution: "semver@npm:7.5.4" + dependencies: + lru-cache: "npm:^6.0.0" + bin: + semver: bin/semver.js + checksum: 10c0/5160b06975a38b11c1ab55950cb5b8a23db78df88275d3d8a42ccf1f29e55112ac995b3a26a522c36e3b5f76b0445f1eef70d696b8c7862a2b4303d7b0e7609e + languageName: node + linkType: hard + +"set-function-length@npm:^1.2.1": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.1": + version: 2.0.2 + resolution: "set-function-name@npm:2.0.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + functions-have-names: "npm:^1.2.3" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/fce59f90696c450a8523e754abb305e2b8c73586452619c2bad5f7bf38c7b6b4651895c9db895679c5bef9554339cf3ef1c329b66ece3eda7255785fbe299316 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"shell-quote@npm:^1.7.3": + version: 1.8.1 + resolution: "shell-quote@npm:1.8.1" + checksum: 10c0/8cec6fd827bad74d0a49347057d40dfea1e01f12a6123bf82c4649f3ef152fc2bc6d6176e6376bffcd205d9d0ccb4f1f9acae889384d20baff92186f01ea455a + languageName: node + linkType: hard + +"shiki@npm:^0.14.7": + version: 0.14.7 + resolution: "shiki@npm:0.14.7" + dependencies: + ansi-sequence-parser: "npm:^1.1.0" + jsonc-parser: "npm:^3.2.0" + vscode-oniguruma: "npm:^1.7.0" + vscode-textmate: "npm:^8.0.0" + checksum: 10c0/5c7fcbb870d0facccc7ae2f3410a28121f8e0b3f298e4e956de817ad6ab60a4c7e20a9184edfe50a93447addbb88b95b69e6ef88ac16ac6ca3e94c50771a6459 + languageName: node + linkType: hard + +"side-channel@npm:^1.0.4": + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + object-inspect: "npm:^1.13.1" + checksum: 10c0/d2afd163dc733cc0a39aa6f7e39bf0c436293510dbccbff446733daeaf295857dbccf94297092ec8c53e2503acac30f0b78830876f0485991d62a90e9cad305f + languageName: node + linkType: hard + +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"sirv@npm:^2.0.4": + version: 2.0.4 + resolution: "sirv@npm:2.0.4" + dependencies: + "@polka/url": "npm:^1.0.0-next.24" + mrmime: "npm:^2.0.0" + totalist: "npm:^3.0.0" + checksum: 10c0/68f8ee857f6a9415e9c07a1f31c7c561df8d5f1b1ba79bee3de583fa37da8718def5309f6b1c6e2c3ef77de45d74f5e49efc7959214443aa92d42e9c99180a4e + languageName: node + linkType: hard + +"slash@npm:^3.0.0": + version: 3.0.0 + resolution: "slash@npm:3.0.0" + checksum: 10c0/e18488c6a42bdfd4ac5be85b2ced3ccd0224773baae6ad42cfbb9ec74fc07f9fa8396bd35ee638084ead7a2a0818eb5e7151111544d4731ce843019dab4be47b + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.3 + resolution: "socks-proxy-agent@npm:8.0.3" + dependencies: + agent-base: "npm:^7.1.1" + debug: "npm:^4.3.4" + socks: "npm:^2.7.1" + checksum: 10c0/4950529affd8ccd6951575e21c1b7be8531b24d924aa4df3ee32df506af34b618c4e50d261f4cc603f1bfd8d426915b7d629966c8ce45b05fb5ad8c8b9a6459d + languageName: node + linkType: hard + +"socks@npm:^2.7.1": + version: 2.8.3 + resolution: "socks@npm:2.8.3" + dependencies: + ip-address: "npm:^9.0.5" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7 + languageName: node + linkType: hard + +"source-map-js@npm:^1.2.0": + version: 1.2.0 + resolution: "source-map-js@npm:1.2.0" + checksum: 10c0/7e5f896ac10a3a50fe2898e5009c58ff0dc102dcb056ed27a354623a0ece8954d4b2649e1a1b2b52ef2e161d26f8859c7710350930751640e71e374fe2d321a4 + languageName: node + linkType: hard + +"source-map@npm:~0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"speakingurl@npm:^14.0.1": + version: 14.0.1 + resolution: "speakingurl@npm:14.0.1" + checksum: 10c0/1de1d1b938a7c4d9e79593ff7a26d312ec04a7c3234ca40b7f9b8106daf74ea9d2110a077f5db97ecf3762b83069e3ccbf9694431b51d4fcfd863f0b3333c342 + languageName: node + linkType: hard + +"split2@npm:^4.2.0": + version: 4.2.0 + resolution: "split2@npm:4.2.0" + checksum: 10c0/b292beb8ce9215f8c642bb68be6249c5a4c7f332fc8ecadae7be5cbdf1ea95addc95f0459ef2e7ad9d45fd1064698a097e4eb211c83e772b49bc0ee423e91534 + languageName: node + linkType: hard + +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec + languageName: node + linkType: hard + +"sprintf-js@npm:~1.0.2": + version: 1.0.3 + resolution: "sprintf-js@npm:1.0.3" + checksum: 10c0/ecadcfe4c771890140da5023d43e190b7566d9cf8b2d238600f31bec0fc653f328da4450eb04bd59a431771a8e9cc0e118f0aa3974b683a4981b4e07abc2a5bb + languageName: node + linkType: hard + +"ssri@npm:^10.0.0": + version: 10.0.5 + resolution: "ssri@npm:10.0.5" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/b091f2ae92474183c7ac5ed3f9811457e1df23df7a7e70c9476eaa9a0c4a0c8fc190fb45acefbf023ca9ee864dd6754237a697dc52a0fb182afe65d8e77443d8 + languageName: node + linkType: hard + +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + +"std-env@npm:^3.5.0": + version: 3.7.0 + resolution: "std-env@npm:3.7.0" + checksum: 10c0/60edf2d130a4feb7002974af3d5a5f3343558d1ccf8d9b9934d225c638606884db4a20d2fe6440a09605bca282af6b042ae8070a10490c0800d69e82e478f41e + languageName: node + linkType: hard + +"stop-iteration-iterator@npm:^1.0.0": + version: 1.0.0 + resolution: "stop-iteration-iterator@npm:1.0.0" + dependencies: + internal-slot: "npm:^1.0.4" + checksum: 10c0/c4158d6188aac510d9e92925b58709207bd94699e9c31186a040c80932a687f84a51356b5895e6dc72710aad83addb9411c22171832c9ae0e6e11b7d61b0dfb9 + languageName: node + linkType: hard + +"string-argv@npm:~0.3.1": + version: 0.3.2 + resolution: "string-argv@npm:0.3.2" + checksum: 10c0/75c02a83759ad1722e040b86823909d9a2fc75d15dd71ec4b537c3560746e33b5f5a07f7332d1e3f88319909f82190843aa2f0a0d8c8d591ec08e93d5b8dec82 + languageName: node + linkType: hard + +"string-hash@npm:^1.1.1": + version: 1.1.3 + resolution: "string-hash@npm:1.1.3" + checksum: 10c0/179725d7706b49fbbc0a4901703a2d8abec244140879afd5a17908497e586a6b07d738f6775450aefd9f8dd729e4a0abd073fbc6fa3bd020b7a1d2369614af88 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: "npm:~5.1.0" + checksum: 10c0/b4f89f3a92fd101b5653ca3c99550e07bdf9e13b35037e9e2a1c7b47cec4e55e06ff3fc468e314a0b5e80bfbaf65c1ca5a84978764884ae9413bec1fc6ca924e + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10c0/a198c3762e8832505328cbf9e8c8381de14a4fa50a4f9b2160138158ea88c0f5549fb50cb13c651c3088f47e63a108b34622ec18c0499b6c8c3a5ddf6b305ac4 + languageName: node + linkType: hard + +"strip-final-newline@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-final-newline@npm:3.0.0" + checksum: 10c0/a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce + languageName: node + linkType: hard + +"strip-json-comments@npm:^3.1.1, strip-json-comments@npm:~3.1.1": + version: 3.1.1 + resolution: "strip-json-comments@npm:3.1.1" + checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd + languageName: node + linkType: hard + +"strip-literal@npm:^2.0.0": + version: 2.1.0 + resolution: "strip-literal@npm:2.1.0" + dependencies: + js-tokens: "npm:^9.0.0" + checksum: 10c0/bc8b8c8346125ae3c20fcdaf12e10a498ff85baf6f69597b4ab2b5fbf2e58cfd2827f1a44f83606b852da99a5f6c8279770046ddea974c510c17c98934c9cc24 + languageName: node + linkType: hard + +"sucrase@npm:^3.32.0": + version: 3.35.0 + resolution: "sucrase@npm:3.35.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.2" + commander: "npm:^4.0.0" + glob: "npm:^10.3.10" + lines-and-columns: "npm:^1.1.6" + mz: "npm:^2.7.0" + pirates: "npm:^4.0.1" + ts-interface-checker: "npm:^0.1.9" + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: 10c0/ac85f3359d2c2ecbf5febca6a24ae9bf96c931f05fde533c22a94f59c6a74895e5d5f0e871878dfd59c2697a75ebb04e4b2224ef0bfc24ca1210735c2ec191ef + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: "npm:^3.0.0" + checksum: 10c0/6ae5ff319bfbb021f8a86da8ea1f8db52fac8bd4d499492e30ec17095b58af11f0c55f8577390a749b1c4dde691b6a0315dab78f5f54c9b3d83f8fb5905c1c05 + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124 + languageName: node + linkType: hard + +"supports-color@npm:~8.1.1": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 + languageName: node + linkType: hard + +"svg-tags@npm:^1.0.0": + version: 1.0.0 + resolution: "svg-tags@npm:1.0.0" + checksum: 10c0/5867e29e8f431bf7aecf5a244d1af5725f80a1086187dbc78f26d8433b5e96b8fe9361aeb10d1699ff483b9afec785a10916b9312fe9d734d1a7afd48226c954 + languageName: node + linkType: hard + +"symbol-tree@npm:^3.2.4": + version: 3.2.4 + resolution: "symbol-tree@npm:3.2.4" + checksum: 10c0/dfbe201ae09ac6053d163578778c53aa860a784147ecf95705de0cd23f42c851e1be7889241495e95c37cabb058edb1052f141387bef68f705afc8f9dd358509 + languageName: node + linkType: hard + +"synckit@npm:^0.8.6": + version: 0.8.8 + resolution: "synckit@npm:0.8.8" + dependencies: + "@pkgr/core": "npm:^0.1.0" + tslib: "npm:^2.6.2" + checksum: 10c0/c3d3aa8e284f3f84f2f868b960c9f49239b364e35f6d20825a448449a3e9c8f49fe36cdd5196b30615682f007830d46f2ea354003954c7336723cb821e4b6519 + languageName: node + linkType: hard + +"tailwindcss@npm:^3.4.3": + version: 3.4.3 + resolution: "tailwindcss@npm:3.4.3" + dependencies: + "@alloc/quick-lru": "npm:^5.2.0" + arg: "npm:^5.0.2" + chokidar: "npm:^3.5.3" + didyoumean: "npm:^1.2.2" + dlv: "npm:^1.1.3" + fast-glob: "npm:^3.3.0" + glob-parent: "npm:^6.0.2" + is-glob: "npm:^4.0.3" + jiti: "npm:^1.21.0" + lilconfig: "npm:^2.1.0" + micromatch: "npm:^4.0.5" + normalize-path: "npm:^3.0.0" + object-hash: "npm:^3.0.0" + picocolors: "npm:^1.0.0" + postcss: "npm:^8.4.23" + postcss-import: "npm:^15.1.0" + postcss-js: "npm:^4.0.1" + postcss-load-config: "npm:^4.0.1" + postcss-nested: "npm:^6.0.1" + postcss-selector-parser: "npm:^6.0.11" + resolve: "npm:^1.22.2" + sucrase: "npm:^3.32.0" + bin: + tailwind: lib/cli.js + tailwindcss: lib/cli.js + checksum: 10c0/11e5546494f2888f693ebaa271b218b3a8e52fe59d7b629e54f2dffd6eaafd5ded2e9f0c37ad04e6a866dffb2b116d91becebad77e1441beee8bf016bb2392f9 + languageName: node + linkType: hard + +"tar@npm:^6.1.11, tar@npm:^6.1.2": + version: 6.2.1 + resolution: "tar@npm:6.2.1" + dependencies: + chownr: "npm:^2.0.0" + fs-minipass: "npm:^2.0.0" + minipass: "npm:^5.0.0" + minizlib: "npm:^2.1.1" + mkdirp: "npm:^1.0.3" + yallist: "npm:^4.0.0" + checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537 + languageName: node + linkType: hard + +"text-table@npm:^0.2.0": + version: 0.2.0 + resolution: "text-table@npm:0.2.0" + checksum: 10c0/02805740c12851ea5982686810702e2f14369a5f4c5c40a836821e3eefc65ffeec3131ba324692a37608294b0fd8c1e55a2dd571ffed4909822787668ddbee5c + languageName: node + linkType: hard + +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + +"through2@npm:^2.0.3": + version: 2.0.5 + resolution: "through2@npm:2.0.5" + dependencies: + readable-stream: "npm:~2.3.6" + xtend: "npm:~4.0.1" + checksum: 10c0/cbfe5b57943fa12b4f8c043658c2a00476216d79c014895cef1ac7a1d9a8b31f6b438d0e53eecbb81054b93128324a82ecd59ec1a4f91f01f7ac113dcb14eade + languageName: node + linkType: hard + +"tinybench@npm:^2.5.1": + version: 2.8.0 + resolution: "tinybench@npm:2.8.0" + checksum: 10c0/5a9a642351fa3e4955e0cbf38f5674be5f3ba6730fd872fd23a5c953ad6c914234d5aba6ea41ef88820180a81829ceece5bd8d3967c490c5171bca1141c2f24d + languageName: node + linkType: hard + +"tinypool@npm:^0.8.3": + version: 0.8.4 + resolution: "tinypool@npm:0.8.4" + checksum: 10c0/779c790adcb0316a45359652f4b025958c1dff5a82460fe49f553c864309b12ad732c8288be52f852973bc76317f5e7b3598878aee0beb8a33322c0e72c4a66c + languageName: node + linkType: hard + +"tinyspy@npm:^2.2.0": + version: 2.2.1 + resolution: "tinyspy@npm:2.2.1" + checksum: 10c0/0b4cfd07c09871e12c592dfa7b91528124dc49a4766a0b23350638c62e6a483d5a2a667de7e6282246c0d4f09996482ddaacbd01f0c05b7ed7e0f79d32409bdc + languageName: node + linkType: hard + +"to-fast-properties@npm:^2.0.0": + version: 2.0.0 + resolution: "to-fast-properties@npm:2.0.0" + checksum: 10c0/b214d21dbfb4bce3452b6244b336806ffea9c05297148d32ebb428d5c43ce7545bdfc65a1ceb58c9ef4376a65c0cb2854d645f33961658b3e3b4f84910ddcdd7 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"totalist@npm:^3.0.0": + version: 3.0.1 + resolution: "totalist@npm:3.0.1" + checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863 + languageName: node + linkType: hard + +"tough-cookie@npm:^4.1.3": + version: 4.1.3 + resolution: "tough-cookie@npm:4.1.3" + dependencies: + psl: "npm:^1.1.33" + punycode: "npm:^2.1.1" + universalify: "npm:^0.2.0" + url-parse: "npm:^1.5.3" + checksum: 10c0/4fc0433a0cba370d57c4b240f30440c848906dee3180bb6e85033143c2726d322e7e4614abb51d42d111ebec119c4876ed8d7247d4113563033eebbc1739c831 + languageName: node + linkType: hard + +"tr46@npm:^5.0.0": + version: 5.0.0 + resolution: "tr46@npm:5.0.0" + dependencies: + punycode: "npm:^2.3.1" + checksum: 10c0/1521b6e7bbc8adc825c4561480f9fe48eb2276c81335eed9fa610aa4c44a48a3221f78b10e5f18b875769eb3413e30efbf209ed556a17a42aa8d690df44b7bee + languageName: node + linkType: hard + +"ts-api-utils@npm:^1.3.0": + version: 1.3.0 + resolution: "ts-api-utils@npm:1.3.0" + peerDependencies: + typescript: ">=4.2.0" + checksum: 10c0/f54a0ba9ed56ce66baea90a3fa087a484002e807f28a8ccb2d070c75e76bde64bd0f6dce98b3802834156306050871b67eec325cb4e918015a360a3f0868c77c + languageName: node + linkType: hard + +"ts-interface-checker@npm:^0.1.9": + version: 0.1.13 + resolution: "ts-interface-checker@npm:0.1.13" + checksum: 10c0/232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7 + languageName: node + linkType: hard + +"ts-node@npm:^10.9.2": + version: 10.9.2 + resolution: "ts-node@npm:10.9.2" + dependencies: + "@cspotcode/source-map-support": "npm:^0.8.0" + "@tsconfig/node10": "npm:^1.0.7" + "@tsconfig/node12": "npm:^1.0.7" + "@tsconfig/node14": "npm:^1.0.0" + "@tsconfig/node16": "npm:^1.0.2" + acorn: "npm:^8.4.1" + acorn-walk: "npm:^8.1.1" + arg: "npm:^4.1.0" + create-require: "npm:^1.1.0" + diff: "npm:^4.0.1" + make-error: "npm:^1.1.1" + v8-compile-cache-lib: "npm:^3.0.1" + yn: "npm:3.1.1" + 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 + bin: + ts-node: dist/bin.js + ts-node-cwd: dist/bin-cwd.js + ts-node-esm: dist/bin-esm.js + ts-node-script: dist/bin-script.js + ts-node-transpile-only: dist/bin-transpile.js + ts-script: dist/bin-script-deprecated.js + checksum: 10c0/5f29938489f96982a25ba650b64218e83a3357d76f7bede80195c65ab44ad279c8357264639b7abdd5d7e75fc269a83daa0e9c62fd8637a3def67254ecc9ddc2 + languageName: node + linkType: hard + +"tslib@npm:^2.6.2": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 10c0/e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb + languageName: node + linkType: hard + +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: "npm:^1.2.1" + checksum: 10c0/7b3fd0ed43891e2080bf0c5c504b418fbb3e5c7b9708d3d015037ba2e6323a28152ec163bcb65212741fa5d2022e3075ac3c76440dbd344c9035f818e8ecee58 + languageName: node + linkType: hard + +"type-detect@npm:^4.0.0, type-detect@npm:^4.0.8": + version: 4.0.8 + resolution: "type-detect@npm:4.0.8" + checksum: 10c0/8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd + languageName: node + linkType: hard + +"type-fest@npm:^0.20.2": + version: 0.20.2 + resolution: "type-fest@npm:0.20.2" + checksum: 10c0/dea9df45ea1f0aaa4e2d3bed3f9a0bfe9e5b2592bddb92eb1bf06e50bcf98dbb78189668cd8bc31a0511d3fc25539b4cd5c704497e53e93e2d40ca764b10bfc3 + languageName: node + linkType: hard + +"typedoc-plugin-mdn-links@npm:^3.1.25": + version: 3.1.25 + resolution: "typedoc-plugin-mdn-links@npm:3.1.25" + peerDependencies: + typedoc: ">= 0.23.14 || 0.24.x || 0.25.x" + checksum: 10c0/3503329783e76efac07786073dcab63bf2f86e05d107ec14e369de4ecadf34255260bbab2844c47ead1c7e30a2116fcd376fab8aa07c2d976b8c4651e6832c55 + languageName: node + linkType: hard + +"typedoc-plugin-missing-exports@npm:^2.2.0": + version: 2.2.0 + resolution: "typedoc-plugin-missing-exports@npm:2.2.0" + peerDependencies: + typedoc: 0.24.x || 0.25.x + checksum: 10c0/b5599e0a3ae901604b845f16d617bf965fd549eaef892e259439e91b4d9a8518662d6a4f8640604ef65ed2768c074c887e3a3e8a61dadb285019e3399dc21d90 + languageName: node + linkType: hard + +"typedoc-plugin-vue@npm:^1.1.0": + version: 1.1.0 + resolution: "typedoc-plugin-vue@npm:1.1.0" + peerDependencies: + typedoc: 0.25.x + checksum: 10c0/b1e99c5eea9520fe3d8dc76fafd17685d60a7405d3a0c19cd607de0de6b9a25f494f302c3700778a72a24808eede1d0d81afdf306ec5a7cffbf18361b5a88c33 + languageName: node + linkType: hard + +"typedoc-umlclass@npm:^0.9.0": + version: 0.9.0 + resolution: "typedoc-umlclass@npm:0.9.0" + dependencies: + decorator-cache-getter: "npm:^1.0.0" + plantuml-encoder: "npm:^1.4.0" + plantuml-pipe: "npm:^1.6.0" + progress: "npm:^2.0.3" + queue-fifo: "npm:^0.2.6" + peerDependencies: + typedoc: 0.24.x || 0.25.x + checksum: 10c0/7de9ea52748c4fe7f8b46aec69295bdb161124bdcdf05b53d1e3ed0da9d5ed4922a6c9e93b0ab931f9e147b789ade7521a0a5f0ded83a02514368d2b010deee6 + languageName: node + linkType: hard + +"typedoc@npm:^0.25.13": + version: 0.25.13 + resolution: "typedoc@npm:0.25.13" + dependencies: + lunr: "npm:^2.3.9" + marked: "npm:^4.3.0" + minimatch: "npm:^9.0.3" + shiki: "npm:^0.14.7" + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x + bin: + typedoc: bin/typedoc + checksum: 10c0/13878e6a9fc2b65d65e3b514efa11b43bdfd57149861cefc4a969ec213f4bc4b36ee9239d0b654ae18bcbbd5174206d409383f9000b7bdea22da1945f7ac91de + languageName: node + linkType: hard + +"typescript@npm:5.4.2": + version: 5.4.2 + resolution: "typescript@npm:5.4.2" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/583ff68cafb0c076695f72d61df6feee71689568179fb0d3a4834dac343df6b6ed7cf7b6f6c801fa52d43cd1d324e2f2d8ae4497b09f9e6cfe3d80a6d6c9ca52 + languageName: node + linkType: hard + +"typescript@npm:^5.4.5, typescript@npm:~5.4.5": + version: 5.4.5 + resolution: "typescript@npm:5.4.5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/2954022ada340fd3d6a9e2b8e534f65d57c92d5f3989a263754a78aba549f7e6529acc1921913560a4b816c46dce7df4a4d29f9f11a3dc0d4213bb76d043251e + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A5.4.2#optional!builtin": + version: 5.4.2 + resolution: "typescript@patch:typescript@npm%3A5.4.2#optional!builtin::version=5.4.2&hash=5adc0c" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/fcf6658073d07283910d9a0e04b1d5d0ebc822c04dbb7abdd74c3151c7aa92fcddbac7d799404e358197222006ccdc4c0db219d223d2ee4ccd9e2b01333b49be + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.4.5#optional!builtin, typescript@patch:typescript@npm%3A~5.4.5#optional!builtin": + version: 5.4.5 + resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin::version=5.4.5&hash=5adc0c" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/db2ad2a16ca829f50427eeb1da155e7a45e598eec7b086d8b4e8ba44e5a235f758e606d681c66992230d3fc3b8995865e5fd0b22a2c95486d0b3200f83072ec9 + languageName: node + linkType: hard + +"ufo@npm:^1.3.2": + version: 1.5.3 + resolution: "ufo@npm:1.5.3" + checksum: 10c0/1df10702582aa74f4deac4486ecdfd660e74be057355f1afb6adfa14243476cf3d3acff734ccc3d0b74e9bfdefe91d578f3edbbb0a5b2430fe93cd672370e024 + languageName: node + linkType: hard + +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 + languageName: node + linkType: hard + +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" + dependencies: + unique-slug: "npm:^4.0.0" + checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f + languageName: node + linkType: hard + +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635 + languageName: node + linkType: hard + +"universalify@npm:^0.1.0": + version: 0.1.2 + resolution: "universalify@npm:0.1.2" + checksum: 10c0/e70e0339f6b36f34c9816f6bf9662372bd241714dc77508d231d08386d94f2c4aa1ba1318614f92015f40d45aae1b9075cd30bd490efbe39387b60a76ca3f045 + languageName: node + linkType: hard + +"universalify@npm:^0.2.0": + version: 0.2.0 + resolution: "universalify@npm:0.2.0" + checksum: 10c0/cedbe4d4ca3967edf24c0800cfc161c5a15e240dac28e3ce575c689abc11f2c81ccc6532c8752af3b40f9120fb5e454abecd359e164f4f6aa44c29cd37e194fe + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.0.13": + version: 1.0.13 + resolution: "update-browserslist-db@npm:1.0.13" + dependencies: + escalade: "npm:^3.1.1" + picocolors: "npm:^1.0.0" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/e52b8b521c78ce1e0c775f356cd16a9c22c70d25f3e01180839c407a5dc787fb05a13f67560cbaf316770d26fa99f78f1acd711b1b54a4f35d4820d4ea7136e6 + languageName: node + linkType: hard + +"uri-js@npm:^4.2.2": + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10c0/4ef57b45aa820d7ac6496e9208559986c665e49447cb072744c13b66925a362d96dd5a46c4530a6b8e203e5db5fe849369444440cb22ecfc26c679359e5dfa3c + languageName: node + linkType: hard + +"url-parse@npm:^1.5.3": + version: 1.5.10 + resolution: "url-parse@npm:1.5.10" + dependencies: + querystringify: "npm:^2.1.1" + requires-port: "npm:^1.0.0" + checksum: 10c0/bd5aa9389f896974beb851c112f63b466505a04b4807cea2e5a3b7092f6fbb75316f0491ea84e44f66fed55f1b440df5195d7e3a8203f64fcefa19d182f5be87 + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + +"uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 10c0/1607dd32ac7fc22f2d8f77051e6a64845c9bce5cd3dd8aa0070c074ec73e666a1f63c7b4e0f4bf2bc8b9d59dc85a15e17807446d9d2b17c8485fbc2147b27f9b + languageName: node + linkType: hard + +"v8-compile-cache-lib@npm:^3.0.1": + version: 3.0.1 + resolution: "v8-compile-cache-lib@npm:3.0.1" + checksum: 10c0/bdc36fb8095d3b41df197f5fb6f11e3a26adf4059df3213e3baa93810d8f0cc76f9a74aaefc18b73e91fe7e19154ed6f134eda6fded2e0f1c8d2272ed2d2d391 + languageName: node + linkType: hard + +"validator@npm:^13.7.0": + version: 13.11.0 + resolution: "validator@npm:13.11.0" + checksum: 10c0/0107da3add5a4ebc6391dac103c55f6d8ed055bbcc29a4c9cbf89eacfc39ba102a5618c470bdc33c6487d30847771a892134a8c791f06ef0962dd4b7a60ae0f5 + languageName: node + linkType: hard + +"vite-hot-client@npm:^0.2.3": + version: 0.2.3 + resolution: "vite-hot-client@npm:0.2.3" + peerDependencies: + vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 + checksum: 10c0/e60eedfb3c06bf70abde65e46ca1cde72fa5449bdd16a8951a6605360f7a036e0622901f75eaa248258ad8446314d05fcdf8d3ca95648ad33a4119fb1576dfd3 + languageName: node + linkType: hard + +"vite-node@npm:1.6.0": + version: 1.6.0 + resolution: "vite-node@npm:1.6.0" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.3.4" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + vite: "npm:^5.0.0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/0807e6501ac7763e0efa2b4bd484ce99fb207e92c98624c9f8999d1f6727ac026e457994260fa7fdb7060d87546d197081e46a705d05b0136a38b6f03715cbc2 + languageName: node + linkType: hard + +"vite-plugin-dts@npm:^3.9.1": + version: 3.9.1 + resolution: "vite-plugin-dts@npm:3.9.1" + dependencies: + "@microsoft/api-extractor": "npm:7.43.0" + "@rollup/pluginutils": "npm:^5.1.0" + "@vue/language-core": "npm:^1.8.27" + debug: "npm:^4.3.4" + kolorist: "npm:^1.8.0" + magic-string: "npm:^0.30.8" + vue-tsc: "npm:^1.8.27" + peerDependencies: + typescript: "*" + vite: "*" + peerDependenciesMeta: + vite: + optional: true + checksum: 10c0/a38e02f2383df9db4b0b35bece0c2f96fc3f4bf695dd6cf717caadb4be457c1fdb4a87ed77b61a7beec494c730ea4c0656c9f968ecefbe42435f765c3b887996 + languageName: node + linkType: hard + +"vite-plugin-inspect@npm:^0.8.4": + version: 0.8.4 + resolution: "vite-plugin-inspect@npm:0.8.4" + dependencies: + "@antfu/utils": "npm:^0.7.7" + "@rollup/pluginutils": "npm:^5.1.0" + debug: "npm:^4.3.4" + error-stack-parser-es: "npm:^0.1.1" + fs-extra: "npm:^11.2.0" + open: "npm:^10.1.0" + perfect-debounce: "npm:^1.0.0" + picocolors: "npm:^1.0.0" + sirv: "npm:^2.0.4" + peerDependencies: + vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 + peerDependenciesMeta: + "@nuxt/kit": + optional: true + checksum: 10c0/cd9924bdf24989f61317a9400e622ab3db574a807aaae8412815558211a572b71c0988f5110bf03eecda718e286786d8c848756322e2e9b1eebc673515f34cec + languageName: node + linkType: hard + +"vite-plugin-vue-devtools@npm:^7.2.0": + version: 7.2.0 + resolution: "vite-plugin-vue-devtools@npm:7.2.0" + dependencies: + "@vue/devtools-core": "npm:^7.2.0" + "@vue/devtools-kit": "npm:^7.2.0" + "@vue/devtools-shared": "npm:^7.2.0" + execa: "npm:^8.0.1" + sirv: "npm:^2.0.4" + vite-plugin-inspect: "npm:^0.8.4" + vite-plugin-vue-inspector: "npm:^5.1.0" + peerDependencies: + vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 + checksum: 10c0/e1f4a3b1de31e9f4ce9f1d10b028e9196e3b71346df8e1a6a4ba9953646478677ed3a14a43ce57fb876195ec297268c690baf0c14e8e55833c833c94cb09e245 + languageName: node + linkType: hard + +"vite-plugin-vue-inspector@npm:^5.1.0": + version: 5.1.0 + resolution: "vite-plugin-vue-inspector@npm:5.1.0" + dependencies: + "@babel/core": "npm:^7.23.0" + "@babel/plugin-proposal-decorators": "npm:^7.23.0" + "@babel/plugin-syntax-import-attributes": "npm:^7.22.5" + "@babel/plugin-syntax-import-meta": "npm:^7.10.4" + "@babel/plugin-transform-typescript": "npm:^7.22.15" + "@vue/babel-plugin-jsx": "npm:^1.1.5" + "@vue/compiler-dom": "npm:^3.3.4" + kolorist: "npm:^1.8.0" + magic-string: "npm:^0.30.4" + peerDependencies: + vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 + checksum: 10c0/6a4aec3e93ee5e5858f3038fc5a898f5258972407c2f9b45ae0b5a4a4022fc58dd2b2eaae093aade5defb55e40b475a06c5a6c15ab4c5717c8bfe7411a0dd56c + languageName: node + linkType: hard + +"vite@npm:^5.0.0": + version: 5.2.10 + resolution: "vite@npm:5.2.10" + dependencies: + esbuild: "npm:^0.20.1" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.38" + rollup: "npm:^4.13.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/d50630ac8de807a6185cd9b5763b3969b2950a454cf6a4482f3780f183865e8d6f7e3aa57dd70ede1c493aaa861efb25b43562287efbcf8b471b7f3b88857a33 + languageName: node + linkType: hard + +"vite@npm:^5.2.11": + version: 5.2.11 + resolution: "vite@npm:5.2.11" + dependencies: + esbuild: "npm:^0.20.1" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.38" + rollup: "npm:^4.13.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/664b8d68e4f5152ae16bd2041af1bbaf11c43630ac461835bc31ff7d019913b33e465386e09f66dc1037d7aeefbb06939e0173787c063319bc2bd30c3b9ad8e4 + languageName: node + linkType: hard + +"vitest@npm:^1.6.0": + version: 1.6.0 + resolution: "vitest@npm:1.6.0" + dependencies: + "@vitest/expect": "npm:1.6.0" + "@vitest/runner": "npm:1.6.0" + "@vitest/snapshot": "npm:1.6.0" + "@vitest/spy": "npm:1.6.0" + "@vitest/utils": "npm:1.6.0" + acorn-walk: "npm:^8.3.2" + chai: "npm:^4.3.10" + debug: "npm:^4.3.4" + execa: "npm:^8.0.1" + local-pkg: "npm:^0.5.0" + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + std-env: "npm:^3.5.0" + strip-literal: "npm:^2.0.0" + tinybench: "npm:^2.5.1" + tinypool: "npm:^0.8.3" + vite: "npm:^5.0.0" + vite-node: "npm:1.6.0" + why-is-node-running: "npm:^2.2.2" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 1.6.0 + "@vitest/ui": 1.6.0 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/065da5b8ead51eb174d93dac0cd50042ca9539856dc25e340ea905d668c41961f7e00df3e388e6c76125b2c22091db2e8465f993d0f6944daf9598d549e562e7 + languageName: node + linkType: hard + +"vscode-oniguruma@npm:^1.7.0": + version: 1.7.0 + resolution: "vscode-oniguruma@npm:1.7.0" + checksum: 10c0/bef0073c665ddf8c86e51da94529c905856559e9aba97a9882f951acd572da560384775941ab6e7e8db94d9c578b25fefb951e4b73c37e8712e16b0231de2689 + languageName: node + linkType: hard + +"vscode-textmate@npm:^8.0.0": + version: 8.0.0 + resolution: "vscode-textmate@npm:8.0.0" + checksum: 10c0/836f7fe73fc94998a38ca193df48173a2b6eab08b4943d83c8cac9a2a0c3546cfdab4cf1b10b890ec4a4374c5bee03a885ef0e83e7fd2bd618cf00781c017c04 + languageName: node + linkType: hard + +"vue-component-type-helpers@npm:^2.0.0": + version: 2.0.14 + resolution: "vue-component-type-helpers@npm:2.0.14" + checksum: 10c0/1ce597eb8a7413246dc59c84e2f4a45b9c4ca7237268d9913f969b18760ca7c4dfff8643280881f914151d1cc861be4e984c8fd6e91d98fb0435923dd7a49ae1 + languageName: node + linkType: hard + +"vue-eslint-parser@npm:^9.3.1, vue-eslint-parser@npm:^9.4.2": + version: 9.4.2 + resolution: "vue-eslint-parser@npm:9.4.2" + dependencies: + debug: "npm:^4.3.4" + eslint-scope: "npm:^7.1.1" + eslint-visitor-keys: "npm:^3.3.0" + espree: "npm:^9.3.1" + esquery: "npm:^1.4.0" + lodash: "npm:^4.17.21" + semver: "npm:^7.3.6" + peerDependencies: + eslint: ">=6.0.0" + checksum: 10c0/79593073adbce8971565133c70a203f12f0be0f8c5e3a4063796fd56e5de64f1f3ad7f91be5a787a7a3fe751306ed22086ee8369d52725be95f452827ce670de + languageName: node + linkType: hard + +"vue-router@npm:^4.3.2": + version: 4.3.2 + resolution: "vue-router@npm:4.3.2" + dependencies: + "@vue/devtools-api": "npm:^6.5.1" + peerDependencies: + vue: ^3.2.0 + checksum: 10c0/63d5be4a983622e130c1ebe6e26dd17dee2346d77b35ef3f09ee0d8693fd38ad2387551c2db87a5cb258348dbc24dd815f48b4a55cc3ccb680c81aa4837ca88e + languageName: node + linkType: hard + +"vue-template-compiler@npm:^2.7.14": + version: 2.7.16 + resolution: "vue-template-compiler@npm:2.7.16" + dependencies: + de-indent: "npm:^1.0.2" + he: "npm:^1.2.0" + checksum: 10c0/66667ffd5095b707f169c902c4f1a011e9d5ab99fc228e4dac14eb5ca7f107ed99bff261b21578a4b391d2f3d320a8050e754404443472acad13ddaa4bd7bae2 + languageName: node + linkType: hard + +"vue-tsc@npm:^1.8.27": + version: 1.8.27 + resolution: "vue-tsc@npm:1.8.27" + dependencies: + "@volar/typescript": "npm:~1.11.1" + "@vue/language-core": "npm:1.8.27" + semver: "npm:^7.5.4" + peerDependencies: + typescript: "*" + bin: + vue-tsc: bin/vue-tsc.js + checksum: 10c0/6e6ba37eb7a0c8b9cc613225729c74edf8bd0632d265e62aca28b1969b5615b9dbe2de03aefb8aed2e26fdbd4b93f134785c8ab0095f92c2469192e2db5d09fd + languageName: node + linkType: hard + +"vue-tsc@npm:^2.0.19": + version: 2.0.19 + resolution: "vue-tsc@npm:2.0.19" + dependencies: + "@volar/typescript": "npm:~2.2.4" + "@vue/language-core": "npm:2.0.19" + semver: "npm:^7.5.4" + peerDependencies: + typescript: "*" + bin: + vue-tsc: bin/vue-tsc.js + checksum: 10c0/7f49ab40fbb171fe38f470cde29268c940fd74adb3c5b5200902a6220baa56b152a13622089a55f47cfc2989a34dd5e29ff2e0953f2de8c3697b6d99d6ad3694 + languageName: node + linkType: hard + +"vue@npm:^3.4.27": + version: 3.4.27 + resolution: "vue@npm:3.4.27" + dependencies: + "@vue/compiler-dom": "npm:3.4.27" + "@vue/compiler-sfc": "npm:3.4.27" + "@vue/runtime-dom": "npm:3.4.27" + "@vue/server-renderer": "npm:3.4.27" + "@vue/shared": "npm:3.4.27" + peerDependencies: + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/73349e05cf554891d5e0076be10083150c92831c1cffdeee1e25c2222a8a4d8291630825a897049add753c4925e1c916c3614fe8d9c0392d9ff0186e553fe24b + languageName: node + linkType: hard + +"w3c-xmlserializer@npm:^5.0.0": + version: 5.0.0 + resolution: "w3c-xmlserializer@npm:5.0.0" + dependencies: + xml-name-validator: "npm:^5.0.0" + checksum: 10c0/8712774c1aeb62dec22928bf1cdfd11426c2c9383a1a63f2bcae18db87ca574165a0fbe96b312b73652149167ac6c7f4cf5409f2eb101d9c805efe0e4bae798b + languageName: node + linkType: hard + +"webidl-conversions@npm:^7.0.0": + version: 7.0.0 + resolution: "webidl-conversions@npm:7.0.0" + checksum: 10c0/228d8cb6d270c23b0720cb2d95c579202db3aaf8f633b4e9dd94ec2000a04e7e6e43b76a94509cdb30479bd00ae253ab2371a2da9f81446cc313f89a4213a2c4 + languageName: node + linkType: hard + +"whatwg-encoding@npm:^3.1.1": + version: 3.1.1 + resolution: "whatwg-encoding@npm:3.1.1" + dependencies: + iconv-lite: "npm:0.6.3" + checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e + languageName: node + linkType: hard + +"whatwg-mimetype@npm:^4.0.0": + version: 4.0.0 + resolution: "whatwg-mimetype@npm:4.0.0" + checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df + languageName: node + linkType: hard + +"whatwg-url@npm:^14.0.0": + version: 14.0.0 + resolution: "whatwg-url@npm:14.0.0" + dependencies: + tr46: "npm:^5.0.0" + webidl-conversions: "npm:^7.0.0" + checksum: 10c0/ac32e9ba9d08744605519bbe9e1371174d36229689ecc099157b6ba102d4251a95e81d81f3d80271eb8da182eccfa65653f07f0ab43ea66a6934e643fd091ba9 + languageName: node + linkType: hard + +"which-boxed-primitive@npm:^1.0.2": + version: 1.0.2 + resolution: "which-boxed-primitive@npm:1.0.2" + dependencies: + is-bigint: "npm:^1.0.1" + is-boolean-object: "npm:^1.1.0" + is-number-object: "npm:^1.0.4" + is-string: "npm:^1.0.5" + is-symbol: "npm:^1.0.3" + checksum: 10c0/0a62a03c00c91dd4fb1035b2f0733c341d805753b027eebd3a304b9cb70e8ce33e25317add2fe9b5fea6f53a175c0633ae701ff812e604410ddd049777cd435e + languageName: node + linkType: hard + +"which-collection@npm:^1.0.1": + version: 1.0.2 + resolution: "which-collection@npm:1.0.2" + dependencies: + is-map: "npm:^2.0.3" + is-set: "npm:^2.0.3" + is-weakmap: "npm:^2.0.2" + is-weakset: "npm:^2.0.3" + checksum: 10c0/3345fde20964525a04cdf7c4a96821f85f0cc198f1b2ecb4576e08096746d129eb133571998fe121c77782ac8f21cbd67745a3d35ce100d26d4e684c142ea1f2 + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.13": + version: 1.1.15 + resolution: "which-typed-array@npm:1.1.15" + dependencies: + available-typed-arrays: "npm:^1.0.7" + call-bind: "npm:^1.0.7" + for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" + has-tostringtag: "npm:^1.0.2" + checksum: 10c0/4465d5348c044032032251be54d8988270e69c6b7154f8fcb2a47ff706fe36f7624b3a24246b8d9089435a8f4ec48c1c1025c5d6b499456b9e5eff4f48212983 + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a + languageName: node + linkType: hard + +"why-is-node-running@npm:^2.2.2": + version: 2.2.2 + resolution: "why-is-node-running@npm:2.2.2" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/805d57eb5d33f0fb4e36bae5dceda7fd8c6932c2aeb705e30003970488f1a2bc70029ee64be1a0e1531e2268b11e65606e88e5b71d667ea745e6dc48fc9014bd + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"ws@npm:^8.16.0": + version: 8.16.0 + resolution: "ws@npm:8.16.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/a7783bb421c648b1e622b423409cb2a58ac5839521d2f689e84bc9dc41d59379c692dd405b15a997ea1d4c0c2e5314ad707332d0c558f15232d2bc07c0b4618a + languageName: node + linkType: hard + +"xml-name-validator@npm:^4.0.0": + version: 4.0.0 + resolution: "xml-name-validator@npm:4.0.0" + checksum: 10c0/c1bfa219d64e56fee265b2bd31b2fcecefc063ee802da1e73bad1f21d7afd89b943c9e2c97af2942f60b1ad46f915a4c81e00039c7d398b53cf410e29d3c30bd + languageName: node + linkType: hard + +"xml-name-validator@npm:^5.0.0": + version: 5.0.0 + resolution: "xml-name-validator@npm:5.0.0" + checksum: 10c0/3fcf44e7b73fb18be917fdd4ccffff3639373c7cb83f8fc35df6001fecba7942f1dbead29d91ebb8315e2f2ff786b508f0c9dc0215b6353f9983c6b7d62cb1f5 + languageName: node + linkType: hard + +"xmlchars@npm:^2.2.0": + version: 2.2.0 + resolution: "xmlchars@npm:2.2.0" + checksum: 10c0/b64b535861a6f310c5d9bfa10834cf49127c71922c297da9d4d1b45eeaae40bf9b4363275876088fbe2667e5db028d2cd4f8ee72eed9bede840a67d57dab7593 + languageName: node + linkType: hard + +"xtend@npm:~4.0.1": + version: 4.0.2 + resolution: "xtend@npm:4.0.2" + checksum: 10c0/366ae4783eec6100f8a02dff02ac907bf29f9a00b82ac0264b4d8b832ead18306797e283cf19de776538babfdcb2101375ec5646b59f08c52128ac4ab812ed0e + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a + languageName: node + linkType: hard + +"yaml@npm:^2.3.4": + version: 2.4.2 + resolution: "yaml@npm:2.4.2" + bin: + yaml: bin.mjs + checksum: 10c0/280ddb2e43ffa7d91a95738e80c8f33e860749cdc25aa6d9e4d350a28e174fd7e494e4aa023108aaee41388e451e3dc1292261d8f022aabcf90df9c63d647549 + languageName: node + linkType: hard + +"yn@npm:3.1.1": + version: 3.1.1 + resolution: "yn@npm:3.1.1" + checksum: 10c0/0732468dd7622ed8a274f640f191f3eaf1f39d5349a1b72836df484998d7d9807fbea094e2f5486d6b0cd2414aad5775972df0e68f8604db89a239f0f4bf7443 + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f + languageName: node + linkType: hard + +"yocto-queue@npm:^1.0.0": + version: 1.0.0 + resolution: "yocto-queue@npm:1.0.0" + checksum: 10c0/856117aa15cf5103d2a2fb173f0ab4acb12b4b4d0ed3ab249fdbbf612e55d1cadfd27a6110940e24746fb0a78cf640b522cc8bca76f30a3b00b66e90cf82abe0 + languageName: node + linkType: hard + +"z-schema@npm:~5.0.2": + version: 5.0.5 + resolution: "z-schema@npm:5.0.5" + dependencies: + commander: "npm:^9.4.1" + lodash.get: "npm:^4.4.2" + lodash.isequal: "npm:^4.5.0" + validator: "npm:^13.7.0" + dependenciesMeta: + commander: + optional: true + bin: + z-schema: bin/z-schema + checksum: 10c0/e4c812cfe6468c19b2a21d07d4ff8fb70359062d33400b45f89017eaa3efe9d51e85963f2b115eaaa99a16b451782249bf9b1fa8b31d35cc473e7becb3e44264 + languageName: node + linkType: hard + +"zod-validation-error@npm:^3.3.0": + version: 3.3.0 + resolution: "zod-validation-error@npm:3.3.0" + peerDependencies: + zod: ^3.18.0 + checksum: 10c0/a233dca6dc9a2237aa7b677cc8ce022c2b6f90894fd4d1e2c7b239d2aad38f36f3b84bf7f7cfaff5bf97fce31e1010d2736ca1ec539e0e8cb8bb9d05977911a2 + languageName: node + linkType: hard + +"zod@npm:^3.23.8": + version: 3.23.8 + resolution: "zod@npm:3.23.8" + checksum: 10c0/8f14c87d6b1b53c944c25ce7a28616896319d95bc46a9660fe441adc0ed0a81253b02b5abdaeffedbeb23bdd25a0bf1c29d2c12dd919aef6447652dd295e3e69 + languageName: node + linkType: hard