diff --git a/.editorconfig b/.editorconfig index 459f3d62..60bfc63b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,10 +1,11 @@ +# https://editorconfig.org/ +# https://prettier.io/docs/en/configuration#editorconfig + root = true [*] charset = utf-8 end_of_line = lf -indent_size = 4 -indent_style = space +indent_style = tab insert_final_newline = true max_line_length = 80 -trim_trailing_whitespace = true diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 5f801e54..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "env": { - "browser": true, - "node": true, - "es2021": true, - "es2022": true, - "webextensions": true - }, - "extends": [ - "eslint:recommended", - "airbnb-base", - "plugin:svelte/recommended" - ], - "globals": { - "_browser": "writable", - "browser": "readonly" - }, - "ignorePatterns": [ - "/etc", - "build", - "dist", - "node_modules", - "public" - ], - "overrides": [ - { - "files": ["*.svelte"], - "parser": "svelte-eslint-parser" - } - ], - "settings": { // https://github.com/sveltejs/svelte-eslint-parser/issues/377#issuecomment-1630941193 - "import/parsers": { - "espree": [".js"] - } - }, - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "rules": { - "array-callback-return": "off", - "arrow-body-style": "off", - "arrow-parens": ["error", "as-needed"], - "comma-dangle": ["error", "never"], - "consistent-return": "off", - "default-case": "off", - "eqeqeq": ["error", "smart"], - "global-require": "off", - "guard-for-in": "warn", - "import/extensions": "off", - "import/first": "off", - "import/no-mutable-exports": "off", - "import/no-extraneous-dependencies": ["error", { - "devDependencies": true - }], - "import/prefer-default-export": "off", - "import/no-unresolved": "off", - "indent": ["error", 4, {"SwitchCase": 1}], - "max-len": "off", - "no-alert":"off", - "no-await-in-loop":"warn", - "no-confusing-arrow": ["warn", { - "allowParens": true, - "onlyOneSimpleParam": true - }], - "no-console": ["warn", {"allow": ["info", "warn", "error"]}], - "no-continue": "off", - "no-else-return": ["error", {"allowElseIf": true}], - "no-nested-ternary": "off", - "no-param-reassign": ["warn", { "props": false }], - "no-plusplus": "off", - "no-restricted-syntax": "off", - "no-return-assign": "off", - "no-underscore-dangle":"off", - "no-unused-expressions": "off", - "no-use-before-define": ["error", { - "functions": false, - "classes": true, - "variables": true, - "allowNamedExports": false - }], - "object-curly-newline": ["error", {"consistent": true}], - "object-curly-spacing": ["error", "never"], - "one-var": "off", - "one-var-declaration-per-line": "off", - "prefer-destructuring": "off", - "prefer-object-spread": "warn", - "quotes": ["error", "double"], - "import/no-namespace": "warn", - "svelte/no-at-html-tags": "warn" - } -} diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..cc156834 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,9 @@ +# https://git-scm.com/docs/git-blame +# https://github.blog/changelog/2022-03-24-ignore-commits-in-the-blame-view-beta/ +# https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view + +# Prettier 3.0.3 +acc35fbec38d72968b735826c6807793a2054aed + +# Xcode swift indent replace spaces to tabs +962c4d81879e85362a5f878889d3aa869f4e6961 diff --git a/.gitattributes b/.gitattributes index bad24153..b4a7b25c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,7 @@ -package-lock.json linguist-generated -pnpm-lock.yaml linguist-generated +# https://git-scm.com/docs/gitattributes +# https://git-scm.com/book/en/Customizing-Git-Git-Attributes +* text=auto + +# https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github +# https://github.com/github-linguist/linguist/blob/master/docs/overrides.md public linguist-generated \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..cab762b7 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,7 @@ +### Please note: the project is currently temporarily suspended from receiving direct code contributions. + +Please do not submit any new pull requests, they will be close immediately. + +If you have any ideas and suggestions, please submit them to us through [`Issues`](https://github.com/quoid/userscripts/issues) or [`Discussions`](https://github.com/quoid/userscripts/discussions). + +We apologize for the inconvenience and thank you for your understanding. diff --git a/.github/workflows/deployment.yaml b/.github/workflows/deployment.yaml new file mode 100644 index 00000000..f9b2e529 --- /dev/null +++ b/.github/workflows/deployment.yaml @@ -0,0 +1,70 @@ +# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions + +name: Userscripts deployment +on: + release: + types: [published] # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release +jobs: + Deployment: + strategy: + matrix: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs + platform: [mac, ios] + runs-on: macos-26 # https://github.com/actions/runner-images + steps: + - uses: actions/checkout@v4 # https://github.com/actions/checkout + with: + fetch-depth: 0 + fetch-tags: true # https://github.com/actions/checkout/issues/1781 + - uses: actions/setup-node@v4 # https://github.com/actions/setup-node + with: + node-version: latest + - id: semver # Parse semver from github ref + uses: userscriptsup/actions/semver-parser@main + - name: Verify release type + run: | + if ${{ github.event.release.prerelease }}; then + ${{ steps.semver.outputs.beta }} || exit 11 + else + ${{ steps.semver.outputs.alpha }} && exit 12 + ${{ steps.semver.outputs.beta }} && exit 13 + [ -n "${{ steps.semver.outputs.prerelease }}" ] && exit 14 + exit 0 + fi + - run: npm ci + - run: npm run lint:js + - run: npm run lint:css + - run: BETA=1 npm run build:${{ matrix.platform }}-safari-15 + if: ${{ github.event.release.prerelease }} + - run: npm run build:${{ matrix.platform }}-safari-15 + if: ${{ ! github.event.release.prerelease }} + # - name: Set xcode version + # run: sudo xcode-select -s "/Applications/Xcode_26.1.1.app" # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md#xcode + # - run: xcodebuild -downloadPlatform iOS # Temporary fix for Xcode_26_beta + # if: matrix.platform == 'ios' + - name: Run fastlane + id: fastlane + working-directory: ./fastlane + env: + FASTLANE_OPT_OUT_USAGE: "YES" + MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }} + MATCH_GIT_PRIVATE_KEY: "fastlane/${{ secrets.GIT_PRIVATE_KEY_FILE }}" + GIT_PRIVATE_KEY_FILE: ${{ secrets.GIT_PRIVATE_KEY_FILE }} + GIT_PRIVATE_KEY_BASE64: ${{ secrets.GIT_PRIVATE_KEY_BASE64 }} # https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#storing-base64-binary-blobs-as-secrets + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + GYM_INSTALLER_CERT_NAME: ${{ secrets.GYM_INSTALLER_CERT_NAME }} # required for mac (unable to auto-detect) + APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY_ID }} + APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }} + APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }} + MARKETING_VERSION: ${{ steps.semver.outputs.core }} + IS_PRERELEASE: ${{ github.event.release.prerelease }} + run: | + echo $GIT_PRIVATE_KEY_BASE64 | base64 --decode > $GIT_PRIVATE_KEY_FILE + chmod 600 $GIT_PRIVATE_KEY_FILE + bundle install + $IS_PRERELEASE && bundle exec fastlane ${{ matrix.platform }} beta + $IS_PRERELEASE || bundle exec fastlane ${{ matrix.platform }} release + - name: Artifact dSYM + uses: actions/upload-artifact@v4 # https://github.com/actions/upload-artifact + with: + name: Userscripts_${{ matrix.platform }}_${{ steps.fastlane.outputs.ver || github.ref_name }}.app.dSYM + path: build/*.dSYM.zip diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 89f2044d..45e462ed 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,10 +5,10 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install modules - run: npm install - - name: Run ESLint - run: npm run lint:js - - name: Run Stylelint - run: npm run lint:css + - uses: actions/checkout@v2 + - name: Install modules + run: npm install + - name: Run ESLint + run: npm run lint:js + - name: Run Stylelint + run: npm run lint:css diff --git a/.gitignore b/.gitignore index 0a6c392f..6e9c9304 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ dist dist-ssr build built -*.local # Logs logs @@ -47,5 +46,13 @@ xcuserdata/ *.xcuserstate *.dev.xcconfig XCBuildData -xcode/**/build/ -xcode/**/dist/ \ No newline at end of file +xcode/Ext-Safari/Resources/ + +# directories and files +local +*.local +/scripts/*.local.js + +# fastlane +/fastlane/report.xml +/fastlane/**/*.local.rb \ No newline at end of file diff --git a/.postcssrc.json b/.postcssrc.json index cc24b151..5c893e80 100644 --- a/.postcssrc.json +++ b/.postcssrc.json @@ -1,5 +1,5 @@ { - "plugins": { - "autoprefixer": {"overrideBrowserslist": ["safari >= 13"]} - } -} \ No newline at end of file + "plugins": { + "autoprefixer": { "overrideBrowserslist": ["safari >= 15"] } + } +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..5c1a0da1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,26 @@ +# env +.env +.env.* +!.env.example +.DS_Store + +# root +/package +/public/**/vendor +/etc + +# dir +node_modules +xcode +build +built +dist +temp + +# files +*.svg + +# Ignore files for PNPM, NPM and YARN +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..a651d67a --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "plugins": ["prettier-plugin-svelte"], + "useTabs": true +} diff --git a/.stylelintrc.json b/.stylelintrc.json index ea60b077..707413f5 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,29 +1,25 @@ { - "extends": [ - "stylelint-config-recommended", - "stylelint-config-standard", - "stylelint-config-html/svelte" - ], - "ignoreFiles": [ - "**/build/**", - "**/dist/**", - "**/etc/**", - "**/node_modules/**", - "**/reset.css" - ], - "rules": { - "alpha-value-notation": "number", - "comment-empty-line-before": ["always",{ - "ignore": ["after-comment", "stylelint-commands"] - }], - "custom-property-empty-line-before": null, - "indentation": 4, - "max-empty-lines": 1, - "no-descending-specificity": null, - "property-no-vendor-prefix": null, - "selector-class-pattern": null, - "selector-pseudo-class-no-unknown": [true, { - "ignorePseudoClasses": ["global"] - }] - } + "extends": ["stylelint-config-standard", "stylelint-config-html"], + "ignoreFiles": [ + "**/etc/**", + "**/dist/**", + "**/build/**", + "**/node_modules/**", + "**/reset.css", + "public/**", + "xcode/**" + ], + "rules": { + "alpha-value-notation": "number", + "custom-property-empty-line-before": null, + "no-descending-specificity": null, + "property-no-vendor-prefix": null, + "selector-class-pattern": null, + "selector-pseudo-class-no-unknown": [ + true, + { + "ignorePseudoClasses": ["global"] + } + ] + } } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 728e8859..02ec4445 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,9 @@ +// https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions { - // See https://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "svelte.svelte-vscode", - "dbaeumer.vscode-eslint", - "stylelint.vscode-stylelint" - ] -} \ No newline at end of file + "recommendations": [ + "svelte.svelte-vscode", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "stylelint.vscode-stylelint" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index ffec34dc..892bfd4d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,24 +1,21 @@ +// https://code.visualstudio.com/docs/getstarted/settings#_workspace-settings { - "editor.formatOnSave": false, - "files.associations": { - ".htmlhintrc": "json" - }, - "eslint.validate": [ - "svelte" - ], - "stylelint.validate": [ - "css", - "postcss", - "svelte" - ], - "svelte.plugin.svelte.compilerWarnings": { - "a11y-missing-attribute": "error" - }, - "svelte.plugin.svelte.rename.enable": false, - "[svelte]": { - "editor.defaultFormatter": "svelte.svelte-vscode" - }, - "[css]": { - "editor.defaultFormatter": "stylelint.vscode-stylelint" - } + "files.associations": { + ".git-blame-ignore-revs": "ignore" + }, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[yaml]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[json]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[html]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[svelte]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[markdown]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + // https://github.com/microsoft/vscode-eslint#settings-options + "eslint.validate": ["javascript", "svelte"], + // https://github.com/stylelint/vscode-stylelint#stylelintvalidate + "stylelint.validate": ["css", "postcss", "svelte"] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 080f3fa1..c967bcab 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,63 +1,142 @@ +// https://code.visualstudio.com/docs/editor/tasks { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "npm: install", - "detail": "Install dev dependencies", - "type": "shell", - "command": "npm install", - "group": "build", - "options": { - "cwd": "./" - } - }, - { - "label": "npm: dev", - "detail": "Run dev server", - "type": "shell", - "command": "npm run dev", - "group": "build", - "options": { - "cwd": "./" - } - }, - { - "label": "npm: build", - "detail": "Build to xcode dist path", - "type": "shell", - "command": "npm run build", - "group": "build", - "options": { - "cwd": "./" - } - }, - { - "label": "xcode: build", - "detail": "xcodebuild", - "type": "shell", - "command": "xcodebuild -scheme Userscripts build", - "group": "build", - "options": { - "cwd": "./xcode/" - } - }, - { - "label": "build all", - "detail": "npm build + xcodebuild", - "presentation": { - "reveal": "silent" - }, - "dependsOrder": "sequence", - "dependsOn": [ - "npm: build", - "xcode: build" - ], - "group": { - "kind": "build", - "isDefault": true - } - } - ] -} \ No newline at end of file + "version": "2.0.0", + "tasks": [ + { + "label": "npm: install", + "detail": "Install dev dependencies", + "type": "shell", + "command": "npm install", + "group": "build", + "options": { + "cwd": "./" + } + }, + { + "label": "npm: dev", + "detail": "Run dev server", + "type": "shell", + "command": "npm run dev", + "group": "build", + "options": { + "cwd": "./" + } + }, + { + "label": "npm: dev ext-safari-mac", + "detail": "Run dev server and build dev resource to xcode dist", + "type": "shell", + "command": "npm run dev:ext-safari-mac", + "group": "build", + "options": { + "cwd": "./" + } + }, + { + "label": "npm: dev ext-safari-ios", + "detail": "Run dev server and build dev resource to xcode dist", + "type": "shell", + "command": "npm run dev:ext-safari-ios", + "group": "build", + "options": { + "cwd": "./" + } + }, + { + "label": "npm: build mac", + "detail": "Build to xcode dist path", + "type": "shell", + "command": "npm run build:mac", + "group": "build", + "options": { + "cwd": "./" + } + }, + { + "label": "npm: build ios", + "detail": "Build to xcode dist path", + "type": "shell", + "command": "npm run build:ios", + "group": "build", + "options": { + "cwd": "./" + } + }, + { + "label": "xcodebuild: mac-debug", + "detail": "xcodebuild mac debug", + "type": "shell", + "command": "xcodebuild -scheme Mac -configuration Debug", + "group": "build", + "options": { + "cwd": "./xcode/" + } + }, + { + "label": "xcodebuild: mac-vite", + "detail": "xcodebuild mac vite", + "type": "shell", + "command": "xcodebuild -scheme Mac-Vite-Dev -configuration Vite", + "group": "build", + "options": { + "cwd": "./xcode/" + } + }, + { + "label": "dev: ext-safari-mac", + "detail": "npm: dev ext-safari-mac + xcodebuild: mac-vite", + "presentation": { + "reveal": "silent" + }, + "dependsOrder": "sequence", + "dependsOn": ["npm: dev ext-safari-mac", "xcodebuild: mac-vite"], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "build: mac debug", + "detail": "npm: build mac + xcodebuild: mac-debug", + "presentation": { + "reveal": "silent" + }, + "dependsOrder": "sequence", + "dependsOn": ["npm: build mac", "xcodebuild: mac-debug"], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "lint: js", + "detail": "lint js with eslint", + "type": "shell", + "command": "npm run lint:js", + "group": "test", + "options": { + "cwd": "./" + } + }, + { + "label": "lint: css", + "detail": "lint css with stylelint", + "type": "shell", + "command": "npm run lint:css", + "group": "test", + "options": { + "cwd": "./" + } + }, + { + "label": "prettier", + "detail": "applying prettier formatting for all supported files", + "type": "shell", + "command": "npm run prettier", + "options": { + "cwd": "./" + }, + "problemMatcher": [] + } + ] +} diff --git a/README.md b/README.md index ab6525cc..09cdc78a 100644 --- a/README.md +++ b/README.md @@ -5,25 +5,26 @@ An open-source userscript editor for Safari ![Userscripts Safari](/etc/screenshot.png) ## Table of Contents -* [Installation](#installation) -* [Usage](#usage) -* [UI Overview](#ui-overview) - * [Browser Page](#browser-page) - * [Settings Modal](#settings-modal) - * [Popover](#popover) -* [Metadata](#metadata) -* [API](#api) -* [Scripts Directory / Save Location](#scripts-directory) -* [Getting Help](#getting-help) -* [FAQs](#faqs) -* [Contributing](#contributing) -* [Support](#support) -* [Privacy Policy](#privacy-policy) -* [License](#license) + +- [Installation](#installation) +- [Usage](#usage) +- [UI Overview](#ui-overview) + - [Browser Page](#browser-page) + - [Settings Modal](#settings-modal) + - [Popover](#popover) +- [Metadata](#metadata) +- [API](#api) +- [Scripts Directory / Save Location](#scripts-directory) +- [Getting Help](#getting-help) +- [FAQs](#faqs) +- [Contributing](#contributing) +- [Support](#support) +- [Privacy Policy](#privacy-policy) +- [License](#license) ## Installation -Userscripts is available for iOS (+ipadOS) and macOS. For all versions, installation is done through [Apple's App Store](https://itunes.apple.com/us/app/userscripts/id1463298887). On macOS, versions prior to `4.x` were made available to download and install directly from the repository, but due to [changes in the way Apple allows developers to distribute apps built with the WebExtension API](https://github.com/quoid/userscripts/issues/154), that is no longer an option. +Userscripts is available for iOS (iPadOS) and macOS. For all versions, installation is done through [Apple's App Store](https://itunes.apple.com/us/app/userscripts/id1463298887). On macOS, versions prior to `4.x` were made available to download and install directly from the repository, but due to [changes in the way Apple allows developers to distribute apps built with the WebExtension API](https://github.com/quoid/userscripts/issues/154), that is no longer an option. To run Userscripts on iOS you should be on iOS 15.1 or higher. @@ -39,22 +40,42 @@ It's recommend to read this documentation and, if you have time, watch the follo Once the app is downloaded and installed the following steps should be taken: -**iOS** +### **iOS (iPadOS)** + +After installing the iOS App, you need two main steps to make the extension work: + +- **Open the App and set a directory** (For saving and loading userscripts) + - After Userscripts for ios v1.5.0, a local default directory will be set automatically + - In earlier versions please click the `Set Userscripts Directory` button and select the directory +- **Enable the extension in Safari** (And grant permissions) + + - Manage extensions from Settings App (Settings > Safari > Extensions) or Safari App (`AA` button on iPhone, `extension-icon` on iPad, also where you open the extension's `popup` interface) + - For optimal experience it's recommended that you `Always Allow` Userscripts for `All Websites` + +> [!NOTE] +> +> The iOS App cannot detect whether you have enabled the extension in Safari, therefore, the App prompt will not change after you enable the extension. Currently the App interface is only used to set or change the userscripts directory. +> +> You could select an iCloud folder for syncing scripts between macOS and iOS, but please note that there may be delays in synchronization, and you may encounter files be evictioned due to iCloud optimization, please refer to [#424](https://github.com/quoid/userscripts/issues/424). (**Since macOS 15 / iOS 18, please set "keep downloaded" for the folder to avoid eviction**) + +There are two main ways to install a user script from the iOS version: + +- Visit a `.user.js` URL in Safari, then open the extension `popup` and you will see an installation prompt + - The URL should end with `.user.js` in `/PATH` part, not the `?QUERY` or `#HASH` parts +- You could also save files with the extension `.user.js` directly to the Userscripts directory you set above -- Go to `Settings > Safari > Extensions > Userscripts` -- Turn Userscripts `on` -- For optimal experience it's recommended that you allow Userscripts for `All Websites` -- Once the above is complete **open the containing app** -- Click the "Set Userscripts Directory" button and select the directory, *within the Files.app*, where your userscripts are located and where you wish newly installed userscripts to be placed - - **Tip:** for optimal cross platform experience it's a good idea to use an iCloud folder for syncing between macOS and iOS - - **Note:** syncing between macOS and iOS is not immediate, it is sometimes necessary to open Files.app in order to applying changes made in macOS to be reflected in iOS - that includes userscript deletions, additions and edits -- Once the directory is set you can close the containing app and open Safari.app -- It **may be necessary** to apply further permissions and it's to `Always Allow` Userscripts for `All Websites` +> [!TIP] +> +> Both of the above work equally well in the macOS version. +> +> The iOS version does not include the script editor provided in the macOS version, but you can always edit script files in the directory you set directly on iOS. (use any third-party code editor apps, support in-place opening and editing) -**macOS** +### **macOS** After installing Userscripts on macOS, you **do not** need to select a userscripts directory if you do not plan on syncing your userscripts between multiple devices. Instead you can choose to use the default directory, which is located at `~/User/Library/Containers/Userscripts/Data/Documents/scripts` - again, this is default (and automatic) behavior. You only need to select a new location if you want to store your userscripts elsewhere, which is especially useful if you are using an external code editor such as Sublime Text or VSCode. +Refer to Apple's official guide page: [Use Safari extensions on your Mac](https://support.apple.com/102343) + [**Here's a short clip showing how to easily create/add a userscript in Safari using this extension on macOS**](https://youtu.be/x1r3-L7pdYQ?t=14) ## UI Overview @@ -64,34 +85,34 @@ After installing Userscripts on macOS, you **do not** need to select a userscrip ![Userscripts Safari Main Application Window](/etc/ui01.png) 1. **Extension button** - click this button to open the extension interface -1. **Filter bar** - use this input to filter items in the sidebar, by *name* +1. **Filter bar** - use this input to filter items in the sidebar, by _name_ 1. **Sort button** - changes the order of the items in the sidebar by name or modified time -1. **Sidebar buttons** - *described left to right* - - The `settings` button (represented by a [cog](https://wikipedia.org/wiki/Gear)) displays the settings modal (discussed below) - - The `plus` button allows users to add new items - - `New CSS` is a "userscript" that expects [CSS](https://www.w3schools.com/css/) code - - `New Javascript` is a prototypical userscript that expects [Javascript](https://www.w3schools.com/js/DEFAULT.asp) code - - `New Remote` allows the user to add a remote hosted userscript (or style) by inputting the web address (*ex:* `https://www.k21p.com/example.user.js`) +1. **Sidebar buttons** - _described left to right_ + - The `settings` button (represented by a [cog](https://wikipedia.org/wiki/Gear)) displays the settings modal (discussed below) + - The `plus` button allows users to add new items + - `New CSS` is a "userscript" that expects [CSS](https://www.w3schools.com/css/) code + - `New Javascript` is a prototypical userscript that expects [Javascript](https://www.w3schools.com/js/DEFAULT.asp) code + - `New Remote` allows the user to add a remote hosted userscript (or style) by inputting the web address (_ex:_ `https://www.k21p.com/example.user.js`) 1. **Item toggle** - this toggle enables or disables an item -1. **Item** - this is the userscript (or style), clicking on it will load it's contents into the editor - *you can hide descriptions in the settings area!* -1. **Editor buttons (top)** - *described left to right* - - **Update button** - this button allows you to update userscripts that meet the following conditions - - metadata contains `@version` tag - - metadata contains `@updateURL` tag - - **Download button** - click this button to download a copy of your userscript - - *Note:* every userscript that is displayed in the interface is already present on your local machine, at your save location - the download button offers a quick way to retrieve a copy of that file, without needing to click the settings button, and then the save location link within the settings modal - - **Trash button** - moves the currently loaded userscript to the trash bin - it will subsequently be removed from the interface and save location +1. **Item** - this is the userscript (or style), clicking on it will load it's contents into the editor - _you can hide descriptions in the settings area!_ +1. **Editor buttons (top)** - _described left to right_ + - **Update button** - this button allows you to update userscripts that meet the following conditions + - metadata contains `@version` tag + - metadata contains `@updateURL` tag + - **Download button** - click this button to download a copy of your userscript + - _Note:_ every userscript that is displayed in the interface is already present on your local machine, at your save location - the download button offers a quick way to retrieve a copy of that file, without needing to click the settings button, and then the save location link within the settings modal + - **Trash button** - moves the currently loaded userscript to the trash bin - it will subsequently be removed from the interface and save location 1. **Editor buttons (bottom)** - - `Discard` - while editing, reverts any unsaved changes you've made to a userscript - - `Save` - while editing, saves all changes you've made to a userscript - - `Command + S` is the keyboard shortcut for the action + - `Discard` - while editing, reverts any unsaved changes you've made to a userscript + - `Save` - while editing, saves all changes you've made to a userscript + - `Command + S` is the keyboard shortcut for the action ### Settings Modal: ![Userscripts Safari Settings Window](/etc/settings.png) - **Auto Close Brackets** - toggles on/off auto closing of brackets in the editor - - this affects the following characters: `() [] {} "" ''` + - this affects the following characters: `() [] {} "" ''` - **Auto Hint** - automatically shows completion hints while editing - **Hide Descriptions** - hides the item descriptions in the sidebar - **Show Invisibles** - toggles the display of invisible characters in the editor @@ -101,7 +122,7 @@ After installing Userscripts on macOS, you **do not** need to select a userscrip - **Show Toolbar Count** - displays a badge on the toolbar icon with a number that represents how many enabled scripts match the url for the page you are on - **Save Location** - where your file are currently located and being saved to (click the blue text to open location) - **Change Save Location (cogs icon)** - this button, located directly to the right of the save location, is a shortcut for opening the host app, which will allow you to change the save location -- **Global Blacklist** - this input accepts a comma separated list of [`@match` patterns](https://developer.chrome.com/docs/extensions/mv3/match_patterns/), a page url that matches against a pattern in this list will be ignored for script injection +- **Global Blacklist** - this input accepts a comma separated list of `@match` patterns ([Match pattern structure](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns#match_pattern_structure)), a page url that matches against a pattern in this list will be ignored for script injection ### Popup: @@ -109,7 +130,7 @@ After installing Userscripts on macOS, you **do not** need to select a userscrip -10. **Open Page Link** - *macOS only*, opens the extension browser page +10. **Open Page Link** - _macOS only_, opens the extension browser page 11. **Enable Injection toggle** - turns on/off page script injection (on/off switch) 12. **Refresh View** - refreshes the popup view 13. **Available Updates View** - the extension periodically checks all userscripts in your save location for updates and when an update is found, it is shown in this view @@ -121,149 +142,189 @@ After installing Userscripts on macOS, you **do not** need to select a userscrip Userscripts Safari currently supports the following userscript metadata: -- `@name` - This will be the name that displays in the sidebar and be used as the filename - you can *not* use the same name for multiple files of the same type +- `@name` - This will be the name that displays in the sidebar and be used as the filename - you can _not_ use the same name for multiple files of the same type - `@description`- Use this to describe what your userscript does - this will be displayed in the sidebar - there is a setting to hide descriptions - `@icon` - This doesn't have a function with this userscript manager, but the **first value** provided in the metadata will be accessible in the `GM_/GM.info` object -- `@match` - Domain match patterns - you can use several instances of this field if you'd like multiple domain matches - view [this article for more information on constructing patterns](https://developer.chrome.com/extensions/match_patterns) - - **Note:** this extension only supports `http/s` -- `@exclude-match` - Domain patterns where you do *not* want the script to run +- `@match` - Domain match patterns - you can use several instances of this field if you'd like multiple domain matches - please refer to: [Match pattern structure](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns#match_pattern_structure) + - **Note:** this extension only supports `http/s` +- `@exclude-match` - Domain patterns where you do _not_ want the script to run - `@include` - Used to match against urls for injection, globs and regular expressions are allowed, [read more here](https://wiki.greasespot.net/Include_and_exclude_rules) - `@exclude` - Functions in a similar way as `@include` but rather than injecting, a match against this key's value will prevent injection - `@inject-into` - allows the user to choose which context to inject the script into - - allows the user to choose which context to inject the script into - - values: `auto` (default), `content`, `page` - - `GM` apis are only available when using `content` - - works like [violentmonkey](https://violentmonkey.github.io/api/metadata-block/#inject-into) + - allows the user to choose which context to inject the script into + - values: `auto` (default), `content`, `page` + - `GM` apis are only available when using `content` + - works like [violentmonkey](https://violentmonkey.github.io/api/metadata-block/#inject-into) - `@run-at` - - allows the user to choose the injection timing - - document-start, document-end (default), document-idle - - works like [violentmonkey](https://violentmonkey.github.io/api/metadata-block/#run-at) - - **JS Only** + - allows the user to choose the injection timing + - document-start, document-end (default), document-idle + - works like [violentmonkey](https://violentmonkey.github.io/api/metadata-block/#run-at) + - **JS Only** - `@weight` - - allows the user to further adjust script injection timing - - can be used to ensure one script injects before another - - ONLY accepts integers (floats, strings and everything else will be ignored) - - min value = 1, max value = 999, higher numbers (“heavier”) execute earlier + - allows the user to further adjust script injection timing + - can be used to ensure one script injects before another + - ONLY accepts integers (floats, strings and everything else will be ignored) + - min value = 1, max value = 999, higher numbers (“heavier”) execute earlier - `@require` - - allows users to include remote resources in their scripts - - the value must be a valid url, currently no local file support - - must require a resource of the same file type (JS for JS, CSS for CSS) - - when a resource is required, it is downloaded and saved locally - - the resources is downloaded once at save and never checked for updates or parsed in anyway - - if you want to update the require resources, and the url does not change, you must remove the resources, save, then re-input it - - **require remote resources at your own risk, the extension never validates remote resource code in any way and be aware that using remote resources from untrusted sources can jeopardize your personal security** + - allows users to include remote resources in their scripts + - the value must be a valid url, currently no local file support + - must require a resource of the same file type (JS for JS, CSS for CSS) + - when a resource is required, it is downloaded and saved locally + - the resources is downloaded once at save and never checked for updates or parsed in anyway + - if you want to update the require resources, and the url does not change, you must remove the resources, save, then re-input it + - **require remote resources at your own risk, the extension never validates remote resource code in any way and be aware that using remote resources from untrusted sources can jeopardize your personal security** - `@version` - - used to determine the current version of a userscript - - when paired with `@updateURL`, this will allow the user to update a userscript from a remote source, if the version on their machine is `<` version at the update URL - - `@version` does nothing by itself, it needs to be paired with` @updateURL` for remote updating to function properly + - used to determine the current version of a userscript + - when paired with `@updateURL`, this will allow the user to update a userscript from a remote source, if the version on their machine is `<` version at the update URL + - `@version` does nothing by itself, it needs to be paired with` @updateURL` for remote updating to function properly - `@updateURL` - - the remote url to check version against - - if the version of the file located at the update URL is `>` the version on the local machine, the file will be updated - - `@updateURL` does nothing by itself, it needs to be paired with `@version` for remote updating to function properly + - the remote url to check version against, the url `/PATH` should end with `.meta.js` and contains the Metadata block + - if the version of the file located at the update URL is `>` the version on the local machine, the file will be updated + - `@updateURL` does nothing by itself, it needs to be paired with `@version` for remote updating to function properly + - Read this [link](https://stackoverflow.com/questions/38023717/) for more details + - _Note: The extension does not correctly implement the entire update process, tracking in issue [#248](https://github.com/quoid/userscripts/issues/248)_ - `@downloadURL` - - optional download location for a remotely updateable file (*i.e. a file that has both `@version` and `@updateURL`) - - when paired with `@version` and `@updateURL`, if the local version is `<` the version of the file that `@updateURL` points to, the extension will attempt to update the file's code with the contents of the file located at the `@downloadURL` - - `@downloadURL` does nothing by itself, it needs `@version` and `@updateURL` to present in order to function properly + - the url `/PATH` should end with `.user.js`, that is, it cannot be in `?QUERY` or `#HASH` parts + - optional download location for a remotely updateable file (\*i.e. a file that has both `@version` and `@updateURL`) + - when paired with `@version` and `@updateURL`, if the local version is `<` the version of the file that `@updateURL` points to, the extension will attempt to update the file's code with the contents of the file located at the `@downloadURL` + - `@downloadURL` does nothing by itself, it needs `@version` and `@updateURL` to present in order to function properly - `@noframes` - - this key takes no value - - prevents code from being injected into nested frames - + - this key takes no value + - prevents code from being injected into nested frames +- `@grant` + - Imperative controls which special [`APIs`](#api) (if any) your script uses, one on each `@grant` line, only those API methods will be provided. + - If no `@grant` values are provided, `none` will be assumed. + - If you specify `none` and something else, `none` takes precedence. + **All userscripts need at least 1 `@match` or `@include` to run!** ## API Userscripts currently supports the following api methods. All methods are asynchronous unless otherwise noted. Users must `@grant` these methods in order to use them in a userscript. When using API methods, it's only possible to inject into the content script scope due to security concerns. +> [!NOTE] +> +> The following API description applies to the latest development branch, you may need to check the documentation for the corresponding version. Please switch to the version you want to check via `Branches` or `Tags` at the top. For example: +> +> For the current version of the App Store: +> https://github.com/quoid/userscripts/tree/release/4.x.x#api +> +> For the v4.5.x and earlier versions: +> https://github.com/quoid/userscripts/tree/v4.5.4#api + +For API type definitions, please refer to: [`types.d.ts`](https://github.com/userscriptsup/testscripts/blob/f2fcde4b556fa436fe806a44a89afb9eb5dccd0b/userscripts/types.d.ts) + - `GM.addStyle(css)` - - `css: String` - - on success returns a promise resolved with the css string argument provided + - `css: String` + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved if succeeds, rejected with error message if fails - `GM.setValue(key, value)` - - `key: String`, `value: Any` - - on success returns a promise resolved with an object indicating success + - `key: String` + - `value: Any` - any can be JSON-serialized + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved if succeeds, rejected with error message if fails - `GM.getValue(key, defaultValue)` - - `key: String`, `defaultValue: Any` - - on success returns a promise resolved with the value that was set or default value provided + - `key: String` + - `defaultValue: Any` - optional + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved with the `value` that was set or `defaultValue` provided or `undefined` if succeeds, rejected with error message if fails - `GM.deleteValue(key)` - - `key: String` - - on success returns a promise resolved with an object indicating success + - `key: String` + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved if succeeds, rejected with error message if fails - `GM.listValues()` - - on success returns a promise resolved with an array of the key names of **presently set** values + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved with an array of the key names of **presently set** values if succeeds, rejected with error message if fails - `GM.getTab()` - - on success returns a promise resolved with `Any` data that is persistent as long as this tab is open + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved with `Any` data that is persistent as long as this tab is open if succeeds, rejected with error message if fails - `GM.saveTab(tabObj)` - - `tabObj: Any` - - on success returns a promise resolved with an object indicating success + - `tabObj: Any` - any can be JSON-serialized + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved if succeeds, rejected with error message if fails - `GM.openInTab(url, openInBackground)` - - `url: String`, `openInBackground: Bool` - - on success returns a promise resolved with the [tab data](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/Tab) for the tab just opened + - `url: String` + - `openInBackground: Bool` - optional, `false` by default + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved with [tab data](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/Tab) for the tab just opened if succeeds, rejected with error message if fails - `GM.closeTab(tabId)` - - `tabId: Int` - - `tabId` is **optional** and if omitted the tab that called `US.closeTab` will be closed - - on success returns a promise resolved with an object indicating success + - `tabId: Int` - optional, the `caller tab` by default + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved if succeeds, rejected with error message if fails - `GM.setClipboard(data, type)` - - `data: String` - **required** - - `type: String` - **optional** and defaults to `text/plain` - - [read more here](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData) - - on success returns a promise resolved with a `Bool` indicating success + - `data: String` + - `type: String` - optional, `text/plain` by default + - [read more here](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData) + - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), resolved with a `Bool` value indicating succeeds or fails, rejected with error message if fails - `GM.info` && `GM_info` - - is available without needing to add it to `@grant` - - an object containing information about the running userscript - - `scriptHandler: String` - returns `Userscripts` - - `version: String` - the version of Userscripts app - - `scriptMetaStr: String` - the metablock for the currently running script - - `script: Object` - contains data about the currently running script - - `description: String` - - `exclude-match: [String]` - - `excludes: [String]` - - `grant: [String]` - - `includes: [String]` - - `inject-into: String` - - `matches: [String]` - - `name: String` - - `namespace: String` - - `noframes: Bool` - - `require: [String]` - - `resources: [String]` - *currently not implemented* - - `run-at: String` - - `version: String` - *the userscript version value* + - is available without needing to add it to `@grant` + - an object containing information about the running userscript + - `scriptHandler: String` - returns `Userscripts` + - `version: String` - the version of Userscripts app + - `scriptMetaStr: String` - the metablock for the currently running script + - `script: Object` - contains data about the currently running script + - `description: String` + - `exclude-match: [String]` + - `excludes: [String]` + - `grant: [String]` + - `includes: [String]` + - `inject-into: String` + - `matches: [String]` + - `name: String` + - `namespace: String` + - `noframes: Bool` + - `require: [String]` + - `resources: [String]` - _currently not implemented_ + - `run-at: String` + - `version: String` - _the userscript version value_ - `GM.xmlHttpRequest(details)` - - `details: Object` - - the `details` object accepts the following properties - - `url` - `String` - **required** - - `method` - `String` - defaults to `GET` - - `user` - `String` - - `password` - `String` - - `headers` - `Object` - - `overrideMimeType` - - `timeout` - `Int` - - `binary` - `Bool` - - `data` - `String` - - `responseType` - `String` - - read more about [XMLHttpRequests here](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) - - event handlers: - - `onabort` - `function` - - `onerror` - `function` - - `onload` - `function` - - `onloadend` - `function` - - `onloadstart` - `function` - - `onprogress` - `function` - - `onreadystatechange` - `function` - - `ontimeout` - `function` - - the response object passed to the event handlers has the following properties: - - `readyState` - - `response` - - `responseHeaders` - - `responseType` - - `responseURL` - - `status` - - `statusText` - - `timeout` - - `withCredentials` - - `responseText` (when `responseType` is `text`) - - returns an object with a single property, `abort`, which is a `function` - - usage: `const foo = GM.xmlHttpRequest({...});` ... `foo.abort()` to abort the request + - `details: Object` + - the `details` object accepts the following properties + - `url: String` - required + - `method: String` - optional, `GET` by default + - `user: String` - optional + - `password: String` - optional + - `headers: Object` - optional + - `overrideMimeType: String` - optional + - `timeout: Int` - optional + - `binary: Bool` - optional (Deprecated, use binary data objects such as `Blob`, `ArrayBuffer`, `TypedArray`, etc. instead.) + - `data: String | Blob | ArrayBuffer | TypedArray | DataView | FormData | URLSearchParams` - optional + - `responseType: String` - optional + - refer to [`XMLHttpRequests`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) + - upload event handlers: + - `upload: Object` - optional + - `onabort: Function` - optional + - `onerror: Function` - optional + - `onload: Function` - optional + - `onloadend: Function` - optional + - `onloadstart: Function` - optional + - `onprogress: Function` - optional + - `ontimeout: Function` - optional + - the progress object passed to the event handlers has the following properties: + - `lengthComputable` + - `loaded` + - `total` + - event handlers: + - `onabort: Function` - optional + - `onerror: Function` - optional + - `onload: Function` - optional + - `onloadend: Function` - optional + - `onloadstart: Function` - optional + - `onprogress: Function` - optional + - `onreadystatechange: Function` - optional + - `ontimeout: Function` - optional + - the response object passed to the event handlers has the following properties: + - `readyState` + - `response` + - `responseHeaders` + - `responseType` + - `responseURL` + - `status` + - `statusText` + - `timeout` + - `responseText` (when `responseType` is `text`) + - returns a custom [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) contains an additional property `abort`, resolved with the response object. + - usage: + - `const xhr = GM.xmlHttpRequest({...});` + - `xhr.abort();` to abort the request + - `const response = await xhr;` + - or just: + - `const response = await GM.xmlHttpRequest({...});` - `GM_xmlhttpRequest(details)` - - an alias for `GM.xmlHttpRequest`, works exactly the same + - Basically the same as `GM.xmlHttpRequest(details)`, except: + - returns an object with a single property, `abort`, which is a `Function` + - usage: `const foo = GM.xmlHttpRequest({...});` ... `foo.abort();` to abort the request ## Scripts Directory @@ -277,7 +338,7 @@ This is the directory where the app/extension will read from and write to. This ## Getting Help -If you encounter a problem while using this app/extension or are in need of some assistance, please open an issue here in the repository. When doing so, please provide as much detail as possible. This includes listing system specs and what website and script you are trying to execute. *Please follow the issue template!* +If you encounter a problem while using this app/extension or are in need of some assistance, please open an issue here in the repository. When doing so, please provide as much detail as possible. This includes listing system specs and what website and script you are trying to execute. _Please follow the issue template!_ ## FAQs @@ -299,35 +360,32 @@ If you encounter a problem while using this app/extension or are in need of some **When I use `@require`, where are the required files stored?** -> All required files are saved *as Javascript files* in the extension container folder in macOS 11.x. That folder is located in the default save location, at: `~/Library/Containers/Userscripts/Data/Documents/require/`. +> All required files are saved _as Javascript files_ in the extension container folder in macOS 11.x. That folder is located in the default save location, at: `~/Library/Containers/Userscripts/Data/Documents/require/`. > > If you move files from the require folder or manually edit the `manifest.json` file, you will likely break app/extension functionality. ## Contributing -Code level contributions are welcome. *I prefer to collaborate directly with contributors rather than receiving spontaneous pull requests*. If you feel you can improve the project in some way, please reach out to me by email or by opening an issue with your improvement/feature request. -Further, any issue marked "help wanted" is actively seeking assistance. Please respond to those issues with feedback, guidance or offers of coding assistance. +Code level contributions please refer to [contributing.md](docs/contributing.md) -Notes: -- use [semantic commit messages](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716) -- under most circumstances, you should fork the most current version of the `develop` branch for your contributions +Further, any issue marked "help wanted" is actively seeking assistance. Please respond to those issues with feedback, guidance or offers of coding assistance. -**Please ensure your contributions align with the project's license before committing anything.** +Participating and interacting with any existing [`Issues`](https://github.com/quoid/userscripts/issues) or [`Discussions`](https://github.com/quoid/userscripts/discussions) would be a great help to the project and open source communities. Thank you for your contributions. ## Support + The quickest and easiest way to support the project is by [leaving a positive review on the App Store](https://apps.apple.com/us/app/userscripts/id1463298887) if you enjoy the extension and want to see future improvements. Seeing these reviews let me know I am doing something right, or wrong, and motivates me to continue working on the project. The second best way to help out is to sign up to beta test new versions of the app. Since this extension values your privacy, and **does not collect any data from users**, it is difficult to gauge how the extension is being used. By signing up to be a beta tester it not only allows you to test upcoming features, but also gives me the opportunity to elicit direct feedback from real users. -**[iOS Beta Sign Up Form](https://forms.gle/QB46uYQHVyCxULue9)** - -**[macOS Beta Sign Up Form](https://forms.gle/cUDtKg1ip4Vc9Xhc7)** +**Please join and test the corresponding beta version in [releases](https://github.com/quoid/userscripts/releases) via the TestFlight public link.** ## Privacy Policy + Userscripts does not collect any data from its users nor monitor activities or actions you perform within the application and extension. This means everything that you do with the application and extension is private to you and is never shared with the developers or third parties. Since there is no data collection, there is no data retention of any kind. ## License -Copyright (c) 2022 Justin Wasack +Copyright © 2018-2025 Justin Wasack Licensed under the [GNU General Public License v3.0](/LICENSE) license for all open source applications. A commercial license is required for all other applications. diff --git a/Userscripts.code-workspace b/Userscripts.code-workspace new file mode 100644 index 00000000..11c4d094 --- /dev/null +++ b/Userscripts.code-workspace @@ -0,0 +1,14 @@ +// https://code.visualstudio.com/docs/editor/workspaces +{ + "folders": [ + { + "name": "Userscripts-JS", + "path": ".", + }, + { + "name": "Userscripts-Xcode", + "path": "xcode", + }, + ], + "settings": {}, +} diff --git a/docs/contributing.md b/docs/contributing.md index b039ead6..3cb92e9d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,16 +1,33 @@ # Welcome to Userscripts contributing guide +### Please note: the project is currently temporarily suspended from receiving direct code contributions. + +Please do not submit any new pull requests, they will be close immediately. + +If you have any ideas and suggestions, please submit them to us through [`Issues`](https://github.com/quoid/userscripts/issues) or [`Discussions`](https://github.com/quoid/userscripts/discussions). + +We apologize for the inconvenience and thank you for your understanding. + +### The following is the original guide for reference only. + Thank you for investing your time in contributing to this open source project! We hope this guide will helps you understand how to contribute to this project. For development and build environments please refer to [development guide](dev.md). -**Note: Portions of this guide are outdated. Some processes are still being tweaked and optimized, we will update them in due course.** +> [!NOTE] +> Portions of this guide are outdated. Some processes are still being tweaked and optimized, we will update them in due course. + +> [!IMPORTANT] +> Please ensure your contributions align with the project's license before committing anything. +> +> Any pull requests must first go through code formatting and linters, otherwise you will fail the CI check. (Refer to the `scripts` in [`package.json`](../package.json)) # Branches ### `main`: default branch + - corresponding to development channel - all development work around this branch - protected branches not accept commits @@ -18,6 +35,7 @@ For development and build environments please refer to [development guide](dev.m - all sub-branches will be merged via PRs ### `beta`: latest test version + - generated from the master branch - corresponding to TestFlight channel - only accept version number commits @@ -25,6 +43,7 @@ For development and build environments please refer to [development guide](dev.m - never merge back into master branch ### `release`: latest public version + - generated from the beta branch - generate GitHub Releases and Tags - corresponding to App Store channel @@ -32,6 +51,7 @@ For development and build environments please refer to [development guide](dev.m - always merge back into master branch # Commits + ### Please use semantic commit messages Format: `(): ` @@ -72,6 +92,7 @@ We will improve the guidelines for creating PRs in the future, like create [`pul If you are not a member of the project, first you need to fork the master branch of the repository. ### General development + - create a new branch from the master branch - suggested branch naming format like: - `issue/333` @@ -95,4 +116,5 @@ If you are not a member of the project, first you need to fork the master branch - Despite various tests, we may still find bugs through user feedback in the public version, and we will fix those minor bugs through hotfixes. # About -[Userscripts](https://github.com/quoid/userscripts) @ 2018-2023 + +[Userscripts](https://github.com/quoid/userscripts) diff --git a/docs/dev.md b/docs/dev.md index 85eb1b96..262aac05 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -1,56 +1,82 @@ +# Environment + The `root` directory is a typical multi-page app of Vite with Svelte JavaScript project. The `xcode` directory is the root of the Xcode project where the Safari extension app is built. For other directory structure instructions, please refer to [structure.md](structure.md). -# Environment +### Recommended code editor + +[Visual Studio Code](https://github.com/Microsoft/vscode) with extensions: [/.vscode/extensions.json](../.vscode/extensions.json) + +# Template + +The project is currently based on [`template-svelte`](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-svelte) from [`create-vite`](https://github.com/vitejs/vite/tree/main/packages/create-vite). + +Additional reference templates are from [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). + +Reviewing the template will help you understand the composition of the project. + +# Requirement + - [`Node.js`](https://nodejs.org/) - [`Xcode`](https://geo.itunes.apple.com/app/id497799835) +- `Developer Team ID` (i.e. Apple Account, see [Xcode section](#xcode)) # Dev -- `npm install`[^1] -- `npm run dev`[^1] + +- `npm install` [^1] +- `npm run dev` [^1] # Build -- `npm run build`[^1] + +> [!NOTE] +> Before building the app with Xcode make sure you create the `.dev.xcconfig` files [below](#xcconfig) and fill in your `Developer Team ID`. +> Otherwise `App groups` related functions will not work properly. + +- `npm run build:mac` [^1][^2] - `cd ./xcode` -- `xcodebuild`[^1][^2] or build with `Xcode` App +- `xcodebuild -scheme Mac` [^1][^2][^3] or build with `Xcode` App + +[^1]: These commands can also be executed directly through the vscode tasks. Please refer to: [/.vscode/tasks.json](../.vscode/tasks.json) -[^1]: These commands can also be executed directly through the VSCode Run Task. Please refer to: [/.vscode/tasks.json](../.vscode/tasks.json) -[^2]: Local setup may be required. Please refer to: [Building from the Command Line with Xcode FAQ](https://developer.apple.com/library/archive/technotes/tn2339/_index.html) +[^2]: Select the corresponding target and platform to build. Please refer to: [/package.json](../package.json) and [xcode-schemes](../xcode/Userscripts.xcodeproj/xcshareddata/xcschemes/) + +[^3]: Local setup may be required. Please refer to: [Building from the Command Line with Xcode FAQ](https://developer.apple.com/library/archive/technotes/tn2339/_index.html) # Xcode +Please note that a developer account is required, which can be a free Apple Account that has agreed to the Apple Developer Agreement. This is required to obtain a `Team ID` and use the `App groups` capability. + ### Configurations -The Xcode project contains two configurations, which have independent `xcconfig` configuration files, and can run on your local at the same time without conflicts. -- `Debug`: No developer account is required, that is `Sign to Run Locally` by default, which will speed up the build during development. This is convenient for developers without an account, and contributors who only need to do simple local debugging. +The Xcode project contains several configurations, which have independent `xcconfig` configuration files, and can run on your local at the same time without conflicts. -- `Release`: A developer account is required, which means it can be used for distribution as well as running on real iOS/iPadOS devices. You will need to override your developer account information in `xcconfig` to complete the build. +- `Vite`: for vite real-time development. -### xcconfig -All `xcconfig` files are in the [`/xcode/xcconfig/`](../xcode/xcconfig) directory. Each `.xcconfig` file can be overridden by `.dev.xcconfig` in the same path, they will be ignored by `git`, so you can override any build settings locally. +- `Debug`: for development and debugging. -For example, you can create an `Userscripts-Release.dev.xcconfig` file to override [`Userscripts-Release.xcconfig`](../xcode/xcconfig/Userscripts-Release.xcconfig) and fill in your own developer account information there: +- `Release`: for building and distributing. -`Userscripts-Release.dev.xcconfig` -``` -DEVELOPMENT_TEAM = XXXXXXXXXX -``` +### xcconfig + +All `xcconfig` files are in the [`/xcode/xcconfig/`](../xcode/xcconfig) directory. Each `.xcconfig` file can be overridden by `.dev.xcconfig` in the same path, they will be ignored by `git`, so you can override any build settings locally. -Another example, if you want `Debug` builds to be also signed, so instead of enabling `Allow Unsigned Extensions` every time in Safari, you can create: +For example, you can create an `Userscripts-Debug.dev.xcconfig` file to override [`Userscripts-Debug.xcconfig`](../xcode/xcconfig/Userscripts-Debug.xcconfig) and fill in your own developer account `Team ID` there: `Userscripts-Debug.dev.xcconfig` + ``` -CODE_SIGN_IDENTITY = Apple Development DEVELOPMENT_TEAM = XXXXXXXXXX ``` Note that all existing `.xcconfig` files already include `.dev.xcconfig` files of the same name, so they will be applied automatically once you create them. You only need to fill in the setting items that need to be overridden or added. # Contribute + [Contributing guide](contributing.md) # About -[Userscripts](https://github.com/quoid/userscripts) @ 2018-2023 \ No newline at end of file + +[Userscripts](https://github.com/quoid/userscripts) diff --git a/docs/structure.md b/docs/structure.md index 602cadea..1a0cd320 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -1,32 +1,41 @@ # Project directory structure + For development and build environments please refer to [development guide](dev.md). ## `docs` + Store documents and project description files, etc. ## `etc` + Readme references and project history assets ## `public` + Project Static Asset https://vitejs.dev/guide/assets.html#the-public-directory ### `./public/extension-page/jshint.min.js` + CodeMirror 5 `codemirror/addon/lint/javascript-lint.js` depends on `jshint.js` https://codemirror.net/5/doc/manual.html#addon_lint https://github.com/jshint/jshint/blob/main/dist/jshint.js ## `scripts` + Custom build scripts ref in `package.json` Used to build the source code in `src` below ## `src` + Project Web App/Ext Source Code Build the project from here to xcode resources ## `xcode` + Project Bundled App Source Code Build the project from here to extension bundled app # About -[Userscripts](https://github.com/quoid/userscripts) @ 2018-2023 + +[Userscripts](https://github.com/quoid/userscripts) diff --git a/docs/template.md b/docs/template.md deleted file mode 100644 index 69c2ac55..00000000 --- a/docs/template.md +++ /dev/null @@ -1,47 +0,0 @@ -# Svelte + Vite - -This template should help get you started developing with Svelte in Vite. - -## Recommended IDE Setup - -[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). - -## Need an official Svelte framework? - -Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. - -## Technical considerations - -**Why use this over SvelteKit?** - -- It brings its own routing solution which might not be preferable for some users. -- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. - -This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. - -Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. - -**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** - -Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. - -**Why include `.vscode/extensions.json`?** - -Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. - -**Why enable `checkJs` in the JS template?** - -It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration. - -**Why is HMR not preserving my local component state?** - -HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr). - -If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. - -```js -// store.js -// An extremely simple external store -import { writable } from 'svelte/store' -export default writable(0) -``` diff --git a/entry-app-webview.html b/entry-app-webview.html index 10167879..d8789222 100644 --- a/entry-app-webview.html +++ b/entry-app-webview.html @@ -1,14 +1,17 @@ - + - - - - - - Userscripts App - - -
- - + + + + + + Userscripts App + + +
+ + diff --git a/entry-ext-action-popup.html b/entry-ext-action-popup.html index e135d374..8d65c1ca 100644 --- a/entry-ext-action-popup.html +++ b/entry-ext-action-popup.html @@ -1,14 +1,14 @@ - + - - - - - Userscripts popup - - - -
- - + + + + + Userscripts popup + + + +
+ + diff --git a/entry-ext-background.html b/entry-ext-background.html new file mode 100644 index 00000000..e3ee5d4c --- /dev/null +++ b/entry-ext-background.html @@ -0,0 +1,9 @@ + + + + Userscripts background + + + + + diff --git a/entry-ext-extension-page.html b/entry-ext-extension-page.html index fbbce42d..24b226cb 100644 --- a/entry-ext-extension-page.html +++ b/entry-ext-extension-page.html @@ -1,15 +1,15 @@ - + - - - - - Userscripts page - - - -
- - - + + + + + Userscripts page + + + +
+ + + diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..e89b2946 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,68 @@ +/** + * @file ESLint project configuration (New flat config) + * @see {@link https://eslint.org/docs/latest/use/configure/configuration-files-new} + * @see {@link https://eslint.org/docs/latest/use/configure/migration-guide} + * @see {@link https://eslint.org/docs/latest/extend/plugin-migration-flat-config} + * @see {@link https://eslint.org/blog/2022/08/new-config-system-part-1/} + * @see {@link https://eslint.org/blog/2022/08/new-config-system-part-2/} + */ + +import js from "@eslint/js"; +import globals from "globals"; +import eslintPluginSvelte from "eslint-plugin-svelte"; +import eslintConfigPrettier from "eslint-config-prettier"; + +/** @type {import("eslint").Linter.Config[]} */ +export default [ + /** + * @see {@link https://eslint.org/docs/latest/use/configure/configuration-files-new#using-predefined-configurations} + * @see {@link https://github.com/eslint/eslint/tree/main/packages/js} + */ + js.configs.recommended, + + /** + * @see {@link https://github.com/sveltejs/eslint-plugin-svelte} + */ + ...eslintPluginSvelte.configs.recommended, + ...eslintPluginSvelte.configs.prettier, + + /** + * @see {@link https://github.com/prettier/eslint-config-prettier} + * turns off the conflict rules, put it last + */ + eslintConfigPrettier, + + /** custom config objects */ + + /** @see {@link https://eslint.org/docs/latest/use/configure/configuration-files-new#globally-ignoring-files-with-ignores} */ + { + ignores: ["**/dist/", "**/build/", "etc/", "xcode/", "public/"], + }, + + /** @see {@link https://eslint.org/docs/latest/use/configure/migration-guide#configuring-language-options} */ + { + files: ["*.js", "scripts/**/*.js"], + languageOptions: { + globals: { + ...globals.node, + }, + }, + }, + { + files: ["src/{app,dev,shared}/**/*.{js,svelte}"], + languageOptions: { + globals: { + ...globals.browser, + }, + }, + }, + { + files: ["src/ext/**/*.{js,svelte}"], + languageOptions: { + globals: { + ...globals.browser, + ...globals.webextensions, + }, + }, + }, +]; diff --git a/etc/uilayout.sketch b/etc/uilayout.sketch index 3c513380..f86be3ba 100644 Binary files a/etc/uilayout.sketch and b/etc/uilayout.sketch differ diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 00000000..86b5e78c --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,4 @@ +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile + +app_identifier("com.userscripts.macos") diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 00000000..dfbdaa49 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,89 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools + +LOCAL_FASTFILE = "local/Fastfile.local.rb" +if File.file?(LOCAL_FASTFILE) + import(LOCAL_FASTFILE) +end + +PROJECT_PATH = "xcode/Userscripts.xcodeproj" + +# https://docs.fastlane.tools/actions/is_ci/ +if is_ci + setup_ci + IS_CI = true +else + IS_CI = false +end + +# https://docs.fastlane.tools/actions/app_store_connect_api_key/ +app_store_connect_api_key( + key_id: ENV['APP_STORE_CONNECT_API_KEY_KEY_ID'], + issuer_id: ENV['APP_STORE_CONNECT_API_KEY_ISSUER_ID'], + key_content: ENV['APP_STORE_CONNECT_API_KEY_KEY'], + key_filepath: ENV['APP_STORE_CONNECT_API_KEY_KEY_FILEPATH'], +) + +default_platform(:ios) + +platform :ios do + lane :beta do + match + BUILD_NUMBER = latest_testflight_build_number(platform: "ios") + 1 + File.open(ENV['GITHUB_OUTPUT'], 'a') do |file| + file.puts "ver=v#{ENV['MARKETING_VERSION']}(#{BUILD_NUMBER})" + end + build_app( + project: PROJECT_PATH, + scheme: "iOS", + xcargs: { + CURRENT_PROJECT_VERSION: BUILD_NUMBER.to_s, + MARKETING_VERSION: ENV['MARKETING_VERSION'], + }, + output_directory: "build", + output_name: "Userscripts-iOS-Beta", + silent: IS_CI, + ) + upload_to_testflight( + changelog: "#{ENV['GITHUB_REF_NAME']}", + distribute_external: true, + groups: ["QA"], + ) + end + + lane :release do + puts "ios release #{ENV['MARKETING_VERSION']}" + beta + end +end + +platform :mac do + lane :beta do + match + BUILD_NUMBER = latest_testflight_build_number(platform: "osx") + 1 + File.open(ENV['GITHUB_OUTPUT'], 'a') do |file| + file.puts "ver=v#{ENV['MARKETING_VERSION']}(#{BUILD_NUMBER})" + end + build_mac_app( + project: PROJECT_PATH, + scheme: "Mac", + xcargs: { + CURRENT_PROJECT_VERSION: BUILD_NUMBER.to_s, + MARKETING_VERSION: ENV['MARKETING_VERSION'], + }, + output_directory: "build", + output_name: "Userscripts-Mac-Beta", + silent: IS_CI, + ) + upload_to_testflight( + changelog: "#{ENV['GITHUB_REF_NAME']}", + distribute_external: true, + groups: ["QA"], + ) + end + + lane :release do + puts "mac release #{ENV['MARKETING_VERSION']}" + beta + end +end \ No newline at end of file diff --git a/fastlane/Gemfile b/fastlane/Gemfile new file mode 100644 index 00000000..a4353219 --- /dev/null +++ b/fastlane/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" + +gem "fastlane" +# gem "rubocop" diff --git a/fastlane/Gemfile.lock b/fastlane/Gemfile.lock new file mode 100644 index 00000000..c6013a88 --- /dev/null +++ b/fastlane/Gemfile.lock @@ -0,0 +1,233 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.8) + abbrev (0.1.2) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.4.0) + aws-partitions (1.1188.0) + aws-sdk-core (3.239.2) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal + jmespath (~> 1, >= 1.6.1) + logger + aws-sdk-kms (1.118.0) + aws-sdk-core (~> 3, >= 3.239.1) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.205.0) + aws-sdk-core (~> 3, >= 3.234.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.12.1) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.2.0) + bigdecimal (3.3.1) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + csv (3.3.5) + declarative (0.0.20) + digest-crc (0.7.0) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.112.0) + faraday (1.10.4) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.8) + faraday (>= 0.8.0) + http-cookie (>= 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.1) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.1.1) + multipart-post (~> 2.0) + faraday-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.1) + faraday (~> 1.0) + fastimage (2.4.0) + fastlane (2.229.1) + CFPropertyList (>= 2.3, < 4.0.0) + abbrev (~> 0.1.2) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + base64 (~> 0.2.0) + bundler (>= 1.12.0, < 3.0.0) + colored (~> 1.2) + commander (~> 4.6) + csv (~> 3.3) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3.0) + naturally (~> 2.2) + nkf (~> 0.2.0) + optparse (>= 0.1.1, < 1.0.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.4.1) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.54.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.3) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.8.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.5.0) + google-cloud-storage (1.47.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.31.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.8) + domain_name (~> 0.5) + httpclient (2.9.0) + mutex_m + jmespath (1.6.2) + json (2.16.0) + jwt (2.10.2) + base64 + logger (1.7.0) + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.17.0) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.3.0) + nkf (0.2.0) + optparse (0.8.0) + os (1.1.4) + plist (3.7.2) + public_suffix (7.0.0) + rake (13.3.1) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rexml (3.4.4) + rouge (3.28.0) + ruby2_keywords (0.0.5) + rubyzip (2.4.1) + security (0.1.5) + signet (0.21.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 4.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + sysrandom (1.0.5) + terminal-notifier (2.0.0) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + trailblazer-option (0.1.2) + tty-cursor (0.7.1) + tty-screen (0.8.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (2.6.0) + word_wrap (1.0.0) + xcodeproj (1.27.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + arm64-darwin-25 + ruby + +DEPENDENCIES + fastlane + +BUNDLED WITH + 2.7.2 diff --git a/fastlane/Matchfile b/fastlane/Matchfile new file mode 100644 index 00000000..3cbce194 --- /dev/null +++ b/fastlane/Matchfile @@ -0,0 +1,12 @@ +# https://docs.fastlane.tools/actions/match + +readonly(true) +type("appstore") +app_identifier([ + "com.userscripts.macos", + "com.userscripts.macos.Userscripts-Extension", +]) + +for_platform :mac do + additional_cert_types(["mac_installer_distribution"]) +end \ No newline at end of file diff --git a/index.html b/index.html index a0b12e43..215948a7 100644 --- a/index.html +++ b/index.html @@ -1,26 +1,28 @@ - + - - - - Userscripts dev - - - -
- - - + + + + Userscripts dev + + + +
+ + + diff --git a/jsconfig.json b/jsconfig.json index a8284bdf..8f85baf3 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,33 +1,26 @@ +// https://code.visualstudio.com/docs/languages/jsconfig +// https://www.typescriptlang.org/docs/handbook/tsconfig-json.html +// https://www.typescriptlang.org/tsconfig + +// https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html +// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html + { - "compilerOptions": { - "moduleResolution": "Node", - "target": "ESNext", - "module": "ESNext", - /** - * svelte-preprocess cannot figure out whether you have - * a value or a type, so tell TypeScript to enforce using - * `import type` instead of `import` for Types. - */ - "importsNotUsedAsValues": "error", - "isolatedModules": true, - "resolveJsonModule": true, - /** - * To have warnings / errors of the Svelte compiler at the - * correct position, enable source maps by default. - */ - "sourceMap": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - /** - * Typecheck JS in `.svelte` and `.js` files by default. - * Disable this if you'd like to use dynamic types. - */ - // "checkJs": true - }, - /** - * Use global.d.ts instead of compilerOptions.types - * to avoid limiting type declarations. - */ - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + + "verbatimModuleSyntax": true, + "isolatedModules": true, + "resolveJsonModule": true, + + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "sourceMap": true + }, + "include": ["*.d.ts", "*.js"] } diff --git a/package-lock.json b/package-lock.json index b857444a..d4f33dbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4329 +1,4194 @@ { - "name": "userscripts", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "userscripts", - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^2.4.5", - "autoprefixer": "^10.4.16", - "cm-show-invisibles": "^3.1.0", - "codemirror": "^5.65.15", - "eslint": "^8.52.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.0", - "eslint-plugin-svelte": "^2.32.4", - "postcss-html": "^1.5.0", - "stylelint": "^15.11.0", - "stylelint-config-html": "^1.1.0", - "stylelint-config-recommended": "^13.0.0", - "stylelint-config-standard": "^34.0.0", - "svelte": "4.2.0", - "vite": "^4.5.0" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "2.3.2", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^2.2.1" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "2.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18" - } - }, - "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.5", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.3.2", - "@csstools/css-tokenizer": "^2.2.1" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "3.0.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": "^14 || ^16 || >=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.0.13" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.9.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.52.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "2.4.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^1.0.4", - "debug": "^4.3.4", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.3", - "svelte-hmr": "^0.15.3", - "vitefu": "^0.2.4" - }, - "engines": { - "node": "^14.18.0 || >= 16" - }, - "peerDependencies": { - "svelte": "^3.54.0 || ^4.0.0", - "vite": "^4.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": "^14.18.0 || >= 16" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^2.2.0", - "svelte": "^3.54.0 || ^4.0.0", - "vite": "^4.0.0" - } - }, - "node_modules/@types/estree": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/minimist": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "dev": true, - "license": "ISC" - }, - "node_modules/acorn": { - "version": "8.10.0", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.16", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001538", - "fraction.js": "^4.3.6", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axobject-query": { - "version": "3.2.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.1", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys": { - "version": "7.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^6.3.0", - "map-obj": "^4.1.0", - "quick-lru": "^5.1.1", - "type-fest": "^1.2.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/type-fest": { - "version": "1.4.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001553", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cm-show-invisibles": { - "version": "3.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/code-red": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, - "node_modules/codemirror": { - "version": "5.65.15", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/colord": { - "version": "2.9.3", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "dev": true, - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "dev": true, - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-functions-list": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12 || >=16" - } - }, - "node_modules/css-tree": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/decamelize": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.563", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/entities": { - "version": "4.5.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.18.20", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.52.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.52.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-svelte": { - "version": "2.34.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@jridgewell/sourcemap-codec": "^1.4.14", - "debug": "^4.3.1", - "esutils": "^2.0.3", - "known-css-properties": "^0.28.0", - "postcss": "^8.4.5", - "postcss-load-config": "^3.1.4", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.11", - "semver": "^7.5.3", - "svelte-eslint-parser": ">=0.33.0 <1.0.0" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0-0", - "svelte": "^3.37.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-svelte/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.15.0", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "dev": true, - "license": "ISC" - }, - "node_modules/for-each": { - "version": "0.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/global-modules": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "13.23.0", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globjoin": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-tags": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/ignore": { - "version": "5.2.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-lazy": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "dev": true, - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-reference": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "8.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/known-css-properties": { - "version": "0.28.0", - "dev": true, - "license": "MIT" - }, - "node_modules/levn": { - "version": "0.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-character": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/magic-string": { - "version": "0.30.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mathml-tag-names": { - "version": "2.1.3", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/meow": { - "version": "10.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/minimist": "^1.2.2", - "camelcase-keys": "^7.0.0", - "decamelize": "^5.0.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.2", - "read-pkg-up": "^8.0.0", - "redent": "^4.0.0", - "trim-newlines": "^4.0.2", - "type-fest": "^1.2.2", - "yargs-parser": "^20.2.9" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "1.4.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.6", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.13", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/periscopic": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.4.31", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-html": { - "version": "1.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "htmlparser2": "^8.0.0", - "js-tokens": "^8.0.0", - "postcss": "^8.4.0", - "postcss-safe-parser": "^6.0.0" - }, - "engines": { - "node": "^12 || >=14" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-resolve-nested-selector": { - "version": "0.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/postcss-safe-parser": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-scss": { - "version": "4.0.9", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.4.29" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^3.0.2", - "parse-json": "^5.2.0", - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0", - "read-pkg": "^6.0.0", - "type-fest": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "1.4.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "1.4.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/redent": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "indent-string": "^5.0.0", - "strip-indent": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "3.29.4", - "dev": true, - "license": "MIT", - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-function-length": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-indent": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-search": { - "version": "0.1.0", - "dev": true, - "license": "ISC" - }, - "node_modules/stylelint": { - "version": "15.11.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-parser-algorithms": "^2.3.1", - "@csstools/css-tokenizer": "^2.2.0", - "@csstools/media-query-list-parser": "^2.1.4", - "@csstools/selector-specificity": "^3.0.0", - "balanced-match": "^2.0.0", - "colord": "^2.9.3", - "cosmiconfig": "^8.2.0", - "css-functions-list": "^3.2.1", - "css-tree": "^2.3.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.1", - "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^7.0.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.3.1", - "ignore": "^5.2.4", - "import-lazy": "^4.0.0", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.29.0", - "mathml-tag-names": "^2.1.3", - "meow": "^10.1.5", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.28", - "postcss-resolve-nested-selector": "^0.1.1", - "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.13", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "style-search": "^0.1.0", - "supports-hyperlinks": "^3.0.0", - "svg-tags": "^1.0.0", - "table": "^6.8.1", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.mjs" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - } - }, - "node_modules/stylelint-config-html": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12 || >=14" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "postcss-html": "^1.0.0", - "stylelint": ">=14.0.0" - } - }, - "node_modules/stylelint-config-recommended": { - "version": "13.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "stylelint": "^15.10.0" - } - }, - "node_modules/stylelint-config-standard": { - "version": "34.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "stylelint-config-recommended": "^13.0.0" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "stylelint": "^15.10.0" - } - }, - "node_modules/stylelint/node_modules/balanced-match": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.1.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/stylelint/node_modules/known-css-properties": { - "version": "0.29.0", - "dev": true, - "license": "MIT" - }, - "node_modules/stylelint/node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svelte": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.0.tgz", - "integrity": "sha512-kVsdPjDbLrv74SmLSUzAsBGquMs4MPgWGkGLpH+PjOYnFOziAvENVzgJmyOCV2gntxE32aNm8/sqNKD6LbIpeQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^3.2.1", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", - "locate-character": "^3.0.0", - "magic-string": "^0.30.0", - "periscopic": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/svelte-eslint-parser": { - "version": "0.33.1", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-scope": "^7.0.0", - "eslint-visitor-keys": "^3.0.0", - "espree": "^9.0.0", - "postcss": "^8.4.29", - "postcss-scss": "^4.0.8" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "svelte": "^3.37.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "svelte": { - "optional": true - } - } - }, - "node_modules/svelte-hmr": { - "version": "0.15.3", - "dev": true, - "license": "ISC", - "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" - }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" - } - }, - "node_modules/svg-tags": { - "version": "1.0.0", - "dev": true - }, - "node_modules/table": { - "version": "6.8.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/trim-newlines": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vite": { - "version": "4.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "@types/node": ">= 14", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "0.2.5", - "dev": true, - "license": "MIT", - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } + "name": "userscripts", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "userscripts", + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@types/webextension-polyfill": "^0.12.3", + "autoprefixer": "^10.4.21", + "cm-show-invisibles": "^3.1.0", + "codemirror": "^5.65.19", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-svelte": "^3.9.0", + "globals": "^16.1.0", + "marked": "^15.0.12", + "prettier": "3.5.3", + "prettier-plugin-svelte": "3.4.0", + "stylelint": "^16.19.1", + "stylelint-config-html": "^1.1.0", + "stylelint-config-standard": "^38.0.0", + "svelte": "^5.33.0", + "vite": "^6.3.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", + "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" + } + }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz", + "integrity": "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz", + "integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz", + "integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz", + "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz", + "integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz", + "integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz", + "integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz", + "integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz", + "integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz", + "integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz", + "integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz", + "integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz", + "integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz", + "integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz", + "integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz", + "integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz", + "integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz", + "integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz", + "integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz", + "integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz", + "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz", + "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.0.3.tgz", + "integrity": "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", + "debug": "^4.4.0", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.15", + "vitefu": "^1.0.4" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz", + "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.7" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/webextension-polyfill": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@types/webextension-polyfill/-/webextension-polyfill-0.12.3.tgz", + "integrity": "sha512-F58aDVSeN/MjUGazXo/cPsmR76EvqQhQ1v4x23hFjUX0cfAJYE+JBWwiOGW36/VJGGxoH74sVlRIF3z7SJCKyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/cacheable": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.9.0.tgz", + "integrity": "sha512-8D5htMCxPDUULux9gFzv30f04Xo3wCnik0oOxKoRTPIBoqA7HtOcJ87uBhQTs3jCfZZTrUBGsYIZOgE0ZRgMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.8.2", + "keyv": "^5.3.3" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.3.tgz", + "integrity": "sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.0.3" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cm-show-invisibles": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cm-show-invisibles/-/cm-show-invisibles-3.1.0.tgz", + "integrity": "sha512-dfWd4QmuSMoSgONkCv4+REfPrxp3R3OE8jnxR3u2IjkLPolHArGOhMetBLo6gTeiL8tA4ff5v+VBZ1u1AZx+4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/codemirror": { + "version": "5.65.19", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.19.tgz", + "integrity": "sha512-+aFkvqhaAVr1gferNMuN8vkTSrWIFvzlMV9I2KBLCWS2WpZ2+UAkZjlMZmEuT+gcXTi6RrGQCkWq1/bDtGqhIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-functions-list": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", + "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12 || >=16" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.155", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", + "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-svelte": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.9.0.tgz", + "integrity": "sha512-nvIUNyyPGbr5922Kd1p/jXe+FfNdVPXsxLyrrXpwfSbZZEFdAYva9O/gm2lObC/wXkQo/AUmQkAihfmNJYeCjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.6.1", + "@jridgewell/sourcemap-codec": "^1.5.0", + "esutils": "^2.0.3", + "globals": "^16.0.0", + "known-css-properties": "^0.36.0", + "postcss": "^8.4.49", + "postcss-load-config": "^3.1.4", + "postcss-safe-parser": "^7.0.0", + "semver": "^7.6.3", + "svelte-eslint-parser": "^1.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": "^8.57.1 || ^9.0.0", + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrap": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.6.tgz", + "integrity": "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hookified": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.9.0.tgz", + "integrity": "sha512-2yEEGqphImtKIe1NXWEhu6yD3hlFR4Mxk4Mtp3XEyScpSt4pQ4ymmXA1zzxZpj99QkFK+nN0nzjeb2+RUi/6CQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/known-css-properties": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.36.0.tgz", + "integrity": "sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-html": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.0.tgz", + "integrity": "sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "htmlparser2": "^8.0.0", + "js-tokens": "^9.0.0", + "postcss": "^8.5.0", + "postcss-safe-parser": "^6.0.0" + }, + "engines": { + "node": "^12 || >=14" + } + }, + "node_modules/postcss-html/node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-svelte": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.4.0.tgz", + "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": "^3.0.0", + "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", + "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.0", + "@rollup/rollup-android-arm64": "4.41.0", + "@rollup/rollup-darwin-arm64": "4.41.0", + "@rollup/rollup-darwin-x64": "4.41.0", + "@rollup/rollup-freebsd-arm64": "4.41.0", + "@rollup/rollup-freebsd-x64": "4.41.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", + "@rollup/rollup-linux-arm-musleabihf": "4.41.0", + "@rollup/rollup-linux-arm64-gnu": "4.41.0", + "@rollup/rollup-linux-arm64-musl": "4.41.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-musl": "4.41.0", + "@rollup/rollup-linux-s390x-gnu": "4.41.0", + "@rollup/rollup-linux-x64-gnu": "4.41.0", + "@rollup/rollup-linux-x64-musl": "4.41.0", + "@rollup/rollup-win32-arm64-msvc": "4.41.0", + "@rollup/rollup-win32-ia32-msvc": "4.41.0", + "@rollup/rollup-win32-x64-msvc": "4.41.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint": { + "version": "16.19.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.19.1.tgz", + "integrity": "sha512-C1SlPZNMKl+d/C867ZdCRthrS+6KuZ3AoGW113RZCOL0M8xOGpgx7G70wq7lFvqvm4dcfdGFVLB/mNaLFChRKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/media-query-list-parser": "^4.0.2", + "@csstools/selector-specificity": "^5.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.3", + "css-tree": "^3.1.0", + "debug": "^4.3.7", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^10.0.8", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^7.0.3", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.36.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.3", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.1.0", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "supports-hyperlinks": "^3.2.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint-config-html": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.1.0.tgz", + "integrity": "sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12 || >=14" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz", + "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.16.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "38.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-38.0.0.tgz", + "integrity": "sha512-uj3JIX+dpFseqd/DJx8Gy3PcRAJhlEZ2IrlFOc4LUxBX/PNMEQ198x7LCOE2Q5oT9Vw8nyc4CIL78xSqPr6iag==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "license": "MIT", + "dependencies": { + "stylelint-config-recommended": "^16.0.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.18.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.0.tgz", + "integrity": "sha512-Et/ex6smi3wOOB+n5mek+Grf7P2AxZR5ueqRUvAAn4qkyatXi3cUC1cuQXVkX0VlzBVsN4BkWJFmY/fYiRTdww==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^6.1.9" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.9.tgz", + "integrity": "sha512-DUqiKkTlAfhtl7g78IuwqYM+YqvT+as0mY+EVk6mfimy19U79pJCzDZQsnqk3Ou/T6hFXWLGbwbADzD/c8Tydg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cacheable": "^1.9.0", + "flatted": "^3.3.3", + "hookified": "^1.8.2" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/svelte": { + "version": "5.33.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.33.0.tgz", + "integrity": "sha512-COPJa+cxefgDShgTr3vcLKXdyBfuE96Z65D/EsuxDcU3fwqBxAN6bVjZ0LoVYyAYAShu4K96/k9BxxlAVO6fpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "esm-env": "^1.2.1", + "esrap": "^1.4.6", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-eslint-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.2.0.tgz", + "integrity": "sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.0", + "postcss": "^8.4.49", + "postcss-scss": "^4.0.9", + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitefu": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.6.tgz", + "integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", + "dev": true, + "license": "MIT" + } + } } diff --git a/package.json b/package.json index 732971b0..6f9ef87f 100644 --- a/package.json +++ b/package.json @@ -1,30 +1,37 @@ { - "name": "userscripts", - "private": true, - "type": "module", - "scripts": { - "dev": "vite", - "build": "npm run build:app && npm run build:ext", - "build:app": "node scripts/build-app.js", - "build:ext": "node scripts/build-ext-v4.js", - "lint:js": "eslint ./", - "lint:css": "stylelint '**/*.{css,svelte}'" - }, - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^2.4.5", - "autoprefixer": "^10.4.16", - "cm-show-invisibles": "^3.1.0", - "codemirror": "^5.65.15", - "eslint": "^8.52.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.0", - "eslint-plugin-svelte": "^2.32.4", - "postcss-html": "^1.5.0", - "stylelint": "^15.11.0", - "stylelint-config-html": "^1.1.0", - "stylelint-config-recommended": "^13.0.0", - "stylelint-config-standard": "^34.0.0", - "svelte": "4.2.0", - "vite": "^4.5.0" - } -} \ No newline at end of file + "name": "userscripts", + "private": true, + "type": "module", + "sideEffects": false, + "scripts": { + "dev": "vite", + "dev:mac-safari": "SAFARI_PLATFORM=mac node scripts/dev-safari.js", + "dev:ios-safari": "SAFARI_PLATFORM=ios node scripts/dev-safari.js", + "build:mac-safari-15": "SAFARI_PLATFORM=mac node scripts/build-safari-15.js", + "build:ios-safari-15": "SAFARI_PLATFORM=ios node scripts/build-safari-15.js", + "build:mac-safari-16.4": "SAFARI_PLATFORM=mac node scripts/build-safari-16.4.js", + "build:ios-safari-16.4": "SAFARI_PLATFORM=ios node scripts/build-safari-16.4.js", + "lint:js": "eslint . && prettier --check . --plugin prettier-plugin-svelte", + "lint:css": "stylelint '**/*.{css,html,svelte}'", + "prettier": "prettier --write . --plugin prettier-plugin-svelte" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@types/webextension-polyfill": "^0.12.3", + "autoprefixer": "^10.4.21", + "cm-show-invisibles": "^3.1.0", + "codemirror": "^5.65.19", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-svelte": "^3.9.0", + "globals": "^16.1.0", + "marked": "^15.0.12", + "prettier": "3.5.3", + "prettier-plugin-svelte": "3.4.0", + "stylelint": "^16.19.1", + "stylelint-config-html": "^1.1.0", + "stylelint-config-standard": "^38.0.0", + "svelte": "^5.33.0", + "vite": "^6.3.5" + } +} diff --git a/public/ext/safari-15/images/icon-128.png b/public/ext/safari-15/images/icon-128.png new file mode 100644 index 00000000..f0030ddd Binary files /dev/null and b/public/ext/safari-15/images/icon-128.png differ diff --git a/public/ext/safari-15/images/icon-256.png b/public/ext/safari-15/images/icon-256.png new file mode 100644 index 00000000..dea9320e Binary files /dev/null and b/public/ext/safari-15/images/icon-256.png differ diff --git a/public/ext/safari-15/images/icon-48.png b/public/ext/safari-15/images/icon-48.png new file mode 100644 index 00000000..3955fd6b Binary files /dev/null and b/public/ext/safari-15/images/icon-48.png differ diff --git a/public/ext/safari-15/images/icon-512.png b/public/ext/safari-15/images/icon-512.png new file mode 100644 index 00000000..3d0c5854 Binary files /dev/null and b/public/ext/safari-15/images/icon-512.png differ diff --git a/public/ext/safari-15/images/icon-64.png b/public/ext/safari-15/images/icon-64.png new file mode 100644 index 00000000..d1ffa334 Binary files /dev/null and b/public/ext/safari-15/images/icon-64.png differ diff --git a/public/ext/safari-15/images/icon-96.png b/public/ext/safari-15/images/icon-96.png new file mode 100644 index 00000000..411a520a Binary files /dev/null and b/public/ext/safari-15/images/icon-96.png differ diff --git a/public/ext/safari-15/images/toolbar-icon-16.png b/public/ext/safari-15/images/toolbar-icon-16.png new file mode 100644 index 00000000..cee9abb0 Binary files /dev/null and b/public/ext/safari-15/images/toolbar-icon-16.png differ diff --git a/public/ext/safari-15/images/toolbar-icon-32.png b/public/ext/safari-15/images/toolbar-icon-32.png new file mode 100644 index 00000000..1de18ba5 Binary files /dev/null and b/public/ext/safari-15/images/toolbar-icon-32.png differ diff --git a/public/ext/safari-15/images/toolbar-icon-48.png b/public/ext/safari-15/images/toolbar-icon-48.png new file mode 100644 index 00000000..e1367b2b Binary files /dev/null and b/public/ext/safari-15/images/toolbar-icon-48.png differ diff --git a/public/ext/safari-15/manifest.json b/public/ext/safari-15/manifest.json new file mode 100644 index 00000000..1f64988a --- /dev/null +++ b/public/ext/safari-15/manifest.json @@ -0,0 +1,62 @@ +{ + "manifest_version": 2, + "default_locale": "en", + "name": "__MSG_extension_name__", + "description": "__MSG_extension_description__", + "version": "_VERSION_", + "icons": { + "48": "images/icon-48.png", + "64": "images/icon-64.png", + "96": "images/icon-96.png", + "128": "images/icon-128.png", + "256": "images/icon-256.png", + "512": "images/icon-512.png" + }, + "background": { + "scripts": ["dist/background.js"], + "persistent": false + }, + "browser_action": { + "default_popup": "dist/entry-ext-action-popup.html", + "default_icon": { + "16": "images/toolbar-icon-16.png", + "32": "images/toolbar-icon-32.png", + "48": "images/toolbar-icon-48.png" + } + }, + "options_ui": { + "page": "dist/entry-ext-extension-page.html#state=settings" + }, + "content_scripts": [ + { + "js": ["dist/content-scripts/userscripts.js"], + "matches": [""], + "run_at": "document_start", + "all_frames": true + }, + { + "js": ["dist/content-scripts/dot-user-js.js"], + "matches": ["*://*/*.user.js", "*://*/*.user.js?*"], + "run_at": "document_start" + }, + { + "js": ["dist/content-scripts/script-market.js"], + "matches": ["*://*.greasyfork.org/*"], + "exclude_matches": ["*://*/*.user.js", "*://*/*.user.js?*"], + "run_at": "document_end" + } + ], + "permissions": [ + "", + "clipboardWrite", + "contextMenus", + "declarativeNetRequest", + "declarativeNetRequestWithHostAccess", + "menus", + "nativeMessaging", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation" + ] +} diff --git a/public/ext/safari-16.4/images/128.svg b/public/ext/safari-16.4/images/128.svg new file mode 100644 index 00000000..c6ec62ad --- /dev/null +++ b/public/ext/safari-16.4/images/128.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/ext/safari-16.4/images/action.svg b/public/ext/safari-16.4/images/action.svg new file mode 100644 index 00000000..ad16f09d --- /dev/null +++ b/public/ext/safari-16.4/images/action.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/ext/safari-16.4/manifest.json b/public/ext/safari-16.4/manifest.json new file mode 100644 index 00000000..ab445895 --- /dev/null +++ b/public/ext/safari-16.4/manifest.json @@ -0,0 +1,51 @@ +{ + "manifest_version": 2, + "default_locale": "en", + "name": "__MSG_extension_name__", + "description": "__MSG_extension_description__", + "version": "_VERSION_", + "icons": { + "48": "images/128.svg", + "96": "images/128.svg", + "128": "images/128.svg", + "256": "images/128.svg", + "512": "images/128.svg" + }, + "background": { + "scripts": ["dist/background.js"], + "persistent": false + }, + "browser_action": { + "default_popup": "dist/entry-ext-action-popup.html", + "default_icon": "images/action.svg" + }, + "options_ui": { + "page": "dist/entry-ext-extension-page.html#state=settings" + }, + "content_scripts": [ + { + "js": ["dist/content-scripts/userscripts.js"], + "matches": [""], + "run_at": "document_start", + "all_frames": true + } + ], + "permissions": [ + "", + "clipboardWrite", + "contextMenus", + "declarativeNetRequestWithHostAccess", + "menus", + "nativeMessaging", + "scripting", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation" + ], + "browser_specific_settings": { + "safari": { + "strict_min_version": "16.4" + } + } +} diff --git a/public/ext/safari-dev/manifest-ios.json b/public/ext/safari-dev/manifest-ios.json new file mode 100644 index 00000000..e1cac050 --- /dev/null +++ b/public/ext/safari-dev/manifest-ios.json @@ -0,0 +1,52 @@ +{ + "manifest_version": 2, + "default_locale": "en", + "name": "__MSG_extension_name__", + "description": "__MSG_extension_description__", + "version": "_VERSION_", + "icons": { + "48": "images/128.svg", + "96": "images/128.svg", + "128": "images/128.svg", + "256": "images/128.svg", + "512": "images/128.svg" + }, + "background": { + "page": "dist/entry-ext-background.html", + "persistent": false + }, + "browser_action": { + "default_popup": "dist/entry-ext-action-popup.html", + "default_icon": "images/action.svg" + }, + "options_ui": { + "page": "dist/entry-ext-extension-page.html#state=settings" + }, + "content_scripts": [ + { + "js": ["dist/content-scripts/userscripts.js"], + "matches": [""], + "run_at": "document_start", + "all_frames": true + } + ], + "permissions": [ + "", + "clipboardWrite", + "contextMenus", + "declarativeNetRequestWithHostAccess", + "menus", + "nativeMessaging", + "scripting", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation" + ], + "browser_specific_settings": { + "safari": { + "strict_min_version": "16.4" + } + }, + "content_security_policy": "script-src 'self' https://localhost:55173 https://userscripts.test:55173" +} diff --git a/public/ext/safari-dev/manifest-mac.json b/public/ext/safari-dev/manifest-mac.json new file mode 100644 index 00000000..e469aa66 --- /dev/null +++ b/public/ext/safari-dev/manifest-mac.json @@ -0,0 +1,52 @@ +{ + "manifest_version": 2, + "default_locale": "en", + "name": "__MSG_extension_name__", + "description": "__MSG_extension_description__", + "version": "_VERSION_", + "icons": { + "48": "images/128.svg", + "96": "images/128.svg", + "128": "images/128.svg", + "256": "images/128.svg", + "512": "images/128.svg" + }, + "background": { + "page": "dist/entry-ext-background.html", + "persistent": true + }, + "browser_action": { + "default_popup": "dist/entry-ext-action-popup.html", + "default_icon": "images/action.svg" + }, + "options_ui": { + "page": "dist/entry-ext-extension-page.html#state=settings" + }, + "content_scripts": [ + { + "js": ["dist/content-scripts/userscripts.js"], + "matches": [""], + "run_at": "document_start", + "all_frames": true + } + ], + "permissions": [ + "", + "clipboardWrite", + "contextMenus", + "declarativeNetRequestWithHostAccess", + "menus", + "nativeMessaging", + "scripting", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation" + ], + "browser_specific_settings": { + "safari": { + "strict_min_version": "16.4" + } + }, + "content_security_policy": "script-src 'self' https://localhost:55173 https://userscripts.test:55173" +} diff --git a/public/ext/shared-dev/images/128.svg b/public/ext/shared-dev/images/128.svg new file mode 100644 index 00000000..76189438 --- /dev/null +++ b/public/ext/shared-dev/images/128.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/ext/shared-dev/images/action.svg b/public/ext/shared-dev/images/action.svg new file mode 100644 index 00000000..3789dd89 --- /dev/null +++ b/public/ext/shared-dev/images/action.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/ext/shared/_locales/en/messages.json b/public/ext/shared/_locales/en/messages.json new file mode 100644 index 00000000..c17c6cad --- /dev/null +++ b/public/ext/shared/_locales/en/messages.json @@ -0,0 +1,268 @@ +{ + "extension_name": { + "message": "_NAME_", + "description": "The display name for the extension." + }, + "extension_description": { + "message": "Save and run javascript for the web pages you visit", + "description": "Description of what the extension does." + }, + "settings": { + "message": "Settings" + }, + "settings_section_editor": { + "message": "Editor Settings" + }, + "settings_section_general": { + "message": "General Settings" + }, + "settings_section_native": { + "message": "Native Settings" + }, + "settings_section_tools": { + "message": "Tools" + }, + "settings_section_about": { + "message": "About" + }, + "settings_editor_auto_hint": { + "message": "Auto Hint" + }, + "settings_editor_auto_hint_desc": { + "message": "Automatically shows completion hints while editing" + }, + "settings_editor_close_brackets": { + "message": "Auto Close Brackets" + }, + "settings_editor_close_brackets_desc": { + "message": "Toggles on/off auto closing of brackets in the editor, this affects the following characters: () [] {} \"\" ''" + }, + "settings_editor_javascript_lint": { + "message": "JavaScript Linter" + }, + "settings_editor_javascript_lint_desc": { + "message": "Toggles the basic javascript linting within the editor" + }, + "settings_editor_list_descriptions": { + "message": "Sidebar Item Descriptions" + }, + "settings_editor_list_descriptions_desc": { + "message": "Show or hides the item descriptions in the sidebar" + }, + "settings_editor_list_sort": { + "message": "Sidebar Item Sort Order" + }, + "settings_editor_list_sort_desc": { + "message": "Display order of items in the sidebar" + }, + "settings_editor_list_sort_nameAsc": { + "message": "Scripts Name: Asc" + }, + "settings_editor_list_sort_nameDesc": { + "message": "Scripts Name: Desc" + }, + "settings_editor_list_sort_lastModifiedAsc": { + "message": "Last Modified: Asc" + }, + "settings_editor_list_sort_lastModifiedDesc": { + "message": "Last Modified: Desc" + }, + "settings_editor_show_whitespace": { + "message": "Show Whitespace Characters" + }, + "settings_editor_show_whitespace_desc": { + "message": "Toggles the display of invisible characters in the editor" + }, + "settings_editor_tab_size": { + "message": "Tab Size" + }, + "settings_editor_tab_size_desc": { + "message": "Choose the number of spaces a tab is equal to when rendering code" + }, + "settings_global_active": { + "message": "Enable Injection" + }, + "settings_global_active_desc": { + "message": "Toggle on/off script injection for the pages you visit" + }, + "settings_theme_mode": { + "message": "Theme mode" + }, + "settings_theme_mode_desc": { + "message": "Toggle theme mode" + }, + "settings_theme_mode_auto": { + "message": "Auto (System)" + }, + "settings_theme_mode_dark": { + "message": "Dark" + }, + "settings_theme_mode_light": { + "message": "Light" + }, + "settings_theme_mode_custom": { + "message": "Custom" + }, + "settings_global_exclude_match": { + "message": "Global Exclude Match patterns" + }, + "settings_global_exclude_match_desc": { + "message": "This input accepts a whitespace (spaces, newlines etc.) separated list of @match patterns, a page url that matches against a pattern in this list will be ignored for script injection" + }, + "settings_global_exclude_match_done": { + "message": "Done" + }, + "settings_global_exclude_match_saving": { + "message": "Saving..." + }, + "settings_global_exclude_match_placeholder": { + "message": "list of @match patterns, for example: \n*://*/*foo.bar\n*://*/*foo.bar?*\n*://*.example.net/*\nhttps://example.net/*/foo/*/\nhttps://*.example.net/a/b/c/?foo=/" + }, + "settings_global_exclude_match_refer": { + "message": "Please refer to:" + }, + "settings_scripts_update_automation": { + "message": "Scripts Updates Automatically" + }, + "settings_scripts_update_automation_desc": { + "message": "Script silently auto-updates in the background, which is dangerous and may introduce unconfirmed malicious code" + }, + "settings_scripts_update_check_interval": { + "message": "Scripts Update Check Interval" + }, + "settings_scripts_update_check_interval_desc": { + "message": "Disable or set user scripts update check interval (days)" + }, + "settings_scripts_update_check_interval_0": { + "message": "Never" + }, + "settings_scripts_update_check_lasttime": { + "message": "Scripts Update Check Lasttime" + }, + "settings_scripts_update_check_lasttime_desc": { + "message": "The lasttime for script update check in background" + }, + "settings_settings_sync": { + "message": "Sync Settings" + }, + "settings_settings_sync_desc": { + "message": "Sync settings across devices" + }, + "settings_augmented_userjs_install": { + "message": "Enhanced installation prompts" + }, + "settings_augmented_userjs_install_desc": { + "message": "Automatically pop up the installation interface when opening a user script URL (.user.js), and takes over the install button of the user scripts market" + }, + "settings_toolbar_badge_count": { + "message": "Show Toolbar Count Badge" + }, + "settings_toolbar_badge_count_desc": { + "message": "Displays a badge on the toolbar icon with a number that represents how many enabled scripts match the url for the page you are on" + }, + "settings_scripts_directory": { + "message": "Save Location" + }, + "settings_scripts_directory_desc": { + "message": "Path to the folder where user scripts are stored" + }, + "settings_set_scripts_directory": { + "message": "Change save location" + }, + "settings_section_tools_import": { + "message": "Import" + }, + "settings_section_tools_export": { + "message": "Export" + }, + "settings_section_tools_reset": { + "message": "Reset" + }, + "settings_section_tools_goback": { + "message": "Go Back" + }, + "settings_section_tools_reset_all": { + "message": "Reset All Settings" + }, + "settings_section_tools_reset_single": { + "message": "Reset" + }, + "settings_section_tools_reset_section": { + "message": "Reset Section" + }, + "settings_scripts_tools_desc": { + "message": "These tools can help you import and export \"settings data\". Reset single, section or all settings." + }, + "settings_about_text1": { + "message": "Get more information about this extension by visiting the open source project:" + }, + "settings_about_text2": { + "message": "If you enjoy using this extension, please consider leaving a review on the App Store or sign up to beta test new versions:" + }, + "settings_about_button_repo": { + "message": "Code repository" + }, + "settings_about_button_docs": { + "message": "Documentation" + }, + "settings_about_button_issues": { + "message": "Report bugs" + }, + "settings_about_button_store": { + "message": "Open in the App Store" + }, + "settings_about_button_beta": { + "message": "Sign up for beta testing" + }, + "utils_check_match_patterns_0": { + "message": "The match pattern will match all URLs" + }, + "utils_check_match_patterns_1": { + "message": "The scheme component should one of *, https, http" + }, + "utils_check_match_patterns_2": { + "message": "The scheme and host should separated by `://`" + }, + "utils_check_match_patterns_3": { + "message": "The match pattern has no path component" + }, + "utils_check_match_patterns_4": { + "message": "The `*.` should followed by part of the hostname" + }, + "utils_check_match_patterns_5": { + "message": "The host component length should be 1-255" + }, + "utils_check_match_patterns_6": { + "message": "The `*` in host component should be independent or `*.` at the start" + }, + "utils_check_match_patterns_7": { + "message": "The host component contains empty label(s)" + }, + "utils_check_match_patterns_8": { + "message": "The hostname label cannot start or end with `-` character" + }, + "utils_check_match_patterns_9": { + "message": "The maximum length of the hostname label cannot exceed 63" + }, + "utils_check_match_patterns_10": { + "message": "The host component contains invalid character(s): $1" + }, + "utils_check_match_patterns_11": { + "message": "The path component contains invalid character(s): $1" + }, + "msg_invalid_match_pattern": { + "message": "Invalid match pattern" + }, + "msg_invalid_backup_file": { + "message": "Not a valid backup file" + }, + "msg_backup_import_finish": { + "message": "Import completed! ✅" + }, + "msg_backup_import_failed": { + "message": "Import failed, please check the error log in the console" + }, + "msg_settings_reset_finish": { + "message": "Reset all settings completed! ✅" + } +} diff --git a/public/ext/shared/_locales/zh/messages.json b/public/ext/shared/_locales/zh/messages.json new file mode 100644 index 00000000..7338f30a --- /dev/null +++ b/public/ext/shared/_locales/zh/messages.json @@ -0,0 +1,246 @@ +{ + "extension_description": { + "message": "用户脚本和样式管理器", + "description": "Description of what the extension does." + }, + "settings": { + "message": "设置" + }, + "settings_section_editor": { + "message": "编辑器设置" + }, + "settings_section_general": { + "message": "通用设置" + }, + "settings_section_native": { + "message": "本地设置" + }, + "settings_section_tools": { + "message": "工具" + }, + "settings_section_about": { + "message": "关于" + }, + "settings_editor_auto_hint": { + "message": "自动提示(Hint)" + }, + "settings_editor_auto_hint_desc": { + "message": "编辑时自动显示完成提示" + }, + "settings_editor_close_brackets": { + "message": "自动关闭括号" + }, + "settings_editor_close_brackets_desc": { + "message": "在编辑器中启用自动关闭括号,这会影响以下字符:() [] {} \"\" ''" + }, + "settings_editor_javascript_lint": { + "message": "JavaScript Linter" + }, + "settings_editor_javascript_lint_desc": { + "message": "在编辑器中启用基本的 JavaScript Linting(代码检查)" + }, + "settings_editor_list_descriptions": { + "message": "侧栏项目描述" + }, + "settings_editor_list_descriptions_desc": { + "message": "显示或隐藏侧边栏中项目的描述" + }, + "settings_editor_list_sort": { + "message": "侧栏项目排序" + }, + "settings_editor_list_sort_desc": { + "message": "侧边栏中项目的显示顺序" + }, + "settings_editor_list_sort_nameAsc": { + "message": "项目名称: 升序" + }, + "settings_editor_list_sort_nameDesc": { + "message": "项目名称: 降序" + }, + "settings_editor_list_sort_lastModifiedAsc": { + "message": "最后修改: 升序" + }, + "settings_editor_list_sort_lastModifiedDesc": { + "message": "最后修改: 降序" + }, + "settings_editor_show_whitespace": { + "message": "显示空白字符" + }, + "settings_editor_show_whitespace_desc": { + "message": "切换编辑器中不可见字符的显示" + }, + "settings_editor_tab_size": { + "message": "制表符大小" + }, + "settings_editor_tab_size_desc": { + "message": "选择渲染代码时制表符等于的空格数" + }, + "settings_global_active": { + "message": "启用注入" + }, + "settings_global_active_desc": { + "message": "全局脚本注入的开启或关闭" + }, + "settings_global_exclude_match": { + "message": "全局排除匹配模式列表" + }, + "settings_global_exclude_match_desc": { + "message": "此输入接受以空白符(空格、换行等)分隔的 @match 模式列表,与此列表中的模式匹配的页面 URL 将在脚本注入时被忽略" + }, + "settings_global_exclude_match_done": { + "message": "完成" + }, + "settings_global_exclude_match_saving": { + "message": "保存中..." + }, + "settings_global_exclude_match_placeholder": { + "message": "@match 模式列表,例如:\n*://*/*foo.bar\n*://*/*foo.bar?*\n*://*.example.net/*\nhttps://example.net/*/foo/*/\nhttps://*.example.net/a/b/c/?foo=/" + }, + "settings_global_exclude_match_refer": { + "message": "匹配模式结构请参考:" + }, + "settings_scripts_update_automation": { + "message": "自动更新脚本" + }, + "settings_scripts_update_automation_desc": { + "message": "脚本在后台静默自动更新,这是危险的,可能引入未经确认的恶意代码" + }, + "settings_scripts_update_check_interval": { + "message": "脚本更新检查间隔" + }, + "settings_scripts_update_check_interval_desc": { + "message": "禁用或设置用户脚本更新检查间隔(天)" + }, + "settings_scripts_update_check_interval_0": { + "message": "从不" + }, + "settings_scripts_update_check_lasttime": { + "message": "脚本更新上次检查时间" + }, + "settings_scripts_update_check_lasttime_desc": { + "message": "后台脚本更新检查上次时间" + }, + "settings_settings_sync": { + "message": "同步设置" + }, + "settings_settings_sync_desc": { + "message": "跨设备同步设置" + }, + "settings_augmented_userjs_install": { + "message": "增强的脚本安装提示" + }, + "settings_augmented_userjs_install_desc": { + "message": "当打开一个用户脚本 URL(.user.js)时自动弹出安装界面,并接管用户脚本市场的安装按钮" + }, + "settings_toolbar_badge_count": { + "message": "工具栏图标显示计数徽章" + }, + "settings_toolbar_badge_count_desc": { + "message": "在工具栏图标上显示一个徽章,其中的数字代表有多少个已启用的脚本与您所在页面的 URL 匹配" + }, + "settings_scripts_directory": { + "message": "保存位置" + }, + "settings_scripts_directory_desc": { + "message": "存储用户脚本的文件夹路径" + }, + "settings_set_scripts_directory": { + "message": "更改保存位置" + }, + "settings_section_tools_import": { + "message": "导入" + }, + "settings_section_tools_export": { + "message": "导出" + }, + "settings_section_tools_reset": { + "message": "还原" + }, + "settings_section_tools_goback": { + "message": "返回" + }, + "settings_section_tools_reset_all": { + "message": "还原所有设置" + }, + "settings_section_tools_reset_single": { + "message": "还原" + }, + "settings_section_tools_reset_section": { + "message": "还原该组" + }, + "settings_scripts_tools_desc": { + "message": "这些工具可以帮助您导入和导出“设置数据”。重置单个、一组或所有设置。" + }, + "settings_about_text1": { + "message": "获取有关此扩展的更多信息,请访问本开源项目:" + }, + "settings_about_text2": { + "message": "如果您喜欢使用此扩展,请考虑在 App Store 上留下您的评论或注册 Beta 测试新版本:" + }, + "settings_about_button_repo": { + "message": "代码库" + }, + "settings_about_button_docs": { + "message": "文档" + }, + "settings_about_button_issues": { + "message": "报告错误" + }, + "settings_about_button_store": { + "message": "在 App Store 中打开" + }, + "settings_about_button_beta": { + "message": "注册 Beta 测试版" + }, + "utils_check_match_patterns_0": { + "message": "此匹配模式将匹配所有 URL" + }, + "utils_check_match_patterns_1": { + "message": "这 scheme 部分应当为 *、https、http 之一" + }, + "utils_check_match_patterns_2": { + "message": "这 scheme 和 host 部分应当用 `://` 分隔" + }, + "utils_check_match_patterns_3": { + "message": "匹配模式缺少 path 部分(至少应当有`/`)" + }, + "utils_check_match_patterns_4": { + "message": "这 `*.` 后面应当跟随主机名的一部分" + }, + "utils_check_match_patterns_5": { + "message": "这 host 部分长度应当为 1-255" + }, + "utils_check_match_patterns_6": { + "message": "这 host 部分中的 `*` 应该是独立的或为 `*.` 在开头" + }, + "utils_check_match_patterns_7": { + "message": "这 host 部分包含一个或多个空标签" + }, + "utils_check_match_patterns_8": { + "message": "主机名标签不能以 `-` 字符开头或结尾" + }, + "utils_check_match_patterns_9": { + "message": "主机名标签最大长度不能超过 63" + }, + "utils_check_match_patterns_10": { + "message": "这 host 部分包含无效字符:$1" + }, + "utils_check_match_patterns_11": { + "message": "这 path 部分包含无效字符:$1" + }, + "msg_invalid_match_pattern": { + "message": "无效的匹配模式" + }, + "msg_invalid_backup_file": { + "message": "无效的备份文件" + }, + "msg_backup_import_finish": { + "message": "导入完成!✅" + }, + "msg_backup_import_failed": { + "message": "导入失败,请在控制台中检查错误日志" + }, + "msg_settings_reset_finish": { + "message": "还原所有设置完成!✅" + } +} diff --git a/public/ext/shared/_locales/zh_HK b/public/ext/shared/_locales/zh_HK new file mode 120000 index 00000000..dec9132e --- /dev/null +++ b/public/ext/shared/_locales/zh_HK @@ -0,0 +1 @@ +zh_TW \ No newline at end of file diff --git a/public/ext/shared/_locales/zh_MO b/public/ext/shared/_locales/zh_MO new file mode 120000 index 00000000..dec9132e --- /dev/null +++ b/public/ext/shared/_locales/zh_MO @@ -0,0 +1 @@ +zh_TW \ No newline at end of file diff --git a/public/ext/shared/_locales/zh_TW/messages.json b/public/ext/shared/_locales/zh_TW/messages.json new file mode 100644 index 00000000..07853cb3 --- /dev/null +++ b/public/ext/shared/_locales/zh_TW/messages.json @@ -0,0 +1,246 @@ +{ + "extension_description": { + "message": "使用者腳本和樣式管理器", + "description": "Description of what the extension does." + }, + "settings": { + "message": "設定" + }, + "settings_section_editor": { + "message": "編輯器設定" + }, + "settings_section_general": { + "message": "一般設定" + }, + "settings_section_native": { + "message": "本地設定" + }, + "settings_section_tools": { + "message": "工具" + }, + "settings_section_about": { + "message": "關於" + }, + "settings_editor_auto_hint": { + "message": "自動提示(Hint)" + }, + "settings_editor_auto_hint_desc": { + "message": "編輯時自動顯示完成提示" + }, + "settings_editor_close_brackets": { + "message": "自動關閉括號" + }, + "settings_editor_close_brackets_desc": { + "message": "在編輯器中啟用自動關閉括號,這會影響以下字元:() [] {} \"\" ''" + }, + "settings_editor_javascript_lint": { + "message": "JavaScript Linter" + }, + "settings_editor_javascript_lint_desc": { + "message": "在編輯器中啟用基本的 JavaScript Linting(程式碼檢查)" + }, + "settings_editor_list_descriptions": { + "message": "側欄項目描述" + }, + "settings_editor_list_descriptions_desc": { + "message": "顯示或隱藏側邊欄中項目的描述" + }, + "settings_editor_list_sort": { + "message": "側欄項目排序" + }, + "settings_editor_list_sort_desc": { + "message": "側邊欄中項目的顯示順序" + }, + "settings_editor_list_sort_nameAsc": { + "message": "項目名稱: 升序" + }, + "settings_editor_list_sort_nameDesc": { + "message": "項目名稱: 降序" + }, + "settings_editor_list_sort_lastModifiedAsc": { + "message": "最後修改: 升序" + }, + "settings_editor_list_sort_lastModifiedDesc": { + "message": "最後修改: 降序" + }, + "settings_editor_show_whitespace": { + "message": "顯示空白字元" + }, + "settings_editor_show_whitespace_desc": { + "message": "切換編輯器中不可見字元的顯示" + }, + "settings_editor_tab_size": { + "message": "製表符大小" + }, + "settings_editor_tab_size_desc": { + "message": "選擇渲染程式碼時製表符等於的空格數" + }, + "settings_global_active": { + "message": "啟用注入" + }, + "settings_global_active_desc": { + "message": "全域性腳本注入的開啟或關閉" + }, + "settings_global_exclude_match": { + "message": "全域性排除匹配模式列表" + }, + "settings_global_exclude_match_desc": { + "message": "此輸入接受以空白符(空格、換行等)分隔的 @match 模式列表,與此列表中的模式匹配的頁面 URL 將在腳本注入時被忽略" + }, + "settings_global_exclude_match_done": { + "message": "完成" + }, + "settings_global_exclude_match_saving": { + "message": "儲存中..." + }, + "settings_global_exclude_match_placeholder": { + "message": "@match 模式列表,例如:\n*://*/*foo.bar\n*://*/*foo.bar?*\n*://*.example.net/*\nhttps://example.net/*/foo/*/\nhttps://*.example.net/a/b/c/?foo=/" + }, + "settings_global_exclude_match_refer": { + "message": "匹配模式結構請參考:" + }, + "settings_scripts_update_automation": { + "message": "自動更新腳本" + }, + "settings_scripts_update_automation_desc": { + "message": "腳本在後臺靜默自動更新,這是危險的,可能引入未經確認的惡意程式碼" + }, + "settings_scripts_update_check_interval": { + "message": "腳本更新檢查間隔" + }, + "settings_scripts_update_check_interval_desc": { + "message": "禁用或設定使用者腳本更新檢查間隔(天)" + }, + "settings_scripts_update_check_interval_0": { + "message": "從不" + }, + "settings_scripts_update_check_lasttime": { + "message": "腳本更新上次檢查時間" + }, + "settings_scripts_update_check_lasttime_desc": { + "message": "後臺腳本更新檢查上次時間" + }, + "settings_settings_sync": { + "message": "同步設定" + }, + "settings_settings_sync_desc": { + "message": "跨裝置同步設定" + }, + "settings_augmented_userjs_install": { + "message": "增強的腳本安裝提示" + }, + "settings_augmented_userjs_install_desc": { + "message": "當開啟一個使用者腳本 URL(.user.js)時自動彈出安裝介面,並接管使用者腳本市場的安裝按鈕" + }, + "settings_toolbar_badge_count": { + "message": "工具欄圖示顯示計數徽章" + }, + "settings_toolbar_badge_count_desc": { + "message": "在工具欄圖示上顯示一個徽章,其中的數字代表有多少個已啟用的腳本與您所在頁面的 URL 匹配" + }, + "settings_scripts_directory": { + "message": "儲存位置" + }, + "settings_scripts_directory_desc": { + "message": "儲存使用者腳本的資料夾路徑" + }, + "settings_set_scripts_directory": { + "message": "更改儲存位置" + }, + "settings_section_tools_import": { + "message": "匯入" + }, + "settings_section_tools_export": { + "message": "匯出" + }, + "settings_section_tools_reset": { + "message": "還原" + }, + "settings_section_tools_goback": { + "message": "返回" + }, + "settings_section_tools_reset_all": { + "message": "還原所有設定" + }, + "settings_section_tools_reset_single": { + "message": "還原" + }, + "settings_section_tools_reset_section": { + "message": "還原該組" + }, + "settings_scripts_tools_desc": { + "message": "這些工具可以幫助您匯入和匯出“設定資料”。重置單個、一組或所有設定。" + }, + "settings_about_text1": { + "message": "獲取有關此延伸功能的更多資訊,請訪問本開源項目:" + }, + "settings_about_text2": { + "message": "如果您喜歡使用此延伸功能,請考慮在 App Store 上留下您的評論或註冊 Beta 測試新版本:" + }, + "settings_about_button_repo": { + "message": "代碼庫" + }, + "settings_about_button_docs": { + "message": "文件" + }, + "settings_about_button_issues": { + "message": "報告錯誤" + }, + "settings_about_button_store": { + "message": "在 App Store 中開啟" + }, + "settings_about_button_beta": { + "message": "註冊 Beta 測試版" + }, + "utils_check_match_patterns_0": { + "message": "此匹配模式將匹配所有 URL" + }, + "utils_check_match_patterns_1": { + "message": "這 scheme 部分應當為 *、https、http 之一" + }, + "utils_check_match_patterns_2": { + "message": "這 scheme 和 host 部分應當用 `://` 分隔" + }, + "utils_check_match_patterns_3": { + "message": "匹配模式缺少 path 部分(至少應當有`/`)" + }, + "utils_check_match_patterns_4": { + "message": "這 `*.` 後面應當跟隨主機名的一部分" + }, + "utils_check_match_patterns_5": { + "message": "這 host 部分長度應當為 1-255" + }, + "utils_check_match_patterns_6": { + "message": "這 host 部分中的 `*` 應該是獨立的或為 `*.` 在開頭" + }, + "utils_check_match_patterns_7": { + "message": "這 host 部分包含一個或多個空標籤" + }, + "utils_check_match_patterns_8": { + "message": "主機名標籤不能以 `-` 字元開頭或結尾" + }, + "utils_check_match_patterns_9": { + "message": "主機名標籤最大長度不能超過 63" + }, + "utils_check_match_patterns_10": { + "message": "這 host 部分包含無效字元:$1" + }, + "utils_check_match_patterns_11": { + "message": "這 path 部分包含無效字元:$1" + }, + "msg_invalid_match_pattern": { + "message": "無效的匹配模式" + }, + "msg_invalid_backup_file": { + "message": "無效的備份檔案" + }, + "msg_backup_import_finish": { + "message": "匯入完成!✅" + }, + "msg_backup_import_failed": { + "message": "匯入失敗,請在控制檯中檢查錯誤日誌" + }, + "msg_settings_reset_finish": { + "message": "還原所有設定完成!✅" + } +} diff --git a/public/extension-page/jshint.min.js b/public/ext/vendor/jshint.min.js similarity index 100% rename from public/extension-page/jshint.min.js rename to public/ext/vendor/jshint.min.js diff --git a/scripts/build-app.js b/scripts/build-app.js deleted file mode 100644 index 4563de35..00000000 --- a/scripts/build-app.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file Build App WebView resources using the Vite JavaScript API - * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} - * - * All build processes start at the same time due to asynchronous calls - * The assets name is irrelevant, just need to determine the entry path - */ - -import {build} from "vite"; -import {svelte} from "@sveltejs/vite-plugin-svelte"; - -/** - * Define default vite config options - * Disable auto resolving {@link vite.config.js} - * @see {@link https://vitejs.dev/config/ Config} - * @see {@link https://vitejs.dev/guide/api-javascript.html#inlineconfig configFile} - */ -const defineConfig = { - base: "./", - configFile: false -}; - -/** - * Build App-Shared WebView resources to xcode dist - */ -build({ - ...defineConfig, - plugins: [svelte()], - build: { - outDir: "xcode/App-Shared/Resources/dist/", - copyPublicDir: false, - rollupOptions: { - input: "entry-app-webview.html" - } - } -}); diff --git a/scripts/build-ext-demo.js b/scripts/build-ext-demo.js new file mode 100644 index 00000000..e614fff8 --- /dev/null +++ b/scripts/build-ext-demo.js @@ -0,0 +1,42 @@ +/** + * @file Build extension pages demonstration using the Vite JavaScript API + * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} + */ + +import { build } from "vite"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import * as Utils from "./utils.js"; + +/** @type {import("vite").InlineConfig} */ +const sharedConfig = { + ...Utils.baseConfig, + mode: "development", + + define: { + ...Utils.baseConfig.define, + "import.meta.env.SAFARI_VERSION": JSON.stringify(15), + "import.meta.env.EXT_DEMO_BUILD": JSON.stringify(true), + }, +}; + +/** + * Empty resources directory + * Copy public static assets + */ +await Utils.emptyBuildDir("dist"); +Utils.cp("public/ext/shared", "dist"); + +/** Build shared modules */ +build({ + ...sharedConfig, + plugins: [svelte()], + publicDir: "public/ext/vendor/", + build: { + outDir: `dist/`, + emptyOutDir: false, + rollupOptions: { + input: ["entry-ext-action-popup.html", "entry-ext-extension-page.html"], + }, + target: "esnext", // top-level await + }, +}); diff --git a/scripts/build-ext-v4.js b/scripts/build-ext-v4.js deleted file mode 100644 index fb6379db..00000000 --- a/scripts/build-ext-v4.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @file Build Safari extension resources using the Vite JavaScript API - * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} - * - * Safari supports for modules in background since 16.4 - * @see {@link https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes#Safari-Extensions} - * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background#browser_compatibility} - * To ensure forward compatibility, background script use independent builds in v4 - * - * Content scripts not support import modules, and due to their privileges and the - * speed of injecting user scripts, use a independent build currently - * - * All build processes start at the same time due to asynchronous calls - * The assets name is irrelevant, just need to determine the entry path - */ - -import {build} from "vite"; -import {svelte} from "@sveltejs/vite-plugin-svelte"; - -/** - * Define default vite config options - * Disable auto resolving {@link vite.config.js} - * @see {@link https://vitejs.dev/config/ Config} - * @see {@link https://vitejs.dev/guide/api-javascript.html#inlineconfig configFile} - */ -const defineConfig = { - base: "./", - configFile: false -}; - -/** - * Build shared modules for safari - * These multiple entry files will share the same modules - * Using a subdirectory avoid emptying other built files - */ -build({ - ...defineConfig, - plugins: [svelte()], - build: { - outDir: "xcode/Ext-Safari/Resources/dist/s/", - rollupOptions: { - input: [ - "entry-ext-action-popup.html", - "entry-ext-extension-page.html" - ] - } - } -}); - -/** - * Build independent scripts for safari - * Each entry in the array will generate a separate script - */ -[ - {background: "src/ext/background/main.js"}, - {content: "src/ext/content-scripts/main.js"} -].forEach(input => { - build({ - ...defineConfig, - build: { - outDir: "xcode/Ext-Safari/Resources/dist/", - emptyOutDir: false, - copyPublicDir: false, - rollupOptions: { - input, - output: { - entryFileNames: "[name].js" - } - } - } - }); -}); diff --git a/scripts/build-ext-v5.js b/scripts/build-ext-v5.js deleted file mode 100644 index 6609f418..00000000 --- a/scripts/build-ext-v5.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @file Build Safari extension resources using the Vite JavaScript API - * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} - * - * Safari supports for modules in background since 16.4 - * @see {@link https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes#Safari-Extensions} - * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background#browser_compatibility} - * - * Content scripts not support import modules, and due to their privileges and the - * speed of injecting user scripts, use a independent build currently - * - * All build processes start at the same time due to asynchronous calls - * The assets name is irrelevant, just need to determine the entry path - */ - -import {build} from "vite"; -import {svelte} from "@sveltejs/vite-plugin-svelte"; - -/** - * Define default vite config options - * Disable auto resolving {@link vite.config.js} - * @see {@link https://vitejs.dev/config/ Config} - * @see {@link https://vitejs.dev/guide/api-javascript.html#inlineconfig configFile} - */ -const defineConfig = { - base: "./", - configFile: false -}; - -/** - * Build shared modules for safari - * These multiple entry files will share the same modules - * Using a subdirectory avoid emptying other built files - */ -build({ - ...defineConfig, - plugins: [svelte()], - build: { - outDir: "xcode/Ext-Safari/Resources/dist/s/", - rollupOptions: { - input: { - background: "src/ext/background/main.js", - "action-popup": "entry-ext-action-popup.html", - "extension-page": "entry-ext-extension-page.html" - }, - output: { - entryFileNames: "[name].js" - } - } - } -}); - -/** - * Build independent scripts for safari - * Each entry in the array will generate a separate script - */ -[ - {content: "src/ext/content-scripts/main.js"} -].forEach(input => { - build({ - ...defineConfig, - build: { - outDir: "xcode/Ext-Safari/Resources/dist/", - emptyOutDir: false, - copyPublicDir: false, - rollupOptions: { - input, - output: { - entryFileNames: "[name].js" - } - } - } - }); -}); diff --git a/scripts/build-safari-15.js b/scripts/build-safari-15.js new file mode 100644 index 00000000..1466bf15 --- /dev/null +++ b/scripts/build-safari-15.js @@ -0,0 +1,108 @@ +/** + * @file Build App-WebView and Safari-Extension resources using the Vite JavaScript API + * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} + * + * Safari supports for modules in background since 16.4 + * @see {@link https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes#Safari-Extensions} + * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background#browser_compatibility} + * To ensure forward compatibility, background script use independent builds for safari 15 + * + * Content scripts not support import modules, and due to their privileges and the + * speed of injecting user scripts, use a independent build currently + * + * All build processes start at the same time due to asynchronous calls + * The assets name is irrelevant, just need to determine the entry path + */ + +import { build } from "vite"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import pluginMarkedDivest from "./vite-plugin-marked-divest.js"; +import * as Utils from "./utils.js"; + +/** @type {import("vite").InlineConfig} */ +const sharedConfig = { + ...Utils.baseConfig, + define: { + ...Utils.baseConfig.define, + "import.meta.env.SAFARI_VERSION": JSON.stringify(15), + }, + build: { + sourcemap: process.env.BETA ? true : false, + target: "safari15", + }, +}; + +/** + * Build App-Shared WebView resources to xcode dist + */ +build({ + ...Utils.baseConfig, + plugins: [svelte(), pluginMarkedDivest()], + build: { + ...sharedConfig.build, + outDir: `${Utils.APP_SHARED_RESOURCES}/dist/`, + copyPublicDir: false, + rollupOptions: { + input: "entry-app-webview.html", + }, + }, +}); + +/** + * Empty resources directory + * Copy public static assets + */ +await Utils.emptyBuildDir(Utils.EXT_SAFARI_RESOURCES); +Utils.cp("public/ext/shared", Utils.EXT_SAFARI_RESOURCES); +Utils.cp("public/ext/safari-15", Utils.EXT_SAFARI_RESOURCES); + +/** Build content scripts */ +[ + { userscripts: "src/ext/content-scripts/entry-userscripts.js" }, + { "dot-user-js": "src/ext/content-scripts/entry-dot-user-js.js" }, + { "script-market": "src/ext/content-scripts/entry-script-market.js" }, +].forEach((input) => { + build({ + ...sharedConfig, + build: { + ...sharedConfig.build, + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/content-scripts/`, + emptyOutDir: false, + copyPublicDir: false, + rollupOptions: { + input, + output: { entryFileNames: "[name].js" }, + }, + }, + }); +}); + +/** Build background scripts */ +build({ + ...sharedConfig, + build: { + ...sharedConfig.build, + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/`, + emptyOutDir: false, + copyPublicDir: false, + rollupOptions: { + input: { background: "src/ext/background/main.js" }, + output: { entryFileNames: "[name].js" }, + }, + }, +}); + +/** Build shared modules */ +build({ + ...sharedConfig, + plugins: [svelte()], + publicDir: "public/ext/vendor/", + build: { + ...sharedConfig.build, + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/`, + emptyOutDir: false, + rollupOptions: { + input: ["entry-ext-action-popup.html", "entry-ext-extension-page.html"], + }, + }, +}); diff --git a/scripts/build-safari-16.4.js b/scripts/build-safari-16.4.js new file mode 100644 index 00000000..866b844d --- /dev/null +++ b/scripts/build-safari-16.4.js @@ -0,0 +1,116 @@ +/** + * @file Build App-WebView and Safari-Extension resources using the Vite JavaScript API + * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} + * + * Safari supports for modules in background since 16.4 + * @see {@link https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes#Safari-Extensions} + * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background#browser_compatibility} + * + * Content scripts not support import modules, and due to their privileges and the + * speed of injecting user scripts, use a independent build currently + * + * All build processes start at the same time due to asynchronous calls + * The assets name is irrelevant, just need to determine the entry path + */ + +import { build } from "vite"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import pluginMarkedDivest from "./vite-plugin-marked-divest.js"; +import * as Utils from "./utils.js"; + +/** @type {import("vite").InlineConfig} */ +const sharedConfig = { + ...Utils.baseConfig, + define: { + ...Utils.baseConfig.define, + "import.meta.env.SAFARI_VERSION": JSON.stringify(16.4), + }, + build: { + sourcemap: process.env.BETA ? true : false, + target: "safari16.4", + }, +}; + +/** + * Build App-Shared WebView resources to xcode dist + */ +build({ + ...Utils.baseConfig, + plugins: [svelte(), pluginMarkedDivest()], + build: { + ...sharedConfig.build, + outDir: `${Utils.APP_SHARED_RESOURCES}/dist/`, + copyPublicDir: false, + rollupOptions: { + input: "entry-app-webview.html", + }, + }, +}); + +/** + * Empty resources directory + * Copy public static assets + */ +await Utils.emptyBuildDir(Utils.EXT_SAFARI_RESOURCES); +Utils.cp("public/ext/shared", Utils.EXT_SAFARI_RESOURCES); +Utils.cp("public/ext/safari-16.4", Utils.EXT_SAFARI_RESOURCES); + +/** Build content scripts */ +[ + { userscripts: "src/ext/content-scripts/entry-userscripts.js" }, + { "dot-user-js": "src/ext/content-scripts/entry-dot-user-js.js" }, + { "script-market": "src/ext/content-scripts/entry-script-market.js" }, +].forEach((input) => { + build({ + ...sharedConfig, + build: { + ...sharedConfig.build, + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/content-scripts/`, + emptyOutDir: false, + copyPublicDir: false, + rollupOptions: { + input, + output: { entryFileNames: "[name].js" }, + }, + }, + }); +}); + +/** + * Build background scripts + * Modular background may not load correctly on Safari startup + * Currently build classic script separately to avoid this error + */ +build({ + ...sharedConfig, + build: { + ...sharedConfig.build, + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/`, + emptyOutDir: false, + copyPublicDir: false, + rollupOptions: { + input: { background: "src/ext/background/main.js" }, + output: { entryFileNames: "[name].js" }, + }, + }, +}); + +/** Build shared modules */ +build({ + ...sharedConfig, + plugins: [svelte()], + publicDir: "public/ext/vendor/", + build: { + ...sharedConfig.build, + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/`, + emptyOutDir: false, + rollupOptions: { + input: { + // background: "src/ext/background/main.js", + "action-popup": "entry-ext-action-popup.html", + "extension-page": "entry-ext-extension-page.html", + }, + output: { entryFileNames: "[name].js" }, + }, + }, +}); diff --git a/scripts/dev-safari.js b/scripts/dev-safari.js new file mode 100644 index 00000000..c9236b9f --- /dev/null +++ b/scripts/dev-safari.js @@ -0,0 +1,211 @@ +/** + * @file Develop Safari extension resources using the Vite JavaScript API + * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} + * This development build and server requires a valid https certificate to support remote real-time development + * Typically using a self-signed certificate, it needs to be installed and trusted by the device or simulator + * @see {@link https://developer.apple.com/library/archive/qa/qa1948/} + * @see {@link https://developer.apple.com/library/archive/technotes/tn2326/} + */ + +import { build, createServer } from "vite"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import * as Utils from "./utils.js"; +import https from "node:https"; + +/** @type {import("vite").InlineConfig} */ +const sharedConfig = { + ...Utils.baseConfig, + mode: "development", + define: { + ...Utils.baseConfig.define, + "import.meta.env.SAFARI_VERSION": JSON.stringify(16.4), + }, +}; + +/** + * Build resources for remote real-time development + * @param {import("vite").ViteDevServer} server + * @param {string} origin + */ +async function buildResources(server, origin) { + /** + * empty resources directory + * copy public static assets + */ + await Utils.emptyBuildDir("dist"); + await Utils.emptyBuildDir(Utils.EXT_SAFARI_RESOURCES); + Utils.cp("public/ext/shared", Utils.EXT_SAFARI_RESOURCES); + Utils.cp("public/ext/shared-dev", Utils.EXT_SAFARI_RESOURCES); + if (process.env.SAFARI_PLATFORM === "ios") { + Utils.cp( + "public/ext/safari-dev/manifest-ios.json", + `${Utils.EXT_SAFARI_RESOURCES}/manifest.json`, + ); + } else { + Utils.cp( + "public/ext/safari-dev/manifest-mac.json", + `${Utils.EXT_SAFARI_RESOURCES}/manifest.json`, + ); + } + + /** build content scripts */ + [ + { userscripts: "src/ext/content-scripts/entry-userscripts.js" }, + { "dot-user-js": "src/ext/content-scripts/entry-dot-user-js.js" }, + { "script-market": "src/ext/content-scripts/entry-script-market.js" }, + ].forEach((input) => { + /** build proxy content scripts replace actual code */ + build({ + ...sharedConfig, + plugins: [ + { + name: "generate-content-proxy", + load(id) { + const name = id.replace(/.+entry-/, ""); + const url = `${origin}/dist/content-scripts/${name}`; + return `// proxy content + (function () { + if (window["${id}"]) return; + window["${id}"] = 1; + const xhr = new XMLHttpRequest(); + xhr.open("GET", "${url}", false); + xhr.send(); + const code = xhr.responseText; + try { + Function(code + "//# sourceURL=proxy-${name}")(); + } catch (error) { + console.error(error); + } + })();`; + }, + }, + ], + build: { + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/content-scripts/`, + emptyOutDir: false, + copyPublicDir: false, + rollupOptions: { + input, + output: { entryFileNames: "[name].js" }, + }, + }, + }); + /** build content scripts for dev server and watch changes */ + build({ + ...sharedConfig, + build: { + outDir: `dist/content-scripts/`, + emptyOutDir: false, + copyPublicDir: false, + rollupOptions: { + input, + output: { entryFileNames: "[name].js" }, + }, + watch: {}, + minify: false, + }, + }); + }); + + /** generate entrance dist */ + build({ + ...sharedConfig, + publicDir: "public/ext/vendor/", + plugins: [ + /** + * @see {@link https://github.com/vitejs/vite/issues/14263} + * Dev only requires entrances, so order `pre` without transform modules + */ + { + name: "generate-dev-entrance", + transformIndexHtml: { + order: "pre", + async handler(html, ctx) { + const str = await server.transformIndexHtml(ctx.path, html); + return str.replaceAll(`src="/`, `src="${origin}/`); + }, + }, + }, + ], + build: { + outDir: `${Utils.EXT_SAFARI_RESOURCES}/dist/`, + emptyOutDir: false, + rollupOptions: { + input: { + background: "entry-ext-background.html", + "action-popup": "entry-ext-action-popup.html", + "extension-page": "entry-ext-extension-page.html", + }, + }, + }, + }); +} + +/** + * Define shared constants + * Developing in native machine and simulator can just use `https://localhost:port/` as the origin + * But using a fixed domain name on non-native devices such as real iOS devices is a better choice + * The local domain name needs to be resolved to the LAN IP address on LAN DNS or the target device + * When the origin specified here is unreachable, localhost will be automatically used as the alternate + * The origin requires as an exemption to be added to the `manifest.json` due to default CSP limitations + * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_Security_Policy} + * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_security_policy} + */ +const serverOptions = await Utils.sharedServerOptions(); +const SERVER_PORT = 55173; +const SERVER_ORIGIN = `https://userscripts.test:${SERVER_PORT}`; + +/** + * Check if the server is reachable with self-signed certificate + * @param {string | URL} url + * @returns {Promise} + */ +async function serverCheck(url) { + const options = { + method: "HEAD", + ca: serverOptions.ca, + headers: { Accept: "*/*" }, + }; + return new Promise((resolve) => { + const req = https.request(url, options, (res) => { + if (res.headers.server === SERVER_ORIGIN) { + resolve(true); + } + resolve(false); + }); + req.on("error", (e) => { + console.error(`${req.host}: ${e.message}`); + resolve(false); + }); + req.end(); + }); +} + +/** main process */ +(async () => { + /** run development server */ + const server = await createServer({ + ...sharedConfig, + publicDir: "public/ext/vendor/", + plugins: [svelte()], + server: { + host: true, + port: SERVER_PORT, + strictPort: true, + https: { key: serverOptions.key, cert: serverOptions.cert }, + headers: { server: SERVER_ORIGIN }, + }, + }); + await server.listen(); + server.printUrls(); + + // Check available origins and build resources + for (let url of [SERVER_ORIGIN, ...server.resolvedUrls.local]) { + if (await serverCheck(url)) { + const origin = url.at(-1) === "/" ? url.slice(0, -1) : url; + console.info(`build with origin: ${origin}`); + buildResources(server, origin); + break; + } + } +})(); diff --git a/scripts/jsconfig.json b/scripts/jsconfig.json new file mode 100644 index 00000000..f0633242 --- /dev/null +++ b/scripts/jsconfig.json @@ -0,0 +1,26 @@ +// https://code.visualstudio.com/docs/languages/jsconfig +// https://www.typescriptlang.org/docs/handbook/tsconfig-json.html +// https://www.typescriptlang.org/tsconfig + +// https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html +// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html + +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "moduleResolution": "nodenext", + + "verbatimModuleSyntax": true, + "isolatedModules": true, + "resolveJsonModule": true, + + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "sourceMap": true + }, + "include": ["**/*.d.ts", "**/*.js"] +} diff --git a/scripts/preview-app.js b/scripts/preview-app.js index 5784b2bf..1c1ad4eb 100644 --- a/scripts/preview-app.js +++ b/scripts/preview-app.js @@ -3,32 +3,22 @@ * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} */ -import {preview} from "vite"; - -/** - * Define default vite config options - * Disable auto resolving {@link vite.config.js} - * @see {@link https://vitejs.dev/config/ Config} - * @see {@link https://vitejs.dev/guide/api-javascript.html#inlineconfig configFile} - */ -const defineConfig = { - base: "./", - configFile: false -}; +import { preview } from "vite"; +import { baseConfig } from "./utils.js"; /** * Preview App-Shared WebView resources from xcode dist */ (async () => { - const previewServer = await preview({ - ...defineConfig, - preview: { - // port: 4173, - open: "entry-app-webview.html" - }, - build: { - outDir: "xcode/App-Shared/Resources/dist/" - } - }); - previewServer.printUrls(); + const previewServer = await preview({ + ...baseConfig, + preview: { + // port: 4173, + open: "entry-app-webview.html", + }, + build: { + outDir: "xcode/App-Shared/Resources/dist/", + }, + }); + previewServer.printUrls(); })(); diff --git a/scripts/preview-ext-demo.js b/scripts/preview-ext-demo.js new file mode 100644 index 00000000..a14b0a9a --- /dev/null +++ b/scripts/preview-ext-demo.js @@ -0,0 +1,24 @@ +/** + * @file Preview App WebView resources using the Vite JavaScript API + * @see {@link https://vitejs.dev/guide/api-javascript.html JavaScript API} + */ + +import { preview } from "vite"; +import { baseConfig } from "./utils.js"; + +/** + * Preview App-Shared WebView resources from xcode dist + */ +(async () => { + const previewServer = await preview({ + ...baseConfig, + preview: { + // port: 4173, + open: "entry-ext-extension-page.html", + }, + build: { + outDir: "dist/", + }, + }); + previewServer.printUrls(); +})(); diff --git a/scripts/utils.js b/scripts/utils.js new file mode 100644 index 00000000..e0069126 --- /dev/null +++ b/scripts/utils.js @@ -0,0 +1,201 @@ +/** + * node:process + * @see {@link https://nodejs.org/api/process.html#processchdirdirectory chdir} + * @see {@link https://nodejs.org/api/process.html#processcwd cwd} + * node:child_process + * @see {@link https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback execFile} + * node:util + * @see {@link https://nodejs.org/api/util.html#utilpromisifyoriginal promisify} + * node:url + * @see {@link https://nodejs.org/api/url.html#urlfileurltopathurl fileURLToPath} + * node:fs/promises + * @see {@link https://nodejs.org/api/fs.html#fspromisescopyfilesrc-dest-mode copyFile} + * @see {@link https://nodejs.org/api/fs.html#fspromisesmkdirpath-options mkdir} + * @see {@link https://nodejs.org/api/fs.html#fspromisesreaddirpath-options readdir} + * @see {@link https://nodejs.org/api/fs.html#fspromisesreadfilepath-options readFile} + * @see {@link https://nodejs.org/api/fs.html#fspromisesrealpathpath-options realpath} + * @see {@link https://nodejs.org/api/fs.html#fspromisesrmpath-options rm} + * @see {@link https://nodejs.org/api/fs.html#fspromisesstatpath-options stat} + */ +import { chdir, cwd } from "node:process"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { fileURLToPath } from "node:url"; +import { + copyFile, + mkdir, + readdir, + readFile, + realpath, + rm, + stat, +} from "node:fs/promises"; + +/** Promisified execFile */ +const execFileAsync = promisify(execFile); + +/** Define shared constants */ +export const APP_SHARED_RESOURCES = "xcode/App-Shared/Resources"; +export const EXT_SAFARI_RESOURCES = "xcode/Ext-Safari/Resources"; + +/** + * If not then cd to root dir and returns the path + * @see {@link https://nodejs.org/api/esm.html#importmetaurl} + * @returns {Promise} project root directory + */ +export async function rootDir() { + const root = fileURLToPath(new URL("..", import.meta.url)); + if (cwd() !== (await realpath(root))) { + chdir(root); + console.info("cd:", root); + } + return root; +} + +/** + * Empty the build directory safely + * @see {@link https://nodejs.org/api/errors.html#common-system-errors ENOENT} + * @param {string} dir + * @returns {Promise} + */ +export async function emptyBuildDir(dir) { + const buildPaths = ["dist", EXT_SAFARI_RESOURCES]; + if (!buildPaths.includes(dir)) { + console.error("Non-build path, cannot be empty."); + return false; + } + const root = await rootDir(); + try { + for (const sub of await readdir(dir)) { + const path = `${root}/${dir}/${sub}`; + // console.log("rm:", path); // DEBUG + await rm(path, { force: true, recursive: true }); + } + return true; + } catch (error) { + if (error.code === "ENOENT") { + console.info("[SKIPPED] emptyBuildDir:", error.message); + return true; + } + console.error("emptyBuildDir:", error); + return false; + } +} + +/** + * Copy a file or folder recursively + * @param {string} src Source file or directory + * @param {string} dest Destination file or directory + */ +export async function cp(src, dest) { + // console.log("cp:", src, "->", dest); // DEBUG + await rootDir(); + const srcStat = await stat(src); + if (srcStat.isFile()) return copyFile(src, dest); + if (!srcStat.isDirectory()) return; + await mkdir(dest, { recursive: true }); + for (const sub of await readdir(src)) { + if ([".DS_Store"].includes(sub)) continue; + await cp(`${src}/${sub}`, `${dest}/${sub}`); + } +} + +/** + * Read file and return text + * @param {string} file file path + * @returns {Promise} file content + */ +export async function read(file) { + const path = fileURLToPath(new URL(file, import.meta.url)); + return readFile(path, "utf8"); +} + +/** + * @see {@link https://vitejs.dev/config/server-options.html#server-https} + * @see {@link https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener} + * @returns {Promise} https.ServerOptions + */ +export const sharedServerOptions = async () => ({ + ca: await read("./local/ca.pem"), + key: await read("./local/key.pem"), + cert: await read("./local/cert.pem"), +}); + +/** + * @see {@link https://git-scm.com/docs/git-describe} + * @returns {Promise} git tag, eg: v5.0.0-beta.1 + */ +async function getGitTag() { + try { + const { stdout, stderr } = await execFileAsync("git", [ + "describe", + "--tags", + "--abbrev=0", + ]); + stderr && console.log(stderr); + if (stdout) return stdout.trim(); + } catch (error) { + console.error(error); + process.exit(error.code); + } +} + +/** + * @see {@link https://git-scm.com/docs/git-rev-parse} + * @returns {Promise} git commit, eg: 700c978ccbd387921084ee185f495aa1f795d2e0 + */ +async function getGitCommit() { + try { + const { stdout, stderr } = await execFileAsync("git", [ + "rev-parse", + "HEAD", + ]); + stderr && console.log(stderr); + if (stdout) return stdout.trim(); + } catch (error) { + console.error(error); + process.exit(error.code); + } +} + +const gitTag = await getGitTag(); +const gitCommit = await getGitCommit(); +if (!gitTag || !gitCommit) { + console.error("Failed to get git infos, please check."); + process.exit(1); +} + +/** + * Define default vite config options + * Disable auto resolving {@link vite.config.js} + * @see {@link https://vitejs.dev/config/ Config} + * @see {@link https://vitejs.dev/guide/api-javascript.html#inlineconfig InlineConfig} + * @type {import("vite").InlineConfig} + */ +export const baseConfig = { + configFile: false, + envFile: false, + root: await rootDir(), + base: "./", + define: process.env.SAFARI_PLATFORM + ? { + "import.meta.env.BROWSER": JSON.stringify("Safari"), + "import.meta.env.NATIVE_APP": JSON.stringify("app"), + "import.meta.env.SAFARI_PLATFORM": JSON.stringify( + process.env.SAFARI_PLATFORM, + ), + "import.meta.env.GIT_TAG": JSON.stringify(gitTag), + "import.meta.env.GIT_COMMIT": JSON.stringify(gitCommit), + } + : { + "import.meta.env.GIT_TAG": JSON.stringify(gitTag), + "import.meta.env.GIT_COMMIT": JSON.stringify(gitCommit), + }, + resolve: { + alias: { + // NOTE: Don't forget to modify the `paths` in `jsconfig.json` also + "@shared": fileURLToPath(new URL("../src/shared", import.meta.url)), + "@ext": fileURLToPath(new URL("../src/ext/shared", import.meta.url)), + }, + }, +}; diff --git a/scripts/vite-plugin-marked-divest.js b/scripts/vite-plugin-marked-divest.js new file mode 100644 index 00000000..27093567 --- /dev/null +++ b/scripts/vite-plugin-marked-divest.js @@ -0,0 +1,89 @@ +import { fileURLToPath } from "node:url"; +import { readFile } from "node:fs/promises"; +import { marked } from "marked"; + +/** + * Exclude files that do not need to be processed + * @param {string} id + * @returns {boolean} + */ +function isExclude(id) { + const res = [/\/src\/app\/_locales\/[A-Za-z0-9_]+\/messages\.js/]; + for (const re of res) { + if (re.test(id)) return false; + } + return true; +} + +/** + * Preprocess `marked` calls and `.md?raw` transformations at build time + * to avoid including `marked` modules and runtime transformations + * @returns {import('vite').Plugin} + */ +function preprocessMarked() { + return { + name: "preprocess-marked", + resolveId: { + order: "pre", + handler(source, importer) { + if (isExclude(importer)) return; + if (source.endsWith(".md?raw")) { + const importerUrl = new URL(importer, import.meta.url); + const sourceUrl = new URL(source, importerUrl); + return fileURLToPath(sourceUrl) + "?marked"; + } + }, + }, + load: { + order: "pre", + async handler(id) { + if (!id.endsWith(".md?marked")) return; + const url = new URL(id, import.meta.url); + const text = await readFile(fileURLToPath(url), "utf8"); + const html = await marked.parse(text); + return `export default ${JSON.stringify(html)}`; + }, + }, + transform(code, id) { + if (isExclude(id)) return; + if (!code.includes("marked.parse")) return; + const node = this.parse(code); + if (node.sourceType !== "module") return; + for (const module of node.body) { + if (module.type !== "ImportDeclaration") continue; + if (!module.source.value.toString().endsWith(".md?raw")) continue; + const specifier = module.specifiers.find( + (specifier) => specifier.type === "ImportDefaultSpecifier", + ); + const varName = specifier.local.name; + code = code.replace(`await marked.parse(${varName})`, varName); + // console.debug(`optimizing 'await marked.parse(${varName})'`, id); // DEBUG + } + return { code }; + }, + }; +} + +/** + * Turn off `Marked` module side effects for better tree-shaking + * @returns {import('vite').Plugin} + */ +function disableMarkedSideEffects() { + return { + name: "disable-marked-side-effects", + transform(code, id) { + if (id.includes("/node_modules/marked")) { + return { code, moduleSideEffects: false }; + } + }, + }; +} + +/** + * @returns {import('vite').Plugin[]} + */ +function markedDivest() { + return [disableMarkedSideEffects(), preprocessMarked()]; +} + +export default markedDivest; diff --git a/src/app/App.svelte b/src/app/App.svelte index 870c318f..bbfe8df8 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -1,125 +1,407 @@ + +
- Userscripts App Icon - -

You can turn on the Userscripts iOS Safari extension in Settings. - Read the docs. -

- -
CURRENT DIRECTORY:
-
- -
+ {#await initialize() then app} + {#if OOBE} +
+
{gl("quick_start_guide_title")}
+ + {@html OOBEText} + +
+ {/if} + {#if promptLogger} + + {/if} + {#if enableLogger} +
+ + + + +
+ {/if} +
+
+
+
+ {#if import.meta.env.GIT_TAG && import.meta.env.GIT_COMMIT} + + {import.meta.env.GIT_TAG} + + ( + {import.meta.env.GIT_COMMIT.slice(0, 7)} + ) + {:else} + v{app.version} + ({app.build}) + {/if} +
+ {#if platform === "mac"} +
+ + {gl("safari_extension_status")}: + {gl(extStatus)} +
+ + {/if} +
+
+ +
+
+ +
{gl("current_directory")}
+ +
+ + {:catch error} +
+ {error} +
+ {/await}
diff --git a/src/app/_locales/en/messages.js b/src/app/_locales/en/messages.js new file mode 100644 index 00000000..4cee3c24 --- /dev/null +++ b/src/app/_locales/en/messages.js @@ -0,0 +1,85 @@ +import nativeLoggerCaveat from "./native-logger-caveat.md?raw"; +import quickStartGuideMac from "./quick-start-guide-mac.md?raw"; +import quickStartGuideIos from "./quick-start-guide-ios.md?raw"; + +import { marked } from "marked"; + +/** @satisfies {Types.I18nMessages} */ +export const markdown = { + native_logger_caveat: { + message: await marked.parse(nativeLoggerCaveat), + }, + quick_start_guide_mac: { + message: await marked.parse(quickStartGuideMac), + }, + quick_start_guide_ios: { + message: await marked.parse(quickStartGuideIos), + }, +}; + +/** @satisfies {Types.I18nMessages} */ +export const messages = { + enabled: { + message: "Enabled", + }, + disabled: { + message: "Disabled", + }, + unknown: { + message: "Unknown", + }, + error: { + message: "Error", + }, + button_ok: { + message: "OK", + }, + button_dismiss: { + message: "Dismiss", + }, + button_disable: { + message: "Disable", + }, + safari_extension_status: { + message: "Safari Extension Status", + }, + native_logger_enabled_title: { + message: "Native Logger Enabled", + }, + quick_start_guide_title: { + message: "Quick Start Guide", + }, + open_safari_settings: { + message: "Open Safari Settings", + }, + open_safari_preferences: { + message: "Open Safari Preferences", + }, + show_usage_guide: { + message: "Show Usage Guide", + }, + change_directory: { + message: "Change Userscripts Directory", + }, + current_directory: { + message: "CURRENT DIRECTORY:", + }, + documentation: { + message: "Documentation", + }, + discussions: { + message: "Discussions", + }, + report_an_issue: { + message: "Report an Issue", + }, + privacy_policy: { + message: "Privacy Policy", + }, + export_log_files: { + message: "Export log files", + }, + disable_logger: { + message: "Disable Logger", + }, +}; diff --git a/src/app/_locales/en/native-logger-caveat.md b/src/app/_locales/en/native-logger-caveat.md new file mode 100644 index 00000000..f1ba6b62 --- /dev/null +++ b/src/app/_locales/en/native-logger-caveat.md @@ -0,0 +1,5 @@ +- The logger is for local debugging only and can be streamed through the macOS console while writing to a local file when enabled. +- You can repeat the steps to reproduce the issue after enabling it, then export the log files and inspect them in the app. +- There is no built-in upload function. If you need to submit it to developers, please manually attach the exported log files to your issue report. +- To reduce energy consumption and disk reading and writing, please disbale the logger promptly after debugging. +- If the log file exceeds $1, the logger will be automatically disbaled and the log files will be cleared. diff --git a/src/app/_locales/en/quick-start-guide-ios.md b/src/app/_locales/en/quick-start-guide-ios.md new file mode 100644 index 00000000..4bc2316b --- /dev/null +++ b/src/app/_locales/en/quick-start-guide-ios.md @@ -0,0 +1,27 @@ +### Welcome, follow these two main steps to get the extension up and running: + +- **Open the app and set a directory** + + - When you first launch the app, a local directory will be set by default. + - If needed, you can select a new directory to suit your preferences. + - This directory is used for saving and loading your user scripts. + - Click the current directory to jump to the default directory or the one you set. + +- **Enable the extension in Safari** + + - Manage extensions from the Settings App (Settings > Safari > Extensions) or Safari App (`AA` button on iPhone, `extension-icon` on iPad, the same location where you open the extension's `popup` interface). + - For an optimal experience it's recommended that you "Always Allow" Userscripts for "All Websites", but you could also allow only the sites you want, but please don't forget your settings if your script doesn't work. + +### How to install and inject a user script and some things to note: + +- The mobile extension does not currently have a built-in script editor, but you could still write your own scripts and install them. +- You could save files that extension with `.user.js` directly to the Userscripts directory. + - The script file must contain a valid metadata block to match the web pages and be displayed in the extension popup. +- When you visit a `.user.js` URL in Safari, open the extension popup and you will see an installation prompt, or it will pop up automatically if you have enhanced prompts turned on. + - The URL should end with `.user.js` in `/PATH` part, not the `?QUERY` or `#HASH` parts, otherwise it will not be considered a valid user script URL by the extension. +- When you enabled the extension and write the metadata correctly especially like `@match`, it will automatically complete the injection when you visit a matching web page. + - You could temporarily disable individual user scripts or all of them, just open the extension popup interface to toggle them. + +### That’s it, start improving your quality of life now! + +_Hey, don't forget to read our detailed documentation to learn more._ diff --git a/src/app/_locales/en/quick-start-guide-mac.md b/src/app/_locales/en/quick-start-guide-mac.md new file mode 100644 index 00000000..c6ae6ef7 --- /dev/null +++ b/src/app/_locales/en/quick-start-guide-mac.md @@ -0,0 +1,27 @@ +### Welcome to this project, you will need two main steps to make the extension work: + +- **Open the app and set a directory** + + - When you first launch the app, a local directory will be set by default. + - If needed, you can select a new directory to suit your preferences. + - This directory is used for saving and loading your user scripts. + - Click the current directory to jump to the default directory or the one you set. + +- **Enable the extension in Safari** + + - Enable Userscripts in Safari > Settings... > Extensions list. + - For an optimal experience it's recommended that you "Always Allow" Userscripts for "All Websites", but you could also allow only the sites you want, but please don't forget your settings if your script doesn't work. + +### How to install and inject a user script and some things to note: + +- The preferred method is of course to create your own scripts, open the extension page and click on the plus sign in the editor to create a new JS, write code and save it. +- You could also save files that extension with `.user.js` directly to the Userscripts directory. + - The script file must contain a valid metadata block to match the web pages and be displayed in the extension popup. +- When you visit a `.user.js` URL in Safari, open the extension popup and you will see an installation prompt, or it will pop up automatically if you have enhanced prompts turned on. + - The URL should end with `.user.js` in `/PATH` part, not the `?QUERY` or `#HASH` parts, otherwise it will not be considered a valid user script URL by the extension. +- When you enabled the extension and write the metadata correctly especially like `@match`, it will automatically complete the injection when you visit a matching web page. + - You could temporarily disable individual user scripts or all of them, just open the extension popup interface to toggle them. + +### That’s it, start improving your quality of life now! + +_Hey, don't forget to read our detailed documentation to learn more._ diff --git a/src/app/_locales/zh/messages.js b/src/app/_locales/zh/messages.js new file mode 100644 index 00000000..e69057b0 --- /dev/null +++ b/src/app/_locales/zh/messages.js @@ -0,0 +1,85 @@ +import nativeLoggerCaveat from "./native-logger-caveat.md?raw"; +import quickStartGuideMac from "./quick-start-guide-mac.md?raw"; +import quickStartGuideIos from "./quick-start-guide-ios.md?raw"; + +import { marked } from "marked"; + +/** @satisfies {typeof import("../en/messages.js").markdown} */ +export const markdown = { + native_logger_caveat: { + message: await marked.parse(nativeLoggerCaveat), + }, + quick_start_guide_mac: { + message: await marked.parse(quickStartGuideMac), + }, + quick_start_guide_ios: { + message: await marked.parse(quickStartGuideIos), + }, +}; + +/** @satisfies {typeof import("../en/messages.js").messages} */ +export const messages = { + enabled: { + message: "已启用", + }, + disabled: { + message: "已停用", + }, + unknown: { + message: "未知", + }, + error: { + message: "错误", + }, + button_ok: { + message: "确定", + }, + button_dismiss: { + message: "关闭", + }, + button_disable: { + message: "停用", + }, + safari_extension_status: { + message: "Safari 扩展状态", + }, + native_logger_enabled_title: { + message: "本地日志记录器已启用", + }, + quick_start_guide_title: { + message: "快速入门指南", + }, + open_safari_settings: { + message: "打开 Safari 设置", + }, + open_safari_preferences: { + message: "打开 Safari 偏好", + }, + show_usage_guide: { + message: "显示使用指南", + }, + change_directory: { + message: "更改用户脚本目录", + }, + current_directory: { + message: "当前目录:", + }, + documentation: { + message: "详细文档", + }, + discussions: { + message: "交流讨论", + }, + report_an_issue: { + message: "报告问题", + }, + privacy_policy: { + message: "隐私政策", + }, + export_log_files: { + message: "导出日志文件", + }, + disable_logger: { + message: "停用日志记录", + }, +}; diff --git a/src/app/_locales/zh/native-logger-caveat.md b/src/app/_locales/zh/native-logger-caveat.md new file mode 100644 index 00000000..005723d0 --- /dev/null +++ b/src/app/_locales/zh/native-logger-caveat.md @@ -0,0 +1,5 @@ +- 日志记录器仅用于本地调试,开启期间可通过 macOS 控制台流式传输,同时写入本地文件。 +- 您可以在开启后重复复现问题的相关操作,然后在 App 中导出日志文件和检查它们。 +- 没有内建的上传功能,如需提交给开发者,请手动将导出的日志文件附加到您的问题报告中。 +- 为减少能耗和磁盘读写,请调试结束后及时关闭日志记录器。 +- 如日志文件超过 $1 将自动关闭日志记录器并清除日志。 diff --git a/src/app/_locales/zh/quick-start-guide-ios.md b/src/app/_locales/zh/quick-start-guide-ios.md new file mode 100644 index 00000000..d24bda16 --- /dev/null +++ b/src/app/_locales/zh/quick-start-guide-ios.md @@ -0,0 +1,26 @@ +### 欢迎来到这个项目,您需要两个主要步骤来使扩展工作: + +- **打开 App 并设置目录** + + - 该目录用于保存和加载您的用户脚本。 + - 首次启动 App 时已为您设置默认本地目录。 + - 点击当前目录可跳转到默认目录或您设置的目录。 + +- **在 Safari 中启用扩展** + + - 从 `设置` 应用(设置 > Safari > 扩展)或 `Safari` 应用(iPhone 上的“AA”按钮,iPad 上的“扩展图标”,也是您打开扩展的“弹出界面”的位置)管理扩展。 + - 为了获得最佳体验,建议您“始终允许” Userscripts 为“所有网站”,但您也可以仅允许您想要的网站,但当您的脚本不起作用时,请不要忘记您的设置。 + +### 那么,如何安装和注入用户脚本,以及一些注意事项? + +- 移动版扩展目前不包含内置的脚本编辑器,但您仍然可以编写您自己的脚本并安装它们。 +- 您可以直接保存扩展名为 `.user.js` 的文件到您上面设置的 Userscripts 目录中。 + - 您的脚本文件必须包含有效的元数据块以匹配网站页面并正确显示在扩展的弹出界面中。 +- 当您在 Safari 中访问一个 `.user.js` 网址,然后打开扩展弹出界面,您将看到安装提示;如果您启用了增强提示,它会自动弹出。 + - 请注意该网址的 `/路径` 部分应该以 `.user.js` 结尾,而不是 `?查询` 或 `#锚点` 部分,否则该网址将不会被视为有效的用户脚本。 +- 当您启用该扩展并正确编写元数据尤其是像 `@match` 后,当您访问匹配的网页时,它将自动完成注入。 + - 您可以暂时禁用单个用户脚本或所有用户脚本,只需打开扩展弹出界面即可切换它们。 + +### 就这样,现在就开始改善您的生活质量吧! + +_嘿,不要忘记阅读我们的详细文档以了解更多信息。_ diff --git a/src/app/_locales/zh/quick-start-guide-mac.md b/src/app/_locales/zh/quick-start-guide-mac.md new file mode 100644 index 00000000..f9983ced --- /dev/null +++ b/src/app/_locales/zh/quick-start-guide-mac.md @@ -0,0 +1,26 @@ +### 欢迎来到这个项目,您需要两个主要步骤来使扩展工作: + +- **打开 App 并设置目录** + + - 该目录用于保存和加载您的用户脚本。 + - 首次启动 App 时已为您设置默认本地目录。 + - 点击当前目录可跳转到默认目录或您设置的目录。 + +- **在 Safari 中启用扩展** + + - 在 Safari > 设置... > 扩展列表中启用 Userscripts 条目。 + - 为了获得最佳体验,建议您“始终允许” Userscripts 为“所有网站”,但您也可以仅允许您想要的网站,但当您的脚本不起作用时,请不要忘记您的设置。 + +### 那么,如何安装和注入用户脚本,以及一些注意事项? + +- 首选的方法当然是撰写您自己的用户脚本,打开扩展页面点击加号创建新脚本,编辑和保存它。 +- 您还可以直接保存扩展名为 `.user.js` 的文件到您上面设置的 Userscripts 目录中。 + - 您的脚本文件必须包含有效的元数据块以匹配网站页面并正确显示在扩展的弹出界面中。 +- 当您在 Safari 中访问一个 `.user.js` 网址,然后打开扩展弹出界面,您将看到安装提示;如果您启用了增强提示,它会自动弹出。 + - 请注意该网址的 `/路径` 部分应该以 `.user.js` 结尾,而不是 `?查询` 或 `#锚点` 部分,否则该网址将不会被视为有效的用户脚本。 +- 当您启用该扩展并正确编写元数据尤其是像 `@match` 后,当您访问匹配的网页时,它将自动完成注入。 + - 您可以暂时禁用单个用户脚本或所有用户脚本,只需打开扩展弹出界面即可切换它们。 + +### 就这样,现在就开始改善您的生活质量吧! + +_嘿,不要忘记阅读我们的详细文档以了解更多信息。_ diff --git a/src/app/_locales/zh_Hant/messages.js b/src/app/_locales/zh_Hant/messages.js new file mode 100644 index 00000000..e0fadede --- /dev/null +++ b/src/app/_locales/zh_Hant/messages.js @@ -0,0 +1,85 @@ +import nativeLoggerCaveat from "./native-logger-caveat.md?raw"; +import quickStartGuideMac from "./quick-start-guide-mac.md?raw"; +import quickStartGuideIos from "./quick-start-guide-ios.md?raw"; + +import { marked } from "marked"; + +/** @satisfies {typeof import("../en/messages.js").markdown} */ +export const markdown = { + native_logger_caveat: { + message: await marked.parse(nativeLoggerCaveat), + }, + quick_start_guide_mac: { + message: await marked.parse(quickStartGuideMac), + }, + quick_start_guide_ios: { + message: await marked.parse(quickStartGuideIos), + }, +}; + +/** @satisfies {typeof import("../en/messages.js").messages} */ +export const messages = { + enabled: { + message: "已啟用", + }, + disabled: { + message: "已停用", + }, + unknown: { + message: "未知", + }, + error: { + message: "錯誤", + }, + button_ok: { + message: "確定", + }, + button_dismiss: { + message: "關閉", + }, + button_disable: { + message: "停用", + }, + safari_extension_status: { + message: "Safari 延伸功能狀態", + }, + native_logger_enabled_title: { + message: "本地日誌記錄器已啟用", + }, + quick_start_guide_title: { + message: "快速入門指南", + }, + open_safari_settings: { + message: "開啟 Safari 設定", + }, + open_safari_preferences: { + message: "開啟 Safari 偏好", + }, + show_usage_guide: { + message: "顯示使用指南", + }, + change_directory: { + message: "更改使用者腳本目錄", + }, + current_directory: { + message: "當前目錄:", + }, + documentation: { + message: "詳細文件", + }, + discussions: { + message: "交流討論", + }, + report_an_issue: { + message: "報告問題", + }, + privacy_policy: { + message: "隱私政策", + }, + export_log_files: { + message: "匯出日誌檔案", + }, + disable_logger: { + message: "停用日誌記錄", + }, +}; diff --git a/src/app/_locales/zh_Hant/native-logger-caveat.md b/src/app/_locales/zh_Hant/native-logger-caveat.md new file mode 100644 index 00000000..0c9b4baa --- /dev/null +++ b/src/app/_locales/zh_Hant/native-logger-caveat.md @@ -0,0 +1,5 @@ +- 日誌記錄器僅用於本地除錯,開啟期間可透過 macOS 控制檯流式傳輸,同時寫入本地檔案。 +- 您可以在開啟後重複復現問題的相關操作,然後在 App 中匯出日誌檔案和檢查它們。 +- 沒有內建的上傳功能,如需提交給開發者,請手動將匯出的日誌檔案附加到您的問題報告中。 +- 為減少能耗和磁碟讀寫,請除錯結束後及時關閉日誌記錄器。 +- 如日誌檔案超過 $1 將自動關閉日誌記錄器並清除日誌。 diff --git a/src/app/_locales/zh_Hant/quick-start-guide-ios.md b/src/app/_locales/zh_Hant/quick-start-guide-ios.md new file mode 100644 index 00000000..0d7f5e51 --- /dev/null +++ b/src/app/_locales/zh_Hant/quick-start-guide-ios.md @@ -0,0 +1,26 @@ +### 歡迎來到這個專案,您需要兩個主要步驟來使延伸功能工作: + +- **開啟 App 並設定目錄** + + - 該目錄用於儲存和載入您的使用者腳本。 + - 首次啟動 App 時已為您設定預設本地目錄。 + - 點選當前目錄可跳轉到預設目錄或您設定的目錄。 + +- **在 Safari 中啟用延伸功能** + + - 從 `設定` 應用(設定 > Safari > 延伸功能)或 `Safari` 應用(iPhone 上的“AA”按鈕,iPad 上的“延伸功能圖示”,也是您開啟延伸功能的“彈出介面”的位置)管理延伸功能。 + - 為了獲得最佳體驗,建議您“始終允許” Userscripts 為“所有網站”,但您也可以僅允許您想要的網站,但當您的腳本不起作用時,請不要忘記您的設定。 + +### 那麼,如何安裝和注入使用者腳本,以及一些注意事項? + +- 移動版延伸功能目前不包含內建的腳本編輯器,但您仍然可以編寫您自己的腳本並安裝它們。 +- 您可以直接儲存副檔名為 `.user.js` 的檔案到您上面設定的 Userscripts 目錄中。 + - 您的腳本檔案必須包含有效的元資料塊以匹配網站頁面並正確顯示在延伸功能的彈出介面中。 +- 當您在 Safari 中訪問一個 `.user.js` 網址,然後開啟延伸功能彈出介面,您將看到安裝提示;如果您啟用了增強提示,它會自動彈出。 + - 請注意該網址的 `/路徑` 部分應該以 `.user.js` 結尾,而不是 `?查詢` 或 `#錨點` 部分,否則該網址將不會被視為有效的使用者腳本。 +- 當您啟用該延伸功能並正確編寫元資料尤其是像 `@match` 後,當您訪問匹配的網頁時,它將自動完成注入。 + - 您可以暫時禁用單個使用者腳本或所有使用者腳本,只需開啟延伸功能彈出介面即可切換它們。 + +### 就這樣,現在就開始改善您的生活質量吧! + +_嘿,不要忘記閱讀我們的詳細文件以瞭解更多資訊。_ diff --git a/src/app/_locales/zh_Hant/quick-start-guide-mac.md b/src/app/_locales/zh_Hant/quick-start-guide-mac.md new file mode 100644 index 00000000..3331a109 --- /dev/null +++ b/src/app/_locales/zh_Hant/quick-start-guide-mac.md @@ -0,0 +1,26 @@ +### 歡迎來到這個專案,您需要兩個主要步驟來使延伸功能工作: + +- **開啟 App 並設定目錄** + + - 該目錄用於儲存和載入您的使用者腳本。 + - 首次啟動 App 時已為您設定預設本地目錄。 + - 點選當前目錄可跳轉到預設目錄或您設定的目錄。 + +- **在 Safari 中啟用延伸功能** + + - 在 Safari > 設定... > 延伸功能列表中啟用 Userscripts 條目。 + - 為了獲得最佳體驗,建議您“始終允許” Userscripts 為“所有網站”,但您也可以僅允許您想要的網站,但當您的腳本不起作用時,請不要忘記您的設定。 + +### 那麼,如何安裝和注入使用者腳本,以及一些注意事項? + +- 首選的方法當然是撰寫您自己的使用者腳本,開啟延伸功能頁面點選加號建立新腳本,編輯和儲存它。 +- 您還可以直接儲存副檔名為 `.user.js` 的檔案到您上面設定的 Userscripts 目錄中。 + - 您的腳本檔案必須包含有效的元資料塊以匹配網站頁面並正確顯示在延伸功能的彈出介面中。 +- 當您在 Safari 中訪問一個 `.user.js` 網址,然後開啟延伸功能彈出介面,您將看到安裝提示;如果您啟用了增強提示,它會自動彈出。 + - 請注意該網址的 `/路徑` 部分應該以 `.user.js` 結尾,而不是 `?查詢` 或 `#錨點` 部分,否則該網址將不會被視為有效的使用者腳本。 +- 當您啟用該延伸功能並正確編寫元資料尤其是像 `@match` 後,當您訪問匹配的網頁時,它將自動完成注入。 + - 您可以暫時禁用單個使用者腳本或所有使用者腳本,只需開啟延伸功能彈出介面即可切換它們。 + +### 就這樣,現在就開始改善您的生活質量吧! + +_嘿,不要忘記閱讀我們的詳細文件以瞭解更多資訊。_ diff --git a/src/app/app.css b/src/app/app.css index 2aa6eab7..40c7cfa2 100644 --- a/src/app/app.css +++ b/src/app/app.css @@ -1,51 +1,121 @@ html { - font-size: 100%; - height: 100vh; - overflow: hidden; + height: auto; + overflow: visible; + overscroll-behavior: none; } body { - background-color: var(--color-bg-secondary); - color: var(--text-color-primary); - font: var(--text-medium); - height: 100%; - letter-spacing: var(--letter-spacing-medium); - position: relative; - text-rendering: optimizelegibility; - -webkit-font-smoothing: antialiased; + background-color: var(--color-bg-primary); + color: var(--text-color-primary); + font: var(--text-default); + letter-spacing: var(--letter-spacing-medium); + text-rendering: optimizelegibility; + -webkit-font-smoothing: antialiased; } -@media screen and (height <= 400px) { - html { - font-size: 80%; - } +/* ios */ +@supports (-webkit-touch-callout: none) { + /** + * Dynamic Type + * https://support.apple.com/102453 + * https://webkit.org/blog/3709/using-the-system-font-in-web-content/ + * https://developer.apple.com/library/archive/releasenotes/General/WhatsNewInSafari/Articles/Safari_7_0.html#//apple_ref/doc/uid/TP40014305-CH5-SW10 + * https://github.com/w3c/csswg-drafts/issues/3708 + */ + @supports (font: -apple-system-body) { + html { + /* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */ + font: -apple-system-body; /* default 17px */ + } + } - p { - font: var(--text-large); - letter-spacing: var(--letter-spacing-large); - } + /* Avoid page zoom */ + input, + textarea, + select { + font-size: max(1rem, 16px); + } } -@media screen and (height >= 700px) and (width >= 600px) { - html { - font-size: 150%; - } +#app { + flex-direction: column; } -#app { - height: 100%; +address, +article, +aside, +button, +div, +footer, +header, +hgroup, +main, +nav, +search, +section { + display: flex; } button { - border: none; - cursor: pointer; - padding: 0; - user-select: none; + border: none; + cursor: pointer; + padding: 0; + user-select: none; } +button:disabled { + opacity: var(--opacity-disabled); + pointer-events: none; +} + +a, .link { - background: none; - color: var(--color-blue); - cursor: pointer; - text-decoration: underline; -} \ No newline at end of file + background: none; + color: var(--color-blue); + cursor: pointer; + text-decoration: underline; +} + +.truncate { + flex-grow: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.flexible_space { + flex-grow: 1; +} + +x-markdown { + display: flex; + flex-direction: column; + overflow: auto; + text-align: left; + gap: var(--gap); + padding: 0 3rem; + --gap: 0.75rem; +} + +@media (width < 500px) { + x-markdown { + padding: 0 1.5rem; + } +} + +x-markdown :is(h1, h2, h3, h4, h5, h6) { + padding: 0; + margin: 0; +} + +x-markdown ul { + display: flex; + flex-direction: column; + gap: var(--gap); + padding-left: 1.5rem; +} + +x-markdown > ul ul { + margin-top: var(--gap); +} diff --git a/src/app/global.d.ts b/src/app/global.d.ts new file mode 100644 index 00000000..b3f9487f --- /dev/null +++ b/src/app/global.d.ts @@ -0,0 +1,25 @@ +// https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html + +/// +/// + +declare global { + interface Window { + webapp: { + updateDirectory: (directory: string) => void; + updateExtStatus: (extStatus: ExtensionStatus) => void; + switchLogger: (enableLogger: boolean, promptLogger: boolean) => void; + }; + webkit: { + messageHandlers: { + controller: { + postMessage: ( + message: T, + ) => Promise>; + }; + }; + }; + } +} + +export {}; diff --git a/src/app/i18n.js b/src/app/i18n.js new file mode 100644 index 00000000..389042d8 --- /dev/null +++ b/src/app/i18n.js @@ -0,0 +1,58 @@ +/** + * @typedef {typeof import("./_locales/en/messages.js").messages} MessagesT + * @typedef {typeof import("./_locales/en/messages.js").markdown} MarkdownT + */ + +/** + * @param {Types.I18nMessages} messages + * @param {string} messageName + * @param {string | string[]} substitutions + * @returns {string} + */ +function getLangFrom(messages, messageName, substitutions = undefined) { + if (!(messageName in messages)) { + console.warn(`i18n - "${messageName}" not found`, messages); + return ""; + } + let text = messages[messageName].message; + // handle substitutions + if (typeof substitutions === "string") { + text = text.replace("$1", substitutions); + } else if (Array.isArray(substitutions)) { + for (let i = 0; i < substitutions.length; i++) { + text = text.replace(`$${i + 1}`, substitutions[i]); + } + } + if (import.meta.env.MODE === "development") { + console.debug("i18n", messages[messageName].message, text, substitutions); + } + return text; +} + +export async function i18nInit() { + const languages = [navigator.language.replace("-", "_")]; + if (["zh-HK", "zh-MO", "zh-TW"].includes(navigator.language)) { + languages.unshift("zh_Hant"); + } + languages.push(navigator.language.split("-")[0]); + languages.push("en"); // fallback + if (import.meta.env.MODE === "development") { + // languages.unshift("en"); // DEBUG + } + for (const language of languages) { + try { + /** @type {{messages: MessagesT, markdown: MarkdownT}} */ + const module = await import(`./_locales/${language}/messages.js`); + const { messages, markdown } = module; + return { + /** @type {Types.GetLang} */ + gl: (n, s) => getLangFrom(messages, n, s), + /** @type {Types.GetLang} */ + md: (n, s) => getLangFrom(markdown, n, s), + }; + } catch (error) { + console.debug("Will fall back to `en`", error); + } + } + throw Error("I18n initialization failed"); +} diff --git a/src/app/img/icon.png b/src/app/img/icon.png deleted file mode 100644 index 9fe96182..00000000 Binary files a/src/app/img/icon.png and /dev/null differ diff --git a/src/app/img/logo.svg b/src/app/img/logo.svg deleted file mode 100644 index 9bf95127..00000000 --- a/src/app/img/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/jsconfig.json b/src/app/jsconfig.json new file mode 100644 index 00000000..95a69da9 --- /dev/null +++ b/src/app/jsconfig.json @@ -0,0 +1,30 @@ +// https://code.visualstudio.com/docs/languages/jsconfig +// https://www.typescriptlang.org/docs/handbook/tsconfig-json.html +// https://www.typescriptlang.org/tsconfig + +// https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html +// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html + +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + + "verbatimModuleSyntax": true, + "isolatedModules": true, + "resolveJsonModule": true, + + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "sourceMap": true, + + "paths": { + "@shared/*": ["../shared/*"] + } + }, + "include": ["**/*.d.ts", "**/*.js", "**/*.svelte"] +} diff --git a/src/app/main.js b/src/app/main.js index d7459fa0..8a28fe48 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -1,10 +1,42 @@ import "./reset.css"; -import "./variables.css"; +import "@shared/variables.css"; import "./app.css"; +import { mount } from "svelte"; import App from "./App.svelte"; -const app = new App({ - target: document.getElementById("app") -}); +if (import.meta.env.MODE === "development") { + // Simulation in non-WkWebView dev environment + if (import.meta.env.SAFARI_PLATFORM === undefined) { + console.warn("Simulation webkit..."); + window.webkit = { + messageHandlers: { + controller: { + postMessage: async (message) => { + switch (message) { + case "INIT": + return { + build: "2", + directory: "Userscripts App Documents", + enableLogger: true, + extStatus: "unknown", + firstRunTime: 1, + maxLogFileSize: 500_000_000, + platform: "mac", + promptLogger: true, + useSettingsInsteadOfPreferences: true, + version: "2.0.0", + }; + default: + console.debug("Simulation.webkit.messageHandler:", message); + break; + } + }, + }, + }, + }; + } +} + +const app = mount(App, { target: document.getElementById("app") }); export default app; diff --git a/src/app/reset.css b/src/app/reset.css index 6c181e6b..0b8335ad 100644 --- a/src/app/reset.css +++ b/src/app/reset.css @@ -1,44 +1,42 @@ *, *::before, *::after { - box-sizing: border-box; + box-sizing: border-box; } body, p { - margin: 0; + margin: 0; } ul { - list-style: none; - margin: 0; - padding: 0; + margin: 0; } button, input, select, textarea { - font-family: inherit; - font-size: 100%; - line-height: inherit; - margin: 0; + font-family: inherit; + font-size: 100%; + line-height: inherit; + margin: 0; } button, input[type="button"], input[type="reset"], input[type="submit"] { - -webkit-appearance: button; + -webkit-appearance: button; } ::-webkit-search-decoration, ::-webkit-search-cancel-button, ::-webkit-search-results-button, ::-webkit-search-results-decoration { - -webkit-appearance: none; + -webkit-appearance: none; } img { - border-style: none; + border-style: none; } diff --git a/src/app/types.d.ts b/src/app/types.d.ts new file mode 100644 index 00000000..f282812b --- /dev/null +++ b/src/app/types.d.ts @@ -0,0 +1,43 @@ +declare namespace Types { + /** @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/Locale-Specific_Message_reference} */ + type I18nMessages = { + [x: string]: { + message: string; + description?: string; + placeholders?: Object; + }; + }; + + type GetLang = ( + messageName: T, + substitutions?: string | string[], + ) => string; + + type SystemPlatform = "mac" | "ios"; + + type ExtensionStatus = "unknown" | "disabled" | "enabled" | "error"; + + type MessageBody = + | "INIT" + | "CHANGE_DIRECTORY" + | "OPEN_DIRECTORY" + | "SHOW_PREFERENCES" + | "EXPORT_LOG_FILES" + | "DISABLE_LOGGER" + | "DISMISS_LOGGER_PROMPT"; + + type MessageReply = T extends "INIT" + ? { + build: string; + version: string; + platform: SystemPlatform; + directory: string; + extStatus: ExtensionStatus; + useSettingsInsteadOfPreferences: boolean; + enableLogger: boolean; + promptLogger: boolean; + maxLogFileSize: number; + firstRunTime: number; + } + : void; +} diff --git a/src/app/variables.css b/src/app/variables.css deleted file mode 100644 index acdc671d..00000000 --- a/src/app/variables.css +++ /dev/null @@ -1,51 +0,0 @@ -:root { - --border-radius: 0.188rem; - --box-shadow: 0 0.5rem 1rem 0 rgb(0 0 0 / 0.25); - --color-bg-primary: #323639; - --color-bg-secondary: #2f3337; - --color-script-highlighted: #364049; /* rgba(116, 178, 235, 0.1); */ - --color-black: #1d2023; - --color-blue: #74b1eb; - --color-green: #60f36c; - --color-grey: rgb(255 255 255 / 0.15); - --color-red: #ff453a; - --color-yellow: #e4f360; - --letter-spacing-large: -0.031rem; - --letter-spacing-default: -0.029rem; - --letter-spacing-medium: -0.018rem; - --letter-spacing-small: -0.008rem; - --opacity-disabled: 0.3; - --text-color-primary: rgb(255 255 255); - --text-color-secondary: rgb(255 255 255 / 0.65); - --text-color-disabled: rgb(255 255 255 / 0.4); - --font-family: system-ui, -apple-system, "Helvetica Neue", "Helvetica", sans-serif; - --text-default: 1rem/1.5rem var(--font-family); - --text-large: 1.25rem/1.5rem var(--font-family); - --text-medium: 0.875rem/1.313rem var(--font-family); - --text-small: 0.719rem/1rem var(--font-family); - - /* editor variables */ - --editor-font: monaco, monospace; - --editor-font-size: 14px; - --editor-line-height: 24px; - --editor-invisible: rgb(255 255 255 / 0.15); - --editor-active-line: var(--color-bg-secondary); - --editor-selected-bg: rgb(116 178 235 / 0.35); - --editor-matched-highlight: rgb(116 178 235 / 0.2); - --editor-search-highlight: rgb(255 166 0 / 0.3); - --editor-number: #77e26a; - --editor-comment: rgb(255 255 255 / 0.35); - --editor-def: #efc371; - --editor-default: #cdcfd1; - --editor-keyword: #96c3ed; - --editor-atom: #59ebf5; - --editor-operator: #8c99a7; - --editor-property: #e86c8a; - --editor-string: #f5eea2; - --editor-string-2: #cdabff; - --editor-error: var(--color-red); - --editor-cursor: #e3e7eb; - --editor-matching-bracket-color: #fff; - --editor-matching-bracket-border: var(--editor-number); - --editor-non-matching-bracket: var(--editor-error); -} diff --git a/src/dev/App.svelte b/src/dev/App.svelte index 1d9c1db6..602dae45 100644 --- a/src/dev/App.svelte +++ b/src/dev/App.svelte @@ -1,38 +1,47 @@
-
- - - -
-

Userscripts dev

+
+ + + +
+

Userscripts dev

-

- native-app, - action-popup, - extension-page -

+

+ + native-app + , + + action-popup + , + + extension-page + +

-

- This is the development preview environment -

+

This is the development preview environment

diff --git a/src/dev/app.css b/src/dev/app.css index 1fedad38..19d0543e 100644 --- a/src/dev/app.css +++ b/src/dev/app.css @@ -1,82 +1,82 @@ /* stylelint-disable */ :root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } a:hover { - color: #535bf2; + color: #535bf2; } body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; } h1 { - font-size: 3.2em; - line-height: 1.1; + font-size: 3.2em; + line-height: 1.1; } .card { - padding: 2em; + padding: 2em; } #app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; } button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; } button:hover { - border-color: #646cff; + border-color: #646cff; } button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } } diff --git a/src/dev/jsconfig.json b/src/dev/jsconfig.json new file mode 100644 index 00000000..899038cd --- /dev/null +++ b/src/dev/jsconfig.json @@ -0,0 +1,26 @@ +// https://code.visualstudio.com/docs/languages/jsconfig +// https://www.typescriptlang.org/docs/handbook/tsconfig-json.html +// https://www.typescriptlang.org/tsconfig + +// https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html +// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html + +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + + "verbatimModuleSyntax": true, + "isolatedModules": true, + "resolveJsonModule": true, + + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "sourceMap": true + }, + "include": ["**/*.d.ts", "**/*.js", "**/*.svelte"] +} diff --git a/src/dev/main.js b/src/dev/main.js index 37103c68..85631fee 100644 --- a/src/dev/main.js +++ b/src/dev/main.js @@ -1,8 +1,7 @@ import "./app.css"; +import { mount } from "svelte"; import App from "./App.svelte"; -const app = new App({ - target: document.getElementById("app") -}); +const app = mount(App, { target: document.getElementById("app") }); export default app; diff --git a/src/ext/action-popup/App.svelte b/src/ext/action-popup/App.svelte index 92fbc72a..6d593530 100644 --- a/src/ext/action-popup/App.svelte +++ b/src/ext/action-popup/App.svelte @@ -1,739 +1,761 @@ - -
- - showUpdates = true} - title={"Show updates"} - {disabled} - /> - - -
-{#if !active} - -{/if} -{#if showInstallPrompt} -
- Userscript - {#if scriptChecking} - {showInstallPrompt} - {:else} - {scriptInstalled ? "Installed" : "Detected"}: - - {/if} -
-{/if} -{#if errorNotification} -
- {errorNotification} - errorNotification = undefined} - title={"Clear error"} - /> -
-{/if} -
- {#if loading} - - {:else} - {#if inactive} -
Popup inactive on extension page
- {:else if firstGuide} -
-

Welcome, first use please: 

- -

to complete the initialization

-
- {:else if initError} -
- Something went wrong:  - -
- {:else if items.length < 1} -
No matched userscripts
- {:else} -
- {#each list as item, i (item.filename)} - toggleItem(item)} - /> - {/each} -
- {/if} - {/if} -
-{#if !inactive && platform === "macos"} - -{/if} {#if showUpdates} - showUpdates = false} - showLoaderOnDisabled={true} - abortClick={abortUpdates} - abort={showUpdates} - > - - + (showUpdates = false)} + showLoaderOnDisabled={true} + abortClick={abortUpdates} + abort={showUpdates} + > + + {:else if showInstall} - showInstall = false} - showLoaderOnDisabled={true} - > - showInstall = false} - installConfirmClick={installConfirm} - /> - + (showInstall = false)} + showLoaderOnDisabled={true} + > + (showInstall = false)} + installConfirmClick={installConfirm} + /> + {:else if showAll} - { - showAll = false; - refreshView(); - }} - showLoaderOnDisabled={false} - > - - + { + showAll = false; + refreshView(); + }} + showLoaderOnDisabled={false} + > + + +{:else} +
+
+ + (showUpdates = true)} + title="Show updates" + {disabled} + /> + + +
+ {#if !active} + + {/if} + {#if showBetaNews && platform !== "macos"} +
+ NEW: + is now available! + (showBetaNews = false)} + title="Close" + /> +
+ {/if} + {#if showInstallPrompt} +
+ Userscript + {#if scriptChecking} + {showInstallPrompt} + {:else} + {scriptInstalled ? "Installed" : "Detected"}: + + {/if} +
+ {/if} + {#if errorNotification} +
+ {errorNotification} + (errorNotification = undefined)} + title="Clear error" + /> +
+ {/if} +
+
+ {#if loading} + + {:else if inactive} +
Popup inactive on extension page
+ {:else if firstGuide} +
+

Welcome, first use please: 

+ +

to complete the initialization

+
+ {:else if initError} +
+ Something went wrong:  + +
+ {:else if items.length < 1} +
No matched userscripts
+ {:else} +
+ {#each list as item, i (item.filename)} + toggleItem(item)} + /> + {/each} +
+ {/if} +
+ {#if !inactive && platform === "macos"} + + {/if} {/if} diff --git a/src/ext/action-popup/Components/PopupItem.svelte b/src/ext/action-popup/Components/PopupItem.svelte index 01e091de..0afba81c 100644 --- a/src/ext/action-popup/Components/PopupItem.svelte +++ b/src/ext/action-popup/Components/PopupItem.svelte @@ -1,97 +1,97 @@
- -
{name}
- {#if subframe}
SUB
{/if} - + +
{name}
+ {#if subframe}
SUB
{/if} +
diff --git a/src/ext/action-popup/Components/View.svelte b/src/ext/action-popup/Components/View.svelte index 71115f7b..1bd7ad7c 100644 --- a/src/ext/action-popup/Components/View.svelte +++ b/src/ext/action-popup/Components/View.svelte @@ -1,80 +1,89 @@
-
- {headerTitle} - -
-
- {#if loading && showLoaderOnDisabled} - - {:else} -
Slot content is required...
- {/if} -
+
+ {headerTitle} + +
+
+ {#if loading && showLoaderOnDisabled} + + {:else} +
Slot content is required...
+ {/if} +
diff --git a/src/ext/action-popup/Components/Views/AllItemsView.svelte b/src/ext/action-popup/Components/Views/AllItemsView.svelte index a5a59553..20aea85c 100644 --- a/src/ext/action-popup/Components/Views/AllItemsView.svelte +++ b/src/ext/action-popup/Components/Views/AllItemsView.svelte @@ -1,58 +1,56 @@ {#if allItems.length} -
- {#each list as item (item.filename)} - allItemsToggleItem(item)} - /> - {/each} -
+
+ {#each list as item (item.filename)} + allItemsToggleItem(item)} + /> + {/each} +
{:else} -
No valid files found in directory
+
No valid files found in directory
{/if} diff --git a/src/ext/action-popup/Components/Views/InstallView.svelte b/src/ext/action-popup/Components/Views/InstallView.svelte index 9d9cd3fe..8c54b6cc 100644 --- a/src/ext/action-popup/Components/Views/InstallView.svelte +++ b/src/ext/action-popup/Components/Views/InstallView.svelte @@ -1,180 +1,176 @@
- {#if installError} -
- {@html iconError} -
Couldn't install userscript
-

{installError}

-
- {:else if userscript} -
    -
  • {userscript.name}
  • - {#if userscript.description} -
  • {userscript.description}
  • - {/if} - {#if userscript.match} -
  • -
    @match
    - {#each userscript.match as match} -
    {match}
    - {/each} -
  • - {/if} - {#if userscript.include} -
  • -
    @include
    - {#each userscript.include as include} -
    {include}
    - {/each} -
  • - {/if} - {#if userscript.require} -
  • -
    @require
    - {#each userscript.require as require} -
    {require}
    - {/each} -
  • - {/if} - {#if userscript.grant} -
  • -
    @grant
    - {#each userscript.grant as grant} -
    {grant}
    - {/each} -
  • - {/if} -
-
-
{@html iconWarn}
-
Be sure you trust the author before installing. Nefarious code can exploit your security and privacy.
-
-
- - -
- {/if} + {#if installError} +
+ + {@html iconError} +
Couldn't install userscript
+

{installError}

+
+ {:else if userscript} +
    +
  • {userscript.name}
  • + {#if userscript.description} +
  • {userscript.description}
  • + {/if} + {#if userscript.match} +
  • +
    @match
    + {#each userscript.match as match (match)} +
    {match}
    + {/each} +
  • + {/if} + {#if userscript.include} +
  • +
    @include
    + {#each userscript.include as include (include)} +
    {include}
    + {/each} +
  • + {/if} + {#if userscript.require} +
  • +
    @require
    + {#each userscript.require as require (require)} +
    {require}
    + {/each} +
  • + {/if} + {#if userscript.grant} +
  • +
    @grant
    + {#each userscript.grant as grant (grant)} +
    {grant}
    + {/each} +
  • + {/if} +
+
+ +
{@html iconWarn}
+
+ Be sure you trust the author before installing. Nefarious code can + exploit your security and privacy. +
+
+
+ + +
+ {/if}
diff --git a/src/ext/action-popup/Components/Views/UpdateView.svelte b/src/ext/action-popup/Components/Views/UpdateView.svelte index fd121c84..6da55f46 100644 --- a/src/ext/action-popup/Components/Views/UpdateView.svelte +++ b/src/ext/action-popup/Components/Views/UpdateView.svelte @@ -1,88 +1,83 @@ {#if updates.length} - {#each updates as item (item.name)} - -
-
{item.name}
- - Source - - -
- {/each} -

Be sure you trust the author before saving remote code to your device.

- + {#each updates as item (item.name)} + +
+
{item.name}
+ Source + +
+ {/each} +

Be sure you trust the author before saving remote code to your device.

+ {:else} -
- {@html iconUpdate} -
- There are no file updates available -
- -
-
+
+ + {@html iconUpdate} +
+ There are no file updates available +
+ +
+
{/if} diff --git a/src/ext/action-popup/app.css b/src/ext/action-popup/app.css index 8bbc54fe..88f6c725 100644 --- a/src/ext/action-popup/app.css +++ b/src/ext/action-popup/app.css @@ -1,86 +1,97 @@ html { - font-size: 100%; - overflow: hidden; + font-size: 100%; + overflow: hidden; } body { - background-color: var(--color-bg-secondary); - color: var(--text-color-primary); - font: var(--text-medium); - letter-spacing: var(--letter-spacing-medium); - position: relative; - text-rendering: optimizelegibility; - width: 18rem; - -webkit-font-smoothing: antialiased; + background-color: var(--color-bg-secondary); + color: var(--text-color-primary); + font: var(--text-medium); + letter-spacing: var(--letter-spacing-medium); + text-rendering: optimizelegibility; + -webkit-font-smoothing: antialiased; + position: relative; + width: 18rem; + height: 26rem; } -/* attempts to detect iOS */ -@media (hover: none) { - html { - font-size: 125%; - } +/* ios */ +@supports (-webkit-touch-callout: none) { + html { + font-size: 125%; + overflow: visible; + overscroll-behavior: none; + } - body { - width: 100vw; - } -} + /** + * Dynamic Type + * https://support.apple.com/102453 + * https://webkit.org/blog/3709/using-the-system-font-in-web-content/ + * https://developer.apple.com/library/archive/releasenotes/General/WhatsNewInSafari/Articles/Safari_7_0.html#//apple_ref/doc/uid/TP40014305-CH5-SW10 + * https://github.com/w3c/csswg-drafts/issues/3708 + */ + @supports (font: -apple-system-body) { + html { + /* stylelint-disable-next-line declaration-property-value-no-unknown,font-family-no-missing-generic-family-keyword */ + font: -apple-system-title3; /* default 20px */ + } + } -@media screen and (width >= 481px) and (orientation: portrait) and (hover: none) { - body { - width: 18rem; - } -} + body { + width: auto; + min-width: 16rem; + height: auto; + } -@media screen and (width >= 1024px) and (orientation: landscape) and (hover: none) { - body { - width: 18rem; - } -} - -body.ipados { - width: 18rem; - height: 100vh; -} + /* non-fixed width need to stretch */ + @media (width < 320px) { + body { + width: 18rem; + min-height: 16rem; + } + } -body.ipados #app { - height: 100%; + /* Avoid page zoom */ + input, + textarea, + select { + font-size: max(1rem, 16px); + } } -body.ios { - height: 100vh; - width: 100vw; -} - -body.ios #app { - height: 100%; +noscript { + display: block; } -noscript { - display: block; +#app { + display: flex; + flex-direction: column; + height: 100%; + justify-content: space-between; } button { - border: none; - cursor: pointer; - padding: 0; - user-select: none; + border: none; + cursor: pointer; + padding: 0; + user-select: none; } button:disabled { - opacity: var(--opacity-disabled); - pointer-events: none; + opacity: var(--opacity-disabled); + pointer-events: none; } a, .link { - background: none; - color: var(--color-blue); - cursor: pointer; - text-decoration: underline; + background: none; + color: var(--color-blue); + cursor: pointer; + text-decoration: underline; } .truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } diff --git a/src/ext/action-popup/main.js b/src/ext/action-popup/main.js index ed71b808..a5190622 100644 --- a/src/ext/action-popup/main.js +++ b/src/ext/action-popup/main.js @@ -1,39 +1,38 @@ -import "../shared/reset.css"; -import "../shared/variables.css"; +import "@shared/reset.css"; +import "@shared/variables.css"; import "./app.css"; +import { mount } from "svelte"; import App from "./App.svelte"; -if (import.meta.env.MODE === "development") { // vite feat that only import in dev mode - const modules = import.meta.glob("../shared/dev.js", {eager: true}); - window.browser = modules["../shared/dev.js"].browser; - console.info("DEV-ENV", import.meta.env, modules, browser); - // macos popup simulation - const style = document.createElement("style"); - style.textContent = ` +// vite feat that only import in dev mode +if (import.meta.env.MODE === "development") { + try { + const modules = import.meta.glob("@ext/dev.js", { eager: true }); + const browser = modules["/src/ext/shared/dev.js"]["browser"]; + console.debug("DEV-ENV", import.meta.env, modules, browser); + if (!window?.browser?.extension && browser) { + // assign to window simulation WebExtension APIs + window.browser = browser; + // macos popup simulation + const style = document.createElement("style"); + style.textContent = ` body { - top: 20px; - left: 20px; - box-sizing: content-box; - border: 2px solid #0c0e0f; - border-radius: 10px; - box-shadow: 2px 2px 20px rgba(0, 0, 0, 0.2); -} -body:before { - content: ""; - position: absolute; - top: -1px; - left: -1px; - right: -1px; - bottom: -1px; - border: 1px solid #54575a; - border-radius: 9px; + top: 20px; + left: 20px; + box-sizing: content-box; + border: 1px solid light-dark(#bebebe, #585c5f); + border-radius: 10px; + box-shadow: 2px 2px 20px rgba(0, 0, 0, 0.2); + overflow: hidden; } `; - browser.platform === "macos" && document.head.append(style); + browser.platform === "macos" && document.head.append(style); + } + } catch (error) { + console.error(error); + } } -const app = new App({ - target: document.getElementById("app") -}); +const app = mount(App, { target: document.getElementById("app") }); export default app; diff --git a/src/ext/background/main.js b/src/ext/background/main.js index d8a57ee3..761eda72 100644 --- a/src/ext/background/main.js +++ b/src/ext/background/main.js @@ -1,502 +1,608 @@ -import {openExtensionPage} from "../shared/utils.js"; -// eslint-disable-next-line import/no-namespace -import * as settingsStorage from "../shared/settings.js"; +import { contentScriptRegistration, openExtensionPage } from "@ext/utils.js"; +import * as settingsStorage from "@ext/settings.js"; +import { connectNative, sendNativeMessage } from "@ext/native.js"; // first sorts files by run-at value, then by weight value function userscriptSort(a, b) { - // map the run-at values to numeric values - const runAtValues = { - "document-start": 1, - "document-end": 2, - "document-idle": 3 - }; - const runAtA = a.scriptObject["run-at"]; - const runAtB = b.scriptObject["run-at"]; - if (runAtA !== runAtB && runAtValues[runAtA] && runAtValues[runAtB]) { - return runAtValues[runAtA] > runAtValues[runAtB]; - } - return Number(a.scriptObject.weight) < Number(b.scriptObject.weight); -} - -async function readAsDataURL(blob) { - return new Promise(resolve => { - const reader = new FileReader(); - reader.readAsDataURL(blob); - reader.onloadend = () => resolve(reader.result); // base64data - }); + // map the run-at values to numeric values + const runAtValues = { + "document-start": 1, + "document-end": 2, + "document-idle": 3, + }; + const runAtA = a.scriptObject["run-at"]; + const runAtB = b.scriptObject["run-at"]; + if (runAtA !== runAtB && runAtValues[runAtA] && runAtValues[runAtB]) { + return runAtValues[runAtA] > runAtValues[runAtB]; + } + return Number(a.scriptObject.weight) < Number(b.scriptObject.weight); } async function getPlatform() { - let platform = localStorage.getItem("platform"); - if (!platform) { - const message = {name: "REQ_PLATFORM"}; - const response = await browser.runtime.sendNativeMessage(message); - if (!response.platform) { - console.error("Failed to get platform"); - return ""; - } - platform = response.platform; - localStorage.setItem("platform", platform); - } - return platform; + let platform = localStorage.getItem("platform"); + if (!platform) { + const message = { name: "REQ_PLATFORM" }; + const response = await sendNativeMessage(message); + if (!response.platform) { + console.error("Failed to get platform"); + return ""; + } + platform = response.platform; + localStorage.setItem("platform", platform); + } + return platform; } function setClipboard(data, type = "text/plain") { - // future enhancement? - // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write - // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText - const onCopy = e => { - e.stopImmediatePropagation(); - e.preventDefault(); - e.clipboardData.setData(type, data); - document.removeEventListener("copy", onCopy, true); - }; + // future enhancement? + // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write + // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText + const onCopy = (e) => { + e.stopImmediatePropagation(); + e.preventDefault(); + e.clipboardData.setData(type, data); + document.removeEventListener("copy", onCopy, true); + }; - const textarea = document.createElement("textarea"); - textarea.textContent = ""; - document.body.appendChild(textarea); - textarea.select(); - document.addEventListener("copy", onCopy, true); - try { - return document.execCommand("copy"); - } catch (error) { - console.warn("setClipboard failed", error); - document.removeEventListener("copy", onCopy, true); - return false; - } finally { - document.body.removeChild(textarea); - } + const textarea = document.createElement("textarea"); + textarea.textContent = ""; + document.body.appendChild(textarea); + textarea.select(); + document.addEventListener("copy", onCopy, true); + try { + return document.execCommand("copy"); + } catch (error) { + console.warn("setClipboard failed", error); + document.removeEventListener("copy", onCopy, true); + return false; + } finally { + document.body.removeChild(textarea); + } } async function setBadgeCount() { - const clearBadge = () => browser.browserAction.setBadgeText({text: ""}); - // @todo until better introduce in ios, only set badge on macOS - const platform = await getPlatform(); - if (platform !== "macos") return clearBadge(); - // @todo settingsStorage.get("global_exclude_match") - const settings = await settingsStorage.get(["global_active", "toolbar_badge_count"]); - if (settings.global_active === false) return clearBadge(); - if (settings.toolbar_badge_count === false) return clearBadge(); + const clearBadge = () => browser.browserAction.setBadgeText({ text: null }); + // @todo until better introduce in ios, only set badge on macOS + // set a text badge or an empty string in visionOS will cause the extension's icon to no longer be displayed + const platform = await getPlatform(); + if (platform !== "macos") return clearBadge(); + // @todo settingsStorage.get("global_exclude_match") + const settings = await settingsStorage.get([ + "global_active", + "toolbar_badge_count", + ]); + if (settings["global_active"] === false) return clearBadge(); + if (settings["toolbar_badge_count"] === false) return clearBadge(); - const currentTab = await browser.tabs.getCurrent(); - // no active tabs exist (user closed all windows) - if (!currentTab) return clearBadge(); - const url = currentTab.url; - // if url doesn't exist, stop - if (!url) return clearBadge(); - // only check for http/s pages - if (!url.startsWith("http://") && !url.startsWith("https://")) return clearBadge(); - // @todo if url match in global exclude list, clear badge - const frameUrls = new Set(); - const frames = await browser.webNavigation.getAllFrames({tabId: currentTab.id}); - for (let i = 0; i < frames.length; i++) { - const frameUrl = frames[i].url; - if (frameUrl !== url && frameUrl.startsWith("http")) { - frameUrls.add(frameUrl); - } - } - const message = { - name: "POPUP_BADGE_COUNT", - url, - frameUrls: Array.from(frameUrls) - }; - browser.runtime.sendNativeMessage(message, response => { - if (response.error) return console.error(response.error); - const count = response.count; - if (count > 0) { - browser.browserAction.setBadgeText({text: count.toString()}); - } else { - const _url = new URL(url); - if (_url.pathname.endsWith(".user.js")) { - browser.browserAction.setBadgeText({text: "JS"}); - } else { - clearBadge(); - } - } - }); + const currentTab = await browser.tabs.getCurrent(); + // no active tabs exist (user closed all windows) + if (!currentTab) return clearBadge(); + const url = currentTab.url; + // if url doesn't exist, stop + if (!url) return clearBadge(); + // only check for http/s pages + if (!url.startsWith("http://") && !url.startsWith("https://")) + return clearBadge(); + // @todo if url match in global exclude list, clear badge + const frameUrls = new Set(); + const frames = await browser.webNavigation.getAllFrames({ + tabId: currentTab.id, + }); + for (let i = 0; i < frames.length; i++) { + const frameUrl = frames[i].url; + if (frameUrl !== url && frameUrl.startsWith("http")) { + frameUrls.add(frameUrl); + } + } + const message = { + name: "POPUP_BADGE_COUNT", + url, + frameUrls: Array.from(frameUrls), + }; + const response = await sendNativeMessage(message); + if (response?.error) return console.error(response.error); + if (response?.count > 0) { + browser.browserAction.setBadgeText({ text: response.count.toString() }); + } else { + const _url = new URL(url); + if (_url.pathname.endsWith(".user.js")) { + browser.browserAction.setBadgeText({ text: "JS" }); + } else { + clearBadge(); + } + } } -// on startup get declarativeNetRequests -// and set the requests for the session +// on startup set declarativeNetRequest rulesets // should also check and refresh when: // 1. dnr item save event in the page occurs // 2. dnr item toggle event in the page occurs // 3. external editor changes script file content -async function setSessionRules() { - // not supported below safari 15.4 - if (!browser.declarativeNetRequest.updateSessionRules) return; - await clearAllSessionRules(); - const message = {name: "REQ_REQUESTS"}; - const response = await browser.runtime.sendNativeMessage(message); - if (response.error) { - console.error(response.error); - return; - } - // there are no rules to apply - if (!response.length) return; - // loop through response, parse the rules, push to array and log - const rules = []; - for (let i = 0; i < response.length; i++) { - const rule = response[i]; - const code = JSON.parse(rule.code); - // check if an array or single rule - if (Array.isArray(code)) { - code.forEach(r => rules.push(r)); - console.info(`Setting session rule: ${rule.name} (${code.length})`); - } else { - rules.push(code); - console.info(`Setting session rule: ${rule.name}`); - } - } - // generate unique ids for all rules to ensure no repeats - const ids = randomNumberSet(1000, rules.length); - rules.map((rule, index) => rule.id = ids[index]); - try { - await browser.declarativeNetRequest.updateSessionRules({addRules: rules}); - } catch (error) { - console.error(`Error setting session rules: ${error}`); - return; - } - console.info(`Finished setting ${rules.length} session rules`); -} - -async function clearAllSessionRules() { - const rules = await browser.declarativeNetRequest.getSessionRules(); - if (!rules.length) return; - console.info(`Clearing ${rules.length} session rules`); - const ruleIds = rules.map(a => a.id); - await browser.declarativeNetRequest.updateSessionRules({ - removeRuleIds: ruleIds - }); +async function setDNRRulesets() { + // not supported below safari 15.4 + if (!browser.declarativeNetRequest.updateDynamicRules) return; + const message = { name: "REQ_REQUESTS" }; + const response = await sendNativeMessage(message); + if (response.error) { + console.error(response.error); + return; + } + // loop through response, parse the rules, push to array and log + /** @type {import("webextension-polyfill").DeclarativeNetRequest.Rule[]} */ + const addRules = []; + let ruleId = 1; + for (let i = 0; i < response.length; i++) { + if ( + ruleId > + browser.declarativeNetRequest.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES + ) { + console.warn( + "Rules exceed the maximum number, some rules will be ignored", + ); + break; + } + const ruleset = response[i]; + /** @type {Array} */ + let rules; + try { + const res = JSON.parse(ruleset.code); + // check if an array or single rule + if (Array.isArray(res)) { + rules = res; + } else if (typeof res === "object") { + rules = [res]; + } else { + console.warn(`Not a valid DNR ruleset: ${ruleset.name}`); + continue; + } + console.info(`Setting DNR ruleset: ${ruleset.name} (${rules.length})`); + } catch (error) { + console.warn( + `Failed parsed into a valid DNR ruleset: ${ruleset.name}`, + error, + ); + continue; + } + for (const rule of rules) { + // simple check if it is a rule object + if (!rule.action || !rule.condition || !rule.id) { + console.warn("Not a valid DNR rule:", rule); + continue; + } + // set unique ids for all rules to ensure no repeats + rule.id = ruleId++; + addRules.push(rule); + } + } + // remove all then add declarativeNetRequest rules + try { + const oldRules = await browser.declarativeNetRequest.getDynamicRules(); + const removeRuleIds = oldRules.map((rule) => rule.id); + await browser.declarativeNetRequest.updateDynamicRules({ + addRules, + removeRuleIds, + }); + } catch (error) { + return console.error(error); + } + console.info(`Finished setting ${addRules.length} DNR rules`); } -function randomNumberSet(max, count) { - // generates a set of random unique numbers - // returns an array - const numbers = new Set(); - while (numbers.size < count) { - numbers.add((Math.floor(Math.random() * (max - 1 + 1)) + 1)); - } - return [...numbers]; -} - -// the current update logic is similar to setSessionRules() +// the current update logic is similar to setDNRRulesets() // this feature needs a more detailed redesign in the future // https://github.com/quoid/userscripts/issues/453 async function getContextMenuItems() { - // macos exclusive feature - const platform = await getPlatform(); - if (platform !== "macos") return; - // since it's not possible to get a list of currently active menu items - // on update, all context-menu items are cleared, then re-added - // this is done to ensure fresh code changes appear - await browser.menus.removeAll(); - // get the context-menu scripts - const message = {name: "REQ_CONTEXT_MENU_SCRIPTS"}; - const response = await browser.runtime.sendNativeMessage(message); - if (response.error) { - console.error(response.error); - return; - } - // add menus items - const items = response.files?.menu || []; - if (items.length) { - console.info(`Setting ${items.length} context-menu userscripts`); - } - for (let i = 0; i < items.length; i++) { - const item = items[i]; - // context-menu scripts require @match value - // @include values are ignored - if (!item.scriptObject.matches.length) continue; - addContextMenuItem(item); - } + // macos exclusive feature + const platform = await getPlatform(); + if (platform !== "macos") return; + // since it's not possible to get a list of currently active menu items + // on update, all context-menu items are cleared, then re-added + // this is done to ensure fresh code changes appear + await browser.menus.removeAll(); + // get the context-menu scripts + const message = { name: "REQ_CONTEXT_MENU_SCRIPTS" }; + const response = await sendNativeMessage(message); + if (response.error) { + console.error(response.error); + return; + } + // add menus items + const items = response.files?.menu || []; + if (items.length) { + console.info(`Setting ${items.length} context-menu userscripts`); + } + for (let i = 0; i < items.length; i++) { + const item = items[i]; + // context-menu scripts require @match value + // @include values are ignored + if (!item.scriptObject.matches.length) continue; + addContextMenuItem(item); + } } async function addContextMenuItem(userscript) { - // context-menu items persist for a session - // to avoid duplication, when created, save the filename to session storage - const savedItems = sessionStorage.getItem("menu"); - // if the session storage key doesn't exist use empty array - const activeItems = savedItems ? JSON.parse(savedItems) : []; - if (activeItems.indexOf(userscript.scriptObject.filename) !== -1) { - // if already saved, remove it, to get fresh code changes - await browser.menus.remove(userscript.scriptObject.filename); - } - // potential bug? https://developer.apple.com/forums/thread/685273 - // https://stackoverflow.com/q/68431201 - // parse through match values and change pathnames to deal with bug - const patterns = userscript.scriptObject.matches; - patterns.forEach((pattern, index) => { - try { - const url = new URL(pattern); - let pathname = url.pathname; - if (pathname.length > 1 && pathname.endsWith("/")) { - pathname = pathname.slice(0, -1); - } - patterns[index] = `${url.protocol}//${url.hostname}${pathname}`; - } catch (error) { - // prevent breaking when non-url pattern present - } - }); - - browser.menus.create({ - contexts: ["all"], - documentUrlPatterns: patterns, - id: userscript.scriptObject.filename, - title: userscript.scriptObject.name - }, () => { - // add event listener if needed - if (!browser.menus.onClicked.hasListener(contextClick)) { - browser.menus.onClicked.addListener(contextClick); - } - // save the context-menu item reference to sessionStorage - const value = JSON.stringify([userscript.scriptObject.filename]); - sessionStorage.setItem("menu", value); - }); + const patterns = userscript.scriptObject.matches; + browser.menus.create( + { + contexts: ["all"], + documentUrlPatterns: patterns, + id: userscript.scriptObject.filename, + title: userscript.scriptObject.name, + }, + () => { + // add event listener if needed + if (!browser.menus.onClicked.hasListener(contextClick)) { + browser.menus.onClicked.addListener(contextClick); + } + }, + ); } -function contextClick(info, tab) { - // when any created context-menu item is clicked, send message to tab - // the content script for that tag will have the context-menu code - // which will get send back in the response if/when found - const message = {name: "CONTEXT_RUN", menuItemId: info.menuItemId}; - browser.tabs.sendMessage(tab.id, message, response => { - // if code is returned, execute on that tab - if (!response.code) return; - browser.tabs.executeScript(tab.id, { - code: response.code - }); - }); +async function contextClick(info, tab) { + // when any created context-menu item is clicked, send message to tab + // the content script for that tag will have the context-menu code + // which will get send back in the response if/when found + const message = { name: "CONTEXT_RUN", menuItemId: info.menuItemId }; + const response = await browser.tabs.sendMessage(tab.id, message); + // if code is returned, execute on that tab + if (!response.code) return; + browser.tabs.executeScript(tab.id, { code: response.code }); } async function nativeChecks() { - const response = await browser.runtime.sendNativeMessage({name: "NATIVE_CHECKS"}); - if (response.error) { - settingsStorage.set({error_native: response}); - return false; - } - settingsStorage.reset("error_native"); - return true; + const response = await sendNativeMessage({ + name: "NATIVE_CHECKS", + }); + if (response.error) { + settingsStorage.set({ error_native: response }); + return false; + } + settingsStorage.reset("error_native"); + return true; } -// handles messages sent with browser.runtime.sendMessage -function handleMessage(request, sender, sendResponse) { - switch (request.name) { - case "REQ_USERSCRIPTS": { - // get the page url from the content script that sent request - const url = sender.url; - // use frameId to determine if request came from top level window - // if @noframes true, and isTop false, swift layer won't return code - const isTop = sender.frameId === 0; - // send request to swift layer to provide code for page url - const message = {name: "REQ_USERSCRIPTS", url, isTop}; - browser.runtime.sendNativeMessage(message, response => { - // if request failed, send error to content script for logging - if (response.error) return sendResponse(response); - // sort files - response.files.js.sort(userscriptSort); - response.files.css.sort((a, b) => { - return Number(a.weight) < Number(b.weight); - }); - // return sorted files for injection - sendResponse(response); - }); - return true; - } - case "API_CLOSE_TAB": { - const tabId = request.tabId || sender.tab.id; - browser.tabs.remove(tabId, () => sendResponse({success: 1})); - return true; - } - case "API_OPEN_TAB": { - const props = { - active: request.active, - index: sender.tab.index + 1, - url: request.url - }; - browser.tabs.create(props, response => sendResponse(response)); - return true; - } - case "API_ADD_STYLE": { - const tabId = sender.tab.id; - const details = {code: request.css, cssOrigin: "user"}; - browser.tabs.insertCSS(tabId, details, () => { - sendResponse(request.css); - }); - return true; - } - case "API_GET_TAB": { - let tab = null; - if (typeof sender.tab !== "undefined") { - const tabData = sessionStorage.getItem(`tab-${sender.tab.id}`); - try { - // if tabData is null, can still parse it and return that - tab = JSON.parse(tabData); - } catch (error) { - console.error("failed to parse tab data for getTab"); - } - } else { - console.error("unable to deliver tab due to empty tab id"); - } - sendResponse(tab == null ? {} : tab); - break; - } - case "API_SAVE_TAB": { - if (sender.tab != null && sender.tab.id) { - const key = `tab-${sender.tab.id}`; - sessionStorage.setItem(key, JSON.stringify(request.tab)); - sendResponse({success: true}); - } else { - console.error("unable to save tab, empty tab id"); - sendResponse({success: false}); - } - break; - } - case "API_SET_CLIPBOARD": { - const result = setClipboard(request.clipboardData, request.type); - sendResponse(result); - break; - } - case "API_XHR": { - // parse details and set up for XMLHttpRequest - const details = request.details; - const method = details.method ? details.method : "GET"; - const user = details.user || null; - const password = details.password || null; - let body = details.data || null; - if (body != null && details.binary != null) { - const len = body.length; - const arr = new Uint8Array(len); - for (let i = 0; i < len; i++) { - arr[i] = body.charCodeAt(i); - } - body = new Blob([arr], {type: "text/plain"}); - } - // establish a long-lived port connection to content script - const port = browser.tabs.connect(sender.tab.id, { - name: request.xhrPortName - }); - // set up XMLHttpRequest - const xhr = new XMLHttpRequest(); - xhr.withCredentials = (details.user && details.password); - xhr.timeout = details.timeout || 0; - if (details.overrideMimeType) { - xhr.overrideMimeType(details.overrideMimeType); - } - // add required listeners and send result back to the content script - for (const e of request.events) { - if (!details[e]) continue; - xhr[e] = async event => { - // can not send xhr through postMessage - // construct new object to be sent as "response" - const x = { - readyState: xhr.readyState, - response: xhr.response, - responseHeaders: xhr.getAllResponseHeaders(), - responseType: xhr.responseType, - responseURL: xhr.responseURL, - status: xhr.status, - statusText: xhr.statusText, - timeout: xhr.timeout, - withCredentials: xhr.withCredentials - }; - // only include responseText when needed - if (["", "text"].indexOf(xhr.responseType) !== -1) { - x.responseText = xhr.responseText; - } - // only process when xhr is complete and data exist - if (xhr.readyState === 4 && xhr.response !== null) { - // need to convert arraybuffer data to postMessage - if (xhr.responseType === "arraybuffer") { - const arr = Array.from(new Uint8Array(xhr.response)); - x.response = arr; - } - // need to convert blob data to postMessage - if (xhr.responseType === "blob") { - const base64data = await readAsDataURL(xhr.response); - x.response = { - data: base64data, - type: xhr.responseType - }; - } - } - port.postMessage({name: e, event, response: x}); - }; - } - xhr.open(method, details.url, true, user, password); - xhr.responseType = details.responseType || ""; - if (details.headers) { - for (const key in details.headers) { - if (!key.startsWith("Proxy-") && !key.startsWith("Sec-")) { - const val = details.headers[key]; - xhr.setRequestHeader(key, val); - } - } - } - // receive messages from content script and process them - port.onMessage.addListener(msg => { - if (msg.name === "ABORT") xhr.abort(); - if (msg.name === "DISCONNECT") port.disconnect(); - }); - // handle port disconnect and clean tasks - port.onDisconnect.addListener(p => { - if (p?.error) { - console.error(`port disconnected due to an error: ${p.error.message}`); - } - }); - xhr.send(body); - // if onloadend not set in xhr details - // onloadend event won't be passed to content script - // if that happens port DISCONNECT message won't be posted - // if details lacks onloadend attach listener - if (!details.onloadend) { - xhr.onloadend = event => { - port.postMessage({name: "onloadend", event}); - }; - } - break; - } - case "REFRESH_SESSION_RULES": { - setSessionRules(); - break; - } - case "REFRESH_CONTEXT_MENU_SCRIPTS": { - getContextMenuItems(); - break; - } - } +/** + * Handles messages sent with `browser.runtime.sendMessage` + * Make sure not to return `undefined` or `rejection`, otherwise the reply may never be delivered + * @see {@link https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage#listener} + * @type {Parameters[0]} + * @returns {Promise<{status: "pending"|"fulfilled"|"rejected", result?: any}>} + */ +async function handleMessage(message, sender) { + switch (message.name) { + case "REQ_USERSCRIPTS": { + // get the page url from the content script that sent request + const url = sender.url; + // use frameId to determine if request came from top level window + // if @noframes true, and isTop false, swift layer won't return code + const isTop = sender.frameId === 0; + // send request to swift layer to provide code for page url + const message = { name: "REQ_USERSCRIPTS", url, isTop }; + try { + const response = await sendNativeMessage(message); + if (import.meta.env.MODE === "development") { + console.debug("REQ_USERSCRIPTS", message, response); + } + // if request failed, send error to content script for logging + if (response.error) return response; + // sort files + response.files.js.sort(userscriptSort); + response.files.css.sort((a, b) => Number(a.weight) < Number(b.weight)); + // return sorted files for injection + return response; + } catch (error) { + console.error(error); + // @ts-ignore -- ignore for now and will reconstruct this in the future. + return { error: String(error) }; + } + } + case "API_CLOSE_TAB": { + try { + await browser.tabs.remove(message.tabId || sender.tab.id); + return { status: "fulfilled" }; + } catch (error) { + console.error(message, sender, error); + return { status: "rejected", result: String(error) }; + } + } + case "API_OPEN_TAB": { + try { + const tab = await browser.tabs.create({ + active: message.active, + index: sender.tab.index + 1, + url: message.url, + }); + return { status: "fulfilled", result: tab }; + } catch (error) { + console.error(message, sender, error); + return { status: "rejected", result: String(error) }; + } + } + case "API_ADD_STYLE": { + try { + await browser.tabs.insertCSS(sender.tab.id, { + code: message.css, + cssOrigin: "user", + }); + return { status: "fulfilled" }; + } catch (error) { + console.error(message, sender, error); + return { status: "rejected", result: String(error) }; + } + } + case "API_GET_TAB": { + if (typeof sender.tab === "undefined") { + const error = "unable to deliver tab due to empty tab id"; + return { status: "rejected", result: error }; + } + try { + const tabData = sessionStorage.getItem(`tab-${sender.tab.id}`); + // if tabData is null, can still parse it and return that + const tabObj = JSON.parse(tabData); + return { status: "fulfilled", result: tabObj }; + } catch (error) { + console.error("failed to parse tab data for getTab", error); + return { status: "rejected", result: String(error) }; + } + } + case "API_SAVE_TAB": { + if (sender.tab != null && sender.tab.id) { + const key = `tab-${sender.tab.id}`; + sessionStorage.setItem(key, JSON.stringify(message.tabObj)); + return { status: "fulfilled" }; + } else { + const error = "unable to save tab, empty tab id"; + return { status: "rejected", result: String(error) }; + } + } + case "API_SET_CLIPBOARD": { + const result = setClipboard(message.clipboardData, message.type); + return { status: "fulfilled", result }; + } + case "API_XHR": { + try { + // initializing an xhr instance + const xhr = new XMLHttpRequest(); + // establish a long-lived port connection to content script + /** @type {import("../global.d.ts").TypeBackground.XHRPort} */ + const port = browser.tabs.connect(sender.tab.id, { + name: message.xhrPortName, + }); + // receive messages from content script and process them + port.onMessage.addListener((msg) => { + if (msg.name === "ABORT") xhr.abort(); + if (msg.name === "DISCONNECT") port.disconnect(); + }); + // handle port disconnect and clean tasks + port.onDisconnect.addListener((p) => { + if (p?.error) { + console.error( + `port disconnected due to an error: ${p.error.message}`, + ); + } + }); + // parse details and set up for xhr instance + /** @type {TypeExtMessages.XHRTransportableDetails} */ + const details = message.details; + /** @type {Parameters[0]} */ + const method = details.method || "GET"; + /** @type {Parameters[1]} */ + const url = details.url; + /** @type {Parameters[3]} */ + const user = details.user || null; + /** @type {Parameters[4]} */ + const password = details.password || null; + /** @type {Parameters[0]} */ + let body = null; + if (typeof details.data === "object") { + /** @type {TypeExtMessages.XHRProcessedData} */ + const data = details.data; + if (typeof data.data === "string") { + if (data.type === "Text") { + // deprecate once body supports more data types + // the `binary` key will no longer needed + if (details.binary) { + const binaryString = data.data; + const view = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + view[i] = binaryString.charCodeAt(i); + } + body = view; + } else { + body = data.data; + } + } + if (data.type === "Document") { + body = data.data; + if (!("content-type" in details.headers)) { + details.headers["content-type"] = data.mime; + } + } + if (data.type === "URLSearchParams") { + body = new URLSearchParams(data.data); + } + } + if (Array.isArray(data.data)) { + if ( + data.type === "ArrayBuffer" || + data.type === "ArrayBufferView" + ) { + body = new Uint8Array(data.data); + } + if (data.type === "Blob") { + body = new Uint8Array(data.data); + if (!("content-type" in details.headers)) { + details.headers["content-type"] = data.mime; + } + } + if (data.type === "FormData") { + body = new FormData(); + for (const [k, v] of data.data) { + if (typeof v === "string") { + body.append(k, v); + } else { + const view = new Uint8Array(v.data); + body.append( + k, + new File([view], v.name, { + type: v.mime, + lastModified: v.lastModified, + }), + ); + } + } + } + } + } + // xhr instances automatically filter out unexpected user values + xhr.timeout = details.timeout; + xhr.responseType = details.responseType; + // record parsed values for subsequent use + const responseType = xhr.responseType; + // avoid unexpected behavior of legacy defaults such as parsing XML + if (responseType === "") xhr.responseType = "text"; + // transfer to content script via arraybuffer and then parse to blob + if (responseType === "blob") xhr.responseType = "arraybuffer"; + // transfer to content script via text and then parse to document + if (responseType === "document") xhr.responseType = "text"; + // add required listeners and send result back to the content script + if (details.hasUploadHandlers) { + for (const handler of Object.keys(details.hasUploadHandlers)) { + /** @param {ProgressEvent} event */ + xhr.upload[handler] = async (event) => { + /** @type {TypeExtMessages.XHRProgress} */ + const progress = { + lengthComputable: event.lengthComputable, + loaded: event.loaded, + total: event.total, + }; + port.postMessage({ handler, progress }); + }; + } + } + const handlers = details.hasHandlers ?? {}; + for (const handler of Object.keys(handlers)) { + xhr[handler] = async () => { + // can not send xhr through postMessage + // construct new object to be sent as "response" + /** @type {TypeExtMessages.XHRTransportableResponse} */ + const response = { + contentType: undefined, // non-standard + readyState: xhr.readyState, + response: xhr.response, + responseHeaders: xhr.getAllResponseHeaders(), + responseType, + responseURL: xhr.responseURL, + status: xhr.status, + statusText: xhr.statusText, + timeout: xhr.timeout, + }; + // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/response#value + if (xhr.readyState < xhr.DONE && xhr.responseType !== "text") { + response.response = null; + } + // get content-type when headers received + if (xhr.readyState >= xhr.HEADERS_RECEIVED) { + response.contentType = xhr.getResponseHeader("Content-Type"); + } + // only process when xhr is complete and data exist + // note the status of the last `progress` event in Safari is DONE/4 + // exclude this event to avoid unnecessary processing and transmission + if ( + xhr.readyState === xhr.DONE && + xhr.response !== null && + handler !== "onprogress" + ) { + // need to convert arraybuffer data to postMessage + if ( + xhr.responseType === "arraybuffer" && + xhr.response instanceof ArrayBuffer + ) { + const buffer = xhr.response; + response.response = Array.from(new Uint8Array(buffer)); + } + } + port.postMessage({ handler, response }); + }; + } + // if onloadend not set in xhr details + // onloadend event won't be passed to content script + // if that happens port DISCONNECT message won't be posted + // so if details lacks onloadend then attach the listener + if (!handlers.onloadend) { + xhr.onloadend = () => { + port.postMessage({ handler: "onloadend" }); + }; + } + if (details.overrideMimeType) { + xhr.overrideMimeType(details.overrideMimeType); + } + xhr.open(method, url, true, user, password); + // must set headers after `xhr.open()`, but before `xhr.send()` + if (typeof details.headers === "object") { + for (const [key, val] of Object.entries(details.headers)) { + xhr.setRequestHeader(key, val); + } + } + xhr.send(body); + } catch (error) { + console.error(error); + } + return { status: "fulfilled" }; + } + case "REFRESH_DNR_RULES": { + setDNRRulesets(); + break; + } + case "REFRESH_CONTEXT_MENU_SCRIPTS": { + getContextMenuItems(); + break; + } + case "WEB_USERJS_POPUP": { + const currentTab = await browser.tabs.getCurrent(); + if (currentTab.id === sender.tab.id) { + browser.browserAction.openPopup(); + } + break; + } + } } browser.runtime.onInstalled.addListener(async () => { - nativeChecks(); + await nativeChecks(); + const enable = await settingsStorage.get("augmented_userjs_install"); + await contentScriptRegistration(enable); }); browser.runtime.onStartup.addListener(async () => { - setSessionRules(); - getContextMenuItems(); + setDNRRulesets(); + getContextMenuItems(); }); // listens for messages from content script, popup and page browser.runtime.onMessage.addListener(handleMessage); // set the badge count browser.tabs.onActivated.addListener(setBadgeCount); -browser.windows.onFocusChanged.addListener(async windowId => { - if (windowId < 1) { // lose focus - return; - } - nativeChecks(); - setBadgeCount(); - setSessionRules(); - getContextMenuItems(); +browser.windows.onFocusChanged.addListener(async (windowId) => { + if (windowId < 1) { + // lose focus + return; + } + nativeChecks(); + setBadgeCount(); + setDNRRulesets(); + getContextMenuItems(); }); browser.webNavigation.onCompleted.addListener(setBadgeCount); // handle native app messages -const port = browser.runtime.connectNative(); -port.onMessage.addListener(message => { - // console.info(message); // DEBUG - if (message.name === "SAVE_LOCATION_CHANGED") { - openExtensionPage(); - if (message?.userInfo?.returnApp === true) browser.runtime.sendNativeMessage({name: "OPEN_APP"}); - } - // if (message.name === "OPEN_EXTENSION_PAGE") { - // openExtensionPage(); - // } +const port = connectNative(); +port.onMessage.addListener(async (message) => { + // console.info(message); // DEBUG + if (message.name === "SAVE_LOCATION_CHANGED") { + await openExtensionPage(); + if (message?.userInfo?.returnApp === true) { + sendNativeMessage({ name: "OPEN_APP" }); + } + } + // if (message.name === "OPEN_EXTENSION_PAGE") { + // openExtensionPage(); + // } }); diff --git a/src/ext/content-scripts/api.js b/src/ext/content-scripts/api.js index 3907cb98..e2bf4cdb 100644 --- a/src/ext/content-scripts/api.js +++ b/src/ext/content-scripts/api.js @@ -1,232 +1,503 @@ -function setValue(key, value) { - if (typeof key !== "string" || !key.length) { - return console.error("setValue invalid key arg"); - } - if (value == null) { - return console.error("setValue invalid value arg"); - } - return new Promise(resolve => { - const item = {}; - item[`${this.US_filename}---${key}`] = value; - browser.storage.local.set(item, () => resolve({success: 1})); - }); -} - -function getValue(key, defaultValue) { - if (typeof key !== "string" || !key.length) { - return console.error("getValue invalid key arg"); - } - const prefixedKey = `${this.US_filename}---${key}`; - return new Promise(resolve => { - browser.storage.local.get(prefixedKey, item => { - if (Object.keys(item).length === 0) { - if (defaultValue != null) { - resolve(defaultValue); - } else { - resolve(undefined); - } - } else { - resolve(Object.values(item)[0]); - } - }); - }); -} - -function deleteValue(key) { - if (typeof key !== "string" || !key.length) { - return console.error("deleteValue missing key arg"); - } - return new Promise(resolve => { - const prefixedKey = `${this.US_filename}---${key}`; - browser.storage.local.remove(prefixedKey, () => { - resolve({success: 1}); - }); - }); -} - -function listValues() { - return new Promise(resolve => { - const prefix = `${this.US_filename}---`; - const keys = []; - browser.storage.local.get().then(items => { - for (const key in items) { - if (key.startsWith(prefix)) { - const k = key.replace(prefix, ""); - keys.push(k); - } - } - resolve(keys); - }); - }); -} - -function openInTab(url, openInBackground = false) { - if (!url) return console.error("openInTab missing url arg"); - return new Promise(resolve => { - const message = { - name: "API_OPEN_TAB", - url, - active: !openInBackground - }; - browser.runtime.sendMessage(message, response => resolve(response)); - }); -} - -function getTab() { - return new Promise(resolve => { - const message = {name: "API_GET_TAB"}; - browser.runtime.sendMessage(message, response => { - resolve(response); - }); - }); -} - -function saveTab(tab) { - if (tab == null) return console.error("saveTab invalid arg"); - return new Promise(resolve => { - const message = { - name: "API_SAVE_TAB", - tab - }; - browser.runtime.sendMessage(message, response => { - resolve(response); - }); - }); -} - -function closeTab(tabId) { - return new Promise(resolve => { - const message = { - name: "API_CLOSE_TAB", - tabId - }; - browser.runtime.sendMessage(message, response => resolve(response)); - }); -} - -function addStyle(css) { - if (typeof css !== "string") { - return console.error("addStyle invalid css arg"); - } - return new Promise(resolve => { - const message = { - name: "API_ADD_STYLE", - css - }; - browser.runtime.sendMessage(message, response => resolve(response)); - }); -} - -function setClipboard(clipboardData, type) { - return new Promise(resolve => { - const message = { - name: "API_SET_CLIPBOARD", - clipboardData, - type - }; - browser.runtime.sendMessage(message, response => { - resolve(response); - }); - }); -} - -function xhr(details) { - if (details == null) return console.error("xhr invalid details arg"); - if (!details.url) return console.error("xhr details missing url key"); - // generate random port name for single xhr - const xhrPortName = Math.random().toString(36).substring(1, 9); - // strip out functions from details - const detailsParsed = JSON.parse(JSON.stringify(details)); - // get all the "on" events from XMLHttpRequest object - const events = []; - for (const k in XMLHttpRequest.prototype) { - if (k.slice(0, 2) === "on") events.push(k); - } - // check which functions are included in the original details object - // add a bool to indicate if event listeners should be attached - for (const e of events) { - if (typeof details[e] === "function") detailsParsed[e] = true; - } - // define return method, will be populated after port is established - const response = { - abort: () => console.error("xhr has not yet been initialized") - }; - // port listener, most of the messaging logic goes here - const listener = port => { - if (port.name !== xhrPortName) return; - port.onMessage.addListener(async msg => { - if ( - events.includes(msg.name) - && typeof details[msg.name] === "function" - ) { - // process xhr response - const r = msg.response; - // only process when xhr is complete and data exist - if (r.readyState === 4 && r.response !== null) { - if (r.responseType === "arraybuffer") { - // arraybuffer responses had their data converted in background - // convert it back to arraybuffer - try { - const buffer = new Uint8Array(r.response).buffer; - r.response = buffer; - } catch (err) { - console.error("error parsing xhr arraybuffer", err); - } - } else if (r.responseType === "blob" && r.response.data) { - // blob responses had their data converted in background - // convert it back to blob - const resp = await fetch(r.response.data); - const b = await resp.blob(); - r.response = b; - } - } - // call userscript method - details[msg.name](msg.response); - } - // all messages received - // tell background it's safe to close port - if (msg.name === "onloadend") { - port.postMessage({name: "DISCONNECT"}); - } - }); - - // handle port disconnect and clean tasks - port.onDisconnect.addListener(p => { - if (p?.error) { - console.error(`port disconnected due to an error: ${p.error.message}`); - } - browser.runtime.onConnect.removeListener(listener); - }); - // fill the method returned to the user script - response.abort = () => port.postMessage({name: "ABORT"}); - }; - // wait for the background to establish a port connection - browser.runtime.onConnect.addListener(listener); - // pass the basic information to the background through a common message - const message = { - name: "API_XHR", - details: detailsParsed, - xhrPortName, - events - }; - browser.runtime.sendMessage(message); - return response; +async function setValue(key, value) { + if (typeof key !== "string" || !key.length) { + return Promise.reject(new Error("setValue invalid key arg")); + } + const sid = this.US_filename; + if (typeof sid !== "string" || !sid.length) { + return Promise.reject(new Error("setValue invalid call")); + } + const item = {}; + item[`${sid}---${key}`] = value; + return browser.storage.local.set(item); +} + +async function getValue(key, defaultValue) { + if (typeof key !== "string" || !key.length) { + return Promise.reject(new Error("getValue invalid key arg")); + } + const sid = this.US_filename; + if (typeof sid !== "string" || !sid.length) { + return Promise.reject(new Error("getValue invalid call")); + } + const prefixedKey = `${sid}---${key}`; + const results = await browser.storage.local.get(prefixedKey); + if (prefixedKey in results) return results[prefixedKey]; + if (defaultValue !== undefined) return defaultValue; + return undefined; +} + +async function deleteValue(key) { + if (typeof key !== "string" || !key.length) { + return Promise.reject(new Error("deleteValue missing key arg")); + } + const sid = this.US_filename; + if (typeof sid !== "string" || !sid.length) { + return Promise.reject(new Error("deleteValue invalid call")); + } + const prefixedKey = `${sid}---${key}`; + return browser.storage.local.remove(prefixedKey); +} + +async function listValues() { + const sid = this.US_filename; + if (typeof sid !== "string" || !sid.length) { + return Promise.reject(new Error("listValues invalid call")); + } + const prefix = `${sid}---`; + const results = await browser.storage.local.get(); + const keys = []; + for (const key in results) { + key.startsWith(prefix) && keys.push(key.slice(prefix.length)); + } + return keys; +} + +async function sendMessageProxy(message) { + try { + /** @type {{status: "fulfilled"|"rejected", result: any}} */ + const response = await browser.runtime.sendMessage(message); + if (response.status === "fulfilled") { + return response.result; + } else { + return Promise.reject(response.result); + } + } catch (error) { + console.error(error); + return Promise.reject(error); + } +} + +async function openInTab(url, openInBackground = false) { + try { + new URL(url); + } catch (error) { + return Promise.reject(error); + } + return sendMessageProxy({ + name: "API_OPEN_TAB", + url, + active: !openInBackground, + }); +} + +async function closeTab(tabId) { + return sendMessageProxy({ name: "API_CLOSE_TAB", tabId }); +} + +async function getTab() { + return sendMessageProxy({ name: "API_GET_TAB" }); +} + +async function saveTab(tabObj) { + if (tabObj == null) return Promise.reject(new Error("saveTab invalid arg")); + return sendMessageProxy({ name: "API_SAVE_TAB", tabObj }); +} + +async function addStyle(css) { + if (typeof css !== "string" || !css.length) { + return Promise.reject(new Error("addStyle invalid css arg")); + } + return sendMessageProxy({ name: "API_ADD_STYLE", css }); +} + +async function setClipboard(clipboardData, type) { + return sendMessageProxy({ + name: "API_SET_CLIPBOARD", + clipboardData, + type, + }); +} + +/** + * Restore `response.response` to required `responseType` + * @param {TypeExtMessages.XHRTransportableResponse} msgResponse + * @param {TypeExtMessages.XHRResponse} response + */ +function xhrResponseProcessor(msgResponse, response) { + const res = msgResponse; + /** + * only include responseXML when needed + * NOTE: Only add implementation at this time, not enable, to avoid + * unnecessary calculations, and this legacy default behavior is not + * recommended, users should explicitly use `responseType: "document"` + * to obtain it. + if (res.responseType === "" && typeof res.response === "string") { + const mimeTypes = [ + "text/xml", + "application/xml", + "application/xhtml+xml", + "image/svg+xml", + ]; + for (const mimeType of mimeTypes) { + if (res.contentType.includes(mimeType)) { + const parser = new DOMParser(); + res.responseXML = parser.parseFromString(res.response, "text/xml"); + break; + } + } + } + */ + if (res.responseType === "arraybuffer" && Array.isArray(res.response)) { + // arraybuffer responses had their data converted in background + // convert it back to arraybuffer + try { + response.response = new Uint8Array(res.response).buffer; + } catch (err) { + console.error("error parsing xhr arraybuffer", err); + } + } + if (res.responseType === "blob" && Array.isArray(res.response)) { + // blob responses had their data converted in background + // convert it back to blob + try { + const typedArray = new Uint8Array(res.response); + const type = res.contentType ?? ""; + response.response = new Blob([typedArray], { type }); + } catch (err) { + console.error("error parsing xhr blob", err); + } + } + if (res.responseType === "document" && typeof res.response === "string") { + // document responses had their data converted in background + // convert it back to document + try { + const parser = new DOMParser(); + const mimeType = res.contentType.includes("text/html") + ? "text/html" + : "text/xml"; + response.response = parser.parseFromString(res.response, mimeType); + response.responseXML = response.response; + } catch (err) { + console.error("error parsing xhr document", err); + } + } +} + +/** + * Process data into a transportable object + * @param {Parameters[0]} data + * @returns {Promise} + */ +async function xhrDataProcessor(data) { + if (typeof data === "undefined") return undefined; + if (typeof data === "string") { + return { data, type: "Text" }; + } + if (data instanceof Document) { + if (data instanceof XMLDocument) { + try { + return { + data: new XMLSerializer().serializeToString(data), + type: "Document", + mime: data.contentType || "text/xml", + }; + } catch (error) { + console.error( + "XML serialization failed, the data will be omitted", + error, + ); + } + } else { + let html = data.documentElement.outerHTML; + if (data.doctype) { + html = `` + html; + } + return { + data: html, + type: "Document", + mime: data.contentType || "text/html", + }; + } + } + if (data instanceof Blob) { + try { + const buffer = await data.arrayBuffer(); + return { + data: Array.from(new Uint8Array(buffer)), + type: "Blob", + mime: data.type, + }; + } catch (error) { + throw Error("Document serialization failed, the data will be omitted", { + cause: error, + }); + } + } + if (data instanceof ArrayBuffer) { + return { + data: Array.from(new Uint8Array(data)), + type: "ArrayBuffer", + }; + } + if (ArrayBuffer.isView(data)) { + return { + data: Array.from(new Uint8Array(data.buffer)), + type: "ArrayBufferView", + }; + } + if (data instanceof FormData) { + /** @type {TypeExtMessages.XHRProcessedFormData} */ + const entries = []; + for (const [k, v] of data.entries()) { + if (typeof v === "string") { + entries.push([k, v]); + continue; + } else { + const buffer = await v.arrayBuffer(); + entries.push([ + k, + { + data: Array.from(new Uint8Array(buffer)), + lastModified: v.lastModified, + name: v.name, + mime: v.type, + }, + ]); + } + } + return { data: entries, type: "FormData" }; + } + if (data instanceof URLSearchParams) { + return { data: data.toString(), type: "URLSearchParams" }; + } + throw Error("Unexpected data type, the data will be omitted"); +} + +/** + * @param {Object} details + * @param {Object} control + * @param {{resolve: Function, reject: Function}=} promise + * @returns {Promise} + */ +async function xhr(details, control, promise) { + if (details == null) return console.error("xhr invalid details arg"); + if (!details.url) return console.error("xhr details missing url key"); + // define control method, will be replaced after port is established + control.abort = () => console.error("xhr has not yet been initialized"); + // depreciation notice + if (details.binary) { + console.warn( + "Please make sure your xhr `data` is a binary-string since you have set the `binary` true, however this legacy format is no longer recommended.", + "The `binary` key is deprecated and will be removed in the future, use binary data objects such as `Blob`, `ArrayBuffer`, `TypedArray`, etc. instead.", + ); + } + // can not send details (func, blob, etc.) through message + // construct a new processed object send to background page + /** @type {TypeExtMessages.XHRTransportableDetails} */ + const detailsParsed = { + binary: Boolean(details.binary), + data: undefined, + headers: {}, + method: String(details.method), + overrideMimeType: String(details.overrideMimeType), + password: String(details.password), + responseType: details.responseType, + timeout: Number(details.timeout), + url: String(details.url), + user: String(details.user), + hasHandlers: {}, + hasUploadHandlers: {}, + }; + // preprocess data key + try { + detailsParsed.data = await xhrDataProcessor(details.data); + } catch (error) { + console.error(error); + } + // preprocess headers key + if (typeof details.headers === "object") { + for (const [k, v] of Object.entries(details.headers)) { + detailsParsed.headers[k.toLowerCase()] = v; + } + } + // preprocess handlers + /** + * Record the handlers existing in details to a new object + * to avoid modifying the original object, and to prevent + * the original object from being changed by user scripts + * @type {TypeExtMessages.XHRHandlersObj} + */ + const handlers = {}; + /** @type {TypeExtMessages.XHRHandlers} */ + const XHRHandlers = [ + "onreadystatechange", + "onloadstart", + "onprogress", + "onabort", + "onerror", + "onload", + "ontimeout", + "onloadend", + ]; + for (const handler of XHRHandlers) { + // check which handlers are included in the original details object + if ( + handler in XMLHttpRequest.prototype && + typeof details[handler] === "function" + ) { + // add a bool to indicate if event listeners should be attached + detailsParsed.hasHandlers[handler] = true; + // record to the new object + handlers[handler] = details[handler]; + } + } + // preprocess upload handlers + /** @type {TypeExtMessages.XHRUploadHandlersObj} */ + const uploadHandlers = {}; + /** @type {TypeExtMessages.XHRUploadHandlers} */ + const XHRUploadHandlers = [ + "onabort", + "onerror", + "onload", + "onloadend", + "onloadstart", + "onprogress", + "ontimeout", + ]; + if (typeof details.upload === "object") { + for (const handler of XHRUploadHandlers) { + if ( + handler in XMLHttpRequestEventTarget.prototype && + typeof details.upload[handler] === "function" + ) { + detailsParsed.hasUploadHandlers[handler] = true; + uploadHandlers[handler] = details.upload[handler]; + } + } + } + // resolving asynchronous xmlHttpRequest + if (promise) { + detailsParsed.hasHandlers.onloadend = true; + const _onloadend = handlers.onloadend; + handlers.onloadend = (response) => { + promise.resolve(response); + _onloadend?.(response); + }; + } + // make sure to listen to XHR.DONE events only once, to avoid processing + // and transmitting the same response data multiple times + if (detailsParsed.hasHandlers.onreadystatechange) { + delete detailsParsed.hasHandlers.onload; + delete detailsParsed.hasHandlers.onloadend; + } + if (detailsParsed.hasHandlers.onload) { + delete detailsParsed.hasHandlers.onloadend; + } + // generate random port name for single xhr + const xhrPortName = Math.random().toString(36).substring(1, 9); + /** + * port listener, most of the messaging logic goes here + * @type {Parameters[0]} + * @param {import("../global.d.ts").TypeContentScripts.XHRPort} port + */ + const listener = (port) => { + if (port.name !== xhrPortName) return; + // handle port messages + port.onMessage.addListener(async (msg) => { + const handler = msg.handler; + // handle upload progress + if ( + "progress" in msg && + detailsParsed.hasUploadHandlers[handler] && + typeof uploadHandlers[handler] === "function" + ) { + // call userscript handler + uploadHandlers[handler](msg.progress); + return; + } + // handle download events + if ( + "response" in msg && + detailsParsed.hasHandlers[handler] && + typeof handlers[handler] === "function" + ) { + // process xhr response + /** @type {TypeExtMessages.XHRTransportableResponse} */ + const msgResponse = msg.response; + /** @type {TypeExtMessages.XHRResponse} */ + const response = msgResponse; + // only include responseText when needed + if (["", "text"].includes(response.responseType)) { + response.responseText = response.response; + } + // only process when xhr is complete and data exist + if (response.readyState === 4 && response.response !== null) { + xhrResponseProcessor(msgResponse, response); + } + // call userscript handler + handlers[handler](response); + // call the deleted XHR.DONE handlers above + if (response.readyState === 4) { + if (handler === "onreadystatechange") { + if (typeof handlers.onload === "function") { + handlers.onload(response); + } + if (typeof handlers.onloadend === "function") { + handlers.onloadend(response); + } + } else if (handler === "onload") { + if (typeof handlers.onloadend === "function") { + handlers.onloadend(response); + } + } + } + } + // all messages received + if (handler === "onloadend") { + // tell background it's safe to close port + port.postMessage({ name: "DISCONNECT" }); + } + }); + // handle port disconnect and clean tasks + port.onDisconnect.addListener((p) => { + if (p?.error) { + console.error(`port disconnected due to an error: ${p.error.message}`); + } + browser.runtime.onConnect.removeListener(listener); + }); + // fill the method returned to the user script + control.abort = () => port.postMessage({ name: "ABORT" }); + }; + // wait for the background to establish a port connection + browser.runtime.onConnect.addListener(listener); + // pass the basic information to the background through a common message + const message = { + name: "API_XHR", + details: detailsParsed, + xhrPortName, + }; + sendMessageProxy(message); +} + +function xmlHttpRequest(details) { + let promise; + const control = new Promise((resolve, reject) => { + promise = { resolve, reject }; + }); + xhr(details, control, promise); + return control; +} + +function GM_xmlhttpRequest(details) { + const control = {}; + xhr(details, control); + return control; } export default { - setValue, - getValue, - listValues, - deleteValue, - openInTab, - getTab, - saveTab, - closeTab, - addStyle, - setClipboard, - // notification, - // registerMenuCommand, - // getResourceUrl, - xmlHttpRequest: xhr, - GM_xmlhttpRequest: xhr + setValue, + getValue, + listValues, + deleteValue, + openInTab, + getTab, + saveTab, + closeTab, + addStyle, + setClipboard, + // notification, + // registerMenuCommand, + // getResourceUrl, + xmlHttpRequest, + GM_xmlhttpRequest, }; diff --git a/src/ext/content-scripts/entry-dot-user-js.js b/src/ext/content-scripts/entry-dot-user-js.js new file mode 100644 index 00000000..66757ff0 --- /dev/null +++ b/src/ext/content-scripts/entry-dot-user-js.js @@ -0,0 +1,12 @@ +async function initialize() { + // avoid duplicate injection of content scripts + if (window["CS_ENTRY_DOT_USER_JS"]) return; + window["CS_ENTRY_DOT_USER_JS"] = 1; + // check user settings + const key = "US_AUGMENTED_USERJS_INSTALL"; + if ((await browser.storage.local.get(key))[key] === false) return; + // actual execution content + browser.runtime.sendMessage({ name: "WEB_USERJS_POPUP" }); +} + +initialize(); diff --git a/src/ext/content-scripts/entry-script-market.js b/src/ext/content-scripts/entry-script-market.js new file mode 100644 index 00000000..45ba17d6 --- /dev/null +++ b/src/ext/content-scripts/entry-script-market.js @@ -0,0 +1,63 @@ +let url; + +async function injection() { + const tabUrl = new URL(location.href); + let links; + if (tabUrl.hostname.endsWith("greasyfork.org")) { + links = document.querySelectorAll( + '#install-area a.install-link[data-install-format="js"]', + ); + } + for (const link of links) { + if (link["href"]) url = link["href"]; + link.addEventListener( + "click", + (e) => { + e.stopImmediatePropagation(); + e.preventDefault(); + browser.runtime.sendMessage({ name: "WEB_USERJS_POPUP" }); + }, + true, + ); + } +} + +async function listeners() { + /** + * handle messages from background, popup, etc... + * @type {import("webextension-polyfill").Runtime.OnMessageListener} + */ + const handleMessage = async (message) => { + if (import.meta.env.MODE === "development") { + console.debug(message, url); + } + if (message === "TAB_CLICK_USERJS") { + return url; + } + }; + /** Dynamically remove listeners to avoid memory leaks */ + if (document.visibilityState === "visible") { + browser.runtime.onMessage.addListener(handleMessage); + } + document.addEventListener("visibilitychange", () => { + if (document.hidden) { + browser.runtime.onMessage.removeListener(handleMessage); + } else { + browser.runtime.onMessage.addListener(handleMessage); + } + }); +} + +async function initialize() { + // avoid duplicate injection of content scripts + if (window["CS_ENTRY_SCRIPT_MARKET"]) return; + window["CS_ENTRY_SCRIPT_MARKET"] = 1; + // check user settings + const key = "US_AUGMENTED_USERJS_INSTALL"; + if ((await browser.storage.local.get(key))[key] === false) return; + // actual execution content + injection(); + listeners(); +} + +initialize(); diff --git a/src/ext/content-scripts/entry-userscripts.js b/src/ext/content-scripts/entry-userscripts.js new file mode 100644 index 00000000..362bfacb --- /dev/null +++ b/src/ext/content-scripts/entry-userscripts.js @@ -0,0 +1,294 @@ +import USAPI from "./api.js"; +import { colors } from "@shared/colors.js"; + +// code received from background page will be stored in this variable +// code referenced again when strict CSPs block initial injection attempt +let data; +// determines whether strict csp injection has already run (JS only) +let cspFallbackAttempted = false; + +// label used to distinguish frames in console +const label = randomLabel(); +const usTag = window.self === window.top ? "" : `(${label})`; + +function randomLabel() { + const a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const r = Math.random(); + return a[Math.floor(r * a.length)] + r.toString().slice(5, 6); +} + +function triageJS(userscript) { + const runAt = userscript.scriptObject["run-at"]; + if (runAt === "document-start") { + injectJS(userscript); + } else if (runAt === "document-end") { + if (document.readyState !== "loading") { + injectJS(userscript); + } else { + document.addEventListener( + "DOMContentLoaded", + () => injectJS(userscript), + { once: true }, + ); + } + } else if (runAt === "document-idle") { + if (document.readyState === "complete") { + injectJS(userscript); + } else { + const handle = () => { + if (document.readyState === "complete") { + injectJS(userscript); + document.removeEventListener("readystatechange", handle); + } + }; + document.addEventListener("readystatechange", handle); + } + } +} + +function injectJS(userscript) { + const filename = userscript.scriptObject.filename; + const name = userscript.scriptObject.name; + const code = `\ +(async () => { + try { +// ===UserScript===start=== +${userscript.code} +// ===UserScript====end==== + } catch (error) { + console.error(\`${filename.replaceAll("`", "\\`")}\`, error); + } +})(); //# sourceURL=${filename.replace(/[\s"']/g, "-") + usTag}`; + let injectInto = userscript.scriptObject["inject-into"]; + // change scope to content since strict CSP event detected + if (injectInto === "auto" && (userscript.fallback || cspFallbackAttempted)) { + injectInto = "content"; + console.warn(`Attempting fallback injection for ${name}`); + } + const world = injectInto === "content" ? "content" : "page"; + if (window.self === window.top) { + console.info(`Injecting: ${name} %c(js/${world})`, colors.yellow); + } else { + console.info( + `Injecting: ${name} %c(js/${world})%c - %cframe(${label})(${window.location})`, + colors.yellow, + colors.inherit, + colors.blue, + ); + } + if (world === "page") { + const div = document.createElement("div"); + div.style.display = "none"; + const shadowRoot = div.attachShadow({ mode: "closed" }); + const tag = document.createElement("script"); + tag.textContent = code; + shadowRoot.append(tag); + (document.body ?? document.head ?? document.documentElement).append(div); + } else { + try { + Function( + `{${Object.keys(userscript.apis).join(",")}}`, + code, + )(userscript.apis); + } catch (error) { + console.error(`${filename}`, error); + } + return; + } +} + +function injectCSS(name, code) { + if (window.self === window.top) { + console.info(`Injecting ${name} %c(css)`, colors.green); + } else { + console.info( + `Injecting ${name} %c(css)%c - %cframe(${label})(${window.location})`, + colors.green, + colors.inherit, + colors.blue, + ); + } + // Safari lacks full support for tabs.insertCSS + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/insertCSS + // specifically frameId and cssOrigin + // if support for those details keys arrives, the method below can be used + // NOTE: manifest V3 does support frameId, but not origin + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/insertCSS + + // write the css code to head of the document + const tag = document.createElement("style"); + tag.textContent = code; + document.head.appendChild(tag); +} + +function cspFallback(e) { + // if a security policy violation event has occurred + // and the directive is script-src or script-src-elem + // it's fair to assume that there is a strict CSP for javascript + // and that injection was blocked for all userscripts + // when any script-src violation is detected, re-attempt injection + if ( + e.effectiveDirective === "script-src" || + e.effectiveDirective === "script-src-elem" + ) { + // get all "auto" code + // since other code can trigger a security policy violation event + // make sure data var is not undefined before attempting fallback + if (!data || cspFallbackAttempted) return; + // update global that tracks security policy violations + cspFallbackAttempted = true; + // for all userscripts with @inject-into: auto, attempt re-injection + for (let i = 0; i < data.files.js.length; i++) { + const userscript = data.files.js[i]; + if (userscript.scriptObject["inject-into"] !== "auto") continue; + userscript.fallback = 1; + triageJS(userscript); + } + } +} + +async function injection() { + const response = await browser.runtime.sendMessage({ + name: "REQ_USERSCRIPTS", + }); + if (import.meta.env.MODE === "development") { + console.debug("REQ_USERSCRIPTS", response); + } + // cancel injection if errors detected + if (!response || response.error) { + console.error(response?.error || "REQ_USERSCRIPTS returned undefined"); + return; + } + // save response locally in case CSP events occur + data = response; + // combine regular and context-menu scripts + const scripts = [...data.files.js, ...data.files.menu]; + // loop through each userscript and prepare for processing + for (let i = 0; i < scripts.length; i++) { + const userscript = scripts[i]; + const filename = userscript.scriptObject.filename; + const grants = userscript.scriptObject.grant; + const injectInto = userscript.scriptObject["inject-into"]; + // create GM.info object, all userscripts get access to GM.info + userscript.apis = { GM: {} }; + userscript.apis.GM.info = { + script: userscript.scriptObject, + scriptHandler: data.scriptHandler, + scriptHandlerVersion: data.scriptHandlerVersion, + scriptMetaStr: userscript.scriptMetaStr, + version: data.scriptHandlerVersion, + }; + // add GM_info + userscript.apis.GM_info = userscript.apis.GM.info; + // if @grant explicitly set to none, empty grants array + if (grants.includes("none")) grants.length = 0; + // @grant values exist for page scoped userscript + if (grants.length && injectInto === "page") { + // remove grants + grants.length = 0; + // log warning + console.warn( + `${filename} @grant values removed due to @inject-into value: ${injectInto} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`, + ); + } + // @grant exist for auto scoped userscript + if (grants.length && injectInto === "auto") { + // change scope + userscript.scriptObject["inject-into"] = "content"; + // log warning + console.warn( + `${filename} @inject-into value set to 'content' due to @grant values: ${grants} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`, + ); + } + // loop through each userscript @grant value, add methods as needed + for (let j = 0; j < grants.length; j++) { + const grant = grants[j]; + const method = grant.startsWith("GM.") ? grant.slice(3) : grant; + // ensure API method exists in USAPI object + if (!Object.keys(USAPI).includes(method)) continue; + // add granted methods + switch (method) { + case "info": + case "GM_info": + continue; + case "getValue": + case "setValue": + case "deleteValue": + case "listValues": + userscript.apis.GM[method] = USAPI[method].bind({ + US_filename: filename, + }); + break; + case "GM_xmlhttpRequest": + userscript.apis[method] = USAPI[method]; + break; + default: + userscript.apis.GM[method] = USAPI[method]; + } + } + // triage userjs item for injection + triageJS(userscript); + } + // loop through each usercss and inject + for (let i = 0; i < data.files.css.length; i++) { + const userstyle = data.files.css[i]; + injectCSS(userstyle.name, userstyle.code); + } +} + +function listeners() { + /** listen for CSP violations */ + document.addEventListener("securitypolicyviolation", cspFallback, { + once: true, + }); + /** + * listens for messages from background, popup, etc... + * @type {import("webextension-polyfill").Runtime.OnMessageListener} + */ + const handleMessage = (message) => { + const name = message.name; + if (name === "CONTEXT_RUN") { + // from bg script when context-menu item is clicked + // double check to ensure context-menu scripts only run in top windows + if (window !== window.top) return; + // loop through context-menu scripts saved to data object and find match + // if no match found, nothing will execute and error will log + const filename = message.menuItemId; + for (let i = 0; i < data.files.menu.length; i++) { + const item = data.files.menu[i]; + if (item.scriptObject.filename === filename) { + console.info(`Injecting ${filename} %c(js)`, colors.yellow); + injectJS(item); + return; + } + } + console.error(`Couldn't find ${filename} code!`); + } + }; + /** Dynamically remove listeners to avoid memory leaks */ + if (document.visibilityState === "visible") { + browser.runtime.onMessage.addListener(handleMessage); + } + document.addEventListener("visibilitychange", () => { + if (document.hidden) { + browser.runtime.onMessage.removeListener(handleMessage); + } else { + browser.runtime.onMessage.addListener(handleMessage); + } + }); +} + +async function initialize() { + // avoid duplicate injection of content scripts + if (window["CS_ENTRY_USERSCRIPTS"]) return; + window["CS_ENTRY_USERSCRIPTS"] = 1; + // check user settings + const key = "US_GLOBAL_ACTIVE"; + const results = await browser.storage.local.get(key); + if (results[key] === false) return console.info("Userscripts off"); + // start the injection process and add the listeners + injection(); + listeners(); +} + +initialize(); diff --git a/src/ext/content-scripts/main.js b/src/ext/content-scripts/main.js deleted file mode 100644 index 6d23a254..00000000 --- a/src/ext/content-scripts/main.js +++ /dev/null @@ -1,233 +0,0 @@ -import apis from "./api.js"; - -// code received from background page will be stored in this variable -// code referenced again when strict CSPs block initial injection attempt -let data; -// determines whether strict csp injection has already run (JS only) -let cspFallbackAttempted = false; - -// label used to distinguish frames in console -const label = randomLabel(); -const usTag = window.self === window.top ? "" : `(${label})`; - -function randomLabel() { - const a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", r = Math.random(); - return a[Math.floor(r * a.length)] + r.toString().slice(5, 6); -} - -function triageJS(userscript) { - const runAt = userscript.scriptObject["run-at"]; - if (runAt === "document-start") { - injectJS(userscript); - } else if (runAt === "document-end") { - if (document.readyState !== "loading") { - injectJS(userscript); - } else { - document.addEventListener("DOMContentLoaded", () => { - injectJS(userscript); - }); - } - } else if (runAt === "document-idle") { - if (document.readyState === "complete") { - injectJS(userscript); - } else { - document.addEventListener("readystatechange", () => { - if (document.readyState === "complete") { - injectJS(userscript); - } - }); - } - } -} - -function injectJS(userscript) { - const filename = userscript.scriptObject.filename; - const name = userscript.scriptObject.name; - const code = `${userscript.code} //# sourceURL=${filename.replace(/\s/g, "-") + usTag}`; - let injectInto = userscript.scriptObject["inject-into"]; - // change scope to content since strict CSP event detected - if (injectInto === "auto" && (userscript.fallback || cspFallbackAttempted)) { - injectInto = "content"; - console.warn(`Attempting fallback injection for ${name}`); - } else if (window.self === window.top) { - console.info(`Injecting ${name} %c(js)`, "color: #fff600"); - } else { - console.info(`Injecting ${name} %c(js)%c - %cframe(${label})(${window.location})`, "color: #fff600", "color: inherit", "color: #006fff"); - } - if (injectInto !== "content") { - const div = document.createElement("div"); - div.style.display = "none"; - const shadowRoot = div.attachShadow({mode: "closed"}); - document.body.append(div); - const tag = document.createElement("script"); - tag.textContent = code; - shadowRoot.append(tag); - } else { - try { - // eslint-disable-next-line no-new-func - return Function(`{${Object.keys(userscript.apis).join(",")}}`, code)(userscript.apis); - } catch (error) { - console.error(`"${filename}" error:`, error); - } - } -} - -function injectCSS(name, code) { - if (window.self === window.top) { - console.info(`Injecting ${name} %c(css)`, "color: #60f36c"); - } else { - console.info(`Injecting ${name} %c(css)%c - %cframe(${label})(${window.location})`, "color: #60f36c", "color: inherit", "color: #006fff"); - } - // Safari lacks full support for tabs.insertCSS - // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/insertCSS - // specifically frameId and cssOrigin - // if support for those details keys arrives, the method below can be used - // NOTE: manifest V3 does support frameId, but not origin - // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/insertCSS - - // write the css code to head of the document - const tag = document.createElement("style"); - tag.textContent = code; - document.head.appendChild(tag); -} - -function cspFallback(e) { - // if a security policy violation event has occurred - // and the directive is script-src or script-src-elem - // it's fair to assume that there is a strict CSP for javascript - // and that injection was blocked for all userscripts - // when any script-src violation is detected, re-attempt injection - if ( - e.effectiveDirective === "script-src" - || e.effectiveDirective === "script-src-elem" - ) { - // get all "auto" code - // since other code can trigger a security policy violation event - // make sure data var is not undefined before attempting fallback - if (!data || cspFallbackAttempted) return; - // update global that tracks security policy violations - cspFallbackAttempted = 1; - // for all userscripts with @inject-into: auto, attempt re-injection - for (let i = 0; i < data.files.js.length; i++) { - const userscript = data.files.js[i]; - if (userscript.scriptObject["inject-into"] !== "auto") continue; - userscript.fallback = 1; - triageJS(userscript); - } - } -} - -function injection() { - browser.runtime.sendMessage({name: "REQ_USERSCRIPTS"}, response => { - // cancel injection if errors detected - if (!response || response.error) { - console.error(response?.error || "REQ_USERSCRIPTS returned undefined"); - return; - } - // save response locally in case CSP events occur - data = response; - // combine regular and context-menu scripts - const scripts = [...data.files.js, ...data.files.menu]; - // loop through each userscript and prepare for processing - for (let i = 0; i < scripts.length; i++) { - const userscript = scripts[i]; - const filename = userscript.scriptObject.filename; - const grants = userscript.scriptObject.grant; - const injectInto = userscript.scriptObject["inject-into"]; - // create GM.info object, all userscripts get access to GM.info - userscript.apis = {GM: {}}; - userscript.apis.GM.info = { - script: userscript.scriptObject, - scriptHandler: data.scriptHandler, - scriptHandlerVersion: data.scriptHandlerVersion, - scriptMetaStr: userscript.scriptMetaStr - }; - // add GM_info - userscript.apis.GM_info = userscript.apis.GM.info; - // if @grant explicitly set to none, empty grants array - if (grants.includes("none")) grants.length = 0; - // @grant values exist for page scoped userscript - if (grants.length && injectInto === "page") { - // remove grants - grants.length = 0; - // log warning - console.warn(`${filename} @grant values removed due to @inject-into value: ${injectInto} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`); - } - // @grant exist for auto scoped userscript - if (grants.length && injectInto === "auto") { - // change scope - userscript.scriptObject["inject-into"] = "content"; - // log warning - console.warn(`${filename} @inject-into value set to 'content' due to @grant values: ${grants} - https://github.com/quoid/userscripts/issues/265#issuecomment-1213462394`); - } - // loop through each userscript @grant value, add methods as needed - for (let j = 0; j < grants.length; j++) { - const grant = grants[j]; - const method = grant.split(".")[1] || grant.split(".")[0]; - // ensure API method exists in apis object - if (!Object.keys(apis).includes(method)) continue; - // add granted methods - switch (method) { - case "info": - case "GM_info": - continue; - case "getValue": - case "setValue": - case "deleteValue": - case "listValues": - userscript.apis.GM[method] = apis[method].bind({US_filename: filename}); - break; - case "GM_xmlhttpRequest": - userscript.apis[method] = apis[method]; - break; - default: - userscript.apis.GM[method] = apis[method]; - } - } - // triage userjs item for injection - triageJS(userscript); - } - // loop through each usercss and inject - for (let i = 0; i < data.files.css.length; i++) { - const userstyle = data.files.css[i]; - injectCSS(userstyle.name, userstyle.code); - } - }); -} - -function listeners() { - // listens for messages from background, popup, etc... - browser.runtime.onMessage.addListener(request => { - const name = request.name; - if (name === "CONTEXT_RUN") { - // from bg script when context-menu item is clicked - // double check to ensure context-menu scripts only run in top windows - if (window !== window.top) return; - - // loop through context-menu scripts saved to data object and find match - // if no match found, nothing will execute and error will log - const filename = request.menuItemId; - for (let i = 0; i < data.files.menu.length; i++) { - const item = data.files.menu[i]; - if (item.scriptObject.filename === filename) { - console.info(`Injecting ${filename} %c(js)`, "color: #fff600"); - injectJS(item); - return; - } - } - console.error(`Couldn't find ${filename} code!`); - } - }); - // listen for CSP violations - document.addEventListener("securitypolicyviolation", cspFallback); -} - -async function initialize() { - const results = await browser.storage.local.get("US_GLOBAL_ACTIVE"); - if (results?.US_GLOBAL_ACTIVE === false) return console.info("Userscripts off"); - // start the injection process and add the listeners - injection(); - listeners(); -} - -initialize(); diff --git a/src/ext/extension-page/App.svelte b/src/ext/extension-page/App.svelte index 62e46a66..1c4ce4d3 100644 --- a/src/ext/extension-page/App.svelte +++ b/src/ext/extension-page/App.svelte @@ -1,133 +1,214 @@ - - -{#if $state.includes("init")} -
- {@html logo} - {#if $state.includes("init-error")} - Failed to initialize app, check the browser console - {:else} - Initializing app... - {/if} -
+ + +{#if $v4state.includes("init")} +
+ + {#if $v4state.includes("init-error")} + Failed to initialize app, check the browser console + {:else} + Initializing app... + {/if} +
+{/if} +
+ {#if !sidebarHidden} + +
+ {/if} + +
+{#if splitterActive} +
{/if} -
- - -
    - {#each $notifications as item (item.id)} - notifications.remove(item.id)} {item}/> - {/each} + {#each $notifications as item (item.id)} + notifications.remove(item.id)} {item} /> + {/each}
-{#if $state.includes("settings")}{/if} +{#if $v4state.includes("settings")} + v4state.remove("settings")} let:navRegister> + + +{/if} diff --git a/src/ext/extension-page/Appios.svelte b/src/ext/extension-page/Appios.svelte new file mode 100644 index 00000000..048def7d --- /dev/null +++ b/src/ext/extension-page/Appios.svelte @@ -0,0 +1,88 @@ + + +{#if $v4state.includes("init")} +
+ + {#if $v4state.includes("init-error")} + Failed to initialize app, check the browser console + {:else} + Initializing app... + {/if} +
+{/if} +
    + {#each $notifications as item (item.id)} + notifications.remove(item.id)} {item} /> + {/each} +
+{#if $v4state.includes("settings")} + +{/if} + + diff --git a/src/ext/extension-page/Components/Editor/CodeMirror.svelte b/src/ext/extension-page/Components/Editor/CodeMirror.svelte index 849662ef..b2cbacda 100644 --- a/src/ext/extension-page/Components/Editor/CodeMirror.svelte +++ b/src/ext/extension-page/Components/Editor/CodeMirror.svelte @@ -1,403 +1,436 @@ - + + -{#if instance} - searchActive = false} - instance={instance} - /> +{#if initialized} + (searchActive = false)} + {instance} + /> {/if} + + diff --git a/src/ext/extension-page/Components/Editor/Editor.svelte b/src/ext/extension-page/Components/Editor/Editor.svelte index e8f1ed49..e0b7c719 100644 --- a/src/ext/extension-page/Components/Editor/Editor.svelte +++ b/src/ext/extension-page/Components/Editor/Editor.svelte @@ -1,329 +1,358 @@
- {#if $state.includes("editor-loading") || $state.includes("fetching")} - - {/if} - {#if !activeItem} -
No Item Selected
- {/if} -
-
-
- -
{name}
-
-
-
- {#if $state.includes("saving")} - Saving... - {:else if $state.includes("trashing")} - (◞‸◟) - {:else if $state.includes("updating")} - Updating code, - {:else if remote} - Code was remotely fetched, check carefully before saving! - {:else if temp} - Ready for code! - {:else} - Last modified: {lastModified} - {/if} -
-
-
-
- - - -
-
-
- -
- + {#if $v4state.includes("editor-loading") || $v4state.includes("fetching")} + + {/if} + {#if !activeItem} +
No Item Selected
+ {/if} +
+ {#if sidebarHidden} + + {/if} +
+
+ +
{name}
+
+
+
+ {#if $v4state.includes("saving")} + Saving... + {:else if $v4state.includes("trashing")} + (◞‸◟) + {:else if $v4state.includes("updating")} + Updating code, + {:else if remote} + Code was remotely fetched, + check carefully before saving! + {:else if temp} + Ready for code! + {:else} + Last modified: {lastModified} + {/if} +
+
+
+
+ + + +
+
+
+ +
+
diff --git a/src/ext/extension-page/Components/Editor/EditorSearch.svelte b/src/ext/extension-page/Components/Editor/EditorSearch.svelte index ff1833cb..6c76e4a6 100644 --- a/src/ext/extension-page/Components/Editor/EditorSearch.svelte +++ b/src/ext/extension-page/Components/Editor/EditorSearch.svelte @@ -1,199 +1,203 @@ {#if active} - + {/if} diff --git a/src/ext/extension-page/Components/ModalWrapper.svelte b/src/ext/extension-page/Components/ModalWrapper.svelte new file mode 100644 index 00000000..9cb4c5aa --- /dev/null +++ b/src/ext/extension-page/Components/ModalWrapper.svelte @@ -0,0 +1,150 @@ + + +
+ + + +
+ +
+ + diff --git a/src/ext/extension-page/Components/Notification.svelte b/src/ext/extension-page/Components/Notification.svelte index 9896909b..afdb56b9 100644 --- a/src/ext/extension-page/Components/Notification.svelte +++ b/src/ext/extension-page/Components/Notification.svelte @@ -1,148 +1,154 @@
  • - -
    -
    {@html icon}
    - {item.message} - -
    + +
    + +
    {@html icon}
    + {item.message} + +
  • diff --git a/src/ext/extension-page/Components/Settings.svelte b/src/ext/extension-page/Components/Settings.svelte index 40f7f7b3..8ef12c4c 100644 --- a/src/ext/extension-page/Components/Settings.svelte +++ b/src/ext/extension-page/Components/Settings.svelte @@ -1,318 +1,845 @@ -
    - - -
    state.remove("settings")}>
    - +
    + {#each groups as group (group)} +
    +
    +
    {gl(`settings_section_${group}`)}
    + {#if indicators.resetting} + + {/if} +
    + {#each groupItems(group) as item (item.name)} + {@const ariaAttributes = { + "aria-labelledby": `${item.name}_label`, + "aria-describedby": `${item.name}_desc`, + }} +
    +
    +
    + {gl(`settings_${item.name}`)} +
    +
    + {gl(`settings_${item.name}_desc`)} +
    +
    + {#if item.nodeType === "Toggle"} + + settings.updateSingleSetting( + item.name, + !$settings[item.name], + )} + /> + {/if} + {#if item.nodeType === "select"} + + {/if} +
    +
    + {#if indicators.resetting && !indicators.saving[item.name] && !item.protect && (item.name !== "global_exclude_match" || !gemFocused)} + + {/if} + {#if item.nodeType === "textarea"} + {#if indicators.saving[item.name]} +
    + + {@html iconLoader} + {gl(`settings_${item.name}_saving`)} +
    + {/if} + {#if gemFocused} + + + {/if} + {/if} +
    +
    + {#if item.nodeType === "textarea" && item.name === "global_exclude_match"} +
    + +
    + {#each gemParsed.items as p (p)} + + {p.start}{#if p.warn || p.error}{p.value}{:else}{p.value}{/if}{p.separ} + {/each} +
    +
    +
    + {gl(`settings_${item.name}_refer`)} + +
    + {/if} +
    + {/each} +
    + {/each} +
    +
    +
    {gl(`settings_section_native`)}
    +
    +
    +
    +
    {gl("settings_scripts_directory")}
    +
    {gl("settings_scripts_directory_desc")}
    +
    +
    + {#if indicators.loading.changeSaveLocation} + + {@html iconLoader} + {/if} + +
    +
    +
    + +
    +
    +
    +
    +
    {gl(`settings_section_tools`)}
    +
    +
    +
    + {#if indicators.resetting} + + + {:else} + + + + {/if} +
    + +
    {gl("settings_scripts_tools_desc")}
    +
    +
    +
    +
    + {gl(`settings_section_about`)} +
    +
    +
    +

    + Userscripts {import.meta.env.BROWSER ?? ""} + {#if import.meta.env.GIT_TAG && import.meta.env.GIT_COMMIT} + + () + {:else} + v{$settings["version"]} + ({$settings["build"]}) + {/if} +

    +

    + {gl("settings_about_text1")} + + | + + | + +

    +

    + {gl("settings_about_text2")} + + | + +

    +
    +
    +
    diff --git a/src/ext/extension-page/Components/Sidebar/Sidebar.svelte b/src/ext/extension-page/Components/Sidebar/Sidebar.svelte index 4504b4a1..c5af6b55 100644 --- a/src/ext/extension-page/Components/Sidebar/Sidebar.svelte +++ b/src/ext/extension-page/Components/Sidebar/Sidebar.svelte @@ -1,260 +1,307 @@ -