From a80bc2795199dca4957bd20e877ee5daa8baef95 Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Tue, 4 Nov 2025 13:46:13 +0200 Subject: [PATCH 01/65] feat: implement sortable columns with visual indicators and sorting logic --- adminforth/spa/src/afcl/Table.vue | 112 ++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 6 deletions(-) diff --git a/adminforth/spa/src/afcl/Table.vue b/adminforth/spa/src/afcl/Table.vue index 93e057f1..394bb4fc 100644 --- a/adminforth/spa/src/afcl/Table.vue +++ b/adminforth/spa/src/afcl/Table.vue @@ -4,13 +4,28 @@ - @@ -141,16 +156,21 @@ columns: { label: string, fieldName: string, + sortable?: boolean, }[], data: { [key: string]: any, - }[] | ((params: { offset: number, limit: number }) => Promise<{data: {[key: string]: any}[], total: number}>), + }[] | ((params: { offset: number, limit: number, sortField?: string, sortDirection?: 'asc' | 'desc' }) => Promise<{data: {[key: string]: any}[], total: number}>), evenHighlights?: boolean, pageSize?: number, isLoading?: boolean, + sortable?: boolean, // enable/disable sorting globally + defaultSortField?: string, + defaultSortDirection?: 'asc' | 'desc', }>(), { evenHighlights: true, pageSize: 5, + sortable: true, } ); @@ -163,6 +183,8 @@ const isLoading = ref(false); const dataResult = ref<{data: {[key: string]: any}[], total: number}>({data: [], total: 0}); const isAtLeastOneLoading = ref([false]); + const currentSortField = ref(props.defaultSortField); + const currentSortDirection = ref<'asc' | 'desc'>(props.defaultSortDirection ?? 'asc'); onMounted(() => { refresh(); @@ -181,6 +203,15 @@ emit('update:tableLoading', isLoading.value || props.isLoading); }); + watch([() => currentSortField.value, () => currentSortDirection.value], () => { + // reset to first page on sort change + if (currentPage.value !== 1) currentPage.value = 1; + refresh(); + emit('update:sortField', currentSortField.value); + emit('update:sortDirection', currentSortField.value ? currentSortDirection.value : undefined as any); + emit('sort-change', { field: currentSortField.value, direction: currentSortDirection.value }); + }); + const totalPages = computed(() => { return dataResult.value?.total ? Math.ceil(dataResult.value.total / props.pageSize) : 1; }); @@ -196,6 +227,9 @@ const emit = defineEmits([ 'update:tableLoading', + 'update:sortField', + 'update:sortDirection', + 'sort-change', ]); function onPageInput(event: any) { @@ -231,7 +265,12 @@ isLoading.value = true; const currentLoadingIndex = currentPage.value; isAtLeastOneLoading.value[currentLoadingIndex] = true; - const result = await props.data({ offset: (currentLoadingIndex - 1) * props.pageSize, limit: props.pageSize }); + const result = await props.data({ + offset: (currentLoadingIndex - 1) * props.pageSize, + limit: props.pageSize, + sortField: currentSortField.value, + sortDirection: currentSortDirection.value, + }); isAtLeastOneLoading.value[currentLoadingIndex] = false; if (isAtLeastOneLoading.value.every(v => v === false)) { isLoading.value = false; @@ -240,7 +279,9 @@ } else if (typeof props.data === 'object' && Array.isArray(props.data)) { const start = (currentPage.value - 1) * props.pageSize; const end = start + props.pageSize; - dataResult.value = { data: props.data.slice(start, end), total: props.data.length }; + const total = props.data.length; + const sorted = sortArrayData(props.data, currentSortField.value, currentSortDirection.value); + dataResult.value = { data: sorted.slice(start, end), total }; } } @@ -252,4 +293,63 @@ } } + function isColumnSortable(column: { fieldName: string; sortable?: boolean }) { + return !!props.sortable && column.sortable !== false; + } + + function isSorted(column: { fieldName: string }) { + return currentSortField.value === column.fieldName; + } + + function getAriaSort(column: { fieldName: string; sortable?: boolean }) { + if (!isColumnSortable(column)) return undefined; + if (!isSorted(column)) return 'none'; + return currentSortDirection.value === 'asc' ? 'ascending' : 'descending'; + } + + function onHeaderClick(column: { fieldName: string; sortable?: boolean }) { + if (!isColumnSortable(column)) return; + if (currentSortField.value !== column.fieldName) { + currentSortField.value = column.fieldName; + currentSortDirection.value = props.defaultSortDirection ?? 'asc'; + } else { + if (currentSortDirection.value === 'asc') { + currentSortDirection.value = 'desc'; + } else if (currentSortDirection.value === 'desc') { + currentSortField.value = undefined; + currentSortDirection.value = props.defaultSortDirection ?? 'asc'; + } else { + currentSortDirection.value = 'asc'; + } + } + } + + function getValueByPath(obj: any, path: string | undefined) { + if (!path) return undefined; + return path.split('.').reduce((acc: any, key: string) => (acc == null ? acc : acc[key]), obj); + } + + function compareValues(a: any, b: any) { + if (a == null && b == null) return 0; + if (a == null) return 1; + if (b == null) return -1; + if (typeof a === 'number' && typeof b === 'number') return a - b; + const aDate = a instanceof Date ? a : undefined; + const bDate = b instanceof Date ? b : undefined; + if (aDate && bDate) return aDate.getTime() - bDate.getTime(); + return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' }); + } + + function sortArrayData(data: { [key: string]: any }[], sortField?: string, sortDirection: 'asc' | 'desc' = 'asc') { + if (!props.sortable || !sortField) return data; + const copy = data.slice(); + copy.sort((rowA, rowB) => { + const aVal = getValueByPath(rowA, sortField); + const bVal = getValueByPath(rowB, sortField); + const cmp = compareValues(aVal, bVal); + return sortDirection === 'asc' ? cmp : -cmp; + }); + return copy; + } + \ No newline at end of file From 91ef36b678b13829b1cb5f51585c4e505bf081db Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Tue, 4 Nov 2025 14:04:05 +0200 Subject: [PATCH 02/65] fix: update sortDirection emission to handle undefined values correctly --- adminforth/spa/src/afcl/Table.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adminforth/spa/src/afcl/Table.vue b/adminforth/spa/src/afcl/Table.vue index 394bb4fc..4ea45da4 100644 --- a/adminforth/spa/src/afcl/Table.vue +++ b/adminforth/spa/src/afcl/Table.vue @@ -208,7 +208,7 @@ if (currentPage.value !== 1) currentPage.value = 1; refresh(); emit('update:sortField', currentSortField.value); - emit('update:sortDirection', currentSortField.value ? currentSortDirection.value : undefined as any); + emit('update:sortDirection', currentSortField.value ? currentSortDirection.value : undefined); emit('sort-change', { field: currentSortField.value, direction: currentSortDirection.value }); }); @@ -269,7 +269,7 @@ offset: (currentLoadingIndex - 1) * props.pageSize, limit: props.pageSize, sortField: currentSortField.value, - sortDirection: currentSortDirection.value, + ...(currentSortField.value ? { sortDirection: currentSortDirection.value } : {}), }); isAtLeastOneLoading.value[currentLoadingIndex] = false; if (isAtLeastOneLoading.value.every(v => v === false)) { From b963cde091a1278a208d55a93ca8c1413228bb86 Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Tue, 4 Nov 2025 14:13:17 +0200 Subject: [PATCH 03/65] fix: enhance sorting indicators and logic for table columns --- adminforth/spa/src/afcl/Table.vue | 101 ++++++++++++------------------ 1 file changed, 41 insertions(+), 60 deletions(-) diff --git a/adminforth/spa/src/afcl/Table.vue b/adminforth/spa/src/afcl/Table.vue index 4ea45da4..0b7f1f3e 100644 --- a/adminforth/spa/src/afcl/Table.vue +++ b/adminforth/spa/src/afcl/Table.vue @@ -19,12 +19,14 @@ {{ column.label }} - - - - - - + + + + + + + + @@ -293,63 +295,42 @@ } } - function isColumnSortable(column: { fieldName: string; sortable?: boolean }) { - return !!props.sortable && column.sortable !== false; - } +function isColumnSortable(col:{fieldName:string; sortable?:boolean}) { + return !!props.sortable && col.sortable !== false; +} - function isSorted(column: { fieldName: string }) { - return currentSortField.value === column.fieldName; +function onHeaderClick(col:{fieldName:string; sortable?:boolean}) { + if (!isColumnSortable(col)) return; + if (currentSortField.value !== col.fieldName) { + currentSortField.value = col.fieldName; + currentSortDirection.value = props.defaultSortDirection ?? 'asc'; + } else { + currentSortDirection.value = + currentSortDirection.value === 'asc' ? 'desc' : + currentSortField.value ? (currentSortField.value = undefined, props.defaultSortDirection ?? 'asc') : + 'asc'; } +} - function getAriaSort(column: { fieldName: string; sortable?: boolean }) { - if (!isColumnSortable(column)) return undefined; - if (!isSorted(column)) return 'none'; - return currentSortDirection.value === 'asc' ? 'ascending' : 'descending'; - } +function getAriaSort(col:{fieldName:string; sortable?:boolean}) { + if (!isColumnSortable(col)) return undefined; + if (currentSortField.value !== col.fieldName) return 'none'; + return currentSortDirection.value === 'asc' ? 'ascending' : 'descending'; +} - function onHeaderClick(column: { fieldName: string; sortable?: boolean }) { - if (!isColumnSortable(column)) return; - if (currentSortField.value !== column.fieldName) { - currentSortField.value = column.fieldName; - currentSortDirection.value = props.defaultSortDirection ?? 'asc'; - } else { - if (currentSortDirection.value === 'asc') { - currentSortDirection.value = 'desc'; - } else if (currentSortDirection.value === 'desc') { - currentSortField.value = undefined; - currentSortDirection.value = props.defaultSortDirection ?? 'asc'; - } else { - currentSortDirection.value = 'asc'; - } - } - } - - function getValueByPath(obj: any, path: string | undefined) { - if (!path) return undefined; - return path.split('.').reduce((acc: any, key: string) => (acc == null ? acc : acc[key]), obj); - } - - function compareValues(a: any, b: any) { - if (a == null && b == null) return 0; - if (a == null) return 1; - if (b == null) return -1; - if (typeof a === 'number' && typeof b === 'number') return a - b; - const aDate = a instanceof Date ? a : undefined; - const bDate = b instanceof Date ? b : undefined; - if (aDate && bDate) return aDate.getTime() - bDate.getTime(); - return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' }); - } - - function sortArrayData(data: { [key: string]: any }[], sortField?: string, sortDirection: 'asc' | 'desc' = 'asc') { - if (!props.sortable || !sortField) return data; - const copy = data.slice(); - copy.sort((rowA, rowB) => { - const aVal = getValueByPath(rowA, sortField); - const bVal = getValueByPath(rowB, sortField); - const cmp = compareValues(aVal, bVal); - return sortDirection === 'asc' ? cmp : -cmp; - }); - return copy; - } +const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); +function sortArrayData(data:any[], sortField?:string, dir:'asc'|'desc'='asc') { + if (!props.sortable || !sortField) return data; + const get = (o:any, p:string) => p.split('.').reduce((a:any,k)=>a?.[k], o); + return [...data].sort((a,b) => { + let av = get(a, sortField), bv = get(b, sortField); + if (av == null && bv == null) return 0; + if (av == null) return 1; if (bv == null) return -1; + if (av instanceof Date && bv instanceof Date) return dir === 'asc' ? av.getTime() - bv.getTime() : bv.getTime() - av.getTime(); + if (typeof av === 'number' && typeof bv === 'number') return dir === 'asc' ? av - bv : bv - av; + const cmp = collator.compare(String(av), String(bv)); + return dir === 'asc' ? cmp : -cmp; + }); +} \ No newline at end of file From 3a66c2c5b4e4c5497b40f2cb48108b54c19ac23a Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Tue, 4 Nov 2025 16:23:24 +0200 Subject: [PATCH 04/65] feat: update table component to support configurable sortable columns and enhance sorting logic --- .../docs/tutorial/03-Customization/15-afcl.md | 104 ++++++++++++++++++ adminforth/spa/src/afcl/Table.vue | 15 ++- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md b/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md index e2b77bcb..40e58ef0 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md @@ -22,6 +22,57 @@ import { Button } from '@/afcl' ``` ```html + +### Sorting + +Table supports column sorting out of the box. + +- Sorting is enabled globally by default. You can disable it entirely with `:sortable="false"`. +- Per column, sorting can be disabled with `sortable: false` inside the column definition. +- Clicking a sortable header cycles sorting in a tri‑state order: + - none β†’ ascending β†’ descending β†’ none + - When it returns to "none", the sorting is cleared. + +Basic example (client-side sorting when `data` is an array): + +```html +
- - + + {{ column.label }} + + + + + + + +
+``` + +You can also predefine a default sort: + +```html +
+``` + +Notes: +- Client-side sorting supports nested field paths using dot-notation, e.g. `user.name`. +- When a column is not currently sorted, a subtle double-arrow icon is shown; arrows switch up/down for ascending/descending. +
+``` + +Events you can listen to: + +```html +
+``` + ### Table loading states For tables where you load data externally and pass them to `data` prop as array (including case with front-end pagination) you might want to show skeleton loaders in table externaly using `isLoading` props. diff --git a/adminforth/spa/src/afcl/Table.vue b/adminforth/spa/src/afcl/Table.vue index 0b7f1f3e..8d0f0654 100644 --- a/adminforth/spa/src/afcl/Table.vue +++ b/adminforth/spa/src/afcl/Table.vue @@ -172,7 +172,7 @@ }>(), { evenHighlights: true, pageSize: 5, - sortable: true, + sortable: false, } ); @@ -206,7 +206,7 @@ }); watch([() => currentSortField.value, () => currentSortDirection.value], () => { - // reset to first page on sort change + if (!props.sortable) return; if (currentPage.value !== 1) currentPage.value = 1; refresh(); emit('update:sortField', currentSortField.value); @@ -296,7 +296,7 @@ } function isColumnSortable(col:{fieldName:string; sortable?:boolean}) { - return !!props.sortable && col.sortable !== false; + return props.sortable === true && col.sortable === true; } function onHeaderClick(col:{fieldName:string; sortable?:boolean}) { @@ -322,12 +322,17 @@ const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'bas function sortArrayData(data:any[], sortField?:string, dir:'asc'|'desc'='asc') { if (!props.sortable || !sortField) return data; - const get = (o:any, p:string) => p.split('.').reduce((a:any,k)=>a?.[k], o); + // Helper function to get nested properties by path + const getByPath = (o:any, p:string) => p.split('.').reduce((a:any,k)=>a?.[k], o); return [...data].sort((a,b) => { - let av = get(a, sortField), bv = get(b, sortField); + let av = getByPath(a, sortField), bv = getByPath(b, sortField); + // Handle null/undefined values if (av == null && bv == null) return 0; + // Handle null/undefined values if (av == null) return 1; if (bv == null) return -1; + // Data types if (av instanceof Date && bv instanceof Date) return dir === 'asc' ? av.getTime() - bv.getTime() : bv.getTime() - av.getTime(); + // Strings and numbers if (typeof av === 'number' && typeof bv === 'number') return dir === 'asc' ? av - bv : bv - av; const cmp = collator.compare(String(av), String(bv)); return dir === 'asc' ? cmp : -cmp; From 05bf682f4f7108e5908f600219a44a4c396bf5ed Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Wed, 5 Nov 2025 12:42:41 +0200 Subject: [PATCH 05/65] chore: update dependencies and devDependencies in package.json - Bumped versions for several dependencies including: - @iconify-prerendered/vue-flag from ^0.28.1748584105 to ^0.28.1754899047 - @unhead/vue from ^1.9.12 to ^1.11.20 - @vueuse/core from ^10.10.0 to ^10.11.1 - dayjs from ^1.11.11 to ^1.11.19 - debounce from ^2.1.0 to ^2.2.0 - flowbite-datepicker from ^1.2.6 to ^1.3.2 - javascript-time-ago from ^2.5.11 to ^2.5.12 - pinia from ^2.1.7 to ^2.3.1 - sanitize-html from ^2.13.0 to ^2.17.0 - unhead from ^1.9.12 to ^1.11.20 - vue from ^3.5.12 to ^3.5.22 - vue-i18n from ^10.0.5 to ^10.0.8 - vue-router from ^4.3.0 to ^4.6.3 - @rushstack/eslint-patch from ^1.8.0 to ^1.14.1 - @tsconfig/node20 from ^20.1.4 to ^20.1.6 - @types/node from ^20.12.5 to ^20.19.24 - @vitejs/plugin-vue from ^5.0.4 to ^5.2.4 - @vue/tsconfig from ^0.5.1 to ^0.8.1 - autoprefixer from ^10.4.19 to ^10.4.21 - eslint from ^8.57.0 to ^8.57.1 - eslint-plugin-vue from ^9.23.0 to ^9.33.0 - flag-icons from ^7.2.3 to ^7.5.0 - i18n-iso-countries from ^7.12.0 to ^7.14.0 - npm-run-all2 from ^6.1.2 to ^6.2.6 - portfinder from ^1.0.32 to ^1.0.38 - postcss from ^8.4.38 to ^8.5.6 - sass from ^1.77.2 to ^1.93.3 - tailwindcss from ^3.4.17 to ^3.4.18 - typescript from ~5.4.0 to ~5.9.3 - vite from ^5.2.13 to ^5.4.21 - vue-tsc from ^2.0.11 to ^2.2.12 - vue3-json-viewer from ^2.2.2 to ^2.4.1 --- adminforth/spa/package-lock.json | 1907 +++++++++++++++++++----------- adminforth/spa/package.json | 64 +- 2 files changed, 1235 insertions(+), 736 deletions(-) diff --git a/adminforth/spa/package-lock.json b/adminforth/spa/package-lock.json index 1cfd3233..a626ebb2 100644 --- a/adminforth/spa/package-lock.json +++ b/adminforth/spa/package-lock.json @@ -8,48 +8,48 @@ "name": "spa", "version": "0.0.0", "dependencies": { - "@iconify-prerendered/vue-flag": "^0.28.1748584105", + "@iconify-prerendered/vue-flag": "^0.28.1754899047", "@iconify-prerendered/vue-flowbite": "^0.28.1754899090", - "@unhead/vue": "^1.9.12", - "@vueuse/core": "^10.10.0", + "@unhead/vue": "^1.11.20", + "@vueuse/core": "^10.11.1", "apexcharts": "^4.7.0", - "dayjs": "^1.11.11", - "debounce": "^2.1.0", - "flowbite-datepicker": "^1.2.6", - "javascript-time-ago": "^2.5.11", - "pinia": "^2.1.7", - "sanitize-html": "^2.13.0", - "unhead": "^1.9.12", + "dayjs": "^1.11.19", + "debounce": "^2.2.0", + "flowbite-datepicker": "^1.3.2", + "javascript-time-ago": "^2.5.12", + "pinia": "^2.3.1", + "sanitize-html": "^2.17.0", + "unhead": "^1.11.20", "uuid": "^10.0.0", - "vue": "^3.5.12", + "vue": "^3.5.22", "vue-diff": "^1.2.4", - "vue-i18n": "^10.0.5", - "vue-router": "^4.3.0", + "vue-i18n": "^10.0.8", + "vue-router": "^4.6.3", "vue-slider-component": "^4.1.0-beta.7" }, "devDependencies": { - "@rushstack/eslint-patch": "^1.8.0", - "@tsconfig/node20": "^20.1.4", - "@types/node": "^20.12.5", - "@vitejs/plugin-vue": "^5.0.4", + "@rushstack/eslint-patch": "^1.14.1", + "@tsconfig/node20": "^20.1.6", + "@types/node": "^20.19.24", + "@vitejs/plugin-vue": "^5.2.4", "@vue/eslint-config-typescript": "^13.0.0", - "@vue/tsconfig": "^0.5.1", - "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", - "eslint-plugin-vue": "^9.23.0", - "flag-icons": "^7.2.3", + "@vue/tsconfig": "^0.8.1", + "autoprefixer": "^10.4.21", + "eslint": "^8.57.1", + "eslint-plugin-vue": "^9.33.0", + "flag-icons": "^7.5.0", "flowbite": "^3.1.2", - "i18n-iso-countries": "^7.12.0", - "npm-run-all2": "^6.1.2", - "portfinder": "^1.0.32", - "postcss": "^8.4.38", - "sass": "^1.77.2", - "tailwindcss": "^3.4.17", - "typescript": "~5.4.0", - "vite": "^5.2.13", + "i18n-iso-countries": "^7.14.0", + "npm-run-all2": "^6.2.6", + "portfinder": "^1.0.38", + "postcss": "^8.5.6", + "sass": "^1.93.3", + "tailwindcss": "^3.4.18", + "typescript": "~5.9.3", + "vite": "^5.4.21", "vue-i18n-extract": "^2.0.7", - "vue-tsc": "^2.0.11", - "vue3-json-viewer": "^2.2.2" + "vue-tsc": "^2.2.12", + "vue3-json-viewer": "^2.4.1" } }, "node_modules/@alloc/quick-lru": { @@ -65,30 +65,30 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -98,26 +98,27 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -127,13 +128,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -143,13 +145,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -159,13 +162,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -175,13 +179,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -191,13 +196,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -207,13 +213,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -223,13 +230,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -239,13 +247,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -255,13 +264,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -271,13 +281,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -287,13 +298,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -303,13 +315,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -319,13 +332,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -335,13 +349,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -351,13 +366,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -367,13 +383,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -383,13 +400,14 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -399,13 +417,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -415,13 +434,14 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -431,13 +451,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -447,13 +468,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -463,13 +485,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -548,21 +571,24 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -571,10 +597,11 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -585,6 +612,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -609,12 +637,14 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@iconify-prerendered/vue-flag": { - "version": "0.28.1748584105", - "resolved": "https://registry.npmjs.org/@iconify-prerendered/vue-flag/-/vue-flag-0.28.1748584105.tgz", - "integrity": "sha512-7wJ9UsXJ1h10S4Y/qZsnK3pFkwh6R0542PTv8SykD+80M9i9Iw7xUI9H65Qbcvmk0oNSuHTb/aoFXxqMpuEfnA==", + "version": "0.28.1754899047", + "resolved": "https://registry.npmjs.org/@iconify-prerendered/vue-flag/-/vue-flag-0.28.1754899047.tgz", + "integrity": "sha512-pJ98pR7laS3UxJ+BZt4FhpkxYGkeFAT8B5ERC1bkiI1Jr0ws2DwjMx7dAnae7h9lRKdyNcKfhATZwQuNBheryQ==", "license": "MIT", "funding": { "url": "https://www.buymeacoffee.com/kozack/" @@ -636,13 +666,13 @@ } }, "node_modules/@intlify/core-base": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.5.tgz", - "integrity": "sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz", + "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==", "license": "MIT", "dependencies": { - "@intlify/message-compiler": "10.0.5", - "@intlify/shared": "10.0.5" + "@intlify/message-compiler": "10.0.8", + "@intlify/shared": "10.0.8" }, "engines": { "node": ">= 16" @@ -652,12 +682,12 @@ } }, "node_modules/@intlify/message-compiler": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.5.tgz", - "integrity": "sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz", + "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==", "license": "MIT", "dependencies": { - "@intlify/shared": "10.0.5", + "@intlify/shared": "10.0.8", "source-map-js": "^1.0.2" }, "engines": { @@ -668,9 +698,9 @@ } }, "node_modules/@intlify/shared": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.5.tgz", - "integrity": "sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz", + "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==", "license": "MIT", "engines": { "node": ">= 16" @@ -756,9 +786,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -806,6 +836,316 @@ "node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -884,218 +1224,319 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", - "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz", - "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", - "dev": true + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.14.1.tgz", + "integrity": "sha512-jGTk8UD/RdjsNZW8qq10r0RBvxL8OWtoT+kImlzPDFilmozzM+9QmIJsmze9UiSBrFU45ZxhTYBypn9q9z/VfQ==", + "dev": true, + "license": "MIT" }, "node_modules/@svgdotjs/svg.draggable.js": { "version": "3.0.6", @@ -1154,23 +1595,26 @@ } }, "node_modules/@tsconfig/node20": { - "version": "20.1.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", - "integrity": "sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==", - "dev": true + "version": "20.1.6", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.6.tgz", + "integrity": "sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", - "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "version": "20.19.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", + "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/@types/resolve": { @@ -1376,21 +1820,23 @@ "dev": true }, "node_modules/@unhead/dom": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.9.12.tgz", - "integrity": "sha512-3MY1TbZmEjGNZapi3wvJW0vWNS2CLKHt7/m57sScDHCNvNBe1mTwrIOhtZFDgAndhml2EVQ68RMa0Vhum/M+cw==", + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.11.20.tgz", + "integrity": "sha512-jgfGYdOH+xHJF/j8gudjsYu3oIjFyXhCWcgKaw3vQnT616gSqyqnGQGOItL+BQtQZACKNISwIfx5PuOtztMKLA==", + "license": "MIT", "dependencies": { - "@unhead/schema": "1.9.12", - "@unhead/shared": "1.9.12" + "@unhead/schema": "1.11.20", + "@unhead/shared": "1.11.20" }, "funding": { "url": "https://github.com/sponsors/harlan-zw" } }, "node_modules/@unhead/schema": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.9.12.tgz", - "integrity": "sha512-ue2FKyIZKsuZDpWJBMlBGwMm4s+vFeU3NUWsNt8Z+2JkOUIqO/VG43LxNgY1M595bOS71Gdxk+G9VtzfKJ5uEA==", + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.11.20.tgz", + "integrity": "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA==", + "license": "MIT", "dependencies": { "hookable": "^5.5.3", "zhead": "^2.2.4" @@ -1400,25 +1846,28 @@ } }, "node_modules/@unhead/shared": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.9.12.tgz", - "integrity": "sha512-72wlLXG3FP3sXUrwd42Uv8jYpHSg4R6IFJcsl+QisRjKM89JnjOFSw1DqWO4IOftW5xOxS4J5v7SQyJ4NJo7Bw==", + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.11.20.tgz", + "integrity": "sha512-1MOrBkGgkUXS+sOKz/DBh4U20DNoITlJwpmvSInxEUNhghSNb56S0RnaHRq0iHkhrO/cDgz2zvfdlRpoPLGI3w==", + "license": "MIT", "dependencies": { - "@unhead/schema": "1.9.12" + "@unhead/schema": "1.11.20", + "packrup": "^0.1.2" }, "funding": { "url": "https://github.com/sponsors/harlan-zw" } }, "node_modules/@unhead/vue": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.9.12.tgz", - "integrity": "sha512-keE4EuswgzCqVU7zmZprU+ToMvNWc3s8NoLreH5AtJd2u0FgBygD8sxRVyEnZw1KwFYOJ2C7yD2TChSKZioPGQ==", + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.11.20.tgz", + "integrity": "sha512-sqQaLbwqY9TvLEGeq8Fd7+F2TIuV3nZ5ihVISHjWpAM3y7DwNWRU7NmT9+yYT+2/jw1Vjwdkv5/HvDnvCLrgmg==", + "license": "MIT", "dependencies": { - "@unhead/schema": "1.9.12", - "@unhead/shared": "1.9.12", + "@unhead/schema": "1.11.20", + "@unhead/shared": "1.11.20", "hookable": "^5.5.3", - "unhead": "1.9.12" + "unhead": "1.11.20" }, "funding": { "url": "https://github.com/sponsors/harlan-zw" @@ -1428,100 +1877,114 @@ } }, "node_modules/@vitejs/plugin-vue": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", - "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "vite": "^5.0.0", + "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "node_modules/@volar/language-core": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.2.4.tgz", - "integrity": "sha512-7As47GndxGxsqqYnbreLrfB5NDUeQioPM2LJKUuB4/34c0NpEJ2byVl3c9KYdjIdiEstWZ9JLtLKNTaPWb5jtA==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", "dev": true, + "license": "MIT", "dependencies": { - "@volar/source-map": "2.2.4" + "@volar/source-map": "2.4.15" } }, "node_modules/@volar/source-map": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.2.4.tgz", - "integrity": "sha512-m92FLpR9vB1YEZfiZ+bfgpLrToL/DNkOrorWVep3pffHrwwI4Tx2oIQN+sqHJfKkiT5N3J1owC+8crhAEinfjg==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", "dev": true, - "dependencies": { - "muggle-string": "^0.4.0" - } + "license": "MIT" }, "node_modules/@volar/typescript": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.2.4.tgz", - "integrity": "sha512-uAQC53tgEbHO62G8NXMfmBrJAlP2QJ9WxVEEQqqK3I6VSy8frL5LbH3hAWODxiwMWixv74wJLWlKbWXOgdIoRQ==", + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz", + "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", "dev": true, + "license": "MIT", "dependencies": { - "@volar/language-core": "2.2.4", - "path-browserify": "^1.0.1" + "@volar/language-core": "2.4.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" } }, "node_modules/@vue/compiler-core": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", - "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", + "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.12", + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" + "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", - "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", + "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", - "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", + "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.12", - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12", + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", "estree-walker": "^2.0.2", - "magic-string": "^0.30.11", - "postcss": "^8.4.47", - "source-map-js": "^1.2.0" + "magic-string": "^0.30.19", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", - "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", + "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" } }, "node_modules/@vue/devtools-api": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz", - "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==" + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" }, "node_modules/@vue/eslint-config-typescript": { "version": "13.0.0", @@ -1548,18 +2011,20 @@ } }, "node_modules/@vue/language-core": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.19.tgz", - "integrity": "sha512-A9EGOnvb51jOvnCYoRLnMP+CcoPlbZVxI9gZXE/y2GksRWM6j/PrLEIC++pnosWTN08tFpJgxhSS//E9v/Sg+Q==", + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", "dev": true, + "license": "MIT", "dependencies": { - "@volar/language-core": "~2.2.4", - "@vue/compiler-dom": "^3.4.0", - "@vue/shared": "^3.4.0", - "computeds": "^0.0.1", + "@volar/language-core": "2.4.15", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", "minimatch": "^9.0.3", - "path-browserify": "^1.0.1", - "vue-template-compiler": "^2.7.14" + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" @@ -1571,142 +2036,108 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.12.tgz", - "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.22.tgz", + "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.12" + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.12.tgz", - "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.22.tgz", + "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/reactivity": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", - "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", + "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/runtime-core": "3.5.12", - "@vue/shared": "3.5.12", + "@vue/reactivity": "3.5.22", + "@vue/runtime-core": "3.5.22", + "@vue/shared": "3.5.22", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.12.tgz", - "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.22.tgz", + "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { - "vue": "3.5.12" + "vue": "3.5.22" } }, "node_modules/@vue/shared": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", - "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", + "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", "license": "MIT" }, "node_modules/@vue/tsconfig": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz", - "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", - "dev": true + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.8.1.tgz", + "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } }, "node_modules/@vueuse/core": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.10.0.tgz", - "integrity": "sha512-vexJ/YXYs2S42B783rI95lMt3GzEwkxzC8Hb0Ndpd8rD+p+Lk/Za4bd797Ym7yq4jXqdSyj3JLChunF/vyYjUw==", + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz", + "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==", + "license": "MIT", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.10.0", - "@vueuse/shared": "10.10.0", - "vue-demi": ">=0.14.7" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.14.8", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", - "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" + "@vueuse/metadata": "10.11.1", + "@vueuse/shared": "10.11.1", + "vue-demi": ">=0.14.8" }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/metadata": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.10.0.tgz", - "integrity": "sha512-UNAo2sTCAW5ge6OErPEHb5z7NEAg3XcO9Cj7OK45aZXfLLH1QkexDcZD77HBi5zvEiLOm1An+p/4b5K3Worpug==", + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz", + "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.10.0.tgz", - "integrity": "sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==", + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz", + "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==", + "license": "MIT", "dependencies": { - "vue-demi": ">=0.14.7" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/shared/node_modules/vue-demi": { - "version": "0.14.8", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", - "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" + "vue-demi": ">=0.14.8" }, "funding": { "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } } }, "node_modules/@yr/monotone-cubic-spline": { @@ -1752,6 +2183,13 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1831,19 +2269,16 @@ } }, "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.14" - } + "license": "MIT" }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "dev": true, "funding": [ { @@ -1859,12 +2294,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -1883,6 +2319,16 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.24.tgz", + "integrity": "sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1924,9 +2370,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", "dev": true, "funding": [ { @@ -1942,11 +2388,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" @@ -1984,9 +2432,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001715", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", - "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", + "version": "1.0.30001753", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", + "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", "dev": true, "funding": [ { @@ -2095,12 +2543,6 @@ "node": ">= 6" } }, - "node_modules/computeds": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", - "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2140,20 +2582,23 @@ "license": "MIT" }, "node_modules/dayjs": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", - "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/debounce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.0.tgz", - "integrity": "sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", + "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -2162,12 +2607,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2199,6 +2645,20 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/diacritics": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", @@ -2329,10 +2789,11 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.774", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz", - "integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==", - "dev": true + "version": "1.5.245", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.245.tgz", + "integrity": "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -2352,11 +2813,12 @@ } }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2364,36 +2826,37 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -2410,16 +2873,18 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -2465,18 +2930,19 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.26.0.tgz", - "integrity": "sha512-eTvlxXgd4ijE1cdur850G6KalZqk65k1JKoOI2d1kT3hr8sPD07j1q98FRFdNnpxBELGPWxZmInxeHGF/GxtqQ==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", + "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "globals": "^13.24.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", - "semver": "^7.6.0", - "vue-eslint-parser": "^9.4.2", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", "xml-name-validator": "^4.0.0" }, "engines": { @@ -2698,9 +3164,9 @@ } }, "node_modules/flag-icons": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-7.2.3.tgz", - "integrity": "sha512-X2gUdteNuqdNqob2KKTJTS+ZCvyWeLCtDz9Ty8uJP17Y4o82Y+U/Vd4JNrdwTAjagYsRznOn9DZ+E/Q52qbmqg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-7.5.0.tgz", + "integrity": "sha512-kd+MNXviFIg5hijH766tt+3x76ele1AXlo4zDdCxIvqWZhKt4T83bOtxUOOMlTx/EcFdUMH5yvQgYlFh1EqqFg==", "dev": true, "license": "MIT" }, @@ -2945,6 +3411,7 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } @@ -2960,7 +3427,8 @@ "node_modules/hookable": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" }, "node_modules/htmlparser2": { "version": "8.0.2", @@ -2981,9 +3449,9 @@ } }, "node_modules/i18n-iso-countries": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.12.0.tgz", - "integrity": "sha512-NDFf5j/raA5JrcPT/NcHP3RUMH7TkdkxQKAKdvDlgb+MS296WJzzqvV0Y5uwavSm7A6oYvBeSV0AxoHdDiHIiw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.14.0.tgz", + "integrity": "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==", "dev": true, "license": "MIT", "dependencies": { @@ -3003,10 +3471,11 @@ } }, "node_modules/immutable": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", - "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", - "dev": true + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.0", @@ -3170,12 +3639,12 @@ } }, "node_modules/javascript-time-ago": { - "version": "2.5.11", - "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.11.tgz", - "integrity": "sha512-Zeyf5R7oM1fSMW9zsU3YgAYwE0bimEeF54Udn2ixGd8PUwu+z1Yc5t4Y8YScJDMHD6uCx6giLt3VJR5K4CMwbg==", + "version": "2.5.12", + "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.12.tgz", + "integrity": "sha512-s8PPq2HQ3HIbSU0SjhNvTitf5VoXbQWof9q6k3gIX7F2il0ptjD5lONTDccpuKt/2U7RjbCp/TCHPK7eDwO7zQ==", "license": "MIT", "dependencies": { - "relative-time-format": "^1.1.6" + "relative-time-format": "^1.1.7" } }, "node_modules/jiti": { @@ -3305,12 +3774,12 @@ } }, "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/memorystream": { @@ -3368,16 +3837,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", @@ -3387,30 +3846,19 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/muggle-string": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mz": { "version": "2.7.0", @@ -3447,11 +3895,20 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -3481,10 +3938,11 @@ } }, "node_modules/npm-run-all2": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.0.tgz", - "integrity": "sha512-wA7yVIkthe6qJBfiJ2g6aweaaRlw72itsFGF6HuwCHKwtwAx/4BY1vVpk6bw6lS8RLMsexoasOkd0aYOmsFG7Q==", + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.6.tgz", + "integrity": "sha512-tkyb4pc0Zb0oOswCb5tORPk9MvVL6gcDq1cMItQHmsbVk1skk7YF6cH+UU2GxeNLHMuk6wFEOSmEmJ2cnAK1jg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.3", @@ -3492,7 +3950,8 @@ "minimatch": "^9.0.0", "pidtree": "^0.6.0", "read-package-json-fast": "^3.0.2", - "shell-quote": "^1.7.3" + "shell-quote": "^1.7.3", + "which": "^3.0.1" }, "bin": { "npm-run-all": "bin/npm-run-all/index.js", @@ -3501,7 +3960,7 @@ "run-s": "bin/run-s/index.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0", + "node": "^14.18.0 || ^16.13.0 || >=18.0.0", "npm": ">= 8" } }, @@ -3517,6 +3976,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -3603,6 +4078,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/packrup": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/packrup/-/packrup-0.1.2.tgz", + "integrity": "sha512-ZcKU7zrr5GlonoS9cxxrb5HVswGnyj6jQvwFBa6p5VFw7G71VAHcUKL5wyZSU/ECtPM/9gacWxy2KFQKt1gMNA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3624,7 +4108,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", @@ -3723,55 +4208,27 @@ } }, "node_modules/pinia": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", - "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.5.0", - "vue-demi": ">=0.14.5" + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" }, "funding": { "url": "https://github.com/sponsors/posva" }, "peerDependencies": { - "@vue/composition-api": "^1.4.0", "typescript": ">=4.4.4", - "vue": "^2.6.14 || ^3.3.0" + "vue": "^2.7.0 || ^3.5.11" }, "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - }, "typescript": { "optional": true } } }, - "node_modules/pinia/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -3782,34 +4239,23 @@ } }, "node_modules/portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", + "integrity": "sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==", "dev": true, "license": "MIT", "dependencies": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" + "async": "^3.2.6", + "debug": "^4.3.6" }, "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" + "node": ">= 10.12" } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -3826,7 +4272,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4024,9 +4470,9 @@ } }, "node_modules/relative-time-format": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.6.tgz", - "integrity": "sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/relative-time-format/-/relative-time-format-1.1.11.tgz", + "integrity": "sha512-TH+oV/w77hjaB9xCzoFYJ/Icmr/12+02IAoCI/YGS2UBTbjCbBjHGEBxGnVy4EJvOR1qadGzyFRI6hGaJJG93Q==", "license": "MIT" }, "node_modules/resolve": { @@ -4080,12 +4526,13 @@ } }, "node_modules/rollup": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", "devOptional": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -4095,22 +4542,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.17.2", - "@rollup/rollup-android-arm64": "4.17.2", - "@rollup/rollup-darwin-arm64": "4.17.2", - "@rollup/rollup-darwin-x64": "4.17.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", - "@rollup/rollup-linux-arm64-gnu": "4.17.2", - "@rollup/rollup-linux-arm64-musl": "4.17.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", - "@rollup/rollup-linux-s390x-gnu": "4.17.2", - "@rollup/rollup-linux-x64-gnu": "4.17.2", - "@rollup/rollup-linux-x64-musl": "4.17.2", - "@rollup/rollup-win32-arm64-msvc": "4.17.2", - "@rollup/rollup-win32-ia32-msvc": "4.17.2", - "@rollup/rollup-win32-x64-msvc": "4.17.2", + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" } }, @@ -4138,9 +4591,10 @@ } }, "node_modules/sanitize-html": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz", - "integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", + "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", + "license": "MIT", "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", @@ -4151,13 +4605,14 @@ } }, "node_modules/sass": { - "version": "1.77.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz", - "integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==", + "version": "1.93.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.3.tgz", + "integrity": "sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg==", "dev": true, + "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -4165,6 +4620,39 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/select": { @@ -4175,10 +4663,11 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4416,9 +4905,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", + "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4430,7 +4919,7 @@ "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.6", + "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", @@ -4439,7 +4928,7 @@ "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", @@ -4543,10 +5032,11 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4556,19 +5046,21 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" }, "node_modules/unhead": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/unhead/-/unhead-1.9.12.tgz", - "integrity": "sha512-s6VxcTV45hy8c/IioKQOonFnAO+kBOSpgDfqEHhnU0YVSQYaRPEp9pzW1qSPf0lx+bg9RKeOQyNNbSGGUP26aQ==", + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/unhead/-/unhead-1.11.20.tgz", + "integrity": "sha512-3AsNQC0pjwlLqEYHLjtichGWankK8yqmocReITecmpB1H0aOabeESueyy+8X1gyJx4ftZVwo9hqQ4O3fPWffCA==", + "license": "MIT", "dependencies": { - "@unhead/dom": "1.9.12", - "@unhead/schema": "1.9.12", - "@unhead/shared": "1.9.12", + "@unhead/dom": "1.11.20", + "@unhead/schema": "1.11.20", + "@unhead/shared": "1.11.20", "hookable": "^5.5.3" }, "funding": { @@ -4576,9 +5068,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "dev": true, "funding": [ { @@ -4594,9 +5086,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -4633,14 +5126,15 @@ } }, "node_modules/vite": { - "version": "5.2.13", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", - "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -4659,6 +5153,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -4676,6 +5171,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -4687,17 +5185,24 @@ } } }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, "node_modules/vue": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.12.tgz", - "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", + "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-sfc": "3.5.12", - "@vue/runtime-dom": "3.5.12", - "@vue/server-renderer": "3.5.12", - "@vue/shared": "3.5.12" + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-sfc": "3.5.22", + "@vue/runtime-dom": "3.5.22", + "@vue/server-renderer": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { "typescript": "*" @@ -4708,6 +5213,32 @@ } } }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/vue-diff": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/vue-diff/-/vue-diff-1.2.4.tgz", @@ -4780,36 +5311,12 @@ } } }, - "node_modules/vue-diff/node_modules/vue-demi": { - "version": "0.14.8", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", - "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, "node_modules/vue-eslint-parser": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", - "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4", "eslint-scope": "^7.1.1", @@ -4830,13 +5337,13 @@ } }, "node_modules/vue-i18n": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.5.tgz", - "integrity": "sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.8.tgz", + "integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==", "license": "MIT", "dependencies": { - "@intlify/core-base": "10.0.5", - "@intlify/shared": "10.0.5", + "@intlify/core-base": "10.0.8", + "@intlify/shared": "10.0.8", "@vue/devtools-api": "^6.5.0" }, "engines": { @@ -4901,17 +5408,18 @@ } }, "node_modules/vue-router": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.2.tgz", - "integrity": "sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.5.1" + "@vue/devtools-api": "^6.6.4" }, "funding": { "url": "https://github.com/sponsors/posva" }, "peerDependencies": { - "vue": "^3.2.0" + "vue": "^3.5.0" } }, "node_modules/vue-slider-component": { @@ -4919,44 +5427,34 @@ "resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-4.1.0-beta.7.tgz", "integrity": "sha512-Qb7K920ZG7PoQswoF6Ias+i3W2rd3k4fpk04JUl82kEUcN86Yg6et7bVSKWt/7VpQe8a5IT3BqCKSCOZ7AJgCA==" }, - "node_modules/vue-template-compiler": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", - "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", - "dev": true, - "dependencies": { - "de-indent": "^1.0.2", - "he": "^1.2.0" - } - }, "node_modules/vue-tsc": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.19.tgz", - "integrity": "sha512-JWay5Zt2/871iodGF72cELIbcAoPyhJxq56mPPh+M2K7IwI688FMrFKc/+DvB05wDWEuCPexQJ6L10zSwzzapg==", + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", "dev": true, + "license": "MIT", "dependencies": { - "@volar/typescript": "~2.2.4", - "@vue/language-core": "2.0.19", - "semver": "^7.5.4" + "@volar/typescript": "2.4.15", + "@vue/language-core": "2.2.12" }, "bin": { "vue-tsc": "bin/vue-tsc.js" }, "peerDependencies": { - "typescript": "*" + "typescript": ">=5.0.0" } }, "node_modules/vue3-json-viewer": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/vue3-json-viewer/-/vue3-json-viewer-2.2.2.tgz", - "integrity": "sha512-56l3XDGggnpwEqZieXsSMhNT4NhtO6d7zuSAxHo4i0UVxymyY2jRb7UMQOU1ztChKALZCAzX7DlgrsnEhxu77A==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/vue3-json-viewer/-/vue3-json-viewer-2.4.1.tgz", + "integrity": "sha512-Z1sunvS58lJ3ZcpNhl3jYQapBVw2wjnXbemigfMWm3QnjCeg3CPMq8R6pxHUYahxMfPKLvrbGve6mUXqhWyLaQ==", "dev": true, "license": "ISC", "dependencies": { "clipboard": "^2.0.10" }, "peerDependencies": { - "vue": "^3.2.0" + "vue": "^3.5.16" } }, "node_modules/which": { @@ -5120,6 +5618,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz", "integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/harlan-zw" } diff --git a/adminforth/spa/package.json b/adminforth/spa/package.json index 231060c1..a87c6c15 100644 --- a/adminforth/spa/package.json +++ b/adminforth/spa/package.json @@ -13,47 +13,47 @@ "i18n:extract": "echo {} > i18n-empty.json && vue-i18n-extract report --vueFiles \"./src/**/*.{js,vue,ts}\" --output ./i18n-messages.json --languageFiles \"i18n-empty.json\" --add" }, "dependencies": { - "@iconify-prerendered/vue-flag": "^0.28.1748584105", + "@iconify-prerendered/vue-flag": "^0.28.1754899047", "@iconify-prerendered/vue-flowbite": "^0.28.1754899090", - "@unhead/vue": "^1.9.12", - "@vueuse/core": "^10.10.0", + "@unhead/vue": "^1.11.20", + "@vueuse/core": "^10.11.1", "apexcharts": "^4.7.0", - "dayjs": "^1.11.11", - "debounce": "^2.1.0", - "flowbite-datepicker": "^1.2.6", - "javascript-time-ago": "^2.5.11", - "pinia": "^2.1.7", - "sanitize-html": "^2.13.0", - "unhead": "^1.9.12", + "dayjs": "^1.11.19", + "debounce": "^2.2.0", + "flowbite-datepicker": "^1.3.2", + "javascript-time-ago": "^2.5.12", + "pinia": "^2.3.1", + "sanitize-html": "^2.17.0", + "unhead": "^1.11.20", "uuid": "^10.0.0", - "vue": "^3.5.12", + "vue": "^3.5.22", "vue-diff": "^1.2.4", - "vue-i18n": "^10.0.5", - "vue-router": "^4.3.0", + "vue-i18n": "^10.0.8", + "vue-router": "^4.6.3", "vue-slider-component": "^4.1.0-beta.7" }, "devDependencies": { - "@rushstack/eslint-patch": "^1.8.0", - "@tsconfig/node20": "^20.1.4", - "@types/node": "^20.12.5", - "@vitejs/plugin-vue": "^5.0.4", + "@rushstack/eslint-patch": "^1.14.1", + "@tsconfig/node20": "^20.1.6", + "@types/node": "^20.19.24", + "@vitejs/plugin-vue": "^5.2.4", "@vue/eslint-config-typescript": "^13.0.0", - "@vue/tsconfig": "^0.5.1", - "autoprefixer": "^10.4.19", - "eslint": "^8.57.0", - "eslint-plugin-vue": "^9.23.0", - "flag-icons": "^7.2.3", + "@vue/tsconfig": "^0.8.1", + "autoprefixer": "^10.4.21", + "eslint": "^8.57.1", + "eslint-plugin-vue": "^9.33.0", + "flag-icons": "^7.5.0", "flowbite": "^3.1.2", - "i18n-iso-countries": "^7.12.0", - "npm-run-all2": "^6.1.2", - "portfinder": "^1.0.32", - "postcss": "^8.4.38", - "sass": "^1.77.2", - "tailwindcss": "^3.4.17", - "typescript": "~5.4.0", - "vite": "^5.2.13", + "i18n-iso-countries": "^7.14.0", + "npm-run-all2": "^6.2.6", + "portfinder": "^1.0.38", + "postcss": "^8.5.6", + "sass": "^1.93.3", + "tailwindcss": "^3.4.18", + "typescript": "~5.9.3", + "vite": "^5.4.21", "vue-i18n-extract": "^2.0.7", - "vue-tsc": "^2.0.11", - "vue3-json-viewer": "^2.2.2" + "vue-tsc": "^2.2.12", + "vue3-json-viewer": "^2.4.1" } } From bdefd99d9980a7c2167e12736ec3333671da7d34 Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Wed, 5 Nov 2025 12:47:03 +0200 Subject: [PATCH 06/65] fix: correct import path for vue3-json-viewer CSS --- adminforth/spa/src/components/ValueRenderer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminforth/spa/src/components/ValueRenderer.vue b/adminforth/spa/src/components/ValueRenderer.vue index c5daf39e..4e274978 100644 --- a/adminforth/spa/src/components/ValueRenderer.vue +++ b/adminforth/spa/src/components/ValueRenderer.vue @@ -117,7 +117,7 @@ import timezone from 'dayjs/plugin/timezone'; import {checkEmptyValues} from '@/utils'; import { useRoute, useRouter } from 'vue-router'; import { JsonViewer } from "vue3-json-viewer"; -import "vue3-json-viewer/dist/index.css"; +import "vue3-json-viewer/dist/vue3-json-viewer.css"; import type { AdminForthResourceColumnCommon } from '@/types/Common'; import { useCoreStore } from '@/stores/core'; From fd2f9274160f059000ea05967acc6440c708584d Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Fri, 7 Nov 2025 14:53:34 +0200 Subject: [PATCH 07/65] fix: update allowed injection keys for the 'list' page in ConfigValidator --- adminforth/modules/configValidator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminforth/modules/configValidator.ts b/adminforth/modules/configValidator.ts index e2d7f629..9e58ed1a 100644 --- a/adminforth/modules/configValidator.ts +++ b/adminforth/modules/configValidator.ts @@ -863,7 +863,7 @@ export default class ConfigValidator implements IConfigValidator { // Validate page-specific allowed injection keys const possiblePages = ['list', 'show', 'create', 'edit']; const allowedInjectionsByPage: Record = { - list: ['beforeBreadcrumbs', 'afterBreadcrumbs', 'bottom', 'threeDotsDropdownItems', 'customActionIcons', 'tableBodyStart'], + list: ['beforeBreadcrumbs', 'afterBreadcrumbs', 'beforeActionButtons', 'bottom', 'threeDotsDropdownItems', 'customActionIcons', 'tableBodyStart'], show: ['beforeBreadcrumbs', 'afterBreadcrumbs', 'bottom', 'threeDotsDropdownItems'], edit: ['beforeBreadcrumbs', 'afterBreadcrumbs', 'bottom', 'threeDotsDropdownItems', 'saveButton'], create: ['beforeBreadcrumbs', 'afterBreadcrumbs', 'bottom', 'threeDotsDropdownItems', 'saveButton'], From a8f54b69b844edfce2ea672993aedcb2463bb65a Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Mon, 10 Nov 2025 15:02:31 +0200 Subject: [PATCH 08/65] feat: enhance table sorting functionality with per-column control and default sort handling --- .../docs/tutorial/03-Customization/15-afcl.md | 139 +++++++++++------- adminforth/spa/src/afcl/Table.vue | 17 ++- 2 files changed, 96 insertions(+), 60 deletions(-) diff --git a/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md b/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md index 40e58ef0..5fb1e3c5 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md @@ -22,57 +22,6 @@ import { Button } from '@/afcl' ``` ```html - -### Sorting - -Table supports column sorting out of the box. - -- Sorting is enabled globally by default. You can disable it entirely with `:sortable="false"`. -- Per column, sorting can be disabled with `sortable: false` inside the column definition. -- Clicking a sortable header cycles sorting in a tri‑state order: - - none β†’ ascending β†’ descending β†’ none - - When it returns to "none", the sorting is cleared. - -Basic example (client-side sorting when `data` is an array): - -```html -
-``` - -You can also predefine a default sort: - -```html -
-``` - -Notes: -- Client-side sorting supports nested field paths using dot-notation, e.g. `user.name`. -- When a column is not currently sorted, a subtle double-arrow icon is shown; arrows switch up/down for ascending/descending. -
+``` + +You can also predefine a default sort: + +```html +
+``` + +Notes: +- Client-side sorting supports nested field paths using dot-notation, e.g. `user.name`. +- When a column is not currently sorted, a subtle double-arrow icon is shown; arrows switch up/down for ascending/descending. + +#### Nested field path sorting + +You can sort by nested properties of objects in rows using dot-notation in `fieldName`. + +Example: + +```html +
+``` + +Behavior details: +- The path is split on dots and resolved step by step: `user.address.city`. +- Missing or `null` nested values are pushed to the bottom for ascending order (and top for descending) because `null/undefined` are treated as greater than defined values in asc ordering. +- Works the same for client-side sorting and for server-side loaders: when using an async loader the same `sortField` (e.g. `user.address.city`) is passed so you can implement equivalent ordering on the backend. +- Date objects at nested paths are detected and compared chronologically. +- Numeric comparison is stable for mixed numeric strings via Intl.Collator with numeric option. + +Edge cases to consider in your own data: +- Deeply missing branches like `user.profile.settings.locale` simply result in `undefined` and will follow the null ordering logic above. +- Arrays are not traversed; if you need array-specific sorting you should pre-normalize data into scalar fields before passing to the table. + ### Server-side sorting When you provide an async function to `data`, the table will pass the current sort along with pagination params. @@ -1049,9 +1081,8 @@ async function loadPageData({ offset, limit, sortField, sortDirection }) { if (sortField) url.searchParams.set('sortField', sortField); if (sortField && sortDirection) url.searchParams.set('sortDirection', sortDirection); - const res = await fetch(url.toString(), { credentials: 'include' }); - const json = await res.json(); - return { data: json.data, total: json.total }; + const { data, total } = callAdminForthApi('getProducts', {limit, offset, sortField, sortDirection}); + return { data, total }; }
(), { evenHighlights: true, pageSize: 5, - sortable: false, } ); @@ -189,6 +187,13 @@ const currentSortDirection = ref<'asc' | 'desc'>(props.defaultSortDirection ?? 'asc'); onMounted(() => { + // If defaultSortField points to a non-sortable column, ignore it + if (currentSortField.value) { + const col = props.columns?.find(c => c.fieldName === currentSortField.value); + if (!col || !isColumnSortable(col)) { + currentSortField.value = undefined; + } + } refresh(); }); @@ -206,7 +211,6 @@ }); watch([() => currentSortField.value, () => currentSortDirection.value], () => { - if (!props.sortable) return; if (currentPage.value !== 1) currentPage.value = 1; refresh(); emit('update:sortField', currentSortField.value); @@ -296,7 +300,8 @@ } function isColumnSortable(col:{fieldName:string; sortable?:boolean}) { - return props.sortable === true && col.sortable === true; + // Sorting is controlled per column; default is NOT sortable. Enable with `sortable: true`. + return col.sortable === true; } function onHeaderClick(col:{fieldName:string; sortable?:boolean}) { @@ -321,11 +326,11 @@ function getAriaSort(col:{fieldName:string; sortable?:boolean}) { const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); function sortArrayData(data:any[], sortField?:string, dir:'asc'|'desc'='asc') { - if (!props.sortable || !sortField) return data; + if (!sortField) return data; // Helper function to get nested properties by path const getByPath = (o:any, p:string) => p.split('.').reduce((a:any,k)=>a?.[k], o); return [...data].sort((a,b) => { - let av = getByPath(a, sortField), bv = getByPath(b, sortField); + const av = getByPath(a, sortField), bv = getByPath(b, sortField); // Handle null/undefined values if (av == null && bv == null) return 0; // Handle null/undefined values From df06a7bdc8efe6bf5fdc40d44247d4d3171bbc66 Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Mon, 10 Nov 2025 15:33:04 +0200 Subject: [PATCH 09/65] fix: correct tri-state sorting description in documentation and update sortDirection emission behavior --- .../documentation/docs/tutorial/03-Customization/15-afcl.md | 2 +- adminforth/spa/src/afcl/Table.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md b/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md index 5fb1e3c5..2132235d 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/15-afcl.md @@ -978,7 +978,7 @@ async function loadPageData(data) { Table supports column sorting out of the box. - By default, columns are NOT sortable. Enable sorting per column with `sortable: true`. -- Clicking a sortable header cycles sorting in a tri‑state order: +- Clicking a sortable header cycles sorting in a tri-state order: - none β†’ ascending β†’ descending β†’ none - When it returns to "none", the sorting is cleared. diff --git a/adminforth/spa/src/afcl/Table.vue b/adminforth/spa/src/afcl/Table.vue index 111199aa..0155d56e 100644 --- a/adminforth/spa/src/afcl/Table.vue +++ b/adminforth/spa/src/afcl/Table.vue @@ -216,7 +216,7 @@ emit('update:sortField', currentSortField.value); emit('update:sortDirection', currentSortField.value ? currentSortDirection.value : undefined); emit('sort-change', { field: currentSortField.value, direction: currentSortDirection.value }); - }); + }, { immediate: false }); const totalPages = computed(() => { return dataResult.value?.total ? Math.ceil(dataResult.value.total / props.pageSize) : 1; From 92c368517b0a10a3675653a0743e11332021722c Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Mon, 10 Nov 2025 17:44:38 +0200 Subject: [PATCH 10/65] fix: reset current page to 1 on sort change and improve sort-change event emission --- adminforth/spa/src/afcl/Table.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/adminforth/spa/src/afcl/Table.vue b/adminforth/spa/src/afcl/Table.vue index 0155d56e..4d55ef8f 100644 --- a/adminforth/spa/src/afcl/Table.vue +++ b/adminforth/spa/src/afcl/Table.vue @@ -211,11 +211,12 @@ }); watch([() => currentSortField.value, () => currentSortDirection.value], () => { - if (currentPage.value !== 1) currentPage.value = 1; refresh(); emit('update:sortField', currentSortField.value); emit('update:sortDirection', currentSortField.value ? currentSortDirection.value : undefined); - emit('sort-change', { field: currentSortField.value, direction: currentSortDirection.value }); + const field = currentSortField.value ?? null; + const direction = currentSortField.value ? currentSortDirection.value : null; + emit('sort-change', { field, direction }); }, { immediate: false }); const totalPages = computed(() => { From aefb0582deab3ec2b0ca0441f09a8e840cc73f20 Mon Sep 17 00:00:00 2001 From: Maksym Pipkun Date: Mon, 10 Nov 2025 17:47:54 +0200 Subject: [PATCH 11/65] fix: reset current page to 1 on sort change to ensure correct data display --- adminforth/spa/src/afcl/Table.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/adminforth/spa/src/afcl/Table.vue b/adminforth/spa/src/afcl/Table.vue index 4d55ef8f..cb7d749b 100644 --- a/adminforth/spa/src/afcl/Table.vue +++ b/adminforth/spa/src/afcl/Table.vue @@ -211,7 +211,12 @@ }); watch([() => currentSortField.value, () => currentSortDirection.value], () => { - refresh(); + const needsPageReset = currentPage.value !== 1; + if (needsPageReset) { + currentPage.value = 1; + } else { + refresh(); + } emit('update:sortField', currentSortField.value); emit('update:sortDirection', currentSortField.value ? currentSortDirection.value : undefined); const field = currentSortField.value ?? null; From ce573d8fead4c44c5c583dfa40b08003de412cf9 Mon Sep 17 00:00:00 2001 From: kirilldorr Date: Tue, 4 Nov 2025 16:22:25 +0200 Subject: [PATCH 12/65] k3s-deployment blogpost --- .../2025-11-04-k3s-ec2-deployment/index.md | 501 ++++++++++++++++++ adminforth/documentation/blog/authors.yml | 7 +- 2 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md diff --git a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md new file mode 100644 index 00000000..8513cfcb --- /dev/null +++ b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md @@ -0,0 +1,501 @@ +--- +slug: k3s-ec2-deployment +title: "IaC Simplified: K3s on EC2 Deployments with Terraform, Docker & Amazon ECR" +authors: kirilldorr +tags: [aws, terraform] +description: "The ultimate step-by-step guide to cost-effective, build-time-efficient, and easy managable EC2 deployments using K3s, Terraform, Docker, and a Amazon ECR registry." +--- + +This guide shows how to deploy own Docker apps (with AdminForth as example) to Amazon EC2 instance with K3s and Terraform involving pushing images into Amazon ECR. + +Needed resources: +- AWS account where we will auto-spawn EC2 instance. We will use `t3a.small` instance (2 vCPUs, 2GB RAM) which costs `~14$` per month in `us-west-2` region (cheapest region). Also it will take `$2` per month for EBS gp2 storage (20GB) for EC2 instance. +- Also AWS ECR will charge for `$0.09` per GB of data egress traffic (from EC2 to the internet) - this needed to load docker build cache. + +The setup shape: +- Build is done using IaaC approach with HashiCorp Terraform, so almoast no manual actions are needed from you. Every resource including EC2 server instance is described in code which is commited to repo. +- Docker build process is done on GitHub actions server, so EC2 server is not overloaded with builds +- Docker images and build cache are stored on Amazon ECR +- Total build time for average commit to AdminForth app (with Vite rebuilds) is around 3 minutes. + + + +## Why exactly K3s? + +Previously, our blog featured posts about different types of application deployment, but without the use of Kubernetes. This post will look at the cheapest option for deploying an application using k3s (a lightweight version of k8s Kubernetes). This option is more interesting than most of the alternatives, primarily because of its automation and scalability (it is, of course, inferior to the β€œolder” K8s, but it also requires significantly fewer resources). + +## How we will store containers? + +The ECR repository will be used for storage. Since we are working with AWS, this is the most reliable option. The image must be assembled from local files on the machine and then sent to the Amazon server. All instructions for performing these actions will be provided below. + +# Prerequisites + +I will assume you run Ubuntu (Native or WSL2). + +You should have terraform, here is official repository: + +``` +wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list +sudo apt update && sudo apt install terraform +``` + + +AWS CLI: + +```bash +sudo snap install aws-cli --classic +``` + +Also you need Doker Daemon running. We recommend Docker Desktop running. ON WSL2 make sure you have Docker Desktop WSL2 integration enabled. + +```bash +docker version +``` + +It is also worth having `kubectl` locally on your machine for more convenient interaction with nodes and pods, but this is not mandatory. + +```bash +curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +``` + +# Practice - deploy setup + +Assume you have your AdminForth project in `myadmin`. + +## Step 1 - create a SSH keypair + +Make sure you are still in `deploy` folder, run next command: + +```bash title="deploy" +mkdir .keys && ssh-keygen -f .keys/id_rsa -N "" +``` + +Now it should create `deploy/.keys/id_rsa` and `deploy/.keys/id_rsa.pub` files with your SSH keypair. Terraform script will put the public key to the EC2 instance and will use private key to connect to the instance. Also you will be able to use it to connect to the instance manually. + +## Step 2 - create key pairs + +I recommend doing this on the official AWS website. Go to EC2>Key pairs>Create key pair, name the new pair k3s-keys, and leave the default settings. Then move the downloaded .pem file to the myadmin/deploy/.keys directory. + +## Step 3 - .gitignore file + +Create `deploy/.gitignore` file with next content: + +```bash +.terraform/ +.keys/ +*.tfstate +*.tfstate.* +*.tfvars +tfplan +.env.secrets.prod +sa-dash.yaml +session-manager-plugin.deb +.env.secrets.prod +.terraform.lock.hcl +compose.yml +``` + +## Step 4 - file with secrets for local deploy + +Create file `deploy/.env.secrets.prod` + +```bash +ADMINFORTH_SECRET= +``` + + +## Step 5 - main terraform file main.tf + +First of all install Terraform as described here [terraform installation](https://developer.hashicorp.com/terraform/install#linux). + + +Create file `main.tf` in `deploy` folder: + +```hcl title="deploy/main.tf" + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +locals { + aws_region = "us-west-2" + vpc_cidr = "10.0.0.0/16" + subnet_a_cidr = "10.0.10.0/24" + subnet_b_cidr = "10.0.11.0/24" + az_a = "us-west-2a" + az_b = "us-west-2b" + cluster_name = "myapp-k3s" + app_name = "" + + app_container_port = 3500 + service_port = 80 + admin_secret = "your_secret" + + ingress_ports = [ + { from = 22, to = 22, protocol = "tcp", desc = "SSH" }, + { from = 80, to = 80, protocol = "tcp", desc = "App HTTP (Traefik)" }, + { from = 443, to = 443, protocol = "tcp", desc = "App HTTPS (Traefik)" }, + { from = 6443, to = 6443, protocol = "tcp", desc = "Kubernetes API" } + ] +} + +provider "aws" { + region = local.aws_region +} + +resource "aws_vpc" "main" { + cidr_block = local.vpc_cidr + + enable_dns_support = true + enable_dns_hostnames = true + + tags = { Name = "main-vpc" } +} + +resource "aws_subnet" "public_a" { + vpc_id = aws_vpc.main.id + cidr_block = local.subnet_a_cidr + map_public_ip_on_launch = true + availability_zone = local.az_a + tags = { + Name = "public-a" + } +} + +resource "aws_subnet" "public_b" { + vpc_id = aws_vpc.main.id + cidr_block = local.subnet_b_cidr + map_public_ip_on_launch = true + availability_zone = local.az_b + tags = { + Name = "public-b" + } +} + +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.main.id + tags = { Name = "main-igw" } +} + +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.main.id + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.igw.id + } + tags = { Name = "public-rt" } +} + +resource "aws_route_table_association" "public_a_assoc" { + subnet_id = aws_subnet.public_a.id + route_table_id = aws_route_table.public_rt.id +} + +resource "aws_route_table_association" "public_b_assoc" { + subnet_id = aws_subnet.public_b.id + route_table_id = aws_route_table.public_rt.id +} + +resource "aws_security_group" "app_sg" { + name = "app-sg-k3s" + vpc_id = aws_vpc.main.id + + dynamic "ingress" { + for_each = local.ingress_ports + content { + from_port = ingress.value.from + to_port = ingress.value.to + protocol = ingress.value.protocol + cidr_blocks = ["0.0.0.0/0"] + description = ingress.value.desc + } + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_iam_role" "k3s_node_role" { + name = "k3s-node-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = { Service = "ec2.amazonaws.com" } + Action = "sts:AssumeRole" + }] + }) +} + +resource "aws_iam_role_policy_attachment" "ecr_read_only_attach" { + policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" + role = aws_iam_role.k3s_node_role.name +} + +resource "aws_iam_role_policy_attachment" "ssm_core_policy" { + policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.k3s_node_role.name +} + +resource "aws_iam_instance_profile" "k3s_instance_profile" { + name = "k3s-instance-profile" + role = aws_iam_role.k3s_node_role.name +} + +data "aws_ecr_repository" "app_repo" { + name = "myadmin" +} + +data "aws_ami" "ubuntu_22_04" { + most_recent = true + owners = ["099720109477"] # Canonical ubuntu account ID + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } +} + +resource "aws_instance" "k3s_server" { + instance_type = "t3a.small" + ami = data.aws_ami.ubuntu_22_04.id + + iam_instance_profile = aws_iam_instance_profile.k3s_instance_profile.name + + subnet_id = aws_subnet.public_a.id + vpc_security_group_ids = [aws_security_group.app_sg.id] + associate_public_ip_address = true + key_name = "k3s-keys" + + tags = { + Name = local.cluster_name + } + + user_data = <<-EOF + #!/bin/bash -e + + echo "LOG update apt..." + export DEBIAN_FRONTEND=noninteractive + apt-get update -y + + echo "LOG Installing Docker&AWS CLI..." + apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release awscli + + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + + apt-get update -y + apt-get install -y docker-ce docker-ce-cli containerd.io + + echo "LOG Starting Docker..." + systemctl start docker + systemctl enable docker + usermod -a -G docker ubuntu + + ECR_REGISTRY="${data.aws_ecr_repository.app_repo.registry_id}.dkr.ecr.${local.aws_region}.amazonaws.com" + + echo "LOG Gettong ETC password..." + ECR_PASSWORD="" + RETRY_COUNT=0 + MAX_RETRIES=12 + + until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do + ECR_PASSWORD=$(aws ecr get-login-password --region ${local.aws_region} 2>/dev/null) + if [ -n "$ECR_PASSWORD" ]; then + echo "LOG Successfull." + break + fi + RETRY_COUNT=$((RETRY_COUNT+1)) + echo "LOG Retry (for 5s)..." + sleep 5 + done + + if [ -z "$ECR_PASSWORD" ]; then + echo "LOG ERROR: Unable to retrieve ECR password after $MAX_RETRIES attempts." + exit 1 + fi + + echo $ECR_PASSWORD | docker login --username AWS --password-stdin $ECR_REGISTRY + + if [ $? -ne 0 ]; then + echo "LOG ERROR: Docker login to ECR failed." + exit 1 + fi + echo "LOG Docker login successful." + + echo "LOG Waiting for Docker socket..." + timeout 60 sh -c 'until docker info > /dev/null 2>&1; do echo "LOG Waiting for Docker socket..."; sleep 3; done' + + if ! docker info > /dev/null 2>&1; then + echo "LOG ERROR: Docker socket not available after timeout." + exit 1 + fi + + echo "LOG Turning off ufw..." + ufw disable || echo "LOG ufw not installed, skipping disable." + + echo "LOG Retrieving public IP..." + PUBLIC_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) + + echo "LOG Installing K3s Π· --tls-san=$${PUBLIC_IP}..." + curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--docker --tls-san $${PUBLIC_IP}" sh - + + echo "LOG Waiting for k3s.yaml to be created..." + START_TIME=$(date +%s) + until [ -f /etc/rancher/k3s/k3s.yaml ]; do + CURRENT_TIME=$(date +%s) + if (( CURRENT_TIME - START_TIME > 300 )); then + echo "LOG ERROR: Timeout waiting for k3s.yaml." + echo "LOG k3s.yaml status check:" + systemctl status k3s.service || systemctl status k3s-server.service || echo "LOG Failed to get k3s service status" + echo "LOG Last 50 lines of k3s logs:" + journalctl -u k3s.service -n 50 --no-pager || journalctl -u k3s-server.service -n 50 --no-pager || echo "LOG Failed to get k3s logs" + exit 1 + fi + echo "LOG Waiting for k3s.yaml... (passed $(( CURRENT_TIME - START_TIME )) seconds)" + sleep 5 + done + + echo "LOG k3s.yaml found." + + echo "LOG Updating k3s.yaml with public IP..." + sed -i "s/127.0.0.1/$${PUBLIC_IP}/g" /etc/rancher/k3s/k3s.yaml + + echo "LOG Copying k3s.yaml to /home/ubuntu/k3s.yaml..." + cp /etc/rancher/k3s/k3s.yaml /home/ubuntu/k3s.yaml + chown ubuntu:ubuntu /home/ubuntu/k3s.yaml + + echo "LOG Creating Kubernetes manifest for our app..." + cat < /tmp/myapp-manifest.yaml + --- + apiVersion: v1 + kind: Namespace + metadata: + name: ${local.app_name} + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ${local.app_name}-deployment + namespace: ${local.app_name} + spec: + replicas: 1 + selector: + matchLabels: + app: ${local.app_name} + template: + metadata: + labels: + app: ${local.app_name} + spec: + containers: + - name: ${local.app_name} + image: "${data.aws_ecr_repository.app_repo.repository_url}:latest" + ports: + - containerPort: ${local.app_container_port} + env: + - name: "ADMINFORTH_SECRET" + value: "${local.admin_secret}" + --- + apiVersion: v1 + kind: Service + metadata: + name: ${local.app_name}-service + namespace: ${local.app_name} + spec: + type: ClusterIP + selector: + app: ${local.app_name} + ports: + - port: ${local.service_port} + targetPort: ${local.app_container_port} + --- + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: ${local.app_name}-ingress + namespace: ${local.app_name} + spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: ${local.app_name}-service + port: + number: ${local.service_port} + EOT_APP + + echo "LOG Applying Kubernetes manifest..." + /usr/local/bin/k3s kubectl apply -f /tmp/myapp-manifest.yaml + + EOF + + # prevent accidental termination of ec2 instance and data loss + # if you will need to recreate the instance still (not sure why it can be?), you will need to remove this block manually by next command: + # > terraform taint aws_instance.app_instance + lifecycle { + create_before_destroy = true + prevent_destroy = true + ignore_changes = [ami] + } + + root_block_device { + volume_size = 20 // Size in GB for root partition + volume_type = "gp2" + + # Even if the instance is terminated, the volume will not be deleted, delete it manually if needed + delete_on_termination = false + } +} + +output "app_endpoint" { + value = "http://${aws_instance.k3s_server.public_dns}" +} + +output "kubectl_config_command" { + value = "scp -i .keys/k3s-keys.pem ubuntu@${aws_instance.k3s_server.public_dns}:/home/ubuntu/k3s.yaml ~/.kube/config-k3s && export KUBECONFIG=~/.kube/config-k3s" +} + +``` + +> πŸ‘† Replace `` with your app name (no spaces, only underscores or letters) + + +### Step 6 - Configure AWS Profile + +Open or create file `~/.aws/credentials` and add (if not already there): + +```ini +[myaws] +aws_access_key_id = +aws_secret_access_key = +``` + + +### Step 7.2 - Run deployment + +All actions related to deployment are automated and written in the script in `user_data`, so no additional actions are required. To deploy the application, you only need to enter the two commands below and wait a few minutes. After that, you can connect to the web application using the link you will receive in `terraform_output`. Next, if you wish, you can add GitHub Actions. To do this, you can use the instructions in [our other post](https://adminforth.dev/blog/compose-aws-ec2-ecr-terraform-github-actions/#chellenges-when-you-build-on-ci). + +```bash +terraform init +``` + +Now run deployement: + +```bash +terraform apply -auto-approve +``` diff --git a/adminforth/documentation/blog/authors.yml b/adminforth/documentation/blog/authors.yml index 0dcbac0b..1a118bb6 100644 --- a/adminforth/documentation/blog/authors.yml +++ b/adminforth/documentation/blog/authors.yml @@ -17,4 +17,9 @@ ypechorkin: name: Yaroslav Pechorkin title: Developer of AdminForth url: https://github.com/yaroslav8765 - image_url: https://avatars.githubusercontent.com/u/189334989?v=4 \ No newline at end of file + image_url: https://avatars.githubusercontent.com/u/189334989?v=4 +kirilldorr: + name: Kyrylo Doropii + title: DevOps Engineer of AdminForth + url: https://github.com/kirilldorr + image_url: https://avatars.githubusercontent.com/u/181721742?s=96&v=4 \ No newline at end of file From 00e883e9a4400e84532d01627498949e9532aa43 Mon Sep 17 00:00:00 2001 From: kirilldorr Date: Thu, 13 Nov 2025 14:41:32 +0200 Subject: [PATCH 13/65] Updated blog content on k3s --- .../2025-11-04-k3s-ec2-deployment/index.md | 765 +++++++++++++----- adminforth/documentation/blog/tags.yml | 10 + 2 files changed, 562 insertions(+), 213 deletions(-) diff --git a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md index 8513cfcb..2c1386af 100644 --- a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md +++ b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md @@ -1,8 +1,8 @@ --- slug: k3s-ec2-deployment -title: "IaC Simplified: K3s on EC2 Deployments with Terraform, Docker & Amazon ECR" +title: "IaC Simplified: K3s on EC2 Deployments with Terraform, Helm & Amazon ECR" authors: kirilldorr -tags: [aws, terraform] +tags: [aws, terraform, helm, k3s] description: "The ultimate step-by-step guide to cost-effective, build-time-efficient, and easy managable EC2 deployments using K3s, Terraform, Docker, and a Amazon ECR registry." --- @@ -47,6 +47,15 @@ AWS CLI: sudo snap install aws-cli --classic ``` +HELM: + +```bash +curl https://baltocdn.com/helm/signing.asc | sudo tee /etc/apt/trusted.gpg.d/helm.asc +sudo apt-get install apt-transport-https --yes +echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list +sudo apt-get install helm +``` + Also you need Doker Daemon running. We recommend Docker Desktop running. ON WSL2 make sure you have Docker Desktop WSL2 integration enabled. ```bash @@ -93,7 +102,7 @@ sa-dash.yaml session-manager-plugin.deb .env.secrets.prod .terraform.lock.hcl -compose.yml +k3s.yaml ``` ## Step 4 - file with secrets for local deploy @@ -105,14 +114,15 @@ ADMINFORTH_SECRET= ``` -## Step 5 - main terraform file main.tf +## Step 5 - Terraform folder First of all install Terraform as described here [terraform installation](https://developer.hashicorp.com/terraform/install#linux). +After this create folder ../deploy/terraform Create file `main.tf` in `deploy` folder: -```hcl title="deploy/main.tf" +```hcl title="deploy/terraform/main.tf" terraform { required_providers { @@ -121,17 +131,19 @@ terraform { version = "~> 5.0" } } + } - + locals { - aws_region = "us-west-2" - vpc_cidr = "10.0.0.0/16" - subnet_a_cidr = "10.0.10.0/24" - subnet_b_cidr = "10.0.11.0/24" - az_a = "us-west-2a" - az_b = "us-west-2b" - cluster_name = "myapp-k3s" - app_name = "" + aws_region = "us-west-2" + vpc_cidr = "10.0.0.0/16" + subnet_a_cidr = "10.0.10.0/24" + subnet_b_cidr = "10.0.11.0/24" + az_a = "us-west-2a" + az_b = "us-west-2b" + cluster_name = "myappk3s" + app_name = + app_source_code_path = "../../" app_container_port = 3500 service_port = 80 @@ -149,6 +161,147 @@ provider "aws" { region = local.aws_region } +provider "kubernetes" { + config_path = "../k3s.yaml" +} + +data "aws_ami" "ubuntu_22_04" { + most_recent = true + owners = ["099720109477"] # Canonical ubuntu account ID + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } +} + +resource "aws_instance" "k3s_server" { + instance_type = "t3a.small" + ami = data.aws_ami.ubuntu_22_04.id + + iam_instance_profile = aws_iam_instance_profile.k3s_instance_profile.name + + subnet_id = aws_subnet.public_a.id + vpc_security_group_ids = [aws_security_group.app_sg.id] + associate_public_ip_address = true + key_name = "k3s-keys" + + tags = { + Name = local.cluster_name + } + + depends_on = [ + null_resource.docker_build_and_push + ] + + user_data = templatefile("../user_data.sh.tpl", { + app_name = local.app_name + aws_region = local.aws_region + admin_secret = local.admin_secret + app_container_port = local.app_container_port + service_port = local.service_port + ecr_registry_id = aws_ecr_repository.app_repo.registry_id + ecr_image_full = "${aws_ecr_repository.app_repo.repository_url}:latest" + } + ) + + # prevent accidental termination of ec2 instance and data loss + lifecycle { + #create_before_destroy = true #uncomment in production + #prevent_destroy = true #uncomment in production + ignore_changes = [ami] + } + + root_block_device { + volume_size = 10 // Size in GB for root partition + volume_type = "gp2" + + # Even if the instance is terminated, the volume will not be deleted, delete it manually if needed + delete_on_termination = true #change to false in production if data persistence is needed + } + +} + +resource "null_resource" "get_kubeconfig" { + depends_on = [aws_instance.k3s_server] + + provisioner "local-exec" { + command = <<-EOT + set -e + for i in {1..15}; do + if nc -z ${aws_instance.k3s_server.public_ip} 22; then + break + fi + sleep 5 + done + + for i in {1..15}; do + scp -q -o StrictHostKeyChecking=no -i ../.keys/k3s-keys.pem \ + ubuntu@${aws_instance.k3s_server.public_dns}:/home/ubuntu/k3s.yaml ../k3s.yaml && { + sleep 5 + exit 0 + } + + echo "k3s.yaml not found yet (attempt $i/15), retrying in 10s..." + sleep 10 + done + EOT + interpreter = ["/bin/bash", "-c"] + } +} +``` + +> πŸ‘† Replace `` with your app name (no spaces, only underscores or letters) + +We will also need a file `container.tf` + +```hcl title="deploy/terraform/container.tf" + +resource "aws_ecr_repository" "app_repo" { + name = local.app_name + + image_tag_mutability = "MUTABLE" + image_scanning_configuration { + scan_on_push = true + } + force_delete = true +} + +data "aws_caller_identity" "current" {} + +resource "null_resource" "docker_build_and_push" { + + depends_on = [aws_ecr_repository.app_repo] + + provisioner "local-exec" { + command = <<-EOT + set -e + unset DOCKER_HOST + + REPO_URL="${aws_ecr_repository.app_repo.repository_url}" + ACCOUNT_ID="${data.aws_caller_identity.current.account_id}" + REGION="${local.aws_region}" + + echo "LOG: Logging in to ECR..." + aws ecr get-login-password --region $${REGION} | docker login --username AWS --password-stdin $${ACCOUNT_ID}.dkr.ecr.$${REGION}.amazonaws.com + + echo "LOG: Building Docker image..." + docker -H unix:///var/run/docker.sock build --pull -t $${REPO_URL}:latest ${local.app_source_code_path} + + echo "LOG: Pushing image to ECR..." + docker -H unix:///var/run/docker.sock push $${REPO_URL}:latest + + echo "LOG: Build and push complete." + EOT + + interpreter = ["/bin/bash", "-c"] + } +} +``` + +Also, `resvpc.tf` + +```hcl title="deploy/terraform/resvpc.tf" resource "aws_vpc" "main" { cidr_block = local.vpc_cidr @@ -251,231 +404,399 @@ resource "aws_iam_instance_profile" "k3s_instance_profile" { name = "k3s-instance-profile" role = aws_iam_role.k3s_node_role.name } +``` + +And `outputs.tf` + +```hcl title="deploy/terraform/outputs.tf" -data "aws_ecr_repository" "app_repo" { - name = "myadmin" +output "app_endpoint" { + value = "http://${aws_instance.k3s_server.public_dns}" } -data "aws_ami" "ubuntu_22_04" { - most_recent = true - owners = ["099720109477"] # Canonical ubuntu account ID +output "kubectl_config_command" { + value = "scp -i .keys/k3s-keys.pem ubuntu@${aws_instance.k3s_server.public_dns}:/home/ubuntu/k3s.yaml ~/.kube/config-k3s && export KUBECONFIG=~/.kube/config-k3s" +} - filter { - name = "name" - values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] +output "ssh_connect_command" { + value = "ssh -i .keys/k3s-keys.pem ubuntu@${aws_instance.k3s_server.public_dns}" +} + +output "instance_public_ip" { + value = aws_instance.k3s_server.public_ip +} + +output "ecr_repository_url" { + value = aws_ecr_repository.app_repo.repository_url +} + +resource "null_resource" "output_to_file" { + provisioner "local-exec" { + command = "terraform output -json > ../terraform_outputs.json" } + depends_on = [null_resource.get_kubeconfig] } +``` -resource "aws_instance" "k3s_server" { - instance_type = "t3a.small" - ami = data.aws_ami.ubuntu_22_04.id +### Step 6 - Helm - iam_instance_profile = aws_iam_instance_profile.k3s_instance_profile.name +**Helm** is a command-line tool and a set of libraries that helps manage applications in Kubernetes. - subnet_id = aws_subnet.public_a.id - vpc_security_group_ids = [aws_security_group.app_sg.id] - associate_public_ip_address = true - key_name = "k3s-keys" +**Helm Chart (Chart)** is a package containing everything needed to run an application in Kubernetes. It's the equivalent of `apt` or `yum` packages in Linux. - tags = { - Name = local.cluster_name - } +A chart has this structure: - user_data = <<-EOF - #!/bin/bash -e - - echo "LOG update apt..." - export DEBIAN_FRONTEND=noninteractive - apt-get update -y - - echo "LOG Installing Docker&AWS CLI..." - apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release awscli - - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg - - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null - - apt-get update -y - apt-get install -y docker-ce docker-ce-cli containerd.io - - echo "LOG Starting Docker..." - systemctl start docker - systemctl enable docker - usermod -a -G docker ubuntu - - ECR_REGISTRY="${data.aws_ecr_repository.app_repo.registry_id}.dkr.ecr.${local.aws_region}.amazonaws.com" - - echo "LOG Gettong ETC password..." - ECR_PASSWORD="" - RETRY_COUNT=0 - MAX_RETRIES=12 - - until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do - ECR_PASSWORD=$(aws ecr get-login-password --region ${local.aws_region} 2>/dev/null) - if [ -n "$ECR_PASSWORD" ]; then - echo "LOG Successfull." - break - fi - RETRY_COUNT=$((RETRY_COUNT+1)) - echo "LOG Retry (for 5s)..." - sleep 5 - done - - if [ -z "$ECR_PASSWORD" ]; then - echo "LOG ERROR: Unable to retrieve ECR password after $MAX_RETRIES attempts." - exit 1 - fi - - echo $ECR_PASSWORD | docker login --username AWS --password-stdin $ECR_REGISTRY - - if [ $? -ne 0 ]; then - echo "LOG ERROR: Docker login to ECR failed." - exit 1 - fi - echo "LOG Docker login successful." - - echo "LOG Waiting for Docker socket..." - timeout 60 sh -c 'until docker info > /dev/null 2>&1; do echo "LOG Waiting for Docker socket..."; sleep 3; done' - - if ! docker info > /dev/null 2>&1; then - echo "LOG ERROR: Docker socket not available after timeout." - exit 1 - fi - - echo "LOG Turning off ufw..." - ufw disable || echo "LOG ufw not installed, skipping disable." - - echo "LOG Retrieving public IP..." - PUBLIC_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) - - echo "LOG Installing K3s Π· --tls-san=$${PUBLIC_IP}..." - curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--docker --tls-san $${PUBLIC_IP}" sh - - - echo "LOG Waiting for k3s.yaml to be created..." - START_TIME=$(date +%s) - until [ -f /etc/rancher/k3s/k3s.yaml ]; do - CURRENT_TIME=$(date +%s) - if (( CURRENT_TIME - START_TIME > 300 )); then - echo "LOG ERROR: Timeout waiting for k3s.yaml." - echo "LOG k3s.yaml status check:" - systemctl status k3s.service || systemctl status k3s-server.service || echo "LOG Failed to get k3s service status" - echo "LOG Last 50 lines of k3s logs:" - journalctl -u k3s.service -n 50 --no-pager || journalctl -u k3s-server.service -n 50 --no-pager || echo "LOG Failed to get k3s logs" - exit 1 - fi - echo "LOG Waiting for k3s.yaml... (passed $(( CURRENT_TIME - START_TIME )) seconds)" - sleep 5 - done - - echo "LOG k3s.yaml found." - - echo "LOG Updating k3s.yaml with public IP..." - sed -i "s/127.0.0.1/$${PUBLIC_IP}/g" /etc/rancher/k3s/k3s.yaml - - echo "LOG Copying k3s.yaml to /home/ubuntu/k3s.yaml..." - cp /etc/rancher/k3s/k3s.yaml /home/ubuntu/k3s.yaml - chown ubuntu:ubuntu /home/ubuntu/k3s.yaml - - echo "LOG Creating Kubernetes manifest for our app..." - cat < /tmp/myapp-manifest.yaml - --- - apiVersion: v1 - kind: Namespace - metadata: - name: ${local.app_name} - --- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: ${local.app_name}-deployment - namespace: ${local.app_name} - spec: - replicas: 1 - selector: - matchLabels: - app: ${local.app_name} - template: - metadata: - labels: - app: ${local.app_name} - spec: - containers: - - name: ${local.app_name} - image: "${data.aws_ecr_repository.app_repo.repository_url}:latest" - ports: - - containerPort: ${local.app_container_port} - env: - - name: "ADMINFORTH_SECRET" - value: "${local.admin_secret}" - --- - apiVersion: v1 - kind: Service - metadata: - name: ${local.app_name}-service - namespace: ${local.app_name} - spec: - type: ClusterIP - selector: - app: ${local.app_name} - ports: - - port: ${local.service_port} - targetPort: ${local.app_container_port} - --- - apiVersion: networking.k8s.io/v1 - kind: Ingress +``` +helm_charts/ +β”œβ”€β”€ Chart.yaml # Metadata about the chart (name, version) +β”œβ”€β”€ values.yaml # Default values (configuration) +└── templates/ # Folder with Kubernetes templates (YAML files) + β”œβ”€β”€ deployment.yaml + β”œβ”€β”€ service.yaml + β”œβ”€β”€ ingress.yaml + └── ... +``` + +### Step 7 - Provider Helm + +Now we need to create .../deploy/helm and .../deploy/helm/helm_charts folders + +you need to create a file `Chart.yaml` in it + +```yaml title="deploy/helm/helm_charts/Chart.yaml" +apiVersion: v2 +name: myappk3s +description: Helm chart for myadmin app +version: 0.1.0 +appVersion: "1.0.0" +``` + +And `values.yaml` + +```yaml title="deploy/helm/helm_charts/values.yaml" +appName: myappk3s +containerPort: 3500 +servicePort: 80 +adminSecret: "your_secret" +``` +After this create .../deploy/helm/helm_charts/templates folder + +And create files here: + + `deployment.yaml` + +```yaml title="deploy/helm/helm_charts/templates/deployment.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.appName }}-deployment + namespace: {{ .Values.appName }} +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Values.appName }} + template: metadata: - name: ${local.app_name}-ingress - namespace: ${local.app_name} + labels: + app: {{ .Values.appName }} spec: - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: ${local.app_name}-service - port: - number: ${local.service_port} - EOT_APP - - echo "LOG Applying Kubernetes manifest..." - /usr/local/bin/k3s kubectl apply -f /tmp/myapp-manifest.yaml - - EOF + containers: + - name: {{ .Values.appName }} + image: "{{ .Values.ecrImageFull }}" + ports: + - containerPort: {{ .Values.containerPort }} + env: + - name: "ADMINFORTH_SECRET" + value: "{{ .Values.adminSecret }}" +``` - # prevent accidental termination of ec2 instance and data loss - # if you will need to recreate the instance still (not sure why it can be?), you will need to remove this block manually by next command: - # > terraform taint aws_instance.app_instance - lifecycle { - create_before_destroy = true - prevent_destroy = true - ignore_changes = [ami] + `ingress.yaml` + +```yaml title="deploy/helm/helm_charts/templates/ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Values.appName }}-ingress + namespace: {{ .Values.appName }} +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Values.appName }}-service + port: + number: {{ .Values.servicePort }} +``` + +And `service.yaml` + +```yaml title="deploy/helm/helm_charts/templates/service.yaml" +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.appName }}-service + namespace: {{ .Values.appName }} +spec: + type: ClusterIP + selector: + app: {{ .Values.appName }} + ports: + - port: {{ .Values.servicePort }} + targetPort: {{ .Values.containerPort }} + +``` +### Step 8 - Control Helm + +For controlling helm we also use terraform, so we need to create one more folder ../deploy/helm/terraform +And inside it are the files: `main.tf`, `outputs.tf`, `variables.tf`, `terraform.tfvars`. + +```hcl title="deploy/helm/terraform/main.tf" +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.0.0, < 3.0.0" + } + helm = { + source = "hashicorp/helm" + version = ">= 3.0.0" + } } +} - root_block_device { - volume_size = 20 // Size in GB for root partition - volume_type = "gp2" +provider "kubernetes" { + config_path = "../../k3s.yaml" +} - # Even if the instance is terminated, the volume will not be deleted, delete it manually if needed - delete_on_termination = false +provider "helm" { + kubernetes = { + config_path = "../../k3s.yaml" } } -output "app_endpoint" { - value = "http://${aws_instance.k3s_server.public_dns}" +data "local_file" "config_file" { + filename = "../../terraform_outputs.json" +} + +locals { + config = jsondecode(data.local_file.config_file.content) +} + +resource "kubernetes_namespace" "myappk3s" { + metadata { + name = "myappk3s" + + labels = { + "app.kubernetes.io/managed-by" = "Helm" + } + + annotations = { + "meta.helm.sh/release-name" = "myapp" + "meta.helm.sh/release-namespace" = "myappk3s" + } + } +} + +resource "helm_release" "myapp" { + name = "myapp" + chart = "../helm_charts" + namespace = kubernetes_namespace.myappk3s.metadata.0.name + create_namespace = false + + set = [ + { + name = "ecrImageFull" + value = local.config.ecr_repository_url.value + }, + { + name = "image.tag" + value = "latest" + }, + { + name = "adminSecret" + value = var.admin_secret + }, + { + name = "ingress.enabled" + value = "true" + }, + { + name = "ingress.hosts[0].host" + value = "${local.config.instance_public_ip.value}.nip.io" + }, + { + name = "ingress.hosts[0].paths[0].path" + value = "/" + }, + { + name = "ingress.hosts[0].paths[0].pathType" + value = "Prefix" + }, + { + name = "appName" + value = var.cluster_name + } + ] + depends_on = [kubernetes_namespace.myappk3s] +} +``` + +```hcl title="deploy/helm/terraform/variables.tf" +variable "admin_secret" { + description = "Admin secret for the application" + type = string +} + +variable "cluster_name" { + description = "The name of the cluster" + type = string +} + +variable "app_name" { + type = string + default = "myapp" +} +``` + +```hcl title="deploy/helm/terraform/outputs.tf" +data "local_file" "json_file" { + filename = "../../terraform_outputs.json" +} + +locals { + duplicated_json = jsondecode(data.local_file.json_file.content) +} + +output "ssh_connect_command" { + value = local.duplicated_json["ssh_connect_command"]["value"] } output "kubectl_config_command" { - value = "scp -i .keys/k3s-keys.pem ubuntu@${aws_instance.k3s_server.public_dns}:/home/ubuntu/k3s.yaml ~/.kube/config-k3s && export KUBECONFIG=~/.kube/config-k3s" + value = local.duplicated_json["kubectl_config_command"]["value"] } +output "app_endpoint" { + value = local.duplicated_json["app_endpoint"]["value"] +} ``` -> πŸ‘† Replace `` with your app name (no spaces, only underscores or letters) +```hcl title="deploy/helm/terraform/terraform.tfstate" +admin_secret = "your_secret" +cluster_name = "myappk3s" +app_name = "myapp" +``` + +### step 9 - ES2 settings instantiation + +The configuration will be performed using a small bash script. + +Fot this step we need to return to ../deploy folder and create `user_data.sh.tpl` + +```bash title="deploy/userdata.sh.tpl" +#!/bin/bash -e + +echo "LOG update apt..." +export DEBIAN_FRONTEND=noninteractive +apt-get update -y + +echo "LOG Installing Docker&AWS CLI..." +apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release awscli + +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + +apt-get update -y +apt-get install -y docker-ce docker-ce-cli containerd.io + +echo "LOG Starting Docker..." +systemctl start docker +systemctl enable docker +usermod -a -G docker ubuntu + +ECR_REGISTRY="${ecr_registry_id}.dkr.ecr.${aws_region}.amazonaws.com" +echo "LOG Gettong ETC password..." +ECR_PASSWORD="" +RETRY_COUNT=0 +MAX_RETRIES=12 -### Step 6 - Configure AWS Profile +until [ $RETRY_COUNT -ge $MAX_RETRIES ]; do + + ECR_PASSWORD=$(aws ecr get-login-password --region ${aws_region} 2>/dev/null) + if [ -n "$ECR_PASSWORD" ]; then + echo "LOG Successfull." + break + fi + RETRY_COUNT=$((RETRY_COUNT+1)) + echo "LOG Retry (for 5s)..." + sleep 5 +done + +if [ -z "$ECR_PASSWORD" ]; then + echo "LOG ERROR: Unable to retrieve ECR password after $MAX_RETRIES attempts." + exit 1 +fi + +echo $ECR_PASSWORD | docker login --username AWS --password-stdin $ECR_REGISTRY + +if [ $? -ne 0 ]; then + echo "LOG ERROR: Docker login to ECR failed." + exit 1 +fi +echo "LOG Docker login successful." + +echo "LOG Waiting for Docker socket..." +timeout 60 sh -c 'until docker info > /dev/null 2>&1; do echo "LOG Waiting for Docker socket..."; sleep 3; done' + +if ! docker info > /dev/null 2>&1; then + echo "LOG ERROR: Docker socket not available after timeout." + exit 1 +fi + +echo "LOG Turning off ufw..." +ufw disable || echo "LOG ufw not installed, skipping disable." + +echo "LOG Retrieving public IP..." +PUBLIC_IP=$(curl -s http://169.254.169.254/latest/meta-data/public-ipv4) + +echo "LOG Installing K3s Π· --tls-san=$${PUBLIC_IP}..." +curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--docker --tls-san $${PUBLIC_IP}" sh - + +echo "LOG Waiting for k3s.yaml to be created..." +START_TIME=$(date +%s) +until [ -f /etc/rancher/k3s/k3s.yaml ]; do + CURRENT_TIME=$(date +%s) + if (( CURRENT_TIME - START_TIME > 300 )); then + echo "LOG ERROR: Timeout waiting for k3s.yaml." + echo "LOG k3s.yaml status check:" + systemctl status k3s.service || systemctl status k3s-server.service || echo "LOG Failed to get k3s service status" + echo "LOG Last 50 lines of k3s logs:" + journalctl -u k3s.service -n 50 --no-pager || journalctl -u k3s-server.service -n 50 --no-pager || echo "LOG Failed to get k3s logs" + exit 1 + fi + echo "LOG Waiting for k3s.yaml... (passed $(( CURRENT_TIME - START_TIME )) seconds)" + sleep 5 +done + +echo "LOG k3s.yaml found." + +echo "LOG Updating k3s.yaml with public IP..." +sed -i "s/127.0.0.1/$${PUBLIC_IP}/g" /etc/rancher/k3s/k3s.yaml + +echo "LOG Copying k3s.yaml to /home/ubuntu/k3s.yaml..." +cp /etc/rancher/k3s/k3s.yaml /home/ubuntu/k3s.yaml +chown ubuntu:ubuntu /home/ubuntu/k3s.yaml +``` +### Step 10 - Configure AWS Profile Open or create file `~/.aws/credentials` and add (if not already there): @@ -485,17 +806,35 @@ aws_access_key_id = aws_secret_access_key = ``` +### Step 11 - Run deployment -### Step 7.2 - Run deployment +All deployment-related actions are automated and recorded in the script in `user_data.sh.tpl`, so no additional actions are required. To deploy the application, you only need to enter a few commands listed below and wait a few minutes. After that, you will be able to connect to the web application using the link you will receive in `terraform_output`. Next, if you wish, you can add GitHub Actions. To do this, follow the instructions in [our other post](https://adminforth.dev/blog/compose-aws-ec2-ecr-terraform-github-actions/#chellenges-when-you-build-on-ci). -All actions related to deployment are automated and written in the script in `user_data`, so no additional actions are required. To deploy the application, you only need to enter the two commands below and wait a few minutes. After that, you can connect to the web application using the link you will receive in `terraform_output`. Next, if you wish, you can add GitHub Actions. To do this, you can use the instructions in [our other post](https://adminforth.dev/blog/compose-aws-ec2-ecr-terraform-github-actions/#chellenges-when-you-build-on-ci). + In ../deploy/terraform folder ```bash terraform init ``` -Now run deployement: +```bash +terraform apply -auto-approve +``` + +Wait for terraform complete the creation of all resources and change directory + +```bash +cd ../../helm/terraform +``` +And repeat the steps in this directory + +```bash +terraform init +``` ```bash terraform apply -auto-approve ``` + +### All done! + +That's it, your application is deployed on Amazon EC2 and available on the Internet. diff --git a/adminforth/documentation/blog/tags.yml b/adminforth/documentation/blog/tags.yml index 5bda9d5c..9180b6c3 100644 --- a/adminforth/documentation/blog/tags.yml +++ b/adminforth/documentation/blog/tags.yml @@ -52,3 +52,13 @@ authentication: label: Auth permalink: /auth description: Authentication is the process of verifying the identity of a user or system. Authentication is a critical component of security in software applications and systems. + +k3s: + label: k3s + permalink: /k3s + description: k3s is a lightweight version of k8s (kubernetes) that is also used for container orchestration but uses fewer resources. + +helm: + label: Helm + permalink: /helm + description: The package manager for Kubernetes From 8f16bae059598eb5a5a57d25c84bfafe10866514 Mon Sep 17 00:00:00 2001 From: kirilldorr Date: Thu, 13 Nov 2025 14:47:25 +0200 Subject: [PATCH 14/65] Updated blog content on k3s --- .../documentation/blog/2025-11-04-k3s-ec2-deployment/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md index 2c1386af..0cc62c7e 100644 --- a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md +++ b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md @@ -837,4 +837,4 @@ terraform apply -auto-approve ### All done! -That's it, your application is deployed on Amazon EC2 and available on the Internet. +Your application is now deployed on Amazon EC2 and available on the Internet. From 4066efb0b66893caf5587c90adf1773b411c66d2 Mon Sep 17 00:00:00 2001 From: kirilldorr Date: Thu, 13 Nov 2025 16:10:00 +0200 Subject: [PATCH 15/65] add build time comparison table --- .../blog/2025-11-04-k3s-ec2-deployment/index.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md index 0cc62c7e..78a83572 100644 --- a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md +++ b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md @@ -299,6 +299,13 @@ resource "null_resource" "docker_build_and_push" { } ``` +This file contains a script that builds the Docker image locally. This is done for more flexible deployment. When changing the program code, there is no need to manually update the image on EC2 or in the repository. It is updated automatically with each terraform apply. Below is a table showing the time it takes to build this image from scratch and with minimal changes. + +| Feature | +| --- | --- | +| Initial build time\* | 0m45.445s | +| Rebuild time (changed `index.ts`)\* | 0m26.757s | + Also, `resvpc.tf` ```hcl title="deploy/terraform/resvpc.tf" From d188a5f6098e36f0ddf9deecee7a3ac996d52cf6 Mon Sep 17 00:00:00 2001 From: kirilldorr Date: Thu, 13 Nov 2025 16:12:06 +0200 Subject: [PATCH 16/65] rework build time comparison table --- .../blog/2025-11-04-k3s-ec2-deployment/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md index 78a83572..ff8fcab3 100644 --- a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md +++ b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md @@ -301,10 +301,10 @@ resource "null_resource" "docker_build_and_push" { This file contains a script that builds the Docker image locally. This is done for more flexible deployment. When changing the program code, there is no need to manually update the image on EC2 or in the repository. It is updated automatically with each terraform apply. Below is a table showing the time it takes to build this image from scratch and with minimal changes. -| Feature | -| --- | --- | -| Initial build time\* | 0m45.445s | -| Rebuild time (changed `index.ts`)\* | 0m26.757s | +| Feature | Time | +| ------------------------------ | --------- | +| Initial build time\* | 0m45.445s | +| Rebuild time (changed index.ts)\* | 0m26.757s | Also, `resvpc.tf` From 031cde287dee4981ca773539917898776421bbe3 Mon Sep 17 00:00:00 2001 From: kirilldorr Date: Thu, 13 Nov 2025 16:14:48 +0200 Subject: [PATCH 17/65] rework build time comparison table --- .../documentation/blog/2025-11-04-k3s-ec2-deployment/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md index ff8fcab3..a61e46a7 100644 --- a/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md +++ b/adminforth/documentation/blog/2025-11-04-k3s-ec2-deployment/index.md @@ -306,6 +306,8 @@ This file contains a script that builds the Docker image locally. This is done f | Initial build time\* | 0m45.445s | | Rebuild time (changed index.ts)\* | 0m26.757s | +\* All tests done from local machine (Intel(R) Core(TM) i7 9760H, Docker Desktop/Ubuntu 32 GB RAM, 300Mbps up/down) up to working state + Also, `resvpc.tf` ```hcl title="deploy/terraform/resvpc.tf" From 667c5b920525f213808198df529ce1c3dc9c5d5f Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Fri, 14 Nov 2025 12:09:48 +0200 Subject: [PATCH 18/65] fix: update version of adminforth on live demo --- live-demo/app/package-lock.json | 8 ++++---- live-demo/app/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/live-demo/app/package-lock.json b/live-demo/app/package-lock.json index b9e8cf75..050903aa 100644 --- a/live-demo/app/package-lock.json +++ b/live-demo/app/package-lock.json @@ -25,7 +25,7 @@ "@adminforth/two-factors-auth": "^1.1.2", "@adminforth/upload": "^2.3.6", "@prisma/client": "^6.6.0", - "adminforth": "^2.4.0-next.280", + "adminforth": "^2.4.0-next.298", "better-sqlite3": "^10.0.0", "express": "^4.19.2" }, @@ -7940,9 +7940,9 @@ } }, "node_modules/adminforth": { - "version": "2.4.0-next.280", - "resolved": "https://registry.npmjs.org/adminforth/-/adminforth-2.4.0-next.280.tgz", - "integrity": "sha512-vMIFOo+LWANi3Wjv58ySW7FfljDm+IwlcAHqUovtkt06KhRFdb9xc+UjcohoPQ0bwGXR2NVyNzVy7Jz6YrcCLg==", + "version": "2.4.0-next.298", + "resolved": "https://registry.npmjs.org/adminforth/-/adminforth-2.4.0-next.298.tgz", + "integrity": "sha512-cuiqjqnLHLucRAuJzL+hCrlSUdaUsM0JFTWQcSTl7a2+Er+PhoXaAsKk6k+UTdt4zt04WtVsxaeQrHwYtGm8Jg==", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/live-demo/app/package.json b/live-demo/app/package.json index 892f9ed5..2f875547 100644 --- a/live-demo/app/package.json +++ b/live-demo/app/package.json @@ -32,7 +32,7 @@ "@adminforth/two-factors-auth": "^1.1.2", "@adminforth/upload": "^2.3.6", "@prisma/client": "^6.6.0", - "adminforth": "^2.4.0-next.280", + "adminforth": "^2.4.0-next.298", "better-sqlite3": "^10.0.0", "express": "^4.19.2" }, From d45c0917965b949b5941b759892ecc350767cfa2 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Fri, 14 Nov 2025 12:33:09 +0200 Subject: [PATCH 19/65] fix: reneme bulk-ai action from "Process" to "Process with AI" on live demo --- live-demo/app/resources/apartments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/live-demo/app/resources/apartments.ts b/live-demo/app/resources/apartments.ts index e4b2d675..e7e2ad24 100644 --- a/live-demo/app/resources/apartments.ts +++ b/live-demo/app/resources/apartments.ts @@ -248,7 +248,7 @@ export default { } }), new BulkAiFlowPlugin({ - actionName: 'Process', + actionName: 'Process with AI', attachFiles: async ({ record }: { record: any }) => { if (!record.apartment_image) { return []; From 520bdbc2d5b283c7e2d3113d0d72f67ac7fddd8c Mon Sep 17 00:00:00 2001 From: Vitalii Kulyk Date: Tue, 18 Nov 2025 14:45:25 +0200 Subject: [PATCH 20/65] feat: add ability in dropzone to remove selected files and upload them on by one --- .../tutorial/03-Customization/07-alert.md | 1 - adminforth/spa/src/afcl/Dropzone.vue | 131 +++++++++++++----- 2 files changed, 94 insertions(+), 38 deletions(-) diff --git a/adminforth/documentation/docs/tutorial/03-Customization/07-alert.md b/adminforth/documentation/docs/tutorial/03-Customization/07-alert.md index 41de7447..ba40ceae 100644 --- a/adminforth/documentation/docs/tutorial/03-Customization/07-alert.md +++ b/adminforth/documentation/docs/tutorial/03-Customization/07-alert.md @@ -24,7 +24,6 @@ Next variants are supported: * `warning` * `info` -// ...existing code... ### Making alert responsive You can pass buttons in the alert method and receive a response like: diff --git a/adminforth/spa/src/afcl/Dropzone.vue b/adminforth/spa/src/afcl/Dropzone.vue index b26ace6d..65a47989 100644 --- a/adminforth/spa/src/afcl/Dropzone.vue +++ b/adminforth/spa/src/afcl/Dropzone.vue @@ -16,37 +16,78 @@ 'h-32': !props.multiple, }" > -
- - - -
- - + + +
+ + + + +
+
- + +

+ {{ $t('Click to upload') }} + {{ $t('or drag and drop') }} +

+ +

+ {{ props.extensions.join(', ').toUpperCase().replace(/\./g, '') }} + +

+
- + \ No newline at end of file From e65aa5d240ec69af79fedfee60eb2fa8beed2e4f Mon Sep 17 00:00:00 2001 From: Vitalii Kulyk Date: Fri, 21 Nov 2025 13:26:12 +0200 Subject: [PATCH 28/65] fix: reset selected and stored files in Dropzone component when no files are provided --- adminforth/spa/src/afcl/Dropzone.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/adminforth/spa/src/afcl/Dropzone.vue b/adminforth/spa/src/afcl/Dropzone.vue index 98943379..929b2f95 100644 --- a/adminforth/spa/src/afcl/Dropzone.vue +++ b/adminforth/spa/src/afcl/Dropzone.vue @@ -131,8 +131,11 @@ watch(() => props.modelValue, (files) => { mime: file.type, })); storedFiles.value = Array.from(files); + } else { + selectedFiles.value = []; + storedFiles.value = []; } -}); +}, { immediate: true }); function doEmit(filesIn: FileList) { From 5c67a4c42de200083f9a542a515c2f0f0d38010c Mon Sep 17 00:00:00 2001 From: Vitalii Kulyk Date: Fri, 21 Nov 2025 13:59:03 +0200 Subject: [PATCH 29/65] fix: update MenuLink component to enhance badge alignment with flexbox --- adminforth/spa/src/components/MenuLink.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminforth/spa/src/components/MenuLink.vue b/adminforth/spa/src/components/MenuLink.vue index fa869ffc..1bca4273 100644 --- a/adminforth/spa/src/components/MenuLink.vue +++ b/adminforth/spa/src/components/MenuLink.vue @@ -33,7 +33,7 @@ > {{ item.label }}
- +
{{ item.badge }}
From 9b3a22d230cb31fbb98c336fc55b283153c07733 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Fri, 21 Nov 2025 15:13:33 +0200 Subject: [PATCH 30/65] fix: pass id of created record to the afterCreate hook and add "recordWithVirtualColumns" to this hook --- adminforth/index.ts | 5 ++++- adminforth/types/Back.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/adminforth/index.ts b/adminforth/index.ts index 0a245a4e..34cd6ffc 100644 --- a/adminforth/index.ts +++ b/adminforth/index.ts @@ -522,6 +522,8 @@ class AdminForth implements IAdminForth { return { error: err }; } + const recordWithVirtualColumns = record; + // execute hook if needed for (const hook of listify(resource.hooks?.create?.beforeSave)) { console.log('πŸͺ² Hook beforeSave', hook); @@ -563,7 +565,7 @@ class AdminForth implements IAdminForth { return { error }; } - const primaryKey = record[resource.columns.find((col) => col.primaryKey).name]; + const primaryKey = createdRecord[resource.columns.find((col) => col.primaryKey).name]; // execute hook if needed for (const hook of listify(resource.hooks?.create?.afterSave)) { @@ -574,6 +576,7 @@ class AdminForth implements IAdminForth { record: createdRecord, adminUser, adminforth: this, + recordWithVirtualColumns, extra, }); diff --git a/adminforth/types/Back.ts b/adminforth/types/Back.ts index b4081114..91e464e0 100644 --- a/adminforth/types/Back.ts +++ b/adminforth/types/Back.ts @@ -568,6 +568,7 @@ export type AfterCreateSaveFunction = (params: { adminUser: AdminUser, record: any, adminforth: IAdminForth, + recordWithVirtualColumns?: any, extra?: HttpExtra, }) => Promise<{ok: boolean, error?: string}>; From 4f06207ba1426d5e96ee78b58cafd7b82da029a0 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Fri, 21 Nov 2025 15:26:09 +0200 Subject: [PATCH 31/65] fix: fix recordWithVirtualColumns in afterCreate hook --- adminforth/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminforth/index.ts b/adminforth/index.ts index 34cd6ffc..20347bf6 100644 --- a/adminforth/index.ts +++ b/adminforth/index.ts @@ -522,7 +522,7 @@ class AdminForth implements IAdminForth { return { error: err }; } - const recordWithVirtualColumns = record; + const recordWithVirtualColumns = { ...record }; // execute hook if needed for (const hook of listify(resource.hooks?.create?.beforeSave)) { From b1cc3a188e6c3a8855a15e7e380bb3431a99a31d Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Sun, 23 Nov 2025 11:18:31 +0200 Subject: [PATCH 32/65] docs: update prisma migration examples in setup guide for the upload, oauth and email-invite plugins --- adminforth/documentation/docs/tutorial/07-Plugins/05-upload.md | 2 +- adminforth/documentation/docs/tutorial/07-Plugins/11-oauth.md | 2 +- .../documentation/docs/tutorial/07-Plugins/16-email-invite.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adminforth/documentation/docs/tutorial/07-Plugins/05-upload.md b/adminforth/documentation/docs/tutorial/07-Plugins/05-upload.md index afd4d203..31a32ddf 100644 --- a/adminforth/documentation/docs/tutorial/07-Plugins/05-upload.md +++ b/adminforth/documentation/docs/tutorial/07-Plugins/05-upload.md @@ -99,7 +99,7 @@ model apartments { Migrate prisma schema: ```bash -npx prisma migrate dev --name add-apartment-image +npm run makemigration -- --name add-apartment-image ; npm run migrate:local ``` Add column to `aparts` resource configuration: diff --git a/adminforth/documentation/docs/tutorial/07-Plugins/11-oauth.md b/adminforth/documentation/docs/tutorial/07-Plugins/11-oauth.md index 3504197e..60607e90 100644 --- a/adminforth/documentation/docs/tutorial/07-Plugins/11-oauth.md +++ b/adminforth/documentation/docs/tutorial/07-Plugins/11-oauth.md @@ -84,7 +84,7 @@ model adminuser { 2. Run the migration: ```bash -npx prisma migrate dev --name add-email-confirmed-to-adminuser +npm run makemigration -- --name add-email-confirmed-to-adminuser ; npm run migrate:local ``` 3. Configure the plugin with `emailConfirmedField`: diff --git a/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md b/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md index 22e30303..6f2ea4b6 100644 --- a/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md +++ b/adminforth/documentation/docs/tutorial/07-Plugins/16-email-invite.md @@ -143,7 +143,7 @@ model adminuser { Run the migration: ```bash -npx prisma migrate dev --name add-email-confirmed +npm run makemigration -- --name add-email-confirmed ; npm run migrate:local ``` Then update your resource configuration: From 39d51876db80111573bdcb5435563e2104138b55 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Mon, 24 Nov 2025 11:26:51 +0200 Subject: [PATCH 33/65] fix: adjust font size for filter options in Filters component --- adminforth/spa/src/components/Filters.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adminforth/spa/src/components/Filters.vue b/adminforth/spa/src/components/Filters.vue index 41be0cc1..2bbba25d 100644 --- a/adminforth/spa/src/components/Filters.vue +++ b/adminforth/spa/src/components/Filters.vue @@ -18,11 +18,11 @@
-
    +
    • -

      {{ c.label }}

      +

      {{ c.label }}