Compare commits

..
Author SHA1 Message Date
Andy Lee 66aedf00d4 build: artifact2
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-31 17:37:39 +08:00
Andy Lee fbb4b7b41b refactor: copilot review
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-31 17:18:22 +08:00
Andy Lee 3817269c0a fix: bugs in VM selection table
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-31 17:18:22 +08:00
Andy Lee facddcb9a7 feat: add selected all | clear all actions for selected VM
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-31 17:18:21 +08:00
Andy Lee 7db6c81c5c feat: add access/ volume mode model
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-31 17:18:21 +08:00
Andy Lee 74ef7b3772 fix: VM migration tab not appear when first login
Signed-off-by: Andy Lee <andy.lee@suse.com>
2026-07-31 17:18:21 +08:00
19 changed files with 337 additions and 930 deletions
+3 -7
View File
@@ -8,16 +8,12 @@
- [ ] Yes, the backend owner is @
- [ ] No, frontend-only.
### Related Issue
### Related Issue #
<!-- Link the issue as harvester/harvester#<issue_number> -->
harvester/harvester#<issue_number>
### Test Steps
<!-- 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 -->
### Test screenshot or video (Required)
<!-- Attach screenshot or video of the changes and eventual comparison if you find it necessary -->
@@ -1,12 +1,12 @@
name: Build Standalone on PR Merge
on:
workflow_dispatch: # manually trigger on Github Actions only support PR branch pushed to harvester/harvester-ui-extension
push:
branches:
- main
- 'release-harvester-v*'
- '*-dev'
- access_volume_mode_demo
pull_request:
branches:
- main
@@ -30,35 +30,3 @@ jobs:
id-token: write
with:
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"
+3 -9
View File
@@ -7,7 +7,7 @@
},
"dependencies": {
"@babel/plugin-transform-class-static-block": "7.29.7",
"@rancher/shell": "3.0.12-rc.7",
"@rancher/shell": "3.0.12-rc.3",
"@vue-flow/background": "^1.3.0",
"@vue-flow/controls": "^1.1.1",
"@vue-flow/core": "^1.33.5",
@@ -15,12 +15,6 @@
"cache-loader": "^4.1.0",
"color": "4.2.3",
"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",
"elkjs": "^0.11.0",
"vue-draggable-next": "^2.2.1",
@@ -35,12 +29,12 @@
"glob": "7.2.3",
"glob-parent": "6.0.2",
"json5": "2.2.3",
"@types/lodash": "4.17.25",
"@types/lodash": "4.17.24",
"merge": "2.1.1",
"node-forge": "1.4.0",
"nth-check": "2.1.1",
"qs": "6.15.3",
"roarr": "7.21.7",
"roarr": "7.21.6",
"semver": "7.8.5",
"@vue/cli-service/html-webpack-plugin": "^5.0.0"
},
-5
View File
@@ -271,9 +271,4 @@ export default {
.filter-label .v-popper__arrow-container {
display: none;
}
.fixed-header-actions .middle .filter + .btn-group {
display: inline-flex;
vertical-align: middle;
}
</style>
@@ -116,11 +116,6 @@ export default {
.vm-schedule-dropdown .v-popper__arrow-container {
display: none;
}
.fixed-header-actions .middle .vm-schedule-filter + .btn-group {
display: inline-flex;
vertical-align: middle;
}
</style>
<style lang="scss" scoped>
+63 -442
View File
@@ -1,6 +1,7 @@
<script>
import { allHash } from '@shell/utils/promise';
import debounce from 'lodash/debounce';
import Socket, {
EVENT_CONNECTED,
EVENT_CONNECTING,
@@ -21,98 +22,74 @@ export default {
data() {
return {
socket: null,
terminal: null,
textDecoder: null,
fitAddon: null,
searchAddon: null,
webglAddon: null,
onResize: null,
isOpen: false,
isOpening: false,
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',
},
},
socket: null,
terminal: null,
fitAddon: null,
searchAddon: null,
webglAddon: null,
isOpen: false,
isOpening: false,
backlog: [],
firstTime: true,
queue: []
};
},
computed: {
serialConsoleUrl() {
return this.value?.getSerialConsolePath || '';
},
xtermConfig() {
return {
allowProposedApi: true,
cursorBlink: true,
cursorStyle: 'bar',
cursorWidth: 2,
scrollback: 5000,
useStyle: true,
fontFamily: 'JetBrains Mono, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
fontSize: 16,
lineHeight: 1.3,
fontSize: 12,
};
},
},
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() {
this.close();
},
watch: {
serialConsoleUrl() {
if (this.terminal) {
this.connect();
}
},
},
async mounted() {
this.textDecoder = new TextDecoder('utf-8');
await this.setupTerminal();
await this.connect();
this.onResize = debounce(() => this.fit(), 100);
window.addEventListener('resize', this.onResize);
this.scheduleFit();
},
methods: {
async setupTerminal() {
const docStyle = getComputedStyle(document.querySelector('body'));
const xterm = await import(/* webpackChunkName: "xterm" */ '@xterm/xterm');
const xterm = await import(/* webpackChunkName: "xterm" */ 'xterm');
const addons = await allHash({
fit: import(/* webpackChunkName: "xterm" */ '@xterm/addon-fit'),
webgl: import(/* webpackChunkName: "xterm" */ '@xterm/addon-webgl'),
weblinks: import(/* webpackChunkName: "xterm" */ '@xterm/addon-web-links'),
search: import(/* webpackChunkName: "xterm" */ '@xterm/addon-search'),
canvas: import(/* webpackChunkName: "xterm" */ '@xterm/addon-canvas'),
fit: import(/* webpackChunkName: "xterm" */ 'xterm-addon-fit'),
webgl: import(/* webpackChunkName: "xterm" */ 'xterm-addon-webgl'),
weblinks: import(/* webpackChunkName: "xterm" */ 'xterm-addon-web-links'),
search: import(/* webpackChunkName: "xterm" */ 'xterm-addon-search'),
});
const terminal = new xterm.Terminal({
theme: {
background: docStyle.getPropertyValue('--terminal-bg').trim(),
cursor: docStyle.getPropertyValue('--terminal-text').trim(),
cursor: docStyle.getPropertyValue('--terminal-cursor').trim(),
foreground: docStyle.getPropertyValue('--terminal-text').trim()
},
...this.xtermConfig,
@@ -120,48 +97,25 @@ export default {
this.fitAddon = new addons.fit.FitAddon();
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.searchAddon);
terminal.loadAddon(new addons.weblinks.WebLinksAddon());
terminal.open(this.$refs.xterm);
// 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);
}
});
if ( 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.flush();
this.scheduleDrainQueue();
terminal.onData((input) => {
const msg = this.str2ab(input);
@@ -169,132 +123,9 @@ export default {
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;
},
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) {
const enc = new TextEncoder();
@@ -309,131 +140,12 @@ 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() {
this.terminal.clear();
},
getSocketUrl() {
return this.serialConsoleUrl;
return `${ this.value?.getSerialConsolePath }`;
},
async connect() {
@@ -469,7 +181,6 @@ export default {
this.fit();
this.flush();
}
this.scheduleFit();
if (this.firstTime) {
this.socket.send(this.str2ab('\n'));
@@ -484,7 +195,7 @@ export default {
});
this.socket.addEventListener(EVENT_MESSAGE, (e) => {
this.enqueueMessage(this.decodeMessageData(e.detail.data));
this.queue.push(e.detail.data.text());
});
this.socket.connect();
@@ -507,19 +218,22 @@ export default {
}
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() {
if (this.onResize) {
window.removeEventListener('resize', this.onResize);
this.onResize = null;
}
if (this.drainTimer) {
cancelAnimationFrame(this.drainTimer);
this.drainTimer = null;
}
if ( this.socket ) {
this.socket.disconnect();
}
@@ -534,44 +248,6 @@ export default {
<template>
<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
ref="xterm"
class="shell-body"
@@ -581,73 +257,18 @@ export default {
</template>
<style lang="scss">
@import '../../../../node_modules/@xterm/xterm/css/xterm.css';
@import '../../../../node_modules/xterm/css/xterm.css';
body, #__nuxt, #__layout, MAIN {
height: 100%;
}
.harvester-shell-container {
display: flex;
flex-direction: column;
position: relative;
height: 100%;
min-height: 0;
overflow: hidden;
.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;
.shell-body, .terminal.xterm {
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>
+1 -5
View File
@@ -55,10 +55,6 @@ export default {
const host = window.location.host;
const prefix = window.location.pathname.replace(this.$route.path, '');
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 }`;
@@ -67,7 +63,7 @@ export default {
window.open(
url,
'_blank',
`toolbars=0,width=${ popupWidth },height=${ popupHeight },left=${ popupLeft },top=${ popupTop },noreferrer`
`toolbars=0,width=${ screen.width - 200 },height=${ screen.height - 200 },left=0,top=0,noreferrer`
);
});
},
@@ -273,6 +273,19 @@ const startMigrationAction = async() => {
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
// lives in a single place (also reused by the dashboard start/restart action).
await plan.startMigration();
@@ -2,16 +2,13 @@
import { exceptionToErrorsArray } from '@shell/utils/error';
import { sortBy } from '@shell/utils/sort';
import { mapGetters } from 'vuex';
import { PVC, STORAGE_CLASS, LONGHORN_DRIVER } from '@shell/config/types';
import { PVC } from '@shell/config/types';
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 { Banner } from '@components/Banner';
import AsyncButton from '@shell/components/AsyncButton';
import { LabeledInput } from '@components/Form/LabeledInput';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { Checkbox } from '@components/Form/Checkbox';
export default {
name: 'HotplugVolumeModal',
@@ -19,7 +16,7 @@ export default {
emits: ['close'],
components: {
AsyncButton, Card, LabeledInput, LabeledSelect, Banner, Checkbox
AsyncButton, Card, LabeledInput, LabeledSelect, Banner
},
props: {
@@ -31,19 +28,14 @@ export default {
async fetch() {
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() {
return {
diskName: '',
volumeName: '',
shareable: false,
errors: [],
allPVCs: [],
allStorageClasses: [],
allVMs: [],
diskName: '',
volumeName: '',
errors: [],
allPVCs: [],
};
},
@@ -58,31 +50,6 @@ export default {
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() {
return sortBy(
this.PVCs
@@ -94,14 +61,6 @@ export default {
if (pvc.isGoldenImageVolume) {
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;
})
@@ -114,62 +73,19 @@ export default {
'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: {
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() {
this.diskName = '';
this.volumeName = '';
this.shareable = false;
this.$emit('close');
},
async save(buttonCb) {
if (this.actionResource) {
try {
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);
const res = await this.actionResource.doAction('addVolume', { volumeSourceName: this.volumeName, diskName: this.diskName }, {}, false);
if (res._status === 200 || res._status === 204) {
this.$store.dispatch('growl/success', {
@@ -225,19 +141,6 @@ export default {
class="mt-20"
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
v-for="(err, i) in errors"
:key="i"
+3 -14
View File
@@ -9,7 +9,7 @@ import Loading from '@shell/components/Loading';
import CreateEditView from '@shell/mixins/create-edit-view';
import { RadioGroup } from '@components/Form/Radio';
import { NETWORK_PROTOCOL, NETWORK_TYPE } from '@pkg/harvester/config/types';
import { set, remove } from '@shell/utils/object';
import { set } from '@shell/utils/object';
import ArrayList from '@shell/components/form/ArrayList';
import { allHash } from '@shell/utils/promise';
import { HCI } from '../../types';
@@ -142,12 +142,10 @@ export default {
const inStore = this.$store.getters['currentProduct'].inStore;
const vlans = this.$store.getters[`${ inStore }/all`](HCI.VLAN) || [];
const options = vlans.map((vlan) => ({
return vlans.map((vlan) => ({
label: vlan.id,
value: vlan.id,
}));
return [{ label: this.t('generic.none'), value: '' }, ...options];
}
},
@@ -162,14 +160,6 @@ export default {
}
},
methods: {
onVlanChange(value) {
if (value === '') {
remove(this.value.spec, 'vlan');
} else {
set(this.value, 'spec.vlan', value);
}
},
async saveSubnet(buttonCb) {
const errors = [];
const name = this.value?.metadata?.name;
@@ -293,13 +283,12 @@ export default {
</div>
<div class="col span-6">
<LabeledSelect
:value="value.spec.vlan ?? ''"
v-model:value="value.spec.vlan"
class="mb-20"
:options="vlanOptions"
:placeholder="t('harvester.subnet.vlan.placeholder')"
:label="t('harvester.subnet.vlan.label')"
:mode="mode"
@update:value="onVlanChange"
/>
</div>
</div>
@@ -5,12 +5,10 @@ import LabelValue from '@shell/components/LabelValue';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import InputOrDisplay from '@shell/components/InputOrDisplay';
import { Banner } from '@components/Banner';
import { Checkbox } from '@components/Form/Checkbox';
import { sortBy } from '@shell/utils/sort';
import { PVC, STORAGE_CLASS, LONGHORN_DRIVER } from '@shell/config/types';
import { PVC } from '@shell/config/types';
import { _CREATE } from '@shell/config/query-params';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
import { VOLUME_MODE } from '@pkg/harvester/config/types';
import { HCI } from '../../../../types';
import { VOLUME_TYPE, InterfaceOption } from '../../../../config/harvester-map';
import { GIBIBYTE } from '../../../../utils/unit';
@@ -21,7 +19,7 @@ export default {
emits: ['update'],
components: {
UnitInput, LabeledInput, LabeledSelect, InputOrDisplay, LabelValue, Banner, Checkbox
UnitInput, LabeledInput, LabeledSelect, InputOrDisplay, LabelValue, Banner
},
props: {
@@ -96,30 +94,6 @@ export default {
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() {
return sortBy(
this.allPVCs
@@ -171,7 +145,6 @@ export default {
this.value.size = pvcResource.spec.resources.requests.storage;
this.value.storageClassName = pvcResource.spec.storageClassName;
this.value.volumeMode = pvcResource.spec.volumeMode;
this.value.shareable = false;
this.update();
},
@@ -182,13 +155,6 @@ export default {
}
},
isShareableCapable(neu) {
if (!neu && this.value.shareable) {
this.value.shareable = false;
this.update();
}
},
pvcResource: {
handler(pvc) {
if (!this.value.volumeName && pvc?.metadata?.name) {
@@ -337,26 +303,6 @@ export default {
/>
</InputOrDisplay>
</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
v-if="value.volumeBackups"
class="col span-6"
@@ -63,7 +63,6 @@ export default {
RANCHER_TYPES,
fvFormRuleSets: [{ path: 'spec.displayName', rules: ['required'] }],
canEditPSPBindings: true,
isQuotasValid: true,
};
},
computed: {
@@ -191,13 +190,15 @@ export default {
this['membershipUpdate'] = update;
},
validateQuotas(isValid) {
this.isQuotasValid = isValid;
},
onQuotasInput({ projectLimit, nsLimit } = {}) {
this.value.spec.resourceQuota = { ...this.value.spec.resourceQuota, limit: projectLimit };
this.value.spec.namespaceDefaultResourceQuota = { ...this.value.spec.namespaceDefaultResourceQuota, limit: nsLimit };
removeQuota(key) {
['resourceQuota', 'namespaceDefaultResourceQuota'].forEach((specProp) => {
if (this.value?.spec[specProp]?.limit && this.value?.spec[specProp]?.limit[key]) {
delete this.value?.spec[specProp]?.limit[key];
}
if (this.value?.spec[specProp]?.usedLimit && this.value?.spec[specProp]?.usedLimit[key]) {
delete this.value?.spec[specProp]?.usedLimit[key];
}
});
}
},
};
@@ -211,7 +212,7 @@ export default {
:resource="value"
:subtypes="[]"
:can-yaml="false"
:validation-passed="fvFormIsValid && isQuotasValid"
:validation-passed="fvFormIsValid"
@error="e=>errors=e"
@finish="save"
@cancel="done"
@@ -266,11 +267,10 @@ export default {
:weight="9"
>
<ResourceQuota
:value="value"
v-model:value="value"
:mode="canEditTabElements"
:types="isStandaloneHarvester ? HARVESTER_TYPES : RANCHER_TYPES"
@input="onQuotasInput"
@validationChanged="validateQuotas"
@remove="removeQuota"
/>
</Tab>
<Tab
+3 -7
View File
@@ -798,10 +798,6 @@ harvester:
setFirst: Set as root volume
saveVolume: Update Volume
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})
lockTooltip:
all: All volumes are encrypted.
@@ -835,13 +831,13 @@ harvester:
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>"
sysprep:
title: Windows Unattended & Sysprep Configuration
title: Windows 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>"
createNew: Create new...
createNewTitle: Create Windows Unattended & Sysprep Template
createNewTitle: Create Windows Sysprep Template
xmlContent: autounattend.xml content
secret:
label: Windows Unattended & Sysprep Template
label: Windows Sysprep Template
validation:
invalidXml: Invalid XML syntax
invalidNamespace: Invalid unattend.xml namespace. Expected 'urn:schemas-microsoft-com:unattend'
-12
View File
@@ -38,10 +38,6 @@ export const SSH_EXISTING_TYPE = {
export default {
methods: {
hasCloudConfigComment(userScript) {
if (typeof userScript === 'string' && /(^|\n)\s*#cloud-config(\s|$)/.test(userScript)) {
return true;
}
// Check that userData contains: #cloud-config
const userDataDoc = userScript ? YAML.parseDocument(userScript) : YAML.parseDocument({});
const items = userDataDoc?.contents?.items || [];
@@ -56,14 +52,6 @@ export default {
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) => {
const key = item.key;
+2 -16
View File
@@ -618,7 +618,6 @@ export default {
type,
storageClassName,
hotpluggable,
shareable: DISK.shareable || false,
volumeStatus,
dataSource,
namespace,
@@ -970,14 +969,6 @@ export default {
const specDisks = this.spec?.template?.spec?.domain?.devices?.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 = {
...this.spec,
runStrategy: this.runStrategy,
@@ -1211,10 +1202,9 @@ export default {
userDataDoc = config.installAgent ? this.mergeQGA({ userDataDoc, ...config }) : this.deleteQGA({ userDataDoc, ...config });
const userDataYaml = userDataDoc.toString();
const userDataValue = userDataDoc.toJSON();
if (userDataValue === null || (typeof userDataValue === 'object' && !Array.isArray(userDataValue) && isEmpty(userDataValue))) {
// Empty userData should not create cloud-init content like `null`.
if (userDataYaml === '{}\n') {
// When the YAML parsed value is '{}\n', it means that the userData is empty, then undefined is returned.
return undefined;
}
@@ -1263,10 +1253,6 @@ export default {
out.cdrom = { bus: R.bus };
}
if (R.shareable) {
out.shareable = true;
}
out.bootOrder = index + 1;
return out;
-5
View File
@@ -69,11 +69,6 @@ export default class HarvesterResource extends SteveModel {
}
}
// Disable Resources/ Extras cards in resource details pages
get resourcesCardRows() {
return [];
}
cleanForSave(data, _forNew) {
return data;
}
@@ -1,9 +1,10 @@
<script>
import Loading from '@shell/components/Loading';
import { HCI } from '../../../../../types';
import SerialConsole from '../../../../../components/SerialConsole';
export default {
components: { SerialConsole },
components: { SerialConsole, Loading },
async fetch() {
this.rows = await this.$store.dispatch('harvester/findAll', { type: HCI.VMI });
@@ -37,44 +38,16 @@ export default {
</script>
<template>
<div class="serial-console-page">
<SerialConsole
ref="serialConsole"
:value="vmi || {}"
/>
</div>
<Loading v-if="$fetchState.pending" />
<SerialConsole
v-else
ref="serialConsole"
v-model:value="vmi"
/>
</template>
<style lang="scss">
html,
body,
#__nuxt,
#__layout,
main {
<style lang="scss" scoped>
body, #__nuxt, #__layout, main {
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>
@@ -68,35 +68,29 @@ export default defineComponent({
return (this.value || []).map((plan) => {
const planName = plan?.metadata?.name || '-';
const namespace = plan?.metadata?.namespace || '';
const networkMapRef = this.getPlanMapRef(plan, 'network');
const storageMapRef = this.getPlanMapRef(plan, 'storage');
const migrations = existing[HCI.FORKLIFT_MIGRATION]
.filter((resource) => this.ownedByPlan(resource, plan))
.map((resource) => resource?.metadata?.name)
.filter(Boolean);
.map((resource) => ({
name: resource?.metadata?.name,
namespace: resource?.metadata?.namespace || namespace,
}));
const matchedNetworkMap = networkMapRef ? existing[HCI.FORKLIFT_NETWORK_MAP].find((resource) => resource?.metadata?.name === networkMapRef.name && resource?.metadata?.namespace === networkMapRef.namespace) : null;
const matchedStorageMap = storageMapRef ? existing[HCI.FORKLIFT_STORAGE_MAP].find((resource) => resource?.metadata?.name === storageMapRef.name && resource?.metadata?.namespace === storageMapRef.namespace) : null;
const matchedNetworkMap = existing[HCI.FORKLIFT_NETWORK_MAP].find((resource) => this.ownedByPlan(resource, plan));
const matchedStorageMap = existing[HCI.FORKLIFT_STORAGE_MAP].find((resource) => this.ownedByPlan(resource, plan));
return {
planName,
namespace,
migrations,
networkMap: matchedNetworkMap ? {
namespace: matchedNetworkMap?.metadata?.namespace || networkMapRef?.namespace || namespace,
namespace: matchedNetworkMap?.metadata?.namespace || namespace,
name: matchedNetworkMap?.metadata?.name,
} : (networkMapRef ? {
namespace: networkMapRef.namespace,
name: networkMapRef.name,
} : null),
} : null,
storageMap: matchedStorageMap ? {
namespace: matchedStorageMap?.metadata?.namespace || storageMapRef?.namespace || namespace,
namespace: matchedStorageMap?.metadata?.namespace || namespace,
name: matchedStorageMap?.metadata?.name,
} : (storageMapRef ? {
namespace: storageMapRef.namespace,
name: storageMapRef.name,
} : null),
} : null,
};
});
},
@@ -119,20 +113,6 @@ export default defineComponent({
methods: {
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)
* is owned by the given plan by inspecting its `metadata.ownerReferences`.
@@ -157,8 +137,9 @@ export default defineComponent({
const existing = this.existingRelatedResources;
const targets = new Map();
const addTarget = (type, name, namespace) => {
const ns = namespace || '';
const addTarget = (type, resource) => {
const name = resource?.metadata?.name;
const ns = resource?.metadata?.namespace;
if (!name) {
return;
@@ -172,20 +153,17 @@ export default defineComponent({
};
for (const plan of this.value || []) {
const networkMapRef = this.getPlanMapRef(plan, 'network');
const storageMapRef = this.getPlanMapRef(plan, 'storage');
existing[HCI.FORKLIFT_NETWORK_MAP]
.filter((resource) => this.ownedByPlan(resource, plan))
.forEach((resource) => addTarget(HCI.FORKLIFT_NETWORK_MAP, resource));
if (networkMapRef) {
addTarget(HCI.FORKLIFT_NETWORK_MAP, networkMapRef.name, networkMapRef.namespace);
}
if (storageMapRef) {
addTarget(HCI.FORKLIFT_STORAGE_MAP, storageMapRef.name, storageMapRef.namespace);
}
existing[HCI.FORKLIFT_STORAGE_MAP]
.filter((resource) => this.ownedByPlan(resource, plan))
.forEach((resource) => addTarget(HCI.FORKLIFT_STORAGE_MAP, resource));
existing[HCI.FORKLIFT_MIGRATION]
.filter((resource) => this.ownedByPlan(resource, plan))
.forEach((resource) => addTarget(HCI.FORKLIFT_MIGRATION, resource?.metadata?.name, resource?.metadata?.namespace));
.forEach((resource) => addTarget(HCI.FORKLIFT_MIGRATION, resource));
}
return [...targets.values()];
@@ -278,11 +256,11 @@ export default defineComponent({
<div>• {{ t('harvester.addons.vmMigration.labels.migration') }}:</div>
<template v-if="summary.migrations.length">
<div
v-for="migrationName in summary.migrations"
:key="migrationName"
v-for="migration in summary.migrations"
:key="`${ migration.namespace }/${ migration.name }`"
>
<div class="ml-20">
- {{ migrationName }}
- {{ migration.name }}
</div>
</div>
</template>
+191 -116
View File
@@ -2181,15 +2181,15 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@rancher/icons@2.0.62":
version "2.0.62"
resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.62.tgz#42d3aa89d03a2e0e551332e99c8102dfdd422746"
integrity sha512-ywGk1NSrIdN1zllpbDRU3ESVW/D0ExRZFBz+YPGAo9G08VzWxKIcT5GG5Nd2GXLTcUestlq1JwkKDaVAM8hkmQ==
"@rancher/icons@2.0.60":
version "2.0.60"
resolved "https://registry.yarnpkg.com/@rancher/icons/-/icons-2.0.60.tgz#a7d8bd39a007ef8ecf1cb7bfe27d82f64f9cd75c"
integrity sha512-5bbXVOc4Uk2OmACX67i1e1jOTeLg87T22u+alhwEZX+/WzBEH/B0DPVqSbuYQtKYkH0Me4ojnOVCzZ2HuuLHWQ==
"@rancher/shell@3.0.12-rc.7":
version "3.0.12-rc.7"
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.12-rc.7.tgz#509b42d1a40fc8f399dc8c15fd07f2ea4361af54"
integrity sha512-aA2uX5roVhJtj8p09OQNREhFraa9MqSrjXjbAC+3uRLniQkhqx+INKpyBDvhkDy0wd7XWIGbX3cfl+Kz6mwSYA==
"@rancher/shell@3.0.12-rc.3":
version "3.0.12-rc.3"
resolved "https://registry.yarnpkg.com/@rancher/shell/-/shell-3.0.12-rc.3.tgz#566ea39fac704fbed2e0bc070f47b5bb3b6a92fb"
integrity sha512-4mKX3Ou2t/P8ko5Tb8UuuxgttgQPPX66Rlq8vC6IftGowJ+ExtM6FJDg4bKy1Lu3DrDERHoQLmTHehDScRnSEw==
dependencies:
"@aws-sdk/client-ec2" "3.1041.0"
"@aws-sdk/client-eks" "3.1041.0"
@@ -2201,28 +2201,21 @@
"@babel/preset-typescript" "7.16.7"
"@novnc/novnc" "1.2.0"
"@popperjs/core" "2.11.8"
"@rancher/icons" "2.0.62"
"@rancher/icons" "2.0.60"
"@smithy/fetch-http-handler" "5.1.1"
"@types/is-url" "1.2.30"
"@types/node" "25.3.3"
"@types/semver" "^7.5.8"
"@typescript-eslint/eslint-plugin" "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-typescript" "~5.0.0"
"@vue/cli-service" "5.0.8"
"@vue/test-utils" "2.4.6"
"@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"
ansi_up "5.0.0"
axios "1.16.0"
axios "1.15.2"
axios-retry "3.1.9"
babel-eslint "10.1.0"
babel-preset-vue "2.0.2"
@@ -2255,13 +2248,14 @@
eslint-plugin-n "15.2.0"
eslint-plugin-vue "9.32.0"
event-target-shim "5.0.1"
express "4.22.2"
file-saver "2.0.5"
express "4.17.1"
file-saver "2.0.2"
floating-vue "5.2.2"
focus-trap "7.6.5"
frontmatter-markdown-loader "3.7.0"
identicon.js "2.3.3"
intl-messageformat "7.8.4"
ip "2.0.1"
is-url "1.2.4"
jest "27.5.1"
jest-serializer-vue "2.0.2"
@@ -2298,8 +2292,13 @@
webpack-virtual-modules "0.6.2"
worker-loader "3.0.8"
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"
zod "3.24.3"
"@rtsao/scc@^1.1.0":
version "1.1.0"
@@ -2825,10 +2824,10 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/lodash@4.17.25":
version "4.17.25"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.25.tgz#69765ac7bcddb0eb072961cf292524a8f5b3c2c0"
integrity sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==
"@types/lodash@4.17.24":
version "4.17.24"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f"
integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==
"@types/mime@^1":
version "1.3.5"
@@ -3099,14 +3098,6 @@
"@typescript-eslint/types" "5.62.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":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@vue-flow/background/-/background-1.3.2.tgz#0c90cd05e5d60da017bbaf5a1c3eb6af7ed9b778"
@@ -3699,36 +3690,6 @@
"@webassemblyjs/ast" "1.14.1"
"@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":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -3769,7 +3730,7 @@ abort-controller@^3.0.0:
dependencies:
event-target-shim "^5.0.0"
accepts@~1.3.8:
accepts@~1.3.7, accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
@@ -4132,12 +4093,12 @@ axios-retry@3.1.9:
dependencies:
is-retry-allowed "^1.1.0"
axios@1.16.0:
version "1.16.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.0.tgz#f8e5dd931cef2a5f8c32216d5784eda2f8750eb7"
integrity sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==
axios@1.15.2:
version "1.15.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.15.2.tgz#eb8fb6d30349abace6ade5b4cb4d9e8a0dc23e5b"
integrity sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==
dependencies:
follow-redirects "^1.16.0"
follow-redirects "^1.15.11"
form-data "^4.0.5"
proxy-from-env "^2.1.0"
@@ -4367,10 +4328,26 @@ bn.js@^5.2.1, bn.js@^5.2.2:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.3.tgz#16a9e409616b23fef3ccbedb8d42f13bff80295e"
integrity sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==
body-parser@~1.20.5:
version "1.20.6"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.6.tgz#60c789c78e0992d906da0a29d71ae01d15c1ed76"
integrity sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==
body-parser@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
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:
bytes "~3.1.2"
content-type "~1.0.5"
@@ -4380,7 +4357,7 @@ body-parser@~1.20.5:
http-errors "~2.0.1"
iconv-lite "~0.4.24"
on-finished "~2.4.1"
qs "~6.15.1"
qs "~6.14.0"
raw-body "~2.5.3"
type-is "~1.6.18"
unpipe "~1.0.0"
@@ -4558,6 +4535,11 @@ builtins@^4.0.0:
dependencies:
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:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
@@ -5044,6 +5026,13 @@ constants-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
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:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
@@ -5090,7 +5079,7 @@ convert-source-map@^2.0.0:
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
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"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
@@ -5103,7 +5092,7 @@ cookie-universal@2.2.2:
"@types/cookie" "^0.3.3"
cookie "^0.4.0"
cookie@^0.4.0:
cookie@0.4.0, cookie@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
@@ -6667,14 +6656,50 @@ expect@^27.5.1:
jest-matcher-utils "^27.5.1"
jest-message-util "^27.5.1"
express@4.22.2, express@^4.17.3:
version "4.22.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.22.2.tgz#c17ae0981e5efc24b22272f0e041c4662503b700"
integrity sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==
express@4.17.1:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
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:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "~1.20.5"
body-parser "~1.20.3"
content-disposition "~0.5.4"
content-type "~1.0.4"
cookie "~0.7.1"
@@ -6693,7 +6718,7 @@ express@4.22.2, express@^4.17.3:
parseurl "~1.3.3"
path-to-regexp "~0.1.12"
proxy-addr "~2.0.7"
qs "~6.15.1"
qs "~6.14.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "~0.19.0"
@@ -6800,10 +6825,10 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
file-saver@2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
file-saver@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.2.tgz#06d6e728a9ea2df2cce2f8d9e84dfcdc338ec17a"
integrity sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==
filelist@^1.0.4:
version "1.0.6"
@@ -6819,6 +6844,19 @@ fill-range@^7.1.1:
dependencies:
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:
version "1.3.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88"
@@ -6899,7 +6937,7 @@ focus-trap@7.6.5:
dependencies:
tabbable "^6.2.0"
follow-redirects@1.16.0, follow-redirects@^1.0.0, follow-redirects@^1.16.0:
follow-redirects@1.16.0, follow-redirects@^1.0.0, follow-redirects@^1.15.11:
version "1.16.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc"
integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==
@@ -7395,9 +7433,9 @@ html-tags@^2.0.0:
integrity sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==
html-webpack-plugin@^5.0.0, html-webpack-plugin@^5.1.0:
version "5.6.8"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.8.tgz#5425dbc63eb533ffa59e92342be94894a125fc23"
integrity sha512-MZmKQcTnhEh1SPSyMiEytIeDZDUoBZVorNHivQGXMASHf/BSGGOrKa2xQ5bGx3TCe1n109ecCt+cpww7wwWhKA==
version "5.6.7"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz#429bab4e12abf3c07e1c608886608e2df2c06b11"
integrity sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==
dependencies:
"@types/html-minifier-terser" "^6.0.0"
html-minifier-terser "^6.0.2"
@@ -7420,7 +7458,7 @@ http-deceiver@^1.2.7:
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==
http-errors@~1.7.2:
http-errors@1.7.2, http-errors@~1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
@@ -9065,6 +9103,11 @@ meow@^12.0.1:
resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6"
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:
version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
@@ -9833,6 +9876,11 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
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:
version "0.1.13"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.13.tgz#9b22ec16bc3ab88d05a0c7e369869421401ab17d"
@@ -10269,7 +10317,7 @@ proto-list@~1.2.1:
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
proxy-addr@~2.0.7:
proxy-addr@~2.0.5, proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
@@ -10329,7 +10377,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"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
qs@6.15.3, qs@^6.12.3, qs@~6.15.1:
qs@6.15.3, qs@6.7.0, qs@^6.12.3, qs@~6.14.0:
version "6.15.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.3.tgz#76852132a58ed5c7c0ef67e4441b9bb5d6061b3b"
integrity sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==
@@ -10372,6 +10420,16 @@ range-parser@^1.2.1, range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
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:
version "2.5.3"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2"
@@ -10639,10 +10697,10 @@ ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.3:
hash-base "^3.1.2"
inherits "^2.0.4"
roarr@7.21.7:
version "7.21.7"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.21.7.tgz#b77c762660378234e1370c3e333596a744c0f6b1"
integrity sha512-6X9ID9jb83kPkDxm5xLb9rZdGm6ksdDjpeL0iW5weKelJFi+NVBSowRpEuH8CdpX2x+7RW0TZaezibE7yQB7pg==
roarr@7.21.6:
version "7.21.6"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.21.6.tgz#91c7f0c4b83de49a76b801e73782086baf4a7c8d"
integrity sha512-bE9gaDGzWu4BxkyxUEVHfnHruvF4gX9NAZBz86EVJoun2J18exVR/3Wwk0hhu8MWXtabkTVveNcOk1lzHaZbVw==
dependencies:
fast-printf "^1.6.9"
safe-stable-stringify "^2.4.3"
@@ -10676,16 +10734,16 @@ safe-array-concat@^1.1.3:
has-symbols "^1.1.0"
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:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
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:
version "1.0.0"
resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5"
@@ -11717,7 +11775,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"
integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==
type-is@~1.6.18:
type-is@~1.6.17, type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
@@ -11860,7 +11918,7 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
unpipe@~1.0.0:
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
@@ -11954,14 +12012,6 @@ vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
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:
version "4.15.1"
resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-4.15.1.tgz#66e787e88916baa05f42e53bc0848e0878d50f91"
@@ -12492,6 +12542,36 @@ xtend@^4.0.2:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
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:
version "4.0.3"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
@@ -12597,8 +12677,3 @@ yocto-queue@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.2.tgz#3e09c95d3f1aa89a58c114c99223edf639152c00"
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==