diff --git a/.github/workflows/build-helper.yml b/.github/workflows/build-helper.yml index f627d8c8..acbcbe1c 100644 --- a/.github/workflows/build-helper.yml +++ b/.github/workflows/build-helper.yml @@ -2,13 +2,19 @@ # For more information on the macOS signing and notarization, see https://www.electron.build/code-signing and https://www.electron.build/configuration/mac # For more information on the Windows Code Signing, see https://docs.digicert.com/en/digicert-keylocker/ci-cd-integrations/plugins/github-custom-action-for-keypair-signing.html and https://docs.digicert.com/en/digicert-keylocker/signing-tools/sign-authenticode-with-electron-builder-using-ksp-integration.html -name: "Build Helper" -on: workflow_dispatch +name: Build Helper +run-name: Version ${{ github.ref_name }} +on: + push: + tags: + - "[0-9]+.[0-9]+.[0-9]+*" env: GO_VERSION: "1.22.5" NODE_VERSION: "22.5.1" jobs: runbuild: + outputs: + version: ${{ steps.set-version.outputs.WAVETERM_VERSION }} strategy: matrix: include: @@ -53,7 +59,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: ${{env.NODE_VERSION}} - - name: Install yarn + - name: Install Yarn run: | corepack enable yarn install @@ -62,9 +68,10 @@ jobs: with: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Set Version + + - name: "Set Version" id: set-version - run: echo "WAVETERM_VERSION=$(node "./version.cjs")" >> "$GITHUB_OUTPUT" + run: echo "WAVETERM_VERSION=$(task version)" >> "$GITHUB_OUTPUT" shell: bash # Windows Code Signing Setup @@ -90,7 +97,7 @@ jobs: - name: Setup Keylocker KSP (Windows only) if: matrix.platform == 'windows' run: | - curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o Keylockertools-windows-x64.msi + curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o Keylockertools-windows-x64.msi msiexec /i Keylockertools-windows-x64.msi /quiet /qn C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user smctl windows certsync diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index 40e2dac8..16b1b424 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -1,19 +1,27 @@ -# Workflow to manage bumping the package version and pushing it to the target branch. +# Workflow to manage bumping the package version and pushing it to the target branch with a new tag. +# This workflow uses a GitHub App to bypass branch protection and uses the GitHub API directly to ensure commits and tags are signed. +# For more information, see this doc: https://github.com/Nautilus-Cyberneering/pygithub/blob/main/docs/how_to_sign_automatic_commits_in_github_actions.md -name: Bump version +name: Bump Version +run-name: "branch: ${{ github.ref_name }}; version: ${{ inputs.bump }}; prerelease: ${{ inputs.is-prerelease }}" on: workflow_dispatch: inputs: bump: description: SemVer Bump required: true - default: beta type: choice + default: none options: - - beta + - none - patch - minor - major + is-prerelease: + description: Is Prerelease + required: true + type: boolean + default: true env: NODE_VERSION: "22.5.1" jobs: @@ -44,16 +52,32 @@ jobs: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: "Set Version: ${{ inputs.bump }}" - id: set-version - run: echo "WAVETERM_VERSION=$(task version:${{ inputs.bump }})" >> "$GITHUB_OUTPUT" + - name: "Bump Version: ${{ inputs.bump }}" + id: bump-version + run: echo "WAVETERM_VERSION=$( task version -- ${{ inputs.bump }} ${{inputs.is-prerelease}} )" >> "$GITHUB_OUTPUT" shell: bash - - name: "Push version bump: ${{ steps.set-version.outputs.WAVETERM_VERSION }}" + - name: "Push version bump: ${{ steps.bump-version.outputs.WAVETERM_VERSION }}" run: | - git config user.name wave-builder - git config user.email builds@commandline.dev - git add package.json - git commit -m "Bump version to ${{ steps.set-version.outputs.WAVETERM_VERSION }}" - git tag -a ${{ steps.set-version.outputs.WAVETERM_VERSION }} -m "Bump version to ${{ steps.set-version.outputs.WAVETERM_VERSION }}" - git push + # Create a new commit for the package version bump in package.json + export VERSION=${{ steps.bump-version.outputs.WAVETERM_VERSION }} + export MESSAGE="chore: bump package version to $VERSION" + export FILE=package.json + export BRANCH=${{github.ref_name}} + export SHA=$( git rev-parse $BRANCH:$FILE ) + export CONTENT=$( base64 -i $FILE ) + gh api --method PUT /repos/:owner/:repo/contents/$FILE \ + --field branch="$BRANCH" \ + --field message="$MESSAGE" \ + --field content="$CONTENT" \ + --field sha="$SHA" + + # Fetch the new commit and create a tag referencing it + git fetch + export TAG_SHA=$( git rev-parse origin/$BRANCH ) + gh api --method POST /repos/:owner/:repo/git/refs \ + --field ref="refs/tags/$VERSION" \ + --field sha="$TAG_SHA" + shell: bash + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/Taskfile.yml b/Taskfile.yml index 923db2d6..998f2367 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -200,6 +200,19 @@ tasks: - frontend/app/store/services.ts - frontend/app/store/wshserver.ts + version: + desc: Get the current package version, or bump version if args are present. + summary: | + If no arguments are present, the current version will be returned. + If only a single argument is given, the following are valid inputs: + - `none`: No-op. + - `patch`: Bumps the patch version. + - `minor`: Bumps the minor version. + - `major`: Bumps the major version. + - '1', 'true': Bumps the prerelease version. + If two arguments are given, the first argument must be either `none`, `patch`, `minor`, or `major`. The second argument must be `1` or `true` to bump the prerelease version. + cmd: node version.cjs {{.CLI_ARGS}} + yarn: desc: Runs `yarn` internal: true diff --git a/electron-builder.config.cjs b/electron-builder.config.cjs index f72f9369..8ff672a2 100644 --- a/electron-builder.config.cjs +++ b/electron-builder.config.cjs @@ -12,6 +12,7 @@ const config = { productName: pkg.productName, executableName: pkg.name, artifactName: "${productName}-${platform}-${arch}-${version}.${ext}", + generateUpdatesFilesForAllChannels: true, npmRebuild: false, nodeGypRebuild: false, electronCompile: false, diff --git a/emain/updater.ts b/emain/updater.ts index e75f9855..8665d0f9 100644 --- a/emain/updater.ts +++ b/emain/updater.ts @@ -29,6 +29,12 @@ export class Updater { autoUpdater.autoInstallOnAppQuit = settings["autoupdate:installonquit"]; + // Only update the release channel if it's specified, otherwise use the one configured in the artifact. + const channel = settings["autoupdate:channel"]; + if (channel) { + autoUpdater.channel = channel; + } + autoUpdater.removeAllListeners(); autoUpdater.on("error", (err) => { diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index c6130876..4eafb43a 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -410,6 +410,7 @@ declare global { "autoupdate:enabled"?: boolean; "autoupdate:intervalms"?: number; "autoupdate:installonquit"?: boolean; + "autoupdate:channel"?: string; "widget:*"?: boolean; "widget:showhelp"?: boolean; "window:*"?: boolean; diff --git a/package.json b/package.json index 62d3c66a..acadd80d 100644 --- a/package.json +++ b/package.json @@ -1,131 +1,133 @@ { - "name": "thenextwave", - "author": { - "name": "Command Line Inc", - "email": "info@commandline.dev" - }, - "productName": "TheNextWave", - "description": "An Open-Source, AI-Native, Terminal Built for Seamless Workflows", - "license": "Apache-2.0", - "version": "0.1.12", - "homepage": "https://waveterm.dev", - "build": { - "appId": "dev.commandline.thenextwave" - }, - "private": true, - "main": "./dist/main/index.js", - "type": "module", - "scripts": { - "dev": "electron-vite dev", - "start": "electron-vite preview", - "build:dev": "electron-vite build --mode development", - "build:prod": "electron-vite build --mode production", - "storybook": "storybook dev -p 6006 --no-open", - "build-storybook": "storybook build", - "coverage": "vitest run --coverage", - "test": "vitest", - "postinstall": "electron-builder install-app-deps" - }, - "devDependencies": { - "@chromatic-com/storybook": "^2.0.2", - "@eslint/js": "^9.10.0", - "@rollup/plugin-node-resolve": "^15.2.3", - "@storybook/addon-essentials": "^8.3.0", - "@storybook/addon-interactions": "^8.3.0", - "@storybook/addon-links": "^8.3.0", - "@storybook/blocks": "^8.3.0", - "@storybook/react": "^8.3.0", - "@storybook/react-vite": "^8.3.0", - "@storybook/test": "^8.3.0", - "@types/css-tree": "^2", - "@types/debug": "^4", - "@types/electron": "^1.6.10", - "@types/node": "^22.5.4", - "@types/papaparse": "^5", - "@types/pngjs": "^6.0.5", - "@types/react": "^18.3.5", - "@types/react-dom": "^18.3.0", - "@types/shell-quote": "^1", - "@types/sprintf-js": "^1", - "@types/throttle-debounce": "^5", - "@types/tinycolor2": "^1", - "@types/uuid": "^10.0.0", - "@vitejs/plugin-react-swc": "^3.7.0", - "@vitest/coverage-istanbul": "^2.1.1", - "electron": "^32.1.0", - "electron-builder": "^25.0.5", - "electron-vite": "^2.3.0", - "eslint": "^9.10.0", - "eslint-config-prettier": "^9.1.0", - "less": "^4.2.0", - "prettier": "^3.3.3", - "prettier-plugin-jsdoc": "^1.3.0", - "prettier-plugin-organize-imports": "^4.0.0", - "rollup-plugin-flow": "^1.1.1", - "storybook": "^8.3.0", - "ts-node": "^10.9.2", - "tslib": "^2.6.3", - "tsx": "^4.19.1", - "typescript": "^5.6.2", - "typescript-eslint": "^8.5.0", - "vite": "^5.4.6", - "vite-plugin-image-optimizer": "^1.1.8", - "vite-plugin-static-copy": "^1.0.6", - "vite-plugin-svgr": "^4.2.0", - "vite-tsconfig-paths": "^5.0.1", - "vitest": "^2.1.1" - }, - "dependencies": { - "@monaco-editor/loader": "^1.4.0", - "@monaco-editor/react": "^4.6.0", - "@observablehq/plot": "^0.6.16", - "@react-hook/resize-observer": "^2.0.2", - "@table-nav/core": "^0.0.7", - "@table-nav/react": "^0.0.7", - "@tanstack/react-table": "^8.20.5", - "@types/color": "^3.0.6", - "@xterm/addon-fit": "^0.10.0", - "@xterm/addon-serialize": "^0.13.0", - "@xterm/addon-web-links": "^0.11.0", - "@xterm/addon-webgl": "^0.18.0", - "@xterm/xterm": "^5.5.0", - "base64-js": "^1.5.1", - "clsx": "^2.1.1", - "color": "^4.2.3", - "css-tree": "^3.0.0", - "dayjs": "^1.11.13", - "debug": "^4.3.7", - "electron-updater": "6.3.4", - "fast-average-color": "^9.4.0", - "htl": "^0.3.1", - "html-to-image": "^1.11.11", - "immer": "^10.1.1", - "jotai": "^2.9.3", - "monaco-editor": "^0.51.0", - "overlayscrollbars": "^2.10.0", - "overlayscrollbars-react": "^0.5.6", - "papaparse": "^5.4.1", - "pngjs": "^7.0.0", - "react": "^18.3.1", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^18.3.1", - "react-frame-component": "^5.2.7", - "react-gauge-chart": "^0.5.1", - "react-markdown": "^9.0.1", - "rehype-highlight": "^7.0.0", - "rehype-raw": "^7.0.0", - "rehype-sanitize": "^6.0.0", - "rehype-slug": "^6.0.0", - "remark-flexible-toc": "^1.1.1", - "remark-gfm": "^4.0.0", - "rxjs": "^7.8.1", - "shell-quote": "^1.8.1", - "sprintf-js": "^1.1.3", - "throttle-debounce": "^5.0.2", - "tinycolor2": "^1.6.0", - "use-device-pixel-ratio": "^1.1.2", - "winston": "^3.14.2" - }, - "packageManager": "yarn@4.4.1" + "name": "thenextwave", + "author": { + "name": "Command Line Inc", + "email": "info@commandline.dev" + }, + "productName": "TheNextWave", + "description": "An Open-Source, AI-Native, Terminal Built for Seamless Workflows", + "license": "Apache-2.0", + "version": "0.1.13-beta.20", + "homepage": "https://waveterm.dev", + "build": { + "appId": "dev.commandline.thenextwave" + }, + "private": true, + "main": "./dist/main/index.js", + "type": "module", + "scripts": { + "dev": "electron-vite dev", + "start": "electron-vite preview", + "build:dev": "electron-vite build --mode development", + "build:prod": "electron-vite build --mode production", + "storybook": "storybook dev -p 6006 --no-open", + "build-storybook": "storybook build", + "coverage": "vitest run --coverage", + "test": "vitest", + "postinstall": "electron-builder install-app-deps" + }, + "devDependencies": { + "@chromatic-com/storybook": "^2.0.2", + "@eslint/js": "^9.10.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@storybook/addon-essentials": "^8.3.0", + "@storybook/addon-interactions": "^8.3.0", + "@storybook/addon-links": "^8.3.0", + "@storybook/blocks": "^8.3.0", + "@storybook/react": "^8.3.0", + "@storybook/react-vite": "^8.3.0", + "@storybook/test": "^8.3.0", + "@types/css-tree": "^2", + "@types/debug": "^4", + "@types/electron": "^1.6.10", + "@types/node": "^22.5.4", + "@types/papaparse": "^5", + "@types/pngjs": "^6.0.5", + "@types/react": "^18.3.5", + "@types/react-dom": "^18.3.0", + "@types/semver": "^7", + "@types/shell-quote": "^1", + "@types/sprintf-js": "^1", + "@types/throttle-debounce": "^5", + "@types/tinycolor2": "^1", + "@types/uuid": "^10.0.0", + "@vitejs/plugin-react-swc": "^3.7.0", + "@vitest/coverage-istanbul": "^2.1.1", + "electron": "^32.1.0", + "electron-builder": "^25.0.5", + "electron-vite": "^2.3.0", + "eslint": "^9.10.0", + "eslint-config-prettier": "^9.1.0", + "less": "^4.2.0", + "prettier": "^3.3.3", + "prettier-plugin-jsdoc": "^1.3.0", + "prettier-plugin-organize-imports": "^4.0.0", + "rollup-plugin-flow": "^1.1.1", + "semver": "^7.6.3", + "storybook": "^8.3.0", + "ts-node": "^10.9.2", + "tslib": "^2.6.3", + "tsx": "^4.19.1", + "typescript": "^5.6.2", + "typescript-eslint": "^8.5.0", + "vite": "^5.4.6", + "vite-plugin-image-optimizer": "^1.1.8", + "vite-plugin-static-copy": "^1.0.6", + "vite-plugin-svgr": "^4.2.0", + "vite-tsconfig-paths": "^5.0.1", + "vitest": "^2.1.1" + }, + "dependencies": { + "@monaco-editor/loader": "^1.4.0", + "@monaco-editor/react": "^4.6.0", + "@observablehq/plot": "^0.6.16", + "@react-hook/resize-observer": "^2.0.2", + "@table-nav/core": "^0.0.7", + "@table-nav/react": "^0.0.7", + "@tanstack/react-table": "^8.20.5", + "@types/color": "^3.0.6", + "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-serialize": "^0.13.0", + "@xterm/addon-web-links": "^0.11.0", + "@xterm/addon-webgl": "^0.18.0", + "@xterm/xterm": "^5.5.0", + "base64-js": "^1.5.1", + "clsx": "^2.1.1", + "color": "^4.2.3", + "css-tree": "^3.0.0", + "dayjs": "^1.11.13", + "debug": "^4.3.7", + "electron-updater": "6.3.4", + "fast-average-color": "^9.4.0", + "htl": "^0.3.1", + "html-to-image": "^1.11.11", + "immer": "^10.1.1", + "jotai": "^2.9.3", + "monaco-editor": "^0.51.0", + "overlayscrollbars": "^2.10.0", + "overlayscrollbars-react": "^0.5.6", + "papaparse": "^5.4.1", + "pngjs": "^7.0.0", + "react": "^18.3.1", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", + "react-dom": "^18.3.1", + "react-frame-component": "^5.2.7", + "react-gauge-chart": "^0.5.1", + "react-markdown": "^9.0.1", + "rehype-highlight": "^7.0.0", + "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", + "rehype-slug": "^6.0.0", + "remark-flexible-toc": "^1.1.1", + "remark-gfm": "^4.0.0", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "sprintf-js": "^1.1.3", + "throttle-debounce": "^5.0.2", + "tinycolor2": "^1.6.0", + "use-device-pixel-ratio": "^1.1.2", + "winston": "^3.14.2" + }, + "packageManager": "yarn@4.4.1" } diff --git a/pkg/wconfig/metaconsts.go b/pkg/wconfig/metaconsts.go index 92c31ae3..ea4c5908 100644 --- a/pkg/wconfig/metaconsts.go +++ b/pkg/wconfig/metaconsts.go @@ -32,6 +32,7 @@ const ( ConfigKey_AutoUpdateEnabled = "autoupdate:enabled" ConfigKey_AutoUpdateIntervalMs = "autoupdate:intervalms" ConfigKey_AutoUpdateInstallOnQuit = "autoupdate:installonquit" + ConfigKey_AutoUpdateChannel = "autoupdate:channel" ConfigKey_WidgetClear = "widget:*" ConfigKey_WidgetShowHelp = "widget:showhelp" diff --git a/pkg/wconfig/settingsconfig.go b/pkg/wconfig/settingsconfig.go index 7f1da030..f3de678a 100644 --- a/pkg/wconfig/settingsconfig.go +++ b/pkg/wconfig/settingsconfig.go @@ -64,6 +64,7 @@ type SettingsType struct { AutoUpdateEnabled bool `json:"autoupdate:enabled,omitempty"` AutoUpdateIntervalMs float64 `json:"autoupdate:intervalms,omitempty"` AutoUpdateInstallOnQuit bool `json:"autoupdate:installonquit,omitempty"` + AutoUpdateChannel string `json:"autoupdate:channel,omitempty"` WidgetClear bool `json:"widget:*,omitempty"` WidgetShowHelp bool `json:"widget:showhelp,omitempty"` diff --git a/scripts/artifacts/README.md b/scripts/artifacts/README.md index d5d315fe..58796d81 100644 --- a/scripts/artifacts/README.md +++ b/scripts/artifacts/README.md @@ -1,9 +1,18 @@ # Building for release +## Bump Version workflow + +All releases start by first bumping the package version and creating a new Git tag. We have a workflow set up to automate this. + +To run it, trigger a new run of the [Bump Version workflow](https://github.com/wavetermdev/thenextwave/actions/workflows/bump-version.yml). When triggering the run, you will be +prompted to select a version bump type, either `none`, `patch`, `minor`, or `major`, and whether the version is prerelease or not. This determines how much the version number is incremented. +See [`version.cjs`](../../version.cjs) for more details on how this works. + +Once the tag has been created, a new [Build Helper](#build-helper-workflow) run will be automatically queued to generate the artifacts. + ## Build Helper workflow -Our release builds are managed by the "Build Helper" GitHub Action, which is defined -in [`build-helper.yml`](../../.github/workflows/build-helper.yml). +Our release builds are managed by the [Build Helper workflow](https://github.com/wavetermdev/thenextwave/actions/workflows/build-helper.yml). Under the hood, this will call the `package` task in [`Taskfile.yml`](../../Taskfile.yml), which will build the Electron codebase using @@ -34,6 +43,11 @@ With each release, `latest-mac.yml`, `latest-linux.yml`, and `latest-linux-arm64 newest release. These also include file sizes and checksums to aid in validating the packages. The app will check these files in our S3 bucket every hour to see if a new version is available. +### Update channels + +We utilize update channels to roll out beta and stable releases. These are determined based on the package versioning [described above](#bump-version-workflow). Users can +select their update channel using the `autoupdate:channel` setting in Wave. See [here](https://www.electron.build/tutorials/release-using-channels.html) for more information. + ### Homebrew Homebrew is automatically bumped when new artifacts are published. diff --git a/scripts/artifacts/publish-from-staging.sh b/scripts/artifacts/publish-from-staging.sh index a1785a7c..5b8e433f 100755 --- a/scripts/artifacts/publish-from-staging.sh +++ b/scripts/artifacts/publish-from-staging.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Takes a release from our staging bucket and publishes it to the public download bucket. # Usage: publish-from-staging.sh # Example: publish-from-staging.sh 0.1.0 storage diff --git a/version.cjs b/version.cjs index 512131da..fca8c93e 100644 --- a/version.cjs +++ b/version.cjs @@ -1,9 +1,57 @@ +/** + * Script to get the current package version and bump the version, if specified. + * + * If no arguments are present, the current version will returned. + * If only a single argument is given, the following are valid inputs: + * - `none`: No-op. + * - `patch`: Bumps the patch version. + * - `minor`: Bumps the minor version. + * - `major`: Bumps the major version. + * - '1', 'true': Bumps the prerelease version. + * If two arguments are given, the first argument must be either `none`, `patch`, `minor`, or `major`. The second argument must be `1` or `true` to bump the prerelease version. + */ + const path = require("path"); -const packageJson = require(path.resolve(__dirname, "package.json")); +const packageJsonPath = path.resolve(__dirname, "package.json"); +const packageJson = require(packageJsonPath); const VERSION = `${packageJson.version}`; module.exports = VERSION; if (typeof require !== "undefined" && require.main === module) { - console.log(VERSION); + if (process.argv.length > 2) { + const fs = require("fs"); + const semver = require("semver"); + + const action = process.argv[2]; + const isPrerelease = + process.argv.length > 3 + ? process.argv[3] === "true" || process.argv[3] === "1" + : action === "true" || action === "1"; + let newVersion = packageJson.version; + switch (action) { + case "major": + case "minor": + case "patch": + newVersion = semver.inc( + VERSION, + `${isPrerelease ? "pre" : ""}${action}`, + null, + isPrerelease ? "beta" : null + ); + break; + case "none": + case "true": + case "1": + if (isPrerelease) newVersion = semver.inc(VERSION, "prerelease", null, "beta"); + break; + default: + throw new Error(`Unknown action ${action}`); + } + packageJson.version = newVersion; + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 4) + "\n"); + console.log(newVersion); + } else { + console.log(VERSION); + } } diff --git a/yarn.lock b/yarn.lock index eff1e917..2f517643 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2496,6 +2496,13 @@ __metadata: languageName: node linkType: hard +"@types/semver@npm:^7": + version: 7.5.8 + resolution: "@types/semver@npm:7.5.8" + checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa + languageName: node + linkType: hard + "@types/send@npm:*": version: 0.17.4 resolution: "@types/send@npm:0.17.4" @@ -10208,6 +10215,7 @@ __metadata: "@types/pngjs": "npm:^6.0.5" "@types/react": "npm:^18.3.5" "@types/react-dom": "npm:^18.3.0" + "@types/semver": "npm:^7" "@types/shell-quote": "npm:^1" "@types/sprintf-js": "npm:^1" "@types/throttle-debounce": "npm:^5" @@ -10261,6 +10269,7 @@ __metadata: remark-gfm: "npm:^4.0.0" rollup-plugin-flow: "npm:^1.1.1" rxjs: "npm:^7.8.1" + semver: "npm:^7.6.3" shell-quote: "npm:^1.8.1" sprintf-js: "npm:^1.1.3" storybook: "npm:^8.3.0"