Compare commits

..
Author SHA1 Message Date
Andy Lee 55bb650b0a feat: add rancher-k3k addon UI
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-31 16:19:39 +08:00
Andy Lee d550046189 fix: vGPU doesn't display when creating harvester downstream cluster (#1123)
* fix: vGPU doesn't display in edit VM page

Signed-off-by: Andy Lee <andy.lee@suse.com>

* fix: align PCI passthrough action buttons height

Match the enable/disable passthrough and Actions dropdown buttons to 40px and move the selected-count label below them.

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-25 16:24:37 +08:00
Po Han Huang 5f32a9ade5 feat: improve readability for vm migration UI (#1112)
Signed-off-by: pohanhuang <pohan.huang@suse.com>
2026-08-18 12:32:02 +08:00
marcelofukumoto 3f39d561be security: fix 15 transitive Dependabot alerts via yarn.lock re-resolution (#1115)
Re-resolve 15 vulnerable transitive dependencies to their latest in-range
patched versions in yarn.lock only (no package.json changes): @babel/core,
@babel/plugin-transform-modules-systemjs, brace-expansion, fast-uri, form-data,
http-proxy-middleware, immutable, joi, js-cookie, launch-editor, nanoid,
shell-quote, svgo, websocket-driver, ws.

The remaining alerts are pinned by parent packages (chiefly @rancher/shell) or
need a cross-major bump, so they can't be resolved by the lockfile alone; they
are tracked separately.

Signed-off-by: Marcelo Fukumoto <marceloyfukumoto@gmail.com>
2026-08-17 10:30:05 +02:00
Andy Lee 7c84269ed6 fix: exclude forklift namespace from system classification (#1114)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-17 15:23:23 +10:00
Andy Lee f63be73b11 fix: ACCESS_MODE undefined error in forklift mapping page (#1111) 2026-08-14 10:01:27 +08:00
Andy Lee fabc1cec21 fix: prevent duplicated cloud-config for empty user data (#1109)
* fix: prevent duplicated cloud-config for empty user data

Signed-off-by: Andy Lee <andy.lee@suse.com>

* refactor: AI review

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-12 14:20:25 +08:00
Andy Lee ed2b6f2551 fix: remove map ownerref writeback and delete maps via plan refs (#1105)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-10 18:14:48 +08:00
renovate[bot] 72282ae50e deps: update dependency @types/lodash to v4.17.25 (#1099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-10 10:10:31 +08:00
Volker Theile 8fba4e7050 fix: Rename to Windows Unattended & Sysprep (#1096)
Rename title and other mentions of Sysprep to prevent misunderstandings regarding functionality. For the average Windows administrator who simply wants to automate the installation of a Windows ISO, it isn't immediately intuitive when the term “Sysprep” is used on its own.

Related to: https://github.com/harvester/harvester/issues/1836
Related to: https://github.com/harvester/harvester-ui-extension/pull/954

Signed-off-by: Volker Theile <vtheile@suse.com>
2026-08-07 11:22:23 +08:00
Andy Lee fbd4a1108f ci: add test steps in PR template (#1093)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-05 15:53:40 +08:00
Andy Lee 823a88c85b ci: not use actions/github-script@v7 (#1092)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-05 14:37:24 +08:00
Andy Lee 3a2c6e3a3d ci: support manual standalone build trigger and PR comment (#1091)
* ci: add manually trigger build

Signed-off-by: Andy Lee <andy.lee@suse.com>

* refactor: PR comment

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-05 14:21:41 +08:00
Andy Lee be6e9b5f06 fix(subnet): support None option for VLAN dropdown (#1085)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-05 12:23:57 +08:00
Andy Lee a88bed9eb4 fix: clear keyword should clear highlight (#1088)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-05 00:24:34 +08:00
Andy Lee f97f14c811 deps: bump @rancher/shell to 3.0.12-rc.7 (#1084)
* deps: update rancher/shell to 3.0.12-rc.7

Signed-off-by: Andy Lee <andy.lee@suse.com>

* fix: move filter labels/schedule buttons position

Signed-off-by: Andy Lee <andy.lee@suse.com>

* feat: improve serial console

Signed-off-by: Andy Lee <andy.lee@suse.com>

* feat(serial-console): add terminal search toolbar and highlight tuning

Signed-off-by: Andy Lee <andy.lee@suse.com>

* refactor: clear searach result after close

Signed-off-by: Andy Lee <andy.lee@suse.com>

* refactor: fine tune serial console

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-04 19:53:24 +08:00
Andy Lee f8a29a1c54 fix: project resource quota not set in POST API payload (#1082)
* fix: project resource quota not set in POST API payload

Signed-off-by: Andy Lee <andy.lee@suse.com>

* fix: guard onQuotasInput against undefined payload

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-04 13:27:30 +08:00
freeze 9a935434c2 feat: support attaching volumes as shareable disks (#1026)
Show a Shareable checkbox when attaching an existing volume whose
    PVC is RWX Block and not provisioned by Longhorn. Checking it sets
    disk.shareable and shows a data-corruption warning banner.

    Related issue: harvester/harvester#9650

Signed-off-by: Vicente Cheng <vicente.cheng@suse.com>
2026-08-03 16:24:33 +08:00
Andy Lee afe632b723 feat: add access/volume mode support and improvements for VM migration (#1074)
* fix: VM migration tab not appear when first login

Signed-off-by: Andy Lee <andy.lee@suse.com>

* feat: add access/ volume mode model

Signed-off-by: Andy Lee <andy.lee@suse.com>

* feat: add selected all | clear all actions for selected VM

Signed-off-by: Andy Lee <andy.lee@suse.com>

* fix: bugs in VM selection table

Signed-off-by: Andy Lee <andy.lee@suse.com>

* refactor: copilot review

Signed-off-by: Andy Lee <andy.lee@suse.com>

---------

Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-08-03 12:58:37 +08:00
renovate[bot] d050f21f9a deps: update patch dependencies (#1075)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-03 12:58:04 +08:00
Andy Lee b87f367583 fix: prevent VM detail page hang when static IP assigned (#1072)
Make syncStaticIpAnnotations idempotent so it no longer mutates VM
annotations when already in sync, breaking a reactive update loop that
froze the browser on the VM detail page. Also stack multiple IP
addresses vertically in the IP Address column.

Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-31 16:55:25 +08:00
Andy Lee 92aa8ec603 ci: disable footer-max-line-length rule (#1069)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-29 14:46:07 +08:00
Alejandro BonillaandVolker Theile e013534373 feat: allow Cloud Config for Windows guests (Cloudbase-Init) (#984)
* feat(vm): allow Cloud Config for Windows guests

Currently the Cloud Config editor is completely hidden when a VM's
OS Type is set to Windows, and no `cloudinitdisk` volume or secret
is written to the VM spec even if the user has entered content.

This blocks a common Windows-on-Harvester workflow: using
Cloudbase-Init to run first-boot configuration (install VMDP,
enable OpenSSH server, add authorized_keys, join a domain, run
`runcmd` steps, etc.). Users on Windows today have to hand-edit the
VM YAML after creation to attach a cloudinitdisk, which is fragile
and undiscoverable.

Changes:
- Remove the `v-if="!isWindows"` on the CloudConfig component so
  the editor is available for every OS type. The Sysprep editor
  continues to render only for Windows, so Windows now gets both
  panels; Linux is unchanged.
- Drop the `if (!this.isWindows)` guard around the cloudinitdisk
  volume serialization so the disk is attached whenever the user
  provided user-data or network-data, regardless of OS.
- Drop the `|| this.isWindows` early-return in `saveSecret` so the
  cloud-init secret is persisted for Windows VMs too.
- `getInitUserData` now returns a bare `#cloud-config\n` header for
  Windows instead of the Linux qemu-guest-agent runcmd template
  (which would fail on Cloudbase-Init). VMDP installs the QGA on
  Windows as a native service, so the runcmd path is not needed.

The "Install guest agent" checkbox stays disabled for Windows
because it specifically drives the Linux QGA-via-runcmd recipe.

Fixes point 2 of harvester/harvester#11124.

Signed-off-by: Alejandro Bonilla <abonilla@suse.com>

* fix(vm): do not clear cloud-config on isWindows watcher fire

The isWindows watcher runs on every change of the isWindows computed
property, including on mount when editing an existing Windows VM. Setting
this['userScript'] and this['networkScript'] to undefined at that point
wipes the cloud-config data that was just loaded from the existing VM's
cloudinit secret, so the Advanced -> Cloud Config editor shows empty
even though the VM has data on the wire.

Remove the two clears - Cloud Config is now supported for Windows guests
by this PR, so there is no reason to unset the user/network scripts when
the OS type is Windows. sshKey and installAgent remain gated (SSH keys
are injected via cloud-init on Linux only, and the qemu-guest-agent
package install checkbox does not apply to Windows).

Signed-off-by: Alejandro Bonilla <abonilla@suse.com>
Co-authored-by: Volker Theile <vtheile@suse.com>

* refactor(vm): Get CloudInit working for VM templates

- Remove unnecessary osType watcher in cloud config. The watcher (see `pkg/harvester/edit/kubevirt.io.virtualmachine/VirtualMachineCloudConfig/DataTemplate.vue`) that cleared the cloud config template for Windows VMs is no longer necessary.
- Automatically strip the CloudInit `User Data` properties `package_update` and `packages` for OS type `Windows`.

Signed-off-by: Volker Theile <vtheile@suse.com>

---------

Signed-off-by: Alejandro Bonilla <abonilla@suse.com>
Signed-off-by: Volker Theile <vtheile@suse.com>
Co-authored-by: Volker Theile <vtheile@suse.com>
2026-07-28 11:32:30 +02:00
Andy Lee d23c3d3b72 revert: PR build artifact (#1066)
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-28 15:39:20 +08:00
38 changed files with 1954 additions and 549 deletions
+7 -3
View File
@@ -8,12 +8,16 @@
- [ ] Yes, the backend owner is @ - [ ] Yes, the backend owner is @
- [ ] No, frontend-only. - [ ] No, frontend-only.
### Related Issue # ### Related Issue
<!-- Link the issue as harvester/harvester#<issue_number> --> <!-- Link the issue as harvester/harvester#<issue_number> -->
harvester/harvester#<issue_number> harvester/harvester#<issue_number>
### Test screenshot or video (Required) ### Test Steps
<!-- Attach screenshot or video of the changes and eventual comparison if you find it necessary --> <!-- Provide the test steps for reviewer to verify the issue -->
1. Go to ... page
### Test screenshot or video
<!-- Attach screenshot or video of the changes and eventual comparison -->
@@ -1,6 +1,7 @@
name: Build Standalone on PR Merge name: Build Standalone on PR Merge
on: on:
workflow_dispatch: # manually trigger on Github Actions only support PR branch pushed to harvester/harvester-ui-extension
push: push:
branches: branches:
- main - main
@@ -13,23 +14,13 @@ on:
- '*-dev' - '*-dev'
types: types:
- merged - merged
- labeled
jobs: jobs:
build-validation: build-validation:
name: Build Test name: Build Test
# allow triggering when a PR is labeled with "build-artifact"
if: >-
github.event_name != 'pull_request' ||
github.event.action != 'labeled' ||
github.event.label.name == 'build-artifact'
uses: ./.github/workflows/run-lint.yaml uses: ./.github/workflows/run-lint.yaml
build: build:
name: Build and Upload Package name: Build and Upload Package
if: >-
github.event_name != 'pull_request' ||
github.event.action != 'labeled' ||
github.event.label.name == 'build-artifact'
uses: ./.github/workflows/build-and-publish-standalone.yaml uses: ./.github/workflows/build-and-publish-standalone.yaml
needs: needs:
- build-validation - build-validation
@@ -38,4 +29,36 @@ jobs:
packages: write packages: write
id-token: write id-token: write
with: with:
CI_BRANCH: ${{ github.event_name == 'push' && github.ref_name || '' }} CI_BRANCH: ${{github.ref_name}}
comment-on-pr:
name: Comment build result on PR
# Only for manual runs so the existing push / pull_request flows are untouched.
if: github.event_name == 'workflow_dispatch'
needs:
- build
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Add build artifact comment to the PR opened from this branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: ${{ github.ref_name }}
run: |
set -euo pipefail
# Match scripts/version: sanitize the branch and map main -> latest.
if [ "$BRANCH" = "main" ]; then
DIR="latest"
else
DIR=$(printf '%s' "$BRANCH" | sed -E 's/[^a-zA-Z0-9.-]+/-/g')
fi
URL="https://releases.rancher.com/harvester-ui/dashboard/${DIR}/index.html"
PR=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --base main --head "$BRANCH" --json number --jq '.[0].number // empty')
if [ -z "$PR" ]; then
echo "No open PR found for branch $BRANCH, skipping comment."
exit 0
fi
BODY=$(printf '🚀 **UI Artifact Build Status:** `SUCCESS` ✅\n> ui-index: %s\n> ui-source: external' "$URL")
gh pr comment "$PR" --repo "$GITHUB_REPOSITORY" --body "$BODY"
+1 -1
View File
@@ -36,7 +36,7 @@ module.exports = {
'body-leading-blank': [2, 'always'], 'body-leading-blank': [2, 'always'],
'body-max-line-length': [0, 'always', 100], 'body-max-line-length': [0, 'always', 100],
'footer-leading-blank': [2, 'always'], 'footer-leading-blank': [2, 'always'],
'footer-max-line-length': [2, 'always', 100], 'footer-max-line-length': [0, 'always', 100],
}, },
// Ignore merge commits and revert commits // Ignore merge commits and revert commits
ignores: [ ignores: [
+9 -3
View File
@@ -7,7 +7,7 @@
}, },
"dependencies": { "dependencies": {
"@babel/plugin-transform-class-static-block": "7.29.7", "@babel/plugin-transform-class-static-block": "7.29.7",
"@rancher/shell": "3.0.12-rc.3", "@rancher/shell": "3.0.12-rc.7",
"@vue-flow/background": "^1.3.0", "@vue-flow/background": "^1.3.0",
"@vue-flow/controls": "^1.1.1", "@vue-flow/controls": "^1.1.1",
"@vue-flow/core": "^1.33.5", "@vue-flow/core": "^1.33.5",
@@ -15,6 +15,12 @@
"cache-loader": "^4.1.0", "cache-loader": "^4.1.0",
"color": "4.2.3", "color": "4.2.3",
"ip": "2.0.1", "ip": "2.0.1",
"@xterm/xterm": "6.0.0",
"@xterm/addon-fit": "0.11.0",
"@xterm/addon-search": "0.16.0",
"@xterm/addon-web-links": "0.12.0",
"@xterm/addon-webgl": "0.19.0",
"@xterm/addon-canvas": "0.7.0",
"node-polyfill-webpack-plugin": "^3.0.0", "node-polyfill-webpack-plugin": "^3.0.0",
"elkjs": "^0.11.0", "elkjs": "^0.11.0",
"vue-draggable-next": "^2.2.1", "vue-draggable-next": "^2.2.1",
@@ -29,12 +35,12 @@
"glob": "7.2.3", "glob": "7.2.3",
"glob-parent": "6.0.2", "glob-parent": "6.0.2",
"json5": "2.2.3", "json5": "2.2.3",
"@types/lodash": "4.17.24", "@types/lodash": "4.17.25",
"merge": "2.1.1", "merge": "2.1.1",
"node-forge": "1.4.0", "node-forge": "1.4.0",
"nth-check": "2.1.1", "nth-check": "2.1.1",
"qs": "6.15.3", "qs": "6.15.3",
"roarr": "7.21.6", "roarr": "7.21.7",
"semver": "7.8.5", "semver": "7.8.5",
"@vue/cli-service/html-webpack-plugin": "^5.0.0" "@vue/cli-service/html-webpack-plugin": "^5.0.0"
}, },
+5
View File
@@ -271,4 +271,9 @@ export default {
.filter-label .v-popper__arrow-container { .filter-label .v-popper__arrow-container {
display: none; display: none;
} }
.fixed-header-actions .middle .filter + .btn-group {
display: inline-flex;
vertical-align: middle;
}
</style> </style>
@@ -116,6 +116,11 @@ export default {
.vm-schedule-dropdown .v-popper__arrow-container { .vm-schedule-dropdown .v-popper__arrow-container {
display: none; display: none;
} }
.fixed-header-actions .middle .vm-schedule-filter + .btn-group {
display: inline-flex;
vertical-align: middle;
}
</style> </style>
<style lang="scss" scoped> <style lang="scss" scoped>
+442 -63
View File
@@ -1,7 +1,6 @@
<script> <script>
import { allHash } from '@shell/utils/promise'; import { allHash } from '@shell/utils/promise';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import Socket, { import Socket, {
EVENT_CONNECTED, EVENT_CONNECTED,
EVENT_CONNECTING, EVENT_CONNECTING,
@@ -22,74 +21,98 @@ export default {
data() { data() {
return { return {
socket: null, socket: null,
terminal: null, terminal: null,
fitAddon: null, textDecoder: null,
searchAddon: null, fitAddon: null,
webglAddon: null, searchAddon: null,
isOpen: false, webglAddon: null,
isOpening: false, onResize: null,
backlog: [], isOpen: false,
firstTime: true, isOpening: false,
queue: [] backlog: [],
firstTime: true,
queue: [],
isDraining: false,
drainScheduled: false,
drainTimer: null,
showSearch: false,
searchQuery: '',
searchOptions: {
caseSensitive: false,
regex: false,
wholeWord: false,
incremental: true,
decorations: {
matchBackground: '#B45309',
matchBorder: '#FDBA74',
matchOverviewRuler: '#FDBA74',
activeMatchBackground: '#EA580C',
activeMatchBorder: '#FED7AA',
activeMatchColorOverviewRuler: '#FED7AA',
},
},
}; };
}, },
computed: { computed: {
serialConsoleUrl() {
return this.value?.getSerialConsolePath || '';
},
xtermConfig() { xtermConfig() {
return { return {
allowProposedApi: true, allowProposedApi: true,
cursorBlink: true, cursorBlink: true,
cursorStyle: 'bar',
cursorWidth: 2,
scrollback: 5000,
useStyle: true, useStyle: true,
fontSize: 12, fontFamily: 'JetBrains Mono, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
fontSize: 16,
lineHeight: 1.3,
}; };
}, },
}, },
watch: {
queue: {
handler: debounce(async function(neu) {
if (neu.length === 0) {
return;
}
const msg = await Promise.all(neu);
(msg || []).forEach((m) => {
this.terminal.write(m);
});
this.queue = [];
}, 10),
deep: true
}
},
beforeUnmount() { beforeUnmount() {
this.close(); this.close();
}, },
watch: {
serialConsoleUrl() {
if (this.terminal) {
this.connect();
}
},
},
async mounted() { async mounted() {
this.textDecoder = new TextDecoder('utf-8');
await this.setupTerminal(); await this.setupTerminal();
await this.connect(); await this.connect();
this.onResize = debounce(() => this.fit(), 100);
window.addEventListener('resize', this.onResize);
this.scheduleFit();
}, },
methods: { methods: {
async setupTerminal() { async setupTerminal() {
const docStyle = getComputedStyle(document.querySelector('body')); const docStyle = getComputedStyle(document.querySelector('body'));
const xterm = await import(/* webpackChunkName: "xterm" */ 'xterm'); const xterm = await import(/* webpackChunkName: "xterm" */ '@xterm/xterm');
const addons = await allHash({ const addons = await allHash({
fit: import(/* webpackChunkName: "xterm" */ 'xterm-addon-fit'), fit: import(/* webpackChunkName: "xterm" */ '@xterm/addon-fit'),
webgl: import(/* webpackChunkName: "xterm" */ 'xterm-addon-webgl'), webgl: import(/* webpackChunkName: "xterm" */ '@xterm/addon-webgl'),
weblinks: import(/* webpackChunkName: "xterm" */ 'xterm-addon-web-links'), weblinks: import(/* webpackChunkName: "xterm" */ '@xterm/addon-web-links'),
search: import(/* webpackChunkName: "xterm" */ 'xterm-addon-search'), search: import(/* webpackChunkName: "xterm" */ '@xterm/addon-search'),
canvas: import(/* webpackChunkName: "xterm" */ '@xterm/addon-canvas'),
}); });
const terminal = new xterm.Terminal({ const terminal = new xterm.Terminal({
theme: { theme: {
background: docStyle.getPropertyValue('--terminal-bg').trim(), background: docStyle.getPropertyValue('--terminal-bg').trim(),
cursor: docStyle.getPropertyValue('--terminal-cursor').trim(), cursor: docStyle.getPropertyValue('--terminal-text').trim(),
foreground: docStyle.getPropertyValue('--terminal-text').trim() foreground: docStyle.getPropertyValue('--terminal-text').trim()
}, },
...this.xtermConfig, ...this.xtermConfig,
@@ -97,25 +120,48 @@ export default {
this.fitAddon = new addons.fit.FitAddon(); this.fitAddon = new addons.fit.FitAddon();
this.searchAddon = new addons.search.SearchAddon(); this.searchAddon = new addons.search.SearchAddon();
try {
this.webglAddon = new addons.webgl.WebGlAddon();
} catch (e) {
// Some browsers (Safari) don't support the webgl renderer, so don't use it.
this.webglAddon = null;
}
terminal.loadAddon(this.fitAddon); terminal.loadAddon(this.fitAddon);
terminal.loadAddon(this.searchAddon); terminal.loadAddon(this.searchAddon);
terminal.loadAddon(new addons.weblinks.WebLinksAddon()); terminal.loadAddon(new addons.weblinks.WebLinksAddon());
terminal.open(this.$refs.xterm); terminal.open(this.$refs.xterm);
if ( this.webglAddon ) { // The webgl renderer can fail without throwing: the context may be lost
// mid-session, or it can attach but silently never paint, leaving the
// terminal's helper textarea at 0x0. Detect both cases and fall back to the
// canvas renderer so the terminal stays interactive.
try {
this.webglAddon = new addons.webgl.WebglAddon();
this.webglAddon.onContextLoss(() => {
if (!this.isUnmounted && this.terminal) {
this.fallbackToCanvas(terminal, addons);
}
});
terminal.loadAddon(this.webglAddon); terminal.loadAddon(this.webglAddon);
// Detect the silent "attaches but never paints" failure after a render frame.
requestAnimationFrame(() => {
if (this.isUnmounted || !this.terminal) {
return;
}
const textarea = this.$refs.xterm?.querySelector('.xterm-helper-textarea');
const hasPainted = textarea && textarea.getBoundingClientRect().height > 0;
if (this.webglAddon && !hasPainted) {
this.fallbackToCanvas(terminal, addons);
this.fit();
}
});
} catch (e) {
// Some browsers (e.g. Safari) don't support the webgl renderer, so don't use it.
this.fallbackToCanvas(terminal, addons);
} }
this.fit(); this.fit();
this.flush(); this.flush();
this.scheduleDrainQueue();
terminal.onData((input) => { terminal.onData((input) => {
const msg = this.str2ab(input); const msg = this.str2ab(input);
@@ -123,9 +169,132 @@ export default {
this.write(msg); this.write(msg);
}); });
terminal.attachCustomKeyEventHandler((event) => {
const key = event.key?.toLowerCase();
if ((event.ctrlKey || event.metaKey) && key === 'f') {
event.preventDefault();
this.openSearch();
return false;
}
if (key === 'escape' && this.showSearch) {
event.preventDefault();
this.closeSearch();
return false;
}
return true;
});
this.terminal = terminal; this.terminal = terminal;
}, },
openSearch() {
this.showSearch = true;
this.$nextTick(() => {
const input = this.$refs.searchInput;
if (input) {
input.focus();
input.select();
}
});
},
closeSearch() {
this.showSearch = false;
this.searchQuery = '';
this.clearSearchHighlights();
this.terminal?.focus();
},
clearSearchHighlights() {
this.searchAddon?.clearActiveDecoration();
this.searchAddon?.clearDecorations();
this.terminal?.clearSelection();
// Disposing the search decorations doesn't repaint the webgl/canvas
// layer, so force a refresh to remove the lingering highlight.
if (this.terminal) {
this.terminal.refresh(0, this.terminal.rows - 1);
}
},
findNext() {
if (!this.searchAddon || !this.searchQuery) {
return;
}
this.searchAddon.findNext(this.searchQuery, this.searchOptions);
},
findPrevious() {
if (!this.searchAddon || !this.searchQuery) {
return;
}
this.searchAddon.findPrevious(this.searchQuery, this.searchOptions);
},
onSearchInput() {
if (!this.searchQuery) {
this.clearSearchHighlights();
return;
}
this.findNext();
},
onSearchKeydown(event) {
if (event.key === 'Enter') {
event.preventDefault();
if (event.shiftKey) {
this.findPrevious();
} else {
this.findNext();
}
}
if (event.key === 'Escape') {
event.preventDefault();
this.closeSearch();
}
},
scheduleFit() {
this.$nextTick(() => {
requestAnimationFrame(() => {
this.fit();
// Re-fit after layout settles to avoid undersized rows on first paint.
setTimeout(() => this.fit(), 120);
});
});
},
// Dispose the webgl renderer (if any) and switch to the canvas renderer.
// Used when webgl fails to construct, loses its context, or silently never paints.
fallbackToCanvas(terminal, addons) {
if (this.isUnmounted) {
return;
}
this.webglAddon?.dispose();
this.webglAddon = null;
if (!this.canvasAddon) {
this.canvasAddon = new addons.canvas.CanvasAddon();
terminal.loadAddon(this.canvasAddon);
}
this.fit();
},
str2ab(str) { str2ab(str) {
const enc = new TextEncoder(); const enc = new TextEncoder();
@@ -140,12 +309,131 @@ export default {
} }
}, },
enqueueMessage(messagePromise) {
this.queue.push(messagePromise);
this.scheduleDrainQueue();
},
scheduleDrainQueue() {
if (this.drainScheduled || this.isDraining || !this.terminal) {
return;
}
this.drainScheduled = true;
const shouldDrainImmediately = this.queue.length <= 3;
const runDrain = () => {
this.drainScheduled = false;
this.drainTimer = null;
this.drainQueue();
};
if (shouldDrainImmediately) {
runDrain();
return;
}
this.drainTimer = requestAnimationFrame(runDrain);
},
async decodeMessageData(data) {
if (typeof data === 'string') {
return data;
}
if (!this.textDecoder) {
this.textDecoder = new TextDecoder('utf-8');
}
if (data instanceof Blob) {
const buffer = await data.arrayBuffer();
return this.textDecoder.decode(new Uint8Array(buffer), { stream: true });
}
if (data instanceof ArrayBuffer) {
return this.textDecoder.decode(new Uint8Array(data), { stream: true });
}
if (ArrayBuffer.isView(data)) {
const view = data;
const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
return this.textDecoder.decode(bytes, { stream: true });
}
return String(data || '');
},
async drainQueue() {
if (this.isDraining || !this.terminal) {
return;
}
this.isDraining = true;
try {
while (this.queue.length > 0) {
const pendingMessages = this.queue.splice(0, this.queue.length);
const settledMessages = await Promise.allSettled(pendingMessages);
const messages = settledMessages
.filter((result) => result.status === 'fulfilled')
.map((result) => result.value);
const output = messages.filter(Boolean).join('');
if (output) {
await this.writeOutputInChunks(output);
}
if (this.queue.length > 0) {
await this.nextFrame();
}
}
} finally {
this.isDraining = false;
// If data arrived between the last while-check and releasing the lock,
// immediately schedule another drain so output never gets stuck.
if (this.queue.length > 0 && this.terminal) {
this.scheduleDrainQueue();
}
}
},
async writeOutputInChunks(output) {
if (!output) {
return;
}
const chunkSize = output.length > 20000 ? 16000 : output.length > 8000 ? 8000 : output.length;
if (chunkSize === output.length) {
this.terminal.write(output);
return;
}
for (let index = 0; index < output.length; index += chunkSize) {
this.terminal.write(output.slice(index, index + chunkSize));
if (index + chunkSize < output.length) {
await this.nextFrame();
}
}
},
nextFrame() {
return new Promise((resolve) => requestAnimationFrame(resolve));
},
clear() { clear() {
this.terminal.clear(); this.terminal.clear();
}, },
getSocketUrl() { getSocketUrl() {
return `${ this.value?.getSerialConsolePath }`; return this.serialConsoleUrl;
}, },
async connect() { async connect() {
@@ -181,6 +469,7 @@ export default {
this.fit(); this.fit();
this.flush(); this.flush();
} }
this.scheduleFit();
if (this.firstTime) { if (this.firstTime) {
this.socket.send(this.str2ab('\n')); this.socket.send(this.str2ab('\n'));
@@ -195,7 +484,7 @@ export default {
}); });
this.socket.addEventListener(EVENT_MESSAGE, (e) => { this.socket.addEventListener(EVENT_MESSAGE, (e) => {
this.queue.push(e.detail.data.text()); this.enqueueMessage(this.decodeMessageData(e.detail.data));
}); });
this.socket.connect(); this.socket.connect();
@@ -218,22 +507,19 @@ export default {
} }
this.fitAddon.fit(); this.fitAddon.fit();
const { rows, cols } = this.fitAddon.proposeDimensions();
if ( !this.isOpen ) {
return;
}
const message = JSON.stringify({
Width: cols,
Height: rows
});
this.socket.send(this.str2ab(message));
}, },
close() { close() {
if (this.onResize) {
window.removeEventListener('resize', this.onResize);
this.onResize = null;
}
if (this.drainTimer) {
cancelAnimationFrame(this.drainTimer);
this.drainTimer = null;
}
if ( this.socket ) { if ( this.socket ) {
this.socket.disconnect(); this.socket.disconnect();
} }
@@ -248,6 +534,44 @@ export default {
<template> <template>
<div class="harvester-shell-container"> <div class="harvester-shell-container">
<div
v-if="showSearch"
class="shell-search"
>
<input
ref="searchInput"
v-model="searchQuery"
type="text"
class="shell-search-input"
placeholder="Search in terminal"
@input="onSearchInput"
@keydown="onSearchKeydown"
>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Shift+Enter"
@click="findPrevious"
>
Prev
</button>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Enter"
@click="findNext"
>
Next
</button>
<button
class="btn role-secondary btn-sm shell-search-btn"
type="button"
title="Esc"
@click="closeSearch"
>
Close
</button>
</div>
<div <div
ref="xterm" ref="xterm"
class="shell-body" class="shell-body"
@@ -257,18 +581,73 @@ export default {
</template> </template>
<style lang="scss"> <style lang="scss">
@import '../../../../node_modules/xterm/css/xterm.css'; @import '../../../../node_modules/@xterm/xterm/css/xterm.css';
body, #__nuxt, #__layout, MAIN { body, #__nuxt, #__layout, MAIN {
height: 100%; height: 100%;
} }
.harvester-shell-container { .harvester-shell-container {
display: flex;
flex-direction: column;
position: relative;
height: 100%; height: 100%;
min-height: 0;
overflow: hidden; overflow: hidden;
.shell-body, .terminal.xterm { .resize-observer {
display: none;
}
.shell-search {
display: flex;
align-items: center;
gap: 8px;
justify-content: flex-end;
position: absolute;
top: 8px;
right: 10px;
z-index: 5;
padding: 8px;
border-radius: 6px;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--body-bg) 88%, transparent);
}
.shell-search-input {
min-width: 220px;
max-width: 420px;
flex: 0 1 320px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--body-bg);
color: var(--body-text);
padding: 6px 8px;
}
.shell-search-btn {
min-width: 64px;
}
.shell-body {
flex: 1 1 auto;
height: 100%; height: 100%;
width: 100%;
min-height: 0;
padding: 0 10px;
box-sizing: border-box;
}
.terminal.xterm,
.terminal.xterm .xterm-viewport,
.terminal.xterm .xterm-screen,
.terminal.xterm .xterm-scrollable-element {
width: 100%;
height: 100%;
}
.terminal.xterm .xterm-scrollable-element {
min-height: 100%;
} }
} }
</style> </style>
+5 -1
View File
@@ -55,6 +55,10 @@ export default {
const host = window.location.host; const host = window.location.host;
const prefix = window.location.pathname.replace(this.$route.path, ''); const prefix = window.location.pathname.replace(this.$route.path, '');
const params = this.$route?.params; const params = this.$route?.params;
const popupWidth = Math.max(800, screen.width - 200);
const popupHeight = Math.max(600, screen.height - 200);
const popupLeft = Math.max(0, window.screenX + Math.floor((window.outerWidth - popupWidth) / 2));
const popupTop = Math.max(0, window.screenY + Math.floor((window.outerHeight - popupHeight) / 2));
const url = `https://${ host }${ prefix }/${ PRODUCT_NAME }/c/${ params.cluster }/console/${ uid }/${ type }`; const url = `https://${ host }${ prefix }/${ PRODUCT_NAME }/c/${ params.cluster }/console/${ uid }/${ type }`;
@@ -63,7 +67,7 @@ export default {
window.open( window.open(
url, url,
'_blank', '_blank',
`toolbars=0,width=${ screen.width - 200 },height=${ screen.height - 200 },left=0,top=0,noreferrer` `toolbars=0,width=${ popupWidth },height=${ popupHeight },left=${ popupLeft },top=${ popupTop },noreferrer`
); );
}); });
}, },
@@ -7,10 +7,15 @@ import { STORAGE_CLASS, NETWORK_ATTACHMENT } from '@shell/config/types';
import { useI18n } from '@shell/composables/useI18n'; import { useI18n } from '@shell/composables/useI18n';
import { randomStr } from '@shell/utils/string'; import { randomStr } from '@shell/utils/string';
import { HCI } from '../../types'; import { HCI } from '../../types';
import { VOLUME_MODE, ACCESS_MODE } from '../../config/types';
import { FORKLIFT_NAMESPACE } from '../../config/harvester-map'; import { FORKLIFT_NAMESPACE } from '../../config/harvester-map';
import { buildNetworkMapEntries, buildStorageMapEntries } from '../../utils/forklift'; import { buildNetworkMapEntries, buildStorageMapEntries } from '../../utils/forklift';
import { isInternalStorageClass } from '../../utils/storage-class'; import { isInternalStorageClass } from '../../utils/storage-class';
import MappingColumn from './MappingColumn.vue'; import MappingColumn from './MappingColumn.vue';
import StorageDefaultsModal from './StorageDefaultsModal.vue';
const DEFAULT_VOLUME_MODE = VOLUME_MODE.FILE_SYSTEM;
const DEFAULT_ACCESS_MODE = ACCESS_MODE?.READ_WRITE_MANY ?? 'ReadWriteMany';
const props = defineProps({ const props = defineProps({
providerName: { type: String, default: '' }, providerName: { type: String, default: '' },
@@ -35,6 +40,10 @@ const allStorageMaps = ref([]);
const errors = ref([]); const errors = ref([]);
const loading = ref(true); const loading = ref(true);
// Storage defaults edit modal state.
const showStorageDefaultsModal = ref(false);
const editingStorageEntry = ref(null);
const { networkEntries, storageEntries } = toRefs(props.stepData); const { networkEntries, storageEntries } = toRefs(props.stepData);
const NAMESPACE = FORKLIFT_NAMESPACE; const NAMESPACE = FORKLIFT_NAMESPACE;
@@ -105,7 +114,7 @@ const applyNetworkMapTargets = (mapSpec) => {
}); });
}; };
const applyStorageMapTargets = (mapSpec) => { const applyStorageMapTargets = (mapSpec, { markOverridden = false, captureInherited = false } = {}) => {
if (!mapSpec) { if (!mapSpec) {
return; return;
} }
@@ -117,6 +126,30 @@ const applyStorageMapTargets = (mapSpec) => {
if (match?.destination?.storageClass) { if (match?.destination?.storageClass) {
entry.target = match.destination.storageClass; entry.target = match.destination.storageClass;
if (captureInherited) {
entry.inheritedFromProvider = true;
}
if (match.destination.volumeMode) {
entry.volumeMode = match.destination.volumeMode;
if (captureInherited) {
entry.inheritedVolumeMode = match.destination.volumeMode;
}
}
if (match.destination.accessMode) {
entry.accessModes = [match.destination.accessMode];
if (captureInherited) {
entry.inheritedAccessModes = [match.destination.accessMode];
}
}
if (markOverridden) {
entry.overridden = true;
}
} }
}); });
}; };
@@ -134,12 +167,39 @@ const applyDefaultStorageMap = () => {
(sm) => sm.metadata.name === `${ props.providerName }-storage-map-default` (sm) => sm.metadata.name === `${ props.providerName }-storage-map-default`
); );
applyStorageMapTargets(defaultMap?.spec?.map); applyStorageMapTargets(defaultMap?.spec?.map, { captureInherited: true });
}; };
const allNetworksMapped = computed(() => networkEntries.value.length > 0 && networkEntries.value.every((e) => !!e.target)); const allNetworksMapped = computed(() => networkEntries.value.length > 0 && networkEntries.value.every((e) => !!e.target));
const allStorageMapped = computed(() => storageEntries.value.length > 0 && storageEntries.value.every((e) => !!e.target)); const allStorageMapped = computed(() => storageEntries.value.length > 0 && storageEntries.value.every((e) => !!e.target));
// The "Inherited from provider" hint only appears in the migration plan wizard
// (where mappings inherit from the provider default), not on the provider creation page.
const inheritedProviderName = computed(() => (props.useAllProviderData ? '' : props.providerName));
const openStorageDefaults = (entry) => {
editingStorageEntry.value = entry;
showStorageDefaultsModal.value = true;
};
const closeStorageDefaults = () => {
showStorageDefaultsModal.value = false;
editingStorageEntry.value = null;
};
const applyStorageDefaults = ({ volumeMode, accessModes }) => {
if (editingStorageEntry.value) {
editingStorageEntry.value.volumeMode = volumeMode;
editingStorageEntry.value.accessModes = accessModes;
const inheritedVolumeMode = editingStorageEntry.value.inheritedVolumeMode;
const inheritedAccessMode = editingStorageEntry.value.inheritedAccessModes?.[0];
const selectedAccessMode = accessModes?.[0];
editingStorageEntry.value.overridden = volumeMode !== inheritedVolumeMode || selectedAccessMode !== inheritedAccessMode;
}
};
const canSave = computed(() => { const canSave = computed(() => {
if (props.useAllProviderData) { if (props.useAllProviderData) {
return true; return true;
@@ -202,13 +262,19 @@ const buildStorageEntries = () => {
if (ds && ds.id) { if (ds && ds.id) {
if (!datastoreMap[ds.id]) { if (!datastoreMap[ds.id]) {
datastoreMap[ds.id] = { datastoreMap[ds.id] = {
name: ds.name || t('harvester.addons.vmMigration.generic.unknown'), name: ds.name || t('harvester.addons.vmMigration.generic.unknown'),
id: ds.id, id: ds.id,
type: ds.type || '', type: ds.type || '',
capacity: 0, capacity: 0,
target: '', target: '',
usedBy: [], volumeMode: DEFAULT_VOLUME_MODE,
_key: `stor-${ ds.id }`, accessModes: [DEFAULT_ACCESS_MODE],
inheritedVolumeMode: DEFAULT_VOLUME_MODE,
inheritedAccessModes: [DEFAULT_ACCESS_MODE],
inheritedFromProvider: false,
overridden: false,
usedBy: [],
_key: `stor-${ ds.id }`,
}; };
} }
@@ -238,13 +304,19 @@ const buildNetworkEntriesFromProvider = (networksData) => {
const buildStorageEntriesFromProvider = (datastoresData) => { const buildStorageEntriesFromProvider = (datastoresData) => {
storageEntries.value = (Array.isArray(datastoresData) ? datastoresData : []).map((ds) => ({ storageEntries.value = (Array.isArray(datastoresData) ? datastoresData : []).map((ds) => ({
name: ds.name || ds.id, name: ds.name || ds.id,
id: ds.id || '', id: ds.id || '',
type: ds.type || '', type: ds.type || '',
capacity: ds.capacity || 0, capacity: ds.capacity || 0,
target: '', target: '',
usedBy: [], volumeMode: DEFAULT_VOLUME_MODE,
_key: `stor-${ ds.id || ds.name }`, accessModes: [DEFAULT_ACCESS_MODE],
inheritedVolumeMode: DEFAULT_VOLUME_MODE,
inheritedAccessModes: [DEFAULT_ACCESS_MODE],
inheritedFromProvider: false,
overridden: false,
usedBy: [],
_key: `stor-${ ds.id || ds.name }`,
})); }));
}; };
@@ -461,7 +533,7 @@ const init = async() => {
if (!hasExistingStorageTargets) { if (!hasExistingStorageTargets) {
if (props.existingStorageMap?.spec?.map) { if (props.existingStorageMap?.spec?.map) {
applyStorageMapTargets(props.existingStorageMap.spec.map); applyStorageMapTargets(props.existingStorageMap.spec.map, { markOverridden: true });
} else { } else {
applyDefaultStorageMap(); applyDefaultStorageMap();
} }
@@ -511,6 +583,9 @@ init();
:placeholder="t('harvester.addons.vmMigration.configureMappings.storageMapping.placeholder')" :placeholder="t('harvester.addons.vmMigration.configureMappings.storageMapping.placeholder')"
:show-used-by="!useAllProviderData" :show-used-by="!useAllProviderData"
:clearable="useAllProviderData" :clearable="useAllProviderData"
:show-volume-settings="true"
:inherited-provider-name="inheritedProviderName"
@edit-defaults="openStorageDefaults"
> >
<template #source-detail="{ entry }"> <template #source-detail="{ entry }">
<span <span
@@ -520,6 +595,19 @@ init();
</template> </template>
</MappingColumn> </MappingColumn>
</div> </div>
<StorageDefaultsModal
v-if="showStorageDefaultsModal && editingStorageEntry"
:storage-class-name="editingStorageEntry.target"
:provider-name="providerName"
:show-inherited="!!inheritedProviderName"
:volume-mode="editingStorageEntry.volumeMode"
:access-modes="editingStorageEntry.accessModes"
:inherited-volume-mode="editingStorageEntry.inheritedVolumeMode"
:inherited-access-modes="editingStorageEntry.inheritedAccessModes"
@apply="applyStorageDefaults"
@close="closeStorageDefaults"
/>
</div> </div>
</template> </template>
@@ -3,20 +3,27 @@ import { useStore } from 'vuex';
import LabeledSelect from '@shell/components/form/LabeledSelect'; import LabeledSelect from '@shell/components/form/LabeledSelect';
import { RcItemCard } from '@components/RcItemCard'; import { RcItemCard } from '@components/RcItemCard';
import { useI18n } from '@shell/composables/useI18n'; import { useI18n } from '@shell/composables/useI18n';
import { VOLUME_MODE } from '../../config/types';
const store = useStore(); const store = useStore();
const { t } = useI18n(store); const { t } = useI18n(store);
const props = defineProps({ const props = defineProps({
title: { type: String, required: true }, title: { type: String, required: true },
description: { type: String, default: '' }, description: { type: String, default: '' },
entries: { type: Array, default: () => [] }, entries: { type: Array, default: () => [] },
options: { type: Array, default: () => [] }, options: { type: Array, default: () => [] },
placeholder: { type: String, default: '' }, placeholder: { type: String, default: '' },
showUsedBy: { type: Boolean, default: false }, showUsedBy: { type: Boolean, default: false },
clearable: { type: Boolean, default: false }, clearable: { type: Boolean, default: false },
// Storage-specific: show the volume/access mode defaults row with an Edit action.
showVolumeSettings: { type: Boolean, default: false },
// When set, the defaults row shows an "Inherited from provider" hint (migration plan wizard only).
inheritedProviderName: { type: String, default: '' },
}); });
const emit = defineEmits(['edit-defaults']);
// Only offer "Remove Map" for entries that already have a target selected; // Only offer "Remove Map" for entries that already have a target selected;
// entries without a selection just show the regular options. // entries without a selection just show the regular options.
const optionsFor = (entry) => { const optionsFor = (entry) => {
@@ -38,6 +45,13 @@ const optionsFor = (entry) => {
...props.options, ...props.options,
]; ];
}; };
const formatModes = (entry) => {
const volumeMode = entry.volumeMode || VOLUME_MODE.FILE_SYSTEM;
const accessModes = (entry.accessModes || []).join(', ');
return t('harvester.addons.vmMigration.storageDefaults.summary', { volumeMode, accessModes });
};
</script> </script>
<template> <template>
@@ -87,6 +101,29 @@ const optionsFor = (entry) => {
/> />
</div> </div>
</div> </div>
<div
v-if="showVolumeSettings && entry.target"
class="storage-defaults-row"
>
<div class="storage-defaults">
<div class="storage-defaults-info">
<span class="storage-defaults-summary">{{ formatModes(entry) }}</span>
<span
v-if="inheritedProviderName && entry.inheritedFromProvider && !entry.overridden"
class="text-deemphasized storage-defaults-inherited"
>
{{ t('harvester.addons.vmMigration.storageDefaults.inherited', { provider: inheritedProviderName }) }}
</span>
</div>
<a
role="button"
class="storage-defaults-edit"
@click.prevent="emit('edit-defaults', entry)"
>
{{ t('harvester.addons.vmMigration.storageDefaults.edit') }}
</a>
</div>
</div>
<div v-if="showUsedBy && entry.usedBy && entry.usedBy.length"> <div v-if="showUsedBy && entry.usedBy && entry.usedBy.length">
<span class="used-by"> <span class="used-by">
{{ t('harvester.addons.vmMigration.generic.usedBy') }} <b>{{ entry.usedBy.join(', ') }}</b> {{ t('harvester.addons.vmMigration.generic.usedBy') }} <b>{{ entry.usedBy.join(', ') }}</b>
@@ -173,6 +210,41 @@ const optionsFor = (entry) => {
line-height: 20px; line-height: 20px;
} }
.storage-defaults-row {
width: 100%;
}
.storage-defaults {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 8px 12px;
border-radius: 4px;
background-color: var(--body-bg);
.storage-defaults-info {
display: flex;
flex-direction: column;
min-width: 0;
}
.storage-defaults-summary {
font-size: 13px;
line-height: 20px;
}
.storage-defaults-inherited {
font-size: 12px;
line-height: 18px;
}
.storage-defaults-edit {
flex-shrink: 0;
cursor: pointer;
}
}
.bg-light-gray { .bg-light-gray {
background-color: var(--category-active) !important; background-color: var(--category-active) !important;
border: 0; border: 0;
@@ -273,19 +273,6 @@ const startMigrationAction = async() => {
await plan.save(); await plan.save();
const planOwnerRef = {
apiVersion: FORKLIFT_API_VERSION,
kind: 'Plan',
name: plan.metadata.name,
uid: plan.metadata.uid,
blockOwnerDeletion: true,
};
networkMap.metadata.ownerReferences = [planOwnerRef];
await networkMap.save();
storageMap.metadata.ownerReferences = [planOwnerRef];
await storageMap.save();
// Kick off the first migration through the model so the Migration payload // Kick off the first migration through the model so the Migration payload
// lives in a single place (also reused by the dashboard start/restart action). // lives in a single place (also reused by the dashboard start/restart action).
await plan.startMigration(); await plan.startMigration();
@@ -25,8 +25,9 @@ const { discoveredVMs, selectedVMIds, tableRows } = toRefs(props.stepData);
const selectedVMs = ref([]); const selectedVMs = ref([]);
const loading = ref(true); const loading = ref(true);
const networkMap = ref({}); // Restored from stepData so friendly names survive step navigation / remount.
const datastoreMap = ref({}); const networkMap = ref(props.stepData.networkMap || {});
const datastoreMap = ref(props.stepData.datastoreMap || {});
const sortableTableRef = ref(null); const sortableTableRef = ref(null);
const allVMsSelected = ref(false); const allVMsSelected = ref(false);
const errors = ref([]); const errors = ref([]);
@@ -94,7 +95,7 @@ const showSelectAllBanner = computed(() => {
const theadElement = computed(() => sortableTableRef.value?.$el?.querySelector('thead')); const theadElement = computed(() => sortableTableRef.value?.$el?.querySelector('thead'));
const headers = [ const headers = ref([
{ {
name: 'vmName', name: 'vmName',
labelKey: 'harvester.addons.vmMigration.selectVms.columns.vmName', labelKey: 'harvester.addons.vmMigration.selectVms.columns.vmName',
@@ -139,7 +140,7 @@ const headers = [
subLabel: t('harvester.addons.vmMigration.generic.identifier'), subLabel: t('harvester.addons.vmMigration.generic.identifier'),
width: 200, width: 200,
}, },
]; ]);
const buildTableRows = () => { const buildTableRows = () => {
return discoveredVMs.value.map((vm) => { return discoveredVMs.value.map((vm) => {
@@ -190,6 +191,7 @@ const buildTableRows = () => {
return { return {
_original: vm, _original: vm,
_key: vm.id || vm.vmId || vm.metadata?.name, _key: vm.id || vm.vmId || vm.metadata?.name,
selectedSort: selectedVMIds.value.has(vm.id) ? 0 : 1,
vmName: vm.name || vm.metadata?.name || '-', vmName: vm.name || vm.metadata?.name || '-',
vmId: vm.id || vm.vmId || vm.metadata?.name || '-', vmId: vm.id || vm.vmId || vm.metadata?.name || '-',
os: vm.guestName || vm.guestOS || vm.os || '-', os: vm.guestName || vm.guestOS || vm.os || '-',
@@ -239,6 +241,10 @@ const clearSelection = () => {
if (table) { if (table) {
table.clearSelection(); table.clearSelection();
if (typeof table.setPage === 'function') {
table.setPage(1);
}
} }
}); });
}; };
@@ -249,6 +255,49 @@ const selectAllVMs = () => {
selectedVMs.value = discoveredVMs.value.slice(); selectedVMs.value = discoveredVMs.value.slice();
}; };
// Moves currently-selected VMs to the first page(s) and re-checks them.
const sortSelectedToFront = () => {
if (selectedVMIds.value.size === 0) {
return;
}
tableRows.value.forEach((row) => {
row.selectedSort = selectedVMIds.value.has(row._original?.id) ? 0 : 1;
});
headers.value = headers.value.map((h) => (
h.name === 'vmName' ? { ...h, sort: ['selectedSort', 'vmName'] } : h
));
skipNextSelectionEvent = true;
nextTick(() => {
const table = sortableTableRef.value;
if (!table) {
skipNextSelectionEvent = false;
return;
}
if (typeof table.changeSort === 'function') {
table.changeSort('vmName', false);
} else if (typeof table.setPage === 'function') {
table.setPage(1);
}
nextTick(() => {
const rowsToReselect = (table.pagedRows || []).filter((row) => selectedVMIds.value.has(row._original?.id));
if (rowsToReselect.length > 0) {
table.update(rowsToReselect, []);
}
skipNextSelectionEvent = false;
});
});
};
watch( watch(
() => sortableTableRef.value?.page, () => sortableTableRef.value?.page,
() => { () => {
@@ -318,6 +367,9 @@ const fetchVMs = async() => {
return map; return map;
}, {}); }, {});
props.stepData.networkMap = networkMap.value;
props.stepData.datastoreMap = datastoreMap.value;
lastFetchedAt.value = Date.now(); lastFetchedAt.value = Date.now();
tableRows.value = buildTableRows(); tableRows.value = buildTableRows();
}; };
@@ -374,6 +426,10 @@ const init = async() => {
tableRows.value = buildTableRows(); tableRows.value = buildTableRows();
loading.value = false; loading.value = false;
// Returning to the step with an existing selection: bring selected VMs forward and re-check them.
await nextTick();
sortSelectedToFront();
return; return;
} }
@@ -441,7 +497,6 @@ init();
:row-actions="false" :row-actions="false"
:groupable="false" :groupable="false"
:paging="true" :paging="true"
:rows-per-page="20"
key-field="_key" key-field="_key"
@selection="onSelect" @selection="onSelect"
> >
@@ -450,7 +505,20 @@ init();
<h3 class="m-0"> <h3 class="m-0">
{{ t('harvester.addons.vmMigration.selectVms.availableVms') }} {{ t('harvester.addons.vmMigration.selectVms.availableVms') }}
</h3> </h3>
<span class="text-deemphasized">{{ selectedCount }} {{ t('harvester.addons.vmMigration.selectVms.selected') }}</span> <span class="selected-actions">
<span class="text-deemphasized">
{{ selectedCount }} {{ t('harvester.addons.vmMigration.selectVms.selected') }}
</span>
<template v-if="selectedCount > 0">
<span class="text-deemphasized">|</span>
<a
role="button"
@click.prevent="clearSelection"
>
{{ t('harvester.addons.vmMigration.selectVms.clearAll') }}
</a>
</template>
</span>
</div> </div>
</template> </template>
<template #cell:vmName="{ row }"> <template #cell:vmName="{ row }">
@@ -466,7 +534,7 @@ init();
<template #cell:powerState="{ row }"> <template #cell:powerState="{ row }">
<BadgeState <BadgeState
:label="row.powerState" :label="row.powerState"
:color="row.powerStateClass === 'power-on' ? 'bg-warning' : 'bg-darker'" :color="row.powerStateClass === 'power-on' ? 'bg-success' : 'bg-darker'"
/> />
</template> </template>
<template #cell:network="{ row }"> <template #cell:network="{ row }">
@@ -533,6 +601,23 @@ init();
.text-deemphasized { .text-deemphasized {
font-size: 13px; font-size: 13px;
} }
.selected-actions {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
a {
cursor: pointer;
&.disabled {
color: var(--muted);
cursor: default;
pointer-events: none;
}
}
}
} }
.vm-name-cell { .vm-name-cell {
@@ -0,0 +1,181 @@
<script setup>
import { ref } from 'vue';
import { useStore } from 'vuex';
import { Card } from '@components/Card';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import AppModal from '@shell/components/AppModal';
import { useI18n } from '@shell/composables/useI18n';
import { VOLUME_MODE, ACCESS_MODE } from '../../config/types';
const VOLUME_MODE_OPTIONS = [VOLUME_MODE.FILE_SYSTEM, VOLUME_MODE.BLOCK];
const {
READ_WRITE_ONCE = 'ReadWriteOnce',
READ_ONLY_MANY = 'ReadOnlyMany',
READ_WRITE_MANY = 'ReadWriteMany',
READ_WRITE_ONCE_POD = 'ReadWriteOncePod'
} = ACCESS_MODE || {};
const ACCESS_MODE_OPTIONS = [READ_WRITE_ONCE, READ_WRITE_MANY, READ_ONLY_MANY, READ_WRITE_ONCE_POD];
const props = defineProps({
storageClassName: { type: String, default: '' },
providerName: { type: String, default: '' },
showInherited: { type: Boolean, default: false },
volumeMode: { type: String, default: VOLUME_MODE.FILE_SYSTEM },
accessModes: { type: Array, default: () => ['ReadWriteMany'] },
inheritedVolumeMode: { type: String, default: VOLUME_MODE.FILE_SYSTEM },
inheritedAccessModes: { type: Array, default: () => ['ReadWriteMany'] },
});
const emit = defineEmits(['apply', 'close']);
const store = useStore();
const { t } = useI18n(store);
const localVolumeMode = ref(props.volumeMode || VOLUME_MODE.FILE_SYSTEM);
const localAccessMode = ref(props.accessModes?.[0] || READ_WRITE_MANY);
const volumeModeOptions = VOLUME_MODE_OPTIONS.map((value) => ({ label: value, value }));
const accessModeOptions = ACCESS_MODE_OPTIONS.map((value) => ({ label: value, value }));
const apply = () => {
emit('apply', {
volumeMode: localVolumeMode.value,
accessModes: [localAccessMode.value],
});
emit('close');
};
// Repopulate the dropdowns with the provider default values; the user then applies.
const reset = () => {
localVolumeMode.value = props.inheritedVolumeMode || VOLUME_MODE.FILE_SYSTEM;
localAccessMode.value = props.inheritedAccessModes?.[0] || READ_WRITE_MANY;
};
const cancel = () => {
emit('close');
};
</script>
<template>
<app-modal
class="storage-defaults-modal"
name="storageDefaultsDialog"
:width="620"
height="auto"
:click-to-close="false"
@close="cancel"
>
<Card
class="storage-defaults-card"
:show-highlight-border="false"
>
<template #title>
<h4 class="text-default-text">
{{ t('harvester.addons.vmMigration.storageDefaults.title', { name: storageClassName }) }}
</h4>
</template>
<template #body>
<p class="text-deemphasized description">
<template v-if="showInherited">
{{ t('harvester.addons.vmMigration.storageDefaults.descriptionInherited', { name: storageClassName, provider: providerName }) }}
</template>
<template v-else>
{{ t('harvester.addons.vmMigration.storageDefaults.description', { name: storageClassName }) }}
</template>
</p>
<div class="settings-box">
<LabeledSelect
v-model:value="localVolumeMode"
class="volume-mode"
:label="t('harvester.addons.vmMigration.storageDefaults.volumeMode')"
:options="volumeModeOptions"
:searchable="false"
:clearable="false"
/>
<LabeledSelect
v-model:value="localAccessMode"
class="access-mode"
:label="t('harvester.addons.vmMigration.storageDefaults.accessMode')"
:options="accessModeOptions"
:searchable="false"
:clearable="false"
/>
</div>
</template>
<template #actions>
<div class="buttons">
<button
class="btn role-secondary"
@click="cancel"
>
{{ t('harvester.addons.vmMigration.storageDefaults.cancel') }}
</button>
<div class="right-buttons">
<button
v-if="showInherited"
class="btn role-secondary mr-10"
@click="reset"
>
{{ t('harvester.addons.vmMigration.storageDefaults.resetToProviderDefault') }}
</button>
<button
class="btn role-primary"
:disabled="!localAccessMode"
@click="apply"
>
{{ showInherited ? t('harvester.addons.vmMigration.storageDefaults.applyOverride') : t('harvester.addons.vmMigration.storageDefaults.apply') }}
</button>
</div>
</div>
</template>
</Card>
</app-modal>
</template>
<style lang="scss" scoped>
.storage-defaults-modal {
z-index: 45;
}
.storage-defaults-card {
margin: 0;
padding: 20px;
}
.description {
margin-bottom: 16px;
line-height: 20px;
}
.settings-box {
display: flex;
flex-direction: column;
gap: 20px;
padding: 16px;
border-radius: 6px;
background-color: var(--category-active);
}
.volume-mode {
max-width: 280px;
}
.access-mode {
max-width: 280px;
}
.buttons {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.right-buttons {
display: flex;
}
}
</style>
@@ -390,14 +390,17 @@ export default {
:vmi-resource="vmi" :vmi-resource="vmi"
:vmim-resource="vmim" :vmim-resource="vmim"
/> />
<DashboardMetrics <div
v-if="showVmMetrics && liveMigrationProgressEnabled" v-if="showVmMetrics && liveMigrationProgressEnabled"
:detail-url="VM_MIGRATION_DETAIL_URL" class="migration-metrics-scroll mb-30"
graph-height="640px" >
:has-summary-and-detail="false" <DashboardMetrics
:vars="graphVars" :detail-url="VM_MIGRATION_DETAIL_URL"
class="mb-30" graph-height="960px"
/> :has-summary-and-detail="false"
:vars="graphVars"
/>
</div>
<Events <Events
v-if="liveMigrationProgressEnabled" v-if="liveMigrationProgressEnabled"
:resource="vmi" :resource="vmi"
@@ -436,3 +439,10 @@ export default {
</Tabbed> </Tabbed>
</div> </div>
</template> </template>
<style lang="scss" scoped>
.migration-metrics-scroll {
max-height: 640px;
overflow-y: auto;
}
</style>
@@ -2,13 +2,16 @@
import { exceptionToErrorsArray } from '@shell/utils/error'; import { exceptionToErrorsArray } from '@shell/utils/error';
import { sortBy } from '@shell/utils/sort'; import { sortBy } from '@shell/utils/sort';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import { PVC } from '@shell/config/types'; import { PVC, STORAGE_CLASS, LONGHORN_DRIVER } from '@shell/config/types';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations'; import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
import { VOLUME_MODE } from '@pkg/harvester/config/types';
import { HCI } from '@pkg/harvester/types';
import { Card } from '@components/Card'; import { Card } from '@components/Card';
import { Banner } from '@components/Banner'; import { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton'; import AsyncButton from '@shell/components/AsyncButton';
import { LabeledInput } from '@components/Form/LabeledInput'; import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect'; import LabeledSelect from '@shell/components/form/LabeledSelect';
import { Checkbox } from '@components/Form/Checkbox';
export default { export default {
name: 'HotplugVolumeModal', name: 'HotplugVolumeModal',
@@ -16,7 +19,7 @@ export default {
emits: ['close'], emits: ['close'],
components: { components: {
AsyncButton, Card, LabeledInput, LabeledSelect, Banner AsyncButton, Card, LabeledInput, LabeledSelect, Banner, Checkbox
}, },
props: { props: {
@@ -28,14 +31,19 @@ export default {
async fetch() { async fetch() {
this.allPVCs = await this.$store.dispatch('harvester/findAll', { type: PVC }); this.allPVCs = await this.$store.dispatch('harvester/findAll', { type: PVC });
this.allStorageClasses = await this.$store.dispatch('harvester/findAll', { type: STORAGE_CLASS });
this.allVMs = await this.$store.dispatch('harvester/findAll', { type: HCI.VM });
}, },
data() { data() {
return { return {
diskName: '', diskName: '',
volumeName: '', volumeName: '',
errors: [], shareable: false,
allPVCs: [], errors: [],
allPVCs: [],
allStorageClasses: [],
allVMs: [],
}; };
}, },
@@ -50,6 +58,31 @@ export default {
return this.resources[0]; return this.resources[0];
}, },
// claim names already attached to the VM this modal was opened for
currentVMClaimNames() {
const volumes = this.actionResource?.spec?.template?.spec?.volumes || [];
return volumes.map((vol) => vol.persistentVolumeClaim?.claimName).filter((name) => !!name);
},
// claim names attached to other VMs in the same namespace
otherVMClaimNames() {
const out = [];
this.allVMs.forEach((vm) => {
if (vm.metadata.namespace !== this.actionResource?.metadata?.namespace || vm.id === this.actionResource?.id) {
return;
}
(vm.spec?.template?.spec?.volumes || []).forEach((vol) => {
if (vol.persistentVolumeClaim?.claimName) {
out.push(vol.persistentVolumeClaim.claimName);
}
});
});
return out;
},
volumeOption() { volumeOption() {
return sortBy( return sortBy(
this.PVCs this.PVCs
@@ -61,6 +94,14 @@ export default {
if (pvc.isGoldenImageVolume) { if (pvc.isGoldenImageVolume) {
return false; return false;
} }
// a volume attached to this VM can never be attached to it again
if (this.currentVMClaimNames.includes(pvc.metadata.name)) {
return false;
}
// a volume attached to another VM can only be re-attached as a shareable disk
if (this.otherVMClaimNames.includes(pvc.metadata.name) && !this.isShareableCapablePVC(pvc)) {
return false;
}
return true; return true;
}) })
@@ -73,19 +114,62 @@ export default {
'label' 'label'
); );
}, },
selectedPVC() {
return this.PVCs.find((P) => P.metadata.name === this.volumeName);
},
isShareableCapable() {
return this.selectedPVC ? this.isShareableCapablePVC(this.selectedPVC) : false;
},
},
watch: {
isShareableCapable(neu) {
if (!neu) {
this.shareable = false;
}
},
}, },
methods: { methods: {
isShareableCapablePVC(pvc) {
const pvcSpec = pvc?.spec;
if (!pvcSpec) {
return false;
}
const isRWX = (pvcSpec.accessModes || []).includes('ReadWriteMany');
const isBlock = pvcSpec.volumeMode === VOLUME_MODE.BLOCK;
const storageClass = this.allStorageClasses.find((sc) => sc.name === pvcSpec.storageClassName);
// fail closed: without a resolved StorageClass the provisioner
// requirement cannot be evaluated
if (!storageClass) {
return false;
}
return isRWX && isBlock && storageClass.provisioner !== LONGHORN_DRIVER;
},
close() { close() {
this.diskName = ''; this.diskName = '';
this.volumeName = ''; this.volumeName = '';
this.shareable = false;
this.$emit('close'); this.$emit('close');
}, },
async save(buttonCb) { async save(buttonCb) {
if (this.actionResource) { if (this.actionResource) {
try { try {
const res = await this.actionResource.doAction('addVolume', { volumeSourceName: this.volumeName, diskName: this.diskName }, {}, false); const input = { volumeSourceName: this.volumeName, diskName: this.diskName };
if (this.isShareableCapable && this.shareable) {
input.shareable = true;
}
const res = await this.actionResource.doAction('addVolume', input, {}, false);
if (res._status === 200 || res._status === 204) { if (res._status === 200 || res._status === 204) {
this.$store.dispatch('growl/success', { this.$store.dispatch('growl/success', {
@@ -141,6 +225,19 @@ export default {
class="mt-20" class="mt-20"
required required
/> />
<Checkbox
v-if="isShareableCapable"
v-model:value="shareable"
class="mt-20"
type="checkbox"
label-key="harvester.virtualMachine.volume.shareable.label"
tooltip-key="harvester.virtualMachine.volume.shareable.tip"
/>
<Banner
v-if="isShareableCapable && shareable"
color="warning"
:label="t('harvester.virtualMachine.volume.shareable.warning')"
/>
<Banner <Banner
v-for="(err, i) in errors" v-for="(err, i) in errors"
:key="i" :key="i"
@@ -0,0 +1,155 @@
<script>
import merge from 'lodash/merge';
import jsyaml from 'js-yaml';
import { LabeledInput } from '@components/Form/LabeledInput';
import { RadioGroup } from '@components/Form/Radio';
const DEFAUL_VALUE = {
rancher: {
hostname: '',
version: '',
bootstrapPassword: '',
},
};
export default {
name: 'EditAddonK3k',
components: { LabeledInput, RadioGroup },
inheritAttrs: false,
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
registerBeforeHook: {
type: Function,
required: true,
},
},
data() {
let valuesContentJson = {};
try {
valuesContentJson = merge({}, DEFAUL_VALUE, jsyaml.load(this.value.spec.valuesContent));
} catch (err) {
valuesContentJson = DEFAUL_VALUE;
this.$store.dispatch('growl/fromError', {
title: this.$store.getters['i18n/t']('generic.notification.title.error'),
err: err.data || err,
}, { root: true });
}
return { valuesContentJson };
},
created() {
if (this.registerBeforeHook) {
this.registerBeforeHook(this.willSave, 'willSave');
}
},
methods: {
willSave() {
const errors = [];
if (!this.value.spec.enabled) {
return Promise.resolve();
}
if (!this.valuesContentJson.rancher.hostname) {
errors.push(this.t('validation.required', { key: this.t('harvester.addons.rancherK3k.hostname') }, true));
}
if (!this.valuesContentJson.rancher.bootstrapPassword) {
errors.push(this.t('validation.required', { key: this.t('harvester.addons.rancherK3k.password') }, true));
}
if (errors.length > 0) {
return Promise.reject(errors);
} else {
return Promise.resolve();
}
},
},
watch: {
valuesContentJson: {
handler(neu) {
this.value.spec['valuesContent'] = jsyaml.dump(neu);
},
deep: true,
immediate: true
},
},
};
</script>
<template>
<div>
<div class="row">
<div class="col span-12">
<RadioGroup
v-model:value="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</div>
</div>
<template v-if="value.spec.enabled">
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model:value="valuesContentJson.rancher.hostname"
label-key="harvester.addons.rancherK3k.hostname"
:required="true"
:mode="mode"
placeholder="rancher.$vip.nip.io"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model:value="valuesContentJson.rancher.version"
label-key="harvester.addons.rancherK3k.version"
:required="true"
:disabled="false"
/>
</div>
</div>
<div class="row mt-20">
<div class="col span-6">
<LabeledInput
v-model:value="valuesContentJson.rancher.bootstrapPassword"
label-key="harvester.addons.rancherK3k.password"
:mode="mode"
:required="true"
type="password"
/>
</div>
</div>
</template>
</div>
</template>
<style lang="scss" scoped>
:deep() .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
+14 -3
View File
@@ -9,7 +9,7 @@ import Loading from '@shell/components/Loading';
import CreateEditView from '@shell/mixins/create-edit-view'; import CreateEditView from '@shell/mixins/create-edit-view';
import { RadioGroup } from '@components/Form/Radio'; import { RadioGroup } from '@components/Form/Radio';
import { NETWORK_PROTOCOL, NETWORK_TYPE } from '@pkg/harvester/config/types'; import { NETWORK_PROTOCOL, NETWORK_TYPE } from '@pkg/harvester/config/types';
import { set } from '@shell/utils/object'; import { set, remove } from '@shell/utils/object';
import ArrayList from '@shell/components/form/ArrayList'; import ArrayList from '@shell/components/form/ArrayList';
import { allHash } from '@shell/utils/promise'; import { allHash } from '@shell/utils/promise';
import { HCI } from '../../types'; import { HCI } from '../../types';
@@ -142,10 +142,12 @@ export default {
const inStore = this.$store.getters['currentProduct'].inStore; const inStore = this.$store.getters['currentProduct'].inStore;
const vlans = this.$store.getters[`${ inStore }/all`](HCI.VLAN) || []; const vlans = this.$store.getters[`${ inStore }/all`](HCI.VLAN) || [];
return vlans.map((vlan) => ({ const options = vlans.map((vlan) => ({
label: vlan.id, label: vlan.id,
value: vlan.id, value: vlan.id,
})); }));
return [{ label: this.t('generic.none'), value: '' }, ...options];
} }
}, },
@@ -160,6 +162,14 @@ export default {
} }
}, },
methods: { methods: {
onVlanChange(value) {
if (value === '') {
remove(this.value.spec, 'vlan');
} else {
set(this.value, 'spec.vlan', value);
}
},
async saveSubnet(buttonCb) { async saveSubnet(buttonCb) {
const errors = []; const errors = [];
const name = this.value?.metadata?.name; const name = this.value?.metadata?.name;
@@ -283,12 +293,13 @@ export default {
</div> </div>
<div class="col span-6"> <div class="col span-6">
<LabeledSelect <LabeledSelect
v-model:value="value.spec.vlan" :value="value.spec.vlan ?? ''"
class="mb-20" class="mb-20"
:options="vlanOptions" :options="vlanOptions"
:placeholder="t('harvester.subnet.vlan.placeholder')" :placeholder="t('harvester.subnet.vlan.placeholder')"
:label="t('harvester.subnet.vlan.label')" :label="t('harvester.subnet.vlan.label')"
:mode="mode" :mode="mode"
@update:value="onVlanChange"
/> />
</div> </div>
</div> </div>
@@ -68,12 +68,6 @@ export default {
}, },
watch: { watch: {
osType(neu) {
if (neu === 'windows') {
this.id = '';
}
},
value(neu) { value(neu) {
this.yamlScript = neu; this.yamlScript = neu;
}, },
@@ -246,4 +246,27 @@ export default {
.group-actions { .group-actions {
display: inline; display: inline;
} }
// Match the enable/disable passthrough bulk-action buttons height to the .btn (40px) on the right.
:deep(.bulk .rc-button.btn-medium.bulk-action:not(.btn-sm)) {
min-height: 40px;
}
// Make the collapsed "Actions" dropdown button 40px too.
:deep(.bulk .rc-button.btn-medium.bulk-actions-dropdown:not(.btn-sm)) {
min-height: 40px;
}
// Lay the bulk row out with flex so the "N selected" label can wrap below the buttons.
:deep(.bulk) {
display: flex;
flex-wrap: wrap;
align-items: center;
}
:deep(.bulk .action-availability) {
flex-basis: 100%;
margin-left: 0;
margin-top: 6px;
}
</style> </style>
@@ -5,6 +5,7 @@ import LabeledSelect from '@shell/components/form/LabeledSelect';
import Banner from '@components/Banner/Banner.vue'; import Banner from '@components/Banner/Banner.vue';
import remove from 'lodash/remove'; import remove from 'lodash/remove';
import { set } from '@shell/utils/object'; import { set } from '@shell/utils/object';
import { uniq } from '@shell/utils/array';
import { HCI } from '../../../types'; import { HCI } from '../../../types';
import DeviceList from './DeviceList'; import DeviceList from './DeviceList';
import CompatibilityMatrix from '../CompatibilityMatrix'; import CompatibilityMatrix from '../CompatibilityMatrix';
@@ -60,7 +61,15 @@ export default {
row.allowDisable = !vmDeviceNames.includes(row.metadata.name); row.allowDisable = !vmDeviceNames.includes(row.metadata.name);
}); });
vmDevices.forEach(({ name }) => { // When off, read from spec; otherwise the spec name is a placeholder ('provisioned'),
// so read the real allocated device names from the deviceAllocationDetails annotation.
const hostDeviceNames = this.vm.isOff ? [
...vmDevices.map(({ name }) => name),
] : [
...Object.values(this.vm?.provisionedHostDevices || {}).reduce((acc, devices) => [...acc, ...devices], []),
];
uniq(hostDeviceNames).forEach((name) => {
if (this.enabledDevices.find((device) => device?.metadata?.name === name)) { if (this.enabledDevices.find((device) => device?.metadata?.name === name)) {
selectedDevices.push(name); selectedDevices.push(name);
} }
@@ -5,10 +5,12 @@ import LabelValue from '@shell/components/LabelValue';
import LabeledSelect from '@shell/components/form/LabeledSelect'; import LabeledSelect from '@shell/components/form/LabeledSelect';
import InputOrDisplay from '@shell/components/InputOrDisplay'; import InputOrDisplay from '@shell/components/InputOrDisplay';
import { Banner } from '@components/Banner'; import { Banner } from '@components/Banner';
import { Checkbox } from '@components/Form/Checkbox';
import { sortBy } from '@shell/utils/sort'; import { sortBy } from '@shell/utils/sort';
import { PVC } from '@shell/config/types'; import { PVC, STORAGE_CLASS, LONGHORN_DRIVER } from '@shell/config/types';
import { _CREATE } from '@shell/config/query-params'; import { _CREATE } from '@shell/config/query-params';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations'; import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
import { VOLUME_MODE } from '@pkg/harvester/config/types';
import { HCI } from '../../../../types'; import { HCI } from '../../../../types';
import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map'; import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map';
import { GIBIBYTE } from '../../../../utils/unit'; import { GIBIBYTE } from '../../../../utils/unit';
@@ -19,7 +21,7 @@ export default {
emits: ['update'], emits: ['update'],
components: { components: {
UnitInput, LabeledInput, LabeledSelect, InputOrDisplay, LabelValue, Banner UnitInput, LabeledInput, LabeledSelect, InputOrDisplay, LabelValue, Banner, Checkbox
}, },
props: { props: {
@@ -94,6 +96,30 @@ export default {
return this.allPVCs.find( (P) => P.metadata.name === this.value.volumeName ); return this.allPVCs.find( (P) => P.metadata.name === this.value.volumeName );
}, },
storageClasses() {
return this.$store.getters['harvester/all'](STORAGE_CLASS) || [];
},
isShareableCapable() {
const pvcSpec = this.pvcResource?.spec;
if (!pvcSpec) {
return false;
}
const isRWX = (pvcSpec.accessModes || []).includes('ReadWriteMany');
const isBlock = pvcSpec.volumeMode === VOLUME_MODE.BLOCK;
const storageClass = this.storageClasses.find((sc) => sc.name === pvcSpec.storageClassName);
// fail closed: without a resolved StorageClass the provisioner
// requirement cannot be evaluated
if (!storageClass) {
return false;
}
return isRWX && isBlock && storageClass.provisioner !== LONGHORN_DRIVER;
},
volumeOption() { volumeOption() {
return sortBy( return sortBy(
this.allPVCs this.allPVCs
@@ -145,6 +171,7 @@ export default {
this.value.size = pvcResource.spec.resources.requests.storage; this.value.size = pvcResource.spec.resources.requests.storage;
this.value.storageClassName = pvcResource.spec.storageClassName; this.value.storageClassName = pvcResource.spec.storageClassName;
this.value.volumeMode = pvcResource.spec.volumeMode; this.value.volumeMode = pvcResource.spec.volumeMode;
this.value.shareable = false;
this.update(); this.update();
}, },
@@ -155,6 +182,13 @@ export default {
} }
}, },
isShareableCapable(neu) {
if (!neu && this.value.shareable) {
this.value.shareable = false;
this.update();
}
},
pvcResource: { pvcResource: {
handler(pvc) { handler(pvc) {
if (!this.value.volumeName && pvc?.metadata?.name) { if (!this.value.volumeName && pvc?.metadata?.name) {
@@ -303,6 +337,26 @@ export default {
/> />
</InputOrDisplay> </InputOrDisplay>
</div> </div>
<div
v-if="isShareableCapable"
data-testid="input-hee-shareable"
class="col span-6"
>
<Checkbox
v-model:value="value.shareable"
class="check"
type="checkbox"
label-key="harvester.virtualMachine.volume.shareable.label"
tooltip-key="harvester.virtualMachine.volume.shareable.tip"
:mode="mode"
@update:value="update"
/>
<Banner
v-if="value.shareable"
color="warning"
:label="t('harvester.virtualMachine.volume.shareable.warning')"
/>
</div>
<div <div
v-if="value.volumeBackups" v-if="value.volumeBackups"
class="col span-6" class="col span-6"
@@ -1062,7 +1062,6 @@ export default {
</div> </div>
<CloudConfig <CloudConfig
v-if="!isWindows"
ref="yamlEditor" ref="yamlEditor"
:user-script="userScript" :user-script="userScript"
:mode="mode" :mode="mode"
@@ -63,6 +63,7 @@ export default {
RANCHER_TYPES, RANCHER_TYPES,
fvFormRuleSets: [{ path: 'spec.displayName', rules: ['required'] }], fvFormRuleSets: [{ path: 'spec.displayName', rules: ['required'] }],
canEditPSPBindings: true, canEditPSPBindings: true,
isQuotasValid: true,
}; };
}, },
computed: { computed: {
@@ -190,15 +191,13 @@ export default {
this['membershipUpdate'] = update; this['membershipUpdate'] = update;
}, },
removeQuota(key) { validateQuotas(isValid) {
['resourceQuota', 'namespaceDefaultResourceQuota'].forEach((specProp) => { this.isQuotasValid = isValid;
if (this.value?.spec[specProp]?.limit && this.value?.spec[specProp]?.limit[key]) { },
delete this.value?.spec[specProp]?.limit[key];
} onQuotasInput({ projectLimit, nsLimit } = {}) {
if (this.value?.spec[specProp]?.usedLimit && this.value?.spec[specProp]?.usedLimit[key]) { this.value.spec.resourceQuota = { ...this.value.spec.resourceQuota, limit: projectLimit };
delete this.value?.spec[specProp]?.usedLimit[key]; this.value.spec.namespaceDefaultResourceQuota = { ...this.value.spec.namespaceDefaultResourceQuota, limit: nsLimit };
}
});
} }
}, },
}; };
@@ -212,7 +211,7 @@ export default {
:resource="value" :resource="value"
:subtypes="[]" :subtypes="[]"
:can-yaml="false" :can-yaml="false"
:validation-passed="fvFormIsValid" :validation-passed="fvFormIsValid && isQuotasValid"
@error="e=>errors=e" @error="e=>errors=e"
@finish="save" @finish="save"
@cancel="done" @cancel="done"
@@ -267,10 +266,11 @@ export default {
:weight="9" :weight="9"
> >
<ResourceQuota <ResourceQuota
v-model:value="value" :value="value"
:mode="canEditTabElements" :mode="canEditTabElements"
:types="isStandaloneHarvester ? HARVESTER_TYPES : RANCHER_TYPES" :types="isStandaloneHarvester ? HARVESTER_TYPES : RANCHER_TYPES"
@remove="removeQuota" @input="onQuotasInput"
@validationChanged="validateQuotas"
/> />
</Tab> </Tab>
<Tab <Tab
@@ -132,16 +132,28 @@ export default {
</script> </script>
<template> <template>
<div v-if="showIP"> <div
<span v-if="showIP"
class="ip-list"
>
<div
v-for="{ ip, name, isCustom } in ips" v-for="{ ip, name, isCustom } in ips"
:key="`${ip}-${name}`" :key="`${ip}-${name}`"
class="ip-item"
> >
<CopyToClipboardText <CopyToClipboardText
v-clean-tooltip="isCustom ? t('harvester.formatters.harvesterIpAddress.customIpTooltip') : name" v-clean-tooltip="isCustom ? t('harvester.formatters.harvesterIpAddress.customIpTooltip') : name"
:text="ip" :text="ip"
:plain="isCustom" :plain="isCustom"
/> />
</span> </div>
</div> </div>
</template> </template>
<style lang="scss" scoped>
.ip-list {
display: flex;
flex-direction: column;
gap: 6px;
}
</style>
+31 -6
View File
@@ -1,4 +1,4 @@
wizard: advancedSettingswizard:
create: Create create: Create
next: Proceed next: Proceed
previous: Back previous: Back
@@ -798,6 +798,10 @@ harvester:
setFirst: Set as root volume setFirst: Set as root volume
saveVolume: Update Volume saveVolume: Update Volume
encryption: Encryption encryption: Encryption
shareable:
label: Shareable
tip: Allow multiple virtual machines to attach and write to this volume simultaneously.
warning: Multiple virtual machines can write to this volume at the same time. The guest workloads must coordinate access to the shared block device, otherwise concurrent writes will corrupt the data. Workloads that fence through SCSI-3 Persistent Reservations (e.g. Windows Server Failover Clustering) also require persistent reservation support from the storage backend. Host cache and hypervisor disk locking are disabled, and backup or snapshot operations are rejected while the volume is shareable.
vmImageVolumeTip: Disk size ({diskSize}) should greater than selected image virtual size ({imageVirtualSize}) vmImageVolumeTip: Disk size ({diskSize}) should greater than selected image virtual size ({imageVirtualSize})
lockTooltip: lockTooltip:
all: All volumes are encrypted. all: All volumes are encrypted.
@@ -831,13 +835,13 @@ harvester:
title: "Network Data:" title: "Network Data:"
tip: "The network-data configuration allows you to customize the instance's networking interfaces by assigning subnet configuration, virtual device creation (bonds, bridges, VLANs) routes and DNS configuration. <a href='https://cloudinit.readthedocs.io/en/latest/reference/network-config-format-v1.html' target='_blank'>Learn more</a>" tip: "The network-data configuration allows you to customize the instance's networking interfaces by assigning subnet configuration, virtual device creation (bonds, bridges, VLANs) routes and DNS configuration. <a href='https://cloudinit.readthedocs.io/en/latest/reference/network-config-format-v1.html' target='_blank'>Learn more</a>"
sysprep: sysprep:
title: Windows Sysprep Configuration title: Windows Unattended & Sysprep Configuration
description: "Configure Windows automated installation using autounattend.xml. The configuration will be stored in a Kubernetes Secret and mounted as a CD-ROM during installation. <a href='https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/automate-windows-setup' target='_blank'>Learn more</a>" description: "Configure Windows automated installation using autounattend.xml. The configuration will be stored in a Kubernetes Secret and mounted as a CD-ROM during installation. <a href='https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/automate-windows-setup' target='_blank'>Learn more</a>"
createNew: Create new... createNew: Create new...
createNewTitle: Create Windows Sysprep Template createNewTitle: Create Windows Unattended & Sysprep Template
xmlContent: autounattend.xml content xmlContent: autounattend.xml content
secret: secret:
label: Windows Sysprep Template label: Windows Unattended & Sysprep Template
validation: validation:
invalidXml: Invalid XML syntax invalidXml: Invalid XML syntax
invalidNamespace: Invalid unattend.xml namespace. Expected 'urn:schemas-microsoft-com:unattend' invalidNamespace: Invalid unattend.xml namespace. Expected 'urn:schemas-microsoft-com:unattend'
@@ -1821,6 +1825,7 @@ harvester:
'harvester-system/pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machine and guest clusters. 'harvester-system/pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machine and guest clusters.
'cattle-logging-system/rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows. 'cattle-logging-system/rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows.
'harvester-system/rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed. 'harvester-system/rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed.
'k3k-system/k3k': k3k deploys a k3k virtual cluster with Rancher installed.
'cattle-monitoring-system/rancher-monitoring': rancher-monitoring is an add-on that collects Harvester cluster and virtual machine metrics and allows you to view the metrics on an embedded dashboard and send alert(s) to remote servers. 'cattle-monitoring-system/rancher-monitoring': rancher-monitoring is an add-on that collects Harvester cluster and virtual machine metrics and allows you to view the metrics on an embedded dashboard and send alert(s) to remote servers.
'vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing Harvester cluster. 'vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing Harvester cluster.
'pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machines and guest clusters. 'pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machines and guest clusters.
@@ -1828,6 +1833,7 @@ harvester:
'rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows. 'rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows.
'rancher-monitoring': rancher-monitoring is an add-on to collect Harvester cluster and virtual machine metrics, view them on the embedded dashboard, and send alert(s) to remote servers. 'rancher-monitoring': rancher-monitoring is an add-on to collect Harvester cluster and virtual machine metrics, view them on the embedded dashboard, and send alert(s) to remote servers.
'rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed. 'rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed.
'k3k': k3k deploys a k3k virtual cluster with Rancher installed.
'harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations. 'harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations.
'harvester-system/harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations. 'harvester-system/harvester-seeder': harvester-seeder is an add-on that uses IPMI and Redfish to discover hardware information and perform out-of-band operations.
'harvester-csi-driver-lvm': harvester-csi-driver-lvm is an add-on allowing users to create PVC through the LVM with local devices. 'harvester-csi-driver-lvm': harvester-csi-driver-lvm is an add-on allowing users to create PVC through the LVM with local devices.
@@ -1922,6 +1928,7 @@ harvester:
refreshNow: Refresh now refreshNow: Refresh now
availableVms: Available Virtual Machines availableVms: Available Virtual Machines
selected: selected selected: selected
clearAll: Clear All
saveSelection: Save Selection and Continue saveSelection: Save Selection and Continue
selectAllBanner: selectAllBanner:
pageOnly: "Your current selection includes only VMs currently shown on the page." pageOnly: "Your current selection includes only VMs currently shown on the page."
@@ -1982,6 +1989,19 @@ harvester:
description: Map VMware datastores to Harvester storage classes description: Map VMware datastores to Harvester storage classes
placeholder: Choose a Harvester Storage Class placeholder: Choose a Harvester Storage Class
template: Use existing storage mapping as template template: Use existing storage mapping as template
storageDefaults:
title: 'Storage defaults — {name}'
description: 'Set the volume mode and access modes applied to every datastore in this storage map that maps to {name}.'
descriptionInherited: 'Applies to every datastore in this storage map that maps to {name}. The provider default for {provider} stays unchanged.'
summary: 'Volume mode: {volumeMode} · Access mode: {accessModes}'
inherited: 'Inherited from provider {provider}'
volumeMode: Volume Mode
accessMode: Access Mode
edit: Edit
cancel: Cancel
apply: Apply
resetToProviderDefault: Reset to Provider Default
applyOverride: Apply Override
reviewMigration: reviewMigration:
title: Review Migration Plan title: Review Migration Plan
description: Confirm your migration settings before starting the transfer of VMs to the target cluster. description: Confirm your migration settings before starting the transfer of VMs to the target cluster.
@@ -2118,6 +2138,11 @@ harvester:
hostname: Hostname hostname: Hostname
rancherVersion: Rancher Version rancherVersion: Rancher Version
password: Bootstrap Password password: Bootstrap Password
rancherK3k:
accessRancher: Access the Rancher Dashboard
hostname: Hostname
version: Rancher Version
password: Bootstrap Password
switchFailed: "{action} {name} failed!" switchFailed: "{action} {name} failed!"
nvidiaDriverToolkit: nvidiaDriverToolkit:
titles: titles:
@@ -2448,8 +2473,8 @@ advancedSettings:
typeLabel: typeLabel:
kubevirt.io.virtualmachine: |- kubevirt.io.virtualmachine: |-
{count, plural, {count, plural,
one { Andy Virtual Machine } one { Virtual Machine }
other { Andy Virtual Machines } other { Virtual Machines }
} }
harvesterhci.io.virtualmachineimage: |- harvesterhci.io.virtualmachineimage: |-
{count, plural, {count, plural,
+1 -1
View File
@@ -72,7 +72,7 @@ export default {
/> />
<a <a
v-if="scope.row.metadata.name === 'rancher-vcluster' && scope.row.spec.enabled" v-if="['rancher-vcluster', 'k3k'].includes(scope.row.metadata.name) && scope.row.spec.enabled"
v-tooltip="t('harvester.addons.rancherVcluster.accessRancher')" v-tooltip="t('harvester.addons.rancherVcluster.accessRancher')"
class="ml-5" class="ml-5"
rel="nofollow noopener noreferrer" rel="nofollow noopener noreferrer"
+12
View File
@@ -38,6 +38,10 @@ export const SSH_EXISTING_TYPE = {
export default { export default {
methods: { methods: {
hasCloudConfigComment(userScript) { hasCloudConfigComment(userScript) {
if (typeof userScript === 'string' && /^\s*#cloud-config(\s|$)/.test(userScript)) {
return true;
}
// Check that userData contains: #cloud-config // Check that userData contains: #cloud-config
const userDataDoc = userScript ? YAML.parseDocument(userScript) : YAML.parseDocument({}); const userDataDoc = userScript ? YAML.parseDocument(userScript) : YAML.parseDocument({});
const items = userDataDoc?.contents?.items || []; const items = userDataDoc?.contents?.items || [];
@@ -52,6 +56,14 @@ export default {
exist = true; exist = true;
} }
if (userDataDoc?.contents?.commentBefore === 'cloud-config' || userDataDoc?.contents?.commentBefore?.includes('cloud-config\n')) {
exist = true;
}
if (userDataDoc?.contents?.comment === 'cloud-config' || userDataDoc?.contents?.comment?.includes('cloud-config\n')) {
exist = true;
}
items.map((item) => { items.map((item) => {
const key = item.key; const key = item.key;
+103 -45
View File
@@ -618,6 +618,7 @@ export default {
type, type,
storageClassName, storageClassName,
hotpluggable, hotpluggable,
shareable: DISK.shareable || false,
volumeStatus, volumeStatus,
dataSource, dataSource,
namespace, namespace,
@@ -791,18 +792,41 @@ export default {
} }
const staticIpPrefix = `${ HCI_ANNOTATIONS.STATIC_IP }/`; const staticIpPrefix = `${ HCI_ANNOTATIONS.STATIC_IP }/`;
const annotations = vm.metadata.annotations;
Object.keys(vm.metadata.annotations).forEach((key) => { const desired = {};
if (key.startsWith(staticIpPrefix)) {
delete vm.metadata.annotations[key];
}
});
this.networkRows.forEach((row) => { this.networkRows.forEach((row) => {
if (row.name && row.staticIp) { if (row.name && row.staticIp) {
vm.metadata.annotations[`${ staticIpPrefix }${ row.name }`] = row.staticIp; desired[`${ staticIpPrefix }${ row.name }`] = row.staticIp;
} }
}); });
const current = {};
Object.keys(annotations).forEach((key) => {
if (key.startsWith(staticIpPrefix)) {
current[key] = annotations[key];
}
});
// Skip mutation when already in sync to avoid triggering a reactive update loop.
const desiredKeys = Object.keys(desired);
const currentKeys = Object.keys(current);
const isSame = desiredKeys.length === currentKeys.length &&
desiredKeys.every((key) => current[key] === desired[key]);
if (isSame) {
return;
}
currentKeys.forEach((key) => {
delete annotations[key];
});
Object.entries(desired).forEach(([key, value]) => {
annotations[key] = value;
});
}, },
setCPUAndMemory() { setCPUAndMemory() {
@@ -917,37 +941,43 @@ export default {
} }
if (!disks.find( (D) => D.name === 'cloudinitdisk') && (this.userData || this.networkScript)) { if (!disks.find( (D) => D.name === 'cloudinitdisk') && (this.userData || this.networkScript)) {
if (!this.isWindows) { disks.push({
disks.push({ name: 'cloudinitdisk',
name: 'cloudinitdisk', disk: { bus: 'virtio' }
disk: { bus: 'virtio' } });
});
const userData = this.getUserData({ osType: this.osType, installAgent: this.installAgent }); const userData = this.getUserData({ osType: this.osType, installAgent: this.installAgent });
const cloudinitdisk = { const cloudinitdisk = {
name: 'cloudinitdisk', name: 'cloudinitdisk',
cloudInitNoCloud: {} cloudInitNoCloud: {}
}; };
if (this.saveUserDataAsClearText) { if (this.saveUserDataAsClearText) {
cloudinitdisk.cloudInitNoCloud.userData = userData; cloudinitdisk.cloudInitNoCloud.userData = userData;
} else { } else {
cloudinitdisk.cloudInitNoCloud.secretRef = { name: this.secretName }; cloudinitdisk.cloudInitNoCloud.secretRef = { name: this.secretName };
}
if (this.saveNetworkDataAsClearText) {
cloudinitdisk.cloudInitNoCloud.networkData = this.networkScript;
} else {
cloudinitdisk.cloudInitNoCloud.networkDataSecretRef = { name: this.secretName };
}
volumes.push(cloudinitdisk);
} }
if (this.saveNetworkDataAsClearText) {
cloudinitdisk.cloudInitNoCloud.networkData = this.networkScript;
} else {
cloudinitdisk.cloudInitNoCloud.networkDataSecretRef = { name: this.secretName };
}
volumes.push(cloudinitdisk);
} }
const specDisks = this.spec?.template?.spec?.domain?.devices?.disks; const specDisks = this.spec?.template?.spec?.domain?.devices?.disks;
const mergedDisks = this.mergeDeviceList(specDisks, disks); const mergedDisks = this.mergeDeviceList(specDisks, disks);
// `shareable` is an attachment-level opt-in, remove the field
// inherited from the old spec when the disk row no longer requests it
mergedDisks.forEach((disk) => {
if (disk.shareable && !disks.find((D) => D.name === disk.name)?.shareable) {
delete disk.shareable;
}
});
let spec = { let spec = {
...this.spec, ...this.spec,
runStrategy: this.runStrategy, runStrategy: this.runStrategy,
@@ -1144,6 +1174,16 @@ export default {
}, },
getInitUserData(config) { getInitUserData(config) {
// Windows guests don't use qemu-guest-agent via systemd (VMDP installs
// the QGA as a Windows service), and the Linux `runcmd` recipe would
// fail on Cloudbase-Init. Return an empty string so `this.userData`
// stays falsy on Windows until the user actually enters cloud-config
// content — this prevents emitting a spurious cloudinitdisk volume
// and Secret for every Windows VM (Copilot review comment on #984).
if (config.osType === 'windows') {
return '';
}
const _QGA_JSON = this.getMatchQGA(config.osType); const _QGA_JSON = this.getMatchQGA(config.osType);
const out = jsyaml.dump(_QGA_JSON); const out = jsyaml.dump(_QGA_JSON);
@@ -1171,9 +1211,10 @@ export default {
userDataDoc = config.installAgent ? this.mergeQGA({ userDataDoc, ...config }) : this.deleteQGA({ userDataDoc, ...config }); userDataDoc = config.installAgent ? this.mergeQGA({ userDataDoc, ...config }) : this.deleteQGA({ userDataDoc, ...config });
const userDataYaml = userDataDoc.toString(); const userDataYaml = userDataDoc.toString();
const userDataValue = userDataDoc.toJSON();
if (userDataYaml === '{}\n') { if (userDataValue === null || (typeof userDataValue === 'object' && !Array.isArray(userDataValue) && isEmpty(userDataValue))) {
// When the YAML parsed value is '{}\n', it means that the userData is empty, then undefined is returned. // Empty userData should not create cloud-init content like `null`.
return undefined; return undefined;
} }
@@ -1222,6 +1263,10 @@ export default {
out.cdrom = { bus: R.bus }; out.cdrom = { bus: R.bus };
} }
if (R.shareable) {
out.shareable = true;
}
out.bootOrder = index + 1; out.bootOrder = index + 1;
return out; return out;
@@ -1346,13 +1391,22 @@ export default {
*/ */
deleteYamlDocProp(doc, paths) { deleteYamlDocProp(doc, paths) {
try { try {
const item = doc.getIn([])?.items[0]; const items = doc.getIn([])?.items;
const key = item?.key; const firstKey = items?.[0]?.key;
const hasCloudConfigComment = !!key?.commentBefore?.includes('cloud-config'); const hasCloudConfigComment = !!firstKey?.commentBefore?.includes('cloud-config');
const isMatchProp = key.source === paths[paths.length - 1]; const isFirstProp = firstKey?.source === paths[paths.length - 1];
if (key && hasCloudConfigComment && isMatchProp) { if (firstKey && hasCloudConfigComment && isFirstProp) {
// Comments are mounted on the next node and we should not delete the node containing cloud-config const comment = firstKey.commentBefore;
doc.deleteIn(paths);
// Move the comment to the new first key; if no keys remain the comment is lost.
const newFirstKey = doc.getIn([])?.items?.[0]?.key;
if (newFirstKey) {
newFirstKey.commentBefore = comment;
}
} else { } else {
doc.deleteIn(paths); doc.deleteIn(paths);
} }
@@ -1403,7 +1457,6 @@ export default {
if (packages.length > 0) { if (packages.length > 0) {
userDataDoc.setIn(['packages'], packages); userDataDoc.setIn(['packages'], packages);
} else { } else {
userDataDoc.setIn(['packages'], []); // It needs to be set empty first, as it is possible that cloud-init comments are mounted on this node
this.deleteYamlDocProp(userDataDoc, ['packages']); this.deleteYamlDocProp(userDataDoc, ['packages']);
this.deleteYamlDocProp(userDataDoc, ['package_update']); this.deleteYamlDocProp(userDataDoc, ['package_update']);
} }
@@ -1418,7 +1471,7 @@ export default {
}, },
deleteQGA(config) { deleteQGA(config) {
const { osType, userDataDoc, deletePackage = false } = config; const { osType, userDataDoc } = config;
const userDataTemplateValue = this.$store.getters['harvester/byId'](CONFIG_MAP, this.userDataTemplateId)?.data?.cloudInit || ''; const userDataTemplateValue = this.$store.getters['harvester/byId'](CONFIG_MAP, this.userDataTemplateId)?.data?.cloudInit || '';
@@ -1427,6 +1480,15 @@ export default {
const packages = userDataJSON?.packages || []; const packages = userDataJSON?.packages || [];
const runcmd = userDataJSON?.runcmd || []; const runcmd = userDataJSON?.runcmd || [];
// Special handling of OS types.
let deletePackage = config.deletePackage ?? false;
switch (osType) {
case 'windows':
deletePackage = true;
break;
}
if (Array.isArray(packages) && deletePackage) { if (Array.isArray(packages) && deletePackage) {
const templateHasQGAPackage = this.convertToJson(userDataTemplateValue); const templateHasQGAPackage = this.convertToJson(userDataTemplateValue);
@@ -1452,7 +1514,6 @@ export default {
if (packages.length > 0) { if (packages.length > 0) {
userDataDoc.setIn(['packages'], packages); userDataDoc.setIn(['packages'], packages);
} else { } else {
userDataDoc.setIn(['packages'], []);
this.deleteYamlDocProp(userDataDoc, ['packages']); this.deleteYamlDocProp(userDataDoc, ['packages']);
this.deleteYamlDocProp(userDataDoc, ['package_update']); this.deleteYamlDocProp(userDataDoc, ['package_update']);
} }
@@ -1485,7 +1546,7 @@ export default {
}, },
async saveSecret(vm) { async saveSecret(vm) {
if (!vm?.spec || !this.secretName || this.isWindows) { if (!vm?.spec || !this.secretName) {
return true; return true;
} }
@@ -1906,8 +1967,6 @@ export default {
isWindows(val) { isWindows(val) {
if (val) { if (val) {
this['sshKey'] = []; this['sshKey'] = [];
this['userScript'] = undefined;
this['networkScript'] = undefined;
this['installAgent'] = false; this['installAgent'] = false;
} }
}, },
@@ -1967,9 +2026,8 @@ export default {
osType(neu, old) { osType(neu, old) {
this.installAgent = old === 'windows' ? true : this.installAgent; this.installAgent = old === 'windows' ? true : this.installAgent;
const out = old === 'windows' ? this.getInitUserData({ osType: neu }) : this.getUserData({ installAgent: this.installAgent, osType: neu }); this.userScript = this.getUserData({ installAgent: this.installAgent, osType: neu });
this['userScript'] = out;
this.refreshYamlEditor(); this.refreshYamlEditor();
}, },
+5
View File
@@ -69,6 +69,11 @@ export default class HarvesterResource extends SteveModel {
} }
} }
// Disable Resources/ Extras cards in resource details pages
get resourcesCardRows() {
return [];
}
cleanForSave(data, _forNew) { cleanForSave(data, _forNew) {
return data; return data;
} }
@@ -5,6 +5,7 @@ import SYSTEM_NAMESPACES from '@shell/config/system-namespaces';
import { get } from '@shell/utils/object'; import { get } from '@shell/utils/object';
import { NAMESPACE } from '@shell/config/types'; import { NAMESPACE } from '@shell/config/types';
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester'; import { PRODUCT_NAME as HARVESTER_PRODUCT } from '@pkg/harvester/config/harvester';
import { FORKLIFT_NAMESPACE } from '@pkg/harvester/config/harvester-map';
import { HCI } from '../../types'; import { HCI } from '../../types';
const OBSCURE_NAMESPACE_PREFIX = [ const OBSCURE_NAMESPACE_PREFIX = [
@@ -93,6 +94,10 @@ export default class HciNamespace extends namespace {
} }
get isSystem() { get isSystem() {
if (this.metadata?.name === FORKLIFT_NAMESPACE) {
return false;
}
const systemNamespaces = ['fleet-default']; const systemNamespaces = ['fleet-default'];
if (systemNamespaces.includes(this.metadata.name)) { if (systemNamespaces.includes(this.metadata.name)) {
@@ -5,12 +5,14 @@ import HarvesterResource from './harvester';
import { HCI } from '../types'; import { HCI } from '../types';
const HARVESTER_NVIDIA_DRIVER_TOOLKIT = 'harvester-system/nvidia-driver-toolkit'; const HARVESTER_NVIDIA_DRIVER_TOOLKIT = 'harvester-system/nvidia-driver-toolkit';
const RANCHER_VCLUSTER = 'harvester-system/rancher-vcluster';
const RANCHER_K3K = 'k3k-system/k3k';
export default class HciAddonConfig extends HarvesterResource { export default class HciAddonConfig extends HarvesterResource {
get availableActions() { get availableActions() {
const out = super._availableActions; const out = super._availableActions;
if (this.id === 'harvester-system/rancher-vcluster') { if (this.id === RANCHER_VCLUSTER || this.id === RANCHER_K3K) {
const rancherDashboard = { const rancherDashboard = {
action: 'goToRancher', action: 'goToRancher',
enabled: this.spec.enabled, enabled: this.spec.enabled,
@@ -69,18 +71,19 @@ export default class HciAddonConfig extends HarvesterResource {
} }
goToRancher() { goToRancher() {
const valuesContent = jsyaml.load(this.spec.valuesContent);
window.open( window.open(
`https://${ valuesContent.hostname }`, this.rancherHostname,
'_blank', '_blank',
); );
} }
get rancherHostname() { get rancherHostname() {
const valuesContent = jsyaml.load(this.spec.valuesContent); const valuesContent = jsyaml.load(this.spec.valuesContent) || {};
return `https://${ valuesContent.hostname }`; // rancher-k3k nests the hostname under `rancher`, rancher-vcluster keeps it at the top level
const hostname = this.id === RANCHER_K3K ? valuesContent.rancher?.hostname : valuesContent.hostname;
return `https://${ hostname }`;
} }
get stateColor() { get stateColor() {
@@ -1315,6 +1315,18 @@ export default class VirtVm extends HarvesterResource {
} }
} }
get provisionedHostDevices() {
// Rancher-provisioned VMs store hostDevices.name as a placeholder ('provisioned'); the
// real allocated PCI device names live in this annotation, keyed by resourceName.
try {
const deviceAllocationDetails = JSON.parse(this.metadata?.annotations[HCI_ANNOTATIONS.VM_DEVICE_ALLOCATION_DETAILS] || '{}');
return deviceAllocationDetails?.hostdevices || {};
} catch (error) {
return {};
}
}
get schedulingVMBackupFeatureEnabled() { get schedulingVMBackupFeatureEnabled() {
return this.$rootGetters['harvester-common/getFeatureEnabled']('schedulingVMBackup'); return this.$rootGetters['harvester-common/getFeatureEnabled']('schedulingVMBackup');
} }
@@ -1,10 +1,9 @@
<script> <script>
import Loading from '@shell/components/Loading';
import { HCI } from '../../../../../types'; import { HCI } from '../../../../../types';
import SerialConsole from '../../../../../components/SerialConsole'; import SerialConsole from '../../../../../components/SerialConsole';
export default { export default {
components: { SerialConsole, Loading }, components: { SerialConsole },
async fetch() { async fetch() {
this.rows = await this.$store.dispatch('harvester/findAll', { type: HCI.VMI }); this.rows = await this.$store.dispatch('harvester/findAll', { type: HCI.VMI });
@@ -38,16 +37,44 @@ export default {
</script> </script>
<template> <template>
<Loading v-if="$fetchState.pending" /> <div class="serial-console-page">
<SerialConsole <SerialConsole
v-else ref="serialConsole"
ref="serialConsole" :value="vmi || {}"
v-model:value="vmi" />
/> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss">
body, #__nuxt, #__layout, main { html,
body,
#__nuxt,
#__layout,
main {
height: 100%; height: 100%;
} }
.serial-console-page {
position: fixed;
inset: 0;
min-height: 0;
background: var(--body-bg);
color: var(--terminal-text);
&::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 6px;
pointer-events: none;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.25));
}
.harvester-shell-container {
height: 100%;
background: var(--terminal-bg);
}
}
</style> </style>
@@ -5,6 +5,7 @@ import CruResource from '@shell/components/CruResource';
import Loading from '@shell/components/Loading'; import Loading from '@shell/components/Loading';
import { SECRET } from '@shell/config/types'; import { SECRET } from '@shell/config/types';
import { useI18n } from '@shell/composables/useI18n'; import { useI18n } from '@shell/composables/useI18n';
import { exceptionToErrorsArray, stringify } from '@shell/utils/error';
import ConfigureProviderStep from '@pkg/harvester/components/vm-migration/ConfigureProviderStep.vue'; import ConfigureProviderStep from '@pkg/harvester/components/vm-migration/ConfigureProviderStep.vue';
import ConfigureMappingsStep from '@pkg/harvester/components/vm-migration/ConfigureMappingsStep.vue'; import ConfigureMappingsStep from '@pkg/harvester/components/vm-migration/ConfigureMappingsStep.vue';
import { PRODUCT_NAME } from '@pkg/harvester/config/harvester'; import { PRODUCT_NAME } from '@pkg/harvester/config/harvester';
@@ -163,7 +164,7 @@ const onFinish = async(buttonCb) => {
buttonCb(true); buttonCb(true);
currentRouter().push(providerListLocation); currentRouter().push(providerListLocation);
} catch (err) { } catch (err) {
errors.value = [err instanceof Error ? err.message : String(err)]; errors.value = exceptionToErrorsArray(err).map((e) => (typeof e === 'string' ? e : stringify(e)));
buttonCb(false); buttonCb(false);
} }
}; };
@@ -233,7 +234,7 @@ const init = async() => {
// Maps may not exist yet // Maps may not exist yet
} }
} catch (err) { } catch (err) {
errors.value = [t('harvester.addons.vmMigration.errors.failedLoadProvider', { error: err.message || err })]; errors.value = [t('harvester.addons.vmMigration.errors.failedLoadProvider', { error: err.message || stringify(err) })];
} }
initialLoading.value = false; initialLoading.value = false;
@@ -68,29 +68,35 @@ export default defineComponent({
return (this.value || []).map((plan) => { return (this.value || []).map((plan) => {
const planName = plan?.metadata?.name || '-'; const planName = plan?.metadata?.name || '-';
const namespace = plan?.metadata?.namespace || ''; const namespace = plan?.metadata?.namespace || '';
const networkMapRef = this.getPlanMapRef(plan, 'network');
const storageMapRef = this.getPlanMapRef(plan, 'storage');
const migrations = existing[HCI.FORKLIFT_MIGRATION] const migrations = existing[HCI.FORKLIFT_MIGRATION]
.filter((resource) => this.ownedByPlan(resource, plan)) .filter((resource) => this.ownedByPlan(resource, plan))
.map((resource) => ({ .map((resource) => resource?.metadata?.name)
name: resource?.metadata?.name, .filter(Boolean);
namespace: resource?.metadata?.namespace || namespace,
}));
const matchedNetworkMap = existing[HCI.FORKLIFT_NETWORK_MAP].find((resource) => this.ownedByPlan(resource, plan)); const matchedNetworkMap = networkMapRef ? existing[HCI.FORKLIFT_NETWORK_MAP].find((resource) => resource?.metadata?.name === networkMapRef.name && resource?.metadata?.namespace === networkMapRef.namespace) : null;
const matchedStorageMap = existing[HCI.FORKLIFT_STORAGE_MAP].find((resource) => this.ownedByPlan(resource, plan)); const matchedStorageMap = storageMapRef ? existing[HCI.FORKLIFT_STORAGE_MAP].find((resource) => resource?.metadata?.name === storageMapRef.name && resource?.metadata?.namespace === storageMapRef.namespace) : null;
return { return {
planName, planName,
namespace, namespace,
migrations, migrations,
networkMap: matchedNetworkMap ? { networkMap: matchedNetworkMap ? {
namespace: matchedNetworkMap?.metadata?.namespace || namespace, namespace: matchedNetworkMap?.metadata?.namespace || networkMapRef?.namespace || namespace,
name: matchedNetworkMap?.metadata?.name, name: matchedNetworkMap?.metadata?.name,
} : null, } : (networkMapRef ? {
namespace: networkMapRef.namespace,
name: networkMapRef.name,
} : null),
storageMap: matchedStorageMap ? { storageMap: matchedStorageMap ? {
namespace: matchedStorageMap?.metadata?.namespace || namespace, namespace: matchedStorageMap?.metadata?.namespace || storageMapRef?.namespace || namespace,
name: matchedStorageMap?.metadata?.name, name: matchedStorageMap?.metadata?.name,
} : null, } : (storageMapRef ? {
namespace: storageMapRef.namespace,
name: storageMapRef.name,
} : null),
}; };
}); });
}, },
@@ -113,6 +119,20 @@ export default defineComponent({
methods: { methods: {
resourceNames, resourceNames,
getPlanMapRef(plan, mapType) {
const ref = plan?.spec?.map?.[mapType];
const name = ref?.name;
if (!name) {
return null;
}
return {
name,
namespace: ref?.namespace || plan?.metadata?.namespace || '',
};
},
/** /**
* Determine whether a related resource (migration / network map / storage map) * Determine whether a related resource (migration / network map / storage map)
* is owned by the given plan by inspecting its `metadata.ownerReferences`. * is owned by the given plan by inspecting its `metadata.ownerReferences`.
@@ -137,9 +157,8 @@ export default defineComponent({
const existing = this.existingRelatedResources; const existing = this.existingRelatedResources;
const targets = new Map(); const targets = new Map();
const addTarget = (type, resource) => { const addTarget = (type, name, namespace) => {
const name = resource?.metadata?.name; const ns = namespace || '';
const ns = resource?.metadata?.namespace;
if (!name) { if (!name) {
return; return;
@@ -153,17 +172,20 @@ export default defineComponent({
}; };
for (const plan of this.value || []) { for (const plan of this.value || []) {
existing[HCI.FORKLIFT_NETWORK_MAP] const networkMapRef = this.getPlanMapRef(plan, 'network');
.filter((resource) => this.ownedByPlan(resource, plan)) const storageMapRef = this.getPlanMapRef(plan, 'storage');
.forEach((resource) => addTarget(HCI.FORKLIFT_NETWORK_MAP, resource));
existing[HCI.FORKLIFT_STORAGE_MAP] if (networkMapRef) {
.filter((resource) => this.ownedByPlan(resource, plan)) addTarget(HCI.FORKLIFT_NETWORK_MAP, networkMapRef.name, networkMapRef.namespace);
.forEach((resource) => addTarget(HCI.FORKLIFT_STORAGE_MAP, resource)); }
if (storageMapRef) {
addTarget(HCI.FORKLIFT_STORAGE_MAP, storageMapRef.name, storageMapRef.namespace);
}
existing[HCI.FORKLIFT_MIGRATION] existing[HCI.FORKLIFT_MIGRATION]
.filter((resource) => this.ownedByPlan(resource, plan)) .filter((resource) => this.ownedByPlan(resource, plan))
.forEach((resource) => addTarget(HCI.FORKLIFT_MIGRATION, resource)); .forEach((resource) => addTarget(HCI.FORKLIFT_MIGRATION, resource?.metadata?.name, resource?.metadata?.namespace));
} }
return [...targets.values()]; return [...targets.values()];
@@ -256,11 +278,11 @@ export default defineComponent({
<div>• {{ t('harvester.addons.vmMigration.labels.migration') }}:</div> <div>• {{ t('harvester.addons.vmMigration.labels.migration') }}:</div>
<template v-if="summary.migrations.length"> <template v-if="summary.migrations.length">
<div <div
v-for="migration in summary.migrations" v-for="migrationName in summary.migrations"
:key="`${ migration.namespace }/${ migration.name }`" :key="migrationName"
> >
<div class="ml-20"> <div class="ml-20">
- {{ migration.name }} - {{ migrationName }}
</div> </div>
</div> </div>
</template> </template>
+21 -11
View File
@@ -24,13 +24,14 @@ export function registerAddonSideNav(store, productName, {
const kickSideNav = () => { const kickSideNav = () => {
const TRIGGER = 'ui.refresh.trigger'; const TRIGGER = 'ui.refresh.trigger';
store.dispatch('type-map/addFavorite', TRIGGER); // Toggle the trigger a few times so an early kick (fired before the SideNav
// has mounted on first login) is retried once the component is listening.
// SideNav component seem to ignore rapid state changes. [0, 600, 1500].forEach((delay) => {
// Wait 600ms to ensure the toggle event triggers a re-render. setTimeout(() => {
setTimeout(() => { store.dispatch('type-map/addFavorite', TRIGGER);
store.dispatch('type-map/removeFavorite', TRIGGER); setTimeout(() => store.dispatch('type-map/removeFavorite', TRIGGER), 300);
}, 600); }, delay);
});
}; };
const hasAccessibleSchema = (t) => { const hasAccessibleSchema = (t) => {
@@ -92,16 +93,25 @@ export function registerAddonSideNav(store, productName, {
// Store is ready. Stop polling. // Store is ready. Stop polling.
clearInterval(waitForStore); clearInterval(waitForStore);
// Watch the specific addon resource for changes to its enabled status. // Watch the addon's enabled status together with the schema availability
// of the gated types. Schemas (e.g. forklift CRDs) can load after the
// addon is already enabled, so the watcher must also re-run when they
// become accessible; otherwise the menu never updates until a refresh.
store.watch( store.watch(
(state, getters) => { (state, getters) => {
const addons = getters[`${ productName }/all`](resourceType); const addons = getters[`${ productName }/all`](resourceType);
const addon = addons.find((a) => a.metadata.name === addonName); const addon = addons.find((a) => a.metadata.name === addonName);
const isEnabled = addon?.spec?.enabled === true;
return addon?.spec?.enabled === true; const schemaReady = requireSchema ? types.every(hasAccessibleSchema) : true;
return `${ isEnabled }:${ schemaReady }`;
}, },
(isEnabled) => { () => {
setMenuVisibility(isEnabled); const addons = store.getters[`${ productName }/all`](resourceType);
const addon = addons.find((a) => a.metadata.name === addonName);
setMenuVisibility(addon?.spec?.enabled === true);
}, },
{ immediate: true, deep: true } { immediate: true, deep: true }
); );
+17 -4
View File
@@ -73,8 +73,21 @@ export function buildNetworkMapEntries(entries = [], defaultNamespace) {
export function buildStorageMapEntries(entries = []) { export function buildStorageMapEntries(entries = []) {
return entries return entries
.filter((entry) => !!entry.target) .filter((entry) => !!entry.target)
.map((entry) => ({ .map((entry) => {
source: { name: entry.name, id: entry.id }, const destination = { storageClass: entry.target };
destination: { storageClass: entry.target },
})); if (entry.volumeMode) {
destination.volumeMode = entry.volumeMode;
}
// Forklift StorageMap destination expects a single `accessMode` value.
if (Array.isArray(entry.accessModes) && entry.accessModes.length) {
destination.accessMode = entry.accessModes[0];
}
return {
source: { name: entry.name, id: entry.id },
destination,
};
});
} }
+282 -273
View File
@@ -515,7 +515,7 @@
dependencies: dependencies:
"@babel/highlight" "^7.10.4" "@babel/highlight" "^7.10.4"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.29.0", "@babel/code-frame@^7.29.7", "@babel/code-frame@^7.8.3": "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.29.7", "@babel/code-frame@^7.8.3":
version "7.29.7" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7"
integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw== integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==
@@ -529,20 +529,25 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d"
integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==
"@babel/compat-data@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.7.tgz#6f0237f0f36d2e51c0570a636faed9d2d0efe629"
integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==
"@babel/core@^7.1.0", "@babel/core@^7.12.16", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.7.2", "@babel/core@^7.8.0": "@babel/core@^7.1.0", "@babel/core@^7.12.16", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.7.2", "@babel/core@^7.8.0":
version "7.29.0" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.7.tgz#80c10b17248082968b57a857b91640971f2070f7"
integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==
dependencies: dependencies:
"@babel/code-frame" "^7.29.0" "@babel/code-frame" "^7.29.7"
"@babel/generator" "^7.29.0" "@babel/generator" "^7.29.7"
"@babel/helper-compilation-targets" "^7.28.6" "@babel/helper-compilation-targets" "^7.29.7"
"@babel/helper-module-transforms" "^7.28.6" "@babel/helper-module-transforms" "^7.29.7"
"@babel/helpers" "^7.28.6" "@babel/helpers" "^7.29.7"
"@babel/parser" "^7.29.0" "@babel/parser" "^7.29.7"
"@babel/template" "^7.28.6" "@babel/template" "^7.29.7"
"@babel/traverse" "^7.29.0" "@babel/traverse" "^7.29.7"
"@babel/types" "^7.29.0" "@babel/types" "^7.29.7"
"@jridgewell/remapping" "^2.3.5" "@jridgewell/remapping" "^2.3.5"
convert-source-map "^2.0.0" convert-source-map "^2.0.0"
debug "^4.1.0" debug "^4.1.0"
@@ -550,7 +555,7 @@
json5 "^2.2.3" json5 "^2.2.3"
semver "^6.3.1" semver "^6.3.1"
"@babel/generator@^7.29.0", "@babel/generator@^7.29.7", "@babel/generator@^7.7.2": "@babel/generator@^7.29.7", "@babel/generator@^7.7.2":
version "7.29.7" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.7.tgz#cca0b8827e6bcf3ba176788e7f3b180ad6db2fa3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.7.tgz#cca0b8827e6bcf3ba176788e7f3b180ad6db2fa3"
integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ== integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==
@@ -561,6 +566,17 @@
"@jridgewell/trace-mapping" "^0.3.28" "@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2" jsesc "^3.0.2"
"@babel/generator@^7.29.8":
version "7.29.8"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.8.tgz#4b0b887885422643339e09022148a4c4ebaa4979"
integrity sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==
dependencies:
"@babel/parser" "^7.29.8"
"@babel/types" "^7.29.8"
"@jridgewell/gen-mapping" "^0.3.12"
"@jridgewell/trace-mapping" "^0.3.28"
jsesc "^3.0.2"
"@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3", "@babel/helper-annotate-as-pure@^7.29.7": "@babel/helper-annotate-as-pure@^7.14.5", "@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3", "@babel/helper-annotate-as-pure@^7.29.7":
version "7.29.7" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz#c70fe3c6ecbdc3fd2dd1b0f498428b88b82ce47f" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz#c70fe3c6ecbdc3fd2dd1b0f498428b88b82ce47f"
@@ -579,6 +595,17 @@
lru-cache "^5.1.1" lru-cache "^5.1.1"
semver "^6.3.1" semver "^6.3.1"
"@babel/helper-compilation-targets@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz#7a1def704302401c47f64fa85589e974ae217042"
integrity sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==
dependencies:
"@babel/compat-data" "^7.29.7"
"@babel/helper-validator-option" "^7.29.7"
browserslist "^4.24.0"
lru-cache "^5.1.1"
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.28.6", "@babel/helper-create-class-features-plugin@^7.29.7": "@babel/helper-create-class-features-plugin@^7.14.5", "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.28.6", "@babel/helper-create-class-features-plugin@^7.29.7":
version "7.29.7" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz#6eddf286f2ec418f740c91d60a83347c55838ddd" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz#6eddf286f2ec418f740c91d60a83347c55838ddd"
@@ -633,6 +660,14 @@
"@babel/traverse" "^7.28.6" "@babel/traverse" "^7.28.6"
"@babel/types" "^7.28.6" "@babel/types" "^7.28.6"
"@babel/helper-module-imports@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz#ef25048a518e828d7393fac5882ddd73921d7396"
integrity sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==
dependencies:
"@babel/traverse" "^7.29.7"
"@babel/types" "^7.29.7"
"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.6": "@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.6":
version "7.28.6" version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e"
@@ -642,6 +677,15 @@
"@babel/helper-validator-identifier" "^7.28.5" "@babel/helper-validator-identifier" "^7.28.5"
"@babel/traverse" "^7.28.6" "@babel/traverse" "^7.28.6"
"@babel/helper-module-transforms@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz#b062747a5997ba138637201328bbff77960574ae"
integrity sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==
dependencies:
"@babel/helper-module-imports" "^7.29.7"
"@babel/helper-validator-identifier" "^7.29.7"
"@babel/traverse" "^7.29.7"
"@babel/helper-optimise-call-expression@^7.29.7": "@babel/helper-optimise-call-expression@^7.29.7":
version "7.29.7" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz#77b0b5b94f1997fa9d6e3125f445227b1faf9d85" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz#77b0b5b94f1997fa9d6e3125f445227b1faf9d85"
@@ -695,6 +739,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f"
integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==
"@babel/helper-validator-option@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz#cf315be940213b354eb4abcc0bd01ebe3f73bc2a"
integrity sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==
"@babel/helper-wrap-function@^7.27.1": "@babel/helper-wrap-function@^7.27.1":
version "7.28.6" version "7.28.6"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz#4e349ff9222dab69a93a019cc296cdd8442e279a" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz#4e349ff9222dab69a93a019cc296cdd8442e279a"
@@ -704,13 +753,13 @@
"@babel/traverse" "^7.28.6" "@babel/traverse" "^7.28.6"
"@babel/types" "^7.28.6" "@babel/types" "^7.28.6"
"@babel/helpers@^7.28.6": "@babel/helpers@^7.29.7":
version "7.29.2" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.2.tgz#9cfbccb02b8e229892c0b07038052cc1a8709c49" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.7.tgz#45abfde7548997e34376c3e69feb475cffb4a607"
integrity sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw== integrity sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==
dependencies: dependencies:
"@babel/template" "^7.28.6" "@babel/template" "^7.29.7"
"@babel/types" "^7.29.0" "@babel/types" "^7.29.7"
"@babel/highlight@^7.10.4": "@babel/highlight@^7.10.4":
version "7.25.9" version "7.25.9"
@@ -729,6 +778,13 @@
dependencies: dependencies:
"@babel/types" "^7.29.7" "@babel/types" "^7.29.7"
"@babel/parser@^7.29.8":
version "7.29.8"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.8.tgz#9653716a2f10c677b98fbc63d4bfb000c302cf17"
integrity sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==
dependencies:
"@babel/types" "^7.29.8"
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5": "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5":
version "7.28.5" version "7.28.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz#fbde57974707bbfa0376d34d425ff4fa6c732421" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz#fbde57974707bbfa0376d34d425ff4fa6c732421"
@@ -1162,14 +1218,14 @@
"@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-plugin-utils" "^7.28.6"
"@babel/plugin-transform-modules-systemjs@^7.29.0": "@babel/plugin-transform-modules-systemjs@^7.29.0":
version "7.29.0" version "7.29.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz#e458a95a17807c415924106a3ff188a3b8dee964" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.8.tgz#e60a6a42ac63a3095f9cc7264f698a100c8fe05d"
integrity sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ== integrity sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==
dependencies: dependencies:
"@babel/helper-module-transforms" "^7.28.6" "@babel/helper-module-transforms" "^7.29.7"
"@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-plugin-utils" "^7.29.7"
"@babel/helper-validator-identifier" "^7.28.5" "@babel/helper-validator-identifier" "^7.29.7"
"@babel/traverse" "^7.29.0" "@babel/traverse" "^7.29.8"
"@babel/plugin-transform-modules-umd@^7.27.1": "@babel/plugin-transform-modules-umd@^7.27.1":
version "7.27.1" version "7.27.1"
@@ -1506,7 +1562,20 @@
"@babel/types" "^7.29.7" "@babel/types" "^7.29.7"
debug "^4.3.1" debug "^4.3.1"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.0", "@babel/types@^7.29.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": "@babel/traverse@^7.29.8":
version "7.29.8"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.8.tgz#4111014cdc71a0f95d9471907590baa0b8a6b28a"
integrity sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==
dependencies:
"@babel/code-frame" "^7.29.7"
"@babel/generator" "^7.29.8"
"@babel/helper-globals" "^7.29.7"
"@babel/parser" "^7.29.8"
"@babel/template" "^7.29.7"
"@babel/types" "^7.29.8"
debug "^4.3.1"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
version "7.29.7" version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92"
integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA== integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==
@@ -1514,6 +1583,14 @@
"@babel/helper-string-parser" "^7.29.7" "@babel/helper-string-parser" "^7.29.7"
"@babel/helper-validator-identifier" "^7.29.7" "@babel/helper-validator-identifier" "^7.29.7"
"@babel/types@^7.29.8":
version "7.29.8"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.8.tgz#1229eef31d85156d70fa3f4cd859376d0eaf6863"
integrity sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==
dependencies:
"@babel/helper-string-parser" "^7.29.7"
"@babel/helper-validator-identifier" "^7.29.7"
"@bcoe/v8-coverage@^0.2.3": "@bcoe/v8-coverage@^0.2.3":
version "0.2.3" version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -2181,15 +2258,15 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@rancher/icons@2.0.60": "@rancher/icons@2.0.62":
version "2.0.60" version "2.0.62"
resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.60.tgz#a7d8bd39a007ef8ecf1cb7bfe27d82f64f9cd75c" resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.62.tgz#42d3aa89d03a2e0e551332e99c8102dfdd422746"
integrity sha512-5bbXVOc4Uk2OmACX67i1e1jOTeLg87T22u+alhwEZX+/WzBEH/B0DPVqSbuYQtKYkH0Me4ojnOVCzZ2HuuLHWQ== integrity sha512-ywGk1NSrIdN1zllpbDRU3ESVW/D0ExRZFBz+YPGAo9G08VzWxKIcT5GG5Nd2GXLTcUestlq1JwkKDaVAM8hkmQ==
"@rancher/shell@3.0.12-rc.3": "@rancher/shell@3.0.12-rc.7":
version "3.0.12-rc.3" version "3.0.12-rc.7"
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.12-rc.3.tgz#566ea39fac704fbed2e0bc070f47b5bb3b6a92fb" resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.12-rc.7.tgz#509b42d1a40fc8f399dc8c15fd07f2ea4361af54"
integrity sha512-4mKX3Ou2t/P8ko5Tb8UuuxgttgQPPX66Rlq8vC6IftGowJ+ExtM6FJDg4bKy1Lu3DrDERHoQLmTHehDScRnSEw== integrity sha512-aA2uX5roVhJtj8p09OQNREhFraa9MqSrjXjbAC+3uRLniQkhqx+INKpyBDvhkDy0wd7XWIGbX3cfl+Kz6mwSYA==
dependencies: dependencies:
"@aws-sdk/client-ec2" "3.1041.0" "@aws-sdk/client-ec2" "3.1041.0"
"@aws-sdk/client-eks" "3.1041.0" "@aws-sdk/client-eks" "3.1041.0"
@@ -2201,21 +2278,28 @@
"@babel/preset-typescript" "7.16.7" "@babel/preset-typescript" "7.16.7"
"@novnc/novnc" "1.2.0" "@novnc/novnc" "1.2.0"
"@popperjs/core" "2.11.8" "@popperjs/core" "2.11.8"
"@rancher/icons" "2.0.60" "@rancher/icons" "2.0.62"
"@smithy/fetch-http-handler" "5.1.1" "@smithy/fetch-http-handler" "5.1.1"
"@types/is-url" "1.2.30" "@types/is-url" "1.2.30"
"@types/node" "25.3.3" "@types/node" "25.3.3"
"@types/semver" "^7.5.8" "@types/semver" "^7.5.8"
"@typescript-eslint/eslint-plugin" "5.62.0" "@typescript-eslint/eslint-plugin" "5.62.0"
"@typescript-eslint/parser" "5.62.0" "@typescript-eslint/parser" "5.62.0"
"@vee-validate/zod" "4.15.0"
"@vue/cli-plugin-babel" "~5.0.0" "@vue/cli-plugin-babel" "~5.0.0"
"@vue/cli-plugin-typescript" "~5.0.0" "@vue/cli-plugin-typescript" "~5.0.0"
"@vue/cli-service" "5.0.8" "@vue/cli-service" "5.0.8"
"@vue/test-utils" "2.4.6" "@vue/test-utils" "2.4.6"
"@vue/vue3-jest" "27.0.0" "@vue/vue3-jest" "27.0.0"
"@xterm/addon-canvas" "0.7.0"
"@xterm/addon-fit" "0.11.0"
"@xterm/addon-search" "0.16.0"
"@xterm/addon-web-links" "0.12.0"
"@xterm/addon-webgl" "0.19.0"
"@xterm/xterm" "6.0.0"
add "2.0.6" add "2.0.6"
ansi_up "5.0.0" ansi_up "5.0.0"
axios "1.15.2" axios "1.16.0"
axios-retry "3.1.9" axios-retry "3.1.9"
babel-eslint "10.1.0" babel-eslint "10.1.0"
babel-preset-vue "2.0.2" babel-preset-vue "2.0.2"
@@ -2248,14 +2332,13 @@
eslint-plugin-n "15.2.0" eslint-plugin-n "15.2.0"
eslint-plugin-vue "9.32.0" eslint-plugin-vue "9.32.0"
event-target-shim "5.0.1" event-target-shim "5.0.1"
express "4.17.1" express "4.22.2"
file-saver "2.0.2" file-saver "2.0.5"
floating-vue "5.2.2" floating-vue "5.2.2"
focus-trap "7.6.5" focus-trap "7.6.5"
frontmatter-markdown-loader "3.7.0" frontmatter-markdown-loader "3.7.0"
identicon.js "2.3.3" identicon.js "2.3.3"
intl-messageformat "7.8.4" intl-messageformat "7.8.4"
ip "2.0.1"
is-url "1.2.4" is-url "1.2.4"
jest "27.5.1" jest "27.5.1"
jest-serializer-vue "2.0.2" jest-serializer-vue "2.0.2"
@@ -2292,13 +2375,8 @@
webpack-virtual-modules "0.6.2" webpack-virtual-modules "0.6.2"
worker-loader "3.0.8" worker-loader "3.0.8"
xmlbuilder2 "4.0.1" xmlbuilder2 "4.0.1"
xterm "5.2.1"
xterm-addon-canvas "0.5.0"
xterm-addon-fit "0.8.0"
xterm-addon-search "0.13.0"
xterm-addon-web-links "0.9.0"
xterm-addon-webgl "0.16.0"
yarn "1.22.22" yarn "1.22.22"
zod "3.24.3"
"@rtsao/scc@^1.1.0": "@rtsao/scc@^1.1.0":
version "1.1.0" version "1.1.0"
@@ -2824,10 +2902,10 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/lodash@4.17.24": "@types/lodash@4.17.25":
version "4.17.24" version "4.17.25"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.25.tgz#69765ac7bcddb0eb072961cf292524a8f5b3c2c0"
integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ== integrity sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==
"@types/mime@^1": "@types/mime@^1":
version "1.3.5" version "1.3.5"
@@ -3098,6 +3176,14 @@
"@typescript-eslint/types" "5.62.0" "@typescript-eslint/types" "5.62.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@vee-validate/zod@4.15.0":
version "4.15.0"
resolved "https://registry.yarnpkg.com/@vee-validate/zod/-/zod-4.15.0.tgz#2de193da114eda9f873b5a677f13c23bcecb58aa"
integrity sha512-MpvIKiyg9X5yD8bJW0no2AU7wtR2T5mrvD9tuPRiie951sU2n6QKgMV38qKKOiqFBCxsMSjIuLLLV3V5kVE4nQ==
dependencies:
type-fest "^4.8.3"
vee-validate "4.15.0"
"@vue-flow/background@^1.3.0": "@vue-flow/background@^1.3.0":
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/@vue-flow/background/-/background-1.3.2.tgz#0c90cd05e5d60da017bbaf5a1c3eb6af7ed9b778" resolved "https://registry.yarnpkg.com/@vue-flow/background/-/background-1.3.2.tgz#0c90cd05e5d60da017bbaf5a1c3eb6af7ed9b778"
@@ -3690,6 +3776,36 @@
"@webassemblyjs/ast" "1.14.1" "@webassemblyjs/ast" "1.14.1"
"@xtuc/long" "4.2.2" "@xtuc/long" "4.2.2"
"@xterm/addon-canvas@0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.7.0.tgz#d3a3835f3634e0106e108661315ae14da23e8dd7"
integrity sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw==
"@xterm/addon-fit@0.11.0":
version "0.11.0"
resolved "https://registry.yarnpkg.com/@xterm/addon-fit/-/addon-fit-0.11.0.tgz#ba4778b69fcc9044a060c2176bbe077657d7b37e"
integrity sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==
"@xterm/addon-search@0.16.0":
version "0.16.0"
resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.16.0.tgz#ddadcbc6e556e45fc4b153f8a0efdf2aa1456b82"
integrity sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA==
"@xterm/addon-web-links@0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@xterm/addon-web-links/-/addon-web-links-0.12.0.tgz#bc34ae46f4c12012256d451ac2b9be791c0b6bfa"
integrity sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==
"@xterm/addon-webgl@0.19.0":
version "0.19.0"
resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.19.0.tgz#02533c3f7c0af3ac9a44bbb095cbd47d48e90c25"
integrity sha512-b3fMOsyLVuCeNJWxolACEUED0vm7qC0cy4wRvf3oURSzDTYVQiGPhTnhWZwIHdvC48Y+oLhvYXnY4XDXPoJo6A==
"@xterm/xterm@6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-6.0.0.tgz#93637b0f2ee3a70718b5746a27c9c506af16745b"
integrity sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==
"@xtuc/ieee754@^1.2.0": "@xtuc/ieee754@^1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -3730,7 +3846,7 @@ abort-controller@^3.0.0:
dependencies: dependencies:
event-target-shim "^5.0.0" event-target-shim "^5.0.0"
accepts@~1.3.7, accepts@~1.3.8: accepts@~1.3.8:
version "1.3.8" version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
@@ -4093,12 +4209,12 @@ axios-retry@3.1.9:
dependencies: dependencies:
is-retry-allowed "^1.1.0" is-retry-allowed "^1.1.0"
axios@1.15.2: axios@1.16.0:
version "1.15.2" version "1.16.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.2.tgz#eb8fb6d30349abace6ade5b4cb4d9e8a0dc23e5b" resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.0.tgz#f8e5dd931cef2a5f8c32216d5784eda2f8750eb7"
integrity sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A== integrity sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==
dependencies: dependencies:
follow-redirects "^1.15.11" follow-redirects "^1.16.0"
form-data "^4.0.5" form-data "^4.0.5"
proxy-from-env "^2.1.0" proxy-from-env "^2.1.0"
@@ -4328,26 +4444,10 @@ bn.js@^5.2.1, bn.js@^5.2.2:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.3.tgz#16a9e409616b23fef3ccbedb8d42f13bff80295e" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.3.tgz#16a9e409616b23fef3ccbedb8d42f13bff80295e"
integrity sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w== integrity sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==
body-parser@1.19.0: body-parser@~1.20.5:
version "1.19.0" version "1.20.6"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.6.tgz#60c789c78e0992d906da0a29d71ae01d15c1ed76"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== integrity sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==
dependencies:
bytes "3.1.0"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "1.7.2"
iconv-lite "0.4.24"
on-finished "~2.3.0"
qs "6.7.0"
raw-body "2.4.0"
type-is "~1.6.17"
body-parser@~1.20.3:
version "1.20.4"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f"
integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==
dependencies: dependencies:
bytes "~3.1.2" bytes "~3.1.2"
content-type "~1.0.5" content-type "~1.0.5"
@@ -4357,7 +4457,7 @@ body-parser@~1.20.3:
http-errors "~2.0.1" http-errors "~2.0.1"
iconv-lite "~0.4.24" iconv-lite "~0.4.24"
on-finished "~2.4.1" on-finished "~2.4.1"
qs "~6.14.0" qs "~6.15.1"
raw-body "~2.5.3" raw-body "~2.5.3"
type-is "~1.6.18" type-is "~1.6.18"
unpipe "~1.0.0" unpipe "~1.0.0"
@@ -4381,17 +4481,17 @@ bowser@^2.11.0:
integrity sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg== integrity sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==
brace-expansion@^1.1.7: brace-expansion@^1.1.7:
version "1.1.14" version "1.1.18"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.14.tgz#d9de602370d91347cd9ddad1224d4fd701eb348b" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.18.tgz#3ce74d89885136be1535341f8c3d4425c29a5cab"
integrity sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g== integrity sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==
dependencies: dependencies:
balanced-match "^1.0.0" balanced-match "^1.0.0"
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^2.0.1, brace-expansion@^2.0.2: brace-expansion@^2.0.1, brace-expansion@^2.0.2:
version "2.1.0" version "2.1.4"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.0.tgz#4f41a41190216ee36067ec381526fe9539c4f0ae" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.4.tgz#589dab11c0018d0366be64cd8bf12c8dbecc8326"
integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w== integrity sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==
dependencies: dependencies:
balanced-match "^1.0.0" balanced-match "^1.0.0"
@@ -4535,11 +4635,6 @@ builtins@^4.0.0:
dependencies: dependencies:
semver "^7.0.0" semver "^7.0.0"
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
bytes@3.1.2, bytes@~3.1.2: bytes@3.1.2, bytes@~3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
@@ -5026,13 +5121,6 @@ constants-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==
content-disposition@0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
dependencies:
safe-buffer "5.1.2"
content-disposition@~0.5.4: content-disposition@~0.5.4:
version "0.5.4" version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
@@ -5079,7 +5167,7 @@ convert-source-map@^2.0.0:
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
cookie-signature@1.0.6, cookie-signature@~1.0.6: cookie-signature@~1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
@@ -5092,7 +5180,7 @@ cookie-universal@2.2.2:
"@types/cookie" "^0.3.3" "@types/cookie" "^0.3.3"
cookie "^0.4.0" cookie "^0.4.0"
cookie@0.4.0, cookie@^0.4.0: cookie@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
@@ -6656,50 +6744,14 @@ expect@^27.5.1:
jest-matcher-utils "^27.5.1" jest-matcher-utils "^27.5.1"
jest-message-util "^27.5.1" jest-message-util "^27.5.1"
express@4.17.1: express@4.22.2, express@^4.17.3:
version "4.17.1" version "4.22.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" resolved "https://registry.yarnpkg.com/express/-/express-4.22.2.tgz#c17ae0981e5efc24b22272f0e041c4662503b700"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== integrity sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==
dependencies:
accepts "~1.3.7"
array-flatten "1.1.1"
body-parser "1.19.0"
content-disposition "0.5.3"
content-type "~1.0.4"
cookie "0.4.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "~1.1.2"
fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.5"
qs "6.7.0"
range-parser "~1.2.1"
safe-buffer "5.1.2"
send "0.17.1"
serve-static "1.14.1"
setprototypeof "1.1.1"
statuses "~1.5.0"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
express@^4.17.3:
version "4.22.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069"
integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==
dependencies: dependencies:
accepts "~1.3.8" accepts "~1.3.8"
array-flatten "1.1.1" array-flatten "1.1.1"
body-parser "~1.20.3" body-parser "~1.20.5"
content-disposition "~0.5.4" content-disposition "~0.5.4"
content-type "~1.0.4" content-type "~1.0.4"
cookie "~0.7.1" cookie "~0.7.1"
@@ -6718,7 +6770,7 @@ express@^4.17.3:
parseurl "~1.3.3" parseurl "~1.3.3"
path-to-regexp "~0.1.12" path-to-regexp "~0.1.12"
proxy-addr "~2.0.7" proxy-addr "~2.0.7"
qs "~6.14.0" qs "~6.15.1"
range-parser "~1.2.1" range-parser "~1.2.1"
safe-buffer "5.2.1" safe-buffer "5.2.1"
send "~0.19.0" send "~0.19.0"
@@ -6768,9 +6820,9 @@ fast-printf@^1.6.9:
integrity sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w== integrity sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w==
fast-uri@^3.0.1: fast-uri@^3.0.1:
version "3.1.0" version "3.1.5"
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.5.tgz#610f37419a030270430cecd68d74e3d4d96725d0"
integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== integrity sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==
fast-xml-builder@^1.1.7: fast-xml-builder@^1.1.7:
version "1.2.0" version "1.2.0"
@@ -6825,10 +6877,10 @@ file-entry-cache@^6.0.1:
dependencies: dependencies:
flat-cache "^3.0.4" flat-cache "^3.0.4"
file-saver@2.0.2: file-saver@2.0.5:
version "2.0.2" version "2.0.5"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.2.tgz#06d6e728a9ea2df2cce2f8d9e84dfcdc338ec17a" resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw== integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
filelist@^1.0.4: filelist@^1.0.4:
version "1.0.6" version "1.0.6"
@@ -6844,19 +6896,6 @@ fill-range@^7.1.1:
dependencies: dependencies:
to-regex-range "^5.0.1" to-regex-range "^5.0.1"
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "~2.3.0"
parseurl "~1.3.3"
statuses "~1.5.0"
unpipe "~1.0.0"
finalhandler@~1.3.1: finalhandler@~1.3.1:
version "1.3.2" version "1.3.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88"
@@ -6937,7 +6976,7 @@ focus-trap@7.6.5:
dependencies: dependencies:
tabbable "^6.2.0" tabbable "^6.2.0"
follow-redirects@1.16.0, follow-redirects@^1.0.0, follow-redirects@^1.15.11: follow-redirects@1.16.0, follow-redirects@^1.0.0, follow-redirects@^1.16.0:
version "1.16.0" version "1.16.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc"
integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==
@@ -6985,26 +7024,26 @@ fork-ts-checker-webpack-plugin@^6.4.0:
tapable "^1.0.0" tapable "^1.0.0"
form-data@^3.0.0: form-data@^3.0.0:
version "3.0.4" version "3.0.5"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.4.tgz#938273171d3f999286a4557528ce022dc2c98df1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.5.tgz#2ea3ec24f0dcb7e0262a11efb732031240ea8e9f"
integrity sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ== integrity sha512-j23EibVLnp4zNXGW7LjryXYa2X6U/M96yoOX+ybZxwkYajdxRNEqYY3zhh7y0i6kfISKS2jr+EJq1YTUDEv5+w==
dependencies: dependencies:
asynckit "^0.4.0" asynckit "^0.4.0"
combined-stream "^1.0.8" combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0" es-set-tostringtag "^2.1.0"
hasown "^2.0.2" hasown "^2.0.4"
mime-types "^2.1.35" mime-types "^2.1.35"
form-data@^4.0.5: form-data@^4.0.5:
version "4.0.5" version "4.0.6"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.6.tgz#28e864e1b786dbebb68db1f452f9635278665827"
integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== integrity sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==
dependencies: dependencies:
asynckit "^0.4.0" asynckit "^0.4.0"
combined-stream "^1.0.8" combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0" es-set-tostringtag "^2.1.0"
hasown "^2.0.2" hasown "^2.0.4"
mime-types "^2.1.12" mime-types "^2.1.35"
forwarded@0.2.0: forwarded@0.2.0:
version "0.2.0" version "0.2.0"
@@ -7345,6 +7384,13 @@ hasown@^2.0.2:
dependencies: dependencies:
function-bind "^1.1.2" function-bind "^1.1.2"
hasown@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003"
integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==
dependencies:
function-bind "^1.1.2"
he@^1.2.0: he@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
@@ -7433,9 +7479,9 @@ html-tags@^2.0.0:
integrity sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g== integrity sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==
html-webpack-plugin@^5.0.0, html-webpack-plugin@^5.1.0: html-webpack-plugin@^5.0.0, html-webpack-plugin@^5.1.0:
version "5.6.7" version "5.6.8"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz#429bab4e12abf3c07e1c608886608e2df2c06b11" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.8.tgz#5425dbc63eb533ffa59e92342be94894a125fc23"
integrity sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw== integrity sha512-MZmKQcTnhEh1SPSyMiEytIeDZDUoBZVorNHivQGXMASHf/BSGGOrKa2xQ5bGx3TCe1n109ecCt+cpww7wwWhKA==
dependencies: dependencies:
"@types/html-minifier-terser" "^6.0.0" "@types/html-minifier-terser" "^6.0.0"
html-minifier-terser "^6.0.2" html-minifier-terser "^6.0.2"
@@ -7458,7 +7504,7 @@ http-deceiver@^1.2.7:
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==
http-errors@1.7.2, http-errors@~1.7.2: http-errors@~1.7.2:
version "1.7.2" version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
@@ -7506,9 +7552,9 @@ http-proxy-agent@^4.0.1:
debug "4" debug "4"
http-proxy-middleware@^2.0.3: http-proxy-middleware@^2.0.3:
version "2.0.9" version "2.0.10"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz#e9e63d68afaa4eee3d147f39149ab84c0c2815ef" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz#b2df7b705203d7a8c269ac8450cf96b00c532f94"
integrity sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q== integrity sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==
dependencies: dependencies:
"@types/http-proxy" "^1.17.8" "@types/http-proxy" "^1.17.8"
http-proxy "^1.18.1" http-proxy "^1.18.1"
@@ -7598,9 +7644,9 @@ immediate@~3.0.5:
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
immutable@^5.0.2: immutable@^5.0.2:
version "5.1.5" version "5.1.9"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.5.tgz#93ee4db5c2a9ab42a4a783069f3c5d8847d40165" resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.9.tgz#ac23c3a01992ab665e14ac9ffff298f28cd74a0c"
integrity sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A== integrity sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==
import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0:
version "3.3.1" version "3.3.1"
@@ -8580,9 +8626,9 @@ jiti@2.6.1:
integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==
joi@^17.4.0: joi@^17.4.0:
version "17.13.3" version "17.13.4"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec" resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.4.tgz#ad6153d97ce558eb3a3b593e0d43eab51df1c474"
integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA== integrity sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==
dependencies: dependencies:
"@hapi/hoek" "^9.3.0" "@hapi/hoek" "^9.3.0"
"@hapi/topo" "^5.1.0" "@hapi/topo" "^5.1.0"
@@ -8607,9 +8653,9 @@ js-beautify@^1.14.9, js-beautify@^1.6.12:
nopt "^7.2.1" nopt "^7.2.1"
js-cookie@^3.0.5: js-cookie@^3.0.5:
version "3.0.5" version "3.0.8"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.8.tgz#444e6f4b27a5d844594fef61c9d6bca5f0787688"
integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== integrity sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==
js-message@1.0.7: js-message@1.0.7:
version "1.0.7" version "1.0.7"
@@ -8798,12 +8844,12 @@ launch-editor-middleware@^2.2.1:
launch-editor "^2.13.2" launch-editor "^2.13.2"
launch-editor@^2.13.2, launch-editor@^2.2.1, launch-editor@^2.6.0: launch-editor@^2.13.2, launch-editor@^2.2.1, launch-editor@^2.6.0:
version "2.13.2" version "2.14.1"
resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.13.2.tgz#41d51baaf8afb393224b89bd2bcb4e02f2306405" resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.14.1.tgz#f7e0da3f58aaea03fea01074d840b5f739ed7ddc"
integrity sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg== integrity sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==
dependencies: dependencies:
picocolors "^1.1.1" picocolors "^1.1.1"
shell-quote "^1.8.3" shell-quote "^1.8.4"
leven@^3.1.0: leven@^3.1.0:
version "3.1.0" version "3.1.0"
@@ -9103,11 +9149,6 @@ meow@^12.0.1:
resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6" resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6"
integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw== integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
merge-descriptors@1.0.3: merge-descriptors@1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
@@ -9161,7 +9202,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34, mime-types@~2.1.35: mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34, mime-types@~2.1.35:
version "2.1.35" version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -9294,9 +9335,9 @@ mz@^2.4.0:
thenify-all "^1.0.0" thenify-all "^1.0.0"
nanoid@^3.3.11: nanoid@^3.3.11:
version "3.3.11" version "3.3.18"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.18.tgz#f66a2de1199ffde0fcf21c8a5f13106b1c081913"
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== integrity sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==
natural-compare-lite@^1.4.0: natural-compare-lite@^1.4.0:
version "1.4.0" version "1.4.0"
@@ -9876,11 +9917,6 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
path-to-regexp@~0.1.12: path-to-regexp@~0.1.12:
version "0.1.13" version "0.1.13"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.13.tgz#9b22ec16bc3ab88d05a0c7e369869421401ab17d" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.13.tgz#9b22ec16bc3ab88d05a0c7e369869421401ab17d"
@@ -10317,7 +10353,7 @@ proto-list@~1.2.1:
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
proxy-addr@~2.0.5, proxy-addr@~2.0.7: proxy-addr@~2.0.7:
version "2.0.7" version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
@@ -10377,7 +10413,7 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0, punycode@^2.3.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
qs@6.15.3, qs@6.7.0, qs@^6.12.3, qs@~6.14.0: qs@6.15.3, qs@^6.12.3, qs@~6.15.1:
version "6.15.3" version "6.15.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.3.tgz#76852132a58ed5c7c0ef67e4441b9bb5d6061b3b" resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.3.tgz#76852132a58ed5c7c0ef67e4441b9bb5d6061b3b"
integrity sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A== integrity sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==
@@ -10420,16 +10456,6 @@ range-parser@^1.2.1, range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
dependencies:
bytes "3.1.0"
http-errors "1.7.2"
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@~2.5.3: raw-body@~2.5.3:
version "2.5.3" version "2.5.3"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2"
@@ -10697,10 +10723,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.3:
hash-base "^3.1.2" hash-base "^3.1.2"
inherits "^2.0.4" inherits "^2.0.4"
roarr@7.21.6: roarr@7.21.7:
version "7.21.6" version "7.21.7"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.21.6.tgz#91c7f0c4b83de49a76b801e73782086baf4a7c8d" resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.21.7.tgz#b77c762660378234e1370c3e333596a744c0f6b1"
integrity sha512-bE9gaDGzWu4BxkyxUEVHfnHruvF4gX9NAZBz86EVJoun2J18exVR/3Wwk0hhu8MWXtabkTVveNcOk1lzHaZbVw== integrity sha512-6X9ID9jb83kPkDxm5xLb9rZdGm6ksdDjpeL0iW5weKelJFi+NVBSowRpEuH8CdpX2x+7RW0TZaezibE7yQB7pg==
dependencies: dependencies:
fast-printf "^1.6.9" fast-printf "^1.6.9"
safe-stable-stringify "^2.4.3" safe-stable-stringify "^2.4.3"
@@ -10734,16 +10760,16 @@ safe-array-concat@^1.1.3:
has-symbols "^1.1.0" has-symbols "^1.1.0"
isarray "^2.0.5" isarray "^2.0.5"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-push-apply@^1.0.0: safe-push-apply@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5"
@@ -11022,10 +11048,10 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
shell-quote@^1.8.3: shell-quote@^1.8.4:
version "1.8.3" version "1.10.0"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.10.0.tgz#482033e192e4f5c07151521ffa03400ec71b1b0f"
integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== integrity sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==
side-channel-list@^1.0.1: side-channel-list@^1.0.1:
version "1.0.1" version "1.0.1"
@@ -11480,9 +11506,9 @@ svg-tags@^1.0.0:
integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==
svgo@^2.7.0: svgo@^2.7.0:
version "2.8.2" version "2.8.3"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.2.tgz#8e99b7ba5ac9ed7e3a446063865f61e03223fe6b" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.3.tgz#8de2fa579f3f7429dec3fb86471005cf46fb483a"
integrity sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA== integrity sha512-5EZD0pafXX6PphdwOGCiVLDSaV1xyuQao2blHajHLsPxr07q4mmEjdtXEWgG07ae2mIz8Ex2CDXNCTiXhy3Khw==
dependencies: dependencies:
commander "^7.2.0" commander "^7.2.0"
css-select "^4.1.3" css-select "^4.1.3"
@@ -11775,7 +11801,7 @@ type-fest@^4.4.0, type-fest@^4.8.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58"
integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==
type-is@~1.6.17, type-is@~1.6.18: type-is@~1.6.18:
version "1.6.18" version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
@@ -11918,7 +11944,7 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
unpipe@1.0.0, unpipe@~1.0.0: unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
@@ -12012,6 +12038,14 @@ vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
vee-validate@4.15.0:
version "4.15.0"
resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-4.15.0.tgz#eb77a9c867669d34abbc33ca5e16f2a991eb7ad5"
integrity sha512-PGJh1QCFwCBjbHu5aN6vB8macYVWrajbDvgo1Y/8fz9n/RVIkLmZCJDpUgu7+mUmCOPMxeyq7vXUOhbwAqdXcA==
dependencies:
"@vue/devtools-api" "^7.5.2"
type-fest "^4.8.3"
vee-validate@^4.15.1: vee-validate@^4.15.1:
version "4.15.1" version "4.15.1"
resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-4.15.1.tgz#66e787e88916baa05f42e53bc0848e0878d50f91" resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-4.15.1.tgz#66e787e88916baa05f42e53bc0848e0878d50f91"
@@ -12319,9 +12353,9 @@ webpack@^5.54.0:
webpack-sources "^3.3.4" webpack-sources "^3.3.4"
websocket-driver@>=0.5.1, websocket-driver@^0.7.4: websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
version "0.7.4" version "0.7.5"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.5.tgz#569d22764ab21f2de20af0e74b411e8ae5a0fa46"
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== integrity sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==
dependencies: dependencies:
http-parser-js ">=0.5.1" http-parser-js ">=0.5.1"
safe-buffer ">=5.1.0" safe-buffer ">=5.1.0"
@@ -12498,14 +12532,14 @@ write-file-atomic@^3.0.0:
typedarray-to-buffer "^3.1.5" typedarray-to-buffer "^3.1.5"
ws@^7.3.1, ws@^7.4.6: ws@^7.3.1, ws@^7.4.6:
version "7.5.10" version "7.5.13"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.13.tgz#12aa507eaca76c295c278b1aebf4698ab2c1845f"
integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== integrity sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==
ws@^8.13.0: ws@^8.13.0:
version "8.20.0" version "8.21.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8" resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.3.tgz#660b4faddb6a3e575c86e078126919961f4de4fc"
integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA== integrity sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==
xml-name-validator@^3.0.0: xml-name-validator@^3.0.0:
version "3.0.0" version "3.0.0"
@@ -12542,36 +12576,6 @@ xtend@^4.0.2:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
xterm-addon-canvas@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xterm-addon-canvas/-/xterm-addon-canvas-0.5.0.tgz#95d056cec6da42a51b2c47746a011409020c388c"
integrity sha512-QOo/eZCMrCleAgMimfdbaZCgmQRWOml63Ued6RwQ+UTPvQj3Av9QKx3xksmyYrDGRO/AVRXa9oNuzlYvLdmoLQ==
xterm-addon-fit@0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz#48ca99015385141918f955ca7819e85f3691d35f"
integrity sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==
xterm-addon-search@0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0.tgz#21286f4db48aa949fbefce34bb8bc0c9d3cec627"
integrity sha512-sDUwG4CnqxUjSEFh676DlS3gsh3XYCzAvBPSvJ5OPgF3MRL3iHLPfsb06doRicLC2xXNpeG2cWk8x1qpESWJMA==
xterm-addon-web-links@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.9.0.tgz#c65b18588d1f613e703eb6feb7f129e7ff1c63e7"
integrity sha512-LIzi4jBbPlrKMZF3ihoyqayWyTXAwGfu4yprz1aK2p71e9UKXN6RRzVONR0L+Zd+Ik5tPVI9bwp9e8fDTQh49Q==
xterm-addon-webgl@0.16.0:
version "0.16.0"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0.tgz#9872d08a64136f893b27ef9a6412136d3bf563c4"
integrity sha512-E8cq1AiqNOv0M/FghPT+zPAEnvIQRDbAbkb04rRYSxUym69elPWVJ4sv22FCLBqM/3LcrmBLl/pELnBebVFKgA==
xterm@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.1.tgz#b3fea7bdb55b9be1d4b31f4cd1091f26ac42afb8"
integrity sha512-cs5Y1fFevgcdoh2hJROMVIWwoBHD80P1fIP79gopLHJIE4kTzzblanoivxTiQ4+92YM9IxS36H1q0MxIJXQBcA==
y18n@^4.0.0: y18n@^4.0.0:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
@@ -12677,3 +12681,8 @@ yocto-queue@^1.0.0:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.2.tgz#3e09c95d3f1aa89a58c114c99223edf639152c00" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.2.tgz#3e09c95d3f1aa89a58c114c99223edf639152c00"
integrity sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ== integrity sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==
zod@3.24.3:
version "3.24.3"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.3.tgz#1f40f750a05e477396da64438e0e1c0995dafd87"
integrity sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==