diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index caa694ef..52fc5cb8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,78 +1,29 @@
-name: build and upload artifacts
+name: Build Server and Plugin
on:
push:
branches: [main]
+ paths:
+ - 'plugin/**'
+ - 'server/**'
+ - '.github/workflows/**'
+ - '.github/actions/**'
pull_request:
+ paths:
+ - 'plugin/**'
+ - 'server/**'
+ - '.github/workflows/**'
+ - '.github/actions/**'
workflow_dispatch:
- workflow_call:
- inputs:
- tag_name:
- type: string
- description: "The tag name of the release without v prefix"
-
-env:
- VERSION: ${{ inputs.tag_name }}
jobs:
- build:
- name: build and upload artifacts
- strategy:
- matrix:
- include:
- - os: windows-2022
- arch: amd64
- target: windows
- ext: .exe
- - os: ubuntu-22.04
- arch: amd64
- target: linux
- - os: ubuntu-22.04
- arch: arm64
- target: linux
- - os: macos-13
- arch: amd64
- target: darwin
- - os: macos-14
- arch: arm64
- target: darwin
-
- runs-on: ${{ matrix.os }}
-
- defaults:
- run:
- working-directory: ./server
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: installing dependencies
- uses: ./.github/actions/install
- with:
- os: ${{ matrix.os }}
- repo-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Check if VERSION exists
- shell: bash
- id: check_version
- run: |
- if [ ! -z "${{ env.VERSION }}" ]; then
- echo "PRODUCTION=true" >> $GITHUB_ENV
- fi
-
- - name: Build Server
- shell: bash
- run: task build
- env:
- OS: ${{ matrix.target }}
- ARCH: ${{ matrix.arch }}
-
- - name: Rename Server artifact
- run: mv build/deweb-server${{ matrix.ext }} build/deweb-server_${{ matrix.target }}_${{ matrix.arch }}${{ matrix.ext }}
-
- - name: Upload Server artifact
- uses: actions/upload-artifact@v4
- with:
- name: deweb-server_${{ matrix.target }}_${{ matrix.arch }}
- path: server/build/deweb-server_${{ matrix.target }}_${{ matrix.arch }}${{ matrix.ext }}
+ build-server:
+ name: Build server
+ uses: ./.github/workflows/server.yml
+ secrets: inherit
+
+ build-plugin:
+ name: Build plugin
+ needs: build-server
+ uses: ./.github/workflows/plugin.yml
+ secrets: inherit
diff --git a/.github/workflows/tests.yml b/.github/workflows/cli.yml
similarity index 66%
rename from .github/workflows/tests.yml
rename to .github/workflows/cli.yml
index c58ab778..518f0acb 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/cli.yml
@@ -1,38 +1,40 @@
-name: tests
+name: CLI
on:
push:
branches: [main]
+ paths:
+ - 'cli/**'
+ - '.github/workflows/cli.yml'
pull_request:
- workflow_call:
+ paths:
+ - 'cli/**'
+ - '.github/workflows/cli.yml'
+ workflow_dispatch:
jobs:
- test-server:
- runs-on: ubuntu-22.04
-
+ lint:
+ runs-on: ubuntu-latest
defaults:
run:
- working-directory: ./server
-
+ working-directory: ./cli
steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: installing dependencies
- uses: ./.github/actions/install
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Run unit tests
- run: go test ./...
+ node-version: 20
+ cache: "npm"
+ cache-dependency-path: ./cli/package-lock.json
+ - name: Install dependencies
+ run: npm ci
+ - name: Run code formatting and linting
+ run: npm run fmt:check
- test-cli:
+ test:
runs-on: ubuntu-22.04
-
defaults:
run:
working-directory: ./cli
-
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -40,19 +42,17 @@ jobs:
with:
node-version: 20
cache: "npm"
- cache-dependency-path: ./server/pages/package-lock.json
+ cache-dependency-path: ./cli/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
- functional-test-cli:
+ functional-test:
runs-on: ubuntu-22.04
-
defaults:
run:
working-directory: ./cli
-
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -60,7 +60,7 @@ jobs:
with:
node-version: 20
cache: "npm"
- cache-dependency-path: ./server/pages/package-lock.json
+ cache-dependency-path: ./cli/package-lock.json
- name: Install dependencies
run: npm ci
- name: Setup wallet
@@ -76,4 +76,4 @@ jobs:
npm run build
npm run start -- upload -y --wallet ./wallet_fullpower.yaml --password $WALLET_TEST_PASSWORD ../smart-contract/src/e2e/test-project/dist
env:
- WALLET_TEST_PASSWORD: ${{ secrets.WALLET_TEST_PASSWORD }}
+ WALLET_TEST_PASSWORD: ${{ secrets.WALLET_TEST_PASSWORD }}
\ No newline at end of file
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
deleted file mode 100644
index 3734bab9..00000000
--- a/.github/workflows/lint.yml
+++ /dev/null
@@ -1,76 +0,0 @@
-name: Lint
-
-on:
- push:
- branches: [main]
- pull_request:
-
-jobs:
- lint-server:
- runs-on: ubuntu-latest
-
- defaults:
- run:
- working-directory: ./server
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: installing dependencies
- uses: ./.github/actions/install
- with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: run golangci-lint
- uses: golangci/golangci-lint-action@v6
- with:
- version: v1.64
- args: --timeout=3m
- working-directory: ./server
-
- - name: install gofumpt
- run: go install mvdan.cc/gofumpt@latest
-
- - name: run gofumpt
- run: |
- ERRORS=$(gofumpt -l . | wc -l)
- if [[ "$ERRORS" != "0" ]]; then
- echo "following files are not gofumpted:"
- gofumpt -l .
- exit 1
- fi
-
- lint-cli:
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: ./cli
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: "npm"
- cache-dependency-path: ./cli/package-lock.json
- - name: Install dependencies
- run: npm ci
- - name: Run code formating and linting
- run: npm run fmt:check
-
- lint-frontend:
- runs-on: ubuntu-latest
- defaults:
- run:
- working-directory: ./server/pages
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v4
- with:
- node-version: 20
- cache: "npm"
- cache-dependency-path: ./server/pages/package-lock.json
- - name: Install dependencies
- run: npm ci
- - name: Run code formating and linting
- run: npm run lint:check
diff --git a/.github/workflows/plugin.yml b/.github/workflows/plugin.yml
new file mode 100644
index 00000000..3693b049
--- /dev/null
+++ b/.github/workflows/plugin.yml
@@ -0,0 +1,148 @@
+name: Plugin
+
+on:
+ workflow_dispatch:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ description: "Version for the build, without v prefix"
+ required: false
+
+env:
+ VERSION: ${{ inputs.version }}
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: ./plugin
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Installing dependencies
+ uses: ./.github/actions/install
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Task generate
+ run: task generate
+
+ - name: Run golangci-lint
+ uses: golangci/golangci-lint-action@v6
+ with:
+ version: v1.64
+ args: --timeout=3m
+ working-directory: ./plugin
+
+ - name: Install gofumpt
+ run: go install mvdan.cc/gofumpt@v0.7.0
+
+ - name: Run gofumpt
+ run: |
+ ERRORS=$(gofumpt -l . | wc -l)
+ if [[ "$ERRORS" != "0" ]]; then
+ echo "following files are not gofumpted:"
+ gofumpt -l .
+ exit 1
+ fi
+
+ build:
+ name: Build plugin
+ strategy:
+ matrix:
+ include:
+ - os: ubuntu-24.04
+ arch: amd64
+ target: windows
+ ext: .exe
+ - os: ubuntu-24.04
+ arch: amd64
+ target: linux
+ - os: ubuntu-24.04
+ arch: arm64
+ target: linux
+ - os: macos-13
+ arch: amd64
+ target: darwin
+ - os: macos-14
+ arch: arm64
+ target: darwin
+
+ runs-on: ${{ matrix.os }}
+ defaults:
+ run:
+ working-directory: ./plugin
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Installing dependencies
+ uses: ./.github/actions/install
+ with:
+ os: ${{ matrix.os }}
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Install Plugin Go Dependencies
+ run: task install
+ shell: bash
+
+ - name: Go Generate Plugin
+ run: task generate
+ shell: bash
+
+ - name: Check if VERSION exists
+ shell: bash
+ id: check_version
+ run: |
+ if [ ! -z "${{ env.VERSION }}" ]; then
+ echo "PRODUCTION=true" >> $GITHUB_ENV
+ fi
+
+ - name: Download server artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: deweb-server_${{ matrix.target }}_${{ matrix.arch }}
+ path: server/build
+
+ - name: Rename server binary
+ shell: bash
+ run: |
+ mkdir -p build
+ mv ../server/build/deweb-server_${{ matrix.target }}_${{ matrix.arch }}${{ matrix.ext }} build/deweb-server${{ matrix.ext }}
+ chmod +x build/deweb-server${{ matrix.ext }}
+
+ - name: Build Plugin
+ shell: bash
+ run: task build
+ env:
+ OS: ${{ matrix.target }}
+ ARCH: ${{ matrix.arch }}
+
+ - name: Rename Plugin artifact
+ run: mv build/deweb-plugin${{ matrix.ext }} deweb-plugin_${{ matrix.target }}_${{ matrix.arch }}${{ matrix.ext }}
+
+ - name: Create platform directory with all required files
+ shell: bash
+ run: |
+ mkdir -p platform_${{ matrix.target }}_${{ matrix.arch }}
+ cp deweb-plugin_${{ matrix.target }}_${{ matrix.arch }}${{ matrix.ext }} platform_${{ matrix.target }}_${{ matrix.arch }}/
+ cp build/deweb-server${{ matrix.ext }} platform_${{ matrix.target }}_${{ matrix.arch }}/
+ cp favicon.png platform_${{ matrix.target }}_${{ matrix.arch }}/
+ cp manifest.json platform_${{ matrix.target }}_${{ matrix.arch }}/
+
+ - name: Create zip package
+ shell: bash
+ run: |
+ cd platform_${{ matrix.target }}_${{ matrix.arch }}
+ zip -r ../deweb-plugin_${{ matrix.target }}_${{ matrix.arch }}.zip *
+ cd ..
+ ls -la deweb-plugin_${{ matrix.target }}_${{ matrix.arch }}.zip
+
+ - name: Upload Plugin zip package
+ uses: actions/upload-artifact@v4
+ with:
+ name: deweb-plugin_${{ matrix.target }}_${{ matrix.arch }}
+ path: plugin/deweb-plugin_${{ matrix.target }}_${{ matrix.arch }}.zip
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8245b8c7..8cef8751 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,10 +1,10 @@
-name: Release workflow
+name: Release DeWeb
on:
workflow_dispatch:
inputs:
- tag_name:
- description: "Version to produce"
+ version:
+ description: 'Version for this release (without v prefix)'
required: true
type: string
release-as-draft:
@@ -24,37 +24,84 @@ on:
default: true
jobs:
- validate-input:
- name: if tag_name is provided it must not start with "v"
+ check-manifest:
runs-on: ubuntu-latest
steps:
- - name: Check if tag_name starts with "v"
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Check the manifest version
+ working-directory: ./plugin
run: |
- if [[ "${{ github.event.inputs.tag_name }}" == v* ]]; then
- echo "tag_name starts with v"
+ sudo apt-get install -y jq
+ version=$(jq -r '.version' manifest.json)
+ input_version=${{ inputs.version }}
+ if [[ "$version" != "$input_version" ]]; then
+ echo "ERROR: The manifest version ($version) does not match the input version ($input_version)"
exit 1
fi
+ echo "Manifest version matches input version: $version"
- build-release:
- needs: validate-input
- uses: ./.github/workflows/build.yml
+ build-server:
+ needs: check-manifest
+ uses: ./.github/workflows/server.yml
+ with:
+ version: ${{ inputs.version }}
secrets: inherit
+
+ build-plugin:
+ needs: build-server
+ uses: ./.github/workflows/plugin.yml
with:
- tag_name: ${{ github.event.inputs.tag_name }}
+ version: ${{ inputs.version }}
+ secrets: inherit
create-release:
- name: Release
- needs: build-release
-
+ needs: [build-server, build-plugin]
runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ id-token: write
steps:
- - uses: actions/download-artifact@v4
- - name: Create release and upload binaries
+ - name: Download server artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: artifacts/server
+ pattern: 'deweb-server_*'
+
+ - name: Download plugin zip packages
+ uses: actions/download-artifact@v4
+ with:
+ path: artifacts/plugin
+ pattern: 'deweb-plugin_*_zip'
+
+ - name: ls -la artifacts/server
+ run: ls -la artifacts/server
+
+ - name: ls -la artifacts/plugin
+ run: ls -la artifacts/plugin
+
+ - name: Create Release
uses: softprops/action-gh-release@v2
with:
- tag_name: v${{ inputs.tag_name }}
+ tag_name: v${{ inputs.version }}
+ name: DeWeb v${{ inputs.version }}
draft: ${{ inputs.release-as-draft }}
prerelease: ${{ inputs.release-as-prerelease }}
generate_release_notes: ${{ inputs.generate-release-notes }}
files: |
- ./deweb-*/deweb-*
+ artifacts/server/deweb-server_*/deweb-server_*
+ artifacts/plugin/deweb-plugin_*_zip/deweb-plugin_*.zip
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-region: eu-west-3
+ aws-access-key-id: ${{ secrets.MS_S3_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.MS_S3_SECRET_ACCESS_KEY }}
+
+ - name: s3 Upload
+ env:
+ AWS3: ${{ vars.MS_S3_BUCKET }}
+ TAG: ${{ inputs.version }}
+ run: aws s3 cp artifacts/plugin/deweb-plugin_*_zip/deweb-plugin_*.zip s3://${AWS3}/plugins/deweb/${TAG}/
diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml
new file mode 100644
index 00000000..1b6fc5da
--- /dev/null
+++ b/.github/workflows/server.yml
@@ -0,0 +1,124 @@
+name: Server
+
+on:
+ workflow_dispatch:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ description: "Version for the build, without v prefix"
+ required: false
+
+env:
+ VERSION: ${{ inputs.version }}
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: ./server
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Installing dependencies
+ uses: ./.github/actions/install
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Run golangci-lint
+ uses: golangci/golangci-lint-action@v6
+ with:
+ version: v1.64
+ args: --timeout=3m
+ working-directory: ./server
+
+ - name: Install gofumpt
+ run: go install mvdan.cc/gofumpt@v0.7.0
+
+ - name: Run gofumpt
+ run: |
+ ERRORS=$(gofumpt -l . | wc -l)
+ if [[ "$ERRORS" != "0" ]]; then
+ echo "following files are not gofumpted:"
+ gofumpt -l .
+ exit 1
+ fi
+
+ test:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: ./server
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Installing dependencies
+ uses: ./.github/actions/install
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Run unit tests
+ run: go test ./...
+
+ build:
+ name: Build server
+ strategy:
+ matrix:
+ include:
+ - os: ubuntu-24.04
+ arch: amd64
+ target: windows
+ ext: .exe
+ - os: ubuntu-24.04
+ arch: amd64
+ target: linux
+ - os: ubuntu-24.04
+ arch: arm64
+ target: linux
+ - os: macos-13
+ arch: amd64
+ target: darwin
+ - os: macos-14
+ arch: arm64
+ target: darwin
+
+ runs-on: ${{ matrix.os }}
+ defaults:
+ run:
+ working-directory: ./server
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Installing dependencies
+ uses: ./.github/actions/install
+ with:
+ os: ${{ matrix.os }}
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Check if VERSION exists
+ shell: bash
+ id: check_version
+ run: |
+ if [ ! -z "${{ env.VERSION }}" ]; then
+ echo "PRODUCTION=true" >> $GITHUB_ENV
+ fi
+
+ - name: Build Server
+ shell: bash
+ run: task build
+ env:
+ OS: ${{ matrix.target }}
+ ARCH: ${{ matrix.arch }}
+
+ - name: Rename Server artifact
+ run: mv build/deweb-server${{ matrix.ext }} build/deweb-server_${{ matrix.target }}_${{ matrix.arch }}${{ matrix.ext }}
+
+ - name: Upload Server artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: deweb-server_${{ matrix.target }}_${{ matrix.arch }}
+ path: server/build/deweb-server_${{ matrix.target }}_${{ matrix.arch }}${{ matrix.ext }}
\ No newline at end of file
diff --git a/plugin/.gitignore b/plugin/.gitignore
new file mode 100644
index 00000000..c251093d
--- /dev/null
+++ b/plugin/.gitignore
@@ -0,0 +1,8 @@
+# build directory
+build/
+
+# generated files
+int/api/html/dist/
+
+# log files
+*.log
diff --git a/plugin/README.md b/plugin/README.md
new file mode 100644
index 00000000..36b8d297
--- /dev/null
+++ b/plugin/README.md
@@ -0,0 +1,39 @@
+# DeWeb Station Plugin
+
+The DeWeb Station Plugin is a plugin for the DeWeb Station that allows you to install and run the DeWeb Server really easily on your computer.
+
+## Developer guide
+
+### Prerequisites
+
+Ensure you have `task` installed. Follow the instructions [here](https://taskfile.dev/installation/).
+
+### Setup
+
+1. Install required tools:
+
+ ```bash
+ task install
+ ```
+
+2. Generate:
+
+ ```bash
+ task generate
+ ```
+
+3. Build the plugin binary:
+
+ ```bash
+ task build
+ ```
+
+4. The binary will be stored in the `./build` directory.
+
+### Install
+
+To install the plugin, you can use the following command:
+
+```bash
+task install-plugin
+```
diff --git a/plugin/Taskfile.yml b/plugin/Taskfile.yml
new file mode 100644
index 00000000..0d0f1cc2
--- /dev/null
+++ b/plugin/Taskfile.yml
@@ -0,0 +1,100 @@
+version: "3"
+
+tasks:
+ install:
+ cmds:
+ - cmd: go install github.com/go-swagger/go-swagger/cmd/swagger@latest
+
+ generate:
+ cmds:
+ - cmd: mkdir -p ./build
+ - cmd: go generate ./...
+
+ build-server:
+ dir: ../server
+ cmds:
+ - cmd: task build
+ - cmd: cp build/deweb-server{{.BIN_EXT}} ../plugin/build
+ vars:
+ BIN_EXT: '{{if eq .OS "windows"}}.exe{{end}}'
+
+ run:
+ cmds:
+ - cmd: ./build/deweb-plugin
+
+ build:
+ cmds:
+ - task: build:internal
+ vars:
+ APP_NAME: plugin
+ BIN_DIR: build
+
+ build:internal:
+ build:
+ desc: Internal build task
+ internal: true
+ cmds:
+ - cmd: echo Building DeWeb {{.APP_NAME}} for {{.OS | default OS}}/{{.ARCH | default ARCH}}
+ silent: true
+ - cmd: 'echo Mode: {{if eq .PRODUCTION "true"}}Production{{else}}Development{{end}}'
+ silent: true
+ - cmd: 'echo Version: {{.VERSION | default "Development"}}'
+ silent: true
+ - cmd: go build {{.BUILD_FLAGS}} -o {{.BIN_DIR}}/deweb-{{.APP_NAME}}{{.BIN_EXT}} ./main.go
+ vars:
+ # We need this check for nil and empty string because a simple check for empty string doesn't work as expected
+ VERSION_FLAG: '{{if ne .VERSION nil}}{{if ne .VERSION ""}}-X github.com/massalabs/deweb-plugin/int/config.Version=v{{.VERSION}}{{end}}{{end}}'
+ BUILD_FLAGS: '{{if eq .PRODUCTION "true"}}-tags production {{end}}-ldflags="{{.VERSION_FLAG}}{{if eq .PRODUCTION "true"}} -w -s{{end}}"'
+ BIN_EXT: '{{if eq .OS "windows"}}.exe{{end}}'
+ env:
+ GOOS: "{{.OS | default OS}}"
+ GOARCH: "{{.ARCH | default ARCH}}"
+ PRODUCTION: '{{.PRODUCTION | default "false"}}'
+ VERSION: "{{.VERSION | default nil}}"
+
+ clean:
+ cmds:
+ - cmd: rm -rf build
+
+ install-plugin:
+ cmds:
+ - cmd: mkdir -p /usr/local/share/massastation/plugins/deweb-plugin
+ platforms: [linux, darwin]
+ - cmd: cp build/deweb-plugin /usr/local/share/massastation/plugins/deweb-plugin
+ platforms: [linux, darwin]
+ - cmd: cp build/deweb-server /usr/local/share/massastation/plugins/deweb-plugin
+ platforms: [linux, darwin]
+ - cmd: cp favicon.png /usr/local/share/massastation/plugins/deweb-plugin
+ platforms: [linux, darwin]
+ - cmd: cp manifest.json /usr/local/share/massastation/plugins/deweb-plugin
+ platforms: [linux, darwin]
+ - cmd: mkdir -p "C:/Program Files (x86)/MassaStation/plugins/deweb-plugin"
+ platforms: [windows]
+ - cmd: cp build/deweb-plugin.exe "C:/Program Files (x86)/MassaStation/plugins/deweb-plugin"
+ platforms: [windows]
+ - cmd: cp build/deweb-server.exe "C:/Program Files (x86)/MassaStation/plugins/deweb-plugin"
+ platforms: [windows]
+ - cmd: cp favicon.png "C:/Program Files (x86)/MassaStation/plugins/deweb-plugin"
+ platforms: [windows]
+ - cmd: cp manifest.json "C:/Program Files (x86)/MassaStation/plugins/deweb-plugin"
+ platforms: [windows]
+
+ test:
+ cmds:
+ - cmd: go test ./...
+
+ install:tools:
+ cmds:
+ - cmd: go install mvdan.cc/gofumpt@latest
+ - cmd: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+
+ lint:
+ cmds:
+ - cmd: golangci-lint run
+
+ fmt:
+ cmds:
+ - cmd: go mod tidy
+ - cmd: gofumpt -l -w .
+ - cmd: gci write . --skip-generated
+ - cmd: golangci-lint run --fix
diff --git a/plugin/api/models/cache_settings.go b/plugin/api/models/cache_settings.go
new file mode 100644
index 00000000..b76c0ffd
--- /dev/null
+++ b/plugin/api/models/cache_settings.go
@@ -0,0 +1,83 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "context"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+ "github.com/go-openapi/validate"
+)
+
+// CacheSettings cache settings
+//
+// swagger:model CacheSettings
+type CacheSettings struct {
+
+ // Directory to store the disk cache
+ DiskCacheDir string `json:"diskCacheDir,omitempty"`
+
+ // Whether caching is enabled
+ // Required: true
+ Enabled *bool `json:"enabled"`
+
+ // Duration in seconds for file list cache
+ FileListCacheDurationSeconds int32 `json:"fileListCacheDurationSeconds,omitempty"`
+
+ // Maximum number of files stored in disk cache
+ SiteDiskCacheMaxItems int32 `json:"siteDiskCacheMaxItems,omitempty"`
+
+ // Maximum number of files stored in RAM cache
+ SiteRAMCacheMaxItems int32 `json:"siteRamCacheMaxItems,omitempty"`
+}
+
+// Validate validates this cache settings
+func (m *CacheSettings) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateEnabled(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *CacheSettings) validateEnabled(formats strfmt.Registry) error {
+
+ if err := validate.Required("enabled", "body", m.Enabled); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ContextValidate validates this cache settings based on context it is used
+func (m *CacheSettings) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *CacheSettings) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *CacheSettings) UnmarshalBinary(b []byte) error {
+ var res CacheSettings
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/plugin/api/models/error.go b/plugin/api/models/error.go
new file mode 100644
index 00000000..cc6855e1
--- /dev/null
+++ b/plugin/api/models/error.go
@@ -0,0 +1,88 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "context"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+ "github.com/go-openapi/validate"
+)
+
+// Error error
+//
+// swagger:model Error
+type Error struct {
+
+ // code
+ // Required: true
+ Code *int32 `json:"code"`
+
+ // message
+ // Required: true
+ Message *string `json:"message"`
+}
+
+// Validate validates this error
+func (m *Error) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateCode(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateMessage(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *Error) validateCode(formats strfmt.Registry) error {
+
+ if err := validate.Required("code", "body", m.Code); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (m *Error) validateMessage(formats strfmt.Registry) error {
+
+ if err := validate.Required("message", "body", m.Message); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ContextValidate validates this error based on context it is used
+func (m *Error) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *Error) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *Error) UnmarshalBinary(b []byte) error {
+ var res Error
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/plugin/api/models/server_status.go b/plugin/api/models/server_status.go
new file mode 100644
index 00000000..821bf239
--- /dev/null
+++ b/plugin/api/models/server_status.go
@@ -0,0 +1,219 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "context"
+ "encoding/json"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+ "github.com/go-openapi/validate"
+)
+
+// ServerStatus server status
+//
+// swagger:model ServerStatus
+type ServerStatus struct {
+
+ // Error message if server failed to start or is in error state
+ ErrorMessage string `json:"errorMessage,omitempty"`
+
+ // network
+ Network *ServerStatusNetwork `json:"network,omitempty"`
+
+ // The port the server is running on
+ ServerPort int32 `json:"serverPort,omitempty"`
+
+ // status
+ // Enum: ["running","stopped","starting","stopping","error"]
+ Status string `json:"status,omitempty"`
+}
+
+// Validate validates this server status
+func (m *ServerStatus) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateNetwork(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateStatus(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ServerStatus) validateNetwork(formats strfmt.Registry) error {
+ if swag.IsZero(m.Network) { // not required
+ return nil
+ }
+
+ if m.Network != nil {
+ if err := m.Network.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("network")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("network")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+var serverStatusTypeStatusPropEnum []interface{}
+
+func init() {
+ var res []string
+ if err := json.Unmarshal([]byte(`["running","stopped","starting","stopping","error"]`), &res); err != nil {
+ panic(err)
+ }
+ for _, v := range res {
+ serverStatusTypeStatusPropEnum = append(serverStatusTypeStatusPropEnum, v)
+ }
+}
+
+const (
+
+ // ServerStatusStatusRunning captures enum value "running"
+ ServerStatusStatusRunning string = "running"
+
+ // ServerStatusStatusStopped captures enum value "stopped"
+ ServerStatusStatusStopped string = "stopped"
+
+ // ServerStatusStatusStarting captures enum value "starting"
+ ServerStatusStatusStarting string = "starting"
+
+ // ServerStatusStatusStopping captures enum value "stopping"
+ ServerStatusStatusStopping string = "stopping"
+
+ // ServerStatusStatusError captures enum value "error"
+ ServerStatusStatusError string = "error"
+)
+
+// prop value enum
+func (m *ServerStatus) validateStatusEnum(path, location string, value string) error {
+ if err := validate.EnumCase(path, location, value, serverStatusTypeStatusPropEnum, true); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (m *ServerStatus) validateStatus(formats strfmt.Registry) error {
+ if swag.IsZero(m.Status) { // not required
+ return nil
+ }
+
+ // value enum
+ if err := m.validateStatusEnum("status", "body", m.Status); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ContextValidate validate this server status based on the context it is used
+func (m *ServerStatus) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.contextValidateNetwork(ctx, formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *ServerStatus) contextValidateNetwork(ctx context.Context, formats strfmt.Registry) error {
+
+ if m.Network != nil {
+
+ if swag.IsZero(m.Network) { // not required
+ return nil
+ }
+
+ if err := m.Network.ContextValidate(ctx, formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("network")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("network")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ServerStatus) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ServerStatus) UnmarshalBinary(b []byte) error {
+ var res ServerStatus
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
+
+// ServerStatusNetwork server status network
+//
+// swagger:model ServerStatusNetwork
+type ServerStatusNetwork struct {
+
+ // chain ID
+ ChainID uint64 `json:"chainID,omitempty"`
+
+ // network
+ Network string `json:"network,omitempty"`
+
+ // version
+ Version string `json:"version,omitempty"`
+}
+
+// Validate validates this server status network
+func (m *ServerStatusNetwork) Validate(formats strfmt.Registry) error {
+ return nil
+}
+
+// ContextValidate validates this server status network based on context it is used
+func (m *ServerStatusNetwork) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *ServerStatusNetwork) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *ServerStatusNetwork) UnmarshalBinary(b []byte) error {
+ var res ServerStatusNetwork
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/plugin/api/models/settings.go b/plugin/api/models/settings.go
new file mode 100644
index 00000000..b86860f4
--- /dev/null
+++ b/plugin/api/models/settings.go
@@ -0,0 +1,130 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "context"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+ "github.com/go-openapi/validate"
+)
+
+// Settings settings
+//
+// swagger:model Settings
+type Settings struct {
+
+ // cache
+ Cache *CacheSettings `json:"cache,omitempty"`
+
+ // The network node URL to connect to
+ // Required: true
+ NetworkURL *string `json:"networkUrl"`
+
+ // The port the server is running on
+ ServerPort int32 `json:"serverPort,omitempty"`
+}
+
+// Validate validates this settings
+func (m *Settings) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateCache(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if err := m.validateNetworkURL(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *Settings) validateCache(formats strfmt.Registry) error {
+ if swag.IsZero(m.Cache) { // not required
+ return nil
+ }
+
+ if m.Cache != nil {
+ if err := m.Cache.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("cache")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("cache")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (m *Settings) validateNetworkURL(formats strfmt.Registry) error {
+
+ if err := validate.Required("networkUrl", "body", m.NetworkURL); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ContextValidate validate this settings based on the context it is used
+func (m *Settings) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.contextValidateCache(ctx, formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *Settings) contextValidateCache(ctx context.Context, formats strfmt.Registry) error {
+
+ if m.Cache != nil {
+
+ if swag.IsZero(m.Cache) { // not required
+ return nil
+ }
+
+ if err := m.Cache.ContextValidate(ctx, formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("cache")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("cache")
+ }
+ return err
+ }
+ }
+
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *Settings) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *Settings) UnmarshalBinary(b []byte) error {
+ var res Settings
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/plugin/api/pluginAPI-V0.yml b/plugin/api/pluginAPI-V0.yml
new file mode 100644
index 00000000..ac206fd0
--- /dev/null
+++ b/plugin/api/pluginAPI-V0.yml
@@ -0,0 +1,163 @@
+swagger: "2.0"
+info:
+ title: DeWeb Plugin API
+ description: API for the DeWeb Plugin in MassaStation
+ version: 0.0.1
+
+schemes:
+ - http
+
+paths:
+ /web/{resource}:
+ get:
+ description: Plugin Frontend router.
+ operationId: PluginWebApp
+ produces:
+ - application/json
+ - text/javascript
+ - text/html
+ - text/css
+ - text/webp
+ - image/png
+ parameters:
+ - in: path
+ name: resource
+ required: true
+ type: string
+ description: Website resource.
+ responses:
+ "200":
+ description: Page found
+ "404":
+ description: Resource not found.
+ schema:
+ $ref: "#/definitions/Error"
+
+ /:
+ get:
+ description: Handle the default page of the plugin
+ operationId: DefaultPage
+ responses:
+ "302":
+ description: Redirect to /web
+
+ /api/server/status:
+ get:
+ description: Get the current server status
+ operationId: GetServerStatus
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: Current server status
+ schema:
+ $ref: "#/definitions/ServerStatus"
+
+ /api/settings:
+ get:
+ description: Get the current plugin settings
+ operationId: GetSettings
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: Current settings
+ schema:
+ $ref: "#/definitions/Settings"
+ put:
+ description: Update plugin settings
+ operationId: UpdateSettings
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: settings
+ description: New settings
+ required: true
+ schema:
+ $ref: "#/definitions/Settings"
+ responses:
+ "200":
+ description: Settings updated successfully
+ "400":
+ description: Error updating settings
+ schema:
+ $ref: "#/definitions/Error"
+
+definitions:
+ Error:
+ type: object
+ properties:
+ code:
+ type: integer
+ format: int32
+ message:
+ type: string
+ required:
+ - code
+ - message
+
+ ServerStatus:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [running, stopped, starting, stopping, error]
+ serverPort:
+ type: integer
+ format: int32
+ description: The port the server is running on
+ errorMessage:
+ type: string
+ description: Error message if server failed to start or is in error state
+ network:
+ type: object
+ properties:
+ network:
+ type: string
+ version:
+ type: string
+ chainID:
+ type: integer
+ format: uint64
+
+ Settings:
+ type: object
+ properties:
+ networkUrl:
+ type: string
+ description: The network node URL to connect to
+ serverPort:
+ type: integer
+ format: int32
+ description: The port the server is running on
+ cache:
+ $ref: "#/definitions/CacheSettings"
+ required:
+ - networkUrl
+
+ CacheSettings:
+ type: object
+ properties:
+ enabled:
+ type: boolean
+ description: Whether caching is enabled
+ siteRamCacheMaxItems:
+ type: integer
+ format: int32
+ description: Maximum number of files stored in RAM cache
+ siteDiskCacheMaxItems:
+ type: integer
+ format: int32
+ description: Maximum number of files stored in disk cache
+ diskCacheDir:
+ type: string
+ description: Directory to store the disk cache
+ fileListCacheDurationSeconds:
+ type: integer
+ format: int32
+ description: Duration in seconds for file list cache
+ required:
+ - enabled
diff --git a/plugin/api/restapi/configure_deweb_plugin.go b/plugin/api/restapi/configure_deweb_plugin.go
new file mode 100644
index 00000000..130c5316
--- /dev/null
+++ b/plugin/api/restapi/configure_deweb_plugin.go
@@ -0,0 +1,108 @@
+// This file is safe to edit. Once it exists it will not be overwritten
+
+package restapi
+
+import (
+ "crypto/tls"
+ "io"
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/massalabs/deweb-plugin/api/restapi/operations"
+)
+
+//go:generate swagger generate server --target ../../api --name DewebPlugin --spec ../pluginAPI-V0.yml --principal interface{} --exclude-main
+
+func configureFlags(api *operations.DewebPluginAPI) {
+ // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... }
+}
+
+func configureAPI(api *operations.DewebPluginAPI) http.Handler {
+ // configure the api here
+ api.ServeError = errors.ServeError
+
+ // Set your custom logger if needed. Default one is log.Printf
+ // Expected interface func(string, ...interface{})
+ //
+ // Example:
+ // api.Logger = log.Printf
+
+ api.UseSwaggerUI()
+ // To continue using redoc as your UI, uncomment the following line
+ // api.UseRedoc()
+
+ api.JSONConsumer = runtime.JSONConsumer()
+
+ api.BinProducer = runtime.ByteStreamProducer()
+ api.CSSProducer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("css producer has not yet been implemented")
+ })
+ api.HTMLProducer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("html producer has not yet been implemented")
+ })
+ api.JsProducer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("js producer has not yet been implemented")
+ })
+ api.JSONProducer = runtime.JSONProducer()
+ api.TextWebpProducer = runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("textWebp producer has not yet been implemented")
+ })
+
+ if api.GetServerStatusHandler == nil {
+ api.GetServerStatusHandler = operations.GetServerStatusHandlerFunc(func(params operations.GetServerStatusParams) middleware.Responder {
+ return middleware.NotImplemented("operation operations.GetServerStatus has not yet been implemented")
+ })
+ }
+ if api.GetSettingsHandler == nil {
+ api.GetSettingsHandler = operations.GetSettingsHandlerFunc(func(params operations.GetSettingsParams) middleware.Responder {
+ return middleware.NotImplemented("operation operations.GetSettings has not yet been implemented")
+ })
+ }
+ if api.PluginWebAppHandler == nil {
+ api.PluginWebAppHandler = operations.PluginWebAppHandlerFunc(func(params operations.PluginWebAppParams) middleware.Responder {
+ return middleware.NotImplemented("operation operations.PluginWebApp has not yet been implemented")
+ })
+ }
+ if api.DefaultPageHandler == nil {
+ api.DefaultPageHandler = operations.DefaultPageHandlerFunc(func(params operations.DefaultPageParams) middleware.Responder {
+ return middleware.NotImplemented("operation operations.RedirectToWeb has not yet been implemented")
+ })
+ }
+ if api.UpdateSettingsHandler == nil {
+ api.UpdateSettingsHandler = operations.UpdateSettingsHandlerFunc(func(params operations.UpdateSettingsParams) middleware.Responder {
+ return middleware.NotImplemented("operation operations.UpdateSettings has not yet been implemented")
+ })
+ }
+
+ api.PreServerShutdown = func() {}
+
+ api.ServerShutdown = func() {}
+
+ return setupGlobalMiddleware(api.Serve(setupMiddlewares))
+}
+
+// The TLS configuration before HTTPS server starts.
+func configureTLS(tlsConfig *tls.Config) {
+ // Make all necessary changes to the TLS configuration here.
+}
+
+// As soon as server is initialized but not run yet, this function will be called.
+// If you need to modify a config, store server instance to stop it individually later, this is the place.
+// This function can be called multiple times, depending on the number of serving schemes.
+// scheme value will be set accordingly: "http", "https" or "unix".
+func configureServer(s *http.Server, scheme, addr string) {
+}
+
+// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
+// The middleware executes after routing but before authentication, binding and validation.
+func setupMiddlewares(handler http.Handler) http.Handler {
+ return handler
+}
+
+// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
+// So this is a good place to plug in a panic handling middleware, logging and metrics.
+func setupGlobalMiddleware(handler http.Handler) http.Handler {
+ return handler
+}
diff --git a/plugin/api/restapi/doc.go b/plugin/api/restapi/doc.go
new file mode 100644
index 00000000..1e1cd67a
--- /dev/null
+++ b/plugin/api/restapi/doc.go
@@ -0,0 +1,24 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+// Package restapi DeWeb Plugin API
+//
+// API for the DeWeb Plugin in MassaStation
+// Schemes:
+// http
+// Host: localhost
+// BasePath: /
+// Version: 0.0.1
+//
+// Consumes:
+// - application/json
+//
+// Produces:
+// - image/png
+// - text/css
+// - text/html
+// - text/javascript
+// - application/json
+// - text/webp
+//
+// swagger:meta
+package restapi
diff --git a/plugin/api/restapi/embedded_spec.go b/plugin/api/restapi/embedded_spec.go
new file mode 100644
index 00000000..72984cae
--- /dev/null
+++ b/plugin/api/restapi/embedded_spec.go
@@ -0,0 +1,499 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package restapi
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "encoding/json"
+)
+
+var (
+ // SwaggerJSON embedded version of the swagger document used at generation time
+ SwaggerJSON json.RawMessage
+ // FlatSwaggerJSON embedded flattened version of the swagger document used at generation time
+ FlatSwaggerJSON json.RawMessage
+)
+
+func init() {
+ SwaggerJSON = json.RawMessage([]byte(`{
+ "schemes": [
+ "http"
+ ],
+ "swagger": "2.0",
+ "info": {
+ "description": "API for the DeWeb Plugin in MassaStation",
+ "title": "DeWeb Plugin API",
+ "version": "0.0.1"
+ },
+ "paths": {
+ "/": {
+ "get": {
+ "description": "Handle the default page of the plugin",
+ "operationId": "DefaultPage",
+ "responses": {
+ "302": {
+ "description": "Redirect to /web"
+ }
+ }
+ }
+ },
+ "/api/server/status": {
+ "get": {
+ "description": "Get the current server status",
+ "produces": [
+ "application/json"
+ ],
+ "operationId": "GetServerStatus",
+ "responses": {
+ "200": {
+ "description": "Current server status",
+ "schema": {
+ "$ref": "#/definitions/ServerStatus"
+ }
+ }
+ }
+ }
+ },
+ "/api/settings": {
+ "get": {
+ "description": "Get the current plugin settings",
+ "produces": [
+ "application/json"
+ ],
+ "operationId": "GetSettings",
+ "responses": {
+ "200": {
+ "description": "Current settings",
+ "schema": {
+ "$ref": "#/definitions/Settings"
+ }
+ }
+ }
+ },
+ "put": {
+ "description": "Update plugin settings",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "operationId": "UpdateSettings",
+ "parameters": [
+ {
+ "description": "New settings",
+ "name": "settings",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Settings"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Settings updated successfully"
+ },
+ "400": {
+ "description": "Error updating settings",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/web/{resource}": {
+ "get": {
+ "description": "Plugin Frontend router.",
+ "produces": [
+ "application/json",
+ "text/javascript",
+ "text/html",
+ "text/css",
+ "text/webp",
+ "image/png"
+ ],
+ "operationId": "PluginWebApp",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Website resource.",
+ "name": "resource",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Page found"
+ },
+ "404": {
+ "description": "Resource not found.",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "CacheSettings": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "diskCacheDir": {
+ "description": "Directory to store the disk cache",
+ "type": "string"
+ },
+ "enabled": {
+ "description": "Whether caching is enabled",
+ "type": "boolean"
+ },
+ "fileListCacheDurationSeconds": {
+ "description": "Duration in seconds for file list cache",
+ "type": "integer",
+ "format": "int32"
+ },
+ "siteDiskCacheMaxItems": {
+ "description": "Maximum number of files stored in disk cache",
+ "type": "integer",
+ "format": "int32"
+ },
+ "siteRamCacheMaxItems": {
+ "description": "Maximum number of files stored in RAM cache",
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ },
+ "Error": {
+ "type": "object",
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ },
+ "ServerStatus": {
+ "type": "object",
+ "properties": {
+ "errorMessage": {
+ "description": "Error message if server failed to start or is in error state",
+ "type": "string"
+ },
+ "network": {
+ "type": "object",
+ "properties": {
+ "chainID": {
+ "type": "integer",
+ "format": "uint64"
+ },
+ "network": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ },
+ "serverPort": {
+ "description": "The port the server is running on",
+ "type": "integer",
+ "format": "int32"
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "running",
+ "stopped",
+ "starting",
+ "stopping",
+ "error"
+ ]
+ }
+ }
+ },
+ "Settings": {
+ "type": "object",
+ "required": [
+ "networkUrl"
+ ],
+ "properties": {
+ "cache": {
+ "$ref": "#/definitions/CacheSettings"
+ },
+ "networkUrl": {
+ "description": "The network node URL to connect to",
+ "type": "string"
+ },
+ "serverPort": {
+ "description": "The port the server is running on",
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ }
+ }
+}`))
+ FlatSwaggerJSON = json.RawMessage([]byte(`{
+ "schemes": [
+ "http"
+ ],
+ "swagger": "2.0",
+ "info": {
+ "description": "API for the DeWeb Plugin in MassaStation",
+ "title": "DeWeb Plugin API",
+ "version": "0.0.1"
+ },
+ "paths": {
+ "/": {
+ "get": {
+ "description": "Handle the default page of the plugin",
+ "operationId": "DefaultPage",
+ "responses": {
+ "302": {
+ "description": "Redirect to /web"
+ }
+ }
+ }
+ },
+ "/api/server/status": {
+ "get": {
+ "description": "Get the current server status",
+ "produces": [
+ "application/json"
+ ],
+ "operationId": "GetServerStatus",
+ "responses": {
+ "200": {
+ "description": "Current server status",
+ "schema": {
+ "$ref": "#/definitions/ServerStatus"
+ }
+ }
+ }
+ }
+ },
+ "/api/settings": {
+ "get": {
+ "description": "Get the current plugin settings",
+ "produces": [
+ "application/json"
+ ],
+ "operationId": "GetSettings",
+ "responses": {
+ "200": {
+ "description": "Current settings",
+ "schema": {
+ "$ref": "#/definitions/Settings"
+ }
+ }
+ }
+ },
+ "put": {
+ "description": "Update plugin settings",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "operationId": "UpdateSettings",
+ "parameters": [
+ {
+ "description": "New settings",
+ "name": "settings",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/Settings"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Settings updated successfully"
+ },
+ "400": {
+ "description": "Error updating settings",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ },
+ "/web/{resource}": {
+ "get": {
+ "description": "Plugin Frontend router.",
+ "produces": [
+ "application/json",
+ "text/javascript",
+ "text/html",
+ "text/css",
+ "text/webp",
+ "image/png"
+ ],
+ "operationId": "PluginWebApp",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Website resource.",
+ "name": "resource",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Page found"
+ },
+ "404": {
+ "description": "Resource not found.",
+ "schema": {
+ "$ref": "#/definitions/Error"
+ }
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "CacheSettings": {
+ "type": "object",
+ "required": [
+ "enabled"
+ ],
+ "properties": {
+ "diskCacheDir": {
+ "description": "Directory to store the disk cache",
+ "type": "string"
+ },
+ "enabled": {
+ "description": "Whether caching is enabled",
+ "type": "boolean"
+ },
+ "fileListCacheDurationSeconds": {
+ "description": "Duration in seconds for file list cache",
+ "type": "integer",
+ "format": "int32"
+ },
+ "siteDiskCacheMaxItems": {
+ "description": "Maximum number of files stored in disk cache",
+ "type": "integer",
+ "format": "int32"
+ },
+ "siteRamCacheMaxItems": {
+ "description": "Maximum number of files stored in RAM cache",
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ },
+ "Error": {
+ "type": "object",
+ "required": [
+ "code",
+ "message"
+ ],
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ },
+ "ServerStatus": {
+ "type": "object",
+ "properties": {
+ "errorMessage": {
+ "description": "Error message if server failed to start or is in error state",
+ "type": "string"
+ },
+ "network": {
+ "type": "object",
+ "properties": {
+ "chainID": {
+ "type": "integer",
+ "format": "uint64"
+ },
+ "network": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ },
+ "serverPort": {
+ "description": "The port the server is running on",
+ "type": "integer",
+ "format": "int32"
+ },
+ "status": {
+ "type": "string",
+ "enum": [
+ "running",
+ "stopped",
+ "starting",
+ "stopping",
+ "error"
+ ]
+ }
+ }
+ },
+ "ServerStatusNetwork": {
+ "type": "object",
+ "properties": {
+ "chainID": {
+ "type": "integer",
+ "format": "uint64"
+ },
+ "network": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ },
+ "Settings": {
+ "type": "object",
+ "required": [
+ "networkUrl"
+ ],
+ "properties": {
+ "cache": {
+ "$ref": "#/definitions/CacheSettings"
+ },
+ "networkUrl": {
+ "description": "The network node URL to connect to",
+ "type": "string"
+ },
+ "serverPort": {
+ "description": "The port the server is running on",
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ }
+ }
+}`))
+}
diff --git a/plugin/api/restapi/operations/default_page.go b/plugin/api/restapi/operations/default_page.go
new file mode 100644
index 00000000..3875419b
--- /dev/null
+++ b/plugin/api/restapi/operations/default_page.go
@@ -0,0 +1,56 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// DefaultPageHandlerFunc turns a function with the right signature into a default page handler
+type DefaultPageHandlerFunc func(DefaultPageParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn DefaultPageHandlerFunc) Handle(params DefaultPageParams) middleware.Responder {
+ return fn(params)
+}
+
+// DefaultPageHandler interface for that can handle valid default page params
+type DefaultPageHandler interface {
+ Handle(DefaultPageParams) middleware.Responder
+}
+
+// NewDefaultPage creates a new http.Handler for the default page operation
+func NewDefaultPage(ctx *middleware.Context, handler DefaultPageHandler) *DefaultPage {
+ return &DefaultPage{Context: ctx, Handler: handler}
+}
+
+/*
+ DefaultPage swagger:route GET / defaultPage
+
+Handle the default page of the plugin
+*/
+type DefaultPage struct {
+ Context *middleware.Context
+ Handler DefaultPageHandler
+}
+
+func (o *DefaultPage) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewDefaultPageParams()
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/plugin/api/restapi/operations/default_page_parameters.go b/plugin/api/restapi/operations/default_page_parameters.go
new file mode 100644
index 00000000..fa657be3
--- /dev/null
+++ b/plugin/api/restapi/operations/default_page_parameters.go
@@ -0,0 +1,46 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// NewDefaultPageParams creates a new DefaultPageParams object
+//
+// There are no default values defined in the spec.
+func NewDefaultPageParams() DefaultPageParams {
+
+ return DefaultPageParams{}
+}
+
+// DefaultPageParams contains all the bound params for the default page operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters DefaultPage
+type DefaultPageParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewDefaultPageParams() beforehand.
+func (o *DefaultPageParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/plugin/api/restapi/operations/default_page_responses.go b/plugin/api/restapi/operations/default_page_responses.go
new file mode 100644
index 00000000..d44d4ee7
--- /dev/null
+++ b/plugin/api/restapi/operations/default_page_responses.go
@@ -0,0 +1,37 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+)
+
+// DefaultPageFoundCode is the HTTP code returned for type DefaultPageFound
+const DefaultPageFoundCode int = 302
+
+/*
+DefaultPageFound Redirect to /web
+
+swagger:response defaultPageFound
+*/
+type DefaultPageFound struct {
+}
+
+// NewDefaultPageFound creates DefaultPageFound with default headers values
+func NewDefaultPageFound() *DefaultPageFound {
+
+ return &DefaultPageFound{}
+}
+
+// WriteResponse to the client
+func (o *DefaultPageFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
+
+ rw.WriteHeader(302)
+}
diff --git a/plugin/api/restapi/operations/default_page_urlbuilder.go b/plugin/api/restapi/operations/default_page_urlbuilder.go
new file mode 100644
index 00000000..75d057f0
--- /dev/null
+++ b/plugin/api/restapi/operations/default_page_urlbuilder.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+)
+
+// DefaultPageURL generates an URL for the default page operation
+type DefaultPageURL struct {
+ _basePath string
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *DefaultPageURL) WithBasePath(bp string) *DefaultPageURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *DefaultPageURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *DefaultPageURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/"
+
+ _basePath := o._basePath
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *DefaultPageURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *DefaultPageURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *DefaultPageURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on DefaultPageURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on DefaultPageURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *DefaultPageURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/plugin/api/restapi/operations/deweb_plugin_api.go b/plugin/api/restapi/operations/deweb_plugin_api.go
new file mode 100644
index 00000000..2b120457
--- /dev/null
+++ b/plugin/api/restapi/operations/deweb_plugin_api.go
@@ -0,0 +1,403 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/loads"
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/runtime/security"
+ "github.com/go-openapi/spec"
+ "github.com/go-openapi/strfmt"
+ "github.com/go-openapi/swag"
+)
+
+// NewDewebPluginAPI creates a new DewebPlugin instance
+func NewDewebPluginAPI(spec *loads.Document) *DewebPluginAPI {
+ return &DewebPluginAPI{
+ handlers: make(map[string]map[string]http.Handler),
+ formats: strfmt.Default,
+ defaultConsumes: "application/json",
+ defaultProduces: "application/json",
+ customConsumers: make(map[string]runtime.Consumer),
+ customProducers: make(map[string]runtime.Producer),
+ PreServerShutdown: func() {},
+ ServerShutdown: func() {},
+ spec: spec,
+ useSwaggerUI: false,
+ ServeError: errors.ServeError,
+ BasicAuthenticator: security.BasicAuth,
+ APIKeyAuthenticator: security.APIKeyAuth,
+ BearerAuthenticator: security.BearerAuth,
+
+ JSONConsumer: runtime.JSONConsumer(),
+
+ BinProducer: runtime.ByteStreamProducer(),
+ CSSProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("css producer has not yet been implemented")
+ }),
+ HTMLProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("html producer has not yet been implemented")
+ }),
+ JsProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("js producer has not yet been implemented")
+ }),
+ JSONProducer: runtime.JSONProducer(),
+ TextWebpProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
+ return errors.NotImplemented("textWebp producer has not yet been implemented")
+ }),
+
+ DefaultPageHandler: DefaultPageHandlerFunc(func(params DefaultPageParams) middleware.Responder {
+ return middleware.NotImplemented("operation DefaultPage has not yet been implemented")
+ }),
+ GetServerStatusHandler: GetServerStatusHandlerFunc(func(params GetServerStatusParams) middleware.Responder {
+ return middleware.NotImplemented("operation GetServerStatus has not yet been implemented")
+ }),
+ GetSettingsHandler: GetSettingsHandlerFunc(func(params GetSettingsParams) middleware.Responder {
+ return middleware.NotImplemented("operation GetSettings has not yet been implemented")
+ }),
+ PluginWebAppHandler: PluginWebAppHandlerFunc(func(params PluginWebAppParams) middleware.Responder {
+ return middleware.NotImplemented("operation PluginWebApp has not yet been implemented")
+ }),
+ UpdateSettingsHandler: UpdateSettingsHandlerFunc(func(params UpdateSettingsParams) middleware.Responder {
+ return middleware.NotImplemented("operation UpdateSettings has not yet been implemented")
+ }),
+ }
+}
+
+/*DewebPluginAPI API for the DeWeb Plugin in MassaStation */
+type DewebPluginAPI struct {
+ spec *loads.Document
+ context *middleware.Context
+ handlers map[string]map[string]http.Handler
+ formats strfmt.Registry
+ customConsumers map[string]runtime.Consumer
+ customProducers map[string]runtime.Producer
+ defaultConsumes string
+ defaultProduces string
+ Middleware func(middleware.Builder) http.Handler
+ useSwaggerUI bool
+
+ // BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function.
+ // It has a default implementation in the security package, however you can replace it for your particular usage.
+ BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
+
+ // APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function.
+ // It has a default implementation in the security package, however you can replace it for your particular usage.
+ APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
+
+ // BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function.
+ // It has a default implementation in the security package, however you can replace it for your particular usage.
+ BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
+
+ // JSONConsumer registers a consumer for the following mime types:
+ // - application/json
+ JSONConsumer runtime.Consumer
+
+ // BinProducer registers a producer for the following mime types:
+ // - image/png
+ BinProducer runtime.Producer
+ // CSSProducer registers a producer for the following mime types:
+ // - text/css
+ CSSProducer runtime.Producer
+ // HTMLProducer registers a producer for the following mime types:
+ // - text/html
+ HTMLProducer runtime.Producer
+ // JsProducer registers a producer for the following mime types:
+ // - text/javascript
+ JsProducer runtime.Producer
+ // JSONProducer registers a producer for the following mime types:
+ // - application/json
+ JSONProducer runtime.Producer
+ // TextWebpProducer registers a producer for the following mime types:
+ // - text/webp
+ TextWebpProducer runtime.Producer
+
+ // DefaultPageHandler sets the operation handler for the default page operation
+ DefaultPageHandler DefaultPageHandler
+ // GetServerStatusHandler sets the operation handler for the get server status operation
+ GetServerStatusHandler GetServerStatusHandler
+ // GetSettingsHandler sets the operation handler for the get settings operation
+ GetSettingsHandler GetSettingsHandler
+ // PluginWebAppHandler sets the operation handler for the plugin web app operation
+ PluginWebAppHandler PluginWebAppHandler
+ // UpdateSettingsHandler sets the operation handler for the update settings operation
+ UpdateSettingsHandler UpdateSettingsHandler
+
+ // ServeError is called when an error is received, there is a default handler
+ // but you can set your own with this
+ ServeError func(http.ResponseWriter, *http.Request, error)
+
+ // PreServerShutdown is called before the HTTP(S) server is shutdown
+ // This allows for custom functions to get executed before the HTTP(S) server stops accepting traffic
+ PreServerShutdown func()
+
+ // ServerShutdown is called when the HTTP(S) server is shut down and done
+ // handling all active connections and does not accept connections any more
+ ServerShutdown func()
+
+ // Custom command line argument groups with their descriptions
+ CommandLineOptionsGroups []swag.CommandLineOptionsGroup
+
+ // User defined logger function.
+ Logger func(string, ...interface{})
+}
+
+// UseRedoc for documentation at /docs
+func (o *DewebPluginAPI) UseRedoc() {
+ o.useSwaggerUI = false
+}
+
+// UseSwaggerUI for documentation at /docs
+func (o *DewebPluginAPI) UseSwaggerUI() {
+ o.useSwaggerUI = true
+}
+
+// SetDefaultProduces sets the default produces media type
+func (o *DewebPluginAPI) SetDefaultProduces(mediaType string) {
+ o.defaultProduces = mediaType
+}
+
+// SetDefaultConsumes returns the default consumes media type
+func (o *DewebPluginAPI) SetDefaultConsumes(mediaType string) {
+ o.defaultConsumes = mediaType
+}
+
+// SetSpec sets a spec that will be served for the clients.
+func (o *DewebPluginAPI) SetSpec(spec *loads.Document) {
+ o.spec = spec
+}
+
+// DefaultProduces returns the default produces media type
+func (o *DewebPluginAPI) DefaultProduces() string {
+ return o.defaultProduces
+}
+
+// DefaultConsumes returns the default consumes media type
+func (o *DewebPluginAPI) DefaultConsumes() string {
+ return o.defaultConsumes
+}
+
+// Formats returns the registered string formats
+func (o *DewebPluginAPI) Formats() strfmt.Registry {
+ return o.formats
+}
+
+// RegisterFormat registers a custom format validator
+func (o *DewebPluginAPI) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) {
+ o.formats.Add(name, format, validator)
+}
+
+// Validate validates the registrations in the DewebPluginAPI
+func (o *DewebPluginAPI) Validate() error {
+ var unregistered []string
+
+ if o.JSONConsumer == nil {
+ unregistered = append(unregistered, "JSONConsumer")
+ }
+
+ if o.BinProducer == nil {
+ unregistered = append(unregistered, "BinProducer")
+ }
+ if o.CSSProducer == nil {
+ unregistered = append(unregistered, "CSSProducer")
+ }
+ if o.HTMLProducer == nil {
+ unregistered = append(unregistered, "HTMLProducer")
+ }
+ if o.JsProducer == nil {
+ unregistered = append(unregistered, "JsProducer")
+ }
+ if o.JSONProducer == nil {
+ unregistered = append(unregistered, "JSONProducer")
+ }
+ if o.TextWebpProducer == nil {
+ unregistered = append(unregistered, "TextWebpProducer")
+ }
+
+ if o.DefaultPageHandler == nil {
+ unregistered = append(unregistered, "DefaultPageHandler")
+ }
+ if o.GetServerStatusHandler == nil {
+ unregistered = append(unregistered, "GetServerStatusHandler")
+ }
+ if o.GetSettingsHandler == nil {
+ unregistered = append(unregistered, "GetSettingsHandler")
+ }
+ if o.PluginWebAppHandler == nil {
+ unregistered = append(unregistered, "PluginWebAppHandler")
+ }
+ if o.UpdateSettingsHandler == nil {
+ unregistered = append(unregistered, "UpdateSettingsHandler")
+ }
+
+ if len(unregistered) > 0 {
+ return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", "))
+ }
+
+ return nil
+}
+
+// ServeErrorFor gets a error handler for a given operation id
+func (o *DewebPluginAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) {
+ return o.ServeError
+}
+
+// AuthenticatorsFor gets the authenticators for the specified security schemes
+func (o *DewebPluginAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator {
+ return nil
+}
+
+// Authorizer returns the registered authorizer
+func (o *DewebPluginAPI) Authorizer() runtime.Authorizer {
+ return nil
+}
+
+// ConsumersFor gets the consumers for the specified media types.
+// MIME type parameters are ignored here.
+func (o *DewebPluginAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer {
+ result := make(map[string]runtime.Consumer, len(mediaTypes))
+ for _, mt := range mediaTypes {
+ switch mt {
+ case "application/json":
+ result["application/json"] = o.JSONConsumer
+ }
+
+ if c, ok := o.customConsumers[mt]; ok {
+ result[mt] = c
+ }
+ }
+ return result
+}
+
+// ProducersFor gets the producers for the specified media types.
+// MIME type parameters are ignored here.
+func (o *DewebPluginAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
+ result := make(map[string]runtime.Producer, len(mediaTypes))
+ for _, mt := range mediaTypes {
+ switch mt {
+ case "image/png":
+ result["image/png"] = o.BinProducer
+ case "text/css":
+ result["text/css"] = o.CSSProducer
+ case "text/html":
+ result["text/html"] = o.HTMLProducer
+ case "text/javascript":
+ result["text/javascript"] = o.JsProducer
+ case "application/json":
+ result["application/json"] = o.JSONProducer
+ case "text/webp":
+ result["text/webp"] = o.TextWebpProducer
+ }
+
+ if p, ok := o.customProducers[mt]; ok {
+ result[mt] = p
+ }
+ }
+ return result
+}
+
+// HandlerFor gets a http.Handler for the provided operation method and path
+func (o *DewebPluginAPI) HandlerFor(method, path string) (http.Handler, bool) {
+ if o.handlers == nil {
+ return nil, false
+ }
+ um := strings.ToUpper(method)
+ if _, ok := o.handlers[um]; !ok {
+ return nil, false
+ }
+ if path == "/" {
+ path = ""
+ }
+ h, ok := o.handlers[um][path]
+ return h, ok
+}
+
+// Context returns the middleware context for the deweb plugin API
+func (o *DewebPluginAPI) Context() *middleware.Context {
+ if o.context == nil {
+ o.context = middleware.NewRoutableContext(o.spec, o, nil)
+ }
+
+ return o.context
+}
+
+func (o *DewebPluginAPI) initHandlerCache() {
+ o.Context() // don't care about the result, just that the initialization happened
+ if o.handlers == nil {
+ o.handlers = make(map[string]map[string]http.Handler)
+ }
+
+ if o.handlers["GET"] == nil {
+ o.handlers["GET"] = make(map[string]http.Handler)
+ }
+ o.handlers["GET"][""] = NewDefaultPage(o.context, o.DefaultPageHandler)
+ if o.handlers["GET"] == nil {
+ o.handlers["GET"] = make(map[string]http.Handler)
+ }
+ o.handlers["GET"]["/api/server/status"] = NewGetServerStatus(o.context, o.GetServerStatusHandler)
+ if o.handlers["GET"] == nil {
+ o.handlers["GET"] = make(map[string]http.Handler)
+ }
+ o.handlers["GET"]["/api/settings"] = NewGetSettings(o.context, o.GetSettingsHandler)
+ if o.handlers["GET"] == nil {
+ o.handlers["GET"] = make(map[string]http.Handler)
+ }
+ o.handlers["GET"]["/web/{resource}"] = NewPluginWebApp(o.context, o.PluginWebAppHandler)
+ if o.handlers["PUT"] == nil {
+ o.handlers["PUT"] = make(map[string]http.Handler)
+ }
+ o.handlers["PUT"]["/api/settings"] = NewUpdateSettings(o.context, o.UpdateSettingsHandler)
+}
+
+// Serve creates a http handler to serve the API over HTTP
+// can be used directly in http.ListenAndServe(":8000", api.Serve(nil))
+func (o *DewebPluginAPI) Serve(builder middleware.Builder) http.Handler {
+ o.Init()
+
+ if o.Middleware != nil {
+ return o.Middleware(builder)
+ }
+ if o.useSwaggerUI {
+ return o.context.APIHandlerSwaggerUI(builder)
+ }
+ return o.context.APIHandler(builder)
+}
+
+// Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit
+func (o *DewebPluginAPI) Init() {
+ if len(o.handlers) == 0 {
+ o.initHandlerCache()
+ }
+}
+
+// RegisterConsumer allows you to add (or override) a consumer for a media type.
+func (o *DewebPluginAPI) RegisterConsumer(mediaType string, consumer runtime.Consumer) {
+ o.customConsumers[mediaType] = consumer
+}
+
+// RegisterProducer allows you to add (or override) a producer for a media type.
+func (o *DewebPluginAPI) RegisterProducer(mediaType string, producer runtime.Producer) {
+ o.customProducers[mediaType] = producer
+}
+
+// AddMiddlewareFor adds a http middleware to existing handler
+func (o *DewebPluginAPI) AddMiddlewareFor(method, path string, builder middleware.Builder) {
+ um := strings.ToUpper(method)
+ if path == "/" {
+ path = ""
+ }
+ o.Init()
+ if h, ok := o.handlers[um][path]; ok {
+ o.handlers[um][path] = builder(h)
+ }
+}
diff --git a/plugin/api/restapi/operations/get_server_status.go b/plugin/api/restapi/operations/get_server_status.go
new file mode 100644
index 00000000..51c054a8
--- /dev/null
+++ b/plugin/api/restapi/operations/get_server_status.go
@@ -0,0 +1,56 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// GetServerStatusHandlerFunc turns a function with the right signature into a get server status handler
+type GetServerStatusHandlerFunc func(GetServerStatusParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn GetServerStatusHandlerFunc) Handle(params GetServerStatusParams) middleware.Responder {
+ return fn(params)
+}
+
+// GetServerStatusHandler interface for that can handle valid get server status params
+type GetServerStatusHandler interface {
+ Handle(GetServerStatusParams) middleware.Responder
+}
+
+// NewGetServerStatus creates a new http.Handler for the get server status operation
+func NewGetServerStatus(ctx *middleware.Context, handler GetServerStatusHandler) *GetServerStatus {
+ return &GetServerStatus{Context: ctx, Handler: handler}
+}
+
+/*
+ GetServerStatus swagger:route GET /api/server/status getServerStatus
+
+Get the current server status
+*/
+type GetServerStatus struct {
+ Context *middleware.Context
+ Handler GetServerStatusHandler
+}
+
+func (o *GetServerStatus) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewGetServerStatusParams()
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/plugin/api/restapi/operations/get_server_status_parameters.go b/plugin/api/restapi/operations/get_server_status_parameters.go
new file mode 100644
index 00000000..24499c0f
--- /dev/null
+++ b/plugin/api/restapi/operations/get_server_status_parameters.go
@@ -0,0 +1,46 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// NewGetServerStatusParams creates a new GetServerStatusParams object
+//
+// There are no default values defined in the spec.
+func NewGetServerStatusParams() GetServerStatusParams {
+
+ return GetServerStatusParams{}
+}
+
+// GetServerStatusParams contains all the bound params for the get server status operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters GetServerStatus
+type GetServerStatusParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewGetServerStatusParams() beforehand.
+func (o *GetServerStatusParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/plugin/api/restapi/operations/get_server_status_responses.go b/plugin/api/restapi/operations/get_server_status_responses.go
new file mode 100644
index 00000000..c83ebc56
--- /dev/null
+++ b/plugin/api/restapi/operations/get_server_status_responses.go
@@ -0,0 +1,59 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+
+ "github.com/massalabs/deweb-plugin/api/models"
+)
+
+// GetServerStatusOKCode is the HTTP code returned for type GetServerStatusOK
+const GetServerStatusOKCode int = 200
+
+/*
+GetServerStatusOK Current server status
+
+swagger:response getServerStatusOK
+*/
+type GetServerStatusOK struct {
+
+ /*
+ In: Body
+ */
+ Payload *models.ServerStatus `json:"body,omitempty"`
+}
+
+// NewGetServerStatusOK creates GetServerStatusOK with default headers values
+func NewGetServerStatusOK() *GetServerStatusOK {
+
+ return &GetServerStatusOK{}
+}
+
+// WithPayload adds the payload to the get server status o k response
+func (o *GetServerStatusOK) WithPayload(payload *models.ServerStatus) *GetServerStatusOK {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the get server status o k response
+func (o *GetServerStatusOK) SetPayload(payload *models.ServerStatus) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *GetServerStatusOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(200)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/plugin/api/restapi/operations/get_server_status_urlbuilder.go b/plugin/api/restapi/operations/get_server_status_urlbuilder.go
new file mode 100644
index 00000000..8ed2ed91
--- /dev/null
+++ b/plugin/api/restapi/operations/get_server_status_urlbuilder.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+)
+
+// GetServerStatusURL generates an URL for the get server status operation
+type GetServerStatusURL struct {
+ _basePath string
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *GetServerStatusURL) WithBasePath(bp string) *GetServerStatusURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *GetServerStatusURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *GetServerStatusURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/api/server/status"
+
+ _basePath := o._basePath
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *GetServerStatusURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *GetServerStatusURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *GetServerStatusURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on GetServerStatusURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on GetServerStatusURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *GetServerStatusURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/plugin/api/restapi/operations/get_settings.go b/plugin/api/restapi/operations/get_settings.go
new file mode 100644
index 00000000..813e767d
--- /dev/null
+++ b/plugin/api/restapi/operations/get_settings.go
@@ -0,0 +1,56 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// GetSettingsHandlerFunc turns a function with the right signature into a get settings handler
+type GetSettingsHandlerFunc func(GetSettingsParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn GetSettingsHandlerFunc) Handle(params GetSettingsParams) middleware.Responder {
+ return fn(params)
+}
+
+// GetSettingsHandler interface for that can handle valid get settings params
+type GetSettingsHandler interface {
+ Handle(GetSettingsParams) middleware.Responder
+}
+
+// NewGetSettings creates a new http.Handler for the get settings operation
+func NewGetSettings(ctx *middleware.Context, handler GetSettingsHandler) *GetSettings {
+ return &GetSettings{Context: ctx, Handler: handler}
+}
+
+/*
+ GetSettings swagger:route GET /api/settings getSettings
+
+Get the current plugin settings
+*/
+type GetSettings struct {
+ Context *middleware.Context
+ Handler GetSettingsHandler
+}
+
+func (o *GetSettings) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewGetSettingsParams()
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/plugin/api/restapi/operations/get_settings_parameters.go b/plugin/api/restapi/operations/get_settings_parameters.go
new file mode 100644
index 00000000..8b8fe5ba
--- /dev/null
+++ b/plugin/api/restapi/operations/get_settings_parameters.go
@@ -0,0 +1,46 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// NewGetSettingsParams creates a new GetSettingsParams object
+//
+// There are no default values defined in the spec.
+func NewGetSettingsParams() GetSettingsParams {
+
+ return GetSettingsParams{}
+}
+
+// GetSettingsParams contains all the bound params for the get settings operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters GetSettings
+type GetSettingsParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewGetSettingsParams() beforehand.
+func (o *GetSettingsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/plugin/api/restapi/operations/get_settings_responses.go b/plugin/api/restapi/operations/get_settings_responses.go
new file mode 100644
index 00000000..137d50d4
--- /dev/null
+++ b/plugin/api/restapi/operations/get_settings_responses.go
@@ -0,0 +1,59 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+
+ "github.com/massalabs/deweb-plugin/api/models"
+)
+
+// GetSettingsOKCode is the HTTP code returned for type GetSettingsOK
+const GetSettingsOKCode int = 200
+
+/*
+GetSettingsOK Current settings
+
+swagger:response getSettingsOK
+*/
+type GetSettingsOK struct {
+
+ /*
+ In: Body
+ */
+ Payload *models.Settings `json:"body,omitempty"`
+}
+
+// NewGetSettingsOK creates GetSettingsOK with default headers values
+func NewGetSettingsOK() *GetSettingsOK {
+
+ return &GetSettingsOK{}
+}
+
+// WithPayload adds the payload to the get settings o k response
+func (o *GetSettingsOK) WithPayload(payload *models.Settings) *GetSettingsOK {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the get settings o k response
+func (o *GetSettingsOK) SetPayload(payload *models.Settings) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *GetSettingsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(200)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/plugin/api/restapi/operations/get_settings_urlbuilder.go b/plugin/api/restapi/operations/get_settings_urlbuilder.go
new file mode 100644
index 00000000..cb477994
--- /dev/null
+++ b/plugin/api/restapi/operations/get_settings_urlbuilder.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+)
+
+// GetSettingsURL generates an URL for the get settings operation
+type GetSettingsURL struct {
+ _basePath string
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *GetSettingsURL) WithBasePath(bp string) *GetSettingsURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *GetSettingsURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *GetSettingsURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/api/settings"
+
+ _basePath := o._basePath
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *GetSettingsURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *GetSettingsURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *GetSettingsURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on GetSettingsURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on GetSettingsURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *GetSettingsURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/plugin/api/restapi/operations/plugin_web_app.go b/plugin/api/restapi/operations/plugin_web_app.go
new file mode 100644
index 00000000..8d120f26
--- /dev/null
+++ b/plugin/api/restapi/operations/plugin_web_app.go
@@ -0,0 +1,56 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// PluginWebAppHandlerFunc turns a function with the right signature into a plugin web app handler
+type PluginWebAppHandlerFunc func(PluginWebAppParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn PluginWebAppHandlerFunc) Handle(params PluginWebAppParams) middleware.Responder {
+ return fn(params)
+}
+
+// PluginWebAppHandler interface for that can handle valid plugin web app params
+type PluginWebAppHandler interface {
+ Handle(PluginWebAppParams) middleware.Responder
+}
+
+// NewPluginWebApp creates a new http.Handler for the plugin web app operation
+func NewPluginWebApp(ctx *middleware.Context, handler PluginWebAppHandler) *PluginWebApp {
+ return &PluginWebApp{Context: ctx, Handler: handler}
+}
+
+/*
+ PluginWebApp swagger:route GET /web/{resource} pluginWebApp
+
+Plugin Frontend router.
+*/
+type PluginWebApp struct {
+ Context *middleware.Context
+ Handler PluginWebAppHandler
+}
+
+func (o *PluginWebApp) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewPluginWebAppParams()
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/plugin/api/restapi/operations/plugin_web_app_parameters.go b/plugin/api/restapi/operations/plugin_web_app_parameters.go
new file mode 100644
index 00000000..747bae53
--- /dev/null
+++ b/plugin/api/restapi/operations/plugin_web_app_parameters.go
@@ -0,0 +1,71 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/strfmt"
+)
+
+// NewPluginWebAppParams creates a new PluginWebAppParams object
+//
+// There are no default values defined in the spec.
+func NewPluginWebAppParams() PluginWebAppParams {
+
+ return PluginWebAppParams{}
+}
+
+// PluginWebAppParams contains all the bound params for the plugin web app operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters PluginWebApp
+type PluginWebAppParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*Website resource.
+ Required: true
+ In: path
+ */
+ Resource string
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewPluginWebAppParams() beforehand.
+func (o *PluginWebAppParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ rResource, rhkResource, _ := route.Params.GetOK("resource")
+ if err := o.bindResource(rResource, rhkResource, route.Formats); err != nil {
+ res = append(res, err)
+ }
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+// bindResource binds and validates parameter Resource from path.
+func (o *PluginWebAppParams) bindResource(rawData []string, hasKey bool, formats strfmt.Registry) error {
+ var raw string
+ if len(rawData) > 0 {
+ raw = rawData[len(rawData)-1]
+ }
+
+ // Required: true
+ // Parameter is provided by construction from the route
+ o.Resource = raw
+
+ return nil
+}
diff --git a/plugin/api/restapi/operations/plugin_web_app_responses.go b/plugin/api/restapi/operations/plugin_web_app_responses.go
new file mode 100644
index 00000000..1a282b02
--- /dev/null
+++ b/plugin/api/restapi/operations/plugin_web_app_responses.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+
+ "github.com/massalabs/deweb-plugin/api/models"
+)
+
+// PluginWebAppOKCode is the HTTP code returned for type PluginWebAppOK
+const PluginWebAppOKCode int = 200
+
+/*
+PluginWebAppOK Page found
+
+swagger:response pluginWebAppOK
+*/
+type PluginWebAppOK struct {
+}
+
+// NewPluginWebAppOK creates PluginWebAppOK with default headers values
+func NewPluginWebAppOK() *PluginWebAppOK {
+
+ return &PluginWebAppOK{}
+}
+
+// WriteResponse to the client
+func (o *PluginWebAppOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
+
+ rw.WriteHeader(200)
+}
+
+// PluginWebAppNotFoundCode is the HTTP code returned for type PluginWebAppNotFound
+const PluginWebAppNotFoundCode int = 404
+
+/*
+PluginWebAppNotFound Resource not found.
+
+swagger:response pluginWebAppNotFound
+*/
+type PluginWebAppNotFound struct {
+
+ /*
+ In: Body
+ */
+ Payload *models.Error `json:"body,omitempty"`
+}
+
+// NewPluginWebAppNotFound creates PluginWebAppNotFound with default headers values
+func NewPluginWebAppNotFound() *PluginWebAppNotFound {
+
+ return &PluginWebAppNotFound{}
+}
+
+// WithPayload adds the payload to the plugin web app not found response
+func (o *PluginWebAppNotFound) WithPayload(payload *models.Error) *PluginWebAppNotFound {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the plugin web app not found response
+func (o *PluginWebAppNotFound) SetPayload(payload *models.Error) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *PluginWebAppNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(404)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/plugin/api/restapi/operations/plugin_web_app_urlbuilder.go b/plugin/api/restapi/operations/plugin_web_app_urlbuilder.go
new file mode 100644
index 00000000..a642bde5
--- /dev/null
+++ b/plugin/api/restapi/operations/plugin_web_app_urlbuilder.go
@@ -0,0 +1,96 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+ "strings"
+)
+
+// PluginWebAppURL generates an URL for the plugin web app operation
+type PluginWebAppURL struct {
+ Resource string
+
+ _basePath string
+ // avoid unkeyed usage
+ _ struct{}
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *PluginWebAppURL) WithBasePath(bp string) *PluginWebAppURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *PluginWebAppURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *PluginWebAppURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/web/{resource}"
+
+ resource := o.Resource
+ if resource != "" {
+ _path = strings.Replace(_path, "{resource}", resource, -1)
+ } else {
+ return nil, errors.New("resource is required on PluginWebAppURL")
+ }
+
+ _basePath := o._basePath
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *PluginWebAppURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *PluginWebAppURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *PluginWebAppURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on PluginWebAppURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on PluginWebAppURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *PluginWebAppURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/plugin/api/restapi/operations/redirect_to_web.go b/plugin/api/restapi/operations/redirect_to_web.go
new file mode 100644
index 00000000..13fddb73
--- /dev/null
+++ b/plugin/api/restapi/operations/redirect_to_web.go
@@ -0,0 +1,56 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// RedirectToWebHandlerFunc turns a function with the right signature into a redirect to web handler
+type RedirectToWebHandlerFunc func(RedirectToWebParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn RedirectToWebHandlerFunc) Handle(params RedirectToWebParams) middleware.Responder {
+ return fn(params)
+}
+
+// RedirectToWebHandler interface for that can handle valid redirect to web params
+type RedirectToWebHandler interface {
+ Handle(RedirectToWebParams) middleware.Responder
+}
+
+// NewRedirectToWeb creates a new http.Handler for the redirect to web operation
+func NewRedirectToWeb(ctx *middleware.Context, handler RedirectToWebHandler) *RedirectToWeb {
+ return &RedirectToWeb{Context: ctx, Handler: handler}
+}
+
+/*
+ RedirectToWeb swagger:route GET / redirectToWeb
+
+Redirect to the web interface
+*/
+type RedirectToWeb struct {
+ Context *middleware.Context
+ Handler RedirectToWebHandler
+}
+
+func (o *RedirectToWeb) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewRedirectToWebParams()
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/plugin/api/restapi/operations/redirect_to_web_parameters.go b/plugin/api/restapi/operations/redirect_to_web_parameters.go
new file mode 100644
index 00000000..cd96de7c
--- /dev/null
+++ b/plugin/api/restapi/operations/redirect_to_web_parameters.go
@@ -0,0 +1,46 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// NewRedirectToWebParams creates a new RedirectToWebParams object
+//
+// There are no default values defined in the spec.
+func NewRedirectToWebParams() RedirectToWebParams {
+
+ return RedirectToWebParams{}
+}
+
+// RedirectToWebParams contains all the bound params for the redirect to web operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters RedirectToWeb
+type RedirectToWebParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewRedirectToWebParams() beforehand.
+func (o *RedirectToWebParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/plugin/api/restapi/operations/redirect_to_web_responses.go b/plugin/api/restapi/operations/redirect_to_web_responses.go
new file mode 100644
index 00000000..c10c84a0
--- /dev/null
+++ b/plugin/api/restapi/operations/redirect_to_web_responses.go
@@ -0,0 +1,37 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+)
+
+// RedirectToWebFoundCode is the HTTP code returned for type RedirectToWebFound
+const RedirectToWebFoundCode int = 302
+
+/*
+RedirectToWebFound Redirect to /web
+
+swagger:response redirectToWebFound
+*/
+type RedirectToWebFound struct {
+}
+
+// NewRedirectToWebFound creates RedirectToWebFound with default headers values
+func NewRedirectToWebFound() *RedirectToWebFound {
+
+ return &RedirectToWebFound{}
+}
+
+// WriteResponse to the client
+func (o *RedirectToWebFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
+
+ rw.WriteHeader(302)
+}
diff --git a/plugin/api/restapi/operations/redirect_to_web_urlbuilder.go b/plugin/api/restapi/operations/redirect_to_web_urlbuilder.go
new file mode 100644
index 00000000..85e08c46
--- /dev/null
+++ b/plugin/api/restapi/operations/redirect_to_web_urlbuilder.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+)
+
+// RedirectToWebURL generates an URL for the redirect to web operation
+type RedirectToWebURL struct {
+ _basePath string
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *RedirectToWebURL) WithBasePath(bp string) *RedirectToWebURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *RedirectToWebURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *RedirectToWebURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/"
+
+ _basePath := o._basePath
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *RedirectToWebURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *RedirectToWebURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *RedirectToWebURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on RedirectToWebURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on RedirectToWebURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *RedirectToWebURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/plugin/api/restapi/operations/update_settings.go b/plugin/api/restapi/operations/update_settings.go
new file mode 100644
index 00000000..23ffc21b
--- /dev/null
+++ b/plugin/api/restapi/operations/update_settings.go
@@ -0,0 +1,56 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime/middleware"
+)
+
+// UpdateSettingsHandlerFunc turns a function with the right signature into a update settings handler
+type UpdateSettingsHandlerFunc func(UpdateSettingsParams) middleware.Responder
+
+// Handle executing the request and returning a response
+func (fn UpdateSettingsHandlerFunc) Handle(params UpdateSettingsParams) middleware.Responder {
+ return fn(params)
+}
+
+// UpdateSettingsHandler interface for that can handle valid update settings params
+type UpdateSettingsHandler interface {
+ Handle(UpdateSettingsParams) middleware.Responder
+}
+
+// NewUpdateSettings creates a new http.Handler for the update settings operation
+func NewUpdateSettings(ctx *middleware.Context, handler UpdateSettingsHandler) *UpdateSettings {
+ return &UpdateSettings{Context: ctx, Handler: handler}
+}
+
+/*
+ UpdateSettings swagger:route PUT /api/settings updateSettings
+
+Update plugin settings
+*/
+type UpdateSettings struct {
+ Context *middleware.Context
+ Handler UpdateSettingsHandler
+}
+
+func (o *UpdateSettings) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ route, rCtx, _ := o.Context.RouteInfo(r)
+ if rCtx != nil {
+ *r = *rCtx
+ }
+ var Params = NewUpdateSettingsParams()
+ if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
+ o.Context.Respond(rw, r, route.Produces, route, err)
+ return
+ }
+
+ res := o.Handler.Handle(Params) // actually handle the request
+ o.Context.Respond(rw, r, route.Produces, route, res)
+
+}
diff --git a/plugin/api/restapi/operations/update_settings_parameters.go b/plugin/api/restapi/operations/update_settings_parameters.go
new file mode 100644
index 00000000..28c21f4b
--- /dev/null
+++ b/plugin/api/restapi/operations/update_settings_parameters.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "io"
+ "net/http"
+
+ "github.com/go-openapi/errors"
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/go-openapi/validate"
+
+ "github.com/massalabs/deweb-plugin/api/models"
+)
+
+// NewUpdateSettingsParams creates a new UpdateSettingsParams object
+//
+// There are no default values defined in the spec.
+func NewUpdateSettingsParams() UpdateSettingsParams {
+
+ return UpdateSettingsParams{}
+}
+
+// UpdateSettingsParams contains all the bound params for the update settings operation
+// typically these are obtained from a http.Request
+//
+// swagger:parameters UpdateSettings
+type UpdateSettingsParams struct {
+
+ // HTTP Request Object
+ HTTPRequest *http.Request `json:"-"`
+
+ /*New settings
+ Required: true
+ In: body
+ */
+ Settings *models.Settings
+}
+
+// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
+// for simple values it will use straight method calls.
+//
+// To ensure default values, the struct must have been initialized with NewUpdateSettingsParams() beforehand.
+func (o *UpdateSettingsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
+ var res []error
+
+ o.HTTPRequest = r
+
+ if runtime.HasBody(r) {
+ defer r.Body.Close()
+ var body models.Settings
+ if err := route.Consumer.Consume(r.Body, &body); err != nil {
+ if err == io.EOF {
+ res = append(res, errors.Required("settings", "body", ""))
+ } else {
+ res = append(res, errors.NewParseError("settings", "body", "", err))
+ }
+ } else {
+ // validate body object
+ if err := body.Validate(route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ ctx := validate.WithOperationRequest(r.Context())
+ if err := body.ContextValidate(ctx, route.Formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) == 0 {
+ o.Settings = &body
+ }
+ }
+ } else {
+ res = append(res, errors.Required("settings", "body", ""))
+ }
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
diff --git a/plugin/api/restapi/operations/update_settings_responses.go b/plugin/api/restapi/operations/update_settings_responses.go
new file mode 100644
index 00000000..7a6aa6eb
--- /dev/null
+++ b/plugin/api/restapi/operations/update_settings_responses.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+ "net/http"
+
+ "github.com/go-openapi/runtime"
+
+ "github.com/massalabs/deweb-plugin/api/models"
+)
+
+// UpdateSettingsOKCode is the HTTP code returned for type UpdateSettingsOK
+const UpdateSettingsOKCode int = 200
+
+/*
+UpdateSettingsOK Settings updated successfully
+
+swagger:response updateSettingsOK
+*/
+type UpdateSettingsOK struct {
+}
+
+// NewUpdateSettingsOK creates UpdateSettingsOK with default headers values
+func NewUpdateSettingsOK() *UpdateSettingsOK {
+
+ return &UpdateSettingsOK{}
+}
+
+// WriteResponse to the client
+func (o *UpdateSettingsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
+
+ rw.WriteHeader(200)
+}
+
+// UpdateSettingsBadRequestCode is the HTTP code returned for type UpdateSettingsBadRequest
+const UpdateSettingsBadRequestCode int = 400
+
+/*
+UpdateSettingsBadRequest Error updating settings
+
+swagger:response updateSettingsBadRequest
+*/
+type UpdateSettingsBadRequest struct {
+
+ /*
+ In: Body
+ */
+ Payload *models.Error `json:"body,omitempty"`
+}
+
+// NewUpdateSettingsBadRequest creates UpdateSettingsBadRequest with default headers values
+func NewUpdateSettingsBadRequest() *UpdateSettingsBadRequest {
+
+ return &UpdateSettingsBadRequest{}
+}
+
+// WithPayload adds the payload to the update settings bad request response
+func (o *UpdateSettingsBadRequest) WithPayload(payload *models.Error) *UpdateSettingsBadRequest {
+ o.Payload = payload
+ return o
+}
+
+// SetPayload sets the payload to the update settings bad request response
+func (o *UpdateSettingsBadRequest) SetPayload(payload *models.Error) {
+ o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *UpdateSettingsBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+ rw.WriteHeader(400)
+ if o.Payload != nil {
+ payload := o.Payload
+ if err := producer.Produce(rw, payload); err != nil {
+ panic(err) // let the recovery middleware deal with this
+ }
+ }
+}
diff --git a/plugin/api/restapi/operations/update_settings_urlbuilder.go b/plugin/api/restapi/operations/update_settings_urlbuilder.go
new file mode 100644
index 00000000..7c8f78ef
--- /dev/null
+++ b/plugin/api/restapi/operations/update_settings_urlbuilder.go
@@ -0,0 +1,84 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package operations
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the generate command
+
+import (
+ "errors"
+ "net/url"
+ golangswaggerpaths "path"
+)
+
+// UpdateSettingsURL generates an URL for the update settings operation
+type UpdateSettingsURL struct {
+ _basePath string
+}
+
+// WithBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *UpdateSettingsURL) WithBasePath(bp string) *UpdateSettingsURL {
+ o.SetBasePath(bp)
+ return o
+}
+
+// SetBasePath sets the base path for this url builder, only required when it's different from the
+// base path specified in the swagger spec.
+// When the value of the base path is an empty string
+func (o *UpdateSettingsURL) SetBasePath(bp string) {
+ o._basePath = bp
+}
+
+// Build a url path and query string
+func (o *UpdateSettingsURL) Build() (*url.URL, error) {
+ var _result url.URL
+
+ var _path = "/api/settings"
+
+ _basePath := o._basePath
+ _result.Path = golangswaggerpaths.Join(_basePath, _path)
+
+ return &_result, nil
+}
+
+// Must is a helper function to panic when the url builder returns an error
+func (o *UpdateSettingsURL) Must(u *url.URL, err error) *url.URL {
+ if err != nil {
+ panic(err)
+ }
+ if u == nil {
+ panic("url can't be nil")
+ }
+ return u
+}
+
+// String returns the string representation of the path with query string
+func (o *UpdateSettingsURL) String() string {
+ return o.Must(o.Build()).String()
+}
+
+// BuildFull builds a full url with scheme, host, path and query string
+func (o *UpdateSettingsURL) BuildFull(scheme, host string) (*url.URL, error) {
+ if scheme == "" {
+ return nil, errors.New("scheme is required for a full url on UpdateSettingsURL")
+ }
+ if host == "" {
+ return nil, errors.New("host is required for a full url on UpdateSettingsURL")
+ }
+
+ base, err := o.Build()
+ if err != nil {
+ return nil, err
+ }
+
+ base.Scheme = scheme
+ base.Host = host
+ return base, nil
+}
+
+// StringFull returns the string representation of a complete url
+func (o *UpdateSettingsURL) StringFull(scheme, host string) string {
+ return o.Must(o.BuildFull(scheme, host)).String()
+}
diff --git a/plugin/api/restapi/server.go b/plugin/api/restapi/server.go
new file mode 100644
index 00000000..e25c6891
--- /dev/null
+++ b/plugin/api/restapi/server.go
@@ -0,0 +1,507 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package restapi
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "os/signal"
+ "strconv"
+ "sync"
+ "sync/atomic"
+ "syscall"
+ "time"
+
+ "github.com/go-openapi/runtime/flagext"
+ "github.com/go-openapi/swag"
+ flags "github.com/jessevdk/go-flags"
+ "golang.org/x/net/netutil"
+
+ "github.com/massalabs/deweb-plugin/api/restapi/operations"
+)
+
+const (
+ schemeHTTP = "http"
+ schemeHTTPS = "https"
+ schemeUnix = "unix"
+)
+
+var defaultSchemes []string
+
+func init() {
+ defaultSchemes = []string{
+ schemeHTTP,
+ }
+}
+
+// NewServer creates a new api deweb plugin server but does not configure it
+func NewServer(api *operations.DewebPluginAPI) *Server {
+ s := new(Server)
+
+ s.shutdown = make(chan struct{})
+ s.api = api
+ s.interrupt = make(chan os.Signal, 1)
+ return s
+}
+
+// ConfigureAPI configures the API and handlers.
+func (s *Server) ConfigureAPI() {
+ if s.api != nil {
+ s.handler = configureAPI(s.api)
+ }
+}
+
+// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
+func (s *Server) ConfigureFlags() {
+ if s.api != nil {
+ configureFlags(s.api)
+ }
+}
+
+// Server for the deweb plugin API
+type Server struct {
+ EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`
+ CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`
+ GracefulTimeout time.Duration `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`
+ MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`
+
+ SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/deweb-plugin.sock"`
+ domainSocketL net.Listener
+
+ Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
+ Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`
+ ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"`
+ KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
+ ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
+ WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"30s"`
+ httpServerL net.Listener
+
+ TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
+ TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
+ TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
+ TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"`
+ TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
+ TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
+ TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
+ TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`
+ TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`
+ httpsServerL net.Listener
+
+ api *operations.DewebPluginAPI
+ handler http.Handler
+ hasListeners bool
+ shutdown chan struct{}
+ shuttingDown int32
+ interrupted bool
+ interrupt chan os.Signal
+}
+
+// Logf logs message either via defined user logger or via system one if no user logger is defined.
+func (s *Server) Logf(f string, args ...interface{}) {
+ if s.api != nil && s.api.Logger != nil {
+ s.api.Logger(f, args...)
+ } else {
+ log.Printf(f, args...)
+ }
+}
+
+// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
+// Exits with non-zero status after printing
+func (s *Server) Fatalf(f string, args ...interface{}) {
+ if s.api != nil && s.api.Logger != nil {
+ s.api.Logger(f, args...)
+ os.Exit(1)
+ } else {
+ log.Fatalf(f, args...)
+ }
+}
+
+// SetAPI configures the server with the specified API. Needs to be called before Serve
+func (s *Server) SetAPI(api *operations.DewebPluginAPI) {
+ if api == nil {
+ s.api = nil
+ s.handler = nil
+ return
+ }
+
+ s.api = api
+ s.handler = configureAPI(api)
+}
+
+func (s *Server) hasScheme(scheme string) bool {
+ schemes := s.EnabledListeners
+ if len(schemes) == 0 {
+ schemes = defaultSchemes
+ }
+
+ for _, v := range schemes {
+ if v == scheme {
+ return true
+ }
+ }
+ return false
+}
+
+// Serve the api
+func (s *Server) Serve() (err error) {
+ if !s.hasListeners {
+ if err = s.Listen(); err != nil {
+ return err
+ }
+ }
+
+ // set default handler, if none is set
+ if s.handler == nil {
+ if s.api == nil {
+ return errors.New("can't create the default handler, as no api is set")
+ }
+
+ s.SetHandler(s.api.Serve(nil))
+ }
+
+ wg := new(sync.WaitGroup)
+ once := new(sync.Once)
+ signalNotify(s.interrupt)
+ go handleInterrupt(once, s)
+
+ servers := []*http.Server{}
+
+ if s.hasScheme(schemeUnix) {
+ domainSocket := new(http.Server)
+ domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
+ domainSocket.Handler = s.handler
+ if int64(s.CleanupTimeout) > 0 {
+ domainSocket.IdleTimeout = s.CleanupTimeout
+ }
+
+ configureServer(domainSocket, "unix", string(s.SocketPath))
+
+ servers = append(servers, domainSocket)
+ wg.Add(1)
+ s.Logf("Serving deweb plugin at unix://%s", s.SocketPath)
+ go func(l net.Listener) {
+ defer wg.Done()
+ if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
+ s.Fatalf("%v", err)
+ }
+ s.Logf("Stopped serving deweb plugin at unix://%s", s.SocketPath)
+ }(s.domainSocketL)
+ }
+
+ if s.hasScheme(schemeHTTP) {
+ httpServer := new(http.Server)
+ httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
+ httpServer.ReadTimeout = s.ReadTimeout
+ httpServer.WriteTimeout = s.WriteTimeout
+ httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
+ if s.ListenLimit > 0 {
+ s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
+ }
+
+ if int64(s.CleanupTimeout) > 0 {
+ httpServer.IdleTimeout = s.CleanupTimeout
+ }
+
+ httpServer.Handler = s.handler
+
+ configureServer(httpServer, "http", s.httpServerL.Addr().String())
+
+ servers = append(servers, httpServer)
+ wg.Add(1)
+ s.Logf("Serving deweb plugin at http://%s", s.httpServerL.Addr())
+ go func(l net.Listener) {
+ defer wg.Done()
+ if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
+ s.Fatalf("%v", err)
+ }
+ s.Logf("Stopped serving deweb plugin at http://%s", l.Addr())
+ }(s.httpServerL)
+ }
+
+ if s.hasScheme(schemeHTTPS) {
+ httpsServer := new(http.Server)
+ httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
+ httpsServer.ReadTimeout = s.TLSReadTimeout
+ httpsServer.WriteTimeout = s.TLSWriteTimeout
+ httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
+ if s.TLSListenLimit > 0 {
+ s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
+ }
+ if int64(s.CleanupTimeout) > 0 {
+ httpsServer.IdleTimeout = s.CleanupTimeout
+ }
+ httpsServer.Handler = s.handler
+
+ // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
+ httpsServer.TLSConfig = &tls.Config{
+ // Causes servers to use Go's default ciphersuite preferences,
+ // which are tuned to avoid attacks. Does nothing on clients.
+ PreferServerCipherSuites: true,
+ // Only use curves which have assembly implementations
+ // https://github.com/golang/go/tree/master/src/crypto/elliptic
+ CurvePreferences: []tls.CurveID{tls.CurveP256},
+ // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
+ NextProtos: []string{"h2", "http/1.1"},
+ // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
+ MinVersion: tls.VersionTLS12,
+ // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
+ CipherSuites: []uint16{
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+ tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+ },
+ }
+
+ // build standard config from server options
+ if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
+ httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
+ httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey))
+ if err != nil {
+ return err
+ }
+ }
+
+ if s.TLSCACertificate != "" {
+ // include specified CA certificate
+ caCert, caCertErr := os.ReadFile(string(s.TLSCACertificate))
+ if caCertErr != nil {
+ return caCertErr
+ }
+ caCertPool := x509.NewCertPool()
+ ok := caCertPool.AppendCertsFromPEM(caCert)
+ if !ok {
+ return fmt.Errorf("cannot parse CA certificate")
+ }
+ httpsServer.TLSConfig.ClientCAs = caCertPool
+ httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
+ }
+
+ // call custom TLS configurator
+ configureTLS(httpsServer.TLSConfig)
+
+ if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
+ // after standard and custom config are passed, this ends up with no certificate
+ if s.TLSCertificate == "" {
+ if s.TLSCertificateKey == "" {
+ s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
+ }
+ s.Fatalf("the required flag `--tls-certificate` was not specified")
+ }
+ if s.TLSCertificateKey == "" {
+ s.Fatalf("the required flag `--tls-key` was not specified")
+ }
+ // this happens with a wrong custom TLS configurator
+ s.Fatalf("no certificate was configured for TLS")
+ }
+
+ configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
+
+ servers = append(servers, httpsServer)
+ wg.Add(1)
+ s.Logf("Serving deweb plugin at https://%s", s.httpsServerL.Addr())
+ go func(l net.Listener) {
+ defer wg.Done()
+ if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
+ s.Fatalf("%v", err)
+ }
+ s.Logf("Stopped serving deweb plugin at https://%s", l.Addr())
+ }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
+ }
+
+ wg.Add(1)
+ go s.handleShutdown(wg, &servers)
+
+ wg.Wait()
+ return nil
+}
+
+// Listen creates the listeners for the server
+func (s *Server) Listen() error {
+ if s.hasListeners { // already done this
+ return nil
+ }
+
+ if s.hasScheme(schemeHTTPS) {
+ // Use http host if https host wasn't defined
+ if s.TLSHost == "" {
+ s.TLSHost = s.Host
+ }
+ // Use http listen limit if https listen limit wasn't defined
+ if s.TLSListenLimit == 0 {
+ s.TLSListenLimit = s.ListenLimit
+ }
+ // Use http tcp keep alive if https tcp keep alive wasn't defined
+ if int64(s.TLSKeepAlive) == 0 {
+ s.TLSKeepAlive = s.KeepAlive
+ }
+ // Use http read timeout if https read timeout wasn't defined
+ if int64(s.TLSReadTimeout) == 0 {
+ s.TLSReadTimeout = s.ReadTimeout
+ }
+ // Use http write timeout if https write timeout wasn't defined
+ if int64(s.TLSWriteTimeout) == 0 {
+ s.TLSWriteTimeout = s.WriteTimeout
+ }
+ }
+
+ if s.hasScheme(schemeUnix) {
+ domSockListener, err := net.Listen("unix", string(s.SocketPath))
+ if err != nil {
+ return err
+ }
+ s.domainSocketL = domSockListener
+ }
+
+ if s.hasScheme(schemeHTTP) {
+ listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
+ if err != nil {
+ return err
+ }
+
+ h, p, err := swag.SplitHostPort(listener.Addr().String())
+ if err != nil {
+ return err
+ }
+ s.Host = h
+ s.Port = p
+ s.httpServerL = listener
+ }
+
+ if s.hasScheme(schemeHTTPS) {
+ tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
+ if err != nil {
+ return err
+ }
+
+ sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
+ if err != nil {
+ return err
+ }
+ s.TLSHost = sh
+ s.TLSPort = sp
+ s.httpsServerL = tlsListener
+ }
+
+ s.hasListeners = true
+ return nil
+}
+
+// Shutdown server and clean up resources
+func (s *Server) Shutdown() error {
+ if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
+ close(s.shutdown)
+ }
+ return nil
+}
+
+func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
+ // wg.Done must occur last, after s.api.ServerShutdown()
+ // (to preserve old behaviour)
+ defer wg.Done()
+
+ <-s.shutdown
+
+ servers := *serversPtr
+
+ ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
+ defer cancel()
+
+ // first execute the pre-shutdown hook
+ s.api.PreServerShutdown()
+
+ shutdownChan := make(chan bool)
+ for i := range servers {
+ server := servers[i]
+ go func() {
+ var success bool
+ defer func() {
+ shutdownChan <- success
+ }()
+ if err := server.Shutdown(ctx); err != nil {
+ // Error from closing listeners, or context timeout:
+ s.Logf("HTTP server Shutdown: %v", err)
+ } else {
+ success = true
+ }
+ }()
+ }
+
+ // Wait until all listeners have successfully shut down before calling ServerShutdown
+ success := true
+ for range servers {
+ success = success && <-shutdownChan
+ }
+ if success {
+ s.api.ServerShutdown()
+ }
+}
+
+// GetHandler returns a handler useful for testing
+func (s *Server) GetHandler() http.Handler {
+ return s.handler
+}
+
+// SetHandler allows for setting a http handler on this server
+func (s *Server) SetHandler(handler http.Handler) {
+ s.handler = handler
+}
+
+// UnixListener returns the domain socket listener
+func (s *Server) UnixListener() (net.Listener, error) {
+ if !s.hasListeners {
+ if err := s.Listen(); err != nil {
+ return nil, err
+ }
+ }
+ return s.domainSocketL, nil
+}
+
+// HTTPListener returns the http listener
+func (s *Server) HTTPListener() (net.Listener, error) {
+ if !s.hasListeners {
+ if err := s.Listen(); err != nil {
+ return nil, err
+ }
+ }
+ return s.httpServerL, nil
+}
+
+// TLSListener returns the https listener
+func (s *Server) TLSListener() (net.Listener, error) {
+ if !s.hasListeners {
+ if err := s.Listen(); err != nil {
+ return nil, err
+ }
+ }
+ return s.httpsServerL, nil
+}
+
+func handleInterrupt(once *sync.Once, s *Server) {
+ once.Do(func() {
+ for range s.interrupt {
+ if s.interrupted {
+ s.Logf("Server already shutting down")
+ continue
+ }
+ s.interrupted = true
+ s.Logf("Shutting down... ")
+ if err := s.Shutdown(); err != nil {
+ s.Logf("HTTP server Shutdown: %v", err)
+ }
+ }
+ })
+}
+
+func signalNotify(interrupt chan<- os.Signal) {
+ signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
+}
diff --git a/plugin/favicon.png b/plugin/favicon.png
new file mode 100644
index 00000000..77a993ea
Binary files /dev/null and b/plugin/favicon.png differ
diff --git a/plugin/gen.go b/plugin/gen.go
new file mode 100644
index 00000000..1a062293
--- /dev/null
+++ b/plugin/gen.go
@@ -0,0 +1,4 @@
+package main
+
+// Swagger API
+//go:generate swagger generate server --quiet --target api --name DewebPlugin --spec api/pluginAPI-V0.yml --exclude-main
diff --git a/plugin/go.mod b/plugin/go.mod
new file mode 100644
index 00000000..51bb9e2f
--- /dev/null
+++ b/plugin/go.mod
@@ -0,0 +1,59 @@
+module github.com/massalabs/deweb-plugin
+
+go 1.23.0
+
+toolchain go1.24.0
+
+replace github.com/massalabs/deweb-server => ../server
+
+require (
+ github.com/go-openapi/errors v0.22.1
+ github.com/go-openapi/loads v0.22.0
+ github.com/go-openapi/runtime v0.28.0
+ github.com/go-openapi/spec v0.21.0
+ github.com/go-openapi/strfmt v0.23.0
+ github.com/go-openapi/swag v0.23.1
+ github.com/go-openapi/validate v0.24.0
+ github.com/jessevdk/go-flags v1.6.1
+ github.com/massalabs/deweb-server v0.0.0-00010101000000-000000000000
+ github.com/massalabs/station v0.6.9
+ github.com/massalabs/station-massa-hello-world v0.0.11-0.20240503070604-6b14a27fcdff
+ github.com/massalabs/station-massa-wallet v0.4.5
+ github.com/shirou/gopsutil/v4 v4.25.3
+ golang.org/x/net v0.39.0
+ gopkg.in/yaml.v2 v2.4.0
+)
+
+require (
+ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
+ github.com/btcsuite/btcutil v1.0.2 // indirect
+ github.com/docker/go-units v0.5.0 // indirect
+ github.com/ebitengine/purego v0.8.2 // indirect
+ github.com/go-ole/go-ole v1.2.6 // indirect
+ github.com/go-openapi/analysis v0.23.0 // indirect
+ github.com/go-openapi/jsonpointer v0.21.0 // indirect
+ github.com/go-openapi/jsonreference v0.21.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/josharian/intern v1.0.0 // indirect
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
+ github.com/mailru/easyjson v0.9.0 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/oklog/ulid v1.3.1 // indirect
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
+ github.com/rogpeppe/go-internal v1.13.1 // indirect
+ github.com/shopspring/decimal v1.3.1 // indirect
+ github.com/stretchr/objx v0.5.2 // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
+ github.com/ybbus/jsonrpc/v3 v3.1.4 // indirect
+ github.com/yusufpapurcu/wmi v1.2.4 // indirect
+ go.mongodb.org/mongo-driver v1.14.0 // indirect
+ go.uber.org/atomic v1.10.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.24.0 // indirect
+ golang.org/x/sync v0.10.0 // indirect
+ golang.org/x/sys v0.32.0 // indirect
+ gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ howett.net/plist v1.0.0 // indirect
+)
diff --git a/plugin/go.sum b/plugin/go.sum
new file mode 100644
index 00000000..a80158b7
--- /dev/null
+++ b/plugin/go.sum
@@ -0,0 +1,151 @@
+github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
+github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
+github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
+github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
+github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
+github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
+github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
+github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
+github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
+github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/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/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
+github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
+github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
+github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU=
+github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0=
+github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
+github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
+github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
+github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
+github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
+github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
+github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
+github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
+github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
+github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
+github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
+github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
+github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
+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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
+github.com/massalabs/station v0.6.9 h1:eampc2dndCq8BHUydRlcMSy1i1dBhxqI5tKWKGMMj+4=
+github.com/massalabs/station v0.6.9/go.mod h1:fvIMuIS8v1/tu5e4FPvvqEJEcLsZN+wUD5OMSHD/rO8=
+github.com/massalabs/station-massa-hello-world v0.0.11-0.20240503070604-6b14a27fcdff h1:SZoRmXdbolVjhbsxO/3KWX8429Q1VnaQdQy0h718CqE=
+github.com/massalabs/station-massa-hello-world v0.0.11-0.20240503070604-6b14a27fcdff/go.mod h1:QbRHQvJFrm4mO+vPzr4Uiwa5REL/CgBlV4PDFdhewa0=
+github.com/massalabs/station-massa-wallet v0.4.5 h1:0rTHxGPlJ5cKjgB/yQclOBHbWiZO5rOwO0lT7ZjFuVQ=
+github.com/massalabs/station-massa-wallet v0.4.5/go.mod h1:Eu6Zlijs0uAuGM5CxEUOxFrcIlWtuZVAbiWPCUni9XY=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
+github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
+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/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/ybbus/jsonrpc/v3 v3.1.4 h1:pPmgfWXnqR2GdIlealyCzmV6LV3nxm3w9gwA1B3cP3Y=
+github.com/ybbus/jsonrpc/v3 v3.1.4/go.mod h1:4HQTl0UzErqWGa6bSXhp8rIjifMAMa55E4D5wdhe768=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
+go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
+go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
+go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
+go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
+golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
+golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.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=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
+gopkg.in/yaml.v2 v2.2.1/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
+howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
diff --git a/plugin/home/.env b/plugin/home/.env
new file mode 100644
index 00000000..369c6d48
--- /dev/null
+++ b/plugin/home/.env
@@ -0,0 +1,3 @@
+VITE_BASE_APP=""
+VITE_BASE_API=""
+VITE_ENV="dev"
\ No newline at end of file
diff --git a/plugin/home/.env.massastation b/plugin/home/.env.massastation
new file mode 100644
index 00000000..362e8b90
--- /dev/null
+++ b/plugin/home/.env.massastation
@@ -0,0 +1,3 @@
+VITE_BASE_APP="/plugin/massa-labs/local-deweb-provider/web"
+VITE_BASE_API="/plugin/massa-labs/local-deweb-provider/api"
+VITE_ENV="massastation"
\ No newline at end of file
diff --git a/plugin/home/.gitignore b/plugin/home/.gitignore
new file mode 100644
index 00000000..a547bf36
--- /dev/null
+++ b/plugin/home/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/plugin/home/README.md b/plugin/home/README.md
new file mode 100644
index 00000000..a3112158
--- /dev/null
+++ b/plugin/home/README.md
@@ -0,0 +1,62 @@
+# DeWeb Plugin Homepage
+
+This is the homepage for the DeWeb plugin, providing a central navigation hub and search interface for the decentralized web ecosystem on Massa.
+
+## Features
+
+- Search for DeWeb domains
+- Quick access to key DeWeb services:
+ - Explore DeWeb: Browse available decentralized websites
+ - Massa Name System (MNS): Manage decentralized domain names
+ - DeWeb Uploader: Upload and manage websites
+- Network status indicator showing connection info
+
+## Development
+
+### Prerequisites
+
+- Node.js (v18+)
+- npm or yarn
+
+### Setup
+
+```bash
+# Install dependencies
+npm install
+
+# Run development server
+npm run dev
+```
+
+The development server will start at http://localhost:5173 (or another port if 5173 is in use).
+
+### Project Structure
+
+- `src/` - Source code
+ - `App.tsx` - Main application component
+- `public/` - Static assets
+
+## Building for Production
+
+```bash
+# Build the project
+npm run build
+```
+
+The `build` command builds and output the project in the plugin `int/api/html` directory to be served by the plugin API.
+
+## Dependencies
+
+This project uses:
+- React 19
+- TypeScript
+- Vite
+- TailwindCSS for styling
+- React Icons
+- DeWeb Pages (local dependency)
+
+## Integration
+
+This homepage is designed to work with the DeWeb plugin for Massa. It communicates with the plugin's API endpoints to retrieve network information and port configurations.
+
+The application dynamically generates URLs based on the current environment (development or production) to ensure proper navigation between different DeWeb services.
diff --git a/plugin/home/eslint.config.js b/plugin/home/eslint.config.js
new file mode 100644
index 00000000..092408a9
--- /dev/null
+++ b/plugin/home/eslint.config.js
@@ -0,0 +1,28 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+
+export default tseslint.config(
+ { ignores: ['dist'] },
+ {
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...reactHooks.configs.recommended.rules,
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+ },
+)
diff --git a/plugin/home/gen.go b/plugin/home/gen.go
new file mode 100644
index 00000000..67a7dc93
--- /dev/null
+++ b/plugin/home/gen.go
@@ -0,0 +1,4 @@
+package main
+
+//go:generate npm ci
+//go:generate npm run build
diff --git a/plugin/home/index.html b/plugin/home/index.html
new file mode 100644
index 00000000..679492c5
--- /dev/null
+++ b/plugin/home/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ Local DeWeb Provider
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugin/home/package-lock.json b/plugin/home/package-lock.json
new file mode 100644
index 00000000..f7a3397e
--- /dev/null
+++ b/plugin/home/package-lock.json
@@ -0,0 +1,4817 @@
+{
+ "name": "home",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "home",
+ "version": "0.0.0",
+ "dependencies": {
+ "deweb-pages": "file:../../server/pages/",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-icons": "^5.5.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.21.0",
+ "@types/node": "^22.13.10",
+ "@types/react": "^19.0.10",
+ "@types/react-dom": "^19.0.4",
+ "@vitejs/plugin-react": "^4.3.4",
+ "archiver": "^7.0.1",
+ "autoprefixer": "^10.4.21",
+ "eslint": "^9.21.0",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^15.15.0",
+ "postcss": "^8.5.3",
+ "tailwindcss": "^3.4.17",
+ "typescript": "~5.7.2",
+ "typescript-eslint": "^8.24.1",
+ "vite": "^6.2.0"
+ }
+ },
+ "../../server/pages": {
+ "name": "pages",
+ "version": "0.0.0",
+ "dependencies": {
+ "@massalabs/massa-web3": "^5.1.2-dev",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-icons": "^5.3.0",
+ "tw-colors": "^3.3.2"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.8.0",
+ "@types/node": "^22.8.4",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@vitejs/plugin-react": "^4.3.1",
+ "archiver": "^7.0.1",
+ "autoprefixer": "^10.4.20",
+ "eslint": "^9.8.0",
+ "eslint-plugin-react-hooks": "^5.1.0-rc.0",
+ "eslint-plugin-react-refresh": "^0.4.9",
+ "globals": "^15.9.0",
+ "postcss": "^8.4.41",
+ "tailwindcss": "^3.4.10",
+ "typescript": "^5.5.3",
+ "typescript-eslint": "^8.0.0",
+ "vite": "^5.4.0",
+ "vite-plugin-image-optimizer": "^1.1.8",
+ "vite-plugin-singlefile": "^2.0.2",
+ "vite-plugin-svgr": "^4.2.0"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.26.8",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz",
+ "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.26.10",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz",
+ "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.26.10",
+ "@babel/helper-compilation-targets": "^7.26.5",
+ "@babel/helper-module-transforms": "^7.26.0",
+ "@babel/helpers": "^7.26.10",
+ "@babel/parser": "^7.26.10",
+ "@babel/template": "^7.26.9",
+ "@babel/traverse": "^7.26.10",
+ "@babel/types": "^7.26.10",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz",
+ "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz",
+ "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.26.8",
+ "@babel/helper-validator-option": "^7.25.9",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
+ "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.25.9",
+ "@babel/types": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.26.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
+ "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "@babel/traverse": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.26.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz",
+ "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+ "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
+ "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
+ "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
+ "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz",
+ "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz",
+ "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
+ "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/parser": "^7.27.0",
+ "@babel/types": "^7.27.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz",
+ "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.26.2",
+ "@babel/generator": "^7.27.0",
+ "@babel/parser": "^7.27.0",
+ "@babel/template": "^7.27.0",
+ "@babel/types": "^7.27.0",
+ "debug": "^4.3.1",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
+ "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.25.9",
+ "@babel/helper-validator-identifier": "^7.25.9"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
+ "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
+ "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
+ "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
+ "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
+ "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
+ "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
+ "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
+ "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
+ "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
+ "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
+ "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
+ "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
+ "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
+ "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
+ "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
+ "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
+ "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
+ "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
+ "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
+ "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
+ "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
+ "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
+ "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
+ "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
+ "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz",
+ "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
+ "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz",
+ "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
+ "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz",
+ "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
+ "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.13.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
+ "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz",
+ "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz",
+ "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz",
+ "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz",
+ "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz",
+ "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz",
+ "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz",
+ "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz",
+ "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz",
+ "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz",
+ "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz",
+ "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz",
+ "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz",
+ "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz",
+ "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz",
+ "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz",
+ "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz",
+ "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz",
+ "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz",
+ "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz",
+ "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.8",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz",
+ "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.7",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz",
+ "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.14.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz",
+ "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz",
+ "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.1",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz",
+ "integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.0.tgz",
+ "integrity": "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.29.0",
+ "@typescript-eslint/type-utils": "8.29.0",
+ "@typescript-eslint/utils": "8.29.0",
+ "@typescript-eslint/visitor-keys": "8.29.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.0.tgz",
+ "integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.29.0",
+ "@typescript-eslint/types": "8.29.0",
+ "@typescript-eslint/typescript-estree": "8.29.0",
+ "@typescript-eslint/visitor-keys": "8.29.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.0.tgz",
+ "integrity": "sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.29.0",
+ "@typescript-eslint/visitor-keys": "8.29.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.0.tgz",
+ "integrity": "sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.29.0",
+ "@typescript-eslint/utils": "8.29.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.0.tgz",
+ "integrity": "sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.0.tgz",
+ "integrity": "sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.29.0",
+ "@typescript-eslint/visitor-keys": "8.29.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.0.tgz",
+ "integrity": "sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.29.0",
+ "@typescript-eslint/types": "8.29.0",
+ "@typescript-eslint/typescript-estree": "8.29.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.0.tgz",
+ "integrity": "sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.29.0",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz",
+ "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.26.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.25.9",
+ "@babel/plugin-transform-react-jsx-source": "^7.25.9",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.14.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.14.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/archiver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
+ "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.2",
+ "async": "^3.2.4",
+ "buffer-crc32": "^1.0.0",
+ "readable-stream": "^4.0.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^3.0.0",
+ "zip-stream": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
+ "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^10.0.0",
+ "graceful-fs": "^4.2.0",
+ "is-stream": "^2.0.1",
+ "lazystream": "^1.0.0",
+ "lodash": "^4.17.15",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.21",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
+ "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.24.4",
+ "caniuse-lite": "^1.0.30001702",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/b4a": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
+ "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
+ "dev": true,
+ "license": "Apache-2.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,
+ "license": "MIT"
+ },
+ "node_modules/bare-events": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz",
+ "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": 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==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.24.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+ "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
+ "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001709",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001709.tgz",
+ "integrity": "sha512-NgL3vUTnDrPCZ3zTahp4fsugQ4dc7EKTSzwQDPEel6DMoMnfH2jhry9n2Zm8onbSR+f/QtKHFOA+iAQu4kbtWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "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,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/compress-commons": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
+ "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "crc32-stream": "^6.0.0",
+ "is-stream": "^2.0.1",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz",
+ "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deweb-pages": {
+ "resolved": "../../server/pages",
+ "link": true
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.130",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.130.tgz",
+ "integrity": "sha512-Ou2u7L9j2XLZbhqzyX0jWDj6gA8D3jIfVzt4rikLf3cGBa0VdReuFimBKS9tQJA4+XpeCxj1NoWlfBXzbMa9IA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
+ "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.2",
+ "@esbuild/android-arm": "0.25.2",
+ "@esbuild/android-arm64": "0.25.2",
+ "@esbuild/android-x64": "0.25.2",
+ "@esbuild/darwin-arm64": "0.25.2",
+ "@esbuild/darwin-x64": "0.25.2",
+ "@esbuild/freebsd-arm64": "0.25.2",
+ "@esbuild/freebsd-x64": "0.25.2",
+ "@esbuild/linux-arm": "0.25.2",
+ "@esbuild/linux-arm64": "0.25.2",
+ "@esbuild/linux-ia32": "0.25.2",
+ "@esbuild/linux-loong64": "0.25.2",
+ "@esbuild/linux-mips64el": "0.25.2",
+ "@esbuild/linux-ppc64": "0.25.2",
+ "@esbuild/linux-riscv64": "0.25.2",
+ "@esbuild/linux-s390x": "0.25.2",
+ "@esbuild/linux-x64": "0.25.2",
+ "@esbuild/netbsd-arm64": "0.25.2",
+ "@esbuild/netbsd-x64": "0.25.2",
+ "@esbuild/openbsd-arm64": "0.25.2",
+ "@esbuild/openbsd-x64": "0.25.2",
+ "@esbuild/sunos-x64": "0.25.2",
+ "@esbuild/win32-arm64": "0.25.2",
+ "@esbuild/win32-ia32": "0.25.2",
+ "@esbuild/win32-x64": "0.25.2"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.23.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz",
+ "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.2",
+ "@eslint/config-helpers": "^0.2.0",
+ "@eslint/core": "^0.12.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.23.0",
+ "@eslint/plugin-kit": "^0.2.7",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.3.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
+ "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz",
+ "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
+ "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "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,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globals": {
+ "version": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/lazystream/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,
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.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,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-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,
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.8",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+ "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+ "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
+ "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
+ "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.0"
+ }
+ },
+ "node_modules/react-icons": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
+ "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
+ "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/minimatch": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.39.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz",
+ "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.7"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.39.0",
+ "@rollup/rollup-android-arm64": "4.39.0",
+ "@rollup/rollup-darwin-arm64": "4.39.0",
+ "@rollup/rollup-darwin-x64": "4.39.0",
+ "@rollup/rollup-freebsd-arm64": "4.39.0",
+ "@rollup/rollup-freebsd-x64": "4.39.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.39.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.39.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.39.0",
+ "@rollup/rollup-linux-arm64-musl": "4.39.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.39.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.39.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.39.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.39.0",
+ "@rollup/rollup-linux-x64-gnu": "4.39.0",
+ "@rollup/rollup-linux-x64-musl": "4.39.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.39.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.39.0",
+ "@rollup/rollup-win32-x64-msvc": "4.39.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT"
+ },
+ "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,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/streamx": {
+ "version": "2.22.0",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz",
+ "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ },
+ "optionalDependencies": {
+ "bare-events": "^2.2.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.0",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
+ "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "glob": "^10.3.10",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-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,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.17",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
+ "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.6.0",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.2",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.6",
+ "lilconfig": "^3.1.3",
+ "micromatch": "^4.0.8",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.47",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.2",
+ "postcss-nested": "^6.2.0",
+ "postcss-selector-parser": "^6.1.2",
+ "resolve": "^1.22.8",
+ "sucrase": "^3.35.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/text-decoder": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
+ "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+ "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.29.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.0.tgz",
+ "integrity": "sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.29.0",
+ "@typescript-eslint/parser": "8.29.0",
+ "@typescript-eslint/utils": "8.29.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "6.2.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz",
+ "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "postcss": "^8.5.3",
+ "rollup": "^4.30.1"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
+ "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zip-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz",
+ "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.0",
+ "compress-commons": "^6.0.2",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ }
+ }
+}
diff --git a/plugin/home/package.json b/plugin/home/package.json
new file mode 100644
index 00000000..66401137
--- /dev/null
+++ b/plugin/home/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "home",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build --mode massastation",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "deweb-pages": "file:../../server/pages/",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-icons": "^5.5.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.21.0",
+ "@types/node": "^22.13.10",
+ "@types/react": "^19.0.10",
+ "@types/react-dom": "^19.0.4",
+ "@vitejs/plugin-react": "^4.3.4",
+ "archiver": "^7.0.1",
+ "autoprefixer": "^10.4.21",
+ "eslint": "^9.21.0",
+ "eslint-plugin-react-hooks": "^5.1.0",
+ "eslint-plugin-react-refresh": "^0.4.19",
+ "globals": "^15.15.0",
+ "postcss": "^8.5.3",
+ "tailwindcss": "^3.4.17",
+ "typescript": "~5.7.2",
+ "typescript-eslint": "^8.24.1",
+ "vite": "^6.2.0"
+ }
+}
diff --git a/plugin/home/postcss.config.js b/plugin/home/postcss.config.js
new file mode 100644
index 00000000..2e7af2b7
--- /dev/null
+++ b/plugin/home/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/plugin/home/public/favicon.png b/plugin/home/public/favicon.png
new file mode 100644
index 00000000..77a993ea
Binary files /dev/null and b/plugin/home/public/favicon.png differ
diff --git a/plugin/home/src/App.tsx b/plugin/home/src/App.tsx
new file mode 100644
index 00000000..20b0f7cb
--- /dev/null
+++ b/plugin/home/src/App.tsx
@@ -0,0 +1,191 @@
+import { FiSearch, FiInfo, FiCheckCircle, FiAlertTriangle, FiXCircle, FiClock } from "react-icons/fi";
+import { useEffect, useState } from "react";
+import { UseGenerateTheme } from "deweb-pages/src/hooks/UseGenerateTheme";
+import { ServerStatusResponse, NetworkInfo } from "./types/server";
+import { QuickAccessItem } from "./QuickAccessItem";
+
+const POLLING_INTERVAL = 1000;
+
+export default function App() {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [port, setPort] = useState(null);
+ const [status, setStatus] = useState<"running" | "stopped" | "starting" | "stopping" | "error" | null>(null);
+ const [network, setNetwork] = useState(undefined);
+ const [loading, setLoading] = useState(true);
+ const [errorMessage, setErrorMessage] = useState(null);
+
+ const fetchStatus = () => {
+ const url = window.location.href.endsWith("/")
+ ? window.location.href
+ : `${window.location.href}/`;
+
+ const urlWithoutWeb = url.endsWith("/web/index/") ? url.slice(0, -10) : url;
+
+ fetch(`${urlWithoutWeb}/api/server/status`)
+ .then((res) => res.json())
+ .then((data: ServerStatusResponse) => {
+ setPort(data.serverPort);
+ setStatus(data.status);
+ setNetwork(data.network);
+ if (data.errorMessage) {
+ setErrorMessage(data.errorMessage);
+ } else {
+ setErrorMessage(null);
+ }
+ })
+ .catch(err => console.error("Failed to fetch port:", err))
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ // On mount, fetch status and set up polling interval
+ useEffect(() => {
+ fetchStatus();
+
+ // Set up polling interval to fetch status every POLLING_INTERVAL
+ const intervalId = setInterval(fetchStatus, POLLING_INTERVAL);
+
+ // Clean up interval on component unmount
+ return () => clearInterval(intervalId);
+ }, []);
+
+ const getStatusIcon = () => {
+ if (loading) return ;
+
+ switch (status) {
+ case "running":
+ return ;
+ case "stopped":
+ return ;
+ case "starting":
+ case "stopping":
+ return ;
+ case "error":
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ const getStatusText = () => {
+ if (loading) return "Connecting...";
+
+ if (status === "error" && errorMessage) {
+ return `Error: ${errorMessage}`;
+ }
+
+ if (status === "running" && network) {
+ return `Connected to ${network.network} ${network.version || ''}`;
+ }
+
+ const statusMessages = {
+ running: "Server running",
+ stopped: "Server stopped",
+ starting: "Server starting...",
+ stopping: "Server stopping...",
+ error: "Server error"
+ };
+
+ return statusMessages[status || "stopped"];
+ };
+
+ const getStatusClass = () => {
+ if (loading) return "bg-blue-100 text-blue-800";
+
+ const statusClasses = {
+ running: "bg-green-100 text-green-800",
+ stopped: "bg-gray-100 text-gray-800",
+ starting: "bg-yellow-100 text-yellow-800",
+ stopping: "bg-yellow-100 text-yellow-800",
+ error: "bg-red-100 text-red-800"
+ };
+
+ return statusClasses[status || "stopped"];
+ };
+
+ const generateUrl = (service: string, path: string = '') => {
+ const { host } = new URL(window.location.href);
+ return host === "station.massa" && port
+ ? `http://${service}.localhost:${port}${path}`
+ : `//${service}.${host}${path}`;
+ };
+
+ const handleSearch = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (searchQuery.trim()) {
+ window.open(generateUrl(searchQuery), '_blank');
+ }
+ };
+
+ const quickAccessItems = [
+ {
+ path: "/explore",
+ title: "Explore DeWeb",
+ description: "Browse the list of all websites available in the decentralized web ecosystem",
+ service: "deweb",
+ pathUrl: "/explore"
+ },
+ {
+ path: "/mns",
+ title: "Massa Name System",
+ description: "Decentralized naming service for users and smart contracts on the Massa blockchain",
+ service: "mns"
+ },
+ {
+ path: "/upload",
+ title: "DeWeb Uploader",
+ description: "Upload and manage your websites on DeWeb",
+ service: "dws"
+ }
+ ];
+
+ const theme = UseGenerateTheme();
+
+ return (
+
+ {/* Network Status Indicator */}
+
+ {getStatusIcon()}
+ {getStatusText()}
+
+
+
Search on DeWeb
+
+
+
Quick Access
+
+
+ {quickAccessItems.map((item) => (
+
+ ))}
+
+
+ );
+}
diff --git a/plugin/home/src/QuickAccessItem.tsx b/plugin/home/src/QuickAccessItem.tsx
new file mode 100644
index 00000000..9bad6a05
--- /dev/null
+++ b/plugin/home/src/QuickAccessItem.tsx
@@ -0,0 +1,20 @@
+type QuickAccessItemProps = {
+ path: string;
+ title: string;
+ description: string;
+ href: string;
+};
+export const QuickAccessItem = ({ path, title, description, href }: QuickAccessItemProps) => (
+
+
+ {path}
+
+ {title}
+ {description}
+
+);
diff --git a/plugin/home/src/index.css b/plugin/home/src/index.css
new file mode 100644
index 00000000..bd6213e1
--- /dev/null
+++ b/plugin/home/src/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
diff --git a/plugin/home/src/main.tsx b/plugin/home/src/main.tsx
new file mode 100644
index 00000000..bef5202a
--- /dev/null
+++ b/plugin/home/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.tsx'
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/plugin/home/src/types/server.ts b/plugin/home/src/types/server.ts
new file mode 100644
index 00000000..3aa46c19
--- /dev/null
+++ b/plugin/home/src/types/server.ts
@@ -0,0 +1,28 @@
+export type ServerStatus = 'running' | 'stopped' | 'starting' | 'stopping' | 'error';
+
+export interface NetworkInfo {
+ network: string;
+ version: string;
+ chainID: number;
+}
+
+export interface ServerStatusResponse {
+ status: ServerStatus;
+ serverPort: number;
+ errorMessage?: string;
+ network?: NetworkInfo;
+}
+
+export interface CacheSettings {
+ enabled: boolean;
+ siteRamCacheMaxItems?: number;
+ siteDiskCacheMaxItems?: number;
+ diskCacheDir?: string;
+ fileListCacheDurationSeconds?: number;
+}
+
+export interface Settings {
+ networkUrl: string;
+ serverPort?: number;
+ cache?: CacheSettings;
+}
\ No newline at end of file
diff --git a/plugin/home/src/vite-env.d.ts b/plugin/home/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/plugin/home/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/plugin/home/tailwind.config.js b/plugin/home/tailwind.config.js
new file mode 100644
index 00000000..550ed618
--- /dev/null
+++ b/plugin/home/tailwind.config.js
@@ -0,0 +1,7 @@
+/** @type {import('tailwindcss').Config} */
+
+export default {
+ content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
+
+ presets: [require("../../server/pages/src/colors/preset.js")],
+};
diff --git a/plugin/home/tsconfig.app.json b/plugin/home/tsconfig.app.json
new file mode 100644
index 00000000..358ca9ba
--- /dev/null
+++ b/plugin/home/tsconfig.app.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/plugin/home/tsconfig.json b/plugin/home/tsconfig.json
new file mode 100644
index 00000000..1ffef600
--- /dev/null
+++ b/plugin/home/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/plugin/home/tsconfig.node.json b/plugin/home/tsconfig.node.json
new file mode 100644
index 00000000..db0becc8
--- /dev/null
+++ b/plugin/home/tsconfig.node.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/plugin/home/vite.config.ts b/plugin/home/vite.config.ts
new file mode 100644
index 00000000..84c68168
--- /dev/null
+++ b/plugin/home/vite.config.ts
@@ -0,0 +1,16 @@
+import { defineConfig, loadEnv } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default ({ mode }: any) => {
+ // loadEnv(mode, process.cwd()) will load the .env files depending on the mode
+ // import.meta.env.BASE_URL available here with: process.env.BASE_URL
+ process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
+
+ return defineConfig({
+ plugins: [react()],
+ base: process.env.VITE_BASE_APP,
+ build: {
+ outDir: '../int/api/html/dist',
+ },
+ })
+}
diff --git a/plugin/int/api/api.go b/plugin/int/api/api.go
new file mode 100644
index 00000000..00ab2088
--- /dev/null
+++ b/plugin/int/api/api.go
@@ -0,0 +1,108 @@
+package api
+
+import (
+ "log"
+ "net/http"
+
+ "github.com/go-openapi/loads"
+ "github.com/massalabs/deweb-plugin/api/restapi"
+ "github.com/massalabs/deweb-plugin/api/restapi/operations"
+ "github.com/massalabs/deweb-plugin/int/api/html"
+ apiserver "github.com/massalabs/deweb-plugin/int/api/server"
+ "github.com/massalabs/deweb-plugin/int/server"
+ "github.com/massalabs/station-massa-hello-world/pkg/plugin"
+ "github.com/massalabs/station/pkg/logger"
+)
+
+type API struct {
+ apiServer *restapi.Server
+ pluginAPI *operations.DewebPluginAPI
+ serverManager *server.ServerManager
+ configDir string
+}
+
+// NewAPI creates a new API with the provided plugin directory
+func NewAPI(configDir string) *API {
+ swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ dewebAPI := operations.NewDewebPluginAPI(swaggerSpec)
+ apiServer := restapi.NewServer(dewebAPI)
+
+ manager, err := server.NewServerManager(configDir)
+ if err != nil {
+ logger.Errorf("Failed to create server manager: %v", err)
+ }
+
+ return &API{
+ apiServer: apiServer,
+ pluginAPI: dewebAPI,
+ configDir: configDir,
+ serverManager: manager,
+ }
+}
+
+// Start starts the API server.
+func (a *API) Start() {
+ defer func() {
+ if err := a.apiServer.Shutdown(); err != nil {
+ log.Fatalln(err)
+ }
+
+ // Shutdown the server manager if it is running
+ if a.serverManager != nil {
+ if err := a.serverManager.Stop(); err != nil {
+ logger.Errorf("Error stopping server on shutdown: %v", err)
+ }
+ }
+ }()
+
+ // We don't care about the port of the plugin API as MassaStation will handle the port mapping
+ a.apiServer.Port = 0
+
+ a.configureAPI()
+
+ a.apiServer.ConfigureAPI()
+
+ a.apiServer.SetHandler(webAppMiddleware(a.pluginAPI.Serve(nil)))
+
+ listener, err := a.apiServer.HTTPListener()
+ if err != nil {
+ logger.Fatalf("Failed to get HTTP listener: %v", err)
+ }
+
+ if err := plugin.RegisterPlugin(listener); err != nil {
+ logger.Fatalf("Failed to register plugin: %v", err)
+ }
+
+ // Start the server if manager exists
+ if a.serverManager != nil {
+ go func() {
+ if err := a.serverManager.Start(); err != nil && err != server.ErrServerAlreadyRunning {
+ logger.Errorf("Failed to start DeWeb server: %v", err)
+ }
+ }()
+ }
+
+ if err := a.apiServer.Serve(); err != nil {
+ log.Fatalln(err)
+ }
+}
+
+// ConfigureAPI sets up the API handlers and error handling.
+func (a *API) configureAPI() {
+ a.pluginAPI.ServeError = func(w http.ResponseWriter, r *http.Request, err error) {
+ log.Printf("ServeError: %v", err)
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+
+ html.AppendEndpoints(a.pluginAPI)
+
+ if a.serverManager != nil {
+ apiserver.RegisterHandlers(a.pluginAPI, a.serverManager, a.configDir)
+ } else {
+ logger.Errorf("Server manager not available for registering handlers")
+ }
+}
diff --git a/plugin/int/api/html/html.go b/plugin/int/api/html/html.go
new file mode 100644
index 00000000..d9685516
--- /dev/null
+++ b/plugin/int/api/html/html.go
@@ -0,0 +1,54 @@
+package html
+
+import (
+ "embed"
+ "mime"
+ "net/http"
+ "path/filepath"
+
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/massalabs/deweb-plugin/api/restapi/operations"
+ "github.com/massalabs/station-massa-wallet/pkg/openapi"
+)
+
+const (
+ indexHTML = "index.html"
+ basePathWebApp = "dist/"
+)
+
+//nolint:typecheck
+//go:embed dist
+var contentWebApp embed.FS
+
+// Handle a Web request.
+func HandleWebApp(params operations.PluginWebAppParams) middleware.Responder {
+ resourceName := params.Resource
+
+ resourceContent, err := contentWebApp.ReadFile(basePathWebApp + resourceName)
+ if err != nil {
+ resourceName = "index.html"
+
+ resourceContent, err = contentWebApp.ReadFile(basePathWebApp + resourceName)
+ if err != nil {
+ return operations.NewPluginWebAppNotFound()
+ }
+ }
+
+ fileExtension := filepath.Ext(resourceName)
+
+ mimeType := mime.TypeByExtension(fileExtension)
+
+ header := map[string]string{"Content-Type": mimeType}
+
+ return openapi.NewCustomResponder(resourceContent, header, http.StatusOK)
+}
+
+// DefaultRedirectHandler redirects request to "/" URL to "web/index"
+func DefaultRedirectHandler(_ operations.DefaultPageParams) middleware.Responder {
+ return openapi.NewCustomResponder(nil, map[string]string{"Location": "web/index"}, http.StatusPermanentRedirect)
+}
+
+// AppendEndpoints appends web endpoints to the API.
+func AppendEndpoints(api *operations.DewebPluginAPI) {
+ api.DefaultPageHandler = operations.DefaultPageHandlerFunc(DefaultRedirectHandler)
+}
diff --git a/plugin/int/api/middleware.go b/plugin/int/api/middleware.go
new file mode 100644
index 00000000..ebde0ba9
--- /dev/null
+++ b/plugin/int/api/middleware.go
@@ -0,0 +1,29 @@
+package api
+
+import (
+ "net/http"
+ "strings"
+
+ "github.com/go-openapi/runtime"
+ "github.com/massalabs/deweb-plugin/api/restapi/operations"
+ "github.com/massalabs/deweb-plugin/int/api/html"
+)
+
+const frontendPrefix = "/web/"
+
+func webAppMiddleware(handler http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if strings.HasPrefix(r.URL.Path, frontendPrefix) {
+ params := operations.PluginWebAppParams{
+ HTTPRequest: r,
+ Resource: strings.TrimPrefix(r.URL.Path, frontendPrefix),
+ }
+ responder := html.HandleWebApp(params)
+ responder.WriteResponse(w, runtime.JSONProducer())
+
+ return
+ }
+
+ handler.ServeHTTP(w, r)
+ })
+}
diff --git a/plugin/int/api/server/handlers.go b/plugin/int/api/server/handlers.go
new file mode 100644
index 00000000..87c0e3d0
--- /dev/null
+++ b/plugin/int/api/server/handlers.go
@@ -0,0 +1,190 @@
+package server
+
+import (
+ "net/http"
+ "os"
+ "path/filepath"
+
+ "github.com/go-openapi/runtime"
+ "github.com/go-openapi/runtime/middleware"
+ "github.com/massalabs/deweb-plugin/api/models"
+ "github.com/massalabs/deweb-plugin/api/restapi/operations"
+ "github.com/massalabs/deweb-plugin/int/server"
+ "github.com/massalabs/deweb-server/int/api/config"
+ "github.com/massalabs/station/pkg/logger"
+ "gopkg.in/yaml.v2"
+)
+
+// RegisterHandlers registers the server-related API handlers
+func RegisterHandlers(api *operations.DewebPluginAPI, manager *server.ServerManager, configDir string) {
+ configPath := filepath.Join(configDir, "deweb_server_config.yaml")
+
+ api.GetServerStatusHandler = operations.GetServerStatusHandlerFunc(handleGetServerStatus(manager))
+ api.GetSettingsHandler = operations.GetSettingsHandlerFunc(handleGetSettings(configPath))
+ api.UpdateSettingsHandler = operations.UpdateSettingsHandlerFunc(handleUpdateSettings(manager, configPath))
+}
+
+// handleGetServerStatus returns a handler function for the GET /api/server/status endpoint
+func handleGetServerStatus(manager *server.ServerManager) func(operations.GetServerStatusParams) middleware.Responder {
+ return func(params operations.GetServerStatusParams) middleware.Responder {
+ status := manager.GetStatus()
+
+ response := &models.ServerStatus{
+ Status: string(status),
+ }
+
+ if status == server.StatusError {
+ errorMsg := manager.GetLastError()
+ response.ErrorMessage = errorMsg
+ }
+
+ if status == server.StatusRunning {
+ serverConfig, err := manager.GetConfig()
+ if err != nil {
+ return createErrorResponse(http.StatusInternalServerError, "Failed to get server config")
+ }
+
+ response.Network = &models.ServerStatusNetwork{
+ ChainID: serverConfig.NetworkInfos.ChainID,
+ Network: serverConfig.NetworkInfos.Network,
+ Version: serverConfig.NetworkInfos.Version,
+ }
+
+ apiPort, err := manager.GetServerPort()
+ if err != nil {
+ return createErrorResponse(http.StatusInternalServerError, "Failed to get server port")
+ }
+
+ response.ServerPort = int32(apiPort)
+ }
+
+ return operations.NewGetServerStatusOK().WithPayload(response)
+ }
+}
+
+// handleGetSettings returns a handler function for the GET /api/settings endpoint
+func handleGetSettings(configPath string) func(operations.GetSettingsParams) middleware.Responder {
+ return func(params operations.GetSettingsParams) middleware.Responder {
+ serverConfig, err := config.LoadServerConfig(configPath)
+ if err != nil {
+ return createErrorResponse(http.StatusInternalServerError, "Failed to load server configuration")
+ }
+
+ // Map server config to API model
+ networkURL := serverConfig.NetworkInfos.NodeURL
+ enabled := serverConfig.CacheConfig.Enabled
+ diskCacheDir := serverConfig.CacheConfig.DiskCacheDir
+
+ cacheSettings := &models.CacheSettings{
+ Enabled: &enabled,
+ SiteRAMCacheMaxItems: int32(serverConfig.CacheConfig.SiteRAMCacheMaxItems),
+ SiteDiskCacheMaxItems: int32(serverConfig.CacheConfig.SiteDiskCacheMaxItems),
+ DiskCacheDir: diskCacheDir,
+ FileListCacheDurationSeconds: int32(serverConfig.CacheConfig.FileListCacheDurationSeconds),
+ }
+
+ return operations.NewGetSettingsOK().WithPayload(&models.Settings{
+ NetworkURL: &networkURL,
+ ServerPort: int32(serverConfig.APIPort),
+ Cache: cacheSettings,
+ })
+ }
+}
+
+// handleUpdateSettings returns a handler function for the PUT /api/settings endpoint
+func handleUpdateSettings(manager *server.ServerManager, configPath string) func(operations.UpdateSettingsParams) middleware.Responder {
+ return func(params operations.UpdateSettingsParams) middleware.Responder {
+ if params.Settings == nil {
+ return createErrorResponse(http.StatusBadRequest, "Settings data is required")
+ }
+
+ // Load current config
+ serverConfig, err := config.LoadServerConfig(configPath)
+ if err != nil {
+ return createErrorResponse(http.StatusInternalServerError, "Failed to load server configuration")
+ }
+
+ // Update config with new settings
+ settings := params.Settings
+ if settings.NetworkURL != nil {
+ serverConfig.NetworkInfos.NodeURL = *settings.NetworkURL
+ }
+
+ if settings.ServerPort != 0 {
+ serverConfig.APIPort = int(settings.ServerPort)
+ }
+
+ if settings.Cache != nil {
+ if settings.Cache.Enabled != nil {
+ serverConfig.CacheConfig.Enabled = *settings.Cache.Enabled
+ }
+
+ if settings.Cache.SiteRAMCacheMaxItems != 0 {
+ serverConfig.CacheConfig.SiteRAMCacheMaxItems = uint64(settings.Cache.SiteRAMCacheMaxItems)
+ }
+
+ if settings.Cache.SiteDiskCacheMaxItems != 0 {
+ serverConfig.CacheConfig.SiteDiskCacheMaxItems = uint64(settings.Cache.SiteDiskCacheMaxItems)
+ }
+
+ if settings.Cache.DiskCacheDir != "" {
+ serverConfig.CacheConfig.DiskCacheDir = settings.Cache.DiskCacheDir
+ }
+
+ if settings.Cache.FileListCacheDurationSeconds != 0 {
+ serverConfig.CacheConfig.FileListCacheDurationSeconds = int(settings.Cache.FileListCacheDurationSeconds)
+ }
+ }
+
+ // Marshal config to YAML
+ yamlData, err := yaml.Marshal(serverConfig)
+ if err != nil {
+ return createErrorResponse(http.StatusInternalServerError, "Failed to marshal configuration")
+ }
+
+ // Stop the server
+ if err := manager.Stop(); err != nil && err != server.ErrServerNotRunning {
+ return createErrorResponse(http.StatusInternalServerError, "Failed to stop server")
+ }
+
+ // Write config file
+ if err := os.WriteFile(configPath, yamlData, 0o644); err != nil {
+ return createErrorResponse(http.StatusInternalServerError, "Failed to write configuration file")
+ }
+
+ // Restart the server
+ go func() {
+ if err := manager.Start(); err != nil {
+ logger.Errorf("Failed to restart server: %v", err)
+ }
+ }()
+
+ return operations.NewUpdateSettingsOK()
+ }
+}
+
+// Create an error response with the given status code and message
+func createErrorResponse(statusCode int, message string) middleware.Responder {
+ code := int32(statusCode)
+ return &customErrorResponder{
+ statusCode: statusCode,
+ payload: &models.Error{
+ Code: &code,
+ Message: &message,
+ },
+ }
+}
+
+// customErrorResponder implements the Responder interface for error responses
+type customErrorResponder struct {
+ statusCode int
+ payload *models.Error
+}
+
+// WriteResponse writes the error response
+func (r *customErrorResponder) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+ rw.WriteHeader(r.statusCode)
+ if err := producer.Produce(rw, r.payload); err != nil {
+ panic(err)
+ }
+}
diff --git a/plugin/int/server/config.go b/plugin/int/server/config.go
new file mode 100644
index 00000000..6f171390
--- /dev/null
+++ b/plugin/int/server/config.go
@@ -0,0 +1,148 @@
+package server
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/massalabs/deweb-server/int/api/config"
+ "github.com/massalabs/station/pkg/logger"
+ "gopkg.in/yaml.v2"
+)
+
+const (
+ defaultAPIPort = 0
+
+ DefaultConfigFileName = "deweb_server_config.yaml"
+)
+
+// ensureConfigFileExists makes sure the config file exists, creating it with defaults if needed
+func ensureConfigFileExists(configDir string) error {
+ configPath := filepath.Join(configDir, DefaultConfigFileName)
+
+ if _, err := os.Stat(configPath); os.IsNotExist(err) {
+ yamlConfig := createDefaultYamlConfig(configDir)
+
+ if err := saveYamlConfig(yamlConfig, configPath); err != nil {
+ return fmt.Errorf("failed to save default config: %w", err)
+ }
+
+ logger.Infof("Created default server config at: %s", configPath)
+ }
+
+ return nil
+}
+
+// createDefaultYamlConfig creates a default YAML config with proper paths
+func createDefaultYamlConfig(configDir string) config.YamlServerConfig {
+ domain := config.DefaultDomain
+ networkNodeURL := config.DefaultNetworkNodeURL
+ apiPort := defaultAPIPort
+
+ yamlConfig := config.YamlServerConfig{
+ Domain: &domain,
+ NetworkNodeURL: &networkNodeURL,
+ APIPort: &apiPort,
+ AllowList: []string{},
+ BlockList: []string{},
+ }
+
+ cacheDir := filepath.Join(configDir, "cache")
+ if err := os.MkdirAll(cacheDir, 0o755); err != nil {
+ logger.Errorf("Failed to create cache directory: %v", err)
+ }
+
+ enabled := true
+ cacheDirPtr := cacheDir
+ ramItems := config.DefaultMaxRAMItems
+ diskItems := config.DefaultMaxDiskItems
+ cacheDuration := config.DefaultFileListCachePeriod
+
+ yamlConfig.CacheConfig = &config.YamlCacheConfig{
+ Enabled: &enabled,
+ DiskCacheDir: &cacheDirPtr,
+ SiteRAMCacheMaxItems: &ramItems,
+ SiteDiskCacheMaxItems: &diskItems,
+ FileListCacheDurationSeconds: &cacheDuration,
+ }
+
+ return yamlConfig
+}
+
+// SaveServerConfig saves a ServerConfig to the given path
+func SaveServerConfig(serverConfig *config.ServerConfig, configPath string) error {
+ yamlConfig := convertToYamlConfig(serverConfig)
+
+ return saveYamlConfig(yamlConfig, configPath)
+}
+
+// convertToYamlConfig converts a ServerConfig to YamlServerConfig
+func convertToYamlConfig(serverConfig *config.ServerConfig) config.YamlServerConfig {
+ if serverConfig == nil {
+ return createDefaultYamlConfig("")
+ }
+
+ domain := serverConfig.Domain
+ networkNodeURL := serverConfig.NetworkInfos.NodeURL
+ apiPort := serverConfig.APIPort
+
+ // Create YAML config from server config
+ yamlConfig := config.YamlServerConfig{
+ Domain: &domain,
+ NetworkNodeURL: &networkNodeURL,
+ APIPort: &apiPort,
+ AllowList: serverConfig.AllowList,
+ BlockList: serverConfig.BlockList,
+ MiscPublicInfoJson: serverConfig.MiscPublicInfoJson,
+ }
+
+ // Convert cache config to YAML format
+ enabled := serverConfig.CacheConfig.Enabled
+ diskCacheDir := serverConfig.CacheConfig.DiskCacheDir
+ ramItems := serverConfig.CacheConfig.SiteRAMCacheMaxItems
+ diskItems := serverConfig.CacheConfig.SiteDiskCacheMaxItems
+ cacheDuration := serverConfig.CacheConfig.FileListCacheDurationSeconds
+
+ yamlConfig.CacheConfig = &config.YamlCacheConfig{
+ Enabled: &enabled,
+ DiskCacheDir: &diskCacheDir,
+ SiteRAMCacheMaxItems: &ramItems,
+ SiteDiskCacheMaxItems: &diskItems,
+ FileListCacheDurationSeconds: &cacheDuration,
+ }
+
+ return yamlConfig
+}
+
+// saveYamlConfig saves a YamlServerConfig to disk
+func saveYamlConfig(yamlConfig config.YamlServerConfig, configPath string) error {
+ if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
+ return fmt.Errorf("failed to create config directory: %w", err)
+ }
+
+ yamlData, err := yaml.Marshal(yamlConfig)
+ if err != nil {
+ return fmt.Errorf("failed to marshal config to YAML: %w", err)
+ }
+
+ if err := os.WriteFile(configPath, yamlData, 0o644); err != nil {
+ return fmt.Errorf("failed to write config file: %w", err)
+ }
+
+ return nil
+}
+
+// GetConfigPath returns the path to the server config file
+func getConfigPath(configDir string) string {
+ return filepath.Join(configDir, DefaultConfigFileName)
+}
+
+// loadConfig loads a ServerConfig from the given path
+func loadConfig(configPath string) (*config.ServerConfig, error) {
+ serverConfig, err := config.LoadServerConfig(configPath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load config: %w", err)
+ }
+
+ return serverConfig, nil
+}
diff --git a/plugin/int/server/errors.go b/plugin/int/server/errors.go
new file mode 100644
index 00000000..1138504e
--- /dev/null
+++ b/plugin/int/server/errors.go
@@ -0,0 +1,21 @@
+package server
+
+var (
+ ErrServerNotRunning = NewError("server is not running")
+ ErrServerAlreadyRunning = NewError("server is already running")
+)
+
+// Error represents a server manager error
+type Error struct {
+ message string
+}
+
+// NewError creates a new Error
+func NewError(msg string) *Error {
+ return &Error{message: msg}
+}
+
+// Error returns the error message
+func (e *Error) Error() string {
+ return e.message
+}
diff --git a/plugin/int/server/manager.go b/plugin/int/server/manager.go
new file mode 100644
index 00000000..07877bf9
--- /dev/null
+++ b/plugin/int/server/manager.go
@@ -0,0 +1,262 @@
+package server
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "sync"
+ "syscall"
+ "time"
+
+ "github.com/massalabs/deweb-server/int/api/config"
+ "github.com/massalabs/station/pkg/logger"
+ "github.com/shirou/gopsutil/v4/process"
+)
+
+const (
+ DefaultLogPath = "deweb-server.log"
+)
+
+// ServerManager handles DeWeb server operations with thread safety
+type ServerManager struct {
+ mu sync.Mutex
+ serverProcess *os.Process
+ serverBinPath string
+ configDir string
+ isRunning bool
+ lastError string
+ binaryExists bool
+}
+
+// NewServerManager creates a new server manager
+func NewServerManager(configDir string) (*ServerManager, error) {
+ // Ensure config directory exists
+ if err := os.MkdirAll(configDir, 0o755); err != nil {
+ return nil, fmt.Errorf("failed to create config directory: %v", err)
+ }
+
+ // Determine the plugin's executable path
+ execPath, err := os.Executable()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get executable path: %v", err)
+ }
+
+ // Server binary should be in the same directory as the plugin
+ pluginDir := filepath.Dir(execPath)
+ serverBinPath := filepath.Join(pluginDir, "deweb-server")
+
+ // On Windows, add .exe extension
+ if filepath.Ext(execPath) == ".exe" {
+ serverBinPath += ".exe"
+ }
+
+ manager := &ServerManager{
+ serverBinPath: serverBinPath,
+ configDir: configDir,
+ binaryExists: true,
+ }
+
+ // Check if config file exists, if not, create it with defaults
+ if err := ensureConfigFileExists(configDir); err != nil {
+ manager.lastError = fmt.Sprintf("Failed to ensure config file exists: %v", err)
+ logger.Errorf(manager.lastError)
+ }
+
+ return manager, nil
+}
+
+// Start launches the server process
+func (m *ServerManager) Start() error {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ _, err := os.Stat(m.serverBinPath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ m.binaryExists = false
+ m.lastError = fmt.Sprintf("Server binary not found at %s", m.serverBinPath)
+ return fmt.Errorf(m.lastError)
+ }
+
+ return fmt.Errorf("failed to check server binary: %v", err)
+ }
+
+ if m.isRunning {
+ logger.Infof("Server is already running")
+ return ErrServerAlreadyRunning
+ }
+
+ configPath := m.GetConfigPath()
+ logger.Infof("Starting DeWeb server with config: %s", configPath)
+
+ logPath := filepath.Join(m.configDir, DefaultLogPath)
+
+ cmd := exec.Command(m.serverBinPath, "--configPath", configPath, "--logPath", logPath, "--accept-disclaimer")
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+
+ if err := cmd.Start(); err != nil {
+ m.lastError = fmt.Sprintf("Failed to start server: %v", err)
+ logger.Errorf(m.lastError)
+ return err
+ }
+
+ m.serverProcess = cmd.Process
+ m.isRunning = true
+ m.lastError = ""
+
+ // Start a goroutine to monitor the process
+ go func() {
+ err := cmd.Wait()
+ if err != nil {
+ m.mu.Lock()
+ m.lastError = fmt.Sprintf("Server process exited with error: %v", err)
+ m.mu.Unlock()
+ logger.Errorf(m.lastError)
+ }
+
+ m.mu.Lock()
+ m.isRunning = false
+ m.serverProcess = nil
+ m.mu.Unlock()
+
+ logger.Infof("Server process exited")
+ }()
+
+ return nil
+}
+
+// kill forcefully terminates the server process and updates state
+// NOTE: This is an unsafe internal method that doesn't handle mutex locking.
+// It should only be called by methods that have already acquired the mutex lock.
+func (m *ServerManager) kill() error {
+ if !m.isRunning || m.serverProcess == nil {
+ return ErrServerNotRunning
+ }
+
+ err := m.serverProcess.Kill()
+ if err != nil {
+ return err
+ }
+
+ m.isRunning = false
+ m.serverProcess = nil
+
+ return nil
+}
+
+// Stop terminates the server process
+func (m *ServerManager) Stop() error {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if !m.isRunning || m.serverProcess == nil {
+ logger.Infof("Server is not running")
+ return ErrServerNotRunning
+ }
+
+ logger.Infof("Stopping DeWeb server")
+
+ // Send a SIGTERM signal to gracefully shut down
+ if err := m.serverProcess.Signal(syscall.SIGTERM); err != nil {
+ logger.Errorf("Failed to send SIGTERM: %v", err)
+
+ // Force kill as a fallback
+ if err = m.kill(); err != nil {
+ return err
+ }
+ }
+
+ // Wait for the process to exit
+ timeout := time.Now().Add(5 * time.Second)
+ for time.Now().Before(timeout) && m.isRunning {
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ // If still running after timeout, force kill
+ if m.isRunning {
+ _ = m.kill()
+ }
+
+ logger.Infof("Server stopped")
+
+ return nil
+}
+
+// Restart restarts the server
+func (m *ServerManager) Restart() error {
+ if err := m.Stop(); err != nil && err != ErrServerNotRunning {
+ return err
+ }
+
+ return m.Start()
+}
+
+// GetStatus returns the current server status
+func (m *ServerManager) GetStatus() Status {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if !m.binaryExists || m.lastError != "" {
+ return StatusError
+ }
+
+ if m.isRunning {
+ return StatusRunning
+ }
+ return StatusStopped
+}
+
+// IsRunning returns whether the server is currently running
+func (m *ServerManager) IsRunning() bool {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ return m.isRunning
+}
+
+func (m *ServerManager) GetConfigPath() string {
+ return getConfigPath(m.configDir)
+}
+
+func (m *ServerManager) GetConfig() (*config.ServerConfig, error) {
+ return loadConfig(m.GetConfigPath())
+}
+
+// GetLastError returns the last error message
+func (m *ServerManager) GetLastError() string {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ return m.lastError
+}
+
+// GetServerPort retrieves the actual port the server is running on
+func (m *ServerManager) GetServerPort() (uint32, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if !m.isRunning || m.serverProcess == nil {
+ return 0, ErrServerNotRunning
+ }
+
+ proc, err := process.NewProcess(int32(m.serverProcess.Pid))
+ if err != nil {
+ return 0, fmt.Errorf("failed to access process: %v", err)
+ }
+
+ connections, err := proc.Connections()
+ if err != nil {
+ return 0, fmt.Errorf("failed to get process connections: %v", err)
+ }
+
+ // Find TCP connections in LISTEN state
+ for _, conn := range connections {
+ if conn.Status == "LISTEN" {
+ return conn.Laddr.Port, nil
+ }
+ }
+
+ return 0, fmt.Errorf("no listening ports found for server process")
+}
diff --git a/plugin/int/server/status.go b/plugin/int/server/status.go
new file mode 100644
index 00000000..eb301ef2
--- /dev/null
+++ b/plugin/int/server/status.go
@@ -0,0 +1,13 @@
+package server
+
+// Status represents the current status of the server
+type Status string
+
+// Server status constants
+const (
+ StatusRunning Status = "running"
+ StatusStopped Status = "stopped"
+ StatusStarting Status = "starting"
+ StatusStopping Status = "stopping"
+ StatusError Status = "error"
+)
diff --git a/plugin/main.go b/plugin/main.go
new file mode 100644
index 00000000..e4de7b5f
--- /dev/null
+++ b/plugin/main.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ _ "embed"
+ "fmt"
+ "log"
+ "os"
+ "path/filepath"
+
+ "github.com/massalabs/deweb-plugin/int/api"
+ "github.com/massalabs/station/pkg/logger"
+)
+
+func main() {
+ pluginDir, err := PluginDir()
+ if err != nil {
+ log.Fatalf("failed to get plugin directory: %v", err)
+ }
+
+ logPath := filepath.Join(pluginDir, "./deweb-plugin.log")
+
+ err = logger.InitializeGlobal(logPath)
+ if err != nil {
+ log.Fatalf("failed to initialize logger: %v", err)
+ }
+
+ // Create and start the API with the plugin directory
+ apiInstance := api.NewAPI(pluginDir)
+ apiInstance.Start()
+
+ logger.Warnf("DeWeb plugin stopped")
+}
+
+const directoryName = "station-deweb-plugin"
+
+func PluginDir() (string, error) {
+ configDir, err := os.UserConfigDir()
+ if err != nil {
+ return "", fmt.Errorf("getting user config directory: %w", err)
+ }
+
+ path := filepath.Join(configDir, directoryName)
+
+ _, err = os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ err = os.MkdirAll(path, os.ModePerm)
+ if err != nil {
+ return "", fmt.Errorf("creating account directory '%s': %w", path, err)
+ }
+ } else {
+ return "", fmt.Errorf("checking directory '%s': %w", path, err)
+ }
+ }
+
+ return path, nil
+}
diff --git a/plugin/manifest.json b/plugin/manifest.json
new file mode 100644
index 00000000..9067ac6c
--- /dev/null
+++ b/plugin/manifest.json
@@ -0,0 +1,9 @@
+{
+ "author": "Massa Labs",
+ "name": "Local DeWeb Provider",
+ "description": "Your private access to DeWeb.",
+ "logo": "favicon.png",
+ "home": "",
+ "version": "0.1.0",
+ "apispec": ""
+}
\ No newline at end of file
diff --git a/server/api/read/models/de_web_info.go b/server/api/read/models/de_web_info.go
index 5de16cda..2657db3f 100644
--- a/server/api/read/models/de_web_info.go
+++ b/server/api/read/models/de_web_info.go
@@ -8,6 +8,7 @@ package models
import (
"context"
+ "github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
@@ -23,17 +24,78 @@ type DeWebInfo struct {
// misc
Misc interface{} `json:"misc,omitempty"`
+ // network
+ Network *DeWebInfoNetwork `json:"network,omitempty"`
+
// version
Version string `json:"version,omitempty"`
}
// Validate validates this de web info
func (m *DeWebInfo) Validate(formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.validateNetwork(formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *DeWebInfo) validateNetwork(formats strfmt.Registry) error {
+ if swag.IsZero(m.Network) { // not required
+ return nil
+ }
+
+ if m.Network != nil {
+ if err := m.Network.Validate(formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("network")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("network")
+ }
+ return err
+ }
+ }
+
return nil
}
-// ContextValidate validates this de web info based on context it is used
+// ContextValidate validate this de web info based on the context it is used
func (m *DeWebInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ var res []error
+
+ if err := m.contextValidateNetwork(ctx, formats); err != nil {
+ res = append(res, err)
+ }
+
+ if len(res) > 0 {
+ return errors.CompositeValidationError(res...)
+ }
+ return nil
+}
+
+func (m *DeWebInfo) contextValidateNetwork(ctx context.Context, formats strfmt.Registry) error {
+
+ if m.Network != nil {
+
+ if swag.IsZero(m.Network) { // not required
+ return nil
+ }
+
+ if err := m.Network.ContextValidate(ctx, formats); err != nil {
+ if ve, ok := err.(*errors.Validation); ok {
+ return ve.ValidateName("network")
+ } else if ce, ok := err.(*errors.CompositeError); ok {
+ return ce.ValidateName("network")
+ }
+ return err
+ }
+ }
+
return nil
}
@@ -54,3 +116,46 @@ func (m *DeWebInfo) UnmarshalBinary(b []byte) error {
*m = res
return nil
}
+
+// DeWebInfoNetwork de web info network
+//
+// swagger:model DeWebInfoNetwork
+type DeWebInfoNetwork struct {
+
+ // chain ID
+ ChainID int64 `json:"chainID,omitempty"`
+
+ // network
+ Network string `json:"network,omitempty"`
+
+ // version
+ Version string `json:"version,omitempty"`
+}
+
+// Validate validates this de web info network
+func (m *DeWebInfoNetwork) Validate(formats strfmt.Registry) error {
+ return nil
+}
+
+// ContextValidate validates this de web info network based on context it is used
+func (m *DeWebInfoNetwork) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
+ return nil
+}
+
+// MarshalBinary interface implementation
+func (m *DeWebInfoNetwork) MarshalBinary() ([]byte, error) {
+ if m == nil {
+ return nil, nil
+ }
+ return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *DeWebInfoNetwork) UnmarshalBinary(b []byte) error {
+ var res DeWebInfoNetwork
+ if err := swag.ReadJSON(b, &res); err != nil {
+ return err
+ }
+ *m = res
+ return nil
+}
diff --git a/server/api/read/restapi/embedded_spec.go b/server/api/read/restapi/embedded_spec.go
index 2ee7c46f..caea7814 100644
--- a/server/api/read/restapi/embedded_spec.go
+++ b/server/api/read/restapi/embedded_spec.go
@@ -90,6 +90,21 @@ func init() {
"type": "object",
"additionalProperties": true
},
+ "network": {
+ "type": "object",
+ "properties": {
+ "chainID": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "network": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ },
"version": {
"type": "string"
}
@@ -178,6 +193,36 @@ func init() {
"type": "object",
"additionalProperties": true
},
+ "network": {
+ "type": "object",
+ "properties": {
+ "chainID": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "network": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ },
+ "DeWebInfoNetwork": {
+ "type": "object",
+ "properties": {
+ "chainID": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "network": {
+ "type": "string"
+ },
"version": {
"type": "string"
}
diff --git a/server/api/readAPI-V0.yml b/server/api/readAPI-V0.yml
index 56d9865a..47400252 100644
--- a/server/api/readAPI-V0.yml
+++ b/server/api/readAPI-V0.yml
@@ -56,3 +56,13 @@ definitions:
misc:
type: object
additionalProperties: true
+ network:
+ type: object
+ properties:
+ network:
+ type: string
+ version:
+ type: string
+ chainID:
+ type: integer
+ format: int64
diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go
index c2d5163e..08725ac7 100644
--- a/server/cmd/server/main.go
+++ b/server/cmd/server/main.go
@@ -10,11 +10,19 @@ import (
)
func main() {
+ // Add command-line flag for config file path
+ configPath := flag.String("configPath", "./deweb_server_config.yaml", "Path to server configuration file")
+ logPath := flag.String("logPath", "./deweb-server.log", "Path to server log file")
// If the --accept-disclaimer (or -a) flag is set, the disclaimer will not be displayed. This is for CI purposes.
acceptDisclaimer := flag.Bool("accept-disclaimer", false, "Automatically accept the disclaimer")
flag.BoolVar(acceptDisclaimer, "a", false, "Shortcut for --accept-disclaimer")
flag.Parse()
+ err := logger.InitializeGlobal(*logPath)
+ if err != nil {
+ log.Fatalf("failed to initialize logger: %v", err)
+ }
+
if !*acceptDisclaimer {
err := api.HandleDisclaimer()
if err != nil {
@@ -22,12 +30,7 @@ func main() {
}
}
- err := logger.InitializeGlobal("./deweb-server.log")
- if err != nil {
- log.Fatalf("failed to initialize logger: %v", err)
- }
-
- conf, err := config.LoadServerConfig("./deweb_server_config.yaml")
+ conf, err := config.LoadServerConfig(*configPath)
if err != nil {
log.Fatalf("failed to load server config: %v", err)
}
diff --git a/server/int/api/api.go b/server/int/api/api.go
index 7e37e8ac..5fc92b11 100644
--- a/server/int/api/api.go
+++ b/server/int/api/api.go
@@ -124,5 +124,5 @@ func (a *API) configureAPI() {
a.DewebAPI.GetResourceHandler = operations.GetResourceHandlerFunc(getResourceHandler)
a.DewebAPI.DefaultPageHandler = operations.DefaultPageHandlerFunc(defaultPageHandler)
- a.DewebAPI.GetDeWebInfoHandler = NewDewebInfo(a.Conf.MiscPublicInfoJson)
+ a.DewebAPI.GetDeWebInfoHandler = NewDewebInfo(a.Conf.MiscPublicInfoJson, a.Conf.NetworkInfos)
}
diff --git a/server/int/api/config/cache.go b/server/int/api/config/cache.go
index 1b3b73e6..ef43e8de 100644
--- a/server/int/api/config/cache.go
+++ b/server/int/api/config/cache.go
@@ -1,6 +1,10 @@
package config
-import "github.com/massalabs/station/pkg/logger"
+import (
+ "path/filepath"
+
+ "github.com/massalabs/station/pkg/logger"
+)
const (
// Default cache size limits
@@ -26,6 +30,7 @@ type YamlCacheConfig struct {
FileListCacheDurationSeconds *int `yaml:"file_list_cache_duration_seconds"`
}
+// DefaultCacheConfig returns a cache configuration with default values
func DefaultCacheConfig() CacheConfig {
return CacheConfig{
Enabled: true,
@@ -36,16 +41,42 @@ func DefaultCacheConfig() CacheConfig {
}
}
-// ConvertYamlCacheConfig converts a yamlCacheConfig to a CacheConfig
-func ConvertYamlCacheConfig(yamlConf *YamlCacheConfig) CacheConfig {
+// resolveCachePath resolves the cache path based on the configuration
+// - If path is absolute, it's used as-is
+// - If path is relative and from config file, it's resolved relative to the config file
+func resolveCachePath(cachePath string, configPath string) string {
+ if filepath.IsAbs(cachePath) {
+ return cachePath
+ }
+
+ if configPath != "" {
+ configDir := filepath.Dir(configPath)
+ return filepath.Join(configDir, cachePath)
+ }
+
+ return cachePath
+}
+
+// ProcessCacheConfig processes YAML config into a ready-to-use CacheConfig
+// It handles defaults, applies overrides from the YAML config, and resolves paths
+func ProcessCacheConfig(yamlConf *YamlCacheConfig, configPath string) CacheConfig {
config := DefaultCacheConfig()
- if yamlConf == nil {
- logger.Debugf("ConvertYamlCacheConfig: yamlConf is nil, returning default config")
- return config
+ // Apply YAML configuration if provided
+ if yamlConf != nil {
+ applyYamlOverrides(&config, yamlConf)
+ } else {
+ logger.Debugf("ProcessCacheConfig: using default cache configuration")
}
- // Only override values that are explicitly set in the YAML
+ // Resolve cache directory path
+ config.DiskCacheDir = resolveCachePath(config.DiskCacheDir, configPath)
+
+ return config
+}
+
+// applyYamlOverrides applies non-nil YAML settings to the cache config
+func applyYamlOverrides(config *CacheConfig, yamlConf *YamlCacheConfig) {
if yamlConf.Enabled != nil {
config.Enabled = *yamlConf.Enabled
}
@@ -65,6 +96,4 @@ func ConvertYamlCacheConfig(yamlConf *YamlCacheConfig) CacheConfig {
if yamlConf.FileListCacheDurationSeconds != nil {
config.FileListCacheDurationSeconds = *yamlConf.FileListCacheDurationSeconds
}
-
- return config
}
diff --git a/server/int/api/config/config.go b/server/int/api/config/config.go
index 8bb4c71b..26a1e411 100644
--- a/server/int/api/config/config.go
+++ b/server/int/api/config/config.go
@@ -27,15 +27,15 @@ type ServerConfig struct {
CacheConfig CacheConfig
}
-type yamlServerConfig struct {
- Domain string `yaml:"domain"`
- NetworkNodeURL string `yaml:"network_node_url"`
- APIPort int `yaml:"api_port"`
- AllowList []string `yaml:"allow_list"`
- BlockList []string `yaml:"block_list"`
- MiscPublicInfoJson interface{} `yaml:"misc_public_info"`
- CacheConfig *YamlCacheConfig `yaml:"cache"`
- AllowOffline bool `yaml:"allow_offline"`
+type YamlServerConfig struct {
+ Domain *string `yaml:"domain,omitempty"`
+ NetworkNodeURL *string `yaml:"network_node_url,omitempty"`
+ APIPort *int `yaml:"api_port,omitempty"`
+ AllowList []string `yaml:"allow_list,omitempty"`
+ BlockList []string `yaml:"block_list,omitempty"`
+ MiscPublicInfoJson interface{} `yaml:"misc_public_info,omitempty"`
+ CacheConfig *YamlCacheConfig `yaml:"cache,omitempty"`
+ AllowOffline bool `yaml:"allow_offline,omitempty"`
}
func DefaultConfig() (*ServerConfig, error) {
@@ -62,7 +62,14 @@ func LoadServerConfig(configPath string) (*ServerConfig, error) {
}
if _, err := os.Stat(configPath); os.IsNotExist(err) {
- return DefaultConfig()
+ defaultConfig, err := DefaultConfig()
+ if err != nil {
+ return nil, fmt.Errorf("failed to create default config: %w", err)
+ }
+ // Process cache config with empty configPath for defaults
+ defaultConfig.CacheConfig = ProcessCacheConfig(nil, "")
+
+ return defaultConfig, nil
}
filebytes, err := utils.ReadFileBytes(configPath)
@@ -70,27 +77,31 @@ func LoadServerConfig(configPath string) (*ServerConfig, error) {
return nil, fmt.Errorf("failed to read file bytes: %w", err)
}
- var yamlConf yamlServerConfig
+ var yamlConf YamlServerConfig
err = yaml.Unmarshal(filebytes, &yamlConf)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal YAML data: %w", err)
}
+ domain := DefaultDomain
+ networkNodeURL := DefaultNetworkNodeURL
+ apiPort := DefaultAPIPort
+
// Set default values if not specified in the YAML file
- if yamlConf.Domain == "" {
- yamlConf.Domain = DefaultDomain
+ if yamlConf.Domain != nil {
+ domain = *yamlConf.Domain
}
- if yamlConf.NetworkNodeURL == "" {
- yamlConf.NetworkNodeURL = DefaultNetworkNodeURL
+ if yamlConf.NetworkNodeURL != nil {
+ networkNodeURL = *yamlConf.NetworkNodeURL
}
- if yamlConf.APIPort == 0 {
- yamlConf.APIPort = DefaultAPIPort
+ if yamlConf.APIPort != nil {
+ apiPort = *yamlConf.APIPort
}
- networkInfos, err := pkgConfig.NewNetworkConfig(yamlConf.NetworkNodeURL)
+ networkInfos, err := pkgConfig.NewNetworkConfig(networkNodeURL)
if err != nil {
if yamlConf.AllowOffline {
logger.Errorf("unable retrieve network config: %v", err)
@@ -100,15 +111,17 @@ func LoadServerConfig(configPath string) (*ServerConfig, error) {
}
}
- // Convert YAML config to ServerConfig
+ // Process cache configuration
+ cacheConfig := ProcessCacheConfig(yamlConf.CacheConfig, configPath)
+
config := &ServerConfig{
- Domain: yamlConf.Domain,
- APIPort: yamlConf.APIPort,
+ Domain: domain,
+ APIPort: apiPort,
NetworkInfos: networkInfos,
AllowList: yamlConf.AllowList,
BlockList: yamlConf.BlockList,
MiscPublicInfoJson: convertYamlMisc2Json(yamlConf.MiscPublicInfoJson),
- CacheConfig: ConvertYamlCacheConfig(yamlConf.CacheConfig),
+ CacheConfig: cacheConfig,
}
return config, nil
diff --git a/server/int/api/handlers.go b/server/int/api/handlers.go
index e0170107..25a7af54 100644
--- a/server/int/api/handlers.go
+++ b/server/int/api/handlers.go
@@ -32,11 +32,12 @@ func defaultPageHandler(params operations.DefaultPageParams) middleware.Responde
/*Handle get deweb public infos*/
type dewebInfo struct {
- miscInfo interface{}
+ miscInfo interface{}
+ networkInfo config.NetworkInfos
}
-func NewDewebInfo(miscInfo interface{}) operations.GetDeWebInfoHandler {
- return &dewebInfo{miscInfo: miscInfo}
+func NewDewebInfo(miscInfo interface{}, networkInfo config.NetworkInfos) operations.GetDeWebInfoHandler {
+ return &dewebInfo{miscInfo: miscInfo, networkInfo: networkInfo}
}
func (dI *dewebInfo) Handle(params operations.GetDeWebInfoParams) middleware.Responder {
@@ -49,6 +50,11 @@ func (dI *dewebInfo) Handle(params operations.GetDeWebInfoParams) middleware.Res
App: "deweb",
Version: config.Version,
Misc: dI.miscInfo,
+ Network: &models.DeWebInfoNetwork{
+ Network: dI.networkInfo.Network,
+ Version: dI.networkInfo.Version,
+ ChainID: int64(dI.networkInfo.ChainID),
+ },
}).WriteResponse(w, runtime)
})
}