diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 302cc59..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Hack to automatically load globally installed eslint modules - * on Archlinux systems placed in /usr/lib/node_modules - * - * Source: https://github.com/eslint/eslint/issues/11914#issuecomment-569108633 - */ - -const Module = require('module') - -const hacks = [ - 'babel-eslint', - 'eslint-plugin-vue', -] - -const ModuleFindPath = Module._findPath -Module._findPath = (request, paths, isMain) => { - const r = ModuleFindPath(request, paths, isMain) - if (!r && hacks.includes(request)) { - return require.resolve(`/usr/lib/node_modules/${request}`) - } - return r -} - -/* - * ESLint configuration derived as differences from eslint:recommended - * with changes I found useful to ensure code quality and equal formatting - * https://eslint.org/docs/user-guide/configuring - */ - -module.exports = { - env: { - browser: true, - node: true, - }, - - extends: [ - 'plugin:vue/recommended', - 'eslint:recommended', // https://eslint.org/docs/rules/ - ], - - globals: { - process: true, - }, - - parserOptions: { - ecmaVersion: 2020, - parser: '@babel/eslint-parser', - requireConfigFile: false, - }, - - plugins: [ - // required to lint *.vue files - 'vue', - ], - - reportUnusedDisableDirectives: true, - - root: true, - - rules: { - 'array-bracket-newline': ['error', { multiline: true }], - 'array-bracket-spacing': ['error'], - 'arrow-body-style': ['error', 'as-needed'], - 'arrow-parens': ['error', 'as-needed'], - 'arrow-spacing': ['error', { after: true, before: true }], - 'block-spacing': ['error'], - 'brace-style': ['error', '1tbs'], - 'comma-dangle': ['error', 'always-multiline'], - 'comma-spacing': ['error'], - 'comma-style': ['error', 'last'], - 'curly': ['error'], - 'default-case-last': ['error'], - 'default-param-last': ['error'], - 'dot-location': ['error', 'property'], - 'dot-notation': ['error'], - 'eol-last': ['error', 'always'], - 'eqeqeq': ['error', 'always', { null: 'ignore' }], - 'func-call-spacing': ['error', 'never'], - 'function-paren-newline': ['error', 'multiline'], - 'generator-star-spacing': ['off'], // allow async-await - 'implicit-arrow-linebreak': ['error'], - 'indent': ['error', 2], - 'key-spacing': ['error', { afterColon: true, beforeColon: false, mode: 'strict' }], - 'keyword-spacing': ['error'], - 'linebreak-style': ['error', 'unix'], - 'lines-between-class-members': ['error'], - 'multiline-comment-style': ['warn'], - 'newline-per-chained-call': ['error'], - 'no-alert': ['error'], - 'no-console': ['off'], - 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // allow debugger during development - 'no-duplicate-imports': ['error'], - 'no-else-return': ['error'], - 'no-empty-function': ['error'], - 'no-extra-parens': ['error'], - 'no-implicit-coercion': ['error'], - 'no-lonely-if': ['error'], - 'no-multi-spaces': ['error'], - 'no-multiple-empty-lines': ['warn', { max: 2, maxBOF: 0, maxEOF: 0 }], - 'no-promise-executor-return': ['error'], - 'no-return-assign': ['error'], - 'no-script-url': ['error'], - 'no-template-curly-in-string': ['error'], - 'no-trailing-spaces': ['error'], - 'no-unneeded-ternary': ['error'], - 'no-unreachable-loop': ['error'], - 'no-unsafe-optional-chaining': ['error'], - 'no-useless-return': ['error'], - 'no-var': ['error'], - 'no-warning-comments': ['error'], - 'no-whitespace-before-property': ['error'], - 'object-curly-newline': ['error', { consistent: true }], - 'object-curly-spacing': ['error', 'always'], - 'object-shorthand': ['error'], - 'padded-blocks': ['error', 'never'], - 'prefer-arrow-callback': ['error'], - 'prefer-const': ['error'], - 'prefer-object-spread': ['error'], - 'prefer-rest-params': ['error'], - 'prefer-template': ['error'], - 'quote-props': ['error', 'consistent-as-needed', { keywords: false }], - 'quotes': ['error', 'single', { allowTemplateLiterals: true }], - 'require-atomic-updates': ['error'], - 'require-await': ['error'], - 'semi': ['error', 'never'], - 'sort-imports': ['error', { ignoreCase: true, ignoreDeclarationSort: false, ignoreMemberSort: false }], - 'sort-keys': ['error', 'asc', { caseSensitive: true, natural: false }], - 'space-before-blocks': ['error', 'always'], - 'space-before-function-paren': ['error', 'never'], - 'space-in-parens': ['error', 'never'], - 'space-infix-ops': ['error'], - 'space-unary-ops': ['error', { nonwords: false, words: true }], - 'spaced-comment': ['warn', 'always'], - 'switch-colon-spacing': ['error'], - 'template-curly-spacing': ['error', 'never'], - 'unicode-bom': ['error', 'never'], - 'vue/new-line-between-multi-line-property': ['error'], - 'vue/no-empty-component-block': ['error'], - 'vue/no-reserved-component-names': ['error'], - 'vue/no-template-target-blank': ['error'], - 'vue/no-unused-properties': ['error'], - 'vue/no-unused-refs': ['error'], - 'vue/no-useless-mustaches': ['error'], - 'vue/order-in-components': ['off'], // Collides with sort-keys - 'vue/require-name-property': ['error'], - 'vue/v-for-delimiter-style': ['error'], - 'vue/v-on-function-call': ['error'], - 'wrap-iife': ['error'], - 'yoda': ['error'], - }, -} diff --git a/.git_changerelease.yaml b/.git_changerelease.yaml new file mode 100644 index 0000000..afef480 --- /dev/null +++ b/.git_changerelease.yaml @@ -0,0 +1,28 @@ +--- + +# Template to format the commit message containing the changelog change +# which will be used to add the tag to. +release_commit_message: "Release: OTS {{.Version}}" + +# Commands to run before committing the changelog and adding the tag. +# Therefore these can add content to be included into the release- +# commit. These commands have access to the `TAG_VERSION` variable +# which contains the tag to be applied after the commit. If the +# command specified here is prefixed with a `-` sign, the exit status +# will not fail the release process. If it is not prefixed with a `-` +# a non-zero exit status will terminate the release process. The +# commands will be run from the repostory root, so sub-dirs MUST be +# specified. All commands are run as `bash -ec "..."` so you can use +# bash inside the commands. +pre_commit_commands: + - |- + yq -iP "(select(.spec.template.spec | has(\"containers\")) | .spec.template.spec.containers[] | select(.name == \"ots\").image) = \"ghcr.io/luzifer/ots:v${TAG_VERSION}\"" docs/k8s_example.yml + git add docs/k8s_example.yml + - |- + yq -iP ".services.app.build.context = \"https://github.com/Luzifer/ots.git#v${TAG_VERSION}\"" docker-compose.yml + git add docker-compose.yml + - |- + sed -i -E "s@org.opencontainers.image.version='[^']*'@org.opencontainers.image.version='${TAG_VERSION}'@" Dockerfile Dockerfile.minimal + git add Dockerfile Dockerfile.minimal + +... diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4e9ce77..178e69f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,13 +1,12 @@ --- - -name: "CodeQL" +name: 'CodeQL' on: push: - branches: [ "master" ] + branches: ['master'] pull_request: # The branches below must be a subset of the branches above - branches: [ "master" ] + branches: ['master'] schedule: - cron: '38 21 * * 3' @@ -24,24 +23,25 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'go', 'javascript' ] + language: ['go', 'javascript'] steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} + - name: Install Go + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version-file: go.mod - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + with: + build-mode: ${{ matrix.language == 'go' && 'autobuild' || '' }} + languages: ${{ matrix.language }} - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" - -... + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + with: + category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..546a2b5 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,48 @@ +--- +name: docker-publish +on: + push: + branches: ['master'] + tags: ['v*'] + +permissions: + packages: write + +jobs: + docker-publish: + defaults: + run: + shell: bash + + runs-on: ubuntu-latest + + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + lfs: true + show-progress: false + + - name: Log into registry + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker Build & Publish + id: taggen + run: bash ci/docker-gen-tagnames.sh + + - name: Build and push + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.taggen.outputs.docker_build_tags }} diff --git a/.github/workflows/pr-regen-translations.yml b/.github/workflows/pr-regen-translations.yml new file mode 100644 index 0000000..887694e --- /dev/null +++ b/.github/workflows/pr-regen-translations.yml @@ -0,0 +1,41 @@ +--- +name: pull-request-ci +on: + pull_request_target: + paths: ['i18n.yaml'] + +jobs: + generate-translations: + defaults: + run: + shell: bash + + container: + image: ghcr.io/luzifer-docker/gh-arch-env@sha256:30a579e63513635d85ad9cc17344f73377a3a4add99c8efdc8b51493f926085d + env: + CGO_ENABLED: 0 + GOPATH: /go + + permissions: + contents: write + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.head_ref }} + + - name: Marking workdir safe + run: git config --global --add safe.directory /__w/ots/ots + + - name: Re-Generate embedded translations file + working-directory: ./ci/translate + run: go run . + + - uses: stefanzweifel/git-auto-commit-action@778341af668090896ca464160c2def5d1d1a3eb0 # v6.0.1 + with: + commit_author: 'github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>' + commit_message: 'CI: Update embedded translations' + file_pattern: 'src/langs/langs.js' diff --git a/.github/workflows/test-and-build.yml b/.github/workflows/test-and-build.yml index c36337f..d3a5522 100644 --- a/.github/workflows/test-and-build.yml +++ b/.github/workflows/test-and-build.yml @@ -1,9 +1,8 @@ --- - name: test-and-build on: push: - branches: ['*'] + branches: ['**'] tags: ['v*'] permissions: @@ -17,7 +16,7 @@ jobs: shell: bash container: - image: luzifer/archlinux + image: ghcr.io/luzifer-docker/gh-arch-env@sha256:30a579e63513635d85ad9cc17344f73377a3a4add99c8efdc8b51493f926085d env: CGO_ENABLED: 0 GOPATH: /go @@ -25,28 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Enable custom AUR package repo - run: echo -e "[luzifer]\nSigLevel = Never\nServer = https://archrepo.hub.luzifer.io/\$arch" >>/etc/pacman.conf - - - name: Install required packages - run: | - pacman -Syy --noconfirm \ - awk \ - curl \ - diffutils \ - git \ - go \ - golangci-lint-bin \ - make \ - nodejs-lts-hydrogen \ - npm \ - tar \ - trivy \ - unzip \ - which \ - zip - - - uses: actions/checkout@v3 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Marking workdir safe run: git config --global --add safe.directory /__w/ots/ots @@ -76,7 +54,7 @@ jobs: grep -q "### Language" translate-issue.md || rm -f translate-issue.md - name: Update Translations Issue - uses: JasonEtco/create-an-issue@v2 + uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2 if: github.ref == 'refs/heads/master' && hashFiles('translate-issue.md') != '' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -99,12 +77,10 @@ jobs: run: 'awk "/^#/ && ++c==2{exit}; /^#/f" "History.md" | tail -n +2 >release_changelog.md' - name: Release - uses: ncipollo/release-action@v1 + uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # v1.18.0 if: startsWith(github.ref, 'refs/tags/') with: artifacts: '.build/*' bodyFile: release_changelog.md draft: false generateReleaseNotes: false - -... diff --git a/.gitignore b/.gitignore index 11a2ef1..43238d9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ frontend/css frontend/js frontend/locale/*.untranslated.json frontend/webfonts +frontend/*.ttf frontend/*.woff2 node_modules ots diff --git a/.golangci.yml b/.golangci.yml index e4d0cf1..bc94d40 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,174 +1,147 @@ -# Derived from https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml - ---- +version: '2' run: - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 5m - # Force readonly modules usage for checking modules-download-mode: readonly + relative-path-mode: wd output: - format: tab + formats: + tab: + path: stdout + colors: false + +linters: + default: none + enable: + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - copyloopvar + - dogsled + - durationcheck + - errcheck + - errchkjson + - forbidigo + - funlen + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - gosec + - govet + - ineffassign + - misspell + - mnd + - nakedret + - nilerr + - nilnil + - noctx + - nolintlint + - revive + - staticcheck + - unconvert + - unused + - wastedassign + - wrapcheck + + settings: + funlen: + lines: 100 + statements: 60 + + gocyclo: + min-complexity: 15 + + mnd: + ignored-functions: + - strconv.(?:Format|Parse)\B+ + + revive: + rules: + - name: atomic + - name: banned-characters + arguments: + - ; + - name: bare-return + - name: blank-imports + - name: bool-literal-in-expr + - name: call-to-gc + - name: confusing-naming + - name: confusing-results + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: deep-exit + - name: defer + - name: dot-imports + - name: duplicated-imports + - name: early-return + - name: empty-block + - name: empty-lines + - name: errorf + - name: error-naming + - name: error-return + - name: error-strings + - name: exported + arguments: + - sayRepetitiveInsteadOfStutters + - name: flag-parameter + - name: get-return + - name: identical-branches + - name: if-return + - name: import-shadowing + - name: increment-decrement + - name: indent-error-flow + - name: modifies-parameter + - name: modifies-value-receiver + - name: optimize-operands-order + - name: range + - name: range-val-address + - name: range-val-in-closure + - name: receiver-naming + - name: redefines-builtin-id + - name: string-of-int + - name: struct-tag + - name: superfluous-else + - name: time-equal + - name: time-naming + - name: unconditional-recursion + - name: unexported-naming + - name: unexported-return + - name: unhandled-error + arguments: + - fmt.(Fp|P)rint(f|ln|) + - name: unnecessary-stmt + - name: unreachable-code + - name: unused-parameter + - name: unused-receiver + - name: useless-break + - name: var-declaration + - name: var-naming + - name: waitgroup-by-value + + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ issues: - # This disables the included exclude-list in golangci-lint as that - # list for example fully hides G304 gosec rule, errcheck, exported - # rule of revive and other errors one really wants to see. - # Smme detail: https://github.com/golangci/golangci-lint/issues/456 - exclude-use-default: false - # Don't limit the number of shown issues: Report ALL of them max-issues-per-linter: 0 max-same-issues: 0 -linters: - disable-all: true +formatters: enable: - - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false] - - bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false] - - bodyclose # checks whether HTTP response body is closed successfully [fast: true, auto-fix: false] - - containedctx # containedctx is a linter that detects struct contained context.Context field [fast: true, auto-fix: false] - - contextcheck # check the function whether use a non-inherited context [fast: false, auto-fix: false] - - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false] - - durationcheck # check for two durations multiplied together [fast: false, auto-fix: false] - - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false] - - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. [fast: false, auto-fix: false] - - exportloopref # checks for pointers to enclosing loop variables [fast: true, auto-fix: false] - - forbidigo # Forbids identifiers [fast: true, auto-fix: false] - - funlen # Tool for detection of long functions [fast: true, auto-fix: false] - - gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false] - - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] - - gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false] - - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false] - - godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false] - - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true] - - gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true] - - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true] - - gomnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false] - - gosec # Inspects source code for security problems [fast: true, auto-fix: false] - - gosimple # Linter for Go source code that specializes in simplifying a code [fast: true, auto-fix: false] - - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: true, auto-fix: false] - - ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false] - - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true] - - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false] - - nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false] - - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false] - - noctx # noctx finds sending http request without context.Context [fast: true, auto-fix: false] - - nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false] - - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false] - - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: true, auto-fix: false] - - stylecheck # Stylecheck is a replacement for golint [fast: true, auto-fix: false] - - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false] - - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: true, auto-fix: false] - - unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false] - - unused # Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false] - - wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false] - - wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false] - -linters-settings: - funlen: - lines: 100 - statements: 60 - - gocyclo: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 15 - - gomnd: - settings: - mnd: - ignored-functions: 'strconv.(?:Format|Parse)\B+' - - revive: - rules: - #- name: add-constant # Suggests using constant for magic numbers and string literals - # Opinion: Makes sense for strings, not for numbers but checks numbers - #- name: argument-limit # Specifies the maximum number of arguments a function can receive | Opinion: Don't need this - - name: atomic # Check for common mistaken usages of the `sync/atomic` package - - name: banned-characters # Checks banned characters in identifiers - arguments: - - ';' # Greek question mark - - name: bare-return # Warns on bare returns - - name: blank-imports # Disallows blank imports - - name: bool-literal-in-expr # Suggests removing Boolean literals from logic expressions - - name: call-to-gc # Warns on explicit call to the garbage collector - #- name: cognitive-complexity # Sets restriction for maximum Cognitive complexity. - # There is a dedicated linter for this - - name: confusing-naming # Warns on methods with names that differ only by capitalization - - name: confusing-results # Suggests to name potentially confusing function results - - name: constant-logical-expr # Warns on constant logical expressions - - name: context-as-argument # `context.Context` should be the first argument of a function. - - name: context-keys-type # Disallows the usage of basic types in `context.WithValue`. - #- name: cyclomatic # Sets restriction for maximum Cyclomatic complexity. - # There is a dedicated linter for this - #- name: datarace # Spots potential dataraces - # Is not (yet) available? - - name: deep-exit # Looks for program exits in funcs other than `main()` or `init()` - - name: defer # Warns on some [defer gotchas](https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-iii-36a1ab3d6ef1) - - name: dot-imports # Forbids `.` imports. - - name: duplicated-imports # Looks for packages that are imported two or more times - - name: early-return # Spots if-then-else statements that can be refactored to simplify code reading - - name: empty-block # Warns on empty code blocks - - name: empty-lines # Warns when there are heading or trailing newlines in a block - - name: errorf # Should replace `errors.New(fmt.Sprintf())` with `fmt.Errorf()` - - name: error-naming # Naming of error variables. - - name: error-return # The error return parameter should be last. - - name: error-strings # Conventions around error strings. - - name: exported # Naming and commenting conventions on exported symbols. - arguments: ['sayRepetitiveInsteadOfStutters'] - #- name: file-header # Header which each file should have. - # Useless without config, have no config for it - - name: flag-parameter # Warns on boolean parameters that create a control coupling - #- name: function-length # Warns on functions exceeding the statements or lines max - # There is a dedicated linter for this - #- name: function-result-limit # Specifies the maximum number of results a function can return - # Opinion: Don't need this - - name: get-return # Warns on getters that do not yield any result - - name: identical-branches # Spots if-then-else statements with identical `then` and `else` branches - - name: if-return # Redundant if when returning an error. - #- name: imports-blacklist # Disallows importing the specified packages - # Useless without config, have no config for it - - name: import-shadowing # Spots identifiers that shadow an import - - name: increment-decrement # Use `i++` and `i--` instead of `i += 1` and `i -= 1`. - - name: indent-error-flow # Prevents redundant else statements. - #- name: line-length-limit # Specifies the maximum number of characters in a lined - # There is a dedicated linter for this - #- name: max-public-structs # The maximum number of public structs in a file. - # Opinion: Don't need this - - name: modifies-parameter # Warns on assignments to function parameters - - name: modifies-value-receiver # Warns on assignments to value-passed method receivers - #- name: nested-structs # Warns on structs within structs - # Opinion: Don't need this - - name: optimize-operands-order # Checks inefficient conditional expressions - #- name: package-comments # Package commenting conventions. - # Opinion: Don't need this - - name: range # Prevents redundant variables when iterating over a collection. - - name: range-val-address # Warns if address of range value is used dangerously - - name: range-val-in-closure # Warns if range value is used in a closure dispatched as goroutine - - name: receiver-naming # Conventions around the naming of receivers. - - name: redefines-builtin-id # Warns on redefinitions of builtin identifiers - #- name: string-format # Warns on specific string literals that fail one or more user-configured regular expressions - # Useless without config, have no config for it - - name: string-of-int # Warns on suspicious casts from int to string - - name: struct-tag # Checks common struct tags like `json`,`xml`,`yaml` - - name: superfluous-else # Prevents redundant else statements (extends indent-error-flow) - - name: time-equal # Suggests to use `time.Time.Equal` instead of `==` and `!=` for equality check time. - - name: time-naming # Conventions around the naming of time variables. - - name: unconditional-recursion # Warns on function calls that will lead to (direct) infinite recursion - - name: unexported-naming # Warns on wrongly named un-exported symbols - - name: unexported-return # Warns when a public return is from unexported type. - - name: unhandled-error # Warns on unhandled errors returned by funcion calls - arguments: - - "fmt.(Fp|P)rint(f|ln|)" - - name: unnecessary-stmt # Suggests removing or simplifying unnecessary statements - - name: unreachable-code # Warns on unreachable code - - name: unused-parameter # Suggests to rename or remove unused function parameters - - name: unused-receiver # Suggests to rename or remove unused method receivers - #- name: use-any # Proposes to replace `interface{}` with its alias `any` - # Is not (yet) available? - - name: useless-break # Warns on useless `break` statements in case clauses - - name: var-declaration # Reduces redundancies around variable declaration. - - name: var-naming # Naming rules. - - name: waitgroup-by-value # Warns on functions taking sync.WaitGroup as a by-value parameter - -... + - gofmt + - gofumpt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/Dockerfile b/Dockerfile index d9e3f64..379adc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM luzifer/archlinux as builder +FROM golang:1.24.5-alpine@sha256:daae04ebad0c21149979cd8e9db38f565ecefd8547cf4a591240dc1972cf1399 AS builder ENV CGO_ENABLED=0 \ GOPATH=/go \ @@ -8,24 +8,28 @@ COPY . /go/src/github.com/Luzifer/ots WORKDIR /go/src/github.com/Luzifer/ots RUN set -ex \ - && pacman --noconfirm -Syy \ + && apk --no-cache add \ curl \ git \ - go \ make \ - nodejs-lts-hydrogen \ + nodejs-current \ npm \ tar \ unzip \ - && make download_libs generate-inner generate-apidocs \ + && make frontend_prod generate-apidocs \ && go install \ -ldflags "-X main.version=$(git describe --tags --always || echo dev)" \ -mod=readonly -FROM alpine:latest +FROM alpine:3.22@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1 -LABEL maintainer "Knut Ahlers " +LABEL org.opencontainers.image.authors='Knut Ahlers ' \ + org.opencontainers.image.version='1.17.2' \ + org.opencontainers.image.url='https://github.com/Luzifer/ots/pkgs/container/ots' \ + org.opencontainers.image.documentation='https://github.com/Luzifer/ots/wiki' \ + org.opencontainers.image.source='https://github.com/Luzifer/ots' \ + org.opencontainers.image.licenses='Apache-2.0' RUN set -ex \ && apk --no-cache add \ diff --git a/Dockerfile.minimal b/Dockerfile.minimal index e4206a4..e3cece1 100644 --- a/Dockerfile.minimal +++ b/Dockerfile.minimal @@ -1,4 +1,4 @@ -FROM luzifer/archlinux as builder +FROM golang:1.24.5-alpine@sha256:daae04ebad0c21149979cd8e9db38f565ecefd8547cf4a591240dc1972cf1399 AS builder ENV CGO_ENABLED=0 \ GOPATH=/go \ @@ -8,16 +8,15 @@ COPY . /go/src/github.com/Luzifer/ots WORKDIR /go/src/github.com/Luzifer/ots RUN set -ex \ - && pacman --noconfirm -Syy \ + && apk --no-cache add \ curl \ git \ - go \ make \ - nodejs-lts-hydrogen \ + nodejs-current \ npm \ tar \ unzip \ - && make download_libs generate-inner generate-apidocs \ + && make frontend_prod generate-apidocs \ && go install \ -ldflags "-X main.version=$(git describe --tags --always || echo dev)" \ -mod=readonly @@ -25,7 +24,12 @@ RUN set -ex \ FROM scratch -LABEL maintainer "Knut Ahlers " +LABEL org.opencontainers.image.authors='Knut Ahlers ' \ + org.opencontainers.image.version='1.17.2' \ + org.opencontainers.image.url='https://github.com/Luzifer/ots/pkgs/container/ots' \ + org.opencontainers.image.documentation='https://github.com/Luzifer/ots/wiki' \ + org.opencontainers.image.source='https://github.com/Luzifer/ots' \ + org.opencontainers.image.licenses='Apache-2.0' COPY --from=builder /go/bin/ots /usr/local/bin/ots diff --git a/History.md b/History.md index 1d71a35..71ea903 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,139 @@ +# 1.17.2 / 2025-06-15 + + * Bugfixes + * chore(deps): update alpine docker tag to v3.22 + * chore(deps): update dependency go to v1.24.4 + * fix(deps): update dependency vue-i18n to v11.1.5 + * fix(deps): update dependency vue to v3.5.16 + * fix(deps): update module github.com/redis/go-redis/v9 to v9.10.0 + +# 1.17.1 / 2025-05-29 + + * Bugfixes + * fix(deps): update dependency vue-i18n to v11.1.4 + * fix(deps): update dependency vue to v3.5.15 + * fix(deps): update module github.com/redis/go-redis/v9 to v9.9.0 + +# 1.17.0 / 2025-05-12 + + * Improvements + * Port to Vue3 and TypeScript + + * Bugfixes + * Update Go dependencies + * Update Node dependencies + + * Translations + * Update Polish translation (#213) (Thanks @Icikowski) + +# 1.16.0 / 2025-05-01 + + * New Features + * feat: support auto theme mode (#212) (Thanks @Ma-ve) + + * Bugfixes + * Cleanup test workflow + * Lint: Update linter config for golangci-lint v2 + * Update Go dependencies + * Update Node dependencies + +# 1.15.1 / 2024-12-12 + + * Bugfixes + * Update Node dependencies + * Update Go dependencies + +# 1.15.0 / 2024-12-05 + + * Improvements + * Add alternative `appIcon` for dark-mode (#204) + +# 1.14.0 / 2024-11-21 + + * Improvements + * Add ability to paste files into textarea + * Add button to burn secrets immediately (#193) + * Add customization to add footer-links (#192) + * Add error message when subtle crypto is unavailable + * Add 'log-requests' option to disable request logging (#199) (Thanks @jimmypw) + * Add multi-platform image build + * Add periodic in-memory store pruner (#200) (Thanks @jimmypw) + * Add TLS configuration for server (#190) (Thanks @hixichen) + + * Bugfixes + * Fix: Use no-cache to satisfy Trivy + + * Translations + * Update Polish translation (#194, #201) (Thanks @Icikowski) + +# 1.13.0 / 2024-08-27 + + * Bugfixes + * Update Node dependencies + * Update Go dependencies + * Lint: Resolve unused-parameter error + + * Translations + * Add Italian translation (#173) (Thanks @ste93cry) + * Update Dutch translation (#168) (Thanks @mboeren & @sorcix) + * Restore old `nl` translation as `nl-BE` + * Update French translation (#167) (Thanks @toindev) + * Update Swedish translation (#171) (Thank @artingu) + +# 1.12.0 / 2024-01-24 + + * Improvements + * [#159] Add version-command for ots-cli + * [#160] Add auto-resizing textareas + * [#160] Add hover tooltips for buttons + * [#160] Make success indicator more clear + * Use OCI Label defaults on Docker images (#145) + + * Bugfixes + * Update dependencies + + * Translations + * Update Polish translation (#166) (Thanks @Icikowski) + +# 1.11.1 / 2023-12-12 + + * Bugfixes + * [#158] Disable Vue Devtools in release builds + +# 1.11.0 / 2023-12-10 + + * Improvements + * [#148] Make secret optional when files are attached (#150) + * [#149] Make attachments stand out more (#152) + * [#154] Add debug logging for rejected attachment types & strip meta-info from mime-type (#155) + * [#154] Improve UX for rejected / allowed files + + * Bugfixes + * [client] Fix wrong method when creating secrets + * Fix: Baked in version-string empty in build-local + * Update dependencies + + * Translations + * Add tool to update translations in PRs + * Update Chinese translations (#151) (Thanks @YongJie-Xie) + +# 1.10.0 / 2023-11-11 + + * New Features + * Add server side check for maximum secret size + * Implement metrics collection for API server (#143) + + * Improvements + * Add frontend check for invalid attached files (#139) + * Implement attachment checking in CLI (#141) + + * Bugfixes + * Fix: Clean error on component navigation + * [CI] Fix: npm@latest cannot run with Node 18 + + * Translations + * Update Polish translation (#140) (Thanks @Icikowski) + # 1.9.2 / 2023-10-18 * Add basic-auth / header addition to OTS-CLI diff --git a/Makefile b/Makefile index cdc0d67..92a4852 100644 --- a/Makefile +++ b/Makefile @@ -1,48 +1,35 @@ -VER_FONTAWESOME:=6.4.0 +default: build-local - -default: generate download_libs - -build-local: download_libs generate-inner generate-apidocs +build-local: frontend generate-apidocs go build \ -buildmode=pie \ - -ldflags "-s -w -X main.version=$(git describe --tags --always || echo dev)" \ + -ldflags "-s -w -X main.version=$(shell git describe --tags --always || echo dev)" \ -mod=readonly \ -trimpath -generate: - docker run --rm -i -v $(CURDIR):$(CURDIR) -w $(CURDIR) node:18-alpine \ - sh -exc "apk add make && make generate-inner generate-apidocs && chown -R $(shell id -u) frontend node_modules" - generate-apidocs: npx --yes @redocly/cli build-docs docs/openapi.yaml --disableGoogleFont true -o /tmp/api.html mv /tmp/api.html frontend/ -generate-inner: - npx --yes npm@latest ci --include=dev - node ./ci/build.mjs +frontend_prod: export NODE_ENV=production +frontend_prod: frontend -publish: download_libs generate-inner generate-apidocs +frontend: node_modules + corepack yarn@1 node ci/build.mjs + +frontend_lint: node_modules + corepack yarn@1 eslint --fix src + +node_modules: + corepack yarn@1 install --production=false --frozen-lockfile + +publish: export NODE_ENV=production +publish: frontend_prod generate-apidocs bash ./ci/build.sh translate: cd ci/translate && go run . --write-issue-file -# -- Download / refresh external libraries -- - -clean_libs: - rm -rf \ - frontend/css \ - frontend/js \ - frontend/webfonts - -download_libs: clean_libs -download_libs: fontawesome - -fontawesome: - curl -sSfL https://github.com/FortAwesome/Font-Awesome/archive/$(VER_FONTAWESOME).tar.gz | \ - tar -vC frontend -xz --strip-components=1 --wildcards --exclude='*/js-packages' '*/css' '*/webfonts' - # -- Vulnerability scanning -- trivy: @@ -55,3 +42,5 @@ trivy: --scanners config,license,secret,vuln \ --severity HIGH,CRITICAL \ --skip-dirs docs + +.PHONY: node_modules diff --git a/README.md b/README.md index 1db58f8..2df4c8d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -![](https://badges.fyi/github/license/Luzifer/ots) -![](https://badges.fyi/github/latest-release/Luzifer/ots) -![](https://badges.fyi/github/downloads/Luzifer/ots) -[![Go Report Card](https://goreportcard.com/badge/github.com/Luzifer/ots)](https://goreportcard.com/report/github.com/Luzifer/ots) +![](https://img.shields.io/github/license/Luzifer/ots) +![](https://img.shields.io/github/v/release/Luzifer/ots) +![](https://img.shields.io/github/downloads/Luzifer/ots/total) # Luzifer / OTS @@ -9,8 +8,8 @@ ## Features -- AES 256bit encryption -- Server does never get the password +- Secrets are encrypted with AES 256bit encryption in browser +- Server never receives the plain text secret - Secret is deleted on first read ## Setup @@ -91,6 +90,19 @@ You will now need to supply the web application with the password in addition to In this case due to how browsers are handling hashes in URLs (the part after the `#`) the only URL the server gets to know is `https://ots.fyi/` which loads the frontend. Afterwards the Javascript executed in the browser fetches the encrypted secret at the given ID and decrypts it with the given password (in this case `mypass`). I will not be able to tell the content of your secret and just see the AES 256bit encrypted content. +## Local development + +This repo contains a `Tilefile` to be used with [tilt v0.33+](https://tilt.dev/) to build and start the server for development. + +Requirements: +- Go v1.23+ +- Node v22+ +- Tilt v0.33+ + +Run `tilt up`, and see `http://localhost:10350/` for the Tilt dashboard. + +Front-end application is available at `http://localhost:15641/`. + ## Localize to your own language If you want to help translating the application to your own language please see the [`i18n.yaml`](https://github.com/Luzifer/ots/blob/master/i18n.yaml) file from this repository and translate the English strings inside. Afterwards please [open an issue](https://github.com/Luzifer/ots/issues/new) and attach your translation including the information which language you translated the strings into. diff --git a/Tiltfile b/Tiltfile new file mode 100644 index 0000000..502d206 --- /dev/null +++ b/Tiltfile @@ -0,0 +1,51 @@ +# Install Node deps on change of package.json +local_resource( + 'yarn', + cmd='corepack yarn@1 install', # Not using the make target to edit the lockfile + deps=['package.json'], +) + +# Rebuild frontend if source files change +local_resource( + 'frontend', + cmd='make frontend', + deps=['src'], + resource_deps=['yarn'], +) + +# Generate translation files on source change +local_resource( + 'translations', + cmd='make translate', + deps=['i18n.yaml'], +) + +# Rebuild and run Go webserver on code changes +local_resource( + 'server', + deps=[ + 'api.go', + 'frontend', + 'helpers.go', + 'main.go', + 'pkg', + 'storage.go', + 'tplFuncs.go', + 'go.mod', 'go.sum', + ], + ignore=[ + 'src' + ], + serve_cmd='go run . --listen=:15641', + serve_env={ + 'CUSTOMIZE': 'customize.yaml', + }, + readiness_probe=probe( + http_get=http_get_action(15641, path='/api/healthz'), + initial_delay_secs=1, + ), + resource_deps=[ + 'frontend', + 'translations', + ], +) diff --git a/api.go b/api.go index 2f76dce..0a17feb 100644 --- a/api.go +++ b/api.go @@ -8,13 +8,24 @@ import ( "strings" "time" + "github.com/Luzifer/ots/pkg/metrics" + "github.com/Luzifer/ots/pkg/storage" "github.com/gofrs/uuid" "github.com/gorilla/mux" "github.com/sirupsen/logrus" ) +const ( + errorReasonInvalidJSON = "invalid_json" + errorReasonSecretMissing = "secret_missing" + errorReasonSecretSize = "secret_size" + errorReasonStorageError = "storage_error" + errorReasonSecretNotFound = "secret_not_found" +) + type apiServer struct { - store storage + collector *metrics.Collector + store storage.Storage } type apiResponse struct { @@ -29,9 +40,10 @@ type apiRequest struct { Secret string `json:"secret"` } -func newAPI(s storage) *apiServer { +func newAPI(s storage.Storage, c *metrics.Collector) *apiServer { return &apiServer{ - store: s, + collector: c, + store: s, } } @@ -39,9 +51,18 @@ func (a apiServer) Register(r *mux.Router) { r.HandleFunc("/create", a.handleCreate) r.HandleFunc("/get/{id}", a.handleRead) r.HandleFunc("/isWritable", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) }) + r.HandleFunc("/settings", a.handleSettings).Methods(http.MethodGet) + r.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }) } func (a apiServer) handleCreate(res http.ResponseWriter, r *http.Request) { + if cust.MaxSecretSize > 0 { + // As a safeguard against HUGE payloads behind a misconfigured + // proxy we take double the maximum secret size after which we + // just close the read and cut the connection to the sender. + r.Body = http.MaxBytesReader(res, r.Body, cust.MaxSecretSize*2) //nolint:mnd + } + var ( expiry = cfg.SecretExpiry secret string @@ -56,6 +77,15 @@ func (a apiServer) handleCreate(res http.ResponseWriter, r *http.Request) { if strings.HasPrefix(r.Header.Get("Content-Type"), "application/json") { tmp := apiRequest{} if err := json.NewDecoder(r.Body).Decode(&tmp); err != nil { + if _, ok := err.(*http.MaxBytesError); ok { + a.collector.CountSecretCreateError(errorReasonSecretSize) + // We don't do an error response here as the MaxBytesReader + // automatically cuts the ResponseWriter and we simply cannot + // answer them. + return + } + + a.collector.CountSecretCreateError(errorReasonInvalidJSON) a.errorResponse(res, http.StatusBadRequest, err, "decoding request body") return } @@ -65,12 +95,20 @@ func (a apiServer) handleCreate(res http.ResponseWriter, r *http.Request) { } if secret == "" { + a.collector.CountSecretCreateError(errorReasonSecretMissing) a.errorResponse(res, http.StatusBadRequest, errors.New("secret missing"), "") return } + if cust.MaxSecretSize > 0 && len(secret) > int(cust.MaxSecretSize) { + a.collector.CountSecretCreateError(errorReasonSecretSize) + a.errorResponse(res, http.StatusBadRequest, errors.New("secret size exceeds maximum"), "") + return + } + id, err := a.store.Create(secret, time.Duration(expiry)*time.Second) if err != nil { + a.collector.CountSecretCreateError(errorReasonStorageError) a.errorResponse(res, http.StatusInternalServerError, err, "creating secret") return } @@ -80,6 +118,8 @@ func (a apiServer) handleCreate(res http.ResponseWriter, r *http.Request) { expiresAt = func(v time.Time) *time.Time { return &v }(time.Now().UTC().Add(time.Duration(expiry) * time.Second)) } + a.collector.CountSecretCreated() + go updateStoredSecretsCount(a.store, a.collector) a.jsonResponse(res, http.StatusCreated, apiResponse{ ExpiresAt: expiresAt, Success: true, @@ -98,19 +138,28 @@ func (a apiServer) handleRead(res http.ResponseWriter, r *http.Request) { secret, err := a.store.ReadAndDestroy(id) if err != nil { status := http.StatusInternalServerError - if err == errSecretNotFound { + if errors.Is(err, storage.ErrSecretNotFound) { + a.collector.CountSecretReadError(errorReasonSecretNotFound) status = http.StatusNotFound + } else { + a.collector.CountSecretReadError(errorReasonStorageError) } a.errorResponse(res, status, err, "reading & destroying secret") return } + a.collector.CountSecretRead() + go updateStoredSecretsCount(a.store, a.collector) a.jsonResponse(res, http.StatusOK, apiResponse{ Success: true, Secret: secret, }) } +func (a apiServer) handleSettings(w http.ResponseWriter, _ *http.Request) { + a.jsonResponse(w, http.StatusOK, cust) +} + func (a apiServer) errorResponse(res http.ResponseWriter, status int, err error, desc string) { errID := uuid.Must(uuid.NewV4()).String() @@ -124,7 +173,7 @@ func (a apiServer) errorResponse(res http.ResponseWriter, status int, err error, }) } -func (apiServer) jsonResponse(res http.ResponseWriter, status int, response apiResponse) { +func (apiServer) jsonResponse(res http.ResponseWriter, status int, response any) { res.Header().Set("Content-Type", "application/json") res.Header().Set("Cache-Control", "no-store, max-age=0") res.WriteHeader(status) diff --git a/ci/build.mjs b/ci/build.mjs index 2d1c83d..b3d3f5b 100644 --- a/ci/build.mjs +++ b/ci/build.mjs @@ -1,6 +1,6 @@ -import { sassPlugin } from 'esbuild-sass-plugin' -import vuePlugin from 'esbuild-vue' import esbuild from 'esbuild' +import { sassPlugin } from 'esbuild-sass-plugin' +import vuePlugin from 'esbuild-plugin-vue3' esbuild.build({ assetNames: '[name]', @@ -8,9 +8,10 @@ esbuild.build({ define: { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'dev'), }, - entryPoints: ['src/main.js'], + entryPoints: ['src/main.ts'], legalComments: 'none', loader: { + '.ttf': 'empty', // Drop files, all targets below support woff2 '.woff2': 'file', }, minify: true, @@ -20,10 +21,10 @@ esbuild.build({ vuePlugin(), ], target: [ - 'chrome87', - 'edge87', + 'chrome109', + 'edge132', 'es2020', - 'firefox84', - 'safari14', + 'firefox115', + 'safari16', ], }) diff --git a/ci/docker-gen-tagnames.sh b/ci/docker-gen-tagnames.sh new file mode 100644 index 0000000..7882820 --- /dev/null +++ b/ci/docker-gen-tagnames.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +function log() { + echo "[$(date +%H:%M:%S)] $@" >&2 +} + +[[ -n ${GITHUB_REF_NAME:-} ]] || { + log "ERR: This script is intended to run on a Github Action only." + exit 1 +} + +repo="ghcr.io/${GITHUB_REPOSITORY,,}" +tags=() + +case "${GITHUB_REF_TYPE}" in +branch) + # Generic build to develop: Workflow has to limit branches to master + tags+=("${repo}:develop") + ;; +tag) + # Build to latest & tag: Older tags are not intended to rebuild + tags+=("${repo}:latest" "${repo}:${GITHUB_REF_NAME}") + ;; +*) + log "ERR: The ref type ${GITHUB_REF_TYPE} is not handled." + exit 1 + ;; +esac + +export IFS=, +echo "docker_build_tags=${tags[*]}" >>${GITHUB_OUTPUT} diff --git a/ci/pr-regen-translations.sh b/ci/pr-regen-translations.sh new file mode 100755 index 0000000..5a0a53e --- /dev/null +++ b/ci/pr-regen-translations.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +set -euo pipefail + +files=( + i18n.yaml + src/langs/langs.js +) +translation_branch=upd-translate + +function log() { + echo "$@" >&2 +} + +PR_REMOTE_URL=${1:-} +[[ -n $PR_REMOTE_URL ]] || { + log "Usage: $(basename $0) " + exit 1 +} + +remote="$(cut -d '/' -f 1-5 <<<"${PR_REMOTE_URL}").git" +branch=$(cut -d '/' -f 7 <<<"${PR_REMOTE_URL}") + +git diff --exit-code >/dev/null || { + log "FATAL: Local changes detected, stopping now." + exit 1 +} + +switch_back_branch=$(git branch --show-current) +trap "git switch ${switch_back_branch}" EXIT + +log "Updating branch '${branch}' of remote '${remote}'..." + +log "+ Fetching remote..." +git fetch "${remote}" "${branch}" + +log "+ Creating work-branch..." +if git branch | grep -q ${translation_branch}; then + git branch -D ${translation_branch} +fi +git branch ${translation_branch} FETCH_HEAD + +log "+ Switching to work-branch..." +git switch ${translation_branch} + +log "+ Updating translations..." +make translate + +if git diff --exit-code "${files[@]}" >/dev/null; then + log "No changed introduced, stopping now." +fi + +log "+ Committing changes..." +git add "${files[@]}" +git commit -m 'CI: Update embedded translations' + +log "+ Please review these changes:" +git show + +log "[Enter] to continue, [Ctrl+C] to cancel..." +read + +log "+ Updating remote branch..." +git push ${remote} ${translation_branch}:${branch} + +log "Updated remote PR, switching back to previous branch..." diff --git a/ci/translate/go.mod b/ci/translate/go.mod index 6624e8a..456e877 100644 --- a/ci/translate/go.mod +++ b/ci/translate/go.mod @@ -1,29 +1,31 @@ module translate -go 1.21 +go 1.23.0 + +toolchain go1.24.5 require ( - github.com/Luzifer/go_helpers/v2 v2.20.0 - github.com/Luzifer/rconfig/v2 v2.4.0 - github.com/Masterminds/sprig/v3 v3.2.3 + github.com/Luzifer/go_helpers/v2 v2.25.0 + github.com/Luzifer/rconfig/v2 v2.6.0 + github.com/Masterminds/sprig/v3 v3.3.0 + github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 gopkg.in/yaml.v3 v3.0.1 ) require ( + dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/google/uuid v1.3.1 // indirect - github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/sys v0.13.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/sys v0.32.0 // indirect gopkg.in/validator.v2 v2.0.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/ci/translate/go.sum b/ci/translate/go.sum index c1cd473..09e437c 100644 --- a/ci/translate/go.sum +++ b/ci/translate/go.sum @@ -1,118 +1,64 @@ -github.com/Luzifer/go_helpers/v2 v2.20.0 h1:OyCUs7TFGwfJpGqD21KEKKOXy92jetw2l7dlmG7HZnA= -github.com/Luzifer/go_helpers/v2 v2.20.0/go.mod h1:KPGjImwm51SmOTZMd9XUsT241gHYJuEyLrS/omQ4/Dw= -github.com/Luzifer/rconfig/v2 v2.4.0 h1:MAdymTlExAZ8mx5VG8xOFAtFQSpWBipKYQHPOmYTn9o= -github.com/Luzifer/rconfig/v2 v2.4.0/go.mod h1:hWF3ZVSusbYlg5bEvCwalEyUSY+0JPJWUiIu7rBmav8= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Luzifer/go_helpers/v2 v2.25.0 h1:k1J4gd1+BfuokTDoWgcgib9P5mdadjzKEgbtKSVe46k= +github.com/Luzifer/go_helpers/v2 v2.25.0/go.mod h1:KSVUdAJAav5cWGyB5oKGxmC27HrKULVTOxwPS/Kr+pc= +github.com/Luzifer/rconfig/v2 v2.6.0 h1:ZKgsO2Wt/XZXawuAZCDkW7xszxZ8hQDTV1Wm63Jvnqk= +github.com/Luzifer/rconfig/v2 v2.6.0/go.mod h1:CxISRwCV2WjO5gnUnaRGDq17u1M3TvmFgzJLr87ejtc= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ci/translate/main.go b/ci/translate/main.go index 0e3d4ff..80944ba 100644 --- a/ci/translate/main.go +++ b/ci/translate/main.go @@ -11,6 +11,7 @@ import ( "time" "github.com/Masterminds/sprig/v3" + "github.com/mitchellh/hashstructure/v2" "github.com/pkg/errors" "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -71,6 +72,11 @@ func main() { logrus.WithError(err).Fatal("loading translation file") } + tfHash, err := hashstructure.Hash(tf, hashstructure.FormatV2, nil) + if err != nil { + logrus.WithError(err).Fatal("hashing source translations") + } + if cfg.AutoTranslate { logrus.Info("auto-translating new strings...") @@ -87,10 +93,16 @@ func main() { } } - logrus.Info("saving translation file...") + tfHashNew, err := hashstructure.Hash(tf, hashstructure.FormatV2, nil) + if err != nil { + logrus.WithError(err).Fatal("hashing processed translations") + } - if err = saveTranslationFile(tf); err != nil { - logrus.WithError(err).Fatal("saving translation file") + if tfHash != tfHashNew { + logrus.Info("saving translation file...") + if err = saveTranslationFile(tf); err != nil { + logrus.WithError(err).Fatal("saving translation file") + } } logrus.Info("updating JS embedded translations...") @@ -276,7 +288,7 @@ func saveTranslationFile(tf translationFile) error { } encoder := yaml.NewEncoder(f) - encoder.SetIndent(2) //nolint:gomnd + encoder.SetIndent(2) //nolint:mnd if err = encoder.Encode(tf); err != nil { f.Close() //nolint:errcheck,gosec,revive // Short-lived fd-leak diff --git a/ci/translate/verify.go b/ci/translate/verify.go index 80e2337..6c21eb9 100644 --- a/ci/translate/verify.go +++ b/ci/translate/verify.go @@ -11,8 +11,6 @@ import ( var langKeyFormat = regexp.MustCompile(`^[a-z]{2}(-[A-Z]{2})?$`) func verify(tf translationFile) error { - var err error - if !langKeyFormat.MatchString(tf.Reference.LanguageKey) { return errors.New("reference contains invalid languageKey") } @@ -29,7 +27,7 @@ func verify(tf translationFile) error { tf.Reference.FormalTranslations, tf.Reference.Translations, false, - ); err != nil { + ) { return errors.New("reference contains error in formalTranslations") } } diff --git a/cmd/ots-cli/cmd_create.go b/cmd/ots-cli/cmd_create.go index 4d4d40d..cefb35e 100644 --- a/cmd/ots-cli/cmd_create.go +++ b/cmd/ots-cli/cmd_create.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "fmt" "io" "mime" @@ -37,12 +38,15 @@ func init() { createCmd.Flags().StringSliceP("header", "H", nil, "Headers to include in the request (i.e. 'Authorization: Token ...')") createCmd.Flags().String("instance", "https://ots.fyi/", "Instance to create the secret with") createCmd.Flags().StringSliceP("file", "f", nil, "File(s) to attach to the secret") + createCmd.Flags().Bool("no-text", false, "Disable secret read (create a secret with only files)") createCmd.Flags().String("secret-from", "-", `File to read the secret content from ("-" for STDIN)`) createCmd.Flags().StringP("user", "u", "", "Username / Password for basic auth, specified as 'user:pass'") rootCmd.AddCommand(createCmd) } func createRunE(cmd *cobra.Command, _ []string) (err error) { + cmd.SilenceUsage = true + var secret client.Secret if client.HTTPClient, err = constructHTTPClient(cmd); err != nil { @@ -51,29 +55,10 @@ func createRunE(cmd *cobra.Command, _ []string) (err error) { // Read the secret content logrus.Info("reading secret content...") - secretSourceName, err := cmd.Flags().GetString("secret-from") - if err != nil { - return fmt.Errorf("getting secret-from flag: %w", err) + if secret.Secret, err = getSecretContent(cmd); err != nil { + return fmt.Errorf("getting secret content: %w", err) } - var secretSource io.Reader - if secretSourceName == "-" { - secretSource = os.Stdin - } else { - f, err := os.Open(secretSourceName) //#nosec:G304 // Opening user specified file is intended - if err != nil { - return fmt.Errorf("opening secret-from file: %w", err) - } - defer f.Close() //nolint:errcheck // The file will be force-closed by program exit - secretSource = f - } - - secretContent, err := io.ReadAll(secretSource) - if err != nil { - return fmt.Errorf("reading secret content: %w", err) - } - secret.Secret = string(secretContent) - // Attach any file given files, err := cmd.Flags().GetStringSlice("file") if err != nil { @@ -93,7 +78,11 @@ func createRunE(cmd *cobra.Command, _ []string) (err error) { }) } - // Create the secret + if secret.Secret == "" && secret.Attachments == nil { + return fmt.Errorf("secret has no content and no attachments") + } + + // Get flags for creation logrus.Info("creating the secret...") instanceURL, err := cmd.Flags().GetString("instance") if err != nil { @@ -105,6 +94,12 @@ func createRunE(cmd *cobra.Command, _ []string) (err error) { return fmt.Errorf("getting expire flag: %w", err) } + // Execute sanity checks + if err = client.SanityCheck(instanceURL, secret); err != nil { + return fmt.Errorf("sanity checking secret: %w", err) + } + + // Create the secret secretURL, expiresAt, err := client.Create(instanceURL, secret, expire) if err != nil { return fmt.Errorf("creating secret: %w", err) @@ -152,6 +147,42 @@ func constructHTTPClient(cmd *cobra.Command) (*http.Client, error) { return &http.Client{Transport: t}, nil } +func getSecretContent(cmd *cobra.Command) (string, error) { + secretSourceName, err := cmd.Flags().GetString("secret-from") + if err != nil { + return "", fmt.Errorf("getting secret-from flag: %w", err) + } + + noSecret, err := cmd.Flags().GetBool("no-text") + if err != nil { + return "", fmt.Errorf("getting no-text flag: %w", err) + } + + var secretSource io.Reader + switch { + case noSecret: + secretSource = bytes.NewReader(nil) + + case secretSourceName == "-": + secretSource = os.Stdin + + default: + f, err := os.Open(secretSourceName) //#nosec:G304 // Opening user specified file is intended + if err != nil { + return "", fmt.Errorf("opening secret-from file: %w", err) + } + defer f.Close() //nolint:errcheck // The file will be force-closed by program exit + secretSource = f + } + + secretContent, err := io.ReadAll(secretSource) + if err != nil { + return "", fmt.Errorf("reading secret content: %w", err) + } + + return strings.TrimSpace(string(secretContent)), nil +} + func (a authRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { if a.user != "" { r.SetBasicAuth(a.user, a.pass) diff --git a/cmd/ots-cli/cmd_fetch.go b/cmd/ots-cli/cmd_fetch.go index ce09228..b738164 100644 --- a/cmd/ots-cli/cmd_fetch.go +++ b/cmd/ots-cli/cmd_fetch.go @@ -16,7 +16,7 @@ import ( const storeFileMode = 0o600 // We assume the attached file to be a secret var fetchCmd = &cobra.Command{ - Use: "fetch url", + Use: "fetch ", Short: "Retrieves a secret from the instance by its URL", Long: "", Args: cobra.ExactArgs(1), @@ -39,6 +39,8 @@ func checkDirWritable(dir string) error { } func fetchRunE(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + fileDir, err := cmd.Flags().GetString("file-dir") if err != nil { return fmt.Errorf("getting file-dir parameter: %w", err) @@ -70,7 +72,7 @@ func fetchRunE(cmd *cobra.Command, args []string) error { func storeAttachment(dir string, f client.SecretAttachment) error { // First lets find a free file name to save the file as var ( - fileNameFragments = strings.SplitN(f.Name, ".", 2) //nolint:gomnd + fileNameFragments = strings.SplitN(f.Name, ".", 2) //nolint:mnd i int storeName = path.Join(dir, f.Name) storeNameTpl string diff --git a/cmd/ots-cli/cmd_root.go b/cmd/ots-cli/cmd_root.go index 48928f8..43316a3 100644 --- a/cmd/ots-cli/cmd_root.go +++ b/cmd/ots-cli/cmd_root.go @@ -3,6 +3,7 @@ package main import ( "fmt" + "github.com/Luzifer/ots/pkg/client" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -28,5 +29,7 @@ func rootPersistentPreRunE(cmd *cobra.Command, _ []string) error { } logrus.SetLevel(ll) + client.Logger = logrus.NewEntry(logrus.StandardLogger()) + return nil } diff --git a/cmd/ots-cli/cmd_version.go b/cmd/ots-cli/cmd_version.go new file mode 100644 index 0000000..8e08a65 --- /dev/null +++ b/cmd/ots-cli/cmd_version.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Displays the tool version", + Run: func(*cobra.Command, []string) { + fmt.Printf("ots-cli %s\n", version) //nolint:forbidigo + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/cmd/ots-cli/go.mod b/cmd/ots-cli/go.mod index 1524256..76196d6 100644 --- a/cmd/ots-cli/go.mod +++ b/cmd/ots-cli/go.mod @@ -1,19 +1,28 @@ module github.com/Luzifer/ots/cmd/ots-cli -go 1.21.1 +go 1.23.0 -replace github.com/Luzifer/ots/pkg/client => ../../pkg/client +toolchain go1.24.5 + +replace ( + github.com/Luzifer/ots/pkg/client => ../../pkg/client + github.com/Luzifer/ots/pkg/customization => ../../pkg/customization +) require ( - github.com/Luzifer/ots/pkg/client v0.0.0-20231009165114-f1e303cbe5a7 + github.com/Luzifer/ots/pkg/client v0.0.0-20250501151834-283ffa548fa8 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.9.1 ) require ( - github.com/Luzifer/go-openssl/v4 v4.2.1 // indirect + github.com/Luzifer/go-openssl/v4 v4.2.4 // indirect + github.com/Luzifer/ots/pkg/customization v0.0.0-20250501151834-283ffa548fa8 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/sys v0.13.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/sys v0.32.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cmd/ots-cli/go.sum b/cmd/ots-cli/go.sum index 2c70960..8c2c466 100644 --- a/cmd/ots-cli/go.sum +++ b/cmd/ots-cli/go.sum @@ -1,29 +1,34 @@ -github.com/Luzifer/go-openssl/v4 v4.2.1 h1:0+/gaQ5TcBhGmVqGrfyA21eujlbbaNwj0VlOA3nh4ts= -github.com/Luzifer/go-openssl/v4 v4.2.1/go.mod h1:CZZZWY0buCtkxrkqDPQYigC4Kn55UuO97TEoV+hwz2s= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/Luzifer/go-openssl/v4 v4.2.4 h1:3Eu3gSeZpr8Ha+IofVnSWttCL1xejRr/lda4l4TZRWk= +github.com/Luzifer/go-openssl/v4 v4.2.4/go.mod h1:ykquxaR0R1Vor83/FAtGBJZZO5zswuSQTVx1FQc1bJY= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/cmd/ots-cli/main.go b/cmd/ots-cli/main.go index 2e7006e..c7801ea 100644 --- a/cmd/ots-cli/main.go +++ b/cmd/ots-cli/main.go @@ -1,6 +1,10 @@ package main -import "os" +import ( + "os" +) + +var version = "dev" func main() { if err := rootCmd.Execute(); err != nil { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0218a90 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: "3.8" +services: + app: + build: + context: https://github.com/Luzifer/ots.git#v1.17.2 + restart: always + environment: + # Optional, see "Customization" in README + #CUSTOMIZE: '/etc/ots/customize.yaml' + # See README for details + REDIS_URL: redis://redis:6379/0 + # 168h = 1w + SECRET_EXPIRY: "604800" + # "mem" or "redis" (See README) + STORAGE_TYPE: redis + depends_on: + - redis + ports: + - 3000:3000 + redis: + image: redis:alpine@sha256:25c0ae32c6c2301798579f5944af53729766a18eff5660bbef196fc2e6214a9c + restart: always + volumes: + - ./data:/data diff --git a/docs/k8s_example.yml b/docs/k8s_example.yml index 063031e..fda2999 100644 --- a/docs/k8s_example.yml +++ b/docs/k8s_example.yml @@ -1,51 +1,42 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: ots - --- apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: redis - namespace: ots + name: ots-redis spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi - --- apiVersion: apps/v1 kind: Deployment metadata: - name: redis - namespace: ots + name: ots-redis labels: - app: redis + app: ots-redis role: leader tier: backend spec: replicas: 1 selector: matchLabels: - app: redis + app: ots-redis template: metadata: labels: - app: redis + app: ots-redis role: leader tier: backend spec: volumes: - name: redis-storage persistentVolumeClaim: - claimName: redis + claimName: ots-redis containers: - name: leader - image: "docker.io/redis:6.2.5-alpine" + image: docker.io/redis:6.2.5-alpine resources: requests: cpu: 100m @@ -53,7 +44,7 @@ spec: ports: - containerPort: 6379 volumeMounts: - - mountPath: "/data" + - mountPath: /data name: redis-storage livenessProbe: tcpSocket: @@ -65,15 +56,13 @@ spec: - redis-cli - ping initialDelaySeconds: 5 - --- apiVersion: v1 kind: Service metadata: - name: redis - namespace: ots + name: ots-redis labels: - app: redis + app: ots-redis role: leader tier: backend spec: @@ -81,16 +70,25 @@ spec: - port: 6379 targetPort: 6379 selector: - app: redis + app: ots-redis role: leader tier: backend - +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: ots-customize +data: + customize.yml: | + appTitle: "My very customized OTS" + disableQRSupport: true + maxAttachmentSizeTotal: 1048576 + maxSecretSize: 2097152 --- apiVersion: apps/v1 kind: Deployment metadata: name: ots - namespace: ots spec: replicas: 2 selector: @@ -103,17 +101,28 @@ spec: app: ots tier: frontend spec: + volumes: + - name: custom + configMap: + name: ots-cutomize containers: - name: ots - image: "luzifer/ots:v0.19.0" - args: ["--storage-type", "redis"] + image: ghcr.io/luzifer/ots:v1.17.2 + args: + - --storage-type + - redis + - --customize + - /custom/customize.yml env: - name: REDIS_URL - value: "tcp://redis.ots.svc.cluster.local:6379" + value: tcp://ots-redis:6379 - name: REDIS_KEY - value: "ots" + value: ots - name: SECRET_EXPIRY value: "172800" + volumeMounts: + - mountPath: /custom + name: custom resources: requests: cpu: 100m @@ -125,13 +134,11 @@ spec: path: / port: 3000 initialDelaySeconds: 5 - --- apiVersion: v1 kind: Service metadata: name: ots - namespace: ots labels: app: ots tier: frontend @@ -142,13 +149,11 @@ spec: selector: app: ots tier: frontend - --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ots - namespace: ots annotations: kubernetes.io/ingress.class: nginx kubernetes.io/tls-acme: "true" @@ -167,4 +172,4 @@ spec: tls: - hosts: - ots.example.com - secretName: ingress-tls + secretName: ots-ingress-tls diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..3f1510a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,126 @@ +import globals from 'globals' +import js from '@eslint/js' +import typescriptEslint from '@typescript-eslint/eslint-plugin' +import vue from 'eslint-plugin-vue' + +export default [ + js.configs.recommended, + ...vue.configs['flat/recommended'], + { + languageOptions: { + ecmaVersion: 'latest', + globals: { + ...globals.browser, + ...globals.node, + process: true, + }, + + parserOptions: { + parser: '@typescript-eslint/parser', + requireConfigFile: true, + }, + }, + + linterOptions: { + reportUnusedDisableDirectives: true, + }, + + plugins: { + '@typescript-eslint': typescriptEslint, + vue, + }, + + rules: { + 'array-bracket-newline': ['error', { multiline: true }], + 'array-bracket-spacing': ['error'], + 'arrow-body-style': ['error', 'as-needed'], + 'arrow-parens': ['error', 'as-needed'], + 'arrow-spacing': ['error', { after: true, before: true }], + 'block-spacing': ['error'], + 'brace-style': ['error', '1tbs'], + 'camelcase': ['warn'], + 'comma-dangle': ['error', 'always-multiline'], + 'comma-spacing': ['error'], + 'comma-style': ['error', 'last'], + 'curly': ['error'], + 'default-case-last': ['error'], + 'default-param-last': ['error'], + 'dot-location': ['error', 'property'], + 'dot-notation': ['error'], + 'eol-last': ['error', 'always'], + 'eqeqeq': ['error', 'always', { null: 'ignore' }], + 'func-call-spacing': ['error', 'never'], + 'function-paren-newline': ['error', 'multiline'], + 'generator-star-spacing': ['off'], + 'implicit-arrow-linebreak': ['error'], + 'indent': ['error', 2], + 'key-spacing': ['error', { afterColon: true, beforeColon: false, mode: 'strict' }], + 'keyword-spacing': ['error'], + 'linebreak-style': ['error', 'unix'], + 'lines-between-class-members': ['error'], + 'multiline-comment-style': ['off'], + 'newline-per-chained-call': ['error'], + 'no-alert': ['error'], + 'no-console': ['off'], + 'no-debugger': 'off', + 'no-duplicate-imports': ['error'], + 'no-else-return': ['error'], + 'no-empty-function': ['error'], + 'no-extra-parens': ['error'], + 'no-implicit-coercion': ['error'], + 'no-lonely-if': ['error'], + 'no-multi-spaces': ['error'], + 'no-multiple-empty-lines': ['warn', { max: 2, maxBOF: 0, maxEOF: 0 }], + 'no-promise-executor-return': ['error'], + 'no-return-assign': ['error'], + 'no-script-url': ['error'], + 'no-template-curly-in-string': ['error'], + 'no-trailing-spaces': ['error'], + 'no-unneeded-ternary': ['error'], + 'no-unreachable-loop': ['error'], + 'no-unsafe-optional-chaining': ['error'], + 'no-useless-return': ['error'], + 'no-var': ['error'], + 'no-warning-comments': ['error'], + 'no-whitespace-before-property': ['error'], + 'object-curly-newline': ['error', { consistent: true }], + 'object-curly-spacing': ['error', 'always'], + 'object-shorthand': ['error'], + 'padded-blocks': ['error', 'never'], + 'prefer-arrow-callback': ['error'], + 'prefer-const': ['error'], + 'prefer-object-spread': ['error'], + 'prefer-rest-params': ['error'], + 'prefer-template': ['error'], + 'quote-props': ['error', 'consistent-as-needed', { keywords: false }], + 'quotes': ['error', 'single', { allowTemplateLiterals: true }], + 'require-atomic-updates': ['error'], + 'require-await': ['error'], + 'semi': ['error', 'never'], + 'sort-imports': ['error', { ignoreCase: true, ignoreDeclarationSort: false, ignoreMemberSort: false }], + 'sort-keys': ['error', 'asc', { caseSensitive: true, natural: false }], + 'space-before-blocks': ['error', 'always'], + 'space-before-function-paren': ['error', 'never'], + 'space-in-parens': ['error', 'never'], + 'space-infix-ops': ['error'], + 'space-unary-ops': ['error', { nonwords: false, words: true }], + 'spaced-comment': ['warn', 'always'], + 'switch-colon-spacing': ['error'], + 'template-curly-spacing': ['error', 'never'], + 'unicode-bom': ['error', 'never'], + 'vue/comment-directive': 'off', + 'vue/new-line-between-multi-line-property': ['error'], + 'vue/no-empty-component-block': ['error'], + 'vue/no-reserved-component-names': ['error'], + 'vue/no-template-target-blank': ['error'], + 'vue/no-unused-properties': ['error'], + 'vue/no-unused-refs': ['error'], + 'vue/no-useless-mustaches': ['error'], + 'vue/order-in-components': ['off'], + 'vue/require-name-property': ['error'], + 'vue/v-for-delimiter-style': ['error'], + 'wrap-iife': ['error'], + 'yoda': ['error'], + }, + }, +] diff --git a/frontend/index.html b/frontend/index.html index 8e9e77a..cceea93 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - {{ range (list "webfonts/fa-solid-900.woff2" "webfonts/fa-brands-400.woff2" "lato-v20-latin-ext_latin-regular.woff2" "lato-v20-latin-ext_latin-700.woff2") }} + {{ range (list "fa-solid-900.woff2" "fa-brands-400.woff2") }} - - {{ .Customize.AppTitle }} diff --git a/go.mod b/go.mod index 4836374..39f9af0 100644 --- a/go.mod +++ b/go.mod @@ -1,33 +1,45 @@ module github.com/Luzifer/ots -go 1.20 +go 1.23.0 + +toolchain go1.24.5 + +replace github.com/Luzifer/ots/pkg/customization => ./pkg/customization require ( - github.com/Luzifer/go_helpers/v2 v2.20.0 - github.com/Luzifer/rconfig/v2 v2.4.0 - github.com/Masterminds/sprig/v3 v3.2.3 + github.com/Luzifer/go_helpers/v2 v2.25.0 + github.com/Luzifer/ots/pkg/customization v0.0.0-20250501151834-283ffa548fa8 + github.com/Luzifer/rconfig/v2 v2.6.0 + github.com/Masterminds/sprig/v3 v3.3.0 github.com/gofrs/uuid v4.4.0+incompatible - github.com/gorilla/mux v1.8.0 + github.com/gorilla/mux v1.8.1 github.com/pkg/errors v0.9.1 - github.com/redis/go-redis/v9 v9.2.1 + github.com/prometheus/client_golang v1.22.0 + github.com/redis/go-redis/v9 v9.11.0 github.com/sirupsen/logrus v1.9.3 - gopkg.in/yaml.v2 v2.4.0 ) require ( + dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/google/uuid v1.3.1 // indirect - github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/sys v0.13.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.63.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/sys v0.32.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/validator.v2 v2.0.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 25148de..4f21fb6 100644 --- a/go.sum +++ b/go.sum @@ -1,116 +1,94 @@ -github.com/Luzifer/go_helpers/v2 v2.20.0 h1:OyCUs7TFGwfJpGqD21KEKKOXy92jetw2l7dlmG7HZnA= -github.com/Luzifer/go_helpers/v2 v2.20.0/go.mod h1:KPGjImwm51SmOTZMd9XUsT241gHYJuEyLrS/omQ4/Dw= -github.com/Luzifer/rconfig/v2 v2.4.0 h1:MAdymTlExAZ8mx5VG8xOFAtFQSpWBipKYQHPOmYTn9o= -github.com/Luzifer/rconfig/v2 v2.4.0/go.mod h1:hWF3ZVSusbYlg5bEvCwalEyUSY+0JPJWUiIu7rBmav8= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Luzifer/go_helpers/v2 v2.25.0 h1:k1J4gd1+BfuokTDoWgcgib9P5mdadjzKEgbtKSVe46k= +github.com/Luzifer/go_helpers/v2 v2.25.0/go.mod h1:KSVUdAJAav5cWGyB5oKGxmC27HrKULVTOxwPS/Kr+pc= +github.com/Luzifer/rconfig/v2 v2.6.0 h1:ZKgsO2Wt/XZXawuAZCDkW7xszxZ8hQDTV1Wm63Jvnqk= +github.com/Luzifer/rconfig/v2 v2.6.0/go.mod h1:CxISRwCV2WjO5gnUnaRGDq17u1M3TvmFgzJLr87ejtc= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= -github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= -github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= +github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs= +github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..7c8e5e3 --- /dev/null +++ b/helpers.go @@ -0,0 +1,52 @@ +package main + +import ( + "net" + "net/http" + + "github.com/Luzifer/ots/pkg/metrics" + "github.com/Luzifer/ots/pkg/storage" + "github.com/sirupsen/logrus" +) + +func requestInSubnetList(r *http.Request, subnets []string) bool { + if len(subnets) == 0 { + // No subnets specififed: None allowed (without doing the parsing) + return false + } + + remote, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + logrus.WithError(err).Error("parsing remote address") + return false + } + + remoteIP := net.ParseIP(remote) + if remoteIP == nil { + logrus.WithError(err).Error("parsing remote address") + return false + } + + for _, sn := range subnets { + _, netw, err := net.ParseCIDR(sn) + if err != nil { + logrus.WithError(err).WithField("subnet", sn).Warn("invalid subnet specified") + continue + } + + if netw.Contains(remoteIP) { + return true + } + } + + return false +} + +func updateStoredSecretsCount(store storage.Storage, collector *metrics.Collector) { + n, err := store.Count() + if err != nil { + logrus.WithError(err).Error("counting stored secrets") + return + } + collector.UpdateSecretsCount(n) +} diff --git a/i18n.yaml b/i18n.yaml index 668a8ab..07ba703 100644 --- a/i18n.yaml +++ b/i18n.yaml @@ -4,6 +4,7 @@ reference: translators: - Luzifer translations: + alert-insecure-environment: You are accessing this instance using an insecure connection. You will not be able to create or read secrets. alert-secret-not-found: This is not the secret you are looking for… - If you expected the secret to be here it might be compromised as someone else might have opened the link already. alert-something-went-wrong: Something went wrong. I'm very sorry about this… btn-create-secret: Create the secret! @@ -12,6 +13,7 @@ reference: btn-reveal-secret: Show me the secret! btn-reveal-secret-processing: Secret is being decrypted… btn-show-explanation: How does this work? + btn-theme-switcher-auto: Auto expire-default: Default Expiry expire-n-days: '{n} day | {n} days' expire-n-hours: '{n} hour | {n} hours' @@ -32,17 +34,22 @@ reference: text-burn-hint: Please remember not to go to this URL yourself as that would destroy the secret. Just pass it to someone else! text-burn-time: 'If not viewed before, this secret will automatically be deleted:' text-hint-burned: Attention: You're only seeing this once. As soon as you reload the page the secret will be gone so maybe copy it now… + text-invalid-files-selected: At least one of the selected files is not allowed as an attachment. text-max-filesize: 'Maximum size: {maxSize}' text-max-filesize-exceeded: 'The file(s) you chose are too big to attach: {curSize} / {maxSize}' text-powered-by: Powered by text-pre-reveal-hint: To reveal the secret click this button but be aware doing so will destroy the secret. You can only view it once! text-pre-url: 'Your secret was created and stored using this URL:' + text-secret-burned: The secret was successfully destroyed. text-secret-create-disabled: The creation of new secrets is disabled in this instance. title-explanation: This is how it works… title-new-secret: Create a new secret title-reading-secret: Reading your secret… title-secret-create-disabled: Secret creation disabled… title-secret-created: Secret created! + tooltip-burn-secret: Burn Secret now! + tooltip-copy-to-clipboard: Copy to Clipboard + tooltip-download-as-file: Download as File translations: ca: translators: @@ -92,6 +99,7 @@ translations: translators: - Luzifer translations: + alert-insecure-environment: Du besuchst diese Instanz über eine unsichere Verbindung. Du kannst deswegen keine Secrets erstellen oder lesen. alert-secret-not-found: Das ist nicht das Secret, was du suchst… - Falls du diesen Link noch nicht selbst geöffnet hast, könnte das Secret kompromittiert sein, da jemand anderes den Link geöffnet haben könnte. alert-something-went-wrong: Irgendwas ging schief. Entschuldigung… btn-create-secret: Secret erstellen! @@ -100,6 +108,7 @@ translations: btn-reveal-secret: Zeig mir das Secret! btn-reveal-secret-processing: Secret wird entschlüsselt… btn-show-explanation: Wie funktioniert das? + btn-theme-switcher-auto: Auto expire-default: Server-Standard expire-n-days: '{n} Tag | {n} Tage' expire-n-hours: '{n} Stunde | {n} Stunden' @@ -120,18 +129,24 @@ translations: text-burn-hint: Bitte rufe die URL nicht selbst auf, da das Secret dadurch zerstört würde. Gib sie einfach weiter! text-burn-time: 'Wenn es vorher nicht eingesehen wurde, wird dieses Secret automatisch gelöscht:' text-hint-burned: Achtung: Du kannst das nur einmal ansehen! Sobald du die Seite neu lädst, ist das Secret verschwunden, also besser direkt kopieren und sicher abspeichern… + text-invalid-files-selected: Mindestens eine der ausgewählten Dateien ist nicht als Anhang erlaubt. text-max-filesize: 'Maximale Größe: {maxSize}' text-max-filesize-exceeded: 'Die ausgewählten Dateien übersteigen die maximale Größe: {curSize} / {maxSize}' text-powered-by: Läuft mit text-pre-reveal-hint: Um das Secret anzuzeigen klicke diesen Button aber denk dran, dass das Secret nur einmal angezeigt und dabei gelöscht wird. text-pre-url: 'Dein Secret wurde angelegt und unter folgender URL gespeichert:' + text-secret-burned: Das Secret wurde zerstört. text-secret-create-disabled: Auf dieser Instanz wurde das Erstellen neuer Secrets deaktiviert. title-explanation: So funktioniert es… title-new-secret: Erstelle ein neues Secret title-reading-secret: Secret auslesen… title-secret-create-disabled: Erstellen von Secrets deaktiviert… title-secret-created: Secret erstellt! + tooltip-burn-secret: Secret jetzt zerstören! + tooltip-copy-to-clipboard: In die Zwischenablage kopieren + tooltip-download-as-file: Als Datei herunterladen formalTranslations: + alert-insecure-environment: Sie besuchen diese Instanz über eine unsichere Verbindung. Sie können deswegen keine Secrets erstellen oder lesen. alert-secret-not-found: Dieses Secret existiert nicht. - Falls Sie diesen Link noch nicht selbst geöffnet haben, könnte der Inhalt kompromittiert sein, da jemand anderes den Link geöffnet haben könnte. btn-reveal-secret: Secret anzeigen items-explanation: @@ -199,9 +214,12 @@ translations: alert-secret-not-found: Ce secret n'est pas celui que vous cherchez… - Si vous comptiez trouvez ce secret ici, il a pu être compromis car quelqu'un a probablement déjà ouvert le lien. alert-something-went-wrong: Un problème est survenu. Nous en sommes désolés… btn-create-secret: Créer le secret! + btn-create-secret-processing: Secret en cours de création ... btn-new-secret: Nouveau secret btn-reveal-secret: Voir le secret! + btn-reveal-secret-processing: Secret en cours de déchiffrement ... btn-show-explanation: Comment ça fonctionne? + expire-default: Expiration par défaut expire-n-days: '{n} jour | {n} jours' expire-n-hours: '{n} heure | {n} heures' expire-n-minutes: '{n} minute | {n} minutes' @@ -212,11 +230,18 @@ translations: - Seul le secret chiffré est envoyé au serveur (ni le secret en clair, ni le mot de passe ne sont envoyés!) - Le serveur stocke le secret chiffré pendant un certain temps - Vous fournissez l'URL affichée contenant l'identifiant et le mot de passe de déchiffrage au destinataire - - 'Le destintaire ne peut voir le secret qu''une fois: si cela ne fonctionne pas, c''est que le secret a été consulté par quelqu''un d''autre!' + - 'Le destinataire ne peut voir le secret qu''une fois: si cela ne fonctionne pas, c''est que le secret a été consulté par quelqu''un d''autre!' - Dès que le secret chiffré a été récupéré, il est supprimé du serveur + label-expiry: 'Expiration dans:' label-secret-data: 'Données secrètes:' + label-secret-files: 'Attacher des fichiers:' + text-attached-files: L'émetteur a attaché des fichiers au secret. Assurez-vous d'avoir confiance en l'émetteur, les fichiers n'ont pas été vérifiés ! text-burn-hint: Attention de ne pas ouvrir cette URL vous-même, cela détruirait le secret. Fournissez-la à quelqu'un d'autre! + text-burn-time: 'S''il n''a pas été vu auparavant, ce secret sera automatiquement supprimé :' text-hint-burned: Attention: Vous ne pouvez consulter ce contenu qu'une fois. Le secret sera détruit dès que vous rechargez la page, donc copiez le maintenant… + text-invalid-files-selected: Au moins l'un des fichiers sélectionnés n'est pas autorisé comme pièce-jointe. + text-max-filesize: 'Taille maximum: {maxSize}' + text-max-filesize-exceeded: 'Le(s) fichier(s) que vous avez choisis sont trop volumineux pour être attachés : {curSize} / {maxSize}' text-powered-by: Propulsé par text-pre-reveal-hint: Pour afficher le secret, cliquez sur ce bouton, mais soyez conscient que cela le détruira. Vous ne pouvez l'afficher qu'une fois! text-pre-url: 'Votre secret a été créé et stocké à cette URL:' @@ -226,6 +251,54 @@ translations: title-reading-secret: Lecture du secret… title-secret-create-disabled: Création secrète désactivée... title-secret-created: Secret créé! + tooltip-copy-to-clipboard: Copier dans le presse-papiers + tooltip-download-as-file: Télécharger en tant que fichier + it: + deeplLanguage: it + translators: [] + translations: + alert-secret-not-found: Questo non è il secret che stai cercando… - Se ti aspettavi di vedere il secret allora potrebbe essere stato compromesso poichè qualcun altro potrebbe aver già aperto il link. + alert-something-went-wrong: Qualcosa non ha funzionato. Mi dispiace davvero… + btn-create-secret: Crea il secret! + btn-create-secret-processing: Creazione del secret in corso… + btn-new-secret: Nuovo secret + btn-reveal-secret: Mostrami il secret! + btn-reveal-secret-processing: Decrittazione del secret in corso… + btn-show-explanation: Come funziona? + expire-default: Scadenza predefinita + expire-n-days: '{n} giorno | {n} giorni' + expire-n-hours: '{n} ora | {n} ore' + expire-n-minutes: '{n} minuto | {n} minuti' + expire-n-seconds: '{n} secondo | {n} secondi' + items-explanation: + - Inserisci un secret nel campo di testo di questa pagina + - Il tuo browser critta il secret usando una password generata in modo casuale + - Solo il secret crittato viene inviato al server (nè il secret decrittato nè la password vengono mai inviati!) + - Il server conserva il secret crittato per un certo periodo di tempo + - Passi l'URL visualizzato contenente l'ID e la password di decrittazione al destinatario + - 'Il destinatario può vedere il secret esattamente una sola volta: se non può, il secret potrebbe essere stato visto da qualcun altro!' + - Dopo che il secret crittato è stato visualizzato la prima volta, viene cancellato dal server + label-expiry: 'Scade in:' + label-secret-data: 'Dati del secret:' + label-secret-files: 'Allega files:' + text-attached-files: Il mittente ha allegato alcuni files al secret. Assicurati di fidarti del mittente perchè i files non sono stati controllati! + text-burn-hint: Per favore ricorda di non visitare questo URL perchè il secret verrebbe cancellato. Passalo semplicemente a qualcun altro! + text-burn-time: 'Se non viene visualizzato prima, questo secret verrà cancellato automaticamente:' + text-hint-burned: Attenzione: Vedrai il secret solo questa volta. Non appena ricaricherai la pagina verrà cancellato, quindi magari copialo ora… + text-invalid-files-selected: Almeno uno dei files selezionati non è consentito come allegato. + text-max-filesize: 'Dimensione massima: {maxSize}' + text-max-filesize-exceeded: 'Il/I file(s) che hai scelto ha/hanno una dimensione troppo grande per essere allegato/allegati: {curSize} / {maxSize}' + text-powered-by: Realizzato con + text-pre-reveal-hint: Per rivelare il secret clicca su questo pulsante, ma attenzione perchè farlo lo cancellerà. Lo puoi vedere solo una volta! + text-pre-url: 'Il tuo secret è stato creato e salvato usando questo URL:' + text-secret-create-disabled: La creazione di nuovi secrets è disabilitata su questa istanza. + title-explanation: Questo è come funziona… + title-new-secret: Crea un nuovo secret + title-reading-secret: Leggi il tuo secret… + title-secret-create-disabled: Creazione secret disabilitata… + title-secret-created: Secret creato! + tooltip-copy-to-clipboard: Copia nella clipboard + tooltip-download-as-file: Scarica come file lv: deeplLanguage: lv translators: [] @@ -263,6 +336,50 @@ translations: nl: deeplLanguage: nl translators: [] + translations: + alert-secret-not-found: De gegevens die je zocht bestaan niet (meer)… - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken! + alert-something-went-wrong: Er ging iets verkeerd, sorry… + btn-create-secret: Geheim aanmaken! + btn-create-secret-processing: Geheim wordt aangemaakt... + btn-new-secret: Nieuw + btn-reveal-secret: Toon mij het geheim! + btn-reveal-secret-processing: Geheim wordt ontsleuteld... + btn-show-explanation: Hoe werkt dit? + btn-theme-switcher-auto: Auto + expire-default: Standaard vervaltermijn + expire-n-days: '{n} dag | {n} dagen' + expire-n-hours: '{n} uur | {n} uur' + expire-n-minutes: '{n} minuut | {n} minuten' + expire-n-seconds: '{n} seconde | {n} seconden' + items-explanation: + - Je vult vertrouwelijke informatie in op deze pagina. + - Je browser versleutelt de ingevulde tekst via een automatisch gegenereerd wachtwoord. + - Alleen de versleutelde data wordt naar de server gestuurd (de onversleutelde data of het wachtwoord worden nooit verstuurd!) + - De server slaat de versleutelde data voor een bepaalde periode op + - Je geeft de URL met ID en het gegenereerde wachtwoord aan de ontvanger. + - 'De ontvanger kan de vertrouwelijke informatie exact eenmaal bekijken: indien het niet lukt heeft mogelijk iemand anders de info gezien!' + - Nadat het versleutelde geheim eenmalig bekeken is, wordt deze van de server verwijderd + label-expiry: 'Verwijder na:' + label-secret-data: 'Vertrouwelijke info:' + label-secret-files: 'Bestanden toevoegen:' + text-attached-files: De afzender heeft bestanden toegevoegd. Deze zijn niet gecontroleerd, gebruik deze enkel als je de afzender vertrouwt! + text-burn-hint: Open de URL niet zelf, deze is slechts eenmalig te gebruiken. Geef de URL aan de ontvanger. + text-burn-time: 'Deze vertrouwelijke informatie wordt automatisch gewist indien niet bekeken voor:' + text-hint-burned: Opgelet: Je ziet deze informatie alleen nu. Je kan het niet meer opnieuw opvragen als je de pagina verlaat. + text-max-filesize: 'Maximum grootte: {maxSize}' + text-max-filesize-exceeded: 'De bestanden die je toevoegde zijn te groot: {curSize} / {maxSize}' + text-powered-by: Mogelijk gemaakt door + text-pre-reveal-hint: 'Gebruik deze knop om het geheim weer te geven. Let op: Je kan dit slechts eenmaal doen!' + text-pre-url: 'Het geheim kan opgevraagd worden via deze URL:' + text-secret-create-disabled: Het aanmaken van nieuwe geheimen is in deze omgeving uitgeschakeld. + title-explanation: Dit is hoe het werkt… + title-new-secret: Nieuw geheim aanmaken + title-reading-secret: Geheim wordt gelezen… + title-secret-create-disabled: Aanmaken geheimen uitgeschakeld... + title-secret-created: Geheim aangemaakt! + nl-BE: + deeplLanguage: nl-BE + translators: [] translations: alert-secret-not-found: De gegevens die je zocht bestaan niet (meer)… - Als je hier informatie verwachtte dan is de link mogelijk al door iemand anders bekeken! alert-something-went-wrong: Er ging iets verkeerd, sorry… @@ -308,6 +425,7 @@ translations: translators: - Icikowski translations: + alert-insecure-environment: Odwiedzasz tę instancję przez niezabezpieczone połączenie. Nie będziesz mógł tworzyć ani odczytywać sekretów. alert-secret-not-found: To nie jest sekret, którego szukasz… - Jeśli spodziewałeś się tu sekretu, to może być on zagrożony, ponieważ ktoś inny mógł już otworzyć ten link. alert-something-went-wrong: Coś poszło nie tak. Bardzo mi przykro… btn-create-secret: Stwórz sekret! @@ -316,6 +434,7 @@ translations: btn-reveal-secret: Pokaż mi sekret! btn-reveal-secret-processing: Sekret jest odszyfrowywany... btn-show-explanation: Jak to działa? + btn-theme-switcher-auto: Auto expire-default: Domyślne wygasanie expire-n-days: '{n} dzień | {n} dni' expire-n-hours: '{n} godzina | {n} godzin(y)' @@ -332,21 +451,26 @@ translations: label-expiry: 'Wygasa po:' label-secret-data: 'Dane sekretu:' label-secret-files: 'Dołącz pliki:' - text-attached-files: Nadawca dołączył pliki do tego skeretu. Upewnij się, że ufasz nadawcy, bo pliki nie są sprawdzane! + text-attached-files: Nadawca dołączył pliki do tego sekretu. Upewnij się, że ufasz nadawcy, bo pliki nie są sprawdzane! text-burn-hint: Pamiętaj, aby nie przechodzić na ten adres URL samemu, ponieważ to zniszczy sekret. Po prostu przekaż go odbiorcy! text-burn-time: 'Jeżeli nie zostanie wyświetlony, ten sekret zostanie automatycznie usunięty:' text-hint-burned: Uwaga: Zobaczysz to tylko raz. Gdy odświeżysz stronę, to sekret nie będzie już dostępny, więc lepiej skopiuj go teraz… + text-invalid-files-selected: Co najmniej jeden z załączonych plików nie jest dopuszczalny jako załącznik. text-max-filesize: 'Maksymalny rozmiar: {maxSize}' - text-max-filesize-exceeded: 'Wybrane załączniki przekraczają makrymalny rozmiar: {curSize} / {maxSize}' + text-max-filesize-exceeded: 'Wybrane załączniki przekraczają maksymalny rozmiar: {curSize} / {maxSize}' text-powered-by: Obsługiwane przez text-pre-reveal-hint: Aby odsłonić sekret, naciśnij ten przycisk, jednak wiedz, że to zniszczy sekret. Możesz go zobaczyć tylko raz! text-pre-url: 'Twój sekret został stworzony i zachowany pod tym adresem URL:' + text-secret-burned: Sekret został pomyślnie zniszczony. text-secret-create-disabled: Tworzenie nowych sekretów jest wyłączone na tej instancji. title-explanation: Oto, jak to działa… title-new-secret: Stwórz nowy sekret title-reading-secret: Odczytywanie Twojego sekretu… title-secret-create-disabled: Tworzenie sekretów wyłączone… title-secret-created: Sekret utworzony! + tooltip-burn-secret: Zniszcz sekret teraz! + tooltip-copy-to-clipboard: Skopiuj do schowka + tooltip-download-as-file: Pobierz jako plik pt-BR: deeplLanguage: pt-BR translators: [] @@ -427,11 +551,12 @@ translations: title-secret-created: Секрет создан! sv: deeplLanguage: sv - translators: [] + translators: + - artingu translations: alert-secret-not-found: Hemlighet hittades inte… - Om du förväntade dig att hemligheten skulle finnas här kan den vara röjd då någon annan kan ha öppnat denna länk tidigare. alert-something-went-wrong: Något gick fel. Jag ber om ursäkt för detta!… - btn-create-secret: Skapa hemliget! + btn-create-secret: Skapa hemlighet! btn-create-secret-processing: Hemlighet håller på att skapas.. btn-new-secret: Ny hemlighet. btn-reveal-secret: Visa mig hemligheten! @@ -466,7 +591,7 @@ translations: title-explanation: Såhär fungerar det… title-new-secret: Skapa ny hemlighet title-reading-secret: Läs din hemlighet… - title-secret-create-disabled: Hemlig skapelse avaktiverad... + title-secret-create-disabled: Skapande av hemlighet avaktiverat... title-secret-created: Hemlighet skapad! tr: deeplLanguage: tr @@ -577,6 +702,7 @@ translations: text-burn-hint: 请您记住不要自己访问此链接,因为这会导致机密被销毁。请原封不动的转发给收件人吧! text-burn-time: 如果机密没有被查看,将会在此时间自动销毁: text-hint-burned: 注意:您只能查看一次机密,请尽快拷贝!刷新页面也会导致机密消失! + text-invalid-files-selected: 选中了至少一个不允许被作为附件的文件。 text-max-filesize: 附加文件大小上限:{maxSize} text-max-filesize-exceeded: 您选择的文件过大,无法附加:{curSize} / {maxSize} text-powered-by: Powered by diff --git a/main.go b/main.go index 6ffc081..376854a 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,8 @@ import ( file_helpers "github.com/Luzifer/go_helpers/v2/file" http_helpers "github.com/Luzifer/go_helpers/v2/http" + "github.com/Luzifer/ots/pkg/customization" + "github.com/Luzifer/ots/pkg/metrics" "github.com/Luzifer/rconfig/v2" ) @@ -27,14 +29,18 @@ var ( cfg struct { Customize string `flag:"customize" default:"" description:"Customize-File to load"` Listen string `flag:"listen" default:":3000" description:"IP/Port to listen on"` + LogRequests bool `flag:"log-requests" default:"true" description:"Enable request logging"` LogLevel string `flag:"log-level" default:"info" description:"Set log level (debug, info, warning, error)"` SecretExpiry int64 `flag:"secret-expiry" default:"0" description:"Maximum expiry of the stored secrets in seconds"` - StorageType string `flag:"storage-type" default:"mem" description:"Storage to use for putting secrets to" validate:"nonzero"` + StorageType string `flag:"storage-type" default:"mem" description:"Storage to use for putting secrets to" validate:"nonzero"` //revive:disable-line:struct-tag // Matches wrong validation library VersionAndExit bool `flag:"version" default:"false" description:"Print version information and exit"` + EnableTLS bool `flag:"enable-tls" default:"false" description:"Enable HTTPS/TLS"` + CertFile string `flag:"cert-file" default:"" description:"Path to the TLS certificate file"` + KeyFile string `flag:"key-file" default:"" description:"Path to the TLS private key file"` } assets file_helpers.FSStack - cust customize + cust customization.Customize indexTpl *template.Template version = "dev" @@ -70,7 +76,7 @@ func initApp() error { } logrus.SetLevel(l) - if cust, err = loadCustomize(cfg.Customize); err != nil { + if cust, err = customization.Load(cfg.Customize); err != nil { return errors.Wrap(err, "loading customizations") } @@ -98,6 +104,9 @@ func main() { os.Exit(0) } + // Initialize metrics collector + collector := metrics.New() + // Initialize index template in order not to parse it multiple times source, err := assets.ReadFile("index.html") if err != nil { @@ -110,29 +119,64 @@ func main() { if err != nil { logrus.WithError(err).Fatal("initializing storage") } - api := newAPI(store) + api := newAPI(store, collector) + // Initialize server r := mux.NewRouter() - r.Use(http_helpers.GzipHandler) api.Register(r.PathPrefix("/api").Subrouter()) - r.HandleFunc("/", handleIndex) - r.PathPrefix("/").HandlerFunc(assetDelivery) + r.Handle("/metrics", metrics.Handler()). + Methods(http.MethodGet). + MatcherFunc(func(r *http.Request, _ *mux.RouteMatch) bool { + return requestInSubnetList(r, cust.MetricsAllowedSubnets) + }) + r.HandleFunc("/", handleIndex). + Methods(http.MethodGet) + r.PathPrefix("/").HandlerFunc(assetDelivery). + Methods(http.MethodGet) + + var hdl http.Handler = r + hdl = http_helpers.GzipHandler(hdl) + if cfg.LogRequests { + hdl = http_helpers.NewHTTPLogHandlerWithLogger(hdl, logrus.StandardLogger()) + } + + server := &http.Server{ + Addr: cfg.Listen, + Handler: hdl, + ReadHeaderTimeout: time.Second, + } + + // Start periodic stored metrics update (required for multi-instance + // OTS hosting as other instances will create / delete secrets and + // we need to keep up with that) + go func() { + for t := time.NewTicker(time.Minute); ; <-t.C { + updateStoredSecretsCount(store, collector) + } + }() + + // Start server logrus.WithFields(logrus.Fields{ "secret_expiry": time.Duration(cfg.SecretExpiry) * time.Second, "version": version, }).Info("ots started") - server := &http.Server{ - Addr: cfg.Listen, - Handler: http_helpers.NewHTTPLogHandlerWithLogger(r, logrus.StandardLogger()), - ReadHeaderTimeout: time.Second, - } - - if err = server.ListenAndServe(); err != nil { - logrus.WithError(err).Fatal("HTTP server quit unexpectedly") + if cfg.EnableTLS { + if cfg.CertFile == "" || cfg.KeyFile == "" { + logrus.Fatal("TLS is enabled but cert-file or key-file is not provided") + } + logrus.Infof("Starting HTTPS server on %s", cfg.Listen) + if err := server.ListenAndServeTLS(cfg.CertFile, cfg.KeyFile); err != nil { + logrus.WithError(err).Fatal("HTTPS server quit unexpectedly") + } + } else { + logrus.Infof("Starting HTTP server on %s", cfg.Listen) + if err := server.ListenAndServe(); err != nil { + logrus.WithError(err).Fatal("HTTP server quit unexpectedly") + } } } @@ -182,7 +226,7 @@ func handleIndex(w http.ResponseWriter, _ *http.Request) { w.Header().Set("X-Content-Type-Options", "nosniff") if err := indexTpl.Execute(w, struct { - Customize customize + Customize customization.Customize InlineContentNonce string MaxSecretExpiry int64 Version string diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 099d9fd..0000000 --- a/package-lock.json +++ /dev/null @@ -1,4346 +0,0 @@ -{ - "name": "ots", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "ots", - "dependencies": { - "base64-js": "^1.5.1", - "bootstrap": "^5.3.2", - "qrcode": "^1.5.3", - "vue": "^2.7.14", - "vue-i18n": "^8.28.2", - "vue-router": "^3.6.5" - }, - "devDependencies": { - "@babel/eslint-parser": "^7.22.5", - "esbuild": "^0.17.17", - "esbuild-sass-plugin": "^2.9.0", - "esbuild-vue": "^1.2.2", - "eslint": "^8.42.0", - "eslint-plugin-vue": "^9.14.1", - "vue-template-compiler": "^2.7.14" - } - }, - "node_modules/@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", - "dev": true - }, - "node_modules/@babel/code-frame": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", - "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/highlight": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", - "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.15.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.5.tgz", - "integrity": "sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-compilation-targets": "^7.15.4", - "@babel/helper-module-transforms": "^7.15.4", - "@babel/helpers": "^7.15.4", - "@babel/parser": "^7.15.5", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/eslint-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.5.tgz", - "integrity": "sha512-C69RWYNYtrgIRE5CmTd77ZiLDXqgBipahJc/jHP3sLcAGj6AJzxNIuKNpVnICqbyK7X3pFUfEvL++rvtbQpZkQ==", - "dev": true, - "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.11.0", - "eslint": "^7.5.0 || ^8.0.0" - } - }, - "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@babel/generator": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.4.tgz", - "integrity": "sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", - "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/compat-data": "^7.15.0", - "@babel/helper-validator-option": "^7.14.5", - "browserslist": "^4.16.6", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", - "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.15.4", - "@babel/template": "^7.15.4", - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", - "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", - "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", - "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", - "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.4.tgz", - "integrity": "sha512-9fHHSGE9zTC++KuXLZcB5FKgvlV83Ox+NLUmQTawovwlJ85+QMhk1CnVk406CQVj97LaWod6KVjl2Sfgw9Aktw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.15.4", - "@babel/helper-replace-supers": "^7.15.4", - "@babel/helper-simple-access": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/helper-validator-identifier": "^7.14.9", - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", - "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", - "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.15.4", - "@babel/helper-optimise-call-expression": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", - "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", - "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", - "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", - "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/template": "^7.15.4", - "@babel/traverse": "^7.15.4", - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", - "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", - "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/helper-function-name": "^7.15.4", - "@babel/helper-hoist-variables": "^7.15.4", - "@babel/helper-split-export-declaration": "^7.15.4", - "@babel/parser": "^7.15.4", - "@babel/types": "^7.15.4", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.15.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", - "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.9", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "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/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.2", - "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/eslintrc/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "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, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dev": true, - "dependencies": { - "eslint-scope": "5.1.1" - } - }, - "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, - "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, - "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, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz", - "integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==", - "dependencies": { - "@babel/parser": "^7.18.4", - "postcss": "^8.4.14", - "source-map": "^0.6.1" - } - }, - "node_modules/@vue/compiler-sfc/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@vue/component-compiler": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@vue/component-compiler/-/component-compiler-4.2.4.tgz", - "integrity": "sha512-tFGw3h3+nxiqnyborwWQ+rUgKAwSFl0Sdg+BCZkWTyFfkEF5fqunTNoklEUDdtRQMmVqsajn1pOZdm0zh4Uicw==", - "dev": true, - "dependencies": { - "@vue/component-compiler-utils": "^3.0.0", - "clean-css": "^4.1.11", - "hash-sum": "^1.0.2", - "postcss-modules-sync": "^1.0.0", - "source-map": "0.6.*" - }, - "optionalDependencies": { - "less": "^3.9.0", - "pug": "^3.0.1", - "sass": "^1.18.0", - "stylus": "^0.54.5" - }, - "peerDependencies": { - "postcss": ">=6.0", - "vue-template-compiler": "*" - } - }, - "node_modules/@vue/component-compiler-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", - "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", - "dev": true, - "dependencies": { - "consolidate": "^0.15.1", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.2", - "merge-source-map": "^1.1.0", - "postcss": "^7.0.36", - "postcss-selector-parser": "^6.0.2", - "source-map": "~0.6.1", - "vue-template-es2015-compiler": "^1.9.0" - }, - "optionalDependencies": { - "prettier": "^1.18.2 || ^2.0.0" - } - }, - "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/@vue/component-compiler-utils/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true - }, - "node_modules/@vue/component-compiler-utils/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/@vue/component-compiler-utils/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@vue/component-compiler-utils/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "node_modules/@vue/component-compiler/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "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, - "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, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "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 - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "optional": true - }, - "node_modules/assert-never": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", - "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==", - "dev": true, - "optional": true - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "optional": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/babel-walk": { - "version": "3.0.0-canary-5", - "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", - "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", - "dev": true, - "optional": true, - "dependencies": { - "@babel/types": "^7.9.6" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "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 - }, - "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==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/bootstrap": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz", - "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "peerDependencies": { - "@popperjs/core": "^2.11.8" - } - }, - "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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.0.tgz", - "integrity": "sha512-g2BJ2a0nEYvEFQC208q8mVAhfNwpZ5Mu8BwgtCdZKO3qx98HChmeg448fPdUzld8aFmfLgVh7yymqV+q1lJZ5g==", - "dev": true, - "peer": true, - "dependencies": { - "caniuse-lite": "^1.0.30001254", - "colorette": "^1.3.0", - "electron-to-chromium": "^1.3.830", - "escalade": "^3.1.1", - "node-releases": "^1.1.75" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "optional": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001255", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001255.tgz", - "integrity": "sha512-F+A3N9jTZL882f/fg/WWVnKSu6IOo3ueLz4zwaOPbPYHNmM/ZaDUyzyJwS1mZhX7Ex5jqTyW599Gdelh5PDYLQ==", - "dev": true, - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", - "dev": true, - "optional": true, - "dependencies": { - "is-regex": "^1.0.3" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/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, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/clean-css": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", - "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", - "dev": true, - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/colorette": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz", - "integrity": "sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==", - "dev": true, - "peer": true - }, - "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 - }, - "node_modules/consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", - "dev": true, - "dependencies": { - "bluebird": "^3.1.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/constantinople": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", - "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", - "dev": true, - "optional": true, - "dependencies": { - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.1" - } - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "peer": true, - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/copy-anything": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", - "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", - "dev": true, - "optional": true, - "dependencies": { - "is-what": "^3.14.1" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dev": true, - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - } - }, - "node_modules/css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha512-UNIFik2RgSbiTwIW1IsFwXWn6vs+bYdq83LKTSOsx7NJR7WII9dxewkHLltfTLVppoUApHV0118a4RZRI9FLwA==", - "dev": true, - "optional": true, - "dependencies": { - "css": "^2.0.0" - } - }, - "node_modules/css-selector-tokenizer": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", - "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "node_modules/css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.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, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "node_modules/de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10" - } - }, - "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 - }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", - "dev": true, - "optional": true - }, - "node_modules/electron-to-chromium": { - "version": "1.3.830", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.830.tgz", - "integrity": "sha512-gBN7wNAxV5vl1430dG+XRcQhD4pIeYeak6p6rjdCtlz5wWNwDad8jwvphe5oi1chL5MV6RNRikfffBBiFuj+rQ==", - "dev": true, - "peer": true - }, - "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==" - }, - "node_modules/emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/encode-utf8": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", - "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dev": true, - "optional": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" - } - }, - "node_modules/esbuild-sass-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-2.9.0.tgz", - "integrity": "sha512-D7c8Ub+C4RfaqhOoo9EEWprYmP5ZCmpmcodmYDtpvCfPZLgsSbLaLHPXdul4TUWUMH5eJ6POw+aes/s42ffwrA==", - "dev": true, - "dependencies": { - "resolve": "^1.22.2", - "sass": "^1.62.0" - }, - "peerDependencies": { - "esbuild": "^0.17.17" - } - }, - "node_modules/esbuild-vue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/esbuild-vue/-/esbuild-vue-1.2.2.tgz", - "integrity": "sha512-6Jmsen63cSOMe0p/0SiLj0JF2bsEoepO29teqEGdje/hQZC00z5txYImw7tCKp+oo+DPCiYKxGjWcNJCRtgwHA==", - "dev": true, - "dependencies": { - "@vue/component-compiler": "^4.2.4", - "lodash.escaperegexp": "^4.1.2", - "piscina": "^2.2.0", - "postcss": ">=6.0.0" - }, - "peerDependencies": { - "vue-template-compiler": "^2.6.12" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "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.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", - "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", - "import-fresh": "^3.0.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.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "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-plugin-vue": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.14.1.tgz", - "integrity": "sha512-LQazDB1qkNEKejLe/b5a9VfEbtbczcOaui5lQ4Qw0tbRBbQYREyxxOV5BQgNDTqGPs9pxqiEpbMi9ywuIaF7vw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.3.0", - "natural-compare": "^1.4.0", - "nth-check": "^2.0.1", - "postcss-selector-parser": "^6.0.9", - "semver": "^7.3.5", - "vue-eslint-parser": "^9.3.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-vue/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/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, - "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/eslint/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, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/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 - }, - "node_modules/eslint/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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "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/node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/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, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/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, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/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, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/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, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/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, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", - "dev": true, - "dependencies": { - "acorn": "^8.8.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/espree/node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/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, - "engines": { - "node": ">=4.0" - } - }, - "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, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true - }, - "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 - }, - "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 - }, - "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 - }, - "node_modules/fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/generic-names": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz", - "integrity": "sha512-b6OHfQuKasIKM9b6YPkX+KUj/TLBTx3B/1aT1T5F12FEuEqyFMdr59OMS53aoaSw8eVtapdqieX6lbg5opaOhA==", - "dev": true, - "dependencies": { - "loader-utils": "^0.2.16" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "optional": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "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", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "optional": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "optional": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, - "node_modules/hdr-histogram-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", - "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", - "dev": true, - "dependencies": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "node_modules/hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", - "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", - "dev": true - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==", - "dev": true - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", - "dev": true, - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "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": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-expression": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", - "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", - "dev": true, - "optional": true, - "dependencies": { - "acorn": "^7.1.1", - "object-assign": "^4.1.1" - } - }, - "node_modules/is-expression/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "optional": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "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, - "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==", - "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, - "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, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true, - "optional": true - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "optional": true, - "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-what": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true, - "optional": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-base64": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", - "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", - "dev": true - }, - "node_modules/js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", - "dev": true, - "optional": true - }, - "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, - "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, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "peer": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "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 - }, - "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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "peer": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", - "dev": true, - "optional": true, - "dependencies": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - } - }, - "node_modules/less": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", - "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", - "dev": true, - "optional": true, - "dependencies": { - "copy-anything": "^2.0.1", - "tslib": "^1.10.0" - }, - "bin": { - "lessc": "bin/lessc" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "native-request": "^1.0.5", - "source-map": "~0.6.0" - } - }, - "node_modules/less/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "optional": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/less/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "optional": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", - "dev": true, - "dependencies": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - }, - "node_modules/loader-utils/node_modules/json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "dev": true - }, - "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 - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "dependencies": { - "source-map": "^0.6.1" - } - }, - "node_modules/merge-source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "optional": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/native-request": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz", - "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==", - "dev": true, - "optional": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/nice-napi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", - "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "!win32" - ], - "dependencies": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" - } - }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true, - "optional": true - }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true, - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-releases": { - "version": "1.1.75", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.75.tgz", - "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", - "dev": true, - "peer": true - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "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.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "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, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/piscina": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-2.2.0.tgz", - "integrity": "sha512-CQb0DfyTdC9FBIMYkVV/00fXRLKDjmWKA8S0N1zDg2JGEc5z3P9qHXtoq8OkJQ+vjCfXySkVonTNMqskMFOW/w==", - "dev": true, - "dependencies": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0" - }, - "optionalDependencies": { - "nice-napi": "^1.0.2" - } - }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "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" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==", - "dev": true, - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "node_modules/postcss-modules-local-by-default/node_modules/postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss-modules-local-by-default/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==", - "dev": true, - "dependencies": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - } - }, - "node_modules/postcss-modules-scope/node_modules/postcss": { - "version": "6.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", - "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss-modules-scope/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-modules-sync": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-sync/-/postcss-modules-sync-1.0.0.tgz", - "integrity": "sha512-kIDk2NYmxHshqUbjtFf1WdBij08IsvRdgDT0nOGWhvwkr8/z1piLSzxVrPt56J4DU6ON986h2H+5xcBnFhT8UQ==", - "dev": true, - "dependencies": { - "generic-names": "^1.0.2", - "icss-replace-symbols": "^1.0.2", - "postcss": "^5.2.5", - "postcss-modules-local-by-default": "^1.1.1", - "postcss-modules-scope": "^1.0.2", - "string-hash": "^1.1.0" - } - }, - "node_modules/postcss-modules-sync/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-modules-sync/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-modules-sync/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-modules-sync/node_modules/chalk/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/postcss-modules-sync/node_modules/has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-modules-sync/node_modules/postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "dependencies": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/postcss-modules-sync/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-modules-sync/node_modules/supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", - "dev": true, - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "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, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true, - "optional": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "optional": true - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "node_modules/pug": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", - "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", - "dev": true, - "optional": true, - "dependencies": { - "pug-code-gen": "^3.0.2", - "pug-filters": "^4.0.0", - "pug-lexer": "^5.0.1", - "pug-linker": "^4.0.0", - "pug-load": "^3.0.0", - "pug-parser": "^6.0.0", - "pug-runtime": "^3.0.1", - "pug-strip-comments": "^2.0.0" - } - }, - "node_modules/pug-attrs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", - "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", - "dev": true, - "optional": true, - "dependencies": { - "constantinople": "^4.0.1", - "js-stringify": "^1.0.2", - "pug-runtime": "^3.0.0" - } - }, - "node_modules/pug-code-gen": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", - "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", - "dev": true, - "optional": true, - "dependencies": { - "constantinople": "^4.0.1", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.2", - "pug-attrs": "^3.0.0", - "pug-error": "^2.0.0", - "pug-runtime": "^3.0.0", - "void-elements": "^3.1.0", - "with": "^7.0.0" - } - }, - "node_modules/pug-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", - "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==", - "dev": true, - "optional": true - }, - "node_modules/pug-filters": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", - "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", - "dev": true, - "optional": true, - "dependencies": { - "constantinople": "^4.0.1", - "jstransformer": "1.0.0", - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0", - "resolve": "^1.15.1" - } - }, - "node_modules/pug-lexer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", - "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", - "dev": true, - "optional": true, - "dependencies": { - "character-parser": "^2.2.0", - "is-expression": "^4.0.0", - "pug-error": "^2.0.0" - } - }, - "node_modules/pug-linker": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", - "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", - "dev": true, - "optional": true, - "dependencies": { - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0" - } - }, - "node_modules/pug-load": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", - "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", - "dev": true, - "optional": true, - "dependencies": { - "object-assign": "^4.1.1", - "pug-walk": "^2.0.0" - } - }, - "node_modules/pug-parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", - "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", - "dev": true, - "optional": true, - "dependencies": { - "pug-error": "^2.0.0", - "token-stream": "1.0.0" - } - }, - "node_modules/pug-runtime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", - "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", - "dev": true, - "optional": true - }, - "node_modules/pug-strip-comments": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", - "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", - "dev": true, - "optional": true, - "dependencies": { - "pug-error": "^2.0.0" - } - }, - "node_modules/pug-walk": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", - "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", - "dev": true, - "optional": true - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz", - "integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==", - "dependencies": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "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" - } - ] - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, - "dependencies": { - "is-core-module": "^2.11.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", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true, - "optional": true - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "peer": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "optional": true - }, - "node_modules/sass": { - "version": "1.63.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.3.tgz", - "integrity": "sha512-ySdXN+DVpfwq49jG1+hmtDslYqpS7SkOR5GpF6o2bmb1RL/xS+wvPmegMvMywyfsmAV6p7TgwXYGrCZIFFbAHg==", - "dev": true, - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "optional": true - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "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, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dev": true, - "optional": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true, - "optional": true - }, - "node_modules/string-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", - "integrity": "sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==", - "dev": true - }, - "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==", - "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==", - "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, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylus": { - "version": "0.54.8", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", - "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", - "dev": true, - "optional": true, - "dependencies": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.6", - "mkdirp": "~1.0.4", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.3.0", - "source-map": "^0.7.3" - }, - "bin": { - "stylus": "bin/stylus" - }, - "engines": { - "node": "*" - } - }, - "node_modules/stylus/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "optional": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/stylus/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "optional": true - }, - "node_modules/stylus/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/token-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", - "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", - "dev": true, - "optional": true - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "optional": true - }, - "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, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true, - "optional": true - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vue": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.14.tgz", - "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==", - "dependencies": { - "@vue/compiler-sfc": "2.7.14", - "csstype": "^3.1.0" - } - }, - "node_modules/vue-eslint-parser": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.0.tgz", - "integrity": "sha512-48IxT9d0+wArT1+3wNIy0tascRoywqSUe2E1YalIC1L8jsUGe5aJQItWfRok7DVFGz3UYvzEI7n5wiTXsCMAcQ==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/vue-eslint-parser/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "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/vue-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/vue-eslint-parser/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, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/vue-eslint-parser/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/vue-i18n": { - "version": "8.28.2", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.28.2.tgz", - "integrity": "sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA==" - }, - "node_modules/vue-router": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz", - "integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==" - }, - "node_modules/vue-template-compiler": { - "version": "2.7.14", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", - "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==", - "dev": true, - "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" - } - }, - "node_modules/vue-template-es2015-compiler": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", - "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", - "dev": 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, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" - }, - "node_modules/with": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", - "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", - "dev": true, - "optional": true, - "dependencies": { - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", - "assert-never": "^1.2.1", - "babel-walk": "3.0.0-canary-5" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/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==" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index da55311..3365f87 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,28 @@ { "devDependencies": { - "@babel/eslint-parser": "^7.22.5", - "esbuild": "^0.17.17", - "esbuild-sass-plugin": "^2.9.0", - "esbuild-vue": "^1.2.2", - "eslint": "^8.42.0", - "eslint-plugin-vue": "^9.14.1", - "vue-template-compiler": "^2.7.14" + "@babel/eslint-parser": "7.28.0", + "@types/bootstrap": "5.2.10", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", + "@vue/tsconfig": "0.7.0", + "esbuild": "0.25.6", + "esbuild-plugin-vue3": "0.4.2", + "esbuild-sass-plugin": "3.3.1", + "eslint": "9.31.0", + "eslint-plugin-vue": "10.3.0", + "typescript": "5.8.3", + "vue-eslint-parser": "10.2.0" }, "name": "ots", "private": true, "dependencies": { - "base64-js": "^1.5.1", - "bootstrap": "^5.3.2", - "qrcode": "^1.5.3", - "vue": "^2.7.14", - "vue-i18n": "^8.28.2", - "vue-router": "^3.6.5" - } + "@fortawesome/fontawesome-free": "6.7.2", + "base64-js": "1.5.1", + "bootstrap": "5.3.7", + "qrcode": "1.5.4", + "vue": "3.5.17", + "vue-i18n": "11.1.10", + "vue-router": "4.5.1" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } \ No newline at end of file diff --git a/pkg/client/client.go b/pkg/client/client.go index 2675b4b..cb7e7f6 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -17,11 +17,21 @@ import ( "time" "github.com/Luzifer/go-openssl/v4" + "github.com/sirupsen/logrus" +) + +type ( + // HTTPClientIntf describes a minimal interface to be fulfilled + // by the given HTTP client. This can be used for mocking and to + // pass in authenticated clients + HTTPClientIntf interface { + Do(*http.Request) (*http.Response, error) + } ) // HTTPClient defines the client to use for create and fetch requests // and can be overwritten to provide authentication -var HTTPClient = http.DefaultClient +var HTTPClient HTTPClientIntf = http.DefaultClient // KeyDerivationFunc defines the key derivation algorithm used in OTS // to derive the key / iv from the password for encryption. You only @@ -30,7 +40,11 @@ var HTTPClient = http.DefaultClient // // The corresponding settings are found in `/src/crypto.js` in the OTS // source code. -var KeyDerivationFunc = openssl.NewPBKDF2Generator(sha512.New, 300000) //nolint:gomnd // that's the definition +var KeyDerivationFunc = openssl.NewPBKDF2Generator(sha512.New, 300000) //nolint:mnd // that's the definition + +// Logger can be set to enable logging from the library. By default +// all log-messages will be discarded. +var Logger *logrus.Entry // PasswordLength defines the length of the generated encryption password var PasswordLength = 20 @@ -45,6 +59,12 @@ var RequestTimeout = 5 * time.Second // provide an URL to useful information about your tool. var UserAgent = "ots-client/1.x +https://github.com/Luzifer/ots" +func init() { + l := logrus.New() + l.SetOutput(io.Discard) + Logger = logrus.NewEntry(l) +} + // Create serializes the secret and creates a new secret on the // instance given by its URL. // @@ -86,7 +106,7 @@ func Create(instanceURL string, secret Secret, expireIn time.Duration) (string, }.Encode() } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, createURL.String(), body) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, createURL.String(), body) if err != nil { return "", time.Time{}, fmt.Errorf("creating request: %w", err) } @@ -138,7 +158,7 @@ func Fetch(secretURL string) (s Secret, err error) { if err != nil { return s, fmt.Errorf("unescaping fragment: %w", err) } - fragmentParts := strings.SplitN(fragment, "|", 2) //nolint:gomnd + fragmentParts := strings.SplitN(fragment, "|", 2) //nolint:mnd fetchURL := u.JoinPath(strings.Join([]string{".", "api", "get", fragmentParts[0]}, "/")).String() ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout) diff --git a/pkg/client/go.mod b/pkg/client/go.mod index 256fda3..ac63a33 100644 --- a/pkg/client/go.mod +++ b/pkg/client/go.mod @@ -1,15 +1,24 @@ module github.com/Luzifer/ots/pkg/client -go 1.21.1 +go 1.23.0 + +toolchain go1.24.5 + +replace github.com/Luzifer/ots/pkg/customization => ../customization require ( - github.com/Luzifer/go-openssl/v4 v4.2.1 - github.com/stretchr/testify v1.8.4 + github.com/Luzifer/go-openssl/v4 v4.2.4 + github.com/Luzifer/ots/pkg/customization v0.0.0-20250501151834-283ffa548fa8 + github.com/ryanuber/go-glob v1.0.0 + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.10.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/sys v0.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/client/go.sum b/pkg/client/go.sum index 9e1f53d..f414ef8 100644 --- a/pkg/client/go.sum +++ b/pkg/client/go.sum @@ -1,14 +1,27 @@ -github.com/Luzifer/go-openssl/v4 v4.2.1 h1:0+/gaQ5TcBhGmVqGrfyA21eujlbbaNwj0VlOA3nh4ts= -github.com/Luzifer/go-openssl/v4 v4.2.1/go.mod h1:CZZZWY0buCtkxrkqDPQYigC4Kn55UuO97TEoV+hwz2s= +github.com/Luzifer/go-openssl/v4 v4.2.4 h1:3Eu3gSeZpr8Ha+IofVnSWttCL1xejRr/lda4l4TZRWk= +github.com/Luzifer/go-openssl/v4 v4.2.4/go.mod h1:ykquxaR0R1Vor83/FAtGBJZZO5zswuSQTVx1FQc1bJY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/client/sanity.go b/pkg/client/sanity.go new file mode 100644 index 0000000..047691f --- /dev/null +++ b/pkg/client/sanity.go @@ -0,0 +1,142 @@ +package client + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "regexp" + "strings" + + "github.com/Luzifer/ots/pkg/customization" + "github.com/ryanuber/go-glob" +) + +var ( + // ErrAttachmentsDisabled signalizes the instance has attachments + // disabled but the checked secret contains attachments + ErrAttachmentsDisabled = errors.New("attachments are disabled on this instance") + // ErrAttachmentsTooLarge signalizes the size of the attached files + // exceeds the configured maximum size of the given instance + ErrAttachmentsTooLarge = errors.New("attachment size exceeds allowed size") + // ErrAttachmentTypeNotAllowed signalizes any file does not match + // the allowed extensions / mime types + ErrAttachmentTypeNotAllowed = errors.New("attachment type is not allowed") + + errSettingsNotFound = errors.New("settings not found") + mimeRegex = regexp.MustCompile(`^(?:[a-z]+|\*)\/(?:[a-zA-Z0-9.+_-]+|\*)$`) +) + +// SanityCheck fetches the instance settings and validates the secret +// against those settings (matching file size, disabled attachments, +// allowed file types, ...) +func SanityCheck(instanceURL string, secret Secret) error { + cust, err := loadSettings(instanceURL) + if err != nil { + if errors.Is(err, errSettingsNotFound) { + // Sanity check is not possible when the API endpoint is not + // supported, therefore we ignore this. + return nil + } + return fmt.Errorf("fetching settings: %w", err) + } + + // Check whether attachments are allowed at all + if cust.DisableFileAttachment && len(secret.Attachments) > 0 { + return ErrAttachmentsDisabled + } + + // Check whether attachments are too large + var totalAttachmentSize int64 + for _, a := range secret.Attachments { + totalAttachmentSize += int64(len(a.Content)) + } + if cust.MaxAttachmentSizeTotal > 0 && totalAttachmentSize > cust.MaxAttachmentSizeTotal { + return ErrAttachmentsTooLarge + } + + // Check for allowed types + if cust.AcceptedFileTypes != "" { + allowed := strings.Split(cust.AcceptedFileTypes, ",") + for _, a := range secret.Attachments { + if !attachmentAllowed(a, allowed) { + return ErrAttachmentTypeNotAllowed + } + } + } + + return nil +} + +func attachmentAllowed(file SecretAttachment, allowed []string) bool { + mimeType, _, _ := strings.Cut(file.Type, ";") + logger := Logger.WithField("content-type", mimeType) + + for _, a := range allowed { + switch { + case mimeRegex.MatchString(a): + // That's a mime type + if glob.Glob(a, mimeType) { + // The mime "glob" matches the file type + logger.WithField("allowed_by", a).Debug("attachment allowed") + return true + } + + case a[0] == '.': + // That's a file extension + if strings.HasSuffix(file.Name, a) { + // The filename has the right extension + logger.WithField("allowed_by", a).Debug("attachment allowed") + return true + } + } + } + + logger.Debug("attachment type not allowed") + return false +} + +func loadSettings(instanceURL string) (c customization.Customize, err error) { + u, err := url.Parse(instanceURL) + if err != nil { + return c, fmt.Errorf("parsing instance URL: %w", err) + } + + createURL := u.JoinPath(strings.Join([]string{".", "api", "settings"}, "/")) + ctx, cancel := context.WithTimeout(context.Background(), RequestTimeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, createURL.String(), nil) + if err != nil { + return c, fmt.Errorf("creating request: %w", err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", UserAgent) + + resp, err := HTTPClient.Do(req) + if err != nil { + return c, fmt.Errorf("executing request: %w", err) + } + defer resp.Body.Close() //nolint:errcheck // possible leaked-fd, lib should not log, potential short-lived leak + + if resp.StatusCode == http.StatusNotFound { + return c, errSettingsNotFound + } + + if resp.StatusCode != http.StatusOK { + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return c, fmt.Errorf("unexpected HTTP status %d", resp.StatusCode) + } + return c, fmt.Errorf("unexpected HTTP status %d (%s)", resp.StatusCode, respBody) + } + + if err = json.NewDecoder(resp.Body).Decode(&c); err != nil { + return c, fmt.Errorf("decoding response: %w", err) + } + + return c, nil +} diff --git a/pkg/client/sanity_test.go b/pkg/client/sanity_test.go new file mode 100644 index 0000000..bb16aa3 --- /dev/null +++ b/pkg/client/sanity_test.go @@ -0,0 +1,100 @@ +package client + +import ( + "bytes" + "net/http" + "net/http/httptest" + "testing" + + "github.com/Luzifer/ots/pkg/customization" + "github.com/stretchr/testify/assert" +) + +type custMockClient struct { + Response *customization.Customize +} + +func (c custMockClient) Do(r *http.Request) (*http.Response, error) { + m := http.NewServeMux() + m.HandleFunc(r.URL.Path, func(w http.ResponseWriter, _ *http.Request) { + if c.Response == nil { + w.WriteHeader(http.StatusNotFound) + return + } + + d, _ := c.Response.ToJSON() + _, _ = w.Write([]byte(d)) + }) + + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + + return w.Result(), nil +} + +func TestSanityCheck(t *testing.T) { + var ( + err error + m = custMockClient{&customization.Customize{ + AcceptedFileTypes: "text/*,image/png,.gif", + DisableFileAttachment: true, + MaxAttachmentSizeTotal: 64, + }} + u = "http://localhost/" + ) + + HTTPClient = &m + defer func() { HTTPClient = http.DefaultClient }() + + s := Secret{Secret: "ohai"} + + // no attachments & attachments disabled + err = SanityCheck(u, s) + assert.NoError(t, err) + + // attachments & attachmetns disabled + s.Attachments = []SecretAttachment{ + {Name: "myfile.webm", Type: "video/webm", Content: []byte{0x0}}, + } + + err = SanityCheck(u, s) + assert.ErrorIs(t, err, ErrAttachmentsDisabled) + + // disallowed attachment + m.Response.DisableFileAttachment = false + err = SanityCheck(u, s) + assert.ErrorIs(t, err, ErrAttachmentTypeNotAllowed) + + // attachment allowed by extension + s.Attachments = []SecretAttachment{ + {Name: "doesthiswork.gif", Type: "image/gif", Content: []byte{0x0}}, + } + err = SanityCheck(u, s) + assert.NoError(t, err) + + // attachment allowed by mime type + s.Attachments = []SecretAttachment{ + {Name: "doesthiswork.png", Type: "image/png", Content: []byte{0x0}}, + } + err = SanityCheck(u, s) + assert.NoError(t, err) + + // attachment allowed by mime type wildcard + s.Attachments = []SecretAttachment{ + {Name: "doesthiswork.md", Type: "text/markdown", Content: []byte{0x0}}, + } + err = SanityCheck(u, s) + assert.NoError(t, err) + + // attachment too large + s.Attachments = []SecretAttachment{ + {Name: "doesthiswork.md", Type: "text/markdown", Content: bytes.Repeat([]byte{0x0}, 128)}, + } + err = SanityCheck(u, s) + assert.ErrorIs(t, err, ErrAttachmentsTooLarge) + + // check without settings API on instance + m.Response = nil + err = SanityCheck(u, s) + assert.NoError(t, err) +} diff --git a/customize.go b/pkg/customization/customize.go similarity index 56% rename from customize.go rename to pkg/customization/customize.go index 1a6f50a..c22e6ac 100644 --- a/customize.go +++ b/pkg/customization/customize.go @@ -1,4 +1,6 @@ -package main +// Package customization contains the structure for the customization +// file to configure the OTS web- and command-line interface +package customization import ( "encoding/json" @@ -7,12 +9,21 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) +// Frontend has a max attachment size of 64MiB as the base64 encoding +// will break afterwards. Therefore we use a maximum secret size of +// 65MiB and increase it by double base64 encoding: +// +// 65 MiB * 16/9 (twice 4/3 base64 size increase) +const defaultMaxSecretSize = 65 * 1024 * 1024 * (16 / 9) // = 115.6MiB + type ( - customize struct { + // Customize holds the structure of the customization file + Customize struct { AppIcon string `json:"appIcon,omitempty" yaml:"appIcon"` + AppIconDark string `json:"appIconDark,omitempty" yaml:"appIconDark"` AppTitle string `json:"appTitle,omitempty" yaml:"appTitle"` DisableAppTitle bool `json:"disableAppTitle,omitempty" yaml:"disableAppTitle"` DisablePoweredBy bool `json:"disablePoweredBy,omitempty" yaml:"disablePoweredBy"` @@ -26,12 +37,22 @@ type ( DisableFileAttachment bool `json:"disableFileAttachment" yaml:"disableFileAttachment"` MaxAttachmentSizeTotal int64 `json:"maxAttachmentSizeTotal" yaml:"maxAttachmentSizeTotal"` - OverlayFSPath string `json:"-" yaml:"overlayFSPath"` - UseFormalLanguage bool `json:"-" yaml:"useFormalLanguage"` + MaxSecretSize int64 `json:"-" yaml:"maxSecretSize"` + MetricsAllowedSubnets []string `json:"-" yaml:"metricsAllowedSubnets"` + OverlayFSPath string `json:"-" yaml:"overlayFSPath"` + UseFormalLanguage bool `json:"-" yaml:"useFormalLanguage"` + + FooterLinks []FooterLink `json:"footerLinks,omitempty" yaml:"footerLinks"` + } + + FooterLink struct { + Name string `json:"name" yaml:"name"` + URL string `json:"url" yaml:"url"` } ) -func loadCustomize(filename string) (cust customize, err error) { +// Load retrieves the Customization file from filesystem +func Load(filename string) (cust Customize, err error) { if filename == "" { // None given, take a shortcut cust.applyFixes() @@ -61,13 +82,19 @@ func loadCustomize(filename string) (cust customize, err error) { return cust, nil } -func (c customize) ToJSON() (string, error) { +// ToJSON is a templating helper which returns the customization +// serialized as JSON in a string +func (c Customize) ToJSON() (string, error) { j, err := json.Marshal(c) return string(j), errors.Wrap(err, "marshalling JSON") } -func (c *customize) applyFixes() { +func (c *Customize) applyFixes() { if len(c.AppTitle) == 0 { c.AppTitle = "OTS - One Time Secrets" } + + if c.MaxSecretSize == 0 { + c.MaxSecretSize = defaultMaxSecretSize + } } diff --git a/pkg/customization/go.mod b/pkg/customization/go.mod new file mode 100644 index 0000000..1957281 --- /dev/null +++ b/pkg/customization/go.mod @@ -0,0 +1,13 @@ +module github.com/Luzifer/ots/pkg/customization + +go 1.23.0 + +toolchain go1.24.5 + +require ( + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.9.3 + gopkg.in/yaml.v3 v3.0.1 +) + +require golang.org/x/sys v0.32.0 // indirect diff --git a/pkg/customization/go.sum b/pkg/customization/go.sum new file mode 100644 index 0000000..81c4e90 --- /dev/null +++ b/pkg/customization/go.sum @@ -0,0 +1,20 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go new file mode 100644 index 0000000..391eb7e --- /dev/null +++ b/pkg/metrics/metrics.go @@ -0,0 +1,99 @@ +// Package metrics provides an abstraction around metrics collection +// in order to bundle all metrics related calls in one location +package metrics + +import ( + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +const ( + metricSecretsCreated = "secrets_created" + metricSecretsRead = "secrets_read" + metricSecretsCreateErrors = "secrets_create_errors" + meticsSecretsReadErrors = "secrets_read_errors" + metricsSecretsStored = "secrets_stored" + + labelReason = "reason" + + namespace = "ots" +) + +type ( + // Collector contains all required methods to collect metrics + // and to populate them into the Handler + Collector struct { + secretsCreated prometheus.Counter + secretsRead prometheus.Counter + secretsCreateErrors *prometheus.CounterVec + secretsReadErrors *prometheus.CounterVec + secretsStored prometheus.Gauge + } +) + +// Handler returns the handler to be registered at /metrics +func Handler() http.Handler { return promhttp.Handler() } + +// New creates a new Collector and registers the metrics +func New() *Collector { + return &Collector{ + secretsCreated: promauto.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: metricSecretsCreated, + Help: "number of successfully created secrets", + }), + + secretsRead: promauto.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: metricSecretsRead, + Help: "number of fetched (and destroyed) secrets", + }), + + secretsCreateErrors: promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: namespace, + Name: metricSecretsCreateErrors, + Help: "number of errors on secret creation for each reason", + }, []string{labelReason}), + + secretsReadErrors: promauto.NewCounterVec(prometheus.CounterOpts{ + Namespace: namespace, + Name: meticsSecretsReadErrors, + Help: "number of read-errors for each reason", + }, []string{labelReason}), + + secretsStored: promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: metricsSecretsStored, + Help: "number of secrets currently held in the backend store", + }), + } +} + +// CountSecretCreated signalizes a secret has successfully been created +func (c Collector) CountSecretCreated() { c.secretsCreated.Inc() } + +// CountSecretRead signalizes a secret has successfully been read and destroyed +func (c Collector) CountSecretRead() { c.secretsRead.Inc() } + +// CountSecretCreateError signalizes an error occurred during secret +// creation. The reason must not be the error.Error() but a simple +// static string describing the error. +func (c Collector) CountSecretCreateError(reason string) { + c.secretsCreateErrors.WithLabelValues(reason).Inc() +} + +// CountSecretReadError signalizes an error occurred during secret +// read. The reason must not be the error.Error() but a simple +// static string describing the error. +func (c Collector) CountSecretReadError(reason string) { + c.secretsReadErrors.WithLabelValues(reason).Inc() +} + +// UpdateSecretsCount sets the current amount of secrets stored in the +// backend storage +func (c Collector) UpdateSecretsCount(count int64) { + c.secretsStored.Set(float64(count)) +} diff --git a/pkg/storage/memory/memory.go b/pkg/storage/memory/memory.go new file mode 100644 index 0000000..694e841 --- /dev/null +++ b/pkg/storage/memory/memory.go @@ -0,0 +1,106 @@ +// Package memory implements a pure in-memory store for secrets which +// is suitable for testing and should not be used for productive use +package memory + +import ( + "sync" + "time" + + "github.com/Luzifer/ots/pkg/storage" + "github.com/gofrs/uuid" +) + +type ( + memStorageSecret struct { + Expiry time.Time + Secret string + } + + storageMem struct { + sync.RWMutex + store map[string]memStorageSecret + storePruneTimer *time.Ticker + } +) + +// New creates a new In-Mem storage +func New() storage.Storage { + store := &storageMem{ + store: make(map[string]memStorageSecret), + storePruneTimer: time.NewTicker(time.Minute), + } + + go store.storePruner() + + return store +} + +func (s *storageMem) storePruner() { + for range s.storePruneTimer.C { + s.pruneStore() + } +} + +func (s *storageMem) pruneStore() { + s.Lock() + defer s.Unlock() + + for k, v := range s.store { + if v.hasExpired() { + delete(s.store, k) + } + } +} + +func (s *storageMem) Count() (int64, error) { + s.RLock() + defer s.RUnlock() + + return int64(len(s.store)), nil +} + +func (s *storageMem) Create(secret string, expireIn time.Duration) (string, error) { + s.Lock() + defer s.Unlock() + + var ( + expire time.Time + id = uuid.Must(uuid.NewV4()).String() + ) + + if expireIn > 0 { + expire = time.Now().Add(expireIn) + } + + s.store[id] = memStorageSecret{ + Expiry: expire, + Secret: secret, + } + + return id, nil +} + +func (s *storageMem) ReadAndDestroy(id string) (string, error) { + s.Lock() + defer s.Unlock() + + secret, ok := s.store[id] + if !ok { + return "", storage.ErrSecretNotFound + } + + defer delete(s.store, id) + + // Still check to see if the secret has expired in order to prevent a + // race condition where a secret has expired but the the store pruner has + // not yet been invoked. + if secret.hasExpired() { + return "", storage.ErrSecretNotFound + } + + return secret.Secret, nil +} + +func (m *memStorageSecret) hasExpired() bool { + return !m.Expiry.IsZero() && m.Expiry.Before(time.Now()) +} diff --git a/storage_redis.go b/pkg/storage/redis/redis.go similarity index 58% rename from storage_redis.go rename to pkg/storage/redis/redis.go index f9dc056..17de296 100644 --- a/storage_redis.go +++ b/pkg/storage/redis/redis.go @@ -1,24 +1,30 @@ -package main +// Package redis implements a Redis backed storage for secrets +package redis import ( "context" + "errors" "fmt" "os" "strings" "time" + "github.com/Luzifer/ots/pkg/storage" "github.com/gofrs/uuid" - "github.com/pkg/errors" redis "github.com/redis/go-redis/v9" ) -const redisDefaultPrefix = "io.luzifer.ots" +const ( + redisDefaultPrefix = "io.luzifer.ots" + redisScanCount = 10 +) type storageRedis struct { conn *redis.Client } -func newStorageRedis() (storage, error) { +// New returns a new Redis backed storage +func New() (storage.Storage, error) { if os.Getenv("REDIS_URL") == "" { return nil, fmt.Errorf("REDIS_URL environment variable not set") } @@ -30,7 +36,7 @@ func newStorageRedis() (storage, error) { // in order to maintain backwards compatibility opt, err := redis.ParseURL(strings.Replace(os.Getenv("REDIS_URL"), "tcp://", "redis://", 1)) if err != nil { - return nil, errors.Wrap(err, "parsing REDIS_URL") + return nil, fmt.Errorf("parsing REDIS_URL: %w", err) } s := &storageRedis{ @@ -40,24 +46,50 @@ func newStorageRedis() (storage, error) { return s, nil } +func (s storageRedis) Count() (n int64, err error) { + var cursor uint64 + + for { + var keys []string + + keys, cursor, err = s.conn.Scan(context.Background(), cursor, s.redisKey("*"), redisScanCount).Result() + if err != nil { + return n, fmt.Errorf("scanning stored keys: %w", err) + } + + n += int64(len(keys)) + if cursor == 0 { + break + } + } + + return n, nil +} + func (s storageRedis) Create(secret string, expireIn time.Duration) (string, error) { id := uuid.Must(uuid.NewV4()).String() err := s.conn.Set(context.Background(), s.redisKey(id), secret, expireIn).Err() + if err != nil { + return "", fmt.Errorf("writing redis key: %w", err) + } - return id, errors.Wrap(err, "writing redis key") + return id, nil } func (s storageRedis) ReadAndDestroy(id string) (string, error) { secret, err := s.conn.Get(context.Background(), s.redisKey(id)).Result() if err != nil { if errors.Is(err, redis.Nil) { - return "", errSecretNotFound + return "", storage.ErrSecretNotFound } - return "", errors.Wrap(err, "getting key") + return "", fmt.Errorf("getting key: %w", err) } err = s.conn.Del(context.Background(), s.redisKey(id)).Err() - return secret, errors.Wrap(err, "deleting key") + if err != nil { + return secret, fmt.Errorf("deleting key: %w", err) + } + return secret, nil } func (storageRedis) redisKey(id string) string { diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go new file mode 100644 index 0000000..52009b6 --- /dev/null +++ b/pkg/storage/storage.go @@ -0,0 +1,21 @@ +// Package storage describes the requirements a storage provider +// has to fulfill ot be usable in OTS +package storage + +import ( + "errors" + "time" +) + +type ( + // Storage is the interface to implement in each storage provider + Storage interface { + Count() (int64, error) + Create(secret string, expireIn time.Duration) (string, error) + ReadAndDestroy(id string) (string, error) + } +) + +// ErrSecretNotFound is a generic error to be returned when a secret +// does not exist in the backend. It will then be handled by API. +var ErrSecretNotFound = errors.New("secret not found") diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..3af7763 --- /dev/null +++ b/renovate.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>luzifer/renovate-config" + ], + "packageRules": [ + { + "enabled": false, + "matchPackageNames": [ + "github.com/Luzifer/ots/pkg/**" + ] + } + ] +} \ No newline at end of file diff --git a/src/app.vue b/src/app.vue index 05f0c04..b7de1f0 100644 --- a/src/app.vue +++ b/src/app.vue @@ -1,14 +1,16 @@ - - diff --git a/src/components/clipboard-button.vue b/src/components/clipboard-button.vue index f0eb625..f395b73 100644 --- a/src/components/clipboard-button.vue +++ b/src/components/clipboard-button.vue @@ -5,13 +5,16 @@ :disabled="!content" @click="copy" > - + - diff --git a/src/components/create.vue b/src/components/create.vue index 111ce75..7532bcf 100644 --- a/src/components/create.vue +++ b/src/components/create.vue @@ -1,8 +1,7 @@ -